summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml2
-rw-r--r--content/encyclopediae/default_encyclopedia.json5
-rw-r--r--src/components/flags.rs21
-rw-r--r--src/components/mod.rs3
-rw-r--r--src/componentwrapper.rs8
-rw-r--r--src/item.rs4
-rw-r--r--src/parameter.rs17
-rw-r--r--src/pos.rs18
-rw-r--r--src/purgatory.rs3
-rw-r--r--src/resources/mod.rs8
-rw-r--r--src/roomtemplate.rs49
-rw-r--r--src/systems/spawn.rs11
-rw-r--r--src/template.rs95
-rw-r--r--src/worldloader.rs3
14 files changed, 206 insertions, 41 deletions
diff --git a/Cargo.toml b/Cargo.toml
index b91b315..db2e00b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,3 +20,5 @@ structopt = "0.3"
unicode_categories = "0.1.1"
base64 = "0.12.0"
ring = "0.16.12"
+strum = "0.19"
+strum_macros = "0.19"
diff --git a/content/encyclopediae/default_encyclopedia.json b/content/encyclopediae/default_encyclopedia.json
index ca12897..8ea6bcc 100644
--- a/content/encyclopediae/default_encyclopedia.json
+++ b/content/encyclopediae/default_encyclopedia.json
@@ -49,7 +49,7 @@
"save": false
},
"spawner": {
- "arguments": [["template", "template"], ["amount", "int", 1], ["delay", "int", 0], ["clan", "string", ""], ["initial_spawn", "bool", true]],
+ "arguments": [["template", "template"], ["amount", "int", 1], ["delay", "int", 0], ["clan", "string", ""], ["initial_spawn", "bool", true], ["radius", "int", 0]],
"components": [
["Timer", {
"delay": ["arg", "delay"],
@@ -60,7 +60,8 @@
["Spawner", {
"template": ["arg", "template"],
"amount": ["arg", "amount"],
- "clan": ["arg", "clan"]
+ "clan": ["arg", "clan"],
+ "radius": ["arg", "radius"]
}]
]
},
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::*;
diff --git a/src/pos.rs b/src/pos.rs
index 9263561..4c0d9cc 100644
--- a/src/pos.rs
+++ b/src/pos.rs
@@ -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)
}