From 98407d2c7718268c02b4bb4ec1de28da603b6806 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Thu, 23 Dec 2021 11:38:37 -0500 Subject: [PATCH] Spawn items from JSON data --- Cargo.toml | 1 + raws/spawns.json | 113 ++++++++++++++++++++++++++++++++---------- src/main.rs | 10 ++-- src/raws.rs | 12 ++++- src/raws/rawmaster.rs | 111 +++++++++++++++++++++++++++++++++++++++++ src/spawner.rs | 11 ++++ 6 files changed, 228 insertions(+), 30 deletions(-) create mode 100644 src/raws/rawmaster.rs diff --git a/Cargo.toml b/Cargo.toml index 4d0129f..a22fe0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +lazy_static = "1.4.0" rltk = { version = "0.8.0", features=["serde"] } specs = { version = "0.16.1", features=["serde"] } specs-derive = "0.4.1" diff --git a/raws/spawns.json b/raws/spawns.json index 9b6beeb..cf0ec1b 100644 --- a/raws/spawns.json +++ b/raws/spawns.json @@ -1,30 +1,93 @@ { - "items": [{ - "name": "Health Potion", - "renderable": { - "glpyh": "!", - "fg": "#FF00FF", - "bg": "#000000", - "order": 2 + "items": [ + { + "name": "Health Potion", + "renderable": { + "glyph": "!", + "fg": "#FF00FF", + "bg": "#000000", + "order": 2 + }, + "consumable": { + "effects": { + "provides_healing": "8" + } + } }, - "consumable": { - "effects": { - "provides_healing": "8" + { + "name": "Magic Missile Scroll", + "renderable": { + "glyph": ")", + "fg": "#00FFFF", + "bg": "#000000", + "order": 2 + }, + "consumable": { + "effects": { + "ranged": "6", + "damage": "20" + } + } + }, + { + "name": "Fireball Scroll", + "renderable": { + "glyph": ")", + "fg": "#FFA500", + "bg": "#000000", + "order": 2 + }, + "consumable": { + "effects": { + "ranged": "6", + "damage": "20", + "area_of_effect": "3" + } + } + }, + { + "name": "Confusion Scroll", + "renderable": { + "glyph": ")", + "fg": "#FFAAAA", + "bg": "#000000", + "order": 2 + }, + "consumable": { + "effects": { + "ranged": "6", + "damage": "20", + "confusion": "4" + } + } + }, + { + "name": "Magic Mapping Scroll", + "renderable": { + "glyph": ")", + "fg": "#AAAAFF", + "bg": "#000000", + "order": 2 + }, + "consumable": { + "effects": { + "magic_mapping": "" + } + } + }, + { + "name": "Rations", + "renderable": { + "glyph": "%", + "fg": "#00FF00", + "bg": "#000000", + "order": 2 + }, + "consumable": { + "effects": { + "food": "" + } } } - }, { - "name": "Magic Missile Scroll", - "renderable": { - "glyph": ")", - "fg": "#00FFFF", - "bg": "#000000", - "order": 2 - }, - "consumable": { - "effects": { - "ranged": "6", - "damage": "20" - } - } - }] + ] } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 67427a3..d33da51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,3 @@ -use rltk::{GameState, Point, RandomNumberGenerator, Rltk}; -use specs::prelude::*; -use specs::saveload::{SimpleMarker, SimpleMarkerAllocator}; - pub mod camera; mod components; mod damage_system; @@ -25,6 +21,9 @@ mod spawner; mod trigger_system; mod visibility_system; +#[macro_use] +extern crate lazy_static; + use components::*; use damage_system::DamageSystem; pub use game_log::GameLog; @@ -35,6 +34,9 @@ use melee_combat_system::MeleeCombatSystem; use monster_ai_system::MonsterAI; use player::*; pub use rect::Rect; +use rltk::{GameState, Point, RandomNumberGenerator, Rltk}; +use specs::prelude::*; +use specs::saveload::{SimpleMarker, SimpleMarkerAllocator}; use visibility_system::VisibilitySystem; /// Cut down on the amount of syntax to register components diff --git a/src/raws.rs b/src/raws.rs index 49ee9d1..80ae80d 100644 --- a/src/raws.rs +++ b/src/raws.rs @@ -1,10 +1,20 @@ mod item_structs; +mod rawmaster; + +use std::sync::Mutex; use item_structs::Raws; +pub use rawmaster::*; rltk::embedded_resource!(RAW_FILE, "../raws/spawns.json"); +lazy_static! { + pub static ref RAWS: Mutex = Mutex::new(RawMaster::empty()); +} + pub fn load_raws() { + rltk::link_resource!(RAW_FILE, "../raws/spawns.json"); + let raw_data = rltk::embedding::EMBED .lock() .get_resource("../raws/spawns.json".to_string()) @@ -14,5 +24,5 @@ pub fn load_raws() { std::str::from_utf8(&raw_data).expect("Unable to convert to a valid UTF-8 string."); let decoder: Raws = serde_json::from_str(&raw_string).expect("Unable to parse JSON"); - rltk::console::log(format!("{:?}", decoder)); + RAWS.lock().unwrap().load(decoder); } diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs new file mode 100644 index 0000000..a0d005f --- /dev/null +++ b/src/raws/rawmaster.rs @@ -0,0 +1,111 @@ +use std::collections::HashMap; + +use specs::prelude::*; + +use super::Raws; +use crate::components::*; + +pub enum SpawnType { + AtPosition { x: i32, y: i32 }, +} + +pub struct RawMaster { + raws: Raws, + item_index: HashMap, +} + +impl RawMaster { + pub fn empty() -> RawMaster { + RawMaster { + raws: Raws { items: Vec::new() }, + item_index: HashMap::new(), + } + } + + pub fn load(&mut self, raws: Raws) { + self.raws = raws; + self.item_index = HashMap::new(); + + for (i, item) in self.raws.items.iter().enumerate() { + self.item_index.insert(item.name.clone(), i); + } + } +} + +pub fn spawn_named_item( + raws: &RawMaster, + new_entity: EntityBuilder, + key: &str, + pos: SpawnType, +) -> Option { + if raws.item_index.contains_key(key) { + let item_template = &raws.raws.items[raws.item_index[key]]; + + let mut eb = new_entity; + + // Spawn in the specified location + match pos { + SpawnType::AtPosition { x, y } => { + eb = eb.with(Position { x, y }); + } + } + + // Renderable + if let Some(renderable) = &item_template.renderable { + eb = eb.with(Renderable { + glyph: rltk::to_cp437(renderable.glyph.chars().next().unwrap()), + fg: rltk::RGB::from_hex(&renderable.fg).expect("Invalid RGB"), + bg: rltk::RGB::from_hex(&renderable.bg).expect("Invalid RGB"), + render_order: renderable.order, + }); + } + + eb = eb.with(Name::from(&item_template.name)).with(Item {}); + + if let Some(consumable) = &item_template.consumable { + eb = eb.with(Consumable {}); + for effect in consumable.effects.iter() { + let effect_name = effect.0.as_str(); + match effect_name { + "provides_healing" => { + eb = eb.with(ProvidesHealing { + heal_amount: effect.1.parse::().unwrap(), + }) + } + "ranged" => { + eb = eb.with(Ranged { + range: effect.1.parse::().unwrap(), + }) + } + "damage" => { + eb = eb.with(InflictsDamage { + damage: effect.1.parse::().unwrap(), + }) + } + "area_of_effect" => { + eb = eb.with(AreaOfEffect { + radius: effect.1.parse::().unwrap(), + }) + } + "confusion" => { + eb = eb.with(Confusion { + turns: effect.1.parse::().unwrap(), + }) + } + "magic_mapping" => eb = eb.with(MagicMapper {}), + "food" => [eb = eb.with(ProvidesFood {})], + _ => { + rltk::console::log(format!( + "Warning: consumable effect {} not implemented.", + effect_name + )); + } + } + } + } + + return Some(eb.build()); + } + + None +} diff --git a/src/spawner.rs b/src/spawner.rs index 86bb6f2..5b69d34 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -6,6 +6,7 @@ use specs::saveload::{MarkedBuilder, SimpleMarker}; use crate::components::*; use crate::random_table::RandomTable; +use crate::raws::{spawn_named_item, SpawnType, RAWS}; use crate::{Map, Rect, TileType}; /// Spawns the player and returns their entity object @@ -132,6 +133,16 @@ pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) { // Drop this map reference to make the borrow checker happy std::mem::drop(map); + let item_result = spawn_named_item( + &RAWS.lock().unwrap(), + ecs.create_entity(), + &spawn.1, + SpawnType::AtPosition { x, y }, + ); + if item_result.is_some() { + return; + } + match spawn.1.as_ref() { "Goblin" => goblin(ecs, x, y), "Orc" => orc(ecs, x, y),