From e832fa86f18386d699908a78d2c659c94e9accd0 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 02:10:15 +0300 Subject: [PATCH 01/15] Add entity template and make 'loadouts' singular --- assets/common/entity/test.ron | 31 +++++++++++++++++++ .../common/{loadouts => loadout}/default.ron | 0 .../dungeon/tier-0/gnarling.ron | 0 .../dungeon/tier-1/adlet_bow.ron | 0 .../dungeon/tier-1/adlet_spear.ron | 0 .../dungeon/tier-2/sahagin.ron | 0 .../dungeon/tier-3/haniwa.ron | 0 .../dungeon/tier-4/myrmidon.ron | 0 .../dungeon/tier-5/beastmaster.ron | 0 .../dungeon/tier-5/husk.ron | 0 .../dungeon/tier-5/warlock.ron | 0 .../dungeon/tier-5/warlord.ron | 0 assets/common/{loadouts => loadout}/test.ron | 0 .../{loadouts => loadout}/village/guard.ron | 0 .../village/merchant.ron | 0 .../village/villager.ron | 0 common/src/comp/inventory/loadout_builder.rs | 30 +++++++++--------- 17 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 assets/common/entity/test.ron rename assets/common/{loadouts => loadout}/default.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-0/gnarling.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-1/adlet_bow.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-1/adlet_spear.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-2/sahagin.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-3/haniwa.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-4/myrmidon.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-5/beastmaster.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-5/husk.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-5/warlock.ron (100%) rename assets/common/{loadouts => loadout}/dungeon/tier-5/warlord.ron (100%) rename assets/common/{loadouts => loadout}/test.ron (100%) rename assets/common/{loadouts => loadout}/village/guard.ron (100%) rename assets/common/{loadouts => loadout}/village/merchant.ron (100%) rename assets/common/{loadouts => loadout}/village/villager.ron (100%) diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron new file mode 100644 index 0000000000..64b15c7be2 --- /dev/null +++ b/assets/common/entity/test.ron @@ -0,0 +1,31 @@ +{ + /// Name of Entity + name: Some("Paddy"), + + /// Body + /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such) + /// or Random (will use random if available for this Body) + /// or RandomWith (will use random_with if available for this Body) + body: Humanoid(Random), + + /// Loot + /// Can be Item (with asset_specifier for item) + /// or LootTable (with asset_specifier for loot table) + loot: LootTable("common.loot_tables.humanoids"), + + /// Main and second tools + /// Can be Option (with asset_specifier for item) + /// or Choice + /// (array of pairs with weight of choosing some item and Option) + main_tool: Some(Item("common.items.weapons.axe_1h.orichalcum-0")), + second_tool: None, + + /// Loadout Config as Option (with asset_specifier for loadout) + loadout_config: Some(Loadout("common.loadout.village.merchant")), + + /// Skillset Config as Option (with asset_specifier for skillset) + skillset_config: None, + + /// Meta Info (level, alignment, agency, etc) + meta: {}, +} diff --git a/assets/common/loadouts/default.ron b/assets/common/loadout/default.ron similarity index 100% rename from assets/common/loadouts/default.ron rename to assets/common/loadout/default.ron diff --git a/assets/common/loadouts/dungeon/tier-0/gnarling.ron b/assets/common/loadout/dungeon/tier-0/gnarling.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-0/gnarling.ron rename to assets/common/loadout/dungeon/tier-0/gnarling.ron diff --git a/assets/common/loadouts/dungeon/tier-1/adlet_bow.ron b/assets/common/loadout/dungeon/tier-1/adlet_bow.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-1/adlet_bow.ron rename to assets/common/loadout/dungeon/tier-1/adlet_bow.ron diff --git a/assets/common/loadouts/dungeon/tier-1/adlet_spear.ron b/assets/common/loadout/dungeon/tier-1/adlet_spear.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-1/adlet_spear.ron rename to assets/common/loadout/dungeon/tier-1/adlet_spear.ron diff --git a/assets/common/loadouts/dungeon/tier-2/sahagin.ron b/assets/common/loadout/dungeon/tier-2/sahagin.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-2/sahagin.ron rename to assets/common/loadout/dungeon/tier-2/sahagin.ron diff --git a/assets/common/loadouts/dungeon/tier-3/haniwa.ron b/assets/common/loadout/dungeon/tier-3/haniwa.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-3/haniwa.ron rename to assets/common/loadout/dungeon/tier-3/haniwa.ron diff --git a/assets/common/loadouts/dungeon/tier-4/myrmidon.ron b/assets/common/loadout/dungeon/tier-4/myrmidon.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-4/myrmidon.ron rename to assets/common/loadout/dungeon/tier-4/myrmidon.ron diff --git a/assets/common/loadouts/dungeon/tier-5/beastmaster.ron b/assets/common/loadout/dungeon/tier-5/beastmaster.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-5/beastmaster.ron rename to assets/common/loadout/dungeon/tier-5/beastmaster.ron diff --git a/assets/common/loadouts/dungeon/tier-5/husk.ron b/assets/common/loadout/dungeon/tier-5/husk.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-5/husk.ron rename to assets/common/loadout/dungeon/tier-5/husk.ron diff --git a/assets/common/loadouts/dungeon/tier-5/warlock.ron b/assets/common/loadout/dungeon/tier-5/warlock.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-5/warlock.ron rename to assets/common/loadout/dungeon/tier-5/warlock.ron diff --git a/assets/common/loadouts/dungeon/tier-5/warlord.ron b/assets/common/loadout/dungeon/tier-5/warlord.ron similarity index 100% rename from assets/common/loadouts/dungeon/tier-5/warlord.ron rename to assets/common/loadout/dungeon/tier-5/warlord.ron diff --git a/assets/common/loadouts/test.ron b/assets/common/loadout/test.ron similarity index 100% rename from assets/common/loadouts/test.ron rename to assets/common/loadout/test.ron diff --git a/assets/common/loadouts/village/guard.ron b/assets/common/loadout/village/guard.ron similarity index 100% rename from assets/common/loadouts/village/guard.ron rename to assets/common/loadout/village/guard.ron diff --git a/assets/common/loadouts/village/merchant.ron b/assets/common/loadout/village/merchant.ron similarity index 100% rename from assets/common/loadouts/village/merchant.ron rename to assets/common/loadout/village/merchant.ron diff --git a/assets/common/loadouts/village/villager.ron b/assets/common/loadout/village/villager.ron similarity index 100% rename from assets/common/loadouts/village/villager.ron rename to assets/common/loadout/village/villager.ron diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index f373b4d800..ed508f39e8 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -485,7 +485,7 @@ impl LoadoutBuilder { /// Set default armor items for the loadout. This may vary with game /// updates, but should be safe defaults for a new character. #[must_use] - pub fn defaults(self) -> Self { self.apply_asset_expect("common.loadouts.default") } + pub fn defaults(self) -> Self { self.apply_asset_expect("common.loadout.default") } /// Builds loadout of creature when spawned #[must_use] @@ -529,45 +529,45 @@ impl LoadoutBuilder { match config { LoadoutConfig::Gnarling => match active_tool_kind { Some(ToolKind::Bow | ToolKind::Staff | ToolKind::Spear) => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-0.gnarling") + builder.apply_asset_expect("common.loadout.dungeon.tier-0.gnarling") }, _ => builder, }, LoadoutConfig::Adlet => match active_tool_kind { Some(ToolKind::Bow) => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-1.adlet_bow") + builder.apply_asset_expect("common.loadout.dungeon.tier-1.adlet_bow") }, Some(ToolKind::Spear | ToolKind::Staff) => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-1.adlet_spear") + builder.apply_asset_expect("common.loadout.dungeon.tier-1.adlet_spear") }, _ => builder, }, LoadoutConfig::Sahagin => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-2.sahagin") + builder.apply_asset_expect("common.loadout.dungeon.tier-2.sahagin") }, LoadoutConfig::Haniwa => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-3.haniwa") + builder.apply_asset_expect("common.loadout.dungeon.tier-3.haniwa") }, LoadoutConfig::Myrmidon => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-4.myrmidon") + builder.apply_asset_expect("common.loadout.dungeon.tier-4.myrmidon") }, LoadoutConfig::Husk => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-5.husk") + builder.apply_asset_expect("common.loadout.dungeon.tier-5.husk") }, LoadoutConfig::Beastmaster => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-5.beastmaster") + builder.apply_asset_expect("common.loadout.dungeon.tier-5.beastmaster") }, LoadoutConfig::Warlord => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-5.warlord") + builder.apply_asset_expect("common.loadout.dungeon.tier-5.warlord") }, LoadoutConfig::Warlock => { - builder.apply_asset_expect("common.loadouts.dungeon.tier-5.warlock") + builder.apply_asset_expect("common.loadout.dungeon.tier-5.warlock") }, LoadoutConfig::Villager => builder - .apply_asset_expect("common.loadouts.village.villager") + .apply_asset_expect("common.loadout.village.villager") .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))), LoadoutConfig::Guard => builder - .apply_asset_expect("common.loadouts.village.guard") + .apply_asset_expect("common.loadout.village.guard") .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))), LoadoutConfig::Merchant => { let mut backpack = @@ -685,7 +685,7 @@ impl LoadoutBuilder { } } builder - .apply_asset_expect("common.loadouts.village.merchant") + .apply_asset_expect("common.loadout.village.merchant") .back(Some(backpack)) .bag(ArmorSlot::Bag1, Some(bag1)) .bag(ArmorSlot::Bag2, Some(bag2)) @@ -947,7 +947,7 @@ 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 loadouts = LoadoutList::load_expect_cloned("common.loadouts.*").0; + let loadouts = LoadoutList::load_expect_cloned("common.loadout.*").0; for loadout in loadouts { let spec = loadout.0; for (key, entry) in spec { From 102f6d3338bc7027f7ec64fb3518083662ac0540 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 17:30:20 +0300 Subject: [PATCH 02/15] EntityInfo assetization * Rename skillset_config to skillset_preset * Rename loadout_config to loadout_preset * Add skillset_config for asset_specifier of skillset * Add loadout_config for asset_specifier of loadout --- common/src/generation.rs | 22 ++++++++++++++++++---- common/src/states/basic_summon.rs | 9 +++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/common/src/generation.rs b/common/src/generation.rs index e922f604c7..e91763659a 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -25,8 +25,10 @@ pub struct EntityInfo { // TODO: Properly give NPCs skills pub level: Option, pub loot_drop: Option, - pub loadout_config: Option, - pub skillset_config: Option, + pub loadout_config: Option, + pub loadout_preset: Option, + pub skillset_config: Option, + pub skillset_preset: Option, pub pet: Option>, // we can't use DHashMap, do we want to move that into common? pub trading_information: Option, @@ -49,7 +51,9 @@ impl EntityInfo { level: None, loot_drop: None, loadout_config: None, + loadout_preset: None, skillset_config: None, + skillset_preset: None, pet: None, trading_information: None, } @@ -117,16 +121,26 @@ impl EntityInfo { self } - pub fn with_loadout_config(mut self, config: LoadoutConfig) -> Self { + pub fn with_loadout_config(mut self, config: String) -> Self { self.loadout_config = Some(config); self } - pub fn with_skillset_config(mut self, config: SkillSetConfig) -> Self { + pub fn with_loadout_preset(mut self, preset: LoadoutConfig) -> Self { + self.loadout_preset = Some(preset); + self + } + + pub fn with_skillset_config(mut self, config: String) -> Self { self.skillset_config = Some(config); self } + pub fn with_skillset_preset(mut self, preset: SkillSetConfig) -> Self { + self.skillset_preset = Some(preset); + self + } + pub fn with_automatic_name(mut self) -> Self { let npc_names = NPC_NAMES.read(); self.name = match &self.body { diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 4abd7f8d8e..857ab191ed 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -79,14 +79,14 @@ impl CharacterBehavior for Data { let loadout = LoadoutBuilder::build_loadout( body, None, - self.static_data.summon_info.loadout_config, + self.static_data.summon_info.loadout_preset, None, ) .build(); let stats = comp::Stats::new("Summon".to_string()); let skill_set = SkillSetBuilder::build_skillset( &None, - self.static_data.summon_info.skillset_config, + self.static_data.summon_info.skillset_preset, ) .build(); @@ -175,6 +175,7 @@ pub struct SummonInfo { body: comp::Body, scale: Option, health_scaling: u16, - loadout_config: Option, - skillset_config: Option, + // TODO: use assets for specifying skills and loadouts? + loadout_preset: Option, + skillset_preset: Option, } From e153cbe20e1cfb0d63ca80335ef6a3394a85e7d0 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 18:20:12 +0300 Subject: [PATCH 03/15] Rename LoadoutBuilder::apply_asset_expect to LoadoutBuilder::with_asset_expect --- common/src/comp/inventory/loadout_builder.rs | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index ed508f39e8..87013950cc 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -397,7 +397,7 @@ impl LoadoutBuilder { pub fn from_asset_expect(asset_specifier: &str) -> Self { let loadout = Self::new(); - loadout.apply_asset_expect(asset_specifier) + loadout.with_asset_expect(asset_specifier) } /// # Usage @@ -409,7 +409,7 @@ impl LoadoutBuilder { /// 2) Will panic if path to item specified in loadout file doesn't exist /// 3) Will panic while runs in tests and asset doesn't have "correct" form #[must_use] - pub fn apply_asset_expect(mut self, asset_specifier: &str) -> Self { + pub fn with_asset_expect(mut self, asset_specifier: &str) -> Self { let spec = LoadoutSpec::load_expect(asset_specifier).read().0.clone(); for (key, entry) in spec { let item = match entry.try_to_item(asset_specifier) { @@ -485,7 +485,7 @@ impl LoadoutBuilder { /// Set default armor items for the loadout. This may vary with game /// updates, but should be safe defaults for a new character. #[must_use] - pub fn defaults(self) -> Self { self.apply_asset_expect("common.loadout.default") } + pub fn defaults(self) -> Self { self.with_asset_expect("common.loadout.default") } /// Builds loadout of creature when spawned #[must_use] @@ -529,45 +529,45 @@ impl LoadoutBuilder { match config { LoadoutConfig::Gnarling => match active_tool_kind { Some(ToolKind::Bow | ToolKind::Staff | ToolKind::Spear) => { - builder.apply_asset_expect("common.loadout.dungeon.tier-0.gnarling") + builder.with_asset_expect("common.loadout.dungeon.tier-0.gnarling") }, _ => builder, }, LoadoutConfig::Adlet => match active_tool_kind { Some(ToolKind::Bow) => { - builder.apply_asset_expect("common.loadout.dungeon.tier-1.adlet_bow") + builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_bow") }, Some(ToolKind::Spear | ToolKind::Staff) => { - builder.apply_asset_expect("common.loadout.dungeon.tier-1.adlet_spear") + builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_spear") }, _ => builder, }, LoadoutConfig::Sahagin => { - builder.apply_asset_expect("common.loadout.dungeon.tier-2.sahagin") + builder.with_asset_expect("common.loadout.dungeon.tier-2.sahagin") }, LoadoutConfig::Haniwa => { - builder.apply_asset_expect("common.loadout.dungeon.tier-3.haniwa") + builder.with_asset_expect("common.loadout.dungeon.tier-3.haniwa") }, LoadoutConfig::Myrmidon => { - builder.apply_asset_expect("common.loadout.dungeon.tier-4.myrmidon") + builder.with_asset_expect("common.loadout.dungeon.tier-4.myrmidon") }, LoadoutConfig::Husk => { - builder.apply_asset_expect("common.loadout.dungeon.tier-5.husk") + builder.with_asset_expect("common.loadout.dungeon.tier-5.husk") }, LoadoutConfig::Beastmaster => { - builder.apply_asset_expect("common.loadout.dungeon.tier-5.beastmaster") + builder.with_asset_expect("common.loadout.dungeon.tier-5.beastmaster") }, LoadoutConfig::Warlord => { - builder.apply_asset_expect("common.loadout.dungeon.tier-5.warlord") + builder.with_asset_expect("common.loadout.dungeon.tier-5.warlord") }, LoadoutConfig::Warlock => { - builder.apply_asset_expect("common.loadout.dungeon.tier-5.warlock") + builder.with_asset_expect("common.loadout.dungeon.tier-5.warlock") }, LoadoutConfig::Villager => builder - .apply_asset_expect("common.loadout.village.villager") + .with_asset_expect("common.loadout.village.villager") .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))), LoadoutConfig::Guard => builder - .apply_asset_expect("common.loadout.village.guard") + .with_asset_expect("common.loadout.village.guard") .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))), LoadoutConfig::Merchant => { let mut backpack = @@ -685,7 +685,7 @@ impl LoadoutBuilder { } } builder - .apply_asset_expect("common.loadout.village.merchant") + .with_asset_expect("common.loadout.village.merchant") .back(Some(backpack)) .bag(ArmorSlot::Bag1, Some(bag1)) .bag(ArmorSlot::Bag2, Some(bag2)) From aad65c615917e0e9c6f8bc26f14ffec07d9981c0 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 19:22:51 +0300 Subject: [PATCH 04/15] Move traveler loadout declaration to asset * New loadout/world/traveler.ron file to specify traveler loadout * LoadoutBuilder::with_asset_expect now can use passed rng to choose items --- assets/common/loadout/world/traveler.ron | 28 +++++++++ common/src/comp/inventory/loadout_builder.rs | 64 +++++++++++--------- server/src/rtsim/entity.rs | 59 +----------------- 3 files changed, 66 insertions(+), 85 deletions(-) create mode 100644 assets/common/loadout/world/traveler.ron diff --git a/assets/common/loadout/world/traveler.ron b/assets/common/loadout/world/traveler.ron new file mode 100644 index 0000000000..c2126e5496 --- /dev/null +++ b/assets/common/loadout/world/traveler.ron @@ -0,0 +1,28 @@ +({ + ActiveMainhand: Choice([ + (1.0, Some(Item("common.items.weapons.sword.wood-2"))), + (1.0, Some(Item("common.items.weapons.sword.starter"))), + (1.0, Some(Item("common.items.weapons.sword.wood-0"))), + (1.0, Some(Item("common.items.weapons.bow.starter"))), + (1.0, Some(Item("common.items.weapons.bow.hardwood-2"))), + ]), + + 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(Back): Choice([ + (1.0, Some(Item("common.items.armor.hide.rawhide.back"))), + (1.0, Some(Item("common.items.armor.misc.back.backpack"))), + (1.0, Some(Item("common.items.npc_armor.back.backpack_blue"))), + (1.0, Some(Item("common.items.npc_armor.back.leather_blue"))), + (1.0, None), + ]), + + Lantern: Choice([ + (1.0, Some(Item("common.items.lantern.black_0"))), + (1.0, Some(Item("common.items.lantern.blue_0"))), + (1.0, Some(Item("common.items.lantern.green_0"))), + (1.0, Some(Item("common.items.lantern.red_0"))), + ]), +}) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 87013950cc..5453a9b0c2 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -73,16 +73,16 @@ enum ItemSpec { } impl ItemSpec { - fn try_to_item(&self, asset_specifier: &str) -> Option { + fn try_to_item(&self, asset_specifier: &str, rng: &mut impl Rng) -> Option { match self { ItemSpec::Item(specifier) => Some(Item::new_from_asset_expect(&specifier)), ItemSpec::Choice(items) => { - choose(&items, asset_specifier) + choose(&items, asset_specifier, rng) .as_ref() .and_then(|e| match e { - entry @ ItemSpec::Item { .. } => entry.try_to_item(asset_specifier), - choice @ ItemSpec::Choice { .. } => choice.try_to_item(asset_specifier), + entry @ ItemSpec::Item { .. } => entry.try_to_item(asset_specifier, rng), + choice @ ItemSpec::Choice { .. } => choice.try_to_item(asset_specifier, rng), }) }, } @@ -108,10 +108,12 @@ impl ItemSpec { } } -fn choose<'a>(items: &'a [(f32, Option)], asset_specifier: &str) -> &'a Option { - let mut rng = rand::thread_rng(); - - items.choose_weighted(&mut rng, |item| item.0).map_or_else( +fn choose<'a>( + items: &'a [(f32, Option)], + asset_specifier: &str, + rng: &mut impl Rng, +) -> &'a Option { + items.choose_weighted(rng, |item| item.0).map_or_else( |err| match err { WeightedError::NoItem | WeightedError::AllWeightsZero => &None, WeightedError::InvalidWeight => { @@ -394,10 +396,15 @@ impl LoadoutBuilder { } #[must_use] - pub fn from_asset_expect(asset_specifier: &str) -> Self { + pub fn from_asset_expect(asset_specifier: &str, rng: Option<&mut impl Rng>) -> Self { let loadout = Self::new(); - loadout.with_asset_expect(asset_specifier) + if let Some(rng) = rng { + loadout.with_asset_expect(asset_specifier, rng) + } else { + let rng = &mut rand::thread_rng(); + loadout.with_asset_expect(asset_specifier, rng) + } } /// # Usage @@ -409,10 +416,10 @@ impl LoadoutBuilder { /// 2) Will panic if path to item specified in loadout file doesn't exist /// 3) Will panic while runs in tests and asset doesn't have "correct" form #[must_use] - pub fn with_asset_expect(mut self, asset_specifier: &str) -> Self { + pub fn with_asset_expect(mut self, asset_specifier: &str, rng: &mut impl Rng) -> Self { let spec = LoadoutSpec::load_expect(asset_specifier).read().0.clone(); for (key, entry) in spec { - let item = match entry.try_to_item(asset_specifier) { + let item = match entry.try_to_item(asset_specifier, rng) { Some(item) => item, None => continue, }; @@ -485,7 +492,10 @@ impl LoadoutBuilder { /// Set default armor items for the loadout. This may vary with game /// updates, but should be safe defaults for a new character. #[must_use] - pub fn defaults(self) -> Self { self.with_asset_expect("common.loadout.default") } + pub fn defaults(self) -> Self { + let rng = &mut rand::thread_rng(); + self.with_asset_expect("common.loadout.default", rng) + } /// Builds loadout of creature when spawned #[must_use] @@ -523,51 +533,52 @@ impl LoadoutBuilder { } }); // Creates rest of loadout + let rng = &mut rand::thread_rng(); let loadout_builder = if let Some(config) = config { let builder = Self::new().active_mainhand(active_item); // NOTE: we apply asset after active mainhand so asset has ability override it match config { LoadoutConfig::Gnarling => match active_tool_kind { Some(ToolKind::Bow | ToolKind::Staff | ToolKind::Spear) => { - builder.with_asset_expect("common.loadout.dungeon.tier-0.gnarling") + builder.with_asset_expect("common.loadout.dungeon.tier-0.gnarling", rng) }, _ => builder, }, LoadoutConfig::Adlet => match active_tool_kind { Some(ToolKind::Bow) => { - builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_bow") + builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_bow", rng) }, Some(ToolKind::Spear | ToolKind::Staff) => { - builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_spear") + builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_spear", rng) }, _ => builder, }, LoadoutConfig::Sahagin => { - builder.with_asset_expect("common.loadout.dungeon.tier-2.sahagin") + builder.with_asset_expect("common.loadout.dungeon.tier-2.sahagin", rng) }, LoadoutConfig::Haniwa => { - builder.with_asset_expect("common.loadout.dungeon.tier-3.haniwa") + builder.with_asset_expect("common.loadout.dungeon.tier-3.haniwa", rng) }, LoadoutConfig::Myrmidon => { - builder.with_asset_expect("common.loadout.dungeon.tier-4.myrmidon") + builder.with_asset_expect("common.loadout.dungeon.tier-4.myrmidon", rng) }, LoadoutConfig::Husk => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.husk") + builder.with_asset_expect("common.loadout.dungeon.tier-5.husk", rng) }, LoadoutConfig::Beastmaster => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.beastmaster") + builder.with_asset_expect("common.loadout.dungeon.tier-5.beastmaster", rng) }, LoadoutConfig::Warlord => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.warlord") + builder.with_asset_expect("common.loadout.dungeon.tier-5.warlord", rng) }, LoadoutConfig::Warlock => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.warlock") + builder.with_asset_expect("common.loadout.dungeon.tier-5.warlock", rng) }, LoadoutConfig::Villager => builder - .with_asset_expect("common.loadout.village.villager") + .with_asset_expect("common.loadout.village.villager", rng) .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))), LoadoutConfig::Guard => builder - .with_asset_expect("common.loadout.village.guard") + .with_asset_expect("common.loadout.village.guard", rng) .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))), LoadoutConfig::Merchant => { let mut backpack = @@ -618,7 +629,6 @@ impl LoadoutBuilder { } } } - let mut rng = rand::thread_rng(); let mut item_with_amount = |item_id: &str, amount: &mut f32| { if *amount > 0.0 { let mut item = Item::new_from_asset_expect(item_id); @@ -685,7 +695,7 @@ impl LoadoutBuilder { } } builder - .with_asset_expect("common.loadout.village.merchant") + .with_asset_expect("common.loadout.village.merchant", rng) .back(Some(backpack)) .bag(ArmorSlot::Bag1, Some(bag1)) .bag(ArmorSlot::Bag2, Some(bag2)) diff --git a/server/src/rtsim/entity.rs b/server/src/rtsim/entity.rs index 940297025d..5405f10cea 100644 --- a/server/src/rtsim/entity.rs +++ b/server/src/rtsim/entity.rs @@ -77,65 +77,8 @@ impl Entity { pub fn get_loadout(&self) -> comp::inventory::loadout::Loadout { let mut rng = self.rng(PERM_LOADOUT); - let main_tool = comp::Item::new_from_asset_expect( - (&[ - "common.items.weapons.sword.wood-2", - "common.items.weapons.sword.starter", - "common.items.weapons.sword.wood-0", - "common.items.weapons.bow.starter", - "common.items.weapons.bow.hardwood-2", - ]) - .choose(&mut rng) - .unwrap(), - ); - let back = match rng.gen_range(0..5) { - 0 => Some(comp::Item::new_from_asset_expect( - "common.items.armor.hide.rawhide.back", - )), - 1 => Some(comp::Item::new_from_asset_expect( - "common.items.armor.misc.back.backpack", - )), - 2 => Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.back.backpack_blue", - )), - 3 => Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.back.leather_blue", - )), - _ => None, - }; - - let lantern = match rng.gen_range(0..4) { - 0 => Some(comp::Item::new_from_asset_expect( - "common.items.lantern.black_0", - )), - 1 => Some(comp::Item::new_from_asset_expect( - "common.items.lantern.blue_0", - )), - 2 => Some(comp::Item::new_from_asset_expect( - "common.items.lantern.green_0", - )), - _ => Some(comp::Item::new_from_asset_expect( - "common.items.lantern.red_0", - )), - }; - - let chest = Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.chest.leather_blue", - )); - let pants = Some(comp::Item::new_from_asset_expect( - "common.items.npc_armor.pants.leather_blue", - )); - let shoulder = Some(comp::Item::new_from_asset_expect( - "common.items.armor.hide.leather.shoulder", - )); - - LoadoutBuilder::build_loadout(self.get_body(), Some(main_tool), None, None) - .back(back) - .lantern(lantern) - .chest(chest) - .pants(pants) - .shoulder(shoulder) + LoadoutBuilder::from_asset_expect("common.loadout.world.traveler", &mut rng) .bag( comp::inventory::slot::ArmorSlot::Bag1, Some(comp::inventory::loadout_builder::make_potion_bag(100)), From 5f3eaddb70c8621e2851a86ce9e02adfc9873886 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sat, 5 Jun 2021 21:05:31 +0300 Subject: [PATCH 05/15] Split LodoutBuilder::build_loadout LoadoutBuilder::build_loadout is a function which has four parameters and 3 of them are Option<>, and although fourth (body) isn't Option<>, it's optional too because it is used only in some combinations of another arguments. Because these combinations produces quirky code flow, it will be better to split it to different methods. So we did following changes to remove it and rewrite code that was using it to use better methods. * Introduce LoadoutPreset as new LoadoutConfig, currently it's only used in Summon ability, because SummonInfo uses Copy and we can't specify String for specifying asset path for loadout. Everything else is rewritten to use asset path to create loadouts. * More builder methods for LoadoutBuilder. Namely: - from_default which is used in server/src/cmd.rs in "/spawn" command. - with_default_equipment, with_default_maintool to use default loadout for specific body - with_preset to use LoadoutPreset * Add new make_loadout field with `fn (loadout_builder, trading_info) -> loadout_builder` to EntityInfo which allows to lazily construct loadout without modifying LoadoutBuilder code * Fix Merchants not having trade site We had heuristic that if something has Merchant LoadoutConfig - it's merchant, which can be false, especially if we create Merchant loadout lazily As side note, we do same check for Guards and it fails too. Too fix it, we introduce new agent::Mark, which explicitly specifies kind of agent for entity * `LoadoutBuilder::build_loadout` was written in a such way that depending on main_tool you will have different loadout. Turns out it was this way only for Adlets though and this behaviour is reproduced by specifying different loadouts directly in world code. --- assets/common/entity/test.ron | 6 +- common/src/comp/agent.rs | 6 + common/src/comp/inventory/loadout_builder.rs | 429 +++++-------------- common/src/generation.rs | 48 ++- common/src/states/basic_summon.rs | 20 +- server/src/cmd.rs | 2 +- server/src/rtsim/entity.rs | 2 +- server/src/sys/terrain.rs | 66 ++- world/src/site/dungeon/mod.rs | 69 +-- world/src/site/settlement/mod.rs | 177 +++++++- 10 files changed, 413 insertions(+), 412 deletions(-) diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index 64b15c7be2..2a70be337c 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -6,7 +6,7 @@ /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such) /// or Random (will use random if available for this Body) /// or RandomWith (will use random_with if available for this Body) - body: Humanoid(Random), + // body: Humanoid(Random), /// Loot /// Can be Item (with asset_specifier for item) @@ -24,8 +24,8 @@ loadout_config: Some(Loadout("common.loadout.village.merchant")), /// Skillset Config as Option (with asset_specifier for skillset) - skillset_config: None, + // skillset_config: None, /// Meta Info (level, alignment, agency, etc) - meta: {}, + // meta: {}, } diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 3fade71b8c..8b792f361f 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -32,6 +32,12 @@ pub enum Alignment { Passive, } +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Mark { + Merchant, + Guard, +} + impl Alignment { // Always attacks pub fn hostile_towards(self, other: Alignment) -> bool { diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 5453a9b0c2..8e49b3c4ae 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -7,12 +7,11 @@ use crate::{ inventory::{ loadout::Loadout, slot::{ArmorSlot, EquipSlot}, - trade_pricing::TradePricing, }, - item::{tool::ToolKind, Item, ItemKind}, + item::Item, object, quadruped_low, quadruped_medium, theropod, Body, }, - trade::{Good, SiteInformation}, + trade::SiteInformation, }; use hashbrown::HashMap; use rand::{self, distributions::WeightedError, seq::SliceRandom, Rng}; @@ -41,20 +40,9 @@ use tracing::warn; #[derive(Clone)] pub struct LoadoutBuilder(Loadout); -#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug, EnumIter)] -pub enum LoadoutConfig { - Gnarling, - Adlet, - Sahagin, - Haniwa, - Myrmidon, +#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, Debug, EnumIter)] +pub enum LoadoutPreset { Husk, - Beastmaster, - Warlord, - Warlock, - Villager, - Guard, - Merchant, } #[derive(Debug, Deserialize, Clone)] @@ -82,7 +70,9 @@ impl ItemSpec { .as_ref() .and_then(|e| match e { entry @ ItemSpec::Item { .. } => entry.try_to_item(asset_specifier, rng), - choice @ ItemSpec::Choice { .. } => choice.try_to_item(asset_specifier, rng), + choice @ ItemSpec::Choice { .. } => { + choice.try_to_item(asset_specifier, rng) + }, }) }, } @@ -165,7 +155,7 @@ pub fn make_potion_bag(quantity: u32) -> Item { // Also we are using default tools for un-specified species so // it's fine to have wildcards #[allow(clippy::too_many_lines, clippy::match_wildcard_for_single_variants)] -pub fn default_main_tool(body: &Body) -> Option { +fn default_main_tool(body: &Body) -> Option { match body { Body::Golem(golem) => match golem.species { golem::Species::StoneGolem => Some(Item::new_from_asset_expect( @@ -350,63 +340,108 @@ impl LoadoutBuilder { pub fn new() -> Self { Self(Loadout::new_empty()) } #[must_use] - fn with_default_equipment(body: &Body, active_item: Option) -> Self { - let mut builder = Self::new(); - builder = match body { - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Mindflayer, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.mindflayer", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Minotaur, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.minotaur", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Tidalwarrior, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.tidal_warrior", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Yeti, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.yeti", - ))), - Body::BipedLarge(biped_large::Body { - species: biped_large::Species::Harvester, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.biped_large.harvester", - ))), - Body::Golem(golem::Body { - species: golem::Species::ClayGolem, - .. - }) => builder.chest(Some(Item::new_from_asset_expect( - "common.items.npc_armor.golem.claygolem", - ))), - _ => builder, - }; - - builder.active_mainhand(active_item) - } - - #[must_use] + /// Construct new `LoadoutBuilder` from `asset_specifier` + /// Will panic if asset is broken pub fn from_asset_expect(asset_specifier: &str, rng: Option<&mut impl Rng>) -> Self { + // It's impossible to use lambdas because `loadout` is used by value + #![allow(clippy::option_if_let_else)] let loadout = Self::new(); if let Some(rng) = rng { loadout.with_asset_expect(asset_specifier, rng) } else { - let rng = &mut rand::thread_rng(); - loadout.with_asset_expect(asset_specifier, rng) + let fallback_rng = &mut rand::thread_rng(); + loadout.with_asset_expect(asset_specifier, fallback_rng) } } + #[must_use] + /// Construct new default `LoadoutBuilder` for corresponding `body` + /// + /// NOTE: make sure that you check what is default for this body + /// Use it if you don't care much about it, for example in "/spawn" command + pub fn from_default(body: &Body) -> Self { + let loadout = Self::new(); + loadout + .with_default_maintool(body) + .with_default_equipment(body) + } + + #[must_use] + /// Set default active mainhand weapon based on `body` + pub fn with_default_maintool(self, body: &Body) -> Self { + self.active_mainhand(default_main_tool(body)) + } + + #[must_use] + /// Set default equipement based on `body` + pub fn with_default_equipment(mut self, body: &Body) -> Self { + self = match body { + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Mindflayer, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.mindflayer", + ))), + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Minotaur, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.minotaur", + ))), + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Tidalwarrior, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.tidal_warrior", + ))), + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Yeti, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.yeti", + ))), + Body::BipedLarge(biped_large::Body { + species: biped_large::Species::Harvester, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.biped_large.harvester", + ))), + Body::Golem(golem::Body { + species: golem::Species::ClayGolem, + .. + }) => self.chest(Some(Item::new_from_asset_expect( + "common.items.npc_armor.golem.claygolem", + ))), + _ => self, + }; + + self + } + + #[must_use] + pub fn with_preset(mut self, preset: LoadoutPreset) -> Self { + let rng = &mut rand::thread_rng(); + match preset { + LoadoutPreset::Husk => { + self = self.with_asset_expect("common.loadout.dungeon.tier-5.husk", rng) + }, + } + + self + } + + #[must_use] + pub fn with_creator( + mut self, + creator: fn(LoadoutBuilder, Option<&SiteInformation>) -> LoadoutBuilder, + economy: Option<&SiteInformation>, + ) -> LoadoutBuilder { + self = creator(self, economy); + + self + } + /// # Usage /// Creates new `LoadoutBuilder` with all field replaced from /// `asset_specifier` which corresponds to loadout config @@ -497,219 +532,6 @@ impl LoadoutBuilder { self.with_asset_expect("common.loadout.default", rng) } - /// Builds loadout of creature when spawned - #[must_use] - // The reason why this function is so long is creating merchant inventory - // with all items to sell. - // Maybe we should do it on the caller side? - #[allow( - clippy::too_many_lines, - clippy::cast_precision_loss, - clippy::cast_sign_loss, - clippy::cast_possible_truncation - )] - pub fn build_loadout( - body: Body, - mut main_tool: Option, - config: Option, - economy: Option<&SiteInformation>, - ) -> Self { - // If no main tool is passed in, checks if species has a default main tool - if main_tool.is_none() { - main_tool = default_main_tool(&body); - } - - // Constructs ItemConfig from Item - let active_item = if let Some(ItemKind::Tool(_)) = main_tool.as_ref().map(Item::kind) { - main_tool - } else { - Some(Item::empty()) - }; - let active_tool_kind = active_item.as_ref().and_then(|i| { - if let ItemKind::Tool(tool) = &i.kind() { - Some(tool.kind) - } else { - None - } - }); - // Creates rest of loadout - let rng = &mut rand::thread_rng(); - let loadout_builder = if let Some(config) = config { - let builder = Self::new().active_mainhand(active_item); - // NOTE: we apply asset after active mainhand so asset has ability override it - match config { - LoadoutConfig::Gnarling => match active_tool_kind { - Some(ToolKind::Bow | ToolKind::Staff | ToolKind::Spear) => { - builder.with_asset_expect("common.loadout.dungeon.tier-0.gnarling", rng) - }, - _ => builder, - }, - LoadoutConfig::Adlet => match active_tool_kind { - Some(ToolKind::Bow) => { - builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_bow", rng) - }, - Some(ToolKind::Spear | ToolKind::Staff) => { - builder.with_asset_expect("common.loadout.dungeon.tier-1.adlet_spear", rng) - }, - _ => builder, - }, - LoadoutConfig::Sahagin => { - builder.with_asset_expect("common.loadout.dungeon.tier-2.sahagin", rng) - }, - LoadoutConfig::Haniwa => { - builder.with_asset_expect("common.loadout.dungeon.tier-3.haniwa", rng) - }, - LoadoutConfig::Myrmidon => { - builder.with_asset_expect("common.loadout.dungeon.tier-4.myrmidon", rng) - }, - LoadoutConfig::Husk => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.husk", rng) - }, - LoadoutConfig::Beastmaster => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.beastmaster", rng) - }, - LoadoutConfig::Warlord => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.warlord", rng) - }, - LoadoutConfig::Warlock => { - builder.with_asset_expect("common.loadout.dungeon.tier-5.warlock", rng) - }, - LoadoutConfig::Villager => builder - .with_asset_expect("common.loadout.village.villager", rng) - .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))), - LoadoutConfig::Guard => builder - .with_asset_expect("common.loadout.village.guard", rng) - .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))), - LoadoutConfig::Merchant => { - let mut backpack = - Item::new_from_asset_expect("common.items.armor.misc.back.backpack"); - let mut coins = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Coin)) - .copied() - .unwrap_or_default() - .round() - .min(rand::thread_rng().gen_range(1000.0..3000.0)) - as u32; - let armor = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Armor)) - .copied() - .unwrap_or_default() - / 10.0; - for s in backpack.slots_mut() { - if coins > 0 { - let mut coin_item = - Item::new_from_asset_expect("common.items.utility.coins"); - coin_item - .set_amount(coins) - .expect("coins should be stackable"); - *s = Some(coin_item); - coins = 0; - } else if armor > 0.0 { - if let Some(item_id) = - TradePricing::random_item(Good::Armor, armor, true) - { - *s = Some(Item::new_from_asset_expect(&item_id)); - } - } - } - let mut bag1 = Item::new_from_asset_expect( - "common.items.armor.misc.bag.reliable_backpack", - ); - let weapon = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Tools)) - .copied() - .unwrap_or_default() - / 10.0; - if weapon > 0.0 { - for i in bag1.slots_mut() { - if let Some(item_id) = - TradePricing::random_item(Good::Tools, weapon, true) - { - *i = Some(Item::new_from_asset_expect(&item_id)); - } - } - } - let mut item_with_amount = |item_id: &str, amount: &mut f32| { - if *amount > 0.0 { - let mut item = Item::new_from_asset_expect(item_id); - // NOTE: Conversion to and from f32 works fine because we make sure the - // number we're converting is ≤ 100. - let max = amount.min(16.min(item.max_amount()) as f32) as u32; - let n = rng.gen_range(1..max.max(2)); - *amount -= if item.set_amount(n).is_ok() { - n as f32 - } else { - 1.0 - }; - Some(item) - } else { - None - } - }; - let mut bag2 = Item::new_from_asset_expect( - "common.items.armor.misc.bag.reliable_backpack", - ); - let mut ingredients = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Ingredients)) - .copied() - .unwrap_or_default() - / 10.0; - for i in bag2.slots_mut() { - if let Some(item_id) = - TradePricing::random_item(Good::Ingredients, ingredients, true) - { - *i = item_with_amount(&item_id, &mut ingredients); - } - } - let mut bag3 = Item::new_from_asset_expect( - "common.items.armor.misc.bag.reliable_backpack", - ); - // TODO: currently econsim spends all its food on population, resulting in none - // for the players to buy; the `.max` is temporary to ensure that there's some - // food for sale at every site, to be used until we have some solution like NPC - // houses as a limit on econsim population growth - let mut food = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Food)) - .copied() - .unwrap_or_default() - .max(10000.0) - / 10.0; - for i in bag3.slots_mut() { - if let Some(item_id) = TradePricing::random_item(Good::Food, food, true) { - *i = item_with_amount(&item_id, &mut food); - } - } - let mut bag4 = Item::new_from_asset_expect( - "common.items.armor.misc.bag.reliable_backpack", - ); - let mut potions = economy - .and_then(|e| e.unconsumed_stock.get(&Good::Potions)) - .copied() - .unwrap_or_default() - / 10.0; - for i in bag4.slots_mut() { - if let Some(item_id) = - TradePricing::random_item(Good::Potions, potions, true) - { - *i = item_with_amount(&item_id, &mut potions); - } - } - builder - .with_asset_expect("common.loadout.village.merchant", rng) - .back(Some(backpack)) - .bag(ArmorSlot::Bag1, Some(bag1)) - .bag(ArmorSlot::Bag2, Some(bag2)) - .bag(ArmorSlot::Bag3, Some(bag3)) - .bag(ArmorSlot::Bag4, Some(bag4)) - }, - } - } else { - Self::with_default_equipment(&body, active_item) - }; - - Self(loadout_builder.build()) - } - #[must_use] pub fn active_mainhand(mut self, item: Option) -> Self { self.0.swap(EquipSlot::ActiveMainhand, item); @@ -838,40 +660,13 @@ mod tests { use rand::thread_rng; use strum::IntoEnumIterator; - // Testing all configs in loadout with weapons of different toolkinds + // Testing all loadout presets // // Things that will be catched - invalid assets paths #[test] - fn test_loadout_configs() { - let test_weapons = vec![ - // Melee - "common.items.weapons.sword.starter", // Sword - "common.items.weapons.axe.starter_axe", // Axe - "common.items.weapons.hammer.starter_hammer", // Hammer - // Ranged - "common.items.weapons.bow.starter", // Bow - "common.items.weapons.staff.starter_staff", // Staff - "common.items.weapons.sceptre.starter_sceptre", // Sceptre - // Other - "common.items.weapons.dagger.starter_dagger", // Dagger - "common.items.weapons.shield.shield_1", // Shield - "common.items.npc_weapons.biped_small.sahagin.wooden_spear", // Spear - // Exotic - "common.items.npc_weapons.unique.beast_claws", // Natural - "common.items.weapons.tool.rake", // Farming - "common.items.tool.pickaxe_stone", // Pick - "common.items.weapons.empty.empty", // Empty - ]; - - for config in LoadoutConfig::iter() { - for test_weapon in &test_weapons { - std::mem::drop(LoadoutBuilder::build_loadout( - Body::Humanoid(comp::humanoid::Body::random()), - Some(Item::new_from_asset_expect(test_weapon)), - Some(config), - None, - )); - } + fn test_loadout_presets() { + for preset in LoadoutPreset::iter() { + std::mem::drop(LoadoutBuilder::default().with_preset(preset)); } } @@ -895,17 +690,11 @@ mod tests { body_type: comp::$species::BodyType::Male, ..body }; - std::mem::drop(LoadoutBuilder::build_loadout( - Body::$body(female_body), - None, - None, - None, + std::mem::drop(LoadoutBuilder::from_default( + &Body::$body(female_body), )); - std::mem::drop(LoadoutBuilder::build_loadout( - Body::$body(male_body), - None, - None, - None, + std::mem::drop(LoadoutBuilder::from_default( + &Body::$body(male_body), )); } }; diff --git a/common/src/generation.rs b/common/src/generation.rs index e91763659a..78e5726bbb 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -1,7 +1,12 @@ use crate::{ - comp::{self, humanoid, inventory::loadout_builder::LoadoutConfig, Alignment, Body, Item}, + comp::{ + self, agent, humanoid, + inventory::loadout_builder::{LoadoutBuilder, LoadoutPreset}, + Alignment, Body, Item, + }, npc::{self, NPC_NAMES}, skillset_builder::SkillSetConfig, + trade, trade::SiteInformation, }; use vek::*; @@ -17,6 +22,7 @@ pub struct EntityInfo { pub is_giant: bool, pub has_agency: bool, pub alignment: Alignment, + pub agent_mark: Option, pub body: Body, pub name: Option, pub main_tool: Option, @@ -25,13 +31,16 @@ pub struct EntityInfo { // TODO: Properly give NPCs skills pub level: Option, pub loot_drop: Option, + // FIXME: using both preset and asset is silly, make it enum + // so it will be correct by construction pub loadout_config: Option, - pub loadout_preset: Option, + pub loadout_preset: Option, + pub make_loadout: Option) -> LoadoutBuilder>, pub skillset_config: Option, pub skillset_preset: Option, pub pet: Option>, // we can't use DHashMap, do we want to move that into common? - pub trading_information: Option, + pub trading_information: Option, //Option>, /* price and available amount */ } @@ -43,6 +52,7 @@ impl EntityInfo { is_giant: false, has_agency: true, alignment: Alignment::Wild, + agent_mark: None, body: Body::Humanoid(humanoid::Body::random()), name: None, main_tool: None, @@ -52,6 +62,7 @@ impl EntityInfo { loot_drop: None, loadout_config: None, loadout_preset: None, + make_loadout: None, skillset_config: None, skillset_preset: None, pet: None, @@ -96,6 +107,11 @@ impl EntityInfo { self } + pub fn with_agent_mark(mut self, agent_mark: agent::Mark) -> Self { + self.agent_mark = Some(agent_mark); + self + } + pub fn with_main_tool(mut self, main_tool: Item) -> Self { self.main_tool = Some(main_tool); self @@ -121,18 +137,21 @@ impl EntityInfo { self } - pub fn with_loadout_config(mut self, config: String) -> Self { - self.loadout_config = Some(config); - self - } - - pub fn with_loadout_preset(mut self, preset: LoadoutConfig) -> Self { + pub fn with_loadout_preset(mut self, preset: LoadoutPreset) -> Self { self.loadout_preset = Some(preset); self } - pub fn with_skillset_config(mut self, config: String) -> Self { - self.skillset_config = Some(config); + pub fn with_loadout_config(mut self, config: &str) -> Self { + self.loadout_config = Some(config.to_owned()); + self + } + + pub fn with_lazy_loadout( + mut self, + creator: fn(LoadoutBuilder, Option<&trade::SiteInformation>) -> LoadoutBuilder, + ) -> Self { + self.make_loadout = Some(creator); self } @@ -141,6 +160,13 @@ impl EntityInfo { 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); + self + } + pub fn with_automatic_name(mut self) -> Self { let npc_names = NPC_NAMES.read(); self.name = match &self.body { diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 857ab191ed..5385dca55a 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ self, - inventory::loadout_builder::{LoadoutBuilder, LoadoutConfig}, + inventory::loadout_builder::{LoadoutBuilder, LoadoutPreset}, Behavior, BehaviorCapability, CharacterState, StateUpdate, }, event::{LocalEvent, ServerEvent}, @@ -76,13 +76,15 @@ impl CharacterBehavior for Data { { let body = self.static_data.summon_info.body; - let loadout = LoadoutBuilder::build_loadout( - body, - None, - self.static_data.summon_info.loadout_preset, - None, - ) - .build(); + let mut loadout_builder = + LoadoutBuilder::new().with_default_maintool(&body); + + if let Some(preset) = self.static_data.summon_info.loadout_preset { + loadout_builder = loadout_builder.with_preset(preset); + } + + let loadout = loadout_builder.build(); + let stats = comp::Stats::new("Summon".to_string()); let skill_set = SkillSetBuilder::build_skillset( &None, @@ -176,6 +178,6 @@ pub struct SummonInfo { scale: Option, health_scaling: u16, // TODO: use assets for specifying skills and loadouts? - loadout_preset: Option, + loadout_preset: Option, skillset_preset: Option, } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index a8f747e5b5..1ca186fbb3 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1005,7 +1005,7 @@ fn handle_spawn( ); let body = body(); - let loadout = LoadoutBuilder::build_loadout(body, None, None, None).build(); + let loadout = LoadoutBuilder::from_default(&body).build(); let inventory = Inventory::new_with_loadout(loadout); let mut entity_base = server diff --git a/server/src/rtsim/entity.rs b/server/src/rtsim/entity.rs index 5405f10cea..eebcb404c6 100644 --- a/server/src/rtsim/entity.rs +++ b/server/src/rtsim/entity.rs @@ -78,7 +78,7 @@ impl Entity { pub fn get_loadout(&self) -> comp::inventory::loadout::Loadout { let mut rng = self.rng(PERM_LOADOUT); - LoadoutBuilder::from_asset_expect("common.loadout.world.traveler", &mut rng) + LoadoutBuilder::from_asset_expect("common.loadout.world.traveler", Some(&mut rng)) .bag( comp::inventory::slot::ArmorSlot::Bag1, Some(comp::inventory::loadout_builder::make_potion_bag(100)), diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index b352de9438..9925a5926a 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -3,12 +3,9 @@ use crate::{ presence::Presence, rtsim::RtSim, settings::Settings, SpawnPoint, Tick, }; use common::{ - comp::{ - self, bird_medium, inventory::loadout_builder::LoadoutConfig, Alignment, - BehaviorCapability, Pos, - }, + comp::{self, agent, bird_medium, Alignment, BehaviorCapability, Pos}, event::{EventBus, ServerEvent}, - generation::get_npc_name, + generation::{get_npc_name, EntityInfo}, npc::NPC_NAMES, terrain::TerrainGrid, LoadoutBuilder, SkillSetBuilder, @@ -178,7 +175,6 @@ impl<'a> System<'a> for Sys { let mut body = entity.body; let name = entity.name.unwrap_or_else(|| "Unnamed".to_string()); let alignment = entity.alignment; - let main_tool = entity.main_tool; let mut stats = comp::Stats::new(name); let mut scale = entity.scale; @@ -198,14 +194,53 @@ impl<'a> System<'a> for Sys { scale = 2.0 + rand::random::(); } - let loadout_config = entity.loadout_config; - let economy = entity.trading_information.as_ref(); - let skillset_config = entity.skillset_config; + let EntityInfo { + skillset_preset, + main_tool, + loadout_preset, + loadout_config, + make_loadout, + trading_information: economy, + .. + } = entity; let skill_set = - SkillSetBuilder::build_skillset(&main_tool, skillset_config).build(); - let loadout = - LoadoutBuilder::build_loadout(body, main_tool, loadout_config, economy).build(); + SkillSetBuilder::build_skillset(&main_tool, skillset_preset).build(); + + 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 there are configs, apply them + match (loadout_preset, &loadout_config) { + (Some(preset), Some(config)) => { + loadout_builder = loadout_builder.with_preset(preset); + loadout_builder = loadout_builder.with_asset_expect(&config, rng); + }, + (Some(preset), None) => { + loadout_builder = loadout_builder.with_preset(preset); + }, + (None, Some(config)) => { + loadout_builder = loadout_builder.with_asset_expect(&config, rng); + }, + // If not, use default equipement for this body + (None, None) => { + 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(); let health = comp::Health::new(body, entity.level.unwrap_or(0)); let poise = comp::Poise::new(body); @@ -219,7 +254,7 @@ impl<'a> System<'a> for Sys { }, _ => false, }; - let trade_for_site = if matches!(loadout_config, Some(LoadoutConfig::Merchant)) { + let trade_for_site = if matches!(entity.agent_mark, Some(agent::Mark::Merchant)) { economy.map(|e| e.id) } else { None @@ -250,10 +285,7 @@ impl<'a> System<'a> for Sys { can_speak.then(|| BehaviorCapability::SPEAK), ) .with_trade_site(trade_for_site), - matches!( - loadout_config, - Some(comp::inventory::loadout_builder::LoadoutConfig::Guard) - ), + matches!(entity.agent_mark, Some(agent::Mark::Guard)), )) } else { None diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 266fba7ed4..c52ce446eb 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -11,10 +11,7 @@ use crate::{ use common::{ assets::{AssetExt, AssetHandle}, astar::Astar, - comp::{ - inventory::loadout_builder, - {self}, - }, + comp::{self}, generation::{ChunkSupplement, EntityInfo}, lottery::{LootSpec, Lottery}, store::{Id, Store}, @@ -936,8 +933,8 @@ fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { ), )) .with_name("Gnarling") - .with_loadout_config(loadout_builder::LoadoutConfig::Gnarling) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Gnarling) + .with_loadout_config("common.loadout.dungeon.tier-0.gnarling") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Gnarling) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..5) { @@ -951,21 +948,31 @@ fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-1.enemy"); - entity + let adlet = entity .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Adlet), )) .with_name("Adlet") - .with_loadout_config(loadout_builder::LoadoutConfig::Adlet) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Adlet) - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.adlet.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.adlet.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.adlet.wooden_spear", - }, - )) + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Adlet) + .with_loot_drop(chosen.read().choose().to_item()); + + match dynamic_rng.gen_range(0..5) { + 0 => adlet + .with_main_tool(comp::Item::new_from_asset_expect( + "common.items.npc_weapons.biped_small.adlet.adlet_bow", + )) + .with_loadout_config("common.loadout.dungeon.tier-1.adlet_bow"), + 1 => adlet + .with_main_tool(comp::Item::new_from_asset_expect( + "common.items.npc_weapons.biped_small.adlet.adlet_staff", + )) + .with_loadout_config("common.loadout.dungeon.tier-1.adlet_spear"), + _ => adlet + .with_main_tool(comp::Item::new_from_asset_expect( + "common.items.npc_weapons.biped_small.adlet.adlet_spear", + )) + .with_loadout_config("common.loadout.dungeon.tier-1.adlet_spear"), + } } fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { @@ -976,8 +983,8 @@ fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Sahagin), )) .with_name("Sahagin") - .with_loadout_config(loadout_builder::LoadoutConfig::Sahagin) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Sahagin) + .with_loadout_config("common.loadout.dungeon.tier-2.sahagin") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Sahagin) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..5) { @@ -1005,8 +1012,8 @@ fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { ), )) .with_name("Haniwa") - .with_loadout_config(loadout_builder::LoadoutConfig::Haniwa) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Haniwa) + .with_loadout_config("common.loadout.dungeon.tier-3.haniwa") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Haniwa) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..5) { @@ -1028,8 +1035,8 @@ fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { ), )) .with_name("Myrmidon") - .with_loadout_config(loadout_builder::LoadoutConfig::Myrmidon) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Myrmidon) + .with_loadout_config("common.loadout.dungeon.tier-4.myrmidon") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Myrmidon) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..5) { @@ -1052,16 +1059,16 @@ fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { 1 => entity .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .with_name("Cultist Warlock") - .with_loadout_config(loadout_builder::LoadoutConfig::Warlock) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Warlock) + .with_loadout_config("common.loadout.dungeon.tier-5.warlock") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlock) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( "common.items.weapons.staff.cultist_staff", )), _ => entity .with_name("Cultist Warlord") - .with_loadout_config(loadout_builder::LoadoutConfig::Warlord) - .with_skillset_config(common::skillset_builder::SkillSetConfig::Warlord) + .with_loadout_config("common.loadout.dungeon.tier-5.warlord") + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlord) .with_loot_drop(chosen.read().choose().to_item()) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..6) { @@ -1176,7 +1183,7 @@ fn boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec) -> Vec "common.items.weapons.axe.malachite_axe-0", @@ -1321,7 +1328,7 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec entity @@ -960,27 +967,28 @@ impl Settlement { "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_loadout_config(loadout_builder::LoadoutConfig::Merchant) - .with_skillset_config( + .with_skillset_preset( common::skillset_builder::SkillSetConfig::Merchant, - ) - .with_economy(&economy), + ), _ => 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 - }, + 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_loadout_config(loadout_builder::LoadoutConfig::Villager) - .with_skillset_config( + .with_lazy_loadout(villager_loadout) + .with_skillset_preset( common::skillset_builder::SkillSetConfig::Villager, ), } @@ -1043,6 +1051,137 @@ impl Settlement { } } +fn merchant_loadout( + loadout_builder: LoadoutBuilder, + economy: Option<&trade::SiteInformation>, +) -> LoadoutBuilder { + let rng = &mut rand::thread_rng(); + let mut backpack = Item::new_from_asset_expect("common.items.armor.misc.back.backpack"); + let mut coins = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Coin)) + .copied() + .unwrap_or_default() + .round() + .min(rand::thread_rng().gen_range(1000.0..3000.0)) as u32; + let armor = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Armor)) + .copied() + .unwrap_or_default() + / 10.0; + for s in backpack.slots_mut() { + if coins > 0 { + let mut coin_item = Item::new_from_asset_expect("common.items.utility.coins"); + coin_item + .set_amount(coins) + .expect("coins should be stackable"); + *s = Some(coin_item); + coins = 0; + } else if armor > 0.0 { + if let Some(item_id) = TradePricing::random_item(Good::Armor, armor, true) { + *s = Some(Item::new_from_asset_expect(&item_id)); + } + } + } + let mut bag1 = Item::new_from_asset_expect("common.items.armor.misc.bag.reliable_backpack"); + let weapon = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Tools)) + .copied() + .unwrap_or_default() + / 10.0; + if weapon > 0.0 { + for i in bag1.slots_mut() { + if let Some(item_id) = TradePricing::random_item(Good::Tools, weapon, true) { + *i = Some(Item::new_from_asset_expect(&item_id)); + } + } + } + let mut item_with_amount = |item_id: &str, amount: &mut f32| { + if *amount > 0.0 { + let mut item = Item::new_from_asset_expect(item_id); + // NOTE: Conversion to and from f32 works fine because we make sure the + // number we're converting is ≤ 100. + let max = amount.min(16.min(item.max_amount()) as f32) as u32; + let n = rng.gen_range(1..max.max(2)); + *amount -= if item.set_amount(n).is_ok() { + n as f32 + } else { + 1.0 + }; + Some(item) + } else { + None + } + }; + let mut bag2 = Item::new_from_asset_expect("common.items.armor.misc.bag.reliable_backpack"); + let mut ingredients = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Ingredients)) + .copied() + .unwrap_or_default() + / 10.0; + for i in bag2.slots_mut() { + if let Some(item_id) = TradePricing::random_item(Good::Ingredients, ingredients, true) { + *i = item_with_amount(&item_id, &mut ingredients); + } + } + let mut bag3 = Item::new_from_asset_expect("common.items.armor.misc.bag.reliable_backpack"); + // TODO: currently econsim spends all its food on population, resulting in none + // for the players to buy; the `.max` is temporary to ensure that there's some + // food for sale at every site, to be used until we have some solution like NPC + // houses as a limit on econsim population growth + let mut food = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Food)) + .copied() + .unwrap_or_default() + .max(10000.0) + / 10.0; + for i in bag3.slots_mut() { + if let Some(item_id) = TradePricing::random_item(Good::Food, food, true) { + *i = item_with_amount(&item_id, &mut food); + } + } + let mut bag4 = Item::new_from_asset_expect("common.items.armor.misc.bag.reliable_backpack"); + let mut potions = economy + .and_then(|e| e.unconsumed_stock.get(&Good::Potions)) + .copied() + .unwrap_or_default() + / 10.0; + for i in bag4.slots_mut() { + if let Some(item_id) = TradePricing::random_item(Good::Potions, potions, true) { + *i = item_with_amount(&item_id, &mut potions); + } + } + + loadout_builder + .with_asset_expect("common.loadout.village.merchant", rng) + .back(Some(backpack)) + .bag(ArmorSlot::Bag1, Some(bag1)) + .bag(ArmorSlot::Bag2, Some(bag2)) + .bag(ArmorSlot::Bag3, Some(bag3)) + .bag(ArmorSlot::Bag4, Some(bag4)) +} + +fn guard_loadout( + loadout_builder: LoadoutBuilder, + _economy: Option<&trade::SiteInformation>, +) -> LoadoutBuilder { + let rng = &mut rand::thread_rng(); + + loadout_builder + .with_asset_expect("common.loadout.village.guard", rng) + .bag(ArmorSlot::Bag1, Some(make_potion_bag(25))) +} + +fn villager_loadout( + loadout_builder: LoadoutBuilder, + _economy: Option<&trade::SiteInformation>, +) -> LoadoutBuilder { + let rng = &mut rand::thread_rng(); + + loadout_builder + .with_asset_expect("common.loadout.village.villager", rng) + .bag(ArmorSlot::Bag1, Some(make_potion_bag(10))) +} + #[derive(Copy, Clone, PartialEq)] pub enum Crop { Corn, From f5bf991eb0ddb0a97689f8dbf2e12bb6dbb008d2 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 6 Jun 2021 18:55:04 +0300 Subject: [PATCH 06/15] Start to load EntityInfo from assets in dungeons * All enemies in dungeons are now specify loadout_config, name and main_tool in assets * Add more variance to the enemies names --- assets/common/entity/dungeon/tier-0/bow.ron | 8 ++ assets/common/entity/dungeon/tier-0/spear.ron | 8 ++ assets/common/entity/dungeon/tier-0/staff.ron | 8 ++ assets/common/entity/dungeon/tier-1/bow.ron | 8 ++ assets/common/entity/dungeon/tier-1/spear.ron | 8 ++ assets/common/entity/dungeon/tier-1/staff.ron | 8 ++ assets/common/entity/dungeon/tier-2/bow.ron | 8 ++ assets/common/entity/dungeon/tier-2/spear.ron | 8 ++ assets/common/entity/dungeon/tier-2/staff.ron | 8 ++ assets/common/entity/dungeon/tier-3/bow.ron | 8 ++ assets/common/entity/dungeon/tier-3/spear.ron | 8 ++ assets/common/entity/dungeon/tier-3/staff.ron | 8 ++ assets/common/entity/dungeon/tier-4/bow.ron | 8 ++ assets/common/entity/dungeon/tier-4/spear.ron | 8 ++ assets/common/entity/dungeon/tier-4/staff.ron | 8 ++ .../common/entity/dungeon/tier-5/warlock.ron | 8 ++ .../common/entity/dungeon/tier-5/warlord.ron | 14 ++ assets/common/entity/test.ron | 18 +-- common/src/comp/inventory/loadout_builder.rs | 6 +- common/src/generation.rs | 92 +++++++++++- world/src/site/dungeon/mod.rs | 132 +++++++----------- 21 files changed, 292 insertions(+), 98 deletions(-) create mode 100644 assets/common/entity/dungeon/tier-0/bow.ron create mode 100644 assets/common/entity/dungeon/tier-0/spear.ron create mode 100644 assets/common/entity/dungeon/tier-0/staff.ron create mode 100644 assets/common/entity/dungeon/tier-1/bow.ron create mode 100644 assets/common/entity/dungeon/tier-1/spear.ron create mode 100644 assets/common/entity/dungeon/tier-1/staff.ron create mode 100644 assets/common/entity/dungeon/tier-2/bow.ron create mode 100644 assets/common/entity/dungeon/tier-2/spear.ron create mode 100644 assets/common/entity/dungeon/tier-2/staff.ron create mode 100644 assets/common/entity/dungeon/tier-3/bow.ron create mode 100644 assets/common/entity/dungeon/tier-3/spear.ron create mode 100644 assets/common/entity/dungeon/tier-3/staff.ron create mode 100644 assets/common/entity/dungeon/tier-4/bow.ron create mode 100644 assets/common/entity/dungeon/tier-4/spear.ron create mode 100644 assets/common/entity/dungeon/tier-4/staff.ron create mode 100644 assets/common/entity/dungeon/tier-5/warlock.ron create mode 100644 assets/common/entity/dungeon/tier-5/warlord.ron diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron new file mode 100644 index 0000000000..0ba5a289d6 --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Gnarling Stalker"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron new file mode 100644 index 0000000000..ffc2e9cf4f --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Gnarling Mugger"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron new file mode 100644 index 0000000000..b0b6a7a751 --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Gnarling Shaman"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron new file mode 100644 index 0000000000..fde472fdb5 --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Adlet Tracker"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron new file mode 100644 index 0000000000..48a07585be --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Adlet Hunter"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron new file mode 100644 index 0000000000..820c12436c --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Adlet Shaman"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron new file mode 100644 index 0000000000..8ea21fa08c --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Sahagin Sniper"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron new file mode 100644 index 0000000000..bd8d7c9e4e --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Sahagin Spearman"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron new file mode 100644 index 0000000000..73cb84ee55 --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Sahagin Sorcerer"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron new file mode 100644 index 0000000000..d1a9d98924 --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Haniwa Archer"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron new file mode 100644 index 0000000000..5e303990af --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Haniwa Guard"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron new file mode 100644 index 0000000000..ef130792c0 --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Haniwa Sorcerer"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron new file mode 100644 index 0000000000..7a9006fb5a --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/bow.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Myrmidon Marksman"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron new file mode 100644 index 0000000000..3ae610eb7e --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/spear.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Myrmidon Hoplite"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron new file mode 100644 index 0000000000..23d0521872 --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/staff.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Myrmidon Wizard"), + + 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"), +) diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron new file mode 100644 index 0000000000..f151381032 --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Cultist Warlock"), + + main_tool: Some(Item("common.items.weapons.staff.cultist_staff")), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-5.warlock"), +) diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron new file mode 100644 index 0000000000..8369c3f29c --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -0,0 +1,14 @@ +EntityConfig ( + name: Some("Cultist Warlord"), + + main_tool: Some(Choice([ + (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), + (2.0, Some(Item("common.items.weapons.sword.cultist"))), + (1.0, Some(Item("common.items.weapons.hammer.cultist_purp_2h-0"))), + (1.0, Some(Item("common.items.weapons.hammer_1h.orichalcum-0"))), + (1.0, Some(Item("common.items.weapons.bow.velorite"))), + ])), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-5.warlord"), +) diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index 2a70be337c..040cc162b2 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -1,4 +1,4 @@ -{ +EntityConfig ( /// Name of Entity name: Some("Paddy"), @@ -8,11 +8,6 @@ /// or RandomWith (will use random_with if available for this Body) // body: Humanoid(Random), - /// Loot - /// Can be Item (with asset_specifier for item) - /// or LootTable (with asset_specifier for loot table) - loot: LootTable("common.loot_tables.humanoids"), - /// Main and second tools /// Can be Option (with asset_specifier for item) /// or Choice @@ -20,12 +15,17 @@ main_tool: Some(Item("common.items.weapons.axe_1h.orichalcum-0")), second_tool: None, - /// Loadout Config as Option (with asset_specifier for loadout) - loadout_config: Some(Loadout("common.loadout.village.merchant")), + /// Loadout Config (with asset_specifier for loadout) + loadout_config: Some("common.loadout.village.merchant"), /// Skillset Config as Option (with asset_specifier for skillset) // skillset_config: None, + /// Loot + /// Can be Item (with asset_specifier for item) + /// or LootTable (with asset_specifier for loot table) + // loot: LootTable("common.loot_tables.humanoids"), + /// Meta Info (level, alignment, agency, etc) // meta: {}, -} +) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 8e49b3c4ae..fff159b3fe 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -46,7 +46,7 @@ pub enum LoadoutPreset { } #[derive(Debug, Deserialize, Clone)] -enum ItemSpec { +pub enum ItemSpec { /// One specific item. /// Example: /// Item("common.items.armor.steel.foot") @@ -61,7 +61,7 @@ enum ItemSpec { } impl ItemSpec { - fn try_to_item(&self, asset_specifier: &str, rng: &mut impl Rng) -> Option { + pub fn try_to_item(&self, asset_specifier: &str, rng: &mut impl Rng) -> Option { match self { ItemSpec::Item(specifier) => Some(Item::new_from_asset_expect(&specifier)), @@ -80,7 +80,7 @@ impl ItemSpec { #[cfg(test)] // Read everything and checks if it's loading - fn validate(&self, key: EquipSlot) { + pub fn validate(&self, key: EquipSlot) { match self { ItemSpec::Item(specifier) => std::mem::drop(Item::new_from_asset_expect(&specifier)), ItemSpec::Choice(items) => { diff --git a/common/src/generation.rs b/common/src/generation.rs index 78e5726bbb..79959c5552 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -1,7 +1,8 @@ use crate::{ + assets::{self, AssetExt}, comp::{ self, agent, humanoid, - inventory::loadout_builder::{LoadoutBuilder, LoadoutPreset}, + inventory::loadout_builder::{ItemSpec, LoadoutBuilder, LoadoutPreset}, Alignment, Body, Item, }, npc::{self, NPC_NAMES}, @@ -9,10 +10,21 @@ use crate::{ trade, trade::SiteInformation, }; +use serde::Deserialize; use vek::*; -pub enum EntityTemplate { - Traveller, +#[derive(Debug, Deserialize, Clone)] +struct EntityConfig { + name: Option, + main_tool: Option, + second_tool: Option, + loadout_config: Option, +} + +impl assets::Asset for EntityConfig { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; } #[derive(Clone)] @@ -70,6 +82,44 @@ impl EntityInfo { } } + pub fn with_asset_expect(self, asset_specifier: &str) -> Self { + let config = EntityConfig::load_expect(asset_specifier).read().clone(); + + self.with_entity_config(config, Some(asset_specifier)) + } + + // helper function to apply config + fn with_entity_config(mut self, config: EntityConfig, asset_specifier: Option<&str>) -> Self { + let EntityConfig { + name, + main_tool, + second_tool, + loadout_config, + } = config; + + if let Some(name) = name { + self = self.with_name(name); + } + + let rng = &mut rand::thread_rng(); + if let Some(main_tool) = + main_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng)) + { + self = self.with_main_tool(main_tool); + } + if let Some(second_tool) = + second_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng)) + { + self = self.with_main_tool(second_tool); + } + + if let Some(loadout_config) = loadout_config { + self = self.with_loadout_config(&loadout_config); + } + + self + } + pub fn do_if(mut self, cond: bool, f: impl FnOnce(Self) -> Self) -> Self { if cond { self = f(self); @@ -224,3 +274,39 @@ pub fn get_npc_name< ) -> &'a str { &body_data.species[&species].generic } + +#[cfg(test)] +mod tests { + use super::*; + use assets::Error; + + #[test] + fn test_all_entity_assets() { + #[derive(Clone)] + struct EntityList(Vec); + impl assets::Compound for EntityList { + fn load( + cache: &assets::AssetCache, + specifier: &str, + ) -> Result { + let list = cache + .load::(specifier)? + .read() + .iter() + .map(|spec| EntityConfig::load_cloned(spec)) + .collect::>()?; + + Ok(Self(list)) + } + } + + // 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)); + } + } +} diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index c52ce446eb..86b8401b06 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -925,24 +925,21 @@ impl Floor { fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.enemy"); - entity + let gnarling = entity .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with( dynamic_rng, &comp::biped_small::Species::Gnarling, ), )) - .with_name("Gnarling") - .with_loadout_config("common.loadout.dungeon.tier-0.gnarling") - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Gnarling) .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.gnarling.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.gnarling.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.gnarling.wooden_spear", - }, - )) + .with_skillset_preset(common::skillset_builder::SkillSetConfig::Gnarling); + + match dynamic_rng.gen_range(0..5) { + 0 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.bow"), + 1 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.staff"), + _ => gnarling.with_asset_expect("common.entity.dungeon.tier-0.spear"), + } } fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { @@ -952,48 +949,33 @@ 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_name("Adlet") .with_skillset_preset(common::skillset_builder::SkillSetConfig::Adlet) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { - 0 => adlet - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.npc_weapons.biped_small.adlet.adlet_bow", - )) - .with_loadout_config("common.loadout.dungeon.tier-1.adlet_bow"), - 1 => adlet - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.npc_weapons.biped_small.adlet.adlet_staff", - )) - .with_loadout_config("common.loadout.dungeon.tier-1.adlet_spear"), - _ => adlet - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.npc_weapons.biped_small.adlet.adlet_spear", - )) - .with_loadout_config("common.loadout.dungeon.tier-1.adlet_spear"), + 0 => adlet.with_asset_expect("common.entity.dungeon.tier-1.bow"), + 1 => adlet.with_asset_expect("common.entity.dungeon.tier-1.staff"), + _ => adlet.with_asset_expect("common.entity.dungeon.tier-1.spear"), } } fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-2.enemy"); - entity + let sahagin = entity .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Sahagin), )) - .with_name("Sahagin") - .with_loadout_config("common.loadout.dungeon.tier-2.sahagin") .with_skillset_preset(common::skillset_builder::SkillSetConfig::Sahagin) - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.sahagin.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.sahagin.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.sahagin.wooden_spear", - }, - )) + .with_loot_drop(chosen.read().choose().to_item()); + + match dynamic_rng.gen_range(0..5) { + 0 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.bow"), + 1 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.staff"), + _ => sahagin.with_asset_expect("common.entity.dungeon.tier-2.spear"), + } } + fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-3.enemy"); @@ -1004,48 +986,45 @@ fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { .with_loot_drop(comp::Item::new_from_asset_expect( "common.items.crafting_ing.stones", )), - _ => entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Haniwa, - ), - )) - .with_name("Haniwa") - .with_loadout_config("common.loadout.dungeon.tier-3.haniwa") - .with_skillset_preset(common::skillset_builder::SkillSetConfig::Haniwa) - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.haniwa.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.haniwa.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.haniwa.wooden_spear", - }, - )), + _ => { + let haniwa = entity + .with_body(comp::Body::BipedSmall( + comp::biped_small::Body::random_with( + dynamic_rng, + &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) { + 0 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.bow"), + 1 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.staff"), + _ => haniwa.with_asset_expect("common.entity.dungeon.tier-3.spear"), + } + }, } } fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.enemy"); - entity + let myrmidon = entity .with_body(comp::Body::BipedSmall( comp::biped_small::Body::random_with( dynamic_rng, &comp::biped_small::Species::Myrmidon, ), )) - .with_name("Myrmidon") - .with_loadout_config("common.loadout.dungeon.tier-4.myrmidon") .with_skillset_preset(common::skillset_builder::SkillSetConfig::Myrmidon) - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..5) { - 0 => "common.items.npc_weapons.biped_small.myrmidon.adlet_bow", - 1 => "common.items.npc_weapons.biped_small.myrmidon.gnoll_staff", - _ => "common.items.npc_weapons.biped_small.myrmidon.wooden_spear", - }, - )) + .with_loot_drop(chosen.read().choose().to_item()); + + match dynamic_rng.gen_range(0..5) { + 0 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.bow"), + 1 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.staff"), + _ => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.spear"), + } } + fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-5.enemy"); @@ -1058,27 +1037,14 @@ fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { )), 1 => entity .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) - .with_name("Cultist Warlock") - .with_loadout_config("common.loadout.dungeon.tier-5.warlock") .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlock) .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.weapons.staff.cultist_staff", - )), + .with_asset_expect("common.entity.dungeon.tier-5.warlock"), _ => entity - .with_name("Cultist Warlord") - .with_loadout_config("common.loadout.dungeon.tier-5.warlord") + .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_main_tool(comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0..6) { - 0 => "common.items.weapons.axe_1h.orichalcum-0", - 1..=2 => "common.items.weapons.sword.cultist", - 3 => "common.items.weapons.hammer.cultist_purp_2h-0", - 4 => "common.items.weapons.hammer_1h.orichalcum-0", - _ => "common.items.weapons.bow.bone-1", - }, - )), + .with_asset_expect("common.entity.dungeon.tier-5.warlord"), } } From 171c66d53d7af45d8f15ebea17e2c45cf33de2a8 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 6 Jun 2021 20:38:17 +0300 Subject: [PATCH 07/15] docs --- common/src/comp/inventory/loadout_builder.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index fff159b3fe..6382dd393f 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -79,7 +79,11 @@ impl ItemSpec { } #[cfg(test)] - // Read everything and checks if it's loading + /// # Usage + /// Read everything and checks if it's loading + /// + /// # Panics + /// 1) If weights are invalid pub fn validate(&self, key: EquipSlot) { match self { ItemSpec::Item(specifier) => std::mem::drop(Item::new_from_asset_expect(&specifier)), From 45e5554ff51f9ffc704f80ba6d55cc4e636db1e5 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Sun, 6 Jun 2021 21:47:12 +0300 Subject: [PATCH 08/15] Fix issue with villagers not having any item --- common/src/comp/inventory/loadout_builder.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 6382dd393f..4bf4cab6ff 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -159,8 +159,8 @@ pub fn make_potion_bag(quantity: u32) -> Item { // Also we are using default tools for un-specified species so // it's fine to have wildcards #[allow(clippy::too_many_lines, clippy::match_wildcard_for_single_variants)] -fn default_main_tool(body: &Body) -> Option { - match body { +fn default_main_tool(body: &Body) -> Item { + let maybe_tool = match body { Body::Golem(golem) => match golem.species { golem::Species::StoneGolem => Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.stone_golems_fist", @@ -332,7 +332,9 @@ fn default_main_tool(body: &Body) -> Option { )), }, _ => None, - } + }; + + maybe_tool.unwrap_or_else(Item::empty) } impl Default for LoadoutBuilder { @@ -374,7 +376,7 @@ impl LoadoutBuilder { #[must_use] /// Set default active mainhand weapon based on `body` pub fn with_default_maintool(self, body: &Body) -> Self { - self.active_mainhand(default_main_tool(body)) + self.active_mainhand(Some(default_main_tool(body))) } #[must_use] From c3a120c551be74970d0b9e09205dfad63dc42f6b Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Mon, 7 Jun 2021 01:54:15 +0300 Subject: [PATCH 09/15] Post refactoring --- .../custom/mindflayer/summonminions.ron | 4 ++-- .../abilities/custom/tidalwarrior/totem.ron | 2 +- .../entity/dungeon/tier-5/beastmaster.ron | 12 +++++++++++ assets/common/entity/dungeon/tier-5/husk.ron | 8 ++++++++ common/src/comp/inventory/loadout_builder.rs | 10 +++++----- common/src/generation.rs | 17 ++++------------ common/src/states/basic_summon.rs | 12 +++++------ server/src/sys/terrain.rs | 20 ++++++------------- world/src/site/dungeon/mod.rs | 13 ++---------- 9 files changed, 46 insertions(+), 52 deletions(-) create mode 100644 assets/common/entity/dungeon/tier-5/beastmaster.ron create mode 100644 assets/common/entity/dungeon/tier-5/husk.ron diff --git a/assets/common/abilities/custom/mindflayer/summonminions.ron b/assets/common/abilities/custom/mindflayer/summonminions.ron index d26ecb51a4..99117ce2c0 100644 --- a/assets/common/abilities/custom/mindflayer/summonminions.ron +++ b/assets/common/abilities/custom/mindflayer/summonminions.ron @@ -10,7 +10,7 @@ BasicSummon( )), scale: None, health_scaling: 80, - loadout_config: Some(Husk), + loadout_config: Some(HuskSummon), skillset_config: None, ), -) \ No newline at end of file +) diff --git a/assets/common/abilities/custom/tidalwarrior/totem.ron b/assets/common/abilities/custom/tidalwarrior/totem.ron index 343f0e4c2d..31df64a7cd 100644 --- a/assets/common/abilities/custom/tidalwarrior/totem.ron +++ b/assets/common/abilities/custom/tidalwarrior/totem.ron @@ -10,4 +10,4 @@ BasicSummon( loadout_config: None, skillset_config: None, ), -) \ No newline at end of file +) diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron new file mode 100644 index 0000000000..c34e9bc99e --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Beastmaster"), + + main_tool: Some(Choice([ + (1.0, Some(Item("common.items.weapons.axe.malachite_axe-0"))), + (1.0, Some(Item("common.items.weapons.sword.bloodsteel-1"))), + (1.0, Some(Item("common.items.weapons.bow.velorite"))), + ])), + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-5.beastmaster"), +) diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron new file mode 100644 index 0000000000..4e8bd6d35b --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/husk.ron @@ -0,0 +1,8 @@ +EntityConfig ( + name: Some("Cultist Husk"), + + main_tool: None, + second_tool: None, + + loadout_config: Some("common.loadout.dungeon.tier-5.husk"), +) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 4bf4cab6ff..0d8d6469a4 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -41,8 +41,8 @@ use tracing::warn; pub struct LoadoutBuilder(Loadout); #[derive(Copy, Clone, PartialEq, Deserialize, Serialize, Debug, EnumIter)] -pub enum LoadoutPreset { - Husk, +pub enum Preset { + HuskSummon, } #[derive(Debug, Deserialize, Clone)] @@ -426,10 +426,10 @@ impl LoadoutBuilder { } #[must_use] - pub fn with_preset(mut self, preset: LoadoutPreset) -> Self { + pub fn with_preset(mut self, preset: Preset) -> Self { let rng = &mut rand::thread_rng(); match preset { - LoadoutPreset::Husk => { + Preset::HuskSummon => { self = self.with_asset_expect("common.loadout.dungeon.tier-5.husk", rng) }, } @@ -671,7 +671,7 @@ mod tests { // Things that will be catched - invalid assets paths #[test] fn test_loadout_presets() { - for preset in LoadoutPreset::iter() { + for preset in Preset::iter() { std::mem::drop(LoadoutBuilder::default().with_preset(preset)); } } diff --git a/common/src/generation.rs b/common/src/generation.rs index 79959c5552..4cf13c361e 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -2,7 +2,7 @@ use crate::{ assets::{self, AssetExt}, comp::{ self, agent, humanoid, - inventory::loadout_builder::{ItemSpec, LoadoutBuilder, LoadoutPreset}, + inventory::loadout_builder::{ItemSpec, LoadoutBuilder}, Alignment, Body, Item, }, npc::{self, NPC_NAMES}, @@ -43,10 +43,7 @@ pub struct EntityInfo { // TODO: Properly give NPCs skills pub level: Option, pub loot_drop: Option, - // FIXME: using both preset and asset is silly, make it enum - // so it will be correct by construction pub loadout_config: Option, - pub loadout_preset: Option, pub make_loadout: Option) -> LoadoutBuilder>, pub skillset_config: Option, pub skillset_preset: Option, @@ -73,7 +70,6 @@ impl EntityInfo { level: None, loot_drop: None, loadout_config: None, - loadout_preset: None, make_loadout: None, skillset_config: None, skillset_preset: None, @@ -114,7 +110,7 @@ impl EntityInfo { } if let Some(loadout_config) = loadout_config { - self = self.with_loadout_config(&loadout_config); + self = self.with_loadout_config(loadout_config); } self @@ -187,13 +183,8 @@ impl EntityInfo { self } - pub fn with_loadout_preset(mut self, preset: LoadoutPreset) -> Self { - self.loadout_preset = Some(preset); - self - } - - pub fn with_loadout_config(mut self, config: &str) -> Self { - self.loadout_config = Some(config.to_owned()); + pub fn with_loadout_config(mut self, config: String) -> Self { + self.loadout_config = Some(config); self } diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 5385dca55a..8b69c6a3ab 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ self, - inventory::loadout_builder::{LoadoutBuilder, LoadoutPreset}, + inventory::loadout_builder::{self, LoadoutBuilder}, Behavior, BehaviorCapability, CharacterState, StateUpdate, }, event::{LocalEvent, ServerEvent}, @@ -79,7 +79,7 @@ impl CharacterBehavior for Data { let mut loadout_builder = LoadoutBuilder::new().with_default_maintool(&body); - if let Some(preset) = self.static_data.summon_info.loadout_preset { + if let Some(preset) = self.static_data.summon_info.loadout_config { loadout_builder = loadout_builder.with_preset(preset); } @@ -88,7 +88,7 @@ impl CharacterBehavior for Data { let stats = comp::Stats::new("Summon".to_string()); let skill_set = SkillSetBuilder::build_skillset( &None, - self.static_data.summon_info.skillset_preset, + self.static_data.summon_info.skillset_config, ) .build(); @@ -177,7 +177,7 @@ pub struct SummonInfo { body: comp::Body, scale: Option, health_scaling: u16, - // TODO: use assets for specifying skills and loadouts? - loadout_preset: Option, - skillset_preset: Option, + // TODO: use assets for specifying skills and loadout? + loadout_config: Option, + skillset_config: Option, } diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 9925a5926a..b4decd5bad 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -197,7 +197,6 @@ impl<'a> System<'a> for Sys { let EntityInfo { skillset_preset, main_tool, - loadout_preset, loadout_config, make_loadout, trading_information: economy, @@ -217,20 +216,13 @@ impl<'a> System<'a> for Sys { loadout_builder = loadout_builder.with_default_maintool(&body); } - // If there are configs, apply them - match (loadout_preset, &loadout_config) { - (Some(preset), Some(config)) => { - loadout_builder = loadout_builder.with_preset(preset); - loadout_builder = loadout_builder.with_asset_expect(&config, rng); + // If there is config, apply it. + // If not, use default equipement for this body. + match loadout_config { + Some(asset) => { + loadout_builder = loadout_builder.with_asset_expect(&asset, rng); }, - (Some(preset), None) => { - loadout_builder = loadout_builder.with_preset(preset); - }, - (None, Some(config)) => { - loadout_builder = loadout_builder.with_asset_expect(&config, rng); - }, - // If not, use default equipement for this body - (None, None) => { + None => { loadout_builder = loadout_builder.with_default_equipment(&body); }, } diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 86b8401b06..dc06ebc758 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -1257,17 +1257,9 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec "common.items.weapons.axe.malachite_axe-0", - 1 => "common.items.weapons.sword.bloodsteel-1", - _ => "common.items.weapons.bow.velorite", - }, - )), + .with_asset_expect("common.entity.dungeon.tier-5.beastmaster"), ); entities.resize_with(entities.len() + 2, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) @@ -1292,9 +1284,8 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec Date: Mon, 7 Jun 2021 14:38:08 +0300 Subject: [PATCH 10/15] SkillSetBuilder pre-refactoring --- common/src/comp/inventory/loadout_builder.rs | 4 +- common/src/skillset_builder.rs | 68 ++++++++++++++++---- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index 0d8d6469a4..a2d7a12a25 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -112,7 +112,7 @@ fn choose<'a>( WeightedError::NoItem | WeightedError::AllWeightsZero => &None, WeightedError::InvalidWeight => { let err = format!("Negative values of probability in {}.", asset_specifier); - if cfg!(tests) { + if cfg!(any(debug_assertions, test)) { panic!("{}", err); } else { warn!("{}", err); @@ -121,7 +121,7 @@ fn choose<'a>( }, WeightedError::TooMany => { let err = format!("More than u32::MAX values in {}.", asset_specifier); - if cfg!(tests) { + if cfg!(any(debug_assertions, test)) { panic!("{}", err); } else { warn!("{}", err); diff --git a/common/src/skillset_builder.rs b/common/src/skillset_builder.rs index e23fa36a29..d61ed16be7 100644 --- a/common/src/skillset_builder.rs +++ b/common/src/skillset_builder.rs @@ -682,25 +682,57 @@ impl SkillSetBuilder { } } + #[must_use] + /// # Panics + /// will panic only in tests + /// 1) If added skill doesn't have any group + /// 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) -> Self { - if let Some(skill_group) = skill.skill_group_kind() { - for _ in 0..level.unwrap_or(1) { - self.0 - .add_skill_points(skill_group, self.0.skill_cost(skill)); - self.0.unlock_skill(skill); - if !self.0.has_skill(skill) { - warn!( - "Failed to add skill: {:?}. Verify that it has the appropriate skill \ - group available and meets all prerequisite skills.", - skill - ); - } - } + #![warn(clippy::pedantic)] + let group = if let Some(skill_group) = skill.skill_group_kind() { + skill_group } else { - warn!( + let err = format!( "Tried to add skill: {:?} which does not have an associated skill group.", skill ); + if cfg!(test) { + panic!("{}", err); + } else { + warn!("{}", err); + } + return self; + }; + + let SkillSetBuilder(ref mut skill_set) = self; + if skill_is_applied(skill_set, skill, level) { + let err = format!( + "Tried to add skill: {:?} with level {:?} which is already applied", + skill, level, + ); + if cfg!(test) { + panic!("{}", err); + } else { + warn!("{}", err); + } + return self; + } + for _ in 0..level.unwrap_or(1) { + skill_set.add_skill_points(group, skill_set.skill_cost(skill)); + skill_set.unlock_skill(skill); + } + if !skill_is_applied(skill_set, skill, level) { + let err = format!( + "Failed to add skill: {:?}. Verify that it has the appropriate skill group \ + available and meets all prerequisite skills.", + skill + ); + if cfg!(test) { + panic!("{}", err); + } else { + warn!("{}", err); + } } self } @@ -711,3 +743,11 @@ impl SkillSetBuilder { pub fn build(self) -> SkillSet { self.0 } } + +fn skill_is_applied(skill_set: &SkillSet, skill: Skill, level: Option) -> bool { + if let Ok(applied_level) = skill_set.skill_level(skill) { + applied_level == level + } else { + false + } +} From 0c9f05b8d1b0bb315dcfc7cb10fa91770a774b4b Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 8 Jun 2021 00:58:05 +0300 Subject: [PATCH 11/15] 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 --- assets/common/entity/dungeon/tier-0/bow.ron | 3 +- assets/common/entity/dungeon/tier-0/spear.ron | 3 +- assets/common/entity/dungeon/tier-0/staff.ron | 3 +- assets/common/entity/dungeon/tier-1/bow.ron | 3 +- assets/common/entity/dungeon/tier-1/spear.ron | 3 +- assets/common/entity/dungeon/tier-1/staff.ron | 3 +- assets/common/entity/dungeon/tier-2/bow.ron | 3 +- assets/common/entity/dungeon/tier-2/spear.ron | 3 +- assets/common/entity/dungeon/tier-2/staff.ron | 3 +- assets/common/entity/dungeon/tier-3/bow.ron | 3 +- assets/common/entity/dungeon/tier-3/spear.ron | 3 +- assets/common/entity/dungeon/tier-3/staff.ron | 3 +- assets/common/entity/dungeon/tier-4/bow.ron | 3 +- assets/common/entity/dungeon/tier-4/spear.ron | 3 +- assets/common/entity/dungeon/tier-4/staff.ron | 3 +- .../entity/dungeon/tier-5/beastmaster.ron | 4 +- assets/common/entity/dungeon/tier-5/boss.ron | 9 + assets/common/entity/dungeon/tier-5/husk.ron | 3 +- .../common/entity/dungeon/tier-5/warlock.ron | 3 +- .../common/entity/dungeon/tier-5/warlord.ron | 3 +- assets/common/entity/test.ron | 6 +- assets/common/entity/village/guard.ron | 9 + assets/common/entity/village/merchant.ron | 9 + assets/common/entity/village/villager.ron | 16 + assets/common/loadout/world/traveler.ron | 2 +- assets/common/skillset/dungeon/tier-0/bow.ron | 12 + assets/common/skillset/dungeon/tier-1/bow.ron | 12 + assets/common/skillset/dungeon/tier-2/bow.ron | 12 + assets/common/skillset/dungeon/tier-3/bow.ron | 12 + assets/common/skillset/dungeon/tier-4/bow.ron | 12 + assets/common/skillset/dungeon/tier-5/axe.ron | 21 + assets/common/skillset/dungeon/tier-5/bow.ron | 21 + .../common/skillset/dungeon/tier-5/enemy.ron | 8 + .../common/skillset/dungeon/tier-5/hammer.ron | 21 + .../skillset/dungeon/tier-5/mindflayer.ron | 21 + .../common/skillset/dungeon/tier-5/staff.ron | 21 + .../common/skillset/dungeon/tier-5/sword.ron | 19 + assets/common/skillset/village/guard.ron | 24 + assets/common/skillset/village/merchant.ron | 17 + common/base/src/lib.rs | 21 + common/src/comp/inventory/loadout_builder.rs | 30 +- common/src/generation.rs | 72 +- common/src/skillset_builder.rs | 817 +++--------------- common/src/states/basic_summon.rs | 41 +- server/src/sys/terrain.rs | 57 +- world/src/site/dungeon/mod.rs | 13 +- world/src/site/settlement/mod.rs | 32 +- 47 files changed, 585 insertions(+), 840 deletions(-) create mode 100644 assets/common/entity/dungeon/tier-5/boss.ron create mode 100644 assets/common/entity/village/guard.ron create mode 100644 assets/common/entity/village/merchant.ron create mode 100644 assets/common/entity/village/villager.ron create mode 100644 assets/common/skillset/dungeon/tier-0/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-1/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-2/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-3/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-4/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-5/axe.ron create mode 100644 assets/common/skillset/dungeon/tier-5/bow.ron create mode 100644 assets/common/skillset/dungeon/tier-5/enemy.ron create mode 100644 assets/common/skillset/dungeon/tier-5/hammer.ron create mode 100644 assets/common/skillset/dungeon/tier-5/mindflayer.ron create mode 100644 assets/common/skillset/dungeon/tier-5/staff.ron create mode 100644 assets/common/skillset/dungeon/tier-5/sword.ron create mode 100644 assets/common/skillset/village/guard.ron create mode 100644 assets/common/skillset/village/merchant.ron diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron index 0ba5a289d6..e135808266 100644 --- a/assets/common/entity/dungeon/tier-0/bow.ron +++ b/assets/common/entity/dungeon/tier-0/bow.ron @@ -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"), ) diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron index ffc2e9cf4f..72c3b46fa3 100644 --- a/assets/common/entity/dungeon/tier-0/spear.ron +++ b/assets/common/entity/dungeon/tier-0/spear.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron index b0b6a7a751..d8928f39fe 100644 --- a/assets/common/entity/dungeon/tier-0/staff.ron +++ b/assets/common/entity/dungeon/tier-0/staff.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron index fde472fdb5..e7ea1ed0dc 100644 --- a/assets/common/entity/dungeon/tier-1/bow.ron +++ b/assets/common/entity/dungeon/tier-1/bow.ron @@ -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"), ) diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron index 48a07585be..0f2d3322e5 100644 --- a/assets/common/entity/dungeon/tier-1/spear.ron +++ b/assets/common/entity/dungeon/tier-1/spear.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron index 820c12436c..522ec1d415 100644 --- a/assets/common/entity/dungeon/tier-1/staff.ron +++ b/assets/common/entity/dungeon/tier-1/staff.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron index 8ea21fa08c..73950982ee 100644 --- a/assets/common/entity/dungeon/tier-2/bow.ron +++ b/assets/common/entity/dungeon/tier-2/bow.ron @@ -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"), ) diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron index bd8d7c9e4e..c9499cfa47 100644 --- a/assets/common/entity/dungeon/tier-2/spear.ron +++ b/assets/common/entity/dungeon/tier-2/spear.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron index 73cb84ee55..4ecbcd121e 100644 --- a/assets/common/entity/dungeon/tier-2/staff.ron +++ b/assets/common/entity/dungeon/tier-2/staff.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron index d1a9d98924..e642c37333 100644 --- a/assets/common/entity/dungeon/tier-3/bow.ron +++ b/assets/common/entity/dungeon/tier-3/bow.ron @@ -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"), ) diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron index 5e303990af..4c107bdd73 100644 --- a/assets/common/entity/dungeon/tier-3/spear.ron +++ b/assets/common/entity/dungeon/tier-3/spear.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron index ef130792c0..adfe8857e8 100644 --- a/assets/common/entity/dungeon/tier-3/staff.ron +++ b/assets/common/entity/dungeon/tier-3/staff.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron index 7a9006fb5a..04cf0e7b02 100644 --- a/assets/common/entity/dungeon/tier-4/bow.ron +++ b/assets/common/entity/dungeon/tier-4/bow.ron @@ -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"), ) diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron index 3ae610eb7e..d73950e575 100644 --- a/assets/common/entity/dungeon/tier-4/spear.ron +++ b/assets/common/entity/dungeon/tier-4/spear.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron index 23d0521872..a022a4b330 100644 --- a/assets/common/entity/dungeon/tier-4/staff.ron +++ b/assets/common/entity/dungeon/tier-4/staff.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron index c34e9bc99e..4c2421ac72 100644 --- a/assets/common/entity/dungeon/tier-5/beastmaster.ron +++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron @@ -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"), ) diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron new file mode 100644 index 0000000000..042951cee6 --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/boss.ron @@ -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"), +) diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron index 4e8bd6d35b..6c23289a44 100644 --- a/assets/common/entity/dungeon/tier-5/husk.ron +++ b/assets/common/entity/dungeon/tier-5/husk.ron @@ -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, ) diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index f151381032..8e5fe4ae70 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -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"), ) diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index 8369c3f29c..37430fc373 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -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"), ) diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index 040cc162b2..915f98ade2 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -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 (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) diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron new file mode 100644 index 0000000000..93d770cb2c --- /dev/null +++ b/assets/common/entity/village/guard.ron @@ -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"), +) diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron new file mode 100644 index 0000000000..07dd497fdf --- /dev/null +++ b/assets/common/entity/village/merchant.ron @@ -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"), +) diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron new file mode 100644 index 0000000000..45a92d5abb --- /dev/null +++ b/assets/common/entity/village/villager.ron @@ -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, +) diff --git a/assets/common/loadout/world/traveler.ron b/assets/common/loadout/world/traveler.ron index c2126e5496..2a38bafaef 100644 --- a/assets/common/loadout/world/traveler.ron +++ b/assets/common/loadout/world/traveler.ron @@ -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"))), diff --git a/assets/common/skillset/dungeon/tier-0/bow.ron b/assets/common/skillset/dungeon/tier-0/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-0/bow.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-1/bow.ron b/assets/common/skillset/dungeon/tier-1/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-1/bow.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-2/bow.ron b/assets/common/skillset/dungeon/tier-2/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-2/bow.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-3/bow.ron b/assets/common/skillset/dungeon/tier-3/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-3/bow.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-4/bow.ron b/assets/common/skillset/dungeon/tier-4/bow.ron new file mode 100644 index 0000000000..5a99b61b66 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-4/bow.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/axe.ron b/assets/common/skillset/dungeon/tier-5/axe.ron new file mode 100644 index 0000000000..abfcaae590 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/axe.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/bow.ron b/assets/common/skillset/dungeon/tier-5/bow.ron new file mode 100644 index 0000000000..a1cd8b0def --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/bow.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/enemy.ron b/assets/common/skillset/dungeon/tier-5/enemy.ron new file mode 100644 index 0000000000..0e5ff1c8e5 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/enemy.ron @@ -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"), +]) diff --git a/assets/common/skillset/dungeon/tier-5/hammer.ron b/assets/common/skillset/dungeon/tier-5/hammer.ron new file mode 100644 index 0000000000..5f0e7561a3 --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/hammer.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/mindflayer.ron b/assets/common/skillset/dungeon/tier-5/mindflayer.ron new file mode 100644 index 0000000000..09cba3e4bd --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/mindflayer.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/staff.ron b/assets/common/skillset/dungeon/tier-5/staff.ron new file mode 100644 index 0000000000..4c3dc1af3a --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/staff.ron @@ -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))), +]) diff --git a/assets/common/skillset/dungeon/tier-5/sword.ron b/assets/common/skillset/dungeon/tier-5/sword.ron new file mode 100644 index 0000000000..2f945fc42f --- /dev/null +++ b/assets/common/skillset/dungeon/tier-5/sword.ron @@ -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))), +]) diff --git a/assets/common/skillset/village/guard.ron b/assets/common/skillset/village/guard.ron new file mode 100644 index 0000000000..ad041b0f2d --- /dev/null +++ b/assets/common/skillset/village/guard.ron @@ -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))), +]) diff --git a/assets/common/skillset/village/merchant.ron b/assets/common/skillset/village/merchant.ron new file mode 100644 index 0000000000..06355a4ff4 --- /dev/null +++ b/assets/common/skillset/village/merchant.ron @@ -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))), +]) diff --git a/common/base/src/lib.rs b/common/base/src/lib.rs index 3325612858..d05b952127 100644 --- a/common/base/src/lib.rs +++ b/common/base/src/lib.rs @@ -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 { diff --git a/common/src/comp/inventory/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs index a2d7a12a25..eb8fc67148 100644 --- a/common/src/comp/inventory/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -45,6 +45,14 @@ pub enum Preset { HuskSummon, } +#[derive(Debug, Deserialize, Clone)] +pub struct LoadoutSpec(HashMap); +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); -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"); diff --git a/common/src/generation.rs b/common/src/generation.rs index 4cf13c361e..f31aa7628e 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -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, main_tool: Option, second_tool: Option, - loadout_config: Option, + loadout_asset: Option, + skillset_asset: Option, } impl assets::Asset for EntityConfig { @@ -43,10 +43,9 @@ pub struct EntityInfo { // TODO: Properly give NPCs skills pub level: Option, pub loot_drop: Option, - pub loadout_config: Option, + pub loadout_asset: Option, pub make_loadout: Option) -> LoadoutBuilder>, - pub skillset_config: Option, - pub skillset_preset: Option, + pub skillset_asset: Option, pub pet: Option>, // we can't use DHashMap, do we want to move that into common? pub trading_information: Option, @@ -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)); + } } } } diff --git a/common/src/skillset_builder.rs b/common/src/skillset_builder.rs index d61ed16be7..8fddd2b7b1 100644 --- a/common/src/skillset_builder.rs +++ b/common/src/skillset_builder.rs @@ -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); +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)), + Group(SkillGroupKind), +} + +#[must_use] +fn skills_from_asset_expect(asset_specifier: &str) -> Vec<(Skill, Option)> { + let nodes = SkillSetTree::load_expect(asset_specifier).read().0.clone(); + + skills_from_nodes(nodes) +} + +#[must_use] +fn skills_from_nodes(nodes: Vec) -> Vec<(Skill, Option)> { + 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, config: Option) -> 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) -> 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) -> 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) -> b false } } + +#[cfg(test)] +mod tests { + use super::*; + use assets::Error; + + #[test] + fn test_all_skillset_assets() { + #[derive(Clone)] + struct SkillSetList(Vec); + + impl assets::Compound for SkillSetList { + fn load( + cache: &assets::AssetCache, + specifier: &str, + ) -> Result { + let list = cache + .load::(specifier)? + .read() + .iter() + .map(|spec| SkillSetTree::load_cloned(spec)) + .collect::>()?; + + 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 + }); + } + } +} diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 8b69c6a3ab..1c094b8883 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -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, - skillset_config: Option, + skillset_config: Option, } diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index b4decd5bad..458c710b28 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -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); diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index dc06ebc758..2439027220 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -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) -> Vec) -> Vec 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"), } }); From 229b253a1762e5bd2d202e0d110306939e13de01 Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Tue, 8 Jun 2021 21:27:35 +0300 Subject: [PATCH 12/15] remove Mindflayer skillset --- assets/common/entity/dungeon/tier-5/boss.ron | 2 +- .../skillset/dungeon/tier-5/mindflayer.ron | 21 ------------------- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 assets/common/skillset/dungeon/tier-5/mindflayer.ron diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron index 042951cee6..ffa83720a7 100644 --- a/assets/common/entity/dungeon/tier-5/boss.ron +++ b/assets/common/entity/dungeon/tier-5/boss.ron @@ -5,5 +5,5 @@ EntityConfig ( second_tool: None, loadout_asset: None, - skillset_asset: Some("common.skillset.dungeon.tier-5.mindflayer"), + skillset_asset: None, ) diff --git a/assets/common/skillset/dungeon/tier-5/mindflayer.ron b/assets/common/skillset/dungeon/tier-5/mindflayer.ron deleted file mode 100644 index 09cba3e4bd..0000000000 --- a/assets/common/skillset/dungeon/tier-5/mindflayer.ron +++ /dev/null @@ -1,21 +0,0 @@ -([ - 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))), -]) From a4cc1e24ee3b7fbc2d9caa2c5515befb72c2aafc Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Wed, 9 Jun 2021 00:58:15 +0300 Subject: [PATCH 13/15] Move body to EntityConfig assets * currently works only for random and random_with, uses FromStr for NpcKind --- assets/common/entity/dungeon/tier-0/bow.ron | 1 + assets/common/entity/dungeon/tier-0/spear.ron | 1 + assets/common/entity/dungeon/tier-0/staff.ron | 1 + assets/common/entity/dungeon/tier-1/bow.ron | 1 + assets/common/entity/dungeon/tier-1/spear.ron | 1 + assets/common/entity/dungeon/tier-1/staff.ron | 1 + assets/common/entity/dungeon/tier-2/bow.ron | 1 + assets/common/entity/dungeon/tier-2/spear.ron | 1 + assets/common/entity/dungeon/tier-2/staff.ron | 1 + assets/common/entity/dungeon/tier-3/bow.ron | 1 + assets/common/entity/dungeon/tier-3/spear.ron | 1 + assets/common/entity/dungeon/tier-3/staff.ron | 1 + assets/common/entity/dungeon/tier-4/bow.ron | 1 + assets/common/entity/dungeon/tier-4/spear.ron | 1 + assets/common/entity/dungeon/tier-4/staff.ron | 1 + .../entity/dungeon/tier-5/beastmaster.ron | 1 + assets/common/entity/dungeon/tier-5/boss.ron | 1 + assets/common/entity/dungeon/tier-5/husk.ron | 1 + .../common/entity/dungeon/tier-5/warlock.ron | 1 + .../common/entity/dungeon/tier-5/warlord.ron | 1 + assets/common/entity/test.ron | 2 +- assets/common/entity/village/guard.ron | 2 ++ assets/common/entity/village/merchant.ron | 2 ++ assets/common/entity/village/villager.ron | 2 ++ common/src/generation.rs | 35 ++++++++++++++++++- world/src/site/dungeon/mod.rs | 28 --------------- 26 files changed, 61 insertions(+), 30 deletions(-) diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron index e135808266..1c3f4539f2 100644 --- a/assets/common/entity/dungeon/tier-0/bow.ron +++ b/assets/common/entity/dungeon/tier-0/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Gnarling Stalker"), + body: Some(RandomWith("gnarling")), main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron index 72c3b46fa3..05cd042552 100644 --- a/assets/common/entity/dungeon/tier-0/spear.ron +++ b/assets/common/entity/dungeon/tier-0/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Gnarling Mugger"), + body: Some(RandomWith("gnarling")), main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron index d8928f39fe..43b1948197 100644 --- a/assets/common/entity/dungeon/tier-0/staff.ron +++ b/assets/common/entity/dungeon/tier-0/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Gnarling Shaman"), + body: Some(RandomWith("gnarling")), main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron index e7ea1ed0dc..51c93c9929 100644 --- a/assets/common/entity/dungeon/tier-1/bow.ron +++ b/assets/common/entity/dungeon/tier-1/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Adlet Tracker"), + body: Some(RandomWith("adlet")), main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron index 0f2d3322e5..9943b5b3c7 100644 --- a/assets/common/entity/dungeon/tier-1/spear.ron +++ b/assets/common/entity/dungeon/tier-1/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Adlet Hunter"), + body: Some(RandomWith("adlet")), main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron index 522ec1d415..d196d2654f 100644 --- a/assets/common/entity/dungeon/tier-1/staff.ron +++ b/assets/common/entity/dungeon/tier-1/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Adlet Shaman"), + body: Some(RandomWith("adlet")), main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron index 73950982ee..435199764c 100644 --- a/assets/common/entity/dungeon/tier-2/bow.ron +++ b/assets/common/entity/dungeon/tier-2/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Sahagin Sniper"), + body: Some(RandomWith("sahagin")), main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron index c9499cfa47..b7270579ff 100644 --- a/assets/common/entity/dungeon/tier-2/spear.ron +++ b/assets/common/entity/dungeon/tier-2/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Sahagin Spearman"), + body: Some(RandomWith("sahagin")), main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron index 4ecbcd121e..1bf74fc93e 100644 --- a/assets/common/entity/dungeon/tier-2/staff.ron +++ b/assets/common/entity/dungeon/tier-2/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Sahagin Sorcerer"), + body: Some(RandomWith("sahagin")), main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron index e642c37333..a667ca255b 100644 --- a/assets/common/entity/dungeon/tier-3/bow.ron +++ b/assets/common/entity/dungeon/tier-3/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Haniwa Archer"), + body: Some(RandomWith("haniwa")), main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron index 4c107bdd73..9d74bd0ba6 100644 --- a/assets/common/entity/dungeon/tier-3/spear.ron +++ b/assets/common/entity/dungeon/tier-3/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Haniwa Guard"), + body: Some(RandomWith("haniwa")), main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron index adfe8857e8..649c669272 100644 --- a/assets/common/entity/dungeon/tier-3/staff.ron +++ b/assets/common/entity/dungeon/tier-3/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Haniwa Sorcerer"), + body: Some(RandomWith("haniwa")), main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron index 04cf0e7b02..205cca7248 100644 --- a/assets/common/entity/dungeon/tier-4/bow.ron +++ b/assets/common/entity/dungeon/tier-4/bow.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Myrmidon Marksman"), + body: Some(RandomWith("myrmidon")), main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron index d73950e575..c5688f6fe9 100644 --- a/assets/common/entity/dungeon/tier-4/spear.ron +++ b/assets/common/entity/dungeon/tier-4/spear.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Myrmidon Hoplite"), + body: Some(RandomWith("myrmidon")), main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron index a022a4b330..2b4926db59 100644 --- a/assets/common/entity/dungeon/tier-4/staff.ron +++ b/assets/common/entity/dungeon/tier-4/staff.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Myrmidon Wizard"), + body: Some(RandomWith("myrmidon")), main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron index 4c2421ac72..3bf5cac4f2 100644 --- a/assets/common/entity/dungeon/tier-5/beastmaster.ron +++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Beastmaster"), + body: Some(RandomWith("humanoid")), main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.axe.malachite_axe-0"))), diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron index ffa83720a7..9f7c6d376a 100644 --- a/assets/common/entity/dungeon/tier-5/boss.ron +++ b/assets/common/entity/dungeon/tier-5/boss.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Mindflayer"), + body: Some(RandomWith("mindflayer")), main_tool: None, second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron index 6c23289a44..0409d98436 100644 --- a/assets/common/entity/dungeon/tier-5/husk.ron +++ b/assets/common/entity/dungeon/tier-5/husk.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Cultist Husk"), + body: Some(RandomWith("husk")), main_tool: None, second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index 8e5fe4ae70..4553ea5ff9 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Cultist Warlock"), + body: Some(RandomWith("humanoid")), main_tool: Some(Item("common.items.weapons.staff.cultist_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index 37430fc373..53d3605fb0 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -1,5 +1,6 @@ EntityConfig ( name: Some("Cultist Warlord"), + body: Some(RandomWith("humanoid")), main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index 915f98ade2..e967f36b6c 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -6,7 +6,7 @@ EntityConfig ( /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such) /// or Random (will use random if available for this Body) /// or RandomWith (will use random_with if available for this Body) - // body: Humanoid(Random), + body: Some(RandomWith("humanoid")), /// Main and second tools /// Can be Option (with asset_specifier for item) diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron index 93d770cb2c..6332487f7a 100644 --- a/assets/common/entity/village/guard.ron +++ b/assets/common/entity/village/guard.ron @@ -1,5 +1,7 @@ EntityConfig ( name: Some("Guard"), + // body is specified outsite + body: None, main_tool: Some(Item("common.items.weapons.sword.iron-4")), second_tool: None, diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron index 07dd497fdf..c1b39be174 100644 --- a/assets/common/entity/village/merchant.ron +++ b/assets/common/entity/village/merchant.ron @@ -1,5 +1,7 @@ EntityConfig ( name: Some("Merchant"), + // body is specified outsite + body: None, main_tool: Some(Item("common.items.weapons.bow.eldwood-0")), second_tool: None, diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron index 45a92d5abb..bef5f5bbad 100644 --- a/assets/common/entity/village/villager.ron +++ b/assets/common/entity/village/villager.ron @@ -1,5 +1,7 @@ EntityConfig ( name: None, + // body is specified outsite + body: None, main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.tool.broom"))), diff --git a/common/src/generation.rs b/common/src/generation.rs index f31aa7628e..131815bbe2 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -12,9 +12,15 @@ use crate::{ use serde::Deserialize; use vek::*; +#[derive(Debug, Deserialize, Clone)] +enum BodyKind { + RandomWith(String), +} + #[derive(Debug, Deserialize, Clone)] struct EntityConfig { name: Option, + body: Option, main_tool: Option, second_tool: Option, loadout_asset: Option, @@ -86,6 +92,7 @@ impl EntityInfo { fn with_entity_config(mut self, config: EntityConfig, asset_specifier: Option<&str>) -> Self { let EntityConfig { name, + body, main_tool, second_tool, loadout_asset, @@ -96,6 +103,19 @@ impl EntityInfo { self = self.with_name(name); } + if let Some(body) = body { + match body { + BodyKind::RandomWith(string) => { + let npc::NpcBody(_body_kind, mut body_creator) = + string.parse::().unwrap_or_else(|err| { + panic!("failed to parse body {:?}. Err: {:?}", &string, err) + }); + let body = body_creator(); + self = self.with_body(body); + }, + } + } + let rng = &mut rand::thread_rng(); if let Some(main_tool) = main_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng)) @@ -296,7 +316,8 @@ mod tests { second_tool, loadout_asset, skillset_asset, - .. + name: _name, + body, } = config; if let Some(main_tool) = main_tool { @@ -307,6 +328,18 @@ mod tests { second_tool.validate(EquipSlot::ActiveOffhand); } + if let Some(body) = body { + match body { + BodyKind::RandomWith(string) => { + let npc::NpcBody(_body_kind, mut body_creator) = + string.parse::().unwrap_or_else(|err| { + panic!("failed to parse body {:?}. Err: {:?}", &string, err) + }); + std::mem::drop(body_creator()); + }, + } + } + if let Some(loadout_asset) = loadout_asset { let rng = &mut rand::thread_rng(); let builder = LoadoutBuilder::default(); diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 2439027220..7fc3cd0410 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -926,12 +926,6 @@ fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.enemy"); let gnarling = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Gnarling, - ), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -945,9 +939,6 @@ fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-1.enemy"); let adlet = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Adlet), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -961,9 +952,6 @@ fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-2.enemy"); let sahagin = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Sahagin), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -985,12 +973,6 @@ fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { )), _ => { let haniwa = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Haniwa, - ), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -1005,12 +987,6 @@ fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.enemy"); let myrmidon = entity - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Myrmidon, - ), - )) .with_loot_drop(chosen.read().choose().to_item()); match dynamic_rng.gen_range(0..5) { @@ -1031,11 +1007,9 @@ fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { "common.items.crafting_ing.twigs", )), 1 => entity - .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .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_loot_drop(chosen.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.warlord"), } @@ -1308,7 +1282,6 @@ mod tests { boss_fallback(&mut dynamic_rng, tile_wcenter); } - //FIXME: it will miss items with rng branching #[test] fn test_creating_enemies() { let mut dynamic_rng = rand::thread_rng(); @@ -1322,7 +1295,6 @@ mod tests { enemy_fallback(&mut dynamic_rng, raw_entity); } - //FIXME: it will miss items with rng branching #[test] fn test_creating_minibosses() { let mut dynamic_rng = rand::thread_rng(); From 057aa7fecffcf7e17f4f149d7c2b5885514c12af Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Wed, 9 Jun 2021 01:07:48 +0300 Subject: [PATCH 14/15] Move loot tables to entityconfigs * Moved all entities in dungeons to assets --- .../common/entity/dungeon/fallback/boss.ron | 12 + .../common/entity/dungeon/fallback/enemy.ron | 20 ++ .../entity/dungeon/fallback/miniboss.ron | 12 + assets/common/entity/dungeon/tier-0/boss.ron | 12 + assets/common/entity/dungeon/tier-0/bow.ron | 2 + .../common/entity/dungeon/tier-0/miniboss.ron | 12 + assets/common/entity/dungeon/tier-0/spear.ron | 2 + assets/common/entity/dungeon/tier-0/staff.ron | 2 + assets/common/entity/dungeon/tier-1/boss.ron | 12 + assets/common/entity/dungeon/tier-1/bow.ron | 2 + assets/common/entity/dungeon/tier-1/rat.ron | 12 + assets/common/entity/dungeon/tier-1/spear.ron | 2 + assets/common/entity/dungeon/tier-1/staff.ron | 2 + assets/common/entity/dungeon/tier-2/boss.ron | 12 + assets/common/entity/dungeon/tier-2/bow.ron | 2 + .../common/entity/dungeon/tier-2/hakulaq.ron | 12 + assets/common/entity/dungeon/tier-2/spear.ron | 2 + assets/common/entity/dungeon/tier-2/staff.ron | 2 + .../entity/dungeon/tier-3/bonerattler.ron | 12 + assets/common/entity/dungeon/tier-3/boss.ron | 12 + assets/common/entity/dungeon/tier-3/bow.ron | 2 + .../common/entity/dungeon/tier-3/sentry.ron | 12 + assets/common/entity/dungeon/tier-3/spear.ron | 2 + assets/common/entity/dungeon/tier-3/staff.ron | 2 + assets/common/entity/dungeon/tier-4/boss.ron | 12 + assets/common/entity/dungeon/tier-4/bow.ron | 2 + .../common/entity/dungeon/tier-4/miniboss.ron | 12 + assets/common/entity/dungeon/tier-4/spear.ron | 2 + assets/common/entity/dungeon/tier-4/staff.ron | 2 + .../entity/dungeon/tier-5/beastmaster.ron | 4 +- assets/common/entity/dungeon/tier-5/boss.ron | 2 + assets/common/entity/dungeon/tier-5/hound.ron | 12 + assets/common/entity/dungeon/tier-5/husk.ron | 2 + .../common/entity/dungeon/tier-5/turret.ron | 12 + .../common/entity/dungeon/tier-5/warlock.ron | 2 + .../common/entity/dungeon/tier-5/warlord.ron | 2 + assets/common/entity/test.ron | 5 +- assets/common/entity/village/guard.ron | 2 + assets/common/entity/village/merchant.ron | 3 + assets/common/entity/village/villager.ron | 2 + common/src/generation.rs | 38 +- world/src/site/dungeon/mod.rs | 337 +++++------------- 42 files changed, 373 insertions(+), 256 deletions(-) create mode 100644 assets/common/entity/dungeon/fallback/boss.ron create mode 100644 assets/common/entity/dungeon/fallback/enemy.ron create mode 100644 assets/common/entity/dungeon/fallback/miniboss.ron create mode 100644 assets/common/entity/dungeon/tier-0/boss.ron create mode 100644 assets/common/entity/dungeon/tier-0/miniboss.ron create mode 100644 assets/common/entity/dungeon/tier-1/boss.ron create mode 100644 assets/common/entity/dungeon/tier-1/rat.ron create mode 100644 assets/common/entity/dungeon/tier-2/boss.ron create mode 100644 assets/common/entity/dungeon/tier-2/hakulaq.ron create mode 100644 assets/common/entity/dungeon/tier-3/bonerattler.ron create mode 100644 assets/common/entity/dungeon/tier-3/boss.ron create mode 100644 assets/common/entity/dungeon/tier-3/sentry.ron create mode 100644 assets/common/entity/dungeon/tier-4/boss.ron create mode 100644 assets/common/entity/dungeon/tier-4/miniboss.ron create mode 100644 assets/common/entity/dungeon/tier-5/hound.ron create mode 100644 assets/common/entity/dungeon/tier-5/turret.ron diff --git a/assets/common/entity/dungeon/fallback/boss.ron b/assets/common/entity/dungeon/fallback/boss.ron new file mode 100644 index 0000000000..caf7fc2256 --- /dev/null +++ b/assets/common/entity/dungeon/fallback/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Crazy Sheep"), + body: Some(RandomWith("sheep")), + + loot: Some(LootTable("common.loot_tables.fallback")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/fallback/enemy.ron b/assets/common/entity/dungeon/fallback/enemy.ron new file mode 100644 index 0000000000..63e9172578 --- /dev/null +++ b/assets/common/entity/dungeon/fallback/enemy.ron @@ -0,0 +1,20 @@ +EntityConfig ( + name: Some("Yan Hus"), + body: Some(RandomWith("humanoid")), + + loot: Some(LootTable("common.loot_tables.fallback")), + + 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"))), + (1.0, Some(Item("common.items.weapons.bow.bone-1"))), + ])), + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/fallback/miniboss.ron b/assets/common/entity/dungeon/fallback/miniboss.ron new file mode 100644 index 0000000000..6f7ba8171e --- /dev/null +++ b/assets/common/entity/dungeon/fallback/miniboss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Big Goose"), + body: Some(RandomWith("goose")), + + loot: Some(LootTable("common.loot_tables.fallback")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-0/boss.ron b/assets/common/entity/dungeon/tier-0/boss.ron new file mode 100644 index 0000000000..98483efc4d --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Harvester"), + body: Some(RandomWith("harvester")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-0/bow.ron b/assets/common/entity/dungeon/tier-0/bow.ron index 1c3f4539f2..be2948eb55 100644 --- a/assets/common/entity/dungeon/tier-0/bow.ron +++ b/assets/common/entity/dungeon/tier-0/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Gnarling Stalker"), body: Some(RandomWith("gnarling")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-0/miniboss.ron b/assets/common/entity/dungeon/tier-0/miniboss.ron new file mode 100644 index 0000000000..411ef1a7b1 --- /dev/null +++ b/assets/common/entity/dungeon/tier-0/miniboss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Deadwood"), + body: Some(RandomWith("deadwood")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.miniboss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-0/spear.ron b/assets/common/entity/dungeon/tier-0/spear.ron index 05cd042552..e5a4d9e2ad 100644 --- a/assets/common/entity/dungeon/tier-0/spear.ron +++ b/assets/common/entity/dungeon/tier-0/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Gnarling Mugger"), body: Some(RandomWith("gnarling")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-0/staff.ron b/assets/common/entity/dungeon/tier-0/staff.ron index 43b1948197..7202904324 100644 --- a/assets/common/entity/dungeon/tier-0/staff.ron +++ b/assets/common/entity/dungeon/tier-0/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Gnarling Shaman"), body: Some(RandomWith("gnarling")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-0.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/boss.ron b/assets/common/entity/dungeon/tier-1/boss.ron new file mode 100644 index 0000000000..30035d7c3f --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Yeti"), + body: Some(RandomWith("yeti")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-1.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-1/bow.ron b/assets/common/entity/dungeon/tier-1/bow.ron index 51c93c9929..013fd7c04c 100644 --- a/assets/common/entity/dungeon/tier-1/bow.ron +++ b/assets/common/entity/dungeon/tier-1/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Adlet Tracker"), body: Some(RandomWith("adlet")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/rat.ron b/assets/common/entity/dungeon/tier-1/rat.ron new file mode 100644 index 0000000000..565057282d --- /dev/null +++ b/assets/common/entity/dungeon/tier-1/rat.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Rat"), + body: Some(RandomWith("rat")), + + loot: Some(LootTable("common.loot_tables.creature.quad_small.generic")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-1/spear.ron b/assets/common/entity/dungeon/tier-1/spear.ron index 9943b5b3c7..0ce2a1b28c 100644 --- a/assets/common/entity/dungeon/tier-1/spear.ron +++ b/assets/common/entity/dungeon/tier-1/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Adlet Hunter"), body: Some(RandomWith("adlet")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-1/staff.ron b/assets/common/entity/dungeon/tier-1/staff.ron index d196d2654f..d9dc6d2a3e 100644 --- a/assets/common/entity/dungeon/tier-1/staff.ron +++ b/assets/common/entity/dungeon/tier-1/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Adlet Shaman"), body: Some(RandomWith("adlet")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-1.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/boss.ron b/assets/common/entity/dungeon/tier-2/boss.ron new file mode 100644 index 0000000000..4db8d8d5d2 --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Tidal Warrior"), + body: Some(RandomWith("tidalwarrior")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-2.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-2/bow.ron b/assets/common/entity/dungeon/tier-2/bow.ron index 435199764c..abd5927b14 100644 --- a/assets/common/entity/dungeon/tier-2/bow.ron +++ b/assets/common/entity/dungeon/tier-2/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Sahagin Sniper"), body: Some(RandomWith("sahagin")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/hakulaq.ron b/assets/common/entity/dungeon/tier-2/hakulaq.ron new file mode 100644 index 0000000000..8f279e3943 --- /dev/null +++ b/assets/common/entity/dungeon/tier-2/hakulaq.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Hakulaq"), + body: Some(RandomWith("hakulaq")), + + loot: Some(LootTable("common.loot_tables.creature.quad_low.fanged")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-2/spear.ron b/assets/common/entity/dungeon/tier-2/spear.ron index b7270579ff..bce3699599 100644 --- a/assets/common/entity/dungeon/tier-2/spear.ron +++ b/assets/common/entity/dungeon/tier-2/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Sahagin Spearman"), body: Some(RandomWith("sahagin")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-2/staff.ron b/assets/common/entity/dungeon/tier-2/staff.ron index 1bf74fc93e..e41cddb070 100644 --- a/assets/common/entity/dungeon/tier-2/staff.ron +++ b/assets/common/entity/dungeon/tier-2/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Sahagin Sorcerer"), body: Some(RandomWith("sahagin")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-2.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/bonerattler.ron b/assets/common/entity/dungeon/tier-3/bonerattler.ron new file mode 100644 index 0000000000..1fbf9329eb --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/bonerattler.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Bonerattler"), + body: Some(RandomWith("bonerattler")), + + loot: Some(LootTable("common.loot_tables.creature.quad_medium.carapace")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-3/boss.ron b/assets/common/entity/dungeon/tier-3/boss.ron new file mode 100644 index 0000000000..cf6ddc11ed --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Clay Golem"), + body: Some(RandomWith("claygolem")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-3.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-3/bow.ron b/assets/common/entity/dungeon/tier-3/bow.ron index a667ca255b..f693a38d03 100644 --- a/assets/common/entity/dungeon/tier-3/bow.ron +++ b/assets/common/entity/dungeon/tier-3/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Haniwa Archer"), body: Some(RandomWith("haniwa")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/sentry.ron b/assets/common/entity/dungeon/tier-3/sentry.ron new file mode 100644 index 0000000000..724f259ee4 --- /dev/null +++ b/assets/common/entity/dungeon/tier-3/sentry.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Haniwa Sentry"), + body: None, + + loot: Some(Item("common.items.crafting_ing.stones")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-3/spear.ron b/assets/common/entity/dungeon/tier-3/spear.ron index 9d74bd0ba6..376d2d67e6 100644 --- a/assets/common/entity/dungeon/tier-3/spear.ron +++ b/assets/common/entity/dungeon/tier-3/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Haniwa Guard"), body: Some(RandomWith("haniwa")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-3/staff.ron b/assets/common/entity/dungeon/tier-3/staff.ron index 649c669272..f8d051f2b7 100644 --- a/assets/common/entity/dungeon/tier-3/staff.ron +++ b/assets/common/entity/dungeon/tier-3/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Haniwa Sorcerer"), body: Some(RandomWith("haniwa")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-3.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/boss.ron b/assets/common/entity/dungeon/tier-4/boss.ron new file mode 100644 index 0000000000..5f8883c1dd --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/boss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Minotaur"), + body: Some(RandomWith("minotaur")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.boss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-4/bow.ron b/assets/common/entity/dungeon/tier-4/bow.ron index 205cca7248..04ec56dc59 100644 --- a/assets/common/entity/dungeon/tier-4/bow.ron +++ b/assets/common/entity/dungeon/tier-4/bow.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Myrmidon Marksman"), body: Some(RandomWith("myrmidon")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/miniboss.ron b/assets/common/entity/dungeon/tier-4/miniboss.ron new file mode 100644 index 0000000000..46ba728ead --- /dev/null +++ b/assets/common/entity/dungeon/tier-4/miniboss.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Dullahan"), + body: Some(RandomWith("dullahan")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.miniboss")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-4/spear.ron b/assets/common/entity/dungeon/tier-4/spear.ron index c5688f6fe9..dffc6440dd 100644 --- a/assets/common/entity/dungeon/tier-4/spear.ron +++ b/assets/common/entity/dungeon/tier-4/spear.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Myrmidon Hoplite"), body: Some(RandomWith("myrmidon")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-4/staff.ron b/assets/common/entity/dungeon/tier-4/staff.ron index 2b4926db59..8d432875d2 100644 --- a/assets/common/entity/dungeon/tier-4/staff.ron +++ b/assets/common/entity/dungeon/tier-4/staff.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Myrmidon Wizard"), body: Some(RandomWith("myrmidon")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-4.enemy")), + main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/beastmaster.ron b/assets/common/entity/dungeon/tier-5/beastmaster.ron index 3bf5cac4f2..29987a13e2 100644 --- a/assets/common/entity/dungeon/tier-5/beastmaster.ron +++ b/assets/common/entity/dungeon/tier-5/beastmaster.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Beastmaster"), body: Some(RandomWith("humanoid")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.miniboss")), + main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.axe.malachite_axe-0"))), (1.0, Some(Item("common.items.weapons.sword.bloodsteel-1"))), @@ -10,6 +12,6 @@ EntityConfig ( second_tool: None, loadout_asset: Some("common.loadout.dungeon.tier-5.beastmaster"), - // TODO: make skillset for him, I'm just too lazy + // TODO: make own skillset for him? skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"), ) diff --git a/assets/common/entity/dungeon/tier-5/boss.ron b/assets/common/entity/dungeon/tier-5/boss.ron index 9f7c6d376a..9957ec5482 100644 --- a/assets/common/entity/dungeon/tier-5/boss.ron +++ b/assets/common/entity/dungeon/tier-5/boss.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Mindflayer"), body: Some(RandomWith("mindflayer")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.boss")), + main_tool: None, second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/hound.ron b/assets/common/entity/dungeon/tier-5/hound.ron new file mode 100644 index 0000000000..0b78c67746 --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/hound.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Tamed Darkhound"), + body: Some(RandomWith("darkhound")), + + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-5/husk.ron b/assets/common/entity/dungeon/tier-5/husk.ron index 0409d98436..a28e46db0e 100644 --- a/assets/common/entity/dungeon/tier-5/husk.ron +++ b/assets/common/entity/dungeon/tier-5/husk.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Cultist Husk"), body: Some(RandomWith("husk")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.minion")), + main_tool: None, second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/turret.ron b/assets/common/entity/dungeon/tier-5/turret.ron new file mode 100644 index 0000000000..bb84413d6a --- /dev/null +++ b/assets/common/entity/dungeon/tier-5/turret.ron @@ -0,0 +1,12 @@ +EntityConfig ( + name: Some("Possessed Turret"), + body: None, + + loot: Some(Item("common.items.crafting_ing.twigs")), + + main_tool: None, + second_tool: None, + + loadout_asset: None, + skillset_asset: None, +) diff --git a/assets/common/entity/dungeon/tier-5/warlock.ron b/assets/common/entity/dungeon/tier-5/warlock.ron index 4553ea5ff9..0932bd714c 100644 --- a/assets/common/entity/dungeon/tier-5/warlock.ron +++ b/assets/common/entity/dungeon/tier-5/warlock.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Cultist Warlock"), body: Some(RandomWith("humanoid")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), + main_tool: Some(Item("common.items.weapons.staff.cultist_staff")), second_tool: None, diff --git a/assets/common/entity/dungeon/tier-5/warlord.ron b/assets/common/entity/dungeon/tier-5/warlord.ron index 53d3605fb0..b85cb504de 100644 --- a/assets/common/entity/dungeon/tier-5/warlord.ron +++ b/assets/common/entity/dungeon/tier-5/warlord.ron @@ -2,6 +2,8 @@ EntityConfig ( name: Some("Cultist Warlord"), body: Some(RandomWith("humanoid")), + loot: Some(LootTable("common.loot_tables.dungeon.tier-5.enemy")), + main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.axe_1h.orichalcum-0"))), (2.0, Some(Item("common.items.weapons.sword.cultist"))), diff --git a/assets/common/entity/test.ron b/assets/common/entity/test.ron index e967f36b6c..20c8ff8fb6 100644 --- a/assets/common/entity/test.ron +++ b/assets/common/entity/test.ron @@ -4,8 +4,7 @@ EntityConfig ( /// Body /// Can be Exact (Body with all fields e.g BodyType, Species, Hair color and such) - /// or Random (will use random if available for this Body) - /// or RandomWith (will use random_with if available for this Body) + /// or RandomWith (will generate random body or species) body: Some(RandomWith("humanoid")), /// Main and second tools @@ -24,7 +23,7 @@ EntityConfig ( /// Loot /// Can be Item (with asset_specifier for item) /// or LootTable (with asset_specifier for loot table) - // loot: LootTable("common.loot_tables.humanoids"), + loot: Some(LootTable("common.loot_tables.humanoids")), /// Meta Info (level, alignment, agency, etc) // meta: {}, diff --git a/assets/common/entity/village/guard.ron b/assets/common/entity/village/guard.ron index 6332487f7a..a80314380c 100644 --- a/assets/common/entity/village/guard.ron +++ b/assets/common/entity/village/guard.ron @@ -3,6 +3,8 @@ EntityConfig ( // body is specified outsite body: None, + loot: None, + main_tool: Some(Item("common.items.weapons.sword.iron-4")), second_tool: None, diff --git a/assets/common/entity/village/merchant.ron b/assets/common/entity/village/merchant.ron index c1b39be174..7fb1de003d 100644 --- a/assets/common/entity/village/merchant.ron +++ b/assets/common/entity/village/merchant.ron @@ -3,6 +3,9 @@ EntityConfig ( // body is specified outsite body: None, + // considering giving some gold/gems/materials? + loot: None, + main_tool: Some(Item("common.items.weapons.bow.eldwood-0")), second_tool: None, diff --git a/assets/common/entity/village/villager.ron b/assets/common/entity/village/villager.ron index bef5f5bbad..37d6528807 100644 --- a/assets/common/entity/village/villager.ron +++ b/assets/common/entity/village/villager.ron @@ -3,6 +3,8 @@ EntityConfig ( // body is specified outsite body: None, + loot: None, + main_tool: Some(Choice([ (1.0, Some(Item("common.items.weapons.tool.broom"))), (1.0, Some(Item("common.items.weapons.tool.hoe"))), diff --git a/common/src/generation.rs b/common/src/generation.rs index 131815bbe2..4994a93702 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -5,6 +5,7 @@ use crate::{ inventory::loadout_builder::{ItemSpec, LoadoutBuilder}, Alignment, Body, Item, }, + lottery::{LootSpec, Lottery}, npc::{self, NPC_NAMES}, trade, trade::SiteInformation, @@ -17,10 +18,17 @@ enum BodyKind { RandomWith(String), } +#[derive(Debug, Deserialize, Clone)] +enum LootKind { + Item(String), + LootTable(String), +} + #[derive(Debug, Deserialize, Clone)] struct EntityConfig { name: Option, body: Option, + loot: Option, main_tool: Option, second_tool: Option, loadout_asset: Option, @@ -93,6 +101,7 @@ impl EntityInfo { let EntityConfig { name, body, + loot, main_tool, second_tool, loadout_asset, @@ -116,6 +125,19 @@ impl EntityInfo { } } + if let Some(loot) = loot { + match loot { + LootKind::Item(asset) => { + self = self.with_loot_drop(Item::new_from_asset_expect(&asset)); + }, + LootKind::LootTable(asset) => { + let table = Lottery::::load_expect(&asset); + let drop = table.read().choose().to_item(); + self = self.with_loot_drop(drop); + }, + } + } + let rng = &mut rand::thread_rng(); if let Some(main_tool) = main_tool.and_then(|i| i.try_to_item(asset_specifier.unwrap_or("??"), rng)) @@ -318,6 +340,7 @@ mod tests { skillset_asset, name: _name, body, + loot, } = config; if let Some(main_tool) = main_tool { @@ -335,7 +358,20 @@ mod tests { string.parse::().unwrap_or_else(|err| { panic!("failed to parse body {:?}. Err: {:?}", &string, err) }); - std::mem::drop(body_creator()); + let _ = body_creator(); + }, + } + } + + if let Some(loot) = loot { + match loot { + LootKind::Item(asset) => { + std::mem::drop(Item::new_from_asset_expect(&asset)); + }, + LootKind::LootTable(asset) => { + // we need to just load it check if it exists, + // because all loot tables are tested in Lottery module + let _ = Lottery::::load_expect(&asset); }, } } diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 7fc3cd0410..aa6b6bda4a 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -9,11 +9,10 @@ use crate::{ }; use common::{ - assets::{AssetExt, AssetHandle}, + assets::AssetHandle, astar::Astar, comp::{self}, generation::{ChunkSupplement, EntityInfo}, - lottery::{LootSpec, Lottery}, store::{Id, Store}, terrain::{Block, BlockKind, SpriteKind, Structure, StructuresGroup, TerrainChunkSize}, vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol}, @@ -567,7 +566,7 @@ impl Floor { 3 => enemy_3(dynamic_rng, raw_entity), 4 => enemy_4(dynamic_rng, raw_entity), 5 => enemy_5(dynamic_rng, raw_entity), - _ => enemy_fallback(dynamic_rng, raw_entity), + _ => enemy_fallback(raw_entity), }; supplement.add_entity( entity.with_alignment(comp::Alignment::Enemy).with_level( @@ -597,13 +596,13 @@ impl Floor { if tile_pos == boss_spawn_tile && tile_wcenter.xy() == wpos2d { let entities = match room.difficulty { - 0 => boss_0(dynamic_rng, tile_wcenter), - 1 => boss_1(dynamic_rng, tile_wcenter), - 2 => boss_2(dynamic_rng, tile_wcenter), - 3 => boss_3(dynamic_rng, tile_wcenter), - 4 => boss_4(dynamic_rng, tile_wcenter), - 5 => boss_5(dynamic_rng, tile_wcenter), - _ => boss_fallback(dynamic_rng, tile_wcenter), + 0 => boss_0(tile_wcenter), + 1 => boss_1(tile_wcenter), + 2 => boss_2(tile_wcenter), + 3 => boss_3(tile_wcenter), + 4 => boss_4(tile_wcenter), + 5 => boss_5(tile_wcenter), + _ => boss_fallback(tile_wcenter), }; for entity in entities { @@ -640,13 +639,13 @@ impl Floor { if tile_pos == miniboss_spawn_tile && tile_wcenter.xy() == wpos2d { let entities = match room.difficulty { - 0 => mini_boss_0(dynamic_rng, tile_wcenter), - 1 => mini_boss_1(dynamic_rng, tile_wcenter), - 2 => mini_boss_2(dynamic_rng, tile_wcenter), - 3 => mini_boss_3(dynamic_rng, tile_wcenter), - 4 => mini_boss_4(dynamic_rng, tile_wcenter), + 0 => mini_boss_0(tile_wcenter), + 1 => mini_boss_1(tile_wcenter), + 2 => mini_boss_2(tile_wcenter), + 3 => mini_boss_3(tile_wcenter), + 4 => mini_boss_4(tile_wcenter), 5 => mini_boss_5(dynamic_rng, tile_wcenter), - _ => mini_boss_fallback(dynamic_rng, tile_wcenter), + _ => mini_boss_fallback(tile_wcenter), }; for entity in entities { @@ -923,292 +922,150 @@ impl Floor { } fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.enemy"); - - let gnarling = entity - .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"), - 1 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.staff"), - _ => gnarling.with_asset_expect("common.entity.dungeon.tier-0.spear"), + 0 => entity.with_asset_expect("common.entity.dungeon.tier-0.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-0.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-0.spear"), } } fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-1.enemy"); - - let adlet = entity - .with_loot_drop(chosen.read().choose().to_item()); - match dynamic_rng.gen_range(0..5) { - 0 => adlet.with_asset_expect("common.entity.dungeon.tier-1.bow"), - 1 => adlet.with_asset_expect("common.entity.dungeon.tier-1.staff"), - _ => adlet.with_asset_expect("common.entity.dungeon.tier-1.spear"), + 0 => entity.with_asset_expect("common.entity.dungeon.tier-1.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-1.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-1.spear"), } } fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-2.enemy"); - - let sahagin = entity - .with_loot_drop(chosen.read().choose().to_item()); - match dynamic_rng.gen_range(0..5) { - 0 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.bow"), - 1 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.staff"), - _ => sahagin.with_asset_expect("common.entity.dungeon.tier-2.spear"), + 0 => entity.with_asset_expect("common.entity.dungeon.tier-2.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-2.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-2.spear"), } } fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-3.enemy"); - - match dynamic_rng.gen_range(0..4) { + match dynamic_rng.gen_range(0..5) { 0 => entity .with_body(comp::Body::Object(comp::object::Body::HaniwaSentry)) - .with_name("Haniwa Sentry".to_string()) - .with_loot_drop(comp::Item::new_from_asset_expect( - "common.items.crafting_ing.stones", - )), - _ => { - let haniwa = entity - .with_loot_drop(chosen.read().choose().to_item()); - - match dynamic_rng.gen_range(0..5) { - 0 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.bow"), - 1 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.staff"), - _ => haniwa.with_asset_expect("common.entity.dungeon.tier-3.spear"), - } - }, + .with_asset_expect("common.entity.dungeon.tier-3.sentry"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-3.bow"), + 2 => entity.with_asset_expect("common.entity.dungeon.tier-3.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-3.spear"), } } fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.enemy"); - - let myrmidon = entity - .with_loot_drop(chosen.read().choose().to_item()); - match dynamic_rng.gen_range(0..5) { - 0 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.bow"), - 1 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.staff"), - _ => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.spear"), + 0 => entity.with_asset_expect("common.entity.dungeon.tier-4.bow"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-4.staff"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-4.spear"), } } fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-5.enemy"); - match dynamic_rng.gen_range(0..6) { 0 => entity .with_body(comp::Body::Object(comp::object::Body::Crossbow)) - .with_name("Possessed Turret".to_string()) - .with_loot_drop(comp::Item::new_from_asset_expect( - "common.items.crafting_ing.twigs", - )), - 1 => entity - .with_loot_drop(chosen.read().choose().to_item()) - .with_asset_expect("common.entity.dungeon.tier-5.warlock"), - _ => entity - .with_loot_drop(chosen.read().choose().to_item()) - .with_asset_expect("common.entity.dungeon.tier-5.warlord"), + .with_asset_expect("common.entity.dungeon.tier-5.turret"), + 1 => entity.with_asset_expect("common.entity.dungeon.tier-5.warlock"), + _ => entity.with_asset_expect("common.entity.dungeon.tier-5.warlord"), } } -fn enemy_fallback(_dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { - let chosen = Lottery::::load_expect("common.loot_tables.fallback"); - - entity - .with_name("Humanoid") - .with_loot_drop(chosen.read().choose().to_item()) - .with_main_tool(comp::Item::new_from_asset_expect( - "common.items.weapons.bow.bone-1", - )) +fn enemy_fallback(entity: EntityInfo) -> EntityInfo { + entity.with_asset_expect("common.entity.dungeon.fallback.enemy") } -fn boss_0(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.boss"); - +fn boss_0(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Harvester, - ), - )) - .with_name("Harvester".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-0.boss"), ] } -fn boss_1(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-1.boss"); +fn boss_1(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Yeti, - ), - )) - .with_name("Yeti".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-1.boss"), ] } -fn boss_2(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-2.boss"); +fn boss_2(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Tidalwarrior, - ), - )) - .with_name("Tidal Warrior".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-2.boss"), ] } -fn boss_3(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-3.boss"); - +fn boss_3(tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); entities.resize_with(2, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::Golem(comp::golem::Body::random_with( - dynamic_rng, - &comp::golem::Species::ClayGolem, - ))) - .with_name("Clay Golem".to_string()) - .with_loot_drop(chosen.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-3.boss") }); + entities } -fn boss_4(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.boss"); - +fn boss_4(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Minotaur, - ), - )) - .with_name("Minotaur".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-4.boss"), ] } -fn boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-5.boss"); - +fn boss_5(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Mindflayer, - ), - )) - .with_loot_drop(chosen.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.boss"), ] } -fn boss_fallback(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - vec![ - EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_body(comp::Body::QuadrupedSmall( - comp::quadruped_small::Body::random_with( - dynamic_rng, - &comp::quadruped_small::Species::Sheep, - ), - )), - ] -} - -fn mini_boss_0(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-0.miniboss"); +fn boss_fallback(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedLow( - comp::quadruped_low::Body::random_with( - dynamic_rng, - &comp::quadruped_low::Species::Deadwood, - ), - )) - .with_name("Deadwood".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.fallback.boss"), ] } -fn mini_boss_1(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.creature.quad_small.generic"); +fn mini_boss_0(tile_wcenter: Vec3) -> Vec { + vec![ + EntityInfo::at(tile_wcenter.map(|e| e as f32)) + .with_asset_expect("common.entity.dungeon.tier-0.miniboss"), + ] +} + +fn mini_boss_1(tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); entities.resize_with(8, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedSmall( - comp::quadruped_small::Body::random_with( - dynamic_rng, - &comp::quadruped_small::Species::Rat, - ), - )) - .with_name("Rat".to_string()) - .with_loot_drop(chosen.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-1.rat") }); entities } -fn mini_boss_2(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.creature.quad_low.fanged"); +fn mini_boss_2(tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); entities.resize_with(6, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedLow( - comp::quadruped_low::Body::random_with( - dynamic_rng, - &comp::quadruped_low::Species::Hakulaq, - ), - )) - .with_name("Hakulaq".to_string()) - .with_loot_drop(chosen.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-2.hakulaq") }); entities } -fn mini_boss_3(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = - Lottery::::load_expect("common.loot_tables.creature.quad_medium.carapace"); +fn mini_boss_3(tile_wcenter: Vec3) -> Vec { let mut entities = Vec::new(); entities.resize_with(3, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedMedium( - comp::quadruped_medium::Body::random_with( - dynamic_rng, - &comp::quadruped_medium::Species::Bonerattler, - ), - )) - .with_name("Bonerattler".to_string()) - .with_loot_drop(chosen.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-3.bonerattler") }); entities } -fn mini_boss_4(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let chosen = Lottery::::load_expect("common.loot_tables.dungeon.tier-4.miniboss"); +fn mini_boss_4(tile_wcenter: Vec3) -> Vec { vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedLarge( - comp::biped_large::Body::random_with( - dynamic_rng, - &comp::biped_large::Species::Dullahan, - ), - )) - .with_name("Dullahan Guard".to_string()) - .with_loot_drop(chosen.read().choose().to_item()), + .with_asset_expect("common.entity.dungeon.tier-4.miniboss"), ] } @@ -1216,40 +1073,18 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec { - let trainer_loot = - Lottery::::load_expect("common.loot_tables.dungeon.tier-5.miniboss"); - let hound_loot = - Lottery::::load_expect("common.loot_tables.dungeon.tier-5.minion"); entities.push( 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_asset_expect("common.entity.dungeon.tier-5.beastmaster"), ); entities.resize_with(entities.len() + 2, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::QuadrupedMedium( - comp::quadruped_medium::Body::random_with( - dynamic_rng, - &comp::quadruped_medium::Species::Darkhound, - ), - )) - .with_name("Tamed Darkhound".to_string()) - .with_loot_drop(hound_loot.read().choose().to_item()) + .with_asset_expect("common.entity.dungeon.tier-5.hound") }); }, _ => { - let chosen = - Lottery::::load_expect("common.loot_tables.dungeon.tier-5.minion"); entities.resize_with(10, || { EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::BipedSmall( - comp::biped_small::Body::random_with( - dynamic_rng, - &comp::biped_small::Species::Husk, - ), - )) - .with_loot_drop(chosen.read().choose().to_item()) .with_asset_expect("common.entity.dungeon.tier-5.husk") }); }, @@ -1257,11 +1092,10 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3) -> Vec) -> Vec { +fn mini_boss_fallback(tile_wcenter: Vec3) -> Vec { vec![ - EntityInfo::at(tile_wcenter.map(|e| e as f32)).with_body(comp::Body::BirdMedium( - comp::bird_medium::Body::random_with(dynamic_rng, &comp::bird_medium::Species::Goose), - )), + EntityInfo::at(tile_wcenter.map(|e| e as f32)) + .with_asset_expect("common.entity.dungeon.fallback.miniboss"), ] } @@ -1271,18 +1105,18 @@ mod tests { #[test] fn test_creating_bosses() { - let mut dynamic_rng = rand::thread_rng(); let tile_wcenter = Vec3::new(0, 0, 0); - boss_0(&mut dynamic_rng, tile_wcenter); - boss_1(&mut dynamic_rng, tile_wcenter); - boss_2(&mut dynamic_rng, tile_wcenter); - boss_3(&mut dynamic_rng, tile_wcenter); - boss_4(&mut dynamic_rng, tile_wcenter); - boss_5(&mut dynamic_rng, tile_wcenter); - boss_fallback(&mut dynamic_rng, tile_wcenter); + boss_0(tile_wcenter); + boss_1(tile_wcenter); + boss_2(tile_wcenter); + boss_3(tile_wcenter); + boss_4(tile_wcenter); + boss_5(tile_wcenter); + boss_fallback(tile_wcenter); } #[test] + // FIXME: Uses random, test may be not great fn test_creating_enemies() { let mut dynamic_rng = rand::thread_rng(); let raw_entity = EntityInfo::at(Vec3::new(0.0, 0.0, 0.0)); @@ -1292,19 +1126,20 @@ mod tests { enemy_3(&mut dynamic_rng, raw_entity.clone()); enemy_4(&mut dynamic_rng, raw_entity.clone()); enemy_5(&mut dynamic_rng, raw_entity.clone()); - enemy_fallback(&mut dynamic_rng, raw_entity); + enemy_fallback(raw_entity); } #[test] + // FIXME: Uses random, test may be not great fn test_creating_minibosses() { let mut dynamic_rng = rand::thread_rng(); let tile_wcenter = Vec3::new(0, 0, 0); - mini_boss_0(&mut dynamic_rng, tile_wcenter); - mini_boss_1(&mut dynamic_rng, tile_wcenter); - mini_boss_2(&mut dynamic_rng, tile_wcenter); - mini_boss_3(&mut dynamic_rng, tile_wcenter); - mini_boss_4(&mut dynamic_rng, tile_wcenter); + mini_boss_0(tile_wcenter); + mini_boss_1(tile_wcenter); + mini_boss_2(tile_wcenter); + mini_boss_3(tile_wcenter); + mini_boss_4(tile_wcenter); mini_boss_5(&mut dynamic_rng, tile_wcenter); - mini_boss_fallback(&mut dynamic_rng, tile_wcenter); + mini_boss_fallback(tile_wcenter); } } From d5cbe27612a08312d72fcf9e297ecffba07b095c Mon Sep 17 00:00:00 2001 From: juliancoffee Date: Mon, 14 Jun 2021 13:55:25 +0300 Subject: [PATCH 15/15] Responding to review - make `skillset_builder::Preset` void enum and left comment about how to extend it - add `.with_default_equipment()` in case if preset is missing to `basic_summon` loadout creation to match old `build_loadout()` behaviour --- common/src/skillset_builder.rs | 14 +++++--------- common/src/states/basic_summon.rs | 3 ++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/common/src/skillset_builder.rs b/common/src/skillset_builder.rs index 8fddd2b7b1..4f5726b1d2 100644 --- a/common/src/skillset_builder.rs +++ b/common/src/skillset_builder.rs @@ -6,10 +6,11 @@ use crate::assets::{self, AssetExt}; use serde::{Deserialize, Serialize}; use tracing::warn; +/// `SkillSetBuilder` preset. Consider using loading from assets, when possible. +/// When you're adding new enum variant, +/// handle it in [`with_preset`](SkillSetBuilder::with_preset) method #[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)] -pub enum Preset { - Empty, -} +pub enum Preset {} #[derive(Debug, Deserialize, Clone)] struct SkillSetTree(Vec); @@ -89,12 +90,7 @@ impl SkillSetBuilder { /// Applies preset #[must_use] - pub const fn with_preset(self, preset: Preset) -> Self { - match preset { - Preset::Empty => {}, - } - self - } + pub const fn with_preset(self, _preset: Preset) -> Self { self } #[must_use] /// # Panics diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 1c094b8883..6a49e33c60 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -84,10 +84,11 @@ impl CharacterBehavior for Data { let loadout = { let loadout_builder = LoadoutBuilder::new().with_default_maintool(&body); + // If preset is none, use default equipment if let Some(preset) = loadout_config { loadout_builder.with_preset(preset).build() } else { - loadout_builder.build() + loadout_builder.with_default_equipment(&body).build() } };