diff options
| -rw-r--r-- | content/encyclopediae/base.json | 4 | ||||
| -rw-r--r-- | content/encyclopediae/crops.json | 12 | ||||
| -rw-r--r-- | content/encyclopediae/default_encyclopedia.json | 16 | ||||
| -rw-r--r-- | docs/encyclopedia_format.md | 11 | ||||
| -rw-r--r-- | src/assemblage.rs | 60 | ||||
| -rw-r--r-- | src/components/mod.rs | 2 | ||||
| -rw-r--r-- | src/componentwrapper.rs | 22 | ||||
| -rw-r--r-- | src/encyclopedia.rs | 11 | ||||
| -rw-r--r-- | src/fromtoparameter.rs | 49 | ||||
| -rw-r--r-- | src/parameter.rs | 47 | ||||
| -rw-r--r-- | src/parameterexpression.rs | 38 | ||||
| -rw-r--r-- | src/roomtemplate.rs | 2 | ||||
| -rw-r--r-- | src/template.rs | 2 |
13 files changed, 98 insertions, 178 deletions
diff --git a/content/encyclopediae/base.json b/content/encyclopediae/base.json index 1964579..8052eb6 100644 --- a/content/encyclopediae/base.json +++ b/content/encyclopediae/base.json @@ -74,13 +74,13 @@ "house": {"height": 3.0, "sprite": "house"}, "freeland": {}, "img": { - "arguments": [["sprite", "string", ""], ["height", "float", 1.0]], + "arguments": {"sprite": null, "height": 1.0}, "components": [ ["Visible", {"name": {"$arg": "sprite"}, "sprite": {"$arg": "sprite"}, "height": {"$arg": "height"}}] ] }, "letter": { - "arguments": [["char", "string"]], + "arguments": {"char": "string"}, "components": [["Visible", { "name": {"$concat": ["letter_", {"$arg": "char"}]}, "sprite": {"$concat": ["emptyletter-", {"$arg": "char"}]}, diff --git a/content/encyclopediae/crops.json b/content/encyclopediae/crops.json index 0c16073..99c5ec3 100644 --- a/content/encyclopediae/crops.json +++ b/content/encyclopediae/crops.json @@ -16,7 +16,7 @@ "flags": ["Occupied"] }, "plantedradishseed": { - "arguments": [["target_time", "int", -1]], + "arguments": {"target_time": []}, "sprite": "seed", "height": 0.05, "name": "seed", @@ -35,7 +35,7 @@ "flags": ["Occupied"] }, "radishseedling": { - "arguments": [["target_time", "int", -1]], + "arguments": {"target_time": []}, "sprite": "seedling", "height": 0.05, "name": "seedling", @@ -54,7 +54,7 @@ "flags": ["Occupied"] }, "youngradishplant": { - "arguments": [["target_time", "int", -1]], + "arguments": {"target_time": []}, "sprite": "youngplant", "height": 0.8, "name": "youngradishplant", @@ -73,7 +73,7 @@ "flags": ["Occupied"] }, "plantedseed": { - "arguments": [["target_time", "int", -1], ["next", "template"], ["delay", "int"]], + "arguments": {"target_time": [], "next": null, "delay": null}, "sprite": "seed", "height": 0.05, "name": "plantedseed", @@ -92,7 +92,7 @@ "flags": ["Occupied"] }, "seedling": { - "arguments": [["target_time", "int", -1], ["next", "template"], ["delay", "int"]], + "arguments": {"target_time": [], "next": null, "delay": null}, "sprite": "seed", "height": 0.09, "name": "seedling", @@ -111,7 +111,7 @@ "flags": ["Occupied"] }, "youngplant": { - "arguments": [["target_time", "int", -1], ["next", "template"], ["crop", "string"], ["delay", "int"]], + "arguments": {"target_time": [], "next": null, "delay": null, "crop": null}, "components": [ ["Timer", { "delay": {"$arg": "delay"}, diff --git a/content/encyclopediae/default_encyclopedia.json b/content/encyclopediae/default_encyclopedia.json index cf58771..2d37aca 100644 --- a/content/encyclopediae/default_encyclopedia.json +++ b/content/encyclopediae/default_encyclopedia.json @@ -1,14 +1,14 @@ { "assemblages": { "portal": { - "arguments": [["destination", "string"], ["destpos", "string", ""]], + "arguments": {"destination": null, "destpos": ""}, "components": [ ["RoomExit", {"destination": {"$arg": "destination"}, "dest_pos": {"$arg": "destpos"}}] ], "flags": ["Floor"] }, "_homeportal": { - "arguments": [["allowed", "list", []]], + "arguments": {"allowed": []}, "extract": {"allowed": ["Whitelist", "allowed"]}, "components": [ ["RoomExit", {"destination": "_home+{player}", "dest_pos": ""}], @@ -18,7 +18,7 @@ "flags": ["Floor"] }, "builtwall": { - "arguments": [["health", "int", 100]], + "arguments": {"health": 100}, "components": [ ["Health", {"health": {"$arg": "health"}, "maxhealth": 100}], ["Loot", {"loot": [[{"$template": "stone"}, 1.0]]}] @@ -34,7 +34,7 @@ "height": 0.8 }, "dummy": { - "arguments": [["health", "int", 20]], + "arguments": {"health": 20}, "sprite": "dummy", "height": 1, "components": [ @@ -44,17 +44,17 @@ "wound": { "sprite": "wound", "height": 0.25, - "components": [["Timer", {"delay": 3, "spread": 0.0, "trigger": "remove", "target_time": -1}]], + "components": [["Timer", {"delay": 3, "spread": 0.0, "trigger": "remove", "target_time": []}]], "save": false }, "spawner": { - "arguments": [["template", "template"], ["amount", "int", 1], ["delay", "int", 0], ["clan", "string", ""], ["initial_spawn", "bool", true], ["radius", "int", 0]], + "arguments": {"template": null, "amount": 1, "delay": 0, "clan": "", "initial_spawn": true, "radius": 0}, "components": [ ["Timer", { "delay": {"$arg": "delay"}, "spread": 0.1, "trigger": "spawn", - "target_time": {"$if": [{"$arg": "initial_spawn"}, 0, -1]} + "target_time": {"$if": [{"$arg": "initial_spawn"}, 0, []]} }], ["Spawner", { "template": {"$arg": "template"}, @@ -65,7 +65,7 @@ ] }, "singleton": { - "arguments": [["ent", "template"], ["clan", "string", ""]], + "arguments": {"ent": "template", "clan": ""}, "components": [ ["Spawner", { "template": {"$arg": "ent"}, diff --git a/docs/encyclopedia_format.md b/docs/encyclopedia_format.md index 52ff7e9..141bc4a 100644 --- a/docs/encyclopedia_format.md +++ b/docs/encyclopedia_format.md @@ -61,17 +61,16 @@ For a full list of components and what type of parameters they take, see: https: An assemblage can be constructed with a template. This template can provide the assemblage with arguments. -The argments are a list of lists of 2 or 3 items. -The first item is the name (as string). -The second item is the parameter type (as string). -The optional third item is the default value. +The arguments are a dictinary. +The key item is the name (as string). +The value is the default value for the argument, or null to indicate that there is no default and that the template should provide one. An "args" ParameterExpression in the component definitions will have its value filled in with the value that is given to this argument, or otherwise the default value (and if that doesn't exist either it will error). Example: "portal": { - "arguments": [["destination", "string"], ["destpos", "string", ""]], + "arguments": {"destination": null, "destpos", ""}, "components": [ ["RoomExit", {"destination": {"$arg": "destination"}, "dest_pos": {"$arg": "destpos"}}] ], @@ -104,7 +103,7 @@ Not all components can have properties extracted, and some components can only h Example: "builtwall": { - "arguments": [["health", "int", 100]], + "arguments": {"health", 100}, "components": [ ["Health", {"health": {"$arg": "health"}, "maxhealth": 100}], ["Loot", {"loot": [[{"$template": "stone"}, 1.0]]}] diff --git a/src/assemblage.rs b/src/assemblage.rs index 5541ec1..fb60463 100644 --- a/src/assemblage.rs +++ b/src/assemblage.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use serde::{de, Serialize, Deserialize, Deserializer}; use crate::{ parameterexpression::ParameterExpression, - parameter::{Parameter, ParameterType}, + parameter::{Parameter}, componentwrapper::{ComponentWrapper, ComponentType}, components::{Serialise, Clan}, Template, @@ -11,11 +11,10 @@ use crate::{ aerr }; -type ArgumentDef = (String, ParameterType, Option<Parameter>); #[derive(Debug, PartialEq, Clone)] pub struct Assemblage { - pub arguments: Vec<ArgumentDef>, + pub arguments: HashMap<String, Option<Parameter>>, pub components: Vec<(ComponentType, HashMap<String, ParameterExpression>)>, pub save: bool, pub extract: Vec<(String, ComponentType, String)> @@ -35,24 +34,16 @@ impl Assemblage { fn prepare_arguments(&self, kwargs: &HashMap<String, Parameter>) -> AnyResult<HashMap<&str, Parameter>> { let mut arguments: HashMap<&str, Parameter> = HashMap::new(); - for (name, typ, def) in self.arguments.iter() { + for (name, def) in self.arguments.iter() { let param: Parameter= { if let Some(val) = kwargs.get(name) { val.clone() } else if let Some(val) = def { val.clone() } else { - return Err(aerr!("argument <{:?}> has no value", (name, typ, def))) + return Err(aerr!("argument <{:?}> has no value", (name, def))) } }; - if param.paramtype() != *typ { - return Err(aerr!( - "argument has incorrect type: {:?}, {:?}, {:?}", - (name, typ, def), - param.paramtype(), - param - )); - } arguments.insert(name, param); } Ok(arguments) @@ -112,12 +103,7 @@ impl<'de> Deserialize<'de> for Assemblage { components.push((ComponentType::Substitute, compmap!{into: sub})); } Ok(Assemblage { - arguments: arguments.into_iter() - .map(|arg| match arg { - ArgumentDefSave::Long(name, typ, def) => (name, typ, Some(def)), - ArgumentDefSave::Short(name, typ) => (name, typ, None) - }) - .collect(), + arguments, components, save, extract: extract.into_iter().map(|(k, (t, v))| (k, t, v)).collect() @@ -125,15 +111,9 @@ impl<'de> Deserialize<'de> for Assemblage { } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -enum ArgumentDefSave{ - Long(String, ParameterType, Parameter), - Short(String, ParameterType) -} -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] struct AssemblageSave { #[serde(default)] - pub arguments: Vec<ArgumentDefSave>, + pub arguments: HashMap<String, Option<Parameter>>, #[serde(default)] pub components: Vec<(ComponentType, HashMap<String, ParameterExpression>)>, #[serde(default="return_true")] @@ -161,11 +141,11 @@ mod tests { fn empty_assemblage_deserialize() { assert_eq!( Assemblage::deserialize(&json!({ - "arguments": [], + "arguments": {}, "components": [] })).unwrap(), Assemblage{ - arguments: vec![], + arguments: hashmap!{}, components: vec![], save: true, extract: Vec::new() @@ -176,9 +156,7 @@ mod tests { #[test] fn grass_deserialize(){ let result = Assemblage::deserialize(&json!({ - "arguments": [ - ["sprite", "string", "grass1"] - ], + "arguments": {"sprite": "grass1"}, "components": [ ["Visible", { "sprite": {"$arg": "sprite"}, @@ -188,7 +166,7 @@ mod tests { ] })).unwrap(); let constructed = Assemblage{ - arguments: vec![("sprite".to_string(), ParameterType::String, Some(Parameter::String("grass1".to_string())))], + arguments: hashmap!{"sprite".to_string() => Some(Parameter::String("grass1".to_string()))}, components: vec![ (ComponentType::Visible, hashmap!( "sprite".to_string() => ParameterExpression::Argument("sprite".to_string()), @@ -205,9 +183,7 @@ mod tests { #[test] fn invalid_component_name(){ Assemblage::deserialize(&json!({ - "arguments": [ - ["sprite", "string", null] - ], + "arguments": {"sprite": null}, "components": [ ["visible", { // no capital so invalid "sprite": {"$arg": "sprite"}, @@ -224,9 +200,7 @@ mod tests { // #[test] fn invalid_parameter_type(){ Assemblage::deserialize(&json!({ - "arguments": [ - ["sprite", "string", "grass1"] - ], + "arguments": {"sprite": "grass1"}, "components": [ ["Visible", { "sprite": {"$arg": "sprite"}, @@ -244,9 +218,7 @@ mod tests { // #[test] fn wrong_argument_default(){ Assemblage::deserialize(&json!({ - "arguments": [ - ["sprite", "string", 1] - ], + "arguments": {"sprite": 1}, "components": [ ["Visible", { "sprite": {"$arg": "sprite"}, @@ -262,9 +234,7 @@ mod tests { #[test] fn null_argument(){ let result = Assemblage::deserialize(&json!({ - "arguments": [ - ["sprite", "string"] - ], + "arguments": {"sprite": null}, "components": [ ["Visible", { "sprite": {"$arg": "sprite"}, @@ -274,7 +244,7 @@ mod tests { ] })).unwrap(); let constructed = Assemblage{ - arguments: vec![("sprite".to_string(), ParameterType::String, None)], + arguments: hashmap!{"sprite".to_string() => None}, components: vec![ (ComponentType::Visible, hashmap!( "sprite".to_string() => ParameterExpression::Argument("sprite".to_string()), diff --git a/src/components/mod.rs b/src/components/mod.rs index 91fc081..34630e4 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -168,8 +168,8 @@ pub struct Autofight { #[storage(HashMapStorage)] pub struct MonsterAI { pub move_chance: f64, + pub view_distance: i64, pub homesickness: f64, - pub view_distance: i64 } #[derive(Component, Debug, Clone, Default)] diff --git a/src/componentwrapper.rs b/src/componentwrapper.rs index 4add5dc..75a0bca 100644 --- a/src/componentwrapper.rs +++ b/src/componentwrapper.rs @@ -159,7 +159,7 @@ components!(all: Fighter (damage: i64, cooldown: i64) {Fighter{attack: AttackType::Attack(damage), cooldown, range: 1}}; Healing (delay: i64, health: i64) {Healing{delay, health, next_heal: None}}; Autofight () {Autofight::default()}; - MonsterAI (move_chance: f64, homesickness: f64, view_distance: i64); + MonsterAI (move_chance: f64, view_distance: i64, homesickness: f64); Spawner (amount: i64, clan: String, template: Template, radius: i64) { Spawner{ amount: amount as usize, @@ -176,25 +176,7 @@ components!(all: Interactable::parse_from_parameter(&typ, &arg).ok_or(aerr!("invalid interaction {:?} {:?}", typ, arg))? }; Loot (loot: Vec<(Template, f64)>); - Timer ( - trigger: String, (panic!("can't turn trigger to string")), - delay: i64, (Timer.delay), - spread: f64, (Timer.spread), - target_time: i64, ({ - if let Some(time) = Timer.target_time { - time.0 - } else { - 0 - } - }) - ) - Timer { - trigger: Trigger::from_str(&trigger).map_err(|_|aerr!("invalid trigger name {}", trigger))?, - delay, - spread, - target_time: if target_time == -1 { None } else { Some(Timestamp(target_time)) } - // please forgive me for using -1 as null - }; + Timer (trigger: Trigger, delay: i64, spread: f64, target_time: Option<Timestamp>); Equipment () {panic!("equipment from parameters not implemented")}; TimeOffset (dtime: i64); Flags (flags: Vec<String>) { diff --git a/src/encyclopedia.rs b/src/encyclopedia.rs index 16d26a8..2bb2960 100644 --- a/src/encyclopedia.rs +++ b/src/encyclopedia.rs @@ -62,7 +62,7 @@ impl<'de> Deserialize<'de> for Encyclopedia { let ent = item.entity.unwrap_or_else(||{ let enttyp = EntityType(id.clone()); assemblages.insert(enttyp.clone(), Assemblage { - arguments: Vec::new(), + arguments: HashMap::new(), save: true, extract: Vec::new(), components: vec![ @@ -78,13 +78,10 @@ impl<'de> Deserialize<'de> for Encyclopedia { action: item.action.unwrap_or(ItemAction::None) }); } - for (templatename, (baseent, mut args)) in templates { + for (templatename, (baseent, args)) in templates { let mut assemblage = assemblages.get(&baseent).ok_or(de::Error::custom(format!("template name '{:?}' does not point to not an assemblage", baseent)))?.clone(); - for arg in assemblage.arguments.iter_mut() { - if let Some(param) = args.remove(&arg.0) { - // todo: verify argument type - arg.2 = Some(param); - } + for (key, val) in args { + assemblage.arguments.insert(key, Some(val)); } assemblages.insert(templatename, assemblage); } diff --git a/src/fromtoparameter.rs b/src/fromtoparameter.rs index 13483d7..180632f 100644 --- a/src/fromtoparameter.rs +++ b/src/fromtoparameter.rs @@ -1,6 +1,7 @@ use std::collections::{HashSet, HashMap}; use std::hash::Hash; +use std::str::FromStr; use crate::{ parameter::Parameter, Template, @@ -8,7 +9,9 @@ use crate::{ PlayerId, Sprite, ItemId, - RoomId + RoomId, + components::{Trigger}, + Timestamp }; pub trait FromToParameter: Sized { @@ -82,11 +85,26 @@ tofrom!(PlayerId(String)); tofrom!(Sprite(String)); tofrom!(ItemId(String)); tofrom!(RoomId(String)); +tofrom!(Timestamp(Int)); + + +macro_rules! fromtostr { + ($t: ty) => { + impl FromToParameter for $t { + fn from_parameter(p: Parameter) -> Option<Self>{ + <$t>::from_str(&String::from_parameter(p)?).ok() + } + fn to_parameter(self) -> Parameter { + self.to_string().to_parameter() + } + } + } +} + +fromtostr!(Trigger); impl<T> FromToParameter for Vec<T> -where - T: FromToParameter, -{ +where T: FromToParameter { fn from_parameter(p: Parameter) -> Option<Self>{ if let Parameter::List(items) = p{ let mut v = Self::new(); @@ -108,9 +126,7 @@ where } impl<T> FromToParameter for HashSet<T> -where - T: FromToParameter + Eq + Hash, -{ +where T: FromToParameter + Eq + Hash { fn from_parameter(p: Parameter) -> Option<Self>{ Some(<Vec<T>>::from_parameter(p)?.into_iter().collect()) } @@ -186,3 +202,22 @@ impl FromToParameter for Pos { (self.x, self.y).to_parameter() } } + +impl<T> FromToParameter for Option<T> +where T: FromToParameter { + fn from_parameter(p: Parameter) -> Option<Self>{ + if p == Parameter::List(Vec::new()) { + Some(None) + } else { + Some(Some(T::from_parameter(p)?)) + } + } + fn to_parameter(self) -> Parameter { + if let Some(p) = self { + p.to_parameter() + } else { + Parameter::List(Vec::new()) + } + } +} + diff --git a/src/parameter.rs b/src/parameter.rs index be846a1..36bb100 100644 --- a/src/parameter.rs +++ b/src/parameter.rs @@ -1,50 +1,21 @@ use serde::{Serialize, Deserialize}; -use strum_macros::{EnumString, Display}; use crate::{ Template, }; - -macro_rules! parameters { - {$($name: ident $typ: ty);*;} => { - #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] - #[serde(untagged)] - pub enum Parameter { - $( - $name($typ), - )* - } - impl Parameter { - pub fn paramtype(&self) -> ParameterType { - match self { - $( - Self::$name(_) => ParameterType::$name, - )* - } - } - } - - #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, EnumString, Display)] - #[serde(rename_all = "lowercase")] - #[strum(serialize_all = "lowercase")] - pub enum ParameterType { - $( - $name, - )* - } - } +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum Parameter { + String(String), + Int(i64), + Float(f64), + Template(Template), + Bool(bool), + List(Vec<Parameter>) } -parameters!{ - String String; - Int i64; - Float f64; - Template Template; - Bool bool; - List Vec<Parameter>; -} impl Parameter { diff --git a/src/parameterexpression.rs b/src/parameterexpression.rs index b111669..c96bd93 100644 --- a/src/parameterexpression.rs +++ b/src/parameterexpression.rs @@ -3,11 +3,9 @@ use std::collections::HashMap; use rand::Rng; use serde::{Serialize, Deserialize, Deserializer, Serializer}; use crate::{ - parameter::{Parameter, ParameterType}, + parameter::Parameter, Template, - template::{EntityType}, - Result as AnyResult, - aerr, + template::{EntityType} }; const MAX_NESTING: usize = 5; @@ -91,38 +89,6 @@ impl ParameterExpression { } } - - #[allow(dead_code)] - pub fn get_type(&self, arguments: &[(String, ParameterType, Option<Parameter>)]) -> AnyResult<ParameterType>{ - Ok(match self { - Self::Constant(param) => param.paramtype(), - Self::List(_) => ParameterType::List, - Self::Template{name: _, kwargs: _, save: _, clan: _} => ParameterType::Template, - Self::Argument(argname) => arguments.iter().find(|(n, _t, _d)| n == argname).ok_or(aerr!("unknown argument name {} in {:?}", argname, arguments))?.1, - Self::Random(options) => { - let typ: ParameterType = options.get(0).ok_or(aerr!("random has no options"))?.get_type(arguments)?; - for param in options { - if param.get_type(arguments)? != typ { - return Err(aerr!("inconsistent parameter types in random")); - } - } - typ - }, - Self::If(condition, thenval, elseval) => { - if condition.get_type(arguments)? != ParameterType::Bool { - return Err(aerr!("if condition is not a bool")); - } - let typ: ParameterType = thenval.get_type(arguments)?; - if elseval.get_type(arguments)? != typ { - return Err(aerr!("inconsistent parameter types in if")); - } - typ - }, - Self::Concat(_s) => ParameterType::String, - Self::TemplateSelf => ParameterType::Template, - Self::TemplateName => ParameterType::String - }) - } } diff --git a/src/roomtemplate.rs b/src/roomtemplate.rs index 1b920ac..009ce02 100644 --- a/src/roomtemplate.rs +++ b/src/roomtemplate.rs @@ -83,7 +83,7 @@ mod tests { "mapping": { "#": ["wall"], ",": "grass", - ".": {"type": "grass", "args": [], "kwargs": {}} + ".": {":template": "grass"} } })).unwrap(); } diff --git a/src/template.rs b/src/template.rs index 530fbc9..8282a79 100644 --- a/src/template.rs +++ b/src/template.rs @@ -125,7 +125,7 @@ mod tests { #[test] fn template_with_kwarg(){ assert_eq!( - Template::deserialize(json!({"type": "wall", "kwargs": {"health": 50}})).unwrap(), + Template::deserialize(json!({":template": "wall", "health": 50})).unwrap(), Template::new("wall", hashmap!{"health".to_string() => Parameter::Int(50)}) ); } |
