diff --git a/raws/factions.json b/raws/factions.json new file mode 100644 index 0000000..bb31885 --- /dev/null +++ b/raws/factions.json @@ -0,0 +1,26 @@ +[ + { + "id": "player", + "responses": {} + }, + { + "id": "mindless", + "responses": { "default": "attack" } + }, + { + "id": "neutral", + "responses": { "default": "ignore" } + }, + { + "id": "hostile", + "responses": { "default": "attack", "hostile": "ignore" } + }, + { + "id": "herbivore", + "responses": { "default": "flee", "herbivores": "ignore" } + }, + { + "id": "carnivore", + "responses": { "default": "attack", "carnivores": "ignore" } + } +] diff --git a/src/bystander_ai_system.rs b/src/ai/bystander_ai_system.rs similarity index 96% rename from src/bystander_ai_system.rs rename to src/ai/bystander_ai_system.rs index e8d4feb..d87e33d 100644 --- a/src/bystander_ai_system.rs +++ b/src/ai/bystander_ai_system.rs @@ -1,4 +1,4 @@ -use super::{Bystander, EntityMoved, Map, Position, TakingTurn, Viewshed}; +use crate::{Bystander, EntityMoved, Map, Position, TakingTurn, Viewshed}; use specs::prelude::*; pub struct BystanderAI {} diff --git a/src/ai/mod.rs b/src/ai/mod.rs index 88553eb..6c37a5f 100644 --- a/src/ai/mod.rs +++ b/src/ai/mod.rs @@ -8,3 +8,7 @@ mod regen_system; pub use regen_system::RegenSystem; mod encumbrance_system; pub use encumbrance_system::{EncumbranceSystem, CARRY_CAPACITY_PER_STRENGTH}; +mod bystander_ai_system; +pub use bystander_ai_system::BystanderAI; +mod monster_ai_system; +pub use monster_ai_system::MonsterAI; diff --git a/src/monster_ai_system.rs b/src/ai/monster_ai_system.rs similarity index 92% rename from src/monster_ai_system.rs rename to src/ai/monster_ai_system.rs index a3fed20..dbf6829 100644 --- a/src/monster_ai_system.rs +++ b/src/ai/monster_ai_system.rs @@ -1,4 +1,5 @@ -use super::{bystander_ai_system, EntityMoved, Map, Monster, Position, TakingTurn, Viewshed, WantsToMelee}; +use super::bystander_ai_system::try_move_randomly; +use crate::{EntityMoved, Map, Monster, Position, TakingTurn, Viewshed, WantsToMelee}; use rltk::Point; use specs::prelude::*; @@ -60,7 +61,7 @@ impl<'a> System<'a> for MonsterAI { entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker"); } } else { - if bystander_ai_system::try_move_randomly(&mut pos, &mut rng, &mut map, &mut viewshed) { + if try_move_randomly(&mut pos, &mut rng, &mut map, &mut viewshed) { entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker"); } } diff --git a/src/components.rs b/src/components.rs index 217e862..4fcf893 100644 --- a/src/components.rs +++ b/src/components.rs @@ -46,6 +46,11 @@ pub struct Renderable { #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Player {} +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Faction { + pub name: String, +} + #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Prop {} diff --git a/src/main.rs b/src/main.rs index f76df06..1d51263 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,9 +20,6 @@ mod saveload_system; mod spawner; mod visibility_system; use visibility_system::VisibilitySystem; -mod monster_ai_system; -use monster_ai_system::MonsterAI; -pub mod bystander_ai_system; mod map_indexing_system; use map_indexing_system::MapIndexingSystem; mod damage_system; @@ -99,8 +96,8 @@ impl State { let mut encumbrance_system = ai::EncumbranceSystem {}; let mut turn_status_system = ai::TurnStatusSystem {}; let mut quip_system = ai::QuipSystem {}; - let mut mob = MonsterAI {}; - let mut bystanders = bystander_ai_system::BystanderAI {}; + let mut mob = ai::MonsterAI {}; + let mut bystanders = ai::BystanderAI {}; let mut trigger_system = trigger_system::TriggerSystem {}; let mut melee_system = MeleeCombatSystem {}; let mut damage_system = DamageSystem {}; @@ -520,6 +517,7 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); diff --git a/src/raws/faction_structs.rs b/src/raws/faction_structs.rs new file mode 100644 index 0000000..83df4b7 --- /dev/null +++ b/src/raws/faction_structs.rs @@ -0,0 +1,15 @@ +use serde::Deserialize; +use std::collections::HashMap; + +#[derive(Deserialize, Debug)] +pub struct FactionData { + pub id: String, + pub responses: HashMap, +} + +#[derive(PartialEq, Eq, Hash, Copy, Clone)] +pub enum Reaction { + Ignore, + Attack, + Flee, +} diff --git a/src/raws/mod.rs b/src/raws/mod.rs index 3b0dfee..7982146 100644 --- a/src/raws/mod.rs +++ b/src/raws/mod.rs @@ -11,6 +11,8 @@ mod spawn_table_structs; use spawn_table_structs::*; mod loot_table_structs; use loot_table_structs::*; +mod faction_structs; +use faction_structs::{FactionData, Reaction}; use std::sync::Mutex; lazy_static! { @@ -24,6 +26,7 @@ pub struct Raws { pub props: Vec, pub spawn_tables: Vec, pub loot_tables: Vec, + pub factions: Vec, } rltk::embedded_resource!(RAW_ITEMS, "../../raws/items.json"); @@ -31,6 +34,7 @@ rltk::embedded_resource!(RAW_MOBS, "../../raws/mobs.json"); rltk::embedded_resource!(RAW_PROPS, "../../raws/props.json"); rltk::embedded_resource!(RAW_SPAWN_TABLES, "../../raws/spawn_tables.json"); rltk::embedded_resource!(RAW_LOOT_TABLES, "../../raws/loot_tables.json"); +rltk::embedded_resource!(RAW_FACTIONS, "../../raws/factions.json"); pub fn load_raws() { rltk::link_resource!(RAW_ITEMS, "../../raws/items.json"); @@ -38,6 +42,7 @@ pub fn load_raws() { rltk::link_resource!(RAW_PROPS, "../../raws/props.json"); rltk::link_resource!(RAW_SPAWN_TABLES, "../../raws/spawn_tables.json"); rltk::link_resource!(RAW_LOOT_TABLES, "../../raws/loot_tables.json"); + rltk::link_resource!(RAW_FACTIONS, "../../raws/factions.json"); let decoded_raws = get_decoded_raws(); RAWS.lock().unwrap().load(decoded_raws); @@ -49,8 +54,9 @@ pub fn get_decoded_raws() -> Raws { let props: Vec = ParseJson::parse_raws_into_vector("../../raws/props.json".to_string()); let spawn_tables: Vec = ParseJson::parse_raws_into_vector("../../raws/spawn_tables.json".to_string()); let loot_tables: Vec = ParseJson::parse_raws_into_vector("../../raws/loot_tables.json".to_string()); + let factions: Vec = ParseJson::parse_raws_into_vector("../../raws/factions.json".to_string()); - return Raws { items, mobs, props, spawn_tables, loot_tables }; + return Raws { items, mobs, props, spawn_tables, loot_tables, factions }; } trait ParseJson { @@ -67,4 +73,4 @@ macro_rules! impl_ParseJson { })* } } -impl_ParseJson!(for Vec, Vec, Vec, Vec, Vec); +impl_ParseJson!(for Vec, Vec, Vec, Vec, Vec, Vec); diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 8002e3f..94be7c2 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -1,4 +1,4 @@ -use super::Raws; +use super::{Raws, Reaction}; use crate::components::*; use crate::gamesystem::*; use crate::random_table::RandomTable; @@ -22,6 +22,7 @@ pub struct RawMaster { prop_index: HashMap, table_index: HashMap, loot_index: HashMap, + faction_index: HashMap>, } impl RawMaster { @@ -33,12 +34,14 @@ impl RawMaster { props: Vec::new(), spawn_tables: Vec::new(), loot_tables: Vec::new(), + factions: Vec::new(), }, item_index: HashMap::new(), mob_index: HashMap::new(), prop_index: HashMap::new(), table_index: HashMap::new(), loot_index: HashMap::new(), + faction_index: HashMap::new(), } } @@ -75,6 +78,20 @@ impl RawMaster { check_for_unspecified_entity(&used_names, &entry.id) } } + for faction in self.raws.factions.iter() { + let mut reactions: HashMap = HashMap::new(); + for other in faction.responses.iter() { + reactions.insert( + other.0.clone(), + match other.1.as_str() { + "flee" => Reaction::Flee, + "attack" => Reaction::Attack, + _ => Reaction::Ignore, + }, + ); + } + self.faction_index.insert(faction.id.clone(), reactions); + } } } @@ -279,7 +296,14 @@ pub fn spawn_named_mob( "BLOCKS_TILE" => eb = eb.with(BlocksTile {}), "BYSTANDER" => eb = eb.with(Bystander {}), "MONSTER" => eb = eb.with(Monster {}), - "MINDLESS" => has_mind = false, + "MINDLESS" => { + eb = eb.with(Faction { name: "mindless".to_string() }); + has_mind = false; + } + "NEUTRAL" => eb = eb.with(Faction { name: "neutral".to_string() }), + "HOSTILE" => eb = eb.with(Faction { name: "hostile".to_string() }), + "HERBIVORE" => eb = eb.with(Faction { name: "herbivore".to_string() }), + "CARNIVORE" => eb = eb.with(Faction { name: "carnivore".to_string() }), "SMALL_GROUP" => {} // These flags are for region spawning, "LARGE_GROUP" => {} // and don't matter here (yet)? "MULTIATTACK" => { @@ -738,3 +762,16 @@ pub fn is_tag_magic(tag: &str) -> bool { return false; } } + +/// Queries the faction index to obtain one faction's reaction to another faction. +pub fn faction_reaction(this_faction: &str, other_faction: &str, raws: &RawMaster) -> Reaction { + if raws.faction_index.contains_key(this_faction) { + let mine = &raws.faction_index[this_faction]; + if mine.contains_key(other_faction) { + return mine[other_faction]; + } else if mine.contains_key("default") { + return mine["default"]; + } + } + return Reaction::Ignore; +} diff --git a/src/saveload_system.rs b/src/saveload_system.rs index a0506f0..31491c7 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -70,6 +70,7 @@ pub fn save_game(ecs: &mut World) { EquipmentChanged, Equippable, Equipped, + Faction, GrantsXP, Hidden, HungerClock, @@ -185,6 +186,7 @@ pub fn load_game(ecs: &mut World) { EquipmentChanged, Equippable, Equipped, + Faction, GrantsXP, Hidden, HungerClock, diff --git a/src/spawner.rs b/src/spawner.rs index fe8bf79..1fee052 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,6 +1,6 @@ use super::{ ai::NORMAL_SPEED, gamesystem, gamesystem::attr_bonus, random_table::RandomTable, raws, Attribute, Attributes, - Clock, Energy, EquipmentChanged, HungerClock, HungerState, Map, Name, Player, Pool, Pools, Position, Rect, + Clock, Energy, EquipmentChanged, Faction, HungerClock, HungerState, Map, Name, Player, Pool, Pools, Position, Rect, Renderable, SerializeMe, Skill, Skills, TileType, Viewshed, }; use rltk::{RandomNumberGenerator, RGB}; @@ -36,6 +36,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { render_order: 0, }) .with(Player {}) + .with(Faction { name: "player".to_string() }) .with(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true }) .with(Name { name: "you".to_string(), plural: "you".to_string() }) .with(HungerClock { state: HungerState::Satiated, duration: 200 })