diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/flags.rs | 21 | ||||
| -rw-r--r-- | src/components/mod.rs | 3 | ||||
| -rw-r--r-- | src/componentwrapper.rs | 8 | ||||
| -rw-r--r-- | src/item.rs | 4 | ||||
| -rw-r--r-- | src/parameter.rs | 17 | ||||
| -rw-r--r-- | src/pos.rs | 18 | ||||
| -rw-r--r-- | src/purgatory.rs | 3 | ||||
| -rw-r--r-- | src/resources/mod.rs | 8 | ||||
| -rw-r--r-- | src/roomtemplate.rs | 49 | ||||
| -rw-r--r-- | src/systems/spawn.rs | 11 | ||||
| -rw-r--r-- | src/template.rs | 95 | ||||
| -rw-r--r-- | src/worldloader.rs | 3 |
12 files changed, 201 insertions, 39 deletions
diff --git a/src/components/flags.rs b/src/components/flags.rs index d985bf2..25ec4ea 100644 --- a/src/components/flags.rs +++ b/src/components/flags.rs @@ -4,26 +4,17 @@ use specs::{ Component, VecStorage, }; +use strum_macros::{EnumString, Display}; +use serde::{Serialize, Deserialize}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumString, Display, Serialize, Deserialize)] pub enum Flag { Blocking, Floor, Occupied, - Soil -} - -use Flag::*; -impl Flag { - pub fn from_str(s: &str) -> Option<Flag> { - Some(match s { - "Blocking" => Blocking, - "Floor" => Floor, - "Occupied" => Occupied, - "Soil" => Soil, - _ => None? - }) - } + Soil, + Build, + Hot } diff --git a/src/components/mod.rs b/src/components/mod.rs index 38a14bd..3f804da 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -184,7 +184,8 @@ pub struct Spawner { pub amount: usize, pub clan: Clan, pub template: Template, - pub saturated: bool + pub saturated: bool, + pub radius: i64 } #[derive(Component, Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/componentwrapper.rs b/src/componentwrapper.rs index 08d95ee..b952657 100644 --- a/src/componentwrapper.rs +++ b/src/componentwrapper.rs @@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet}; use serde::Deserialize; use specs::Builder; use rand::Rng; +use std::str::FromStr; use crate::{ PlayerId, @@ -166,7 +167,7 @@ components!(all: Healing (delay: i64, health: i64) {Healing{delay, health, next_heal: None}}; Autofight () {Autofight::default()}; MonsterAI (move_chance: f64, homesickness: f64, view_distance: i64); - Spawner (amount: i64, clan: String, template: Template) { + Spawner (amount: i64, clan: String, template: Template, radius: i64) { Spawner{ amount: amount as usize, clan: Clan{name: @@ -177,7 +178,8 @@ components!(all: } }, template: template.unsaved(), - saturated: false + saturated: false, + radius } }; Clan (name: String); @@ -212,7 +214,7 @@ components!(all: Flags( flags .iter() - .map(|s| Flag::from_str(s)) + .map(|s| Flag::from_str(s).ok()) .collect::<Option<HashSet<Flag>>>().ok_or(aerr!("invalid flag name"))? ) }; diff --git a/src/item.rs b/src/item.rs index 88fd583..0abf35d 100644 --- a/src/item.rs +++ b/src/item.rs @@ -52,8 +52,8 @@ impl ItemAction { "eat" => Eat(arg.as_i64()?), "build" => Build( Template::from_json(arg.get(0)?).ok()?, - arg.get(1)?.as_array()?.iter().map(|v| Flag::from_str(v.as_str()?)).collect::<Option<HashSet<Flag>>>()?, - arg.get(2)?.as_array()?.iter().map(|v| Flag::from_str(v.as_str()?)).collect::<Option<HashSet<Flag>>>()? + arg.get(1)?.as_array()?.iter().map(|v| Flag::from_str(v.as_str()?).ok()).collect::<Option<HashSet<Flag>>>()?, + arg.get(2)?.as_array()?.iter().map(|v| Flag::from_str(v.as_str()?).ok()).collect::<Option<HashSet<Flag>>>()? ), "none" => None, "equip" => Equip(Equippable::from_json(arg)?), diff --git a/src/parameter.rs b/src/parameter.rs index 99ecdd7..c307a27 100644 --- a/src/parameter.rs +++ b/src/parameter.rs @@ -1,5 +1,6 @@ use serde_json::{Value, json}; +use serde::{de, Serialize, Deserialize, Serializer, Deserializer}; use crate::{ Template, Pos, @@ -114,6 +115,22 @@ impl Parameter { } } + +impl Serialize for Parameter { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where S: Serializer, + { + self.to_json().serialize(serializer) + } +} +impl<'de> Deserialize<'de> for Parameter { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where D: Deserializer<'de>, + { + Self::guess_from_json(&Value::deserialize(deserializer)?).map_err(|e| de::Error::custom(e.text)) + } +} + #[cfg(test)] mod tests { use super::*; @@ -2,7 +2,7 @@ use std::ops::{Add, Sub}; use serde_json::Value; -use serde::{Serialize, Serializer, ser::SerializeTuple}; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; use crate::util::clamp; #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Default)] @@ -47,13 +47,17 @@ impl Pos { impl Serialize for Pos { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, + where S: Serializer, { - let mut tup = serializer.serialize_tuple(2)?; - tup.serialize_element(&self.x)?; - tup.serialize_element(&self.y)?; - tup.end() + (self.x, self.y).serialize(serializer) + } +} +impl<'de> Deserialize<'de> for Pos { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where D: Deserializer<'de>, + { + let (x, y) = <(i64, i64)>::deserialize(deserializer)?; + Ok(Self{x, y}) } } diff --git a/src/purgatory.rs b/src/purgatory.rs index 27db35a..f387b83 100644 --- a/src/purgatory.rs +++ b/src/purgatory.rs @@ -1,6 +1,7 @@ use serde_json::json; +use serde::Deserialize; use specs::{ DispatcherBuilder }; @@ -30,7 +31,7 @@ pub fn create_purgatory<'a, 'b>(encyclopedia: &Encyclopedia) -> Room<'a, 'b> { .with(Move, "move", &["controlinput", "controlai"]) .build(); let mut room = Room::new(purgatory_id(), encyclopedia.clone(), Some(dispatcher)); - room.load_from_template(&RoomTemplate::from_json(&json!({ + room.load_from_template(&RoomTemplate::deserialize(&json!({ "width": 15, "height": 20, "spawn": [7, 9], diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 627a396..025bc6e 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -7,7 +7,7 @@ pub use ground::Ground; pub use newentities::NewEntities; pub use roompermissions::RoomPermissions; -use std::collections::{HashMap}; +use std::collections::{HashMap, HashSet}; use specs::{Entity}; use crate::{ @@ -17,7 +17,8 @@ use crate::{ PlayerId, RoomId, playerstate::RoomPos, - Timestamp + Timestamp, + components::Flag }; @@ -58,3 +59,6 @@ pub struct Time { pub time: Timestamp } +#[derive(Default, Debug, Clone)] +pub struct RoomFlags(pub HashSet<Flag>); + diff --git a/src/roomtemplate.rs b/src/roomtemplate.rs index 87c94fe..7110e64 100644 --- a/src/roomtemplate.rs +++ b/src/roomtemplate.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use serde_json::{json, Value, value}; +use serde::{Deserialize, Deserializer, de, Serialize}; use crate::{ Pos, Template, @@ -18,6 +19,52 @@ pub struct RoomTemplate { pub permissions: RoomPermissions } +#[derive(Debug, Clone, Serialize, Deserialize)] +struct RoomTemplateSave { + pub width: i64, + pub height: i64, + pub spawn: Pos, + pub field: Vec<String>, + pub mapping: HashMap<char, TemplateList>, + #[serde(default)] + pub places: HashMap<String, Pos>, + #[serde(default)] + pub permissions: RoomPermissions +} +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +enum TemplateList { + Single(Template), + List(Vec<Template>) +} +impl<'de> Deserialize<'de> for RoomTemplate { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where D: Deserializer<'de>, + { + let rts = RoomTemplateSave::deserialize(deserializer)?; + let mut field = Vec::new(); + let width = rts.width as usize; + let height = rts.height as usize; + field.resize_with(width * height, Vec::new); + for (y, row) in rts.field.iter().take(height).enumerate() { + for (x, ch) in row.chars().take(width).enumerate() { + let templates = rts.mapping.get(&ch).ok_or(de::Error::custom(format!("char {}not found in mapping", ch)))?.clone(); + field[x + y * width] = match templates { + TemplateList::Single(temp) => vec![temp], + TemplateList::List(temps) => temps + } + } + } + Ok(Self { + size: (rts.width, rts.height), + spawn: rts.spawn, + field, + places: rts.places, + permissions: rts.permissions + }) + } +} + impl RoomTemplate { pub fn from_json(jsonroom: &Value) -> PResult<RoomTemplate>{ @@ -80,7 +127,7 @@ mod tests { #[test] fn simple_from_json() { - RoomTemplate::from_json(&json!({ + RoomTemplate::deserialize(&json!({ "width": 6, "height": 5, "spawn": [1, 1], diff --git a/src/systems/spawn.rs b/src/systems/spawn.rs index 01dfeee..f0a7aaa 100644 --- a/src/systems/spawn.rs +++ b/src/systems/spawn.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use rand; +use rand::Rng; use specs::{ WriteStorage, @@ -21,7 +23,8 @@ use crate::{ TimeOffset }, resources::{NewEntities}, - componentwrapper::ComponentWrapper + componentwrapper::ComponentWrapper, + Pos }; @@ -44,6 +47,7 @@ impl <'a> System<'a> for Spawn { let n: usize = *clan_nums.entry(clan).or_insert(0); clan_nums.insert(clan, n+1); } + let mut rng = rand::thread_rng(); for (entity, spawner, position, triggerbox) in (&entities, &mut spawners, &positions, &triggerboxes).join() { if triggerbox.has_message(&[Trigger::Spawn]) { if *clan_nums.get(&spawner.clan).unwrap_or(&0) < spawner.amount { @@ -54,7 +58,10 @@ impl <'a> System<'a> for Spawn { Ok(mut preent) => { preent.push(ComponentWrapper::Clan(spawner.clan.clone())); preent.push(ComponentWrapper::Home(Home{home: position.pos})); - new.to_build.push((position.pos, preent)); + let offset = Pos::new( + rng.gen::<i64>()%(spawner.radius*2+1)-spawner.radius, + rng.gen::<i64>()%(spawner.radius*2+1)-spawner.radius); + new.to_build.push((position.pos + offset, preent)); } Err(err) => {println!("Error: can not spawn entity from spawner: {}", err);} } diff --git a/src/template.rs b/src/template.rs index 7422465..da758b6 100644 --- a/src/template.rs +++ b/src/template.rs @@ -2,23 +2,62 @@ use std::collections::HashMap; use serde_json::{json, Value}; +use serde::{Serialize, Deserialize}; + use crate::{ parameter::Parameter, PResult, perr }; -#[derive(Debug, PartialEq, Eq, Clone, Hash)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)] pub struct EntityType(pub String); -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)] +#[serde(from="Option<bool>", into="Option<bool>")] pub enum SaveOption { Default, False, Always } -#[derive(Debug, Clone, PartialEq)] +impl From<Option<bool>> for SaveOption { + fn from(b: Option<bool>) -> Self { + match b { + Some(true) => Self::Always, + Some(false) => Self::False, + None => Self::Default + } + } +} +impl Into<Option<bool>> for SaveOption { + fn into(self) -> Option<bool> { + match self { + Self::Always => Some(true), + Self::False => Some(false), + Self::Default => None + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +enum TemplateSave { + Name(EntityType), + Full{ + #[serde(rename = "type")] + name: EntityType, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + args: Vec<Parameter>, + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + kwargs: HashMap<String, Parameter>, + #[serde(default, skip_serializing_if = "Option::is_none")] + save: Option<bool> + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(from="TemplateSave", into="TemplateSave")] pub struct Template { pub name: EntityType, pub args: Vec<Parameter>, @@ -27,6 +66,36 @@ pub struct Template { } +impl From<TemplateSave> for Template { + fn from(ts: TemplateSave) -> Self { + match ts { + TemplateSave::Name(name) => Self{name, args: Vec::new(), kwargs: HashMap::new(), save: SaveOption::Default}, + TemplateSave::Full{name, args, kwargs, save} => Self{name, args, kwargs, save: match save { + Some(true) => SaveOption::Always, + Some(false) => SaveOption::False, + None => SaveOption::Default + }} + } + } +} +impl Into<TemplateSave> for Template { + fn into(self) -> TemplateSave { + if self.args.is_empty() && self.kwargs.is_empty() && self.save == SaveOption::Default { + return TemplateSave::Name(self.name); + } + TemplateSave::Full { + name: self.name, + args: self.args, + kwargs: self.kwargs, + save: match self.save { + SaveOption::Always => Some(true), + SaveOption::False => Some(false), + SaveOption::Default => None + } + } + } +} + impl Template { pub fn new(name: &str, kwargs: HashMap<String, Parameter>) -> Self { @@ -121,3 +190,23 @@ impl Template { }) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::hashmap; + + + #[test] + fn template_from_string(){ + assert_eq!(Template::deserialize(json!("grass")).unwrap(), Template::empty("grass")); + } + + #[test] + fn template_with_kwarg(){ + assert_eq!( + Template::deserialize(json!({"type": "wall", "kwargs": {"health": 50}})).unwrap(), + Template::new("wall", hashmap!{"health".to_string() => Parameter::Int(50)}) + ); + } +} diff --git a/src/worldloader.rs b/src/worldloader.rs index 8e0622e..8934fc8 100644 --- a/src/worldloader.rs +++ b/src/worldloader.rs @@ -57,8 +57,7 @@ impl WorldLoader { let fname = id.name.splitn(2, '+').next().unwrap().to_string() + ".json"; let path = self.directory.join("maps").join(fname); let text = fs::read_to_string(path)?; - let json: Value = serde_json::from_str(&text)?; - let template = RoomTemplate::from_json(&json)?; + let template = serde_json::from_str(&text)?; Ok(template) } |
