mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Implement migration for EntityConfig
This commit is contained in:
parent
15431e7f7a
commit
a4908cf5ae
common/src
@ -5,7 +5,14 @@ use std::{
|
|||||||
io::Write,
|
io::Write,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use veloren_common::comp::inventory::slot::{ArmorSlot, EquipSlot};
|
use veloren_common::{
|
||||||
|
comp::{
|
||||||
|
agent::Alignment,
|
||||||
|
inventory::slot::{ArmorSlot, EquipSlot},
|
||||||
|
Body,
|
||||||
|
},
|
||||||
|
lottery::LootSpec,
|
||||||
|
};
|
||||||
|
|
||||||
/// Old version.
|
/// Old version.
|
||||||
mod loadout_v1 {
|
mod loadout_v1 {
|
||||||
@ -40,7 +47,7 @@ mod loadout_v2 {
|
|||||||
type Weight = u8;
|
type Weight = u8;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
enum Base {
|
pub enum Base {
|
||||||
Asset(String),
|
Asset(String),
|
||||||
/// NOTE: If you have the same item in multiple configs,
|
/// NOTE: If you have the same item in multiple configs,
|
||||||
/// first one will have the priority
|
/// first one will have the priority
|
||||||
@ -68,49 +75,49 @@ mod loadout_v2 {
|
|||||||
pub struct LoadoutSpecNew {
|
pub struct LoadoutSpecNew {
|
||||||
// Meta fields
|
// Meta fields
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
inherit: Option<Base>,
|
pub inherit: Option<Base>,
|
||||||
// Armor
|
// Armor
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
head: Option<ItemSpecNew>,
|
pub head: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
neck: Option<ItemSpecNew>,
|
pub neck: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
shoulders: Option<ItemSpecNew>,
|
pub shoulders: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
chest: Option<ItemSpecNew>,
|
pub chest: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
gloves: Option<ItemSpecNew>,
|
pub gloves: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
ring1: Option<ItemSpecNew>,
|
pub ring1: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
ring2: Option<ItemSpecNew>,
|
pub ring2: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
back: Option<ItemSpecNew>,
|
pub back: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
belt: Option<ItemSpecNew>,
|
pub belt: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
legs: Option<ItemSpecNew>,
|
pub legs: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
feet: Option<ItemSpecNew>,
|
pub feet: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
tabard: Option<ItemSpecNew>,
|
pub tabard: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
bag1: Option<ItemSpecNew>,
|
pub bag1: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
bag2: Option<ItemSpecNew>,
|
pub bag2: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
bag3: Option<ItemSpecNew>,
|
pub bag3: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
bag4: Option<ItemSpecNew>,
|
pub bag4: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
lantern: Option<ItemSpecNew>,
|
pub lantern: Option<ItemSpecNew>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
glider: Option<ItemSpecNew>,
|
pub glider: Option<ItemSpecNew>,
|
||||||
// Weapons
|
// Weapons
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
active_hands: Option<Hands>,
|
pub active_hands: Option<Hands>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
inactive_hands: Option<Hands>,
|
pub inactive_hands: Option<Hands>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(Option<ItemSpec>, Option<ItemSpec>)> for Hands {
|
impl From<(Option<ItemSpec>, Option<ItemSpec>)> for Hands {
|
||||||
@ -188,25 +195,214 @@ mod loadout_v2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod v1 {
|
mod entity_v1 {
|
||||||
use super::*;
|
use super::*;
|
||||||
pub type Config = EntityConfig;
|
pub type Config = EntityConfig;
|
||||||
|
type Weight = u8;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
|
pub enum NameKind {
|
||||||
|
Name(String),
|
||||||
|
Automatic,
|
||||||
|
Uninit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
|
pub enum BodyBuilder {
|
||||||
|
RandomWith(String),
|
||||||
|
Exact(Body),
|
||||||
|
Uninit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub enum AlignmentMark {
|
||||||
|
Alignment(Alignment),
|
||||||
|
Uninit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub enum Meta {
|
||||||
|
SkillSetAsset(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub enum Hands {
|
||||||
|
TwoHanded(super::loadout_v1::ItemSpec),
|
||||||
|
Paired(super::loadout_v1::ItemSpec),
|
||||||
|
Mix {
|
||||||
|
mainhand: super::loadout_v1::ItemSpec,
|
||||||
|
offhand: super::loadout_v1::ItemSpec,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub enum LoadoutAsset {
|
||||||
|
Loadout(String),
|
||||||
|
Choice(Vec<(Weight, String)>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub enum LoadoutKind {
|
||||||
|
FromBody,
|
||||||
|
Asset(LoadoutAsset),
|
||||||
|
Hands(Hands),
|
||||||
|
Extended {
|
||||||
|
hands: Hands,
|
||||||
|
base_asset: LoadoutAsset,
|
||||||
|
inventory: Vec<(u32, String)>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct EntityConfig;
|
pub struct EntityConfig {
|
||||||
|
pub name: NameKind,
|
||||||
|
pub body: BodyBuilder,
|
||||||
|
pub alignment: AlignmentMark,
|
||||||
|
pub loot: LootSpec<String>,
|
||||||
|
pub loadout: LoadoutKind,
|
||||||
|
#[serde(default)]
|
||||||
|
pub meta: Vec<Meta>,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod v2 {
|
mod entity_v2 {
|
||||||
use super::*;
|
use super::{
|
||||||
pub type OldConfig = super::v1::Config;
|
entity_v1::{Hands as OldHands, LoadoutAsset, LoadoutKind},
|
||||||
|
loadout_v1::ItemSpec,
|
||||||
|
loadout_v2::{Base, Hands, ItemSpecNew, LoadoutSpecNew},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
pub type OldConfig = super::entity_v1::Config;
|
||||||
pub type Config = EntityConfig;
|
pub type Config = EntityConfig;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct EntityConfig;
|
pub enum LoadoutKindNew {
|
||||||
|
FromBody,
|
||||||
|
Asset(String),
|
||||||
|
Inline(super::loadout_v2::LoadoutSpecNew),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct InventorySpec {
|
||||||
|
loadout: LoadoutKindNew,
|
||||||
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
|
items: Vec<(u32, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct EntityConfig {
|
||||||
|
pub name: super::entity_v1::NameKind,
|
||||||
|
pub body: super::entity_v1::BodyBuilder,
|
||||||
|
pub alignment: super::entity_v1::AlignmentMark,
|
||||||
|
pub loot: LootSpec<String>,
|
||||||
|
pub inventory: InventorySpec,
|
||||||
|
#[serde(default)]
|
||||||
|
pub meta: Vec<super::entity_v1::Meta>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LoadoutAsset> for LoadoutKindNew {
|
||||||
|
fn from(old: LoadoutAsset) -> Self {
|
||||||
|
match old {
|
||||||
|
LoadoutAsset::Loadout(s) => LoadoutKindNew::Asset(s),
|
||||||
|
LoadoutAsset::Choice(bases) => LoadoutKindNew::Inline(LoadoutSpecNew {
|
||||||
|
inherit: Some(Base::Choice(
|
||||||
|
bases
|
||||||
|
.iter()
|
||||||
|
.map(|(w, s)| (*w, Base::Asset(s.to_owned())))
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OldHands> for Hands {
|
||||||
|
fn from(old: OldHands) -> Self {
|
||||||
|
match old {
|
||||||
|
OldHands::TwoHanded(spec) => Hands::InHands((Some(spec.into()), None)),
|
||||||
|
OldHands::Mix { mainhand, offhand } => {
|
||||||
|
Hands::InHands((Some(mainhand.into()), Some(offhand.into())))
|
||||||
|
},
|
||||||
|
OldHands::Paired(spec) => match spec {
|
||||||
|
ItemSpec::Item(name) => Hands::InHands((
|
||||||
|
Some(ItemSpecNew::Item(name.clone())),
|
||||||
|
Some(ItemSpecNew::Item(name)),
|
||||||
|
)),
|
||||||
|
ItemSpec::Choice(choices) => {
|
||||||
|
let smallest = choices
|
||||||
|
.iter()
|
||||||
|
.map(|(w, _)| *w)
|
||||||
|
.min_by(|x, y| x.partial_cmp(y).expect("floats are evil"))
|
||||||
|
.expect("choice shouldn't empty");
|
||||||
|
// Very imprecise algo, but it works
|
||||||
|
let new_choices = choices
|
||||||
|
.into_iter()
|
||||||
|
.map(|(w, i)| {
|
||||||
|
let new_weight = (w / smallest) as u8;
|
||||||
|
let choice =
|
||||||
|
Hands::InHands((i.clone().map(Into::into), i.map(Into::into)));
|
||||||
|
|
||||||
|
(new_weight, choice)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Hands::Choice(new_choices)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InventorySpec {
|
||||||
|
fn with_hands(
|
||||||
|
hands: OldHands,
|
||||||
|
loadout: Option<LoadoutAsset>,
|
||||||
|
items: Vec<(u32, String)>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
loadout: LoadoutKindNew::Inline(LoadoutSpecNew {
|
||||||
|
inherit: loadout.map(|asset| match asset {
|
||||||
|
LoadoutAsset::Loadout(s) => Base::Asset(s.to_owned()),
|
||||||
|
LoadoutAsset::Choice(bases) => Base::Choice(
|
||||||
|
bases
|
||||||
|
.iter()
|
||||||
|
.map(|(w, s)| (*w, Base::Asset(s.to_owned())))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
active_hands: Some(hands.into()),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
items,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<OldConfig> for Config {
|
impl From<OldConfig> for Config {
|
||||||
fn from(old: OldConfig) -> Self {
|
fn from(old: OldConfig) -> Self {
|
||||||
Self::default()
|
let just_loadout = |loadout| InventorySpec {
|
||||||
|
loadout,
|
||||||
|
items: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
name: old.name,
|
||||||
|
body: old.body,
|
||||||
|
alignment: old.alignment,
|
||||||
|
loot: old.loot,
|
||||||
|
inventory: match old.loadout {
|
||||||
|
LoadoutKind::FromBody => just_loadout(LoadoutKindNew::FromBody),
|
||||||
|
LoadoutKind::Asset(asset) => just_loadout(asset.into()),
|
||||||
|
LoadoutKind::Hands(hands) => InventorySpec::with_hands(hands, None, Vec::new()),
|
||||||
|
LoadoutKind::Extended {
|
||||||
|
hands,
|
||||||
|
base_asset,
|
||||||
|
inventory,
|
||||||
|
} => InventorySpec::with_hands(hands, Some(base_asset), inventory),
|
||||||
|
},
|
||||||
|
meta: old.meta,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,10 +475,17 @@ where
|
|||||||
.extensions(ron::extensions::Extensions::IMPLICIT_SOME);
|
.extensions(ron::extensions::Extensions::IMPLICIT_SOME);
|
||||||
let config_string =
|
let config_string =
|
||||||
ron::ser::to_string_pretty(&new, pretty_config).expect("serialize shouldn't fail");
|
ron::ser::to_string_pretty(&new, pretty_config).expect("serialize shouldn't fail");
|
||||||
let comments_string = comments.join("\n");
|
let comments_string = if comments.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
let mut comments = comments.join("\n");
|
||||||
|
// insert newline for other config content
|
||||||
|
comments.push_str("\n");
|
||||||
|
comments
|
||||||
|
};
|
||||||
|
|
||||||
let mut target = fs::File::create(to.join(&path))?;
|
let mut target = fs::File::create(to.join(&path))?;
|
||||||
write!(&mut target, "{comments_string}\n{config_string}")
|
write!(&mut target, "{comments_string}{config_string}")
|
||||||
.expect("fail to write to the file");
|
.expect("fail to write to the file");
|
||||||
println!("{path:?} done");
|
println!("{path:?} done");
|
||||||
},
|
},
|
||||||
@ -296,7 +499,12 @@ fn convert_loop(from: &str, to: &str) {
|
|||||||
path: Path::new("").to_owned(),
|
path: Path::new("").to_owned(),
|
||||||
content: walk_tree(root, root).unwrap(),
|
content: walk_tree(root, root).unwrap(),
|
||||||
};
|
};
|
||||||
walk_with_migrate::<v1::Config, v2::Config>(files, Path::new(from), Path::new(to)).unwrap();
|
walk_with_migrate::<entity_v1::Config, entity_v2::Config>(
|
||||||
|
files,
|
||||||
|
Path::new(from),
|
||||||
|
Path::new(to),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_string(prompt: &str) -> String { input_validated_string(prompt, &|_| true) }
|
fn input_string(prompt: &str) -> String { input_validated_string(prompt, &|_| true) }
|
||||||
|
@ -82,7 +82,7 @@ impl Base {
|
|||||||
enum Hands {
|
enum Hands {
|
||||||
/// Allows to specify one pair
|
/// Allows to specify one pair
|
||||||
// TODO: add link to tests with example
|
// TODO: add link to tests with example
|
||||||
InHands((Option<ItemSpecNew>, Option<ItemSpecNew>)),
|
InHands((Option<ItemSpec>, Option<ItemSpec>)),
|
||||||
/// Allows specify range of choices
|
/// Allows specify range of choices
|
||||||
// TODO: add link to tests with example
|
// TODO: add link to tests with example
|
||||||
Choice(Vec<(Weight, Hands)>),
|
Choice(Vec<(Weight, Hands)>),
|
||||||
@ -95,7 +95,7 @@ impl Hands {
|
|||||||
) -> Result<(Option<Item>, Option<Item>), LoadoutBuilderError> {
|
) -> Result<(Option<Item>, Option<Item>), LoadoutBuilderError> {
|
||||||
match self {
|
match self {
|
||||||
Hands::InHands((mainhand, offhand)) => {
|
Hands::InHands((mainhand, offhand)) => {
|
||||||
let mut from_spec = |i: &ItemSpecNew| i.try_to_item(rng);
|
let mut from_spec = |i: &ItemSpec| i.try_to_item(rng);
|
||||||
|
|
||||||
let mainhand = mainhand
|
let mainhand = mainhand
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -121,20 +121,20 @@ impl Hands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
enum ItemSpecNew {
|
enum ItemSpec {
|
||||||
Item(String),
|
Item(String),
|
||||||
Choice(Vec<(Weight, Option<ItemSpecNew>)>),
|
Choice(Vec<(Weight, Option<ItemSpec>)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ItemSpecNew {
|
impl ItemSpec {
|
||||||
fn try_to_item(&self, rng: &mut impl Rng) -> Result<Option<Item>, LoadoutBuilderError> {
|
fn try_to_item(&self, rng: &mut impl Rng) -> Result<Option<Item>, LoadoutBuilderError> {
|
||||||
match self {
|
match self {
|
||||||
ItemSpecNew::Item(item_asset) => {
|
ItemSpec::Item(item_asset) => {
|
||||||
let item = Item::new_from_asset(item_asset)
|
let item = Item::new_from_asset(item_asset)
|
||||||
.map_err(LoadoutBuilderError::ItemAssetError)?;
|
.map_err(LoadoutBuilderError::ItemAssetError)?;
|
||||||
Ok(Some(item))
|
Ok(Some(item))
|
||||||
},
|
},
|
||||||
ItemSpecNew::Choice(items) => {
|
ItemSpec::Choice(items) => {
|
||||||
let (_, item_spec) = items
|
let (_, item_spec) = items
|
||||||
.choose_weighted(rng, |(weight, _)| *weight)
|
.choose_weighted(rng, |(weight, _)| *weight)
|
||||||
.map_err(LoadoutBuilderError::ItemChoiceError)?;
|
.map_err(LoadoutBuilderError::ItemChoiceError)?;
|
||||||
@ -156,24 +156,24 @@ pub struct LoadoutSpec {
|
|||||||
// Meta fields
|
// Meta fields
|
||||||
inherit: Option<Base>,
|
inherit: Option<Base>,
|
||||||
// Armor
|
// Armor
|
||||||
head: Option<ItemSpecNew>,
|
head: Option<ItemSpec>,
|
||||||
neck: Option<ItemSpecNew>,
|
neck: Option<ItemSpec>,
|
||||||
shoulders: Option<ItemSpecNew>,
|
shoulders: Option<ItemSpec>,
|
||||||
chest: Option<ItemSpecNew>,
|
chest: Option<ItemSpec>,
|
||||||
gloves: Option<ItemSpecNew>,
|
gloves: Option<ItemSpec>,
|
||||||
ring1: Option<ItemSpecNew>,
|
ring1: Option<ItemSpec>,
|
||||||
ring2: Option<ItemSpecNew>,
|
ring2: Option<ItemSpec>,
|
||||||
back: Option<ItemSpecNew>,
|
back: Option<ItemSpec>,
|
||||||
belt: Option<ItemSpecNew>,
|
belt: Option<ItemSpec>,
|
||||||
legs: Option<ItemSpecNew>,
|
legs: Option<ItemSpec>,
|
||||||
feet: Option<ItemSpecNew>,
|
feet: Option<ItemSpec>,
|
||||||
tabard: Option<ItemSpecNew>,
|
tabard: Option<ItemSpec>,
|
||||||
bag1: Option<ItemSpecNew>,
|
bag1: Option<ItemSpec>,
|
||||||
bag2: Option<ItemSpecNew>,
|
bag2: Option<ItemSpec>,
|
||||||
bag3: Option<ItemSpecNew>,
|
bag3: Option<ItemSpec>,
|
||||||
bag4: Option<ItemSpecNew>,
|
bag4: Option<ItemSpec>,
|
||||||
lantern: Option<ItemSpecNew>,
|
lantern: Option<ItemSpec>,
|
||||||
glider: Option<ItemSpecNew>,
|
glider: Option<ItemSpec>,
|
||||||
// Weapons
|
// Weapons
|
||||||
active_hands: Option<Hands>,
|
active_hands: Option<Hands>,
|
||||||
inactive_hands: Option<Hands>,
|
inactive_hands: Option<Hands>,
|
||||||
@ -264,96 +264,6 @@ impl assets::Asset for LoadoutSpec {
|
|||||||
const EXTENSION: &'static str = "ron";
|
const EXTENSION: &'static str = "ron";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
||||||
pub enum ItemSpec {
|
|
||||||
/// One specific item.
|
|
||||||
/// Example:
|
|
||||||
/// Item("common.items.armor.steel.foot")
|
|
||||||
Item(String),
|
|
||||||
/// Choice from items with weights.
|
|
||||||
/// Example:
|
|
||||||
/// Choice([
|
|
||||||
/// (1.0, Some(Item("common.items.lantern.blue_0"))),
|
|
||||||
/// (1.0, None),
|
|
||||||
/// ])
|
|
||||||
Choice(Vec<(f32, Option<ItemSpec>)>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ItemSpec {
|
|
||||||
pub fn try_to_item(&self, asset_specifier: &str, rng: &mut impl Rng) -> Option<Item> {
|
|
||||||
match self {
|
|
||||||
ItemSpec::Item(specifier) => Some(Item::new_from_asset_expect(specifier)),
|
|
||||||
|
|
||||||
ItemSpec::Choice(items) => {
|
|
||||||
choose(items, asset_specifier, rng)
|
|
||||||
.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)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
/// # Usage
|
|
||||||
/// Read everything and checks if it's loading
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// 1) If weights are invalid
|
|
||||||
/// 2) If item doesn't correspond to `EquipSlot`
|
|
||||||
pub fn validate(&self, equip_slot: EquipSlot) {
|
|
||||||
match self {
|
|
||||||
ItemSpec::Item(specifier) => {
|
|
||||||
let item = Item::new_from_asset_expect(specifier);
|
|
||||||
assert!(
|
|
||||||
equip_slot.can_hold(&item.kind),
|
|
||||||
"Tried to place {} into {:?}",
|
|
||||||
specifier,
|
|
||||||
equip_slot
|
|
||||||
);
|
|
||||||
std::mem::drop(item);
|
|
||||||
},
|
|
||||||
ItemSpec::Choice(items) => {
|
|
||||||
for (p, entry) in items {
|
|
||||||
if p <= &0.0 {
|
|
||||||
let err = format!(
|
|
||||||
"Weight is less or equal to 0.0.\n ({:?}: {:?})",
|
|
||||||
equip_slot, self
|
|
||||||
);
|
|
||||||
panic!("\n\n{}\n\n", err);
|
|
||||||
} else {
|
|
||||||
entry.as_ref().map(|e| e.validate(equip_slot));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn choose<'a>(
|
|
||||||
items: &'a [(f32, Option<ItemSpec>)],
|
|
||||||
asset_specifier: &str,
|
|
||||||
rng: &mut impl Rng,
|
|
||||||
) -> &'a Option<ItemSpec> {
|
|
||||||
items.choose_weighted(rng, |item| item.0).map_or_else(
|
|
||||||
|err| match err {
|
|
||||||
WeightedError::NoItem | WeightedError::AllWeightsZero => &None,
|
|
||||||
WeightedError::InvalidWeight => {
|
|
||||||
let err = format!("Negative values of probability in {}.", asset_specifier);
|
|
||||||
common_base::dev_panic!(err, or return &None)
|
|
||||||
},
|
|
||||||
WeightedError::TooMany => {
|
|
||||||
let err = format!("More than u32::MAX values in {}.", asset_specifier);
|
|
||||||
common_base::dev_panic!(err, or return &None)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|(_p, itemspec)| itemspec,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn make_potion_bag(quantity: u32) -> Item {
|
pub fn make_potion_bag(quantity: u32) -> Item {
|
||||||
let mut bag = Item::new_from_asset_expect("common.items.armor.misc.bag.tiny_leather_pouch");
|
let mut bag = Item::new_from_asset_expect("common.items.armor.misc.bag.tiny_leather_pouch");
|
||||||
@ -779,7 +689,7 @@ impl LoadoutBuilder {
|
|||||||
let spec = spec.eval(rng)?;
|
let spec = spec.eval(rng)?;
|
||||||
|
|
||||||
// Utility function to unwrap our itemspec
|
// Utility function to unwrap our itemspec
|
||||||
let mut to_item = |maybe_item: Option<ItemSpecNew>| {
|
let mut to_item = |maybe_item: Option<ItemSpec>| {
|
||||||
if let Some(item) = maybe_item {
|
if let Some(item) = maybe_item {
|
||||||
item.try_to_item(rng)
|
item.try_to_item(rng)
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
assets::{self, AssetExt, Error},
|
assets::{self, AssetExt, Error},
|
||||||
comp::{
|
comp::{
|
||||||
self, agent, humanoid,
|
self, agent, humanoid,
|
||||||
inventory::loadout_builder::{ItemSpec, LoadoutBuilder, LoadoutSpec},
|
inventory::loadout_builder::{LoadoutBuilder, LoadoutSpec},
|
||||||
Alignment, Body, Item,
|
Alignment, Body, Item,
|
||||||
},
|
},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
@ -38,7 +38,7 @@ impl Default for AlignmentMark {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub enum LoadoutKindNew {
|
pub enum LoadoutKind {
|
||||||
FromBody,
|
FromBody,
|
||||||
Asset(String),
|
Asset(String),
|
||||||
Inline(LoadoutSpec),
|
Inline(LoadoutSpec),
|
||||||
@ -46,43 +46,11 @@ pub enum LoadoutKindNew {
|
|||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub struct InventorySpec {
|
pub struct InventorySpec {
|
||||||
loadout: LoadoutKindNew,
|
loadout: LoadoutKind,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
items: Vec<(u32, String)>,
|
items: Vec<(u32, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// - TwoHanded(ItemSpec) for one 2h or 1h weapon,
|
|
||||||
/// - Paired(ItemSpec) for two 1h weapons aka berserker mode,
|
|
||||||
/// - Mix { mainhand: ItemSpec, offhand: ItemSpec, }
|
|
||||||
/// for two different 1h weapons.
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
pub enum Hands {
|
|
||||||
TwoHanded(ItemSpec),
|
|
||||||
Paired(ItemSpec),
|
|
||||||
Mix {
|
|
||||||
mainhand: ItemSpec,
|
|
||||||
offhand: ItemSpec,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
pub enum LoadoutAsset {
|
|
||||||
Loadout(String),
|
|
||||||
Choice(Vec<(u32, String)>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
|
||||||
pub enum LoadoutKind {
|
|
||||||
FromBody,
|
|
||||||
Asset(LoadoutAsset),
|
|
||||||
Hands(Hands),
|
|
||||||
Extended {
|
|
||||||
hands: Hands,
|
|
||||||
base_asset: LoadoutAsset,
|
|
||||||
inventory: Vec<(u32, String)>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
pub enum Meta {
|
pub enum Meta {
|
||||||
SkillSetAsset(String),
|
SkillSetAsset(String),
|
||||||
@ -335,16 +303,16 @@ impl EntityInfo {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
match loadout {
|
match loadout {
|
||||||
LoadoutKindNew::FromBody => {
|
LoadoutKind::FromBody => {
|
||||||
self = self.with_default_equip();
|
self = self.with_default_equip();
|
||||||
},
|
},
|
||||||
LoadoutKindNew::Asset(loadout) => {
|
LoadoutKind::Asset(loadout) => {
|
||||||
let loadout = LoadoutBuilder::from_asset(&loadout, rng).unwrap_or_else(|e| {
|
let loadout = LoadoutBuilder::from_asset(&loadout, rng).unwrap_or_else(|e| {
|
||||||
panic!("failed to load loadout for {config_asset}: {e:?}");
|
panic!("failed to load loadout for {config_asset}: {e:?}");
|
||||||
});
|
});
|
||||||
self.loadout = loadout;
|
self.loadout = loadout;
|
||||||
},
|
},
|
||||||
LoadoutKindNew::Inline(loadout_spec) => {
|
LoadoutKind::Inline(loadout_spec) => {
|
||||||
let loadout =
|
let loadout =
|
||||||
LoadoutBuilder::from_loadout_spec(loadout_spec, rng).unwrap_or_else(|e| {
|
LoadoutBuilder::from_loadout_spec(loadout_spec, rng).unwrap_or_else(|e| {
|
||||||
panic!("failed to load loadout for {config_asset}: {e:?}");
|
panic!("failed to load loadout for {config_asset}: {e:?}");
|
||||||
|
Loading…
Reference in New Issue
Block a user