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
This commit is contained in:
juliancoffee 2021-06-06 18:55:04 +03:00
parent 5f3eaddb70
commit f5bf991eb0
21 changed files with 292 additions and 98 deletions

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -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"),
)

View File

@ -1,4 +1,4 @@
{ EntityConfig (
/// Name of Entity /// Name of Entity
name: Some("Paddy"), name: Some("Paddy"),
@ -8,11 +8,6 @@
/// or RandomWith (will use random_with 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)
/// or LootTable (with asset_specifier for loot table)
loot: LootTable("common.loot_tables.humanoids"),
/// Main and second tools /// Main and second tools
/// Can be Option<Item> (with asset_specifier for item) /// Can be Option<Item> (with asset_specifier for item)
/// or Choice /// or Choice
@ -20,12 +15,17 @@
main_tool: Some(Item("common.items.weapons.axe_1h.orichalcum-0")), main_tool: Some(Item("common.items.weapons.axe_1h.orichalcum-0")),
second_tool: None, second_tool: None,
/// Loadout Config as Option<Loadout> (with asset_specifier for loadout) /// Loadout Config (with asset_specifier for loadout)
loadout_config: Some(Loadout("common.loadout.village.merchant")), loadout_config: Some("common.loadout.village.merchant"),
/// Skillset Config as Option<SkillSet> (with asset_specifier for skillset) /// Skillset Config as Option<SkillSet> (with asset_specifier for skillset)
// skillset_config: None, // 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 Info (level, alignment, agency, etc)
// meta: {}, // meta: {},
} )

View File

@ -46,7 +46,7 @@ pub enum LoadoutPreset {
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
enum ItemSpec { pub enum ItemSpec {
/// One specific item. /// One specific item.
/// Example: /// Example:
/// Item("common.items.armor.steel.foot") /// Item("common.items.armor.steel.foot")
@ -61,7 +61,7 @@ enum ItemSpec {
} }
impl ItemSpec { impl ItemSpec {
fn try_to_item(&self, asset_specifier: &str, rng: &mut impl Rng) -> Option<Item> { pub fn try_to_item(&self, asset_specifier: &str, rng: &mut impl Rng) -> Option<Item> {
match self { match self {
ItemSpec::Item(specifier) => Some(Item::new_from_asset_expect(&specifier)), ItemSpec::Item(specifier) => Some(Item::new_from_asset_expect(&specifier)),
@ -80,7 +80,7 @@ impl ItemSpec {
#[cfg(test)] #[cfg(test)]
// Read everything and checks if it's loading // Read everything and checks if it's loading
fn validate(&self, key: EquipSlot) { pub fn validate(&self, key: EquipSlot) {
match self { match self {
ItemSpec::Item(specifier) => std::mem::drop(Item::new_from_asset_expect(&specifier)), ItemSpec::Item(specifier) => std::mem::drop(Item::new_from_asset_expect(&specifier)),
ItemSpec::Choice(items) => { ItemSpec::Choice(items) => {

View File

@ -1,7 +1,8 @@
use crate::{ use crate::{
assets::{self, AssetExt},
comp::{ comp::{
self, agent, humanoid, self, agent, humanoid,
inventory::loadout_builder::{LoadoutBuilder, LoadoutPreset}, inventory::loadout_builder::{ItemSpec, LoadoutBuilder, LoadoutPreset},
Alignment, Body, Item, Alignment, Body, Item,
}, },
npc::{self, NPC_NAMES}, npc::{self, NPC_NAMES},
@ -9,10 +10,21 @@ use crate::{
trade, trade,
trade::SiteInformation, trade::SiteInformation,
}; };
use serde::Deserialize;
use vek::*; use vek::*;
pub enum EntityTemplate { #[derive(Debug, Deserialize, Clone)]
Traveller, struct EntityConfig {
name: Option<String>,
main_tool: Option<ItemSpec>,
second_tool: Option<ItemSpec>,
loadout_config: Option<String>,
}
impl assets::Asset for EntityConfig {
type Loader = assets::RonLoader;
const EXTENSION: &'static str = "ron";
} }
#[derive(Clone)] #[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 { pub fn do_if(mut self, cond: bool, f: impl FnOnce(Self) -> Self) -> Self {
if cond { if cond {
self = f(self); self = f(self);
@ -224,3 +274,39 @@ pub fn get_npc_name<
) -> &'a str { ) -> &'a str {
&body_data.species[&species].generic &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<EntityConfig>);
impl assets::Compound for EntityList {
fn load<S: assets::source::Source>(
cache: &assets::AssetCache<S>,
specifier: &str,
) -> Result<Self, Error> {
let list = cache
.load::<assets::Directory>(specifier)?
.read()
.iter()
.map(|spec| EntityConfig::load_cloned(spec))
.collect::<Result<_, Error>>()?;
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));
}
}
}

View File

@ -925,24 +925,21 @@ impl Floor {
fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
let chosen = Lottery::<LootSpec>::load_expect("common.loot_tables.dungeon.tier-0.enemy"); let chosen = Lottery::<LootSpec>::load_expect("common.loot_tables.dungeon.tier-0.enemy");
entity let gnarling = entity
.with_body(comp::Body::BipedSmall( .with_body(comp::Body::BipedSmall(
comp::biped_small::Body::random_with( comp::biped_small::Body::random_with(
dynamic_rng, dynamic_rng,
&comp::biped_small::Species::Gnarling, &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_loot_drop(chosen.read().choose().to_item())
.with_main_tool(comp::Item::new_from_asset_expect( .with_skillset_preset(common::skillset_builder::SkillSetConfig::Gnarling);
match dynamic_rng.gen_range(0..5) {
0 => "common.items.npc_weapons.biped_small.gnarling.adlet_bow", match dynamic_rng.gen_range(0..5) {
1 => "common.items.npc_weapons.biped_small.gnarling.gnoll_staff", 0 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.bow"),
_ => "common.items.npc_weapons.biped_small.gnarling.wooden_spear", 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 { 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( .with_body(comp::Body::BipedSmall(
comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Adlet), 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_skillset_preset(common::skillset_builder::SkillSetConfig::Adlet)
.with_loot_drop(chosen.read().choose().to_item()); .with_loot_drop(chosen.read().choose().to_item());
match dynamic_rng.gen_range(0..5) { match dynamic_rng.gen_range(0..5) {
0 => adlet 0 => adlet.with_asset_expect("common.entity.dungeon.tier-1.bow"),
.with_main_tool(comp::Item::new_from_asset_expect( 1 => adlet.with_asset_expect("common.entity.dungeon.tier-1.staff"),
"common.items.npc_weapons.biped_small.adlet.adlet_bow", _ => adlet.with_asset_expect("common.entity.dungeon.tier-1.spear"),
))
.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 { fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
let chosen = Lottery::<LootSpec>::load_expect("common.loot_tables.dungeon.tier-2.enemy"); let chosen = Lottery::<LootSpec>::load_expect("common.loot_tables.dungeon.tier-2.enemy");
entity let sahagin = entity
.with_body(comp::Body::BipedSmall( .with_body(comp::Body::BipedSmall(
comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Sahagin), 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_skillset_preset(common::skillset_builder::SkillSetConfig::Sahagin)
.with_loot_drop(chosen.read().choose().to_item()) .with_loot_drop(chosen.read().choose().to_item());
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0..5) { match dynamic_rng.gen_range(0..5) {
0 => "common.items.npc_weapons.biped_small.sahagin.adlet_bow", 0 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.bow"),
1 => "common.items.npc_weapons.biped_small.sahagin.gnoll_staff", 1 => sahagin.with_asset_expect("common.entity.dungeon.tier-2.staff"),
_ => "common.items.npc_weapons.biped_small.sahagin.wooden_spear", _ => sahagin.with_asset_expect("common.entity.dungeon.tier-2.spear"),
}, }
))
} }
fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
let chosen = Lottery::<LootSpec>::load_expect("common.loot_tables.dungeon.tier-3.enemy"); let chosen = Lottery::<LootSpec>::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( .with_loot_drop(comp::Item::new_from_asset_expect(
"common.items.crafting_ing.stones", "common.items.crafting_ing.stones",
)), )),
_ => entity _ => {
.with_body(comp::Body::BipedSmall( let haniwa = entity
comp::biped_small::Body::random_with( .with_body(comp::Body::BipedSmall(
dynamic_rng, comp::biped_small::Body::random_with(
&comp::biped_small::Species::Haniwa, 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_skillset_preset(common::skillset_builder::SkillSetConfig::Haniwa) .with_loot_drop(chosen.read().choose().to_item());
.with_loot_drop(chosen.read().choose().to_item())
.with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0..5) {
match dynamic_rng.gen_range(0..5) { 0 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.bow"),
0 => "common.items.npc_weapons.biped_small.haniwa.adlet_bow", 1 => haniwa.with_asset_expect("common.entity.dungeon.tier-3.staff"),
1 => "common.items.npc_weapons.biped_small.haniwa.gnoll_staff", _ => haniwa.with_asset_expect("common.entity.dungeon.tier-3.spear"),
_ => "common.items.npc_weapons.biped_small.haniwa.wooden_spear", }
}, },
)),
} }
} }
fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
let chosen = Lottery::<LootSpec>::load_expect("common.loot_tables.dungeon.tier-4.enemy"); let chosen = Lottery::<LootSpec>::load_expect("common.loot_tables.dungeon.tier-4.enemy");
entity let myrmidon = entity
.with_body(comp::Body::BipedSmall( .with_body(comp::Body::BipedSmall(
comp::biped_small::Body::random_with( comp::biped_small::Body::random_with(
dynamic_rng, dynamic_rng,
&comp::biped_small::Species::Myrmidon, &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_skillset_preset(common::skillset_builder::SkillSetConfig::Myrmidon)
.with_loot_drop(chosen.read().choose().to_item()) .with_loot_drop(chosen.read().choose().to_item());
.with_main_tool(comp::Item::new_from_asset_expect(
match dynamic_rng.gen_range(0..5) { match dynamic_rng.gen_range(0..5) {
0 => "common.items.npc_weapons.biped_small.myrmidon.adlet_bow", 0 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.bow"),
1 => "common.items.npc_weapons.biped_small.myrmidon.gnoll_staff", 1 => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.staff"),
_ => "common.items.npc_weapons.biped_small.myrmidon.wooden_spear", _ => myrmidon.with_asset_expect("common.entity.dungeon.tier-4.spear"),
}, }
))
} }
fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo { fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
let chosen = Lottery::<LootSpec>::load_expect("common.loot_tables.dungeon.tier-5.enemy"); let chosen = Lottery::<LootSpec>::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 1 => entity
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .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_skillset_preset(common::skillset_builder::SkillSetConfig::Warlock)
.with_loot_drop(chosen.read().choose().to_item()) .with_loot_drop(chosen.read().choose().to_item())
.with_main_tool(comp::Item::new_from_asset_expect( .with_asset_expect("common.entity.dungeon.tier-5.warlock"),
"common.items.weapons.staff.cultist_staff",
)),
_ => entity _ => entity
.with_name("Cultist Warlord") .with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
.with_loadout_config("common.loadout.dungeon.tier-5.warlord")
.with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlord) .with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlord)
.with_loot_drop(chosen.read().choose().to_item()) .with_loot_drop(chosen.read().choose().to_item())
.with_main_tool(comp::Item::new_from_asset_expect( .with_asset_expect("common.entity.dungeon.tier-5.warlord"),
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",
},
)),
} }
} }