summaryrefslogtreecommitdiff
path: root/server/src/data.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/data.rs')
-rw-r--r--server/src/data.rs127
1 files changed, 127 insertions, 0 deletions
diff --git a/server/src/data.rs b/server/src/data.rs
new file mode 100644
index 00000000..6affccb5
--- /dev/null
+++ b/server/src/data.rs
@@ -0,0 +1,127 @@
+use crate::{interaction::Recipe, protocol::TileIndex};
+use glam::{IVec2, Vec2};
+use serde::{Deserialize, Serialize};
+use std::{collections::HashMap, sync::RwLock};
+
+#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default)]
+#[serde(rename_all = "snake_case")]
+pub enum Action {
+ #[default]
+ Never,
+ Passive(f32),
+ Active(f32),
+ Instant,
+}
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct RecipeDecl {
+ #[serde(default)]
+ pub tile: Option<String>,
+ #[serde(default)]
+ pub inputs: Vec<String>,
+ #[serde(default)]
+ pub outputs: Vec<String>,
+ #[serde(default)]
+ pub action: Action,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct InitialMap {
+ map: Vec<String>,
+ tiles: HashMap<String, String>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct Gamedata {
+ pub recipes: Vec<Recipe>,
+ pub item_names: Vec<String>,
+ pub tile_names: Vec<String>,
+ #[serde(skip)]
+ pub initial_map: HashMap<IVec2, TileIndex>,
+ pub spawn: Vec2,
+}
+
+pub fn build_gamedata(recipes_in: Vec<RecipeDecl>, map_in: InitialMap) -> Gamedata {
+ let item_names = RwLock::new(Vec::new());
+ let tile_names = RwLock::new(Vec::new());
+ let mut recipes = Vec::new();
+
+ for r in recipes_in {
+ let r2 = r.clone();
+ let mut inputs = r.inputs.into_iter().map(|i| register(&item_names, i));
+ let mut outputs = r.outputs.into_iter().map(|o| register(&item_names, o));
+ let tile = r.tile.map(|t| register(&tile_names, t));
+ match r.action {
+ Action::Never => {}
+ Action::Passive(duration) => recipes.push(Recipe::Passive {
+ duration,
+ tile,
+ input: inputs.next().expect("passive recipe without input"),
+ output: outputs.next(),
+ }),
+ Action::Active(duration) => recipes.push(Recipe::Active {
+ duration,
+ tile,
+ input: inputs.next().expect("active recipe without input"),
+ outputs: [outputs.next(), outputs.next()],
+ }),
+ Action::Instant => {
+ recipes.push(Recipe::Instant {
+ tile,
+ inputs: [inputs.next(), inputs.next()],
+ outputs: [outputs.next(), outputs.next()],
+ });
+ }
+ }
+ assert_eq!(inputs.next(), None, "{r2:?}");
+ assert_eq!(outputs.next(), None, "{r2:?}");
+ }
+
+ let mut spawn = Vec2::new(0., 0.);
+ let mut initial_map = HashMap::new();
+ for (y, line) in map_in.map.iter().enumerate() {
+ for (x, tile) in line.trim().char_indices() {
+ let pos = IVec2::new(x as i32, y as i32);
+ let mut tilename = map_in.tiles[&tile.to_string()].clone();
+ if tilename == "spawn" {
+ spawn = pos.as_vec2();
+ tilename = "floor".to_owned();
+ }
+ let tile = register(&tile_names, tilename);
+ initial_map.insert(pos, tile);
+ }
+ }
+
+ Gamedata {
+ recipes,
+ initial_map,
+ item_names: item_names.into_inner().unwrap(),
+ tile_names: tile_names.into_inner().unwrap(),
+ spawn,
+ }
+}
+
+fn register(db: &RwLock<Vec<String>>, name: String) -> usize {
+ let mut db = db.write().unwrap();
+ if let Some(index) = db.iter().position(|e| e == &name) {
+ index
+ } else {
+ let index = db.len();
+ db.push(name);
+ index
+ }
+}
+
+impl Gamedata {
+ pub fn get_tile(&self, name: &str) -> Option<TileIndex> {
+ self.tile_names.iter().position(|t| t == name)
+ }
+}
+impl Action {
+ pub fn duration(&self) -> f32 {
+ match self {
+ Action::Instant | Action::Never => 0.,
+ Action::Passive(x) | Action::Active(x) => *x,
+ }
+ }
+}