From 02a4f33d11b885c980c805a52638b6beb47a57f4 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Wed, 23 Aug 2023 18:17:04 +0100 Subject: [PATCH] more framework for spells --- src/components.rs | 9 ++++++- src/config/char_create.rs | 25 ++++++++++++++++++- src/effects/triggers.rs | 45 +++++++++++++++++++++++++++-------- src/gamesystem.rs | 31 ++++++++++++------------ src/gui/character_creation.rs | 16 ++++++------- src/gui/mod.rs | 2 +- src/main.rs | 1 + src/saveload_system.rs | 2 ++ 8 files changed, 95 insertions(+), 36 deletions(-) diff --git a/src/components.rs b/src/components.rs index 8cd93ae..7af71f2 100644 --- a/src/components.rs +++ b/src/components.rs @@ -200,9 +200,16 @@ pub struct KnownSpell { #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct KnownSpells { - pub spells: Vec, + pub list: Vec, } +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct GrantsSpell { + pub spell: String, +} + +// TODO: GrantsIntrinsic, Intrinsics, etc. ? Done the same way as spells? + #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Attributes { pub strength: Attribute, diff --git a/src/config/char_create.rs b/src/config/char_create.rs index 28e5494..4790d74 100644 --- a/src/config/char_create.rs +++ b/src/config/char_create.rs @@ -1,4 +1,5 @@ // --- GUI --- +pub const CHAR_CREATE_HEADER: &str = "Who are you? [Aa-Zz]"; pub const ANCESTRY_INFO_HEADER: &str = "Your ancestry grants..."; pub const CLASS_INFO_HEADER: &str = "Your class grants..."; // --- ANCESTRY RENDERABLES --- @@ -13,13 +14,35 @@ pub const ELF_SPEED_BONUS: i32 = 1; pub const ELF_TELEPATH_RANGE: i32 = 6; pub const DWARF_DEFENCE_MOD: i32 = 1; pub const CATFOLK_SPEED_BONUS: i32 = 2; -// --- CLASS STARTING ITEMS --- +// --- ANCESTRY ATTRIBUTE MAXIMUMS --- +pub const TOTAL_ATTRIBUTE_POINTS_MAXIMUM: i32 = 75; +pub const HUMAN_MAX_ATTR: [i32; 6] = [19, 19, 19, 19, 19, 19]; +pub const ELF_MAX_ATTR: [i32; 6] = [15, 18, 15, 20, 20, 18]; +pub const DWARF_MAX_ATTR: [i32; 6] = [19, 17, 20, 16, 16, 16]; +pub const GNOME_MAX_ATTR: [i32; 6] = [16, 18, 16, 20, 18, 18]; +pub const CATFOLK_MAX_ATTR: [i32; 6] = [16, 20, 16, 16, 18, 20]; +pub const UNKNOWN_MAX_ATTR: [i32; 6] = [18, 18, 18, 18, 18, 18]; +// --- CLASS MIN ATTRIBUTES --- +pub const FIGHTER_MIN_ATTR: (i32, i32, i32, i32, i32, i32) = (10, 8, 10, 6, 6, 8); +pub const ROGUE_MIN_ATTR: (i32, i32, i32, i32, i32, i32) = (8, 10, 8, 6, 8, 10); +pub const WIZARD_MIN_ATTR: (i32, i32, i32, i32, i32, i32) = (6, 8, 6, 10, 10, 8); +pub const VILLAGER_MIN_ATTR: (i32, i32, i32, i32, i32, i32) = (6, 6, 6, 6, 6, 6); +// --- CLASS ATTRIBUTE IMPROVE CHANCES --- +pub const FIGHTER_IMPR_CHANCE: [i32; 6] = [30, 20, 30, 6, 7, 7]; +pub const ROGUE_IMPR_CHANCE: [i32; 6] = [18, 30, 20, 9, 8, 15]; +pub const WIZARD_IMPR_CHANCE: [i32; 6] = [10, 15, 20, 30, 15, 10]; +pub const VILLAGER_IMPR_CHANCE: [i32; 6] = [15, 15, 25, 15, 15, 15]; +// --- CLASS STARTING ITEMS --- ## If any of these are changed, update ancestry infotext in src/gui/character_creation.rs. pub const FIGHTER_STARTING_FOOD: &str = "1d2+1"; pub const FIGHTER_STARTING_WEAPON: &str = "equip_shortsword"; pub const FIGHTER_STARTING_ARMOUR: &str = "equip_body_ringmail"; pub const FIGHTER_STARTING_SHIELD: &str = "equip_mediumshield"; pub const ROGUE_STARTING_FOOD: &str = "1d2+2"; +pub const ROGUE_STARTING_WEAPON: &str = "equip_rapier"; +pub const ROGUE_STARTING_ARMOUR: &str = "equip_body_weakleather"; pub const WIZARD_STARTING_FOOD: &str = "1d2+1"; +pub const WIZARD_STARTING_WEAPON: &str = "equip_dagger"; +pub const WIZARD_STARTING_ARMOUR: &str = "equip_back_protection"; pub const WIZARD_MAX_SCROLL_LVL: i32 = 3; pub const WIZARD_SCROLL_AMOUNT: &str = "1d3"; pub const WIZARD_POTION_AMOUNT: &str = "1d3-1"; diff --git a/src/effects/triggers.rs b/src/effects/triggers.rs index 82b1ebd..3b65ffe 100644 --- a/src/effects/triggers.rs +++ b/src/effects/triggers.rs @@ -29,6 +29,9 @@ use crate::{ RunState, SingleActivation, BUC, + GrantsSpell, + KnownSpell, + KnownSpells, }; use crate::config::messages::*; use rltk::prelude::*; @@ -93,13 +96,22 @@ fn event_trigger(source: Option, entity: Entity, target: &Targets, ecs: particles::handle_line_particles(ecs, entity, &target); let (logger, restored_nutrition) = handle_restore_nutrition(ecs, &mut event, logger); let (logger, magic_mapped) = handle_magic_mapper(ecs, &mut event, logger); + let (logger, granted_spell) = handle_grant_spell(ecs, &mut event, logger); let (logger, removed_curse) = handle_remove_curse(ecs, &mut event, logger); let (logger, identified) = handle_identify(ecs, &mut event, logger); let (logger, healed) = handle_healing(ecs, &mut event, logger); let (logger, damaged) = handle_damage(ecs, &mut event, logger); let (logger, confused) = handle_confusion(ecs, &mut event, logger); //let (logger, dug) = handle_dig(ecs, &mut event, logger); -- NYI i.e. Wand of Digging - did_something |= restored_nutrition || magic_mapped || healed || damaged || confused || removed_curse || identified; + did_something |= + restored_nutrition || + magic_mapped || + granted_spell || + healed || + damaged || + confused || + removed_curse || + identified; if event.log { logger.log(); @@ -145,6 +157,17 @@ fn handle_magic_mapper(ecs: &mut World, event: &mut EventInfo, mut logger: gamel return (logger, false); } +fn handle_grant_spell(ecs: &mut World, event: &mut EventInfo, mut logger: gamelog::Logger) -> (gamelog::Logger, bool) { + if let Some(granted_spell) = ecs.read_storage::().get(event.entity) { + if let Some(known_spells) = ecs.write_storage::().get_mut(event.source.unwrap()) { + // TODO: Check if the player knows *this* spell, and add it if not. + } else { + // TODO: Grant the KnownSpells component, and then add the spell. + } + } + return (logger, false); +} + fn handle_healing(ecs: &mut World, event: &mut EventInfo, mut logger: gamelog::Logger) -> (gamelog::Logger, bool) { if let Some(healing_item) = ecs.read_storage::().get(event.entity) { let mut rng = ecs.write_resource::(); @@ -255,21 +278,23 @@ fn handle_identify(ecs: &mut World, event: &mut EventInfo, mut logger: gamelog:: } let mut to_identify: Vec<(Entity, String)> = Vec::new(); let mut beatitudes = ecs.write_storage::(); - for (e, _i, _bp, _o, name) in ( + let obfuscated = ecs.read_storage::(); + for (e, _i, _bp, name) in ( &ecs.entities(), &ecs.read_storage::(), &ecs.read_storage::(), - &ecs.read_storage::(), &ecs.read_storage::(), ) .join() - .filter(|(_e, _i, bp, _o, name)| { - bp.owner == event.source.unwrap() && - (!dm.identified_items.contains(&name.name.clone()) || - !beatitudes - .get(event.source.unwrap()) - .map(|beatitude| beatitude.known) - .unwrap_or(true)) + .filter(|(e, _i, bp, name)| { + let in_this_backpack = bp.owner == event.source.unwrap(); + let has_obfuscated_name = obfuscated.get(*e).is_some(); + let already_identified = dm.identified_items.contains(&name.name.clone()); + let known_beatitude = beatitudes + .get(event.source.unwrap()) + .map(|b| b.known) + .unwrap_or(true); + return in_this_backpack && (has_obfuscated_name || !already_identified || !known_beatitude); }) { to_identify.push((e, name.name.clone())); } diff --git a/src/gamesystem.rs b/src/gamesystem.rs index 00235c6..c9c109f 100644 --- a/src/gamesystem.rs +++ b/src/gamesystem.rs @@ -1,6 +1,7 @@ use super::{ Skill, Skills }; use crate::gui::{ Ancestry, Class }; use crate::config::entity; +use crate::config::char_create::*; use rltk::prelude::*; use std::cmp::max; @@ -84,25 +85,25 @@ pub fn get_attribute_rolls( ancestry: Ancestry ) -> (i32, i32, i32, i32, i32, i32) { let (mut str, mut dex, mut con, mut int, mut wis, mut cha) = match class { - 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), + Class::Fighter => FIGHTER_MIN_ATTR, + Class::Rogue => ROGUE_MIN_ATTR, + Class::Wizard => WIZARD_MIN_ATTR, + Class::Villager => VILLAGER_MIN_ATTR, }; - let mut remaining_points = 75 - (str + dex + con + int + wis + cha); + let mut remaining_points = TOTAL_ATTRIBUTE_POINTS_MAXIMUM - (str + dex + con + int + wis + cha); let improve_chance: [i32; 6] = match class { - 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], + Class::Fighter => FIGHTER_IMPR_CHANCE, + Class::Rogue => ROGUE_IMPR_CHANCE, + Class::Wizard => WIZARD_IMPR_CHANCE, + Class::Villager => VILLAGER_IMPR_CHANCE, }; let ancestry_maximums: [i32; 6] = match ancestry { - Ancestry::Human => [19, 19, 19, 19, 19, 19], // 114 - Ancestry::Elf => [15, 18, 15, 20, 20, 18], // 106 - Ancestry::Dwarf => [19, 17, 20, 16, 16, 16], // 106 - Ancestry::Gnome => [16, 18, 16, 20, 18, 18], // 106 - Ancestry::Catfolk => [16, 20, 16, 16, 18, 20], // 106 - _ => [18, 18, 18, 18, 18, 18], + Ancestry::Human => HUMAN_MAX_ATTR, // 114 + Ancestry::Elf => ELF_MAX_ATTR, // 106 + Ancestry::Dwarf => DWARF_MAX_ATTR, // 106 + Ancestry::Gnome => GNOME_MAX_ATTR, // 106 + Ancestry::Catfolk => CATFOLK_MAX_ATTR, // 106 + _ => UNKNOWN_MAX_ATTR, }; let improve_table = crate::random_table::RandomTable ::new() diff --git a/src/gui/character_creation.rs b/src/gui/character_creation.rs index 4abcd26..d53b238 100644 --- a/src/gui/character_creation.rs +++ b/src/gui/character_creation.rs @@ -64,25 +64,25 @@ lazy_static! { m.insert( "fighter".to_string(), vec![ - "a longsword, ring mail, and 1d2+1 food".to_string(), + format!("a longsword, ring mail, and {} food", FIGHTER_STARTING_FOOD), "10 str, 8 dex, 10 con, 6 int, 6 wis, 8 cha".to_string(), "and 27 random stat points".to_string()]); m.insert( "rogue".to_string(), vec![ - "a rapier, leather armour, and 1d2+2 food".to_string(), + format!("a rapier, leather armour, and {} food", ROGUE_STARTING_FOOD), "8 str, 10 dex, 8 con, 6 int, 8 wis, 10 cha".to_string(), "and 35 random stat points".to_string()]); m.insert( "wizard".to_string(), vec![ - "a dagger, random scrolls/potions, and 1d2+1 food".to_string(), + format!("a dagger, random scrolls/potions, and {} food", WIZARD_STARTING_FOOD), "6 str, 8 dex, 6 con, 10 int, 10 wis, 8 cha".to_string(), "and 17 random stat points".to_string()]); m.insert( "villager".to_string(), vec![ - "the first weapon you could find, and 1d3+2 food".to_string(), + format!("the first weapon you could find, and {} food", VILLAGER_STARTING_FOOD), "6 str, 6 dex, 6 con, 6 int, 6 wis, 6 cha".to_string(), "and 39 random stat points".to_string()]); return m; @@ -109,7 +109,7 @@ pub fn character_creation(gs: &mut State, ctx: &mut Rltk) -> CharCreateResult { let mut y = 11; let column_width = 20; - ctx.print_color(x, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Who are you? [Aa-Zz]"); + ctx.print_color(x, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), CHAR_CREATE_HEADER); y += 2; if let RunState::CharacterCreation { ancestry, class } = *runstate { @@ -313,7 +313,7 @@ pub fn setup_player_class(ecs: &mut World, class: Class, ancestry: Ancestry) { let mut spells = ecs.write_storage::(); spells .insert(player, KnownSpells { - spells: vec![KnownSpell { display_name: "zap".to_string(), mana_cost: 1 }], + list: vec![KnownSpell { display_name: "zap".to_string(), mana_cost: 1 }], }) .expect("Unable to insert known spells component"); } @@ -386,12 +386,12 @@ fn get_starting_inventory(class: Class, rng: &mut RandomNumberGenerator) -> (Vec } Class::Rogue => { starting_food = ROGUE_STARTING_FOOD; - equipped = vec!["equip_rapier".to_string(), "equip_body_weakleather".to_string()]; + equipped = vec![ROGUE_STARTING_WEAPON.to_string(), ROGUE_STARTING_ARMOUR.to_string()]; carried = vec!["equip_dagger".to_string(), "equip_dagger".to_string()]; } Class::Wizard => { starting_food = WIZARD_STARTING_FOOD; - equipped = vec!["equip_dagger".to_string(), "equip_back_protection".to_string()]; + equipped = vec![WIZARD_STARTING_WEAPON.to_string(), WIZARD_STARTING_ARMOUR.to_string()]; pick_random_table_item(rng, &mut carried, "scrolls", WIZARD_SCROLL_AMOUNT, Some(WIZARD_MAX_SCROLL_LVL)); pick_random_table_item(rng, &mut carried, "potions", WIZARD_POTION_AMOUNT, Some(WIZARD_MAX_SCROLL_LVL)); } diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 3d32b5c..877064a 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -246,7 +246,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { ctx.print_color(72, y, RGB::named(BLACK), RGB::named(WHITE), "Known Spells"); y += 1; let mut index = 1; - for spell in known_spells.spells.iter() { + for spell in known_spells.list.iter() { ctx.print_color(72, y, RGB::named(YELLOW), RGB::named(BLACK), &format!("{}", index)); ctx.print_color( 74, diff --git a/src/main.rs b/src/main.rs index f32d015..5111171 100644 --- a/src/main.rs +++ b/src/main.rs @@ -700,6 +700,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/saveload_system.rs b/src/saveload_system.rs index 44471e8..81bd33a 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -75,6 +75,7 @@ pub fn save_game(ecs: &mut World) { Equippable, Equipped, Faction, + GrantsSpell, GrantsXP, HasAncestry, HasClass, @@ -203,6 +204,7 @@ pub fn load_game(ecs: &mut World) { Equippable, Equipped, Faction, + GrantsSpell, GrantsXP, HasAncestry, HasClass,