From 3050219494dcfe2d57758c3f5b6fa60ea15fc240 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Mon, 21 Aug 2023 09:57:47 +0100 Subject: [PATCH] ancestries --- raws/ancestries.json | 22 +++ src/ai/adjacent_ai_system.rs | 51 ++++--- src/ai/visible_ai_system.rs | 50 +++++-- src/components.rs | 12 ++ src/gamesystem.rs | 20 +-- src/gui/race_selection.rs | 134 +++++++++++------- src/main.rs | 22 +-- src/raws/mod.rs | 14 +- src/raws/rawmaster.rs | 47 ++++++ ...faction_structs.rs => reaction_structs.rs} | 8 +- src/saveload_system.rs | 4 + 11 files changed, 274 insertions(+), 110 deletions(-) create mode 100644 raws/ancestries.json rename src/raws/{faction_structs.rs => reaction_structs.rs} (61%) diff --git a/raws/ancestries.json b/raws/ancestries.json new file mode 100644 index 0000000..41c837e --- /dev/null +++ b/raws/ancestries.json @@ -0,0 +1,22 @@ +[ + { + "id": "human", + "allied": [] + }, + { + "id": "elf", + "allied": [] + }, + { + "id": "dwarf", + "allied": ["gnome"] + }, + { + "id": "gnome", + "allied": ["dwarf"] + }, + { + "id": "catfolk", + "allied": [] + } +] diff --git a/src/ai/adjacent_ai_system.rs b/src/ai/adjacent_ai_system.rs index 0312c8d..0f9bd3a 100644 --- a/src/ai/adjacent_ai_system.rs +++ b/src/ai/adjacent_ai_system.rs @@ -1,4 +1,4 @@ -use crate::{raws::Reaction, Faction, Map, Position, TakingTurn, WantsToMelee}; +use crate::{raws::Reaction, Faction, HasAncestry, Map, Position, TakingTurn, WantsToMelee}; use specs::prelude::*; pub struct AdjacentAI {} @@ -8,6 +8,7 @@ impl<'a> System<'a> for AdjacentAI { type SystemData = ( WriteStorage<'a, TakingTurn>, ReadStorage<'a, Faction>, + ReadStorage<'a, HasAncestry>, ReadStorage<'a, Position>, ReadExpect<'a, Map>, WriteStorage<'a, WantsToMelee>, @@ -16,7 +17,7 @@ impl<'a> System<'a> for AdjacentAI { ); fn run(&mut self, data: Self::SystemData) { - let (mut turns, factions, positions, map, mut want_melee, entities, player) = data; + let (mut turns, factions, ancestries, positions, map, mut want_melee, entities, player) = data; let mut turn_done: Vec = Vec::new(); for (entity, _turn, my_faction, pos) in (&entities, &turns, &factions, &positions).join() { @@ -27,28 +28,28 @@ impl<'a> System<'a> for AdjacentAI { let h = map.height; // Add possible reactions to adjacents for each direction if pos.x > 0 { - evaluate(idx - 1, &factions, &my_faction.name, &mut reactions); + evaluate(entity, idx - 1, &ancestries, &factions, &my_faction.name, &mut reactions); } if pos.x < w - 1 { - evaluate(idx + 1, &factions, &my_faction.name, &mut reactions); + evaluate(entity, idx + 1, &ancestries, &factions, &my_faction.name, &mut reactions); } if pos.y > 0 { - evaluate(idx - w as usize, &factions, &my_faction.name, &mut reactions); + evaluate(entity, idx - w as usize, &ancestries, &factions, &my_faction.name, &mut reactions); } if pos.y < h - 1 { - evaluate(idx + w as usize, &factions, &my_faction.name, &mut reactions); + evaluate(entity, idx + w as usize, &ancestries, &factions, &my_faction.name, &mut reactions); } if pos.y > 0 && pos.x > 0 { - evaluate((idx - w as usize) - 1, &factions, &my_faction.name, &mut reactions); + evaluate(entity, (idx - w as usize) - 1, &ancestries, &factions, &my_faction.name, &mut reactions); } if pos.y > 0 && pos.x < w - 1 { - evaluate((idx - w as usize) + 1, &factions, &my_faction.name, &mut reactions); + evaluate(entity, (idx - w as usize) + 1, &ancestries, &factions, &my_faction.name, &mut reactions); } if pos.y < h - 1 && pos.x > 0 { - evaluate((idx + w as usize) - 1, &factions, &my_faction.name, &mut reactions); + evaluate(entity, (idx + w as usize) - 1, &ancestries, &factions, &my_faction.name, &mut reactions); } if pos.y < h - 1 && pos.x < w - 1 { - evaluate((idx + w as usize) + 1, &factions, &my_faction.name, &mut reactions); + evaluate(entity, (idx + w as usize) + 1, &ancestries, &factions, &my_faction.name, &mut reactions); } let mut done = false; @@ -73,13 +74,31 @@ impl<'a> System<'a> for AdjacentAI { } /// Evaluates all possible reactions between this faction and all entities on a given tile idx. -fn evaluate(idx: usize, factions: &ReadStorage, this_faction: &str, reactions: &mut Vec<(Entity, Reaction)>) { +fn evaluate( + entity: Entity, + idx: usize, + ancestries: &ReadStorage, + factions: &ReadStorage, + this_faction: &str, + reactions: &mut Vec<(Entity, Reaction)>, +) { crate::spatial::for_each_tile_content(idx, |other_entity| { - if let Some(faction) = factions.get(other_entity) { - reactions.push(( - other_entity, - crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()), - )); + let mut shared_ancestry = false; + if let Some(this_ancestry) = ancestries.get(entity) { + if let Some(other_ancestry) = ancestries.get(other_entity) { + if this_ancestry.name == other_ancestry.name { + reactions.push((other_entity, Reaction::Ignore)); + shared_ancestry = true; + } + } + } + if !shared_ancestry { + if let Some(faction) = factions.get(other_entity) { + reactions.push(( + other_entity, + crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()), + )); + } } }); } diff --git a/src/ai/visible_ai_system.rs b/src/ai/visible_ai_system.rs index 61863ee..2140446 100644 --- a/src/ai/visible_ai_system.rs +++ b/src/ai/visible_ai_system.rs @@ -1,5 +1,6 @@ use crate::{ - raws::Reaction, Chasing, Faction, Map, Mind, Position, TakingTurn, Telepath, Viewshed, WantsToApproach, WantsToFlee, + raws::Reaction, Chasing, Faction, HasAncestry, Map, Mind, Position, TakingTurn, Telepath, Viewshed, + WantsToApproach, WantsToFlee, }; use specs::prelude::*; use std::collections::HashSet; @@ -11,6 +12,7 @@ impl<'a> System<'a> for VisibleAI { type SystemData = ( ReadStorage<'a, TakingTurn>, ReadStorage<'a, Faction>, + ReadStorage<'a, HasAncestry>, ReadStorage<'a, Position>, ReadExpect<'a, Map>, WriteStorage<'a, WantsToApproach>, @@ -27,6 +29,7 @@ impl<'a> System<'a> for VisibleAI { let ( turns, factions, + ancestries, positions, map, mut wants_to_approach, @@ -50,7 +53,7 @@ impl<'a> System<'a> for VisibleAI { for visible_tile in viewshed.visible_tiles.iter() { let idx = map.xy_idx(visible_tile.x, visible_tile.y); if this_idx != idx { - evaluate(idx, &factions, &faction.name, &mut reactions, None); + evaluate(entity, idx, &ancestries, &factions, &faction.name, &mut reactions, None); idxs.insert(idx); } } @@ -61,7 +64,7 @@ impl<'a> System<'a> for VisibleAI { // and it's not the idx we're standing on, then evaluate here w/ minds taken into // account. if this_idx != idx && idxs.contains(&idx) { - evaluate(idx, &factions, &faction.name, &mut reactions, Some(&minds)); + evaluate(entity, idx, &ancestries, &factions, &faction.name, &mut reactions, Some(&minds)); } } } @@ -89,17 +92,42 @@ impl<'a> System<'a> for VisibleAI { } fn evaluate( + entity: Entity, idx: usize, + ancestries: &ReadStorage, factions: &ReadStorage, this_faction: &str, reactions: &mut Vec<(usize, Reaction, Entity)>, minds: Option<&ReadStorage>, ) { crate::spatial::for_each_tile_content(idx, |other_entity| { - // If minds are passed, we assume we're using telepathy here, - // so if the other entity is mindless, we skip it. - if minds.is_some() { - if minds.unwrap().get(other_entity).is_some() { + let mut shared_ancestry = false; + if let Some(this_ancestry) = ancestries.get(entity) { + if let Some(other_ancestry) = ancestries.get(other_entity) { + if this_ancestry.name == other_ancestry.name { + reactions.push((idx, Reaction::Ignore, other_entity)); + shared_ancestry = true; + } + } + } + if !shared_ancestry { + // If minds are passed, we assume we're using telepathy here, + // so if the other entity is mindless, we skip it. + if minds.is_some() { + if minds.unwrap().get(other_entity).is_some() { + if let Some(faction) = factions.get(other_entity) { + reactions.push(( + idx, + crate::raws::faction_reaction( + this_faction, + &faction.name, + &crate::raws::RAWS.lock().unwrap(), + ), + other_entity, + )); + } + } + } else { if let Some(faction) = factions.get(other_entity) { reactions.push(( idx, @@ -108,14 +136,6 @@ fn evaluate( )); } } - } else { - if let Some(faction) = factions.get(other_entity) { - reactions.push(( - idx, - crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()), - other_entity, - )); - } } }); } diff --git a/src/components.rs b/src/components.rs index 19a5ca6..c34860c 100644 --- a/src/components.rs +++ b/src/components.rs @@ -1,3 +1,5 @@ +use crate::gui::Ancestry; +use crate::gui::Class; use rltk::RGB; use serde::{Deserialize, Serialize}; use specs::error::NoError; @@ -142,6 +144,16 @@ pub struct HungerClock { #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct ProvidesNutrition {} +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct HasAncestry { + pub name: Ancestry, +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct HasClass { + pub name: Class, +} + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Pool { pub max: i32, diff --git a/src/gamesystem.rs b/src/gamesystem.rs index 05e31f9..884836b 100644 --- a/src/gamesystem.rs +++ b/src/gamesystem.rs @@ -1,5 +1,5 @@ use super::{Skill, Skills}; -use crate::gui::Classes; +use crate::gui::Class; use rltk::prelude::*; use std::cmp::max; @@ -77,19 +77,19 @@ pub fn roll_4d6(rng: &mut rltk::RandomNumberGenerator) -> i32 { } /// Handles stat distribution for a player character. -pub fn get_attribute_rolls(rng: &mut RandomNumberGenerator, class: Classes) -> (i32, i32, i32, i32, i32, i32) { +pub fn get_attribute_rolls(rng: &mut RandomNumberGenerator, class: Class) -> (i32, i32, i32, i32, i32, i32) { let (mut str, mut dex, mut con, mut int, mut wis, mut cha) = match class { - Classes::Fighter => (10, 8, 10, 6, 6, 8), - Classes::Rogue => (8, 10, 8, 6, 8, 10), - Classes::Wizard => (6, 8, 6, 10, 10, 8), - Classes::Villager => (6, 6, 6, 6, 6, 6), + Class::Fighter => (10, 8, 10, 6, 6, 8), + Class::Rogue => (8, 10, 8, 6, 8, 10), + Class::Wizard => (6, 8, 6, 10, 10, 8), + Class::Villager => (6, 6, 6, 6, 6, 6), }; let remaining_points = 75 - (str + dex + con + int + wis + cha); let improve_chance: [i32; 6] = match class { - Classes::Fighter => [30, 20, 30, 6, 7, 7], - Classes::Rogue => [18, 30, 20, 9, 8, 15], - Classes::Wizard => [10, 15, 20, 30, 15, 10], - Classes::Villager => [15, 15, 25, 15, 15, 15], + Class::Fighter => [30, 20, 30, 6, 7, 7], + Class::Rogue => [18, 30, 20, 9, 8, 15], + Class::Wizard => [10, 15, 20, 30, 15, 10], + Class::Villager => [15, 15, 25, 15, 15, 15], }; let improve_table = crate::random_table::RandomTable::new() .add("Strength", improve_chance[0]) diff --git a/src/gui/race_selection.rs b/src/gui/race_selection.rs index e24808a..abe49a4 100644 --- a/src/gui/race_selection.rs +++ b/src/gui/race_selection.rs @@ -1,25 +1,36 @@ use super::{gamesystem::attr_bonus, gamesystem::get_attribute_rolls, Attributes, Pools, Renderable, RunState, State}; -use crate::{ai::NORMAL_SPEED, raws, Attribute, Energy, Pool, Skill, Skills, Telepath}; +use crate::{ai::NORMAL_SPEED, raws, Attribute, Energy, HasAncestry, HasClass, Pool, Skill, Skills, Telepath}; use rltk::prelude::*; +use serde::{Deserialize, Serialize}; use specs::prelude::*; use std::collections::HashMap; -#[derive(PartialEq, Copy, Clone)] -pub enum Races { +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] +pub enum Ancestry { NULL, Human, Dwarf, + Gnome, Elf, + Catfolk, +} + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] +pub enum Class { + Fighter, + Rogue, + Wizard, + Villager, } lazy_static! { - static ref RACE_CLASS_DATA: HashMap> = { + static ref ANCESTRY_CLASS_DATA: HashMap> = { let mut m = HashMap::new(); - // Races + // Ancestry m.insert( "human".to_string(), vec![ - "+nothing".to_string()]); + "nothing".to_string()]); m.insert( "dwarf".to_string(), vec![ @@ -29,7 +40,12 @@ lazy_static! { vec![ "minor telepathy".to_string(), "a slightly increased speed".to_string()]); - // Classes + m.insert( + "catfolk".to_string(), + vec![ + "increased speed".to_string(), + "increased unarmed damage".to_string()]); + // Class m.insert( "fighter".to_string(), vec![ @@ -58,18 +74,10 @@ lazy_static! { }; } -#[derive(PartialEq, Copy, Clone)] -pub enum Classes { - Fighter, - Rogue, - Wizard, - Villager, -} - #[derive(PartialEq, Copy, Clone)] pub enum CharCreateResult { - NoSelection { race: Races, class: Classes }, - Selected { race: Races, class: Classes }, + NoSelection { ancestry: Ancestry, class: Class }, + Selected { ancestry: Ancestry, class: Class }, } /// Handles the player character creation screen. @@ -83,102 +91,110 @@ pub fn character_creation(gs: &mut State, ctx: &mut Rltk) -> CharCreateResult { ctx.print_color(x, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Who are you? [Aa-Zz]"); y += 2; - if let RunState::CharacterCreation { race, class } = *runstate { + if let RunState::CharacterCreation { ancestry, class } = *runstate { let selected_fg = RGB::named(GREEN); let unselected_fg = RGB::named(WHITE); let mut fg; let bg = RGB::named(BLACK); - // Races + // Ancestry ctx.print_color(x, y, bg, unselected_fg, "Ancestry"); ctx.print_color(x + column_width, y, bg, unselected_fg, "Class"); y += 1; let mut race_str = "human"; - if race == Races::Human { + if ancestry == Ancestry::Human { fg = selected_fg; } else { fg = unselected_fg; } ctx.print_color(x, y, fg, bg, "h. Human"); - if race == Races::Elf { + if ancestry == Ancestry::Elf { fg = selected_fg; race_str = "elf"; } else { fg = unselected_fg; } ctx.print_color(x, y + 1, fg, bg, "e. Elf"); - if race == Races::Dwarf { + if ancestry == Ancestry::Dwarf { fg = selected_fg; race_str = "dwarf"; } else { fg = unselected_fg; } ctx.print_color(x, y + 2, fg, bg, "d. Dwarf"); - // Classes + if ancestry == Ancestry::Catfolk { + fg = selected_fg; + race_str = "catfolk"; + } else { + fg = unselected_fg; + } + ctx.print_color(x, y + 3, fg, bg, "c. Catfolk"); + // Class let mut class_str = "fighter"; x += column_width; - if class == Classes::Fighter { + if class == Class::Fighter { fg = selected_fg; } else { fg = unselected_fg; } ctx.print_color(x, y, fg, bg, "f. Fighter"); - if class == Classes::Rogue { + if class == Class::Rogue { fg = selected_fg; class_str = "rogue"; } else { fg = unselected_fg; } ctx.print_color(x, y + 1, fg, bg, "r. Rogue"); - if class == Classes::Wizard { + if class == Class::Wizard { fg = selected_fg; class_str = "wizard"; } else { fg = unselected_fg; } ctx.print_color(x, y + 2, fg, bg, "w. Wizard"); - if class == Classes::Villager { + if class == Class::Villager { fg = selected_fg; class_str = "villager"; } else { fg = unselected_fg; } ctx.print_color(x, y + 3, fg, bg, "v. Villager"); - // Selected race/class benefits + // Selected ancestry/class benefits x += column_width; ctx.print_color(x, y, selected_fg, bg, "Your ancestry grants..."); - for line in RACE_CLASS_DATA.get(race_str).unwrap().iter() { + for line in ANCESTRY_CLASS_DATA.get(race_str).unwrap().iter() { y += 1; ctx.print_color(x + 1, y, unselected_fg, bg, line); } y += 2; ctx.print_color(x, y, selected_fg, bg, "Your class grants..."); - for line in RACE_CLASS_DATA.get(class_str).unwrap().iter() { + for line in ANCESTRY_CLASS_DATA.get(class_str).unwrap().iter() { y += 1; ctx.print_color(x + 1, y, unselected_fg, bg, line); } match ctx.key { - None => return CharCreateResult::NoSelection { race, class }, + None => return CharCreateResult::NoSelection { ancestry, class }, Some(key) => match key { - VirtualKeyCode::Escape => return CharCreateResult::Selected { race: Races::NULL, class }, - VirtualKeyCode::Return => return CharCreateResult::Selected { race, class }, - VirtualKeyCode::H => return CharCreateResult::NoSelection { race: Races::Human, class }, - VirtualKeyCode::E => return CharCreateResult::NoSelection { race: Races::Elf, class }, - VirtualKeyCode::D => return CharCreateResult::NoSelection { race: Races::Dwarf, class }, - VirtualKeyCode::F => return CharCreateResult::NoSelection { race, class: Classes::Fighter }, - VirtualKeyCode::R => return CharCreateResult::NoSelection { race, class: Classes::Rogue }, - VirtualKeyCode::W => return CharCreateResult::NoSelection { race, class: Classes::Wizard }, - VirtualKeyCode::V => return CharCreateResult::NoSelection { race, class: Classes::Villager }, - _ => return CharCreateResult::NoSelection { race, class }, + VirtualKeyCode::Escape => return CharCreateResult::Selected { ancestry: Ancestry::NULL, class }, + VirtualKeyCode::Return => return CharCreateResult::Selected { ancestry, class }, + VirtualKeyCode::H => return CharCreateResult::NoSelection { ancestry: Ancestry::Human, class }, + VirtualKeyCode::E => return CharCreateResult::NoSelection { ancestry: Ancestry::Elf, class }, + VirtualKeyCode::D => return CharCreateResult::NoSelection { ancestry: Ancestry::Dwarf, class }, + VirtualKeyCode::C => return CharCreateResult::NoSelection { ancestry: Ancestry::Catfolk, class }, + VirtualKeyCode::F => return CharCreateResult::NoSelection { ancestry, class: Class::Fighter }, + VirtualKeyCode::R => return CharCreateResult::NoSelection { ancestry, class: Class::Rogue }, + VirtualKeyCode::W => return CharCreateResult::NoSelection { ancestry, class: Class::Wizard }, + VirtualKeyCode::V => return CharCreateResult::NoSelection { ancestry, class: Class::Villager }, + _ => return CharCreateResult::NoSelection { ancestry, class }, }, } } - return CharCreateResult::NoSelection { race: Races::Human, class: Classes::Fighter }; + return CharCreateResult::NoSelection { ancestry: Ancestry::Human, class: Class::Fighter }; } -/// Handles player race setup. -pub fn setup_player_race(ecs: &mut World, race: Races) { +/// Handles player ancestry setup. +pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) { let player = ecs.fetch::(); let mut renderables = ecs.write_storage::(); // SKILLS @@ -189,9 +205,11 @@ pub fn setup_player_race(ecs: &mut World, race: Races) { skills.insert(*player, Skills { skills: HashMap::new() }).expect("Unable to insert skills component"); skills.get_mut(*player).unwrap() }; - match race { - Races::Human => {} - Races::Dwarf => { + let mut ancestries = ecs.write_storage::(); + ancestries.insert(*player, HasAncestry { name: ancestry }).expect("Unable to insert ancestry"); + match ancestry { + Ancestry::Human => {} + Ancestry::Dwarf => { renderables .insert( *player, @@ -205,7 +223,7 @@ pub fn setup_player_race(ecs: &mut World, race: Races) { .expect("Unable to insert renderable component"); *player_skills.skills.entry(Skill::Defence).or_insert(0) += 1; } - Races::Elf => { + Ancestry::Elf => { renderables .insert( *player, @@ -226,15 +244,23 @@ pub fn setup_player_race(ecs: &mut World, race: Races) { .insert(*player, Energy { current: 0, speed: NORMAL_SPEED + 1 }) .expect("Unable to insert energy component"); } + Ancestry::Catfolk => { + let mut speeds = ecs.write_storage::(); + speeds + .insert(*player, Energy { current: 0, speed: NORMAL_SPEED + 2 }) + .expect("Unable to insert energy component"); + } _ => {} } } /// Handles player class setup -pub fn setup_player_class(ecs: &mut World, class: Classes) { +pub fn setup_player_class(ecs: &mut World, class: Class) { let player = *ecs.fetch::(); // ATTRIBUTES { + let mut classes = ecs.write_storage::(); + classes.insert(player, HasClass { name: class }).expect("Unable to insert class component"); let mut rng = ecs.write_resource::(); let mut attributes = ecs.write_storage::(); @@ -281,12 +307,12 @@ pub fn setup_player_class(ecs: &mut World, class: Classes) { } } -fn get_starting_inventory(class: Classes, rng: &mut RandomNumberGenerator) -> (Vec, Vec) { +fn get_starting_inventory(class: Class, rng: &mut RandomNumberGenerator) -> (Vec, Vec) { let mut equipped: Vec = Vec::new(); let mut carried: Vec = Vec::new(); let starting_food: &str; match class { - Classes::Fighter => { + Class::Fighter => { starting_food = "1d2+1"; equipped = vec![ "equip_shortsword".to_string(), @@ -294,18 +320,18 @@ fn get_starting_inventory(class: Classes, rng: &mut RandomNumberGenerator) -> (V "equip_mediumshield".to_string(), ]; } - Classes::Rogue => { + Class::Rogue => { starting_food = "1d2+2"; equipped = vec!["equip_rapier".to_string(), "equip_body_weakleather".to_string()]; carried = vec!["equip_dagger".to_string(), "equip_dagger".to_string()]; } - Classes::Wizard => { + Class::Wizard => { starting_food = "1d2+1"; equipped = vec!["equip_dagger".to_string(), "equip_back_protection".to_string()]; pick_random_table_item(rng, &mut carried, "scrolls", "1d3", Some(3)); pick_random_table_item(rng, &mut carried, "potions", "1d3-1", Some(3)); } - Classes::Villager => { + Class::Villager => { starting_food = "1d3+2"; pick_random_table_item(rng, &mut equipped, "villager_equipment", "1", None); } diff --git a/src/main.rs b/src/main.rs index bf3113e..1522a6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,7 +55,7 @@ pub enum RunState { ShowTargeting { range: i32, item: Entity, aoe: i32 }, ActionWithDirection { function: fn(i: i32, j: i32, ecs: &mut World) -> RunState }, MainMenu { menu_selection: gui::MainMenuSelection }, - CharacterCreation { race: gui::Races, class: gui::Classes }, + CharacterCreation { ancestry: gui::Ancestry, class: gui::Class }, SaveGame, GameOver, NextLevel, @@ -383,8 +383,10 @@ impl GameState for State { } gui::MainMenuResult::Selected { selected } => match selected { gui::MainMenuSelection::NewGame => { - new_runstate = - RunState::CharacterCreation { race: gui::Races::Human, class: gui::Classes::Fighter } + new_runstate = RunState::CharacterCreation { + ancestry: gui::Ancestry::Human, + class: gui::Class::Fighter, + } } gui::MainMenuSelection::LoadGame => { saveload_system::load_game(&mut self.ecs); @@ -400,14 +402,14 @@ impl GameState for State { RunState::CharacterCreation { .. } => { let result = gui::character_creation(self, ctx); match result { - gui::CharCreateResult::NoSelection { race, class } => { - new_runstate = RunState::CharacterCreation { race, class } + gui::CharCreateResult::NoSelection { ancestry, class } => { + new_runstate = RunState::CharacterCreation { ancestry, class } } - gui::CharCreateResult::Selected { race, class } => { - if race == gui::Races::NULL { + gui::CharCreateResult::Selected { ancestry, class } => { + if ancestry == gui::Ancestry::NULL { new_runstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame }; } else { - gui::setup_player_race(&mut self.ecs, race); + gui::setup_player_ancestry(&mut self.ecs, ancestry); gui::setup_player_class(&mut self.ecs, class); new_runstate = RunState::PreRun; } @@ -550,6 +552,8 @@ 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::(); gs.ecs.register::(); @@ -618,7 +622,7 @@ fn main() -> rltk::BError { gs.ecs.insert(map::MasterDungeonMap::new()); // Master map list gs.ecs.insert(Map::new(1, 64, 64, 0, "New Map")); // Map gs.ecs.insert(Point::new(0, 0)); // Player pos - gs.ecs.insert(gui::Races::Dwarf); // Race + gs.ecs.insert(gui::Ancestry::Dwarf); // ancestry let player_entity = spawner::player(&mut gs.ecs, 0, 0); gs.ecs.insert(player_entity); // Player entity gs.ecs.insert(RunState::MapGeneration {}); // RunState diff --git a/src/raws/mod.rs b/src/raws/mod.rs index 6316075..66c9c83 100644 --- a/src/raws/mod.rs +++ b/src/raws/mod.rs @@ -11,9 +11,9 @@ mod spawn_table_structs; use spawn_table_structs::*; mod loot_table_structs; use loot_table_structs::*; -mod faction_structs; -use faction_structs::FactionData; -pub use faction_structs::Reaction; +mod reaction_structs; +pub use reaction_structs::Reaction; +use reaction_structs::{AncestryData, FactionData}; use std::sync::Mutex; lazy_static! { @@ -28,6 +28,7 @@ pub struct Raws { pub spawn_tables: Vec, pub loot_tables: Vec, pub factions: Vec, + pub ancestries: Vec, } rltk::embedded_resource!(RAW_ITEMS, "../../raws/items.json"); @@ -36,6 +37,7 @@ 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"); +rltk::embedded_resource!(RAW_ANCESTRIES, "../../raws/ancestries.json"); pub fn load_raws() { rltk::link_resource!(RAW_ITEMS, "../../raws/items.json"); @@ -44,6 +46,7 @@ pub fn load_raws() { 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"); + rltk::link_resource!(RAW_ANCESTRIES, "../../raws/ancestries.json"); let decoded_raws = get_decoded_raws(); RAWS.lock().unwrap().load(decoded_raws); @@ -56,8 +59,9 @@ pub fn get_decoded_raws() -> Raws { 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()); + let ancestries: Vec = ParseJson::parse_raws_into_vector("../../raws/ancestries.json".to_string()); - return Raws { items, mobs, props, spawn_tables, loot_tables, factions }; + return Raws { items, mobs, props, spawn_tables, loot_tables, factions, ancestries }; } trait ParseJson { @@ -74,4 +78,4 @@ macro_rules! impl_ParseJson { })* } } -impl_ParseJson!(for Vec, Vec, Vec, Vec, Vec, Vec); +impl_ParseJson!(for Vec, Vec, Vec, Vec, Vec, Vec, Vec); diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 3dd0e64..c537ed6 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -1,6 +1,7 @@ use super::{Raws, Reaction}; use crate::components::*; use crate::gamesystem::*; +use crate::gui::Ancestry; use crate::random_table::RandomTable; use crate::LOG_SPAWNING; use regex::Regex; @@ -23,6 +24,7 @@ pub struct RawMaster { table_index: HashMap, loot_index: HashMap, faction_index: HashMap>, + ancestry_index: HashMap>, } impl RawMaster { @@ -35,6 +37,7 @@ impl RawMaster { spawn_tables: Vec::new(), loot_tables: Vec::new(), factions: Vec::new(), + ancestries: Vec::new(), }, item_index: HashMap::new(), mob_index: HashMap::new(), @@ -42,6 +45,7 @@ impl RawMaster { table_index: HashMap::new(), loot_index: HashMap::new(), faction_index: HashMap::new(), + ancestry_index: HashMap::new(), } } @@ -322,6 +326,21 @@ pub fn spawn_named_mob( eb = eb.with(Faction { name: "carnivore".to_string() }); has_faction = true; } + "IS_GNOME" => { + eb = eb.with(HasAncestry { name: Ancestry::Gnome }); + } + "IS_DWARF" => { + eb = eb.with(HasAncestry { name: Ancestry::Dwarf }); + } + "IS_HUMAN" => { + eb = eb.with(HasAncestry { name: Ancestry::Human }); + } + "IS_CATFOLK" => { + eb = eb.with(HasAncestry { name: Ancestry::Catfolk }); + } + "IS_Elf" => { + eb = eb.with(HasAncestry { name: Ancestry::Elf }); + } "SMALL_GROUP" => {} // These flags are for region spawning, "LARGE_GROUP" => {} // and don't matter here (yet)? "MULTIATTACK" => { @@ -825,3 +844,31 @@ pub fn faction_reaction(this_faction: &str, other_faction: &str, raws: &RawMaste } return Reaction::Ignore; } + +pub fn ancestry_reaction(this_ancestry: Ancestry, other_ancestry: Ancestry, raws: &RawMaster) -> Option { + if this_ancestry == other_ancestry { + return Some(Reaction::Ignore); + } else { + let this_ancestry = get_ancestry_string(this_ancestry); + let other_ancestry = get_ancestry_string(other_ancestry); + if raws.ancestry_index.contains_key(this_ancestry) { + let mine = &raws.ancestry_index[this_ancestry]; + if mine.contains(other_ancestry) { + return Some(Reaction::Ignore); + } + } + } + + return None; +} + +fn get_ancestry_string(ancestry: Ancestry) -> &'static str { + match ancestry { + Ancestry::Human => return "human", + Ancestry::Elf => return "elf", + Ancestry::Dwarf => return "dwarf", + Ancestry::Catfolk => return "catfolk", + Ancestry::Gnome => return "gnome", + Ancestry::NULL => return "NULL", + } +} diff --git a/src/raws/faction_structs.rs b/src/raws/reaction_structs.rs similarity index 61% rename from src/raws/faction_structs.rs rename to src/raws/reaction_structs.rs index 83df4b7..6095089 100644 --- a/src/raws/faction_structs.rs +++ b/src/raws/reaction_structs.rs @@ -1,5 +1,5 @@ use serde::Deserialize; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; #[derive(Deserialize, Debug)] pub struct FactionData { @@ -7,6 +7,12 @@ pub struct FactionData { pub responses: HashMap, } +#[derive(Deserialize, Debug)] +pub struct AncestryData { + pub id: String, + pub allies: HashSet, +} + #[derive(PartialEq, Eq, Hash, Copy, Clone)] pub enum Reaction { Ignore, diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 39a5477..78d1236 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -72,6 +72,8 @@ pub fn save_game(ecs: &mut World) { Equipped, Faction, GrantsXP, + HasAncestry, + HasClass, Hidden, HungerClock, IdentifiedItem, @@ -189,6 +191,8 @@ pub fn load_game(ecs: &mut World) { Equipped, Faction, GrantsXP, + HasAncestry, + HasClass, Hidden, HungerClock, IdentifiedItem,