more framework for spells

This commit is contained in:
Llywelwyn 2023-08-23 18:17:04 +01:00
parent 76c74df028
commit 02a4f33d11
8 changed files with 95 additions and 36 deletions

View file

@ -200,9 +200,16 @@ pub struct KnownSpell {
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct KnownSpells {
pub spells: Vec<KnownSpell>,
pub list: Vec<KnownSpell>,
}
#[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,

View file

@ -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";

View file

@ -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: 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::<GrantsSpell>().get(event.entity) {
if let Some(known_spells) = ecs.write_storage::<KnownSpells>().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::<ProvidesHealing>().get(event.entity) {
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
@ -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::<Beatitude>();
for (e, _i, _bp, _o, name) in (
let obfuscated = ecs.read_storage::<ObfuscatedName>();
for (e, _i, _bp, name) in (
&ecs.entities(),
&ecs.read_storage::<Item>(),
&ecs.read_storage::<InBackpack>(),
&ecs.read_storage::<ObfuscatedName>(),
&ecs.read_storage::<Name>(),
)
.join()
.filter(|(_e, _i, bp, _o, name)| {
bp.owner == event.source.unwrap() &&
(!dm.identified_items.contains(&name.name.clone()) ||
!beatitudes
.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(|beatitude| beatitude.known)
.unwrap_or(true))
.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()));
}

View file

@ -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()

View file

@ -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::<KnownSpells>();
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));
}

View file

@ -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,

View file

@ -700,6 +700,7 @@ fn main() -> rltk::BError {
gs.ecs.register::<ProvidesRemoveCurse>();
gs.ecs.register::<ProvidesIdentify>();
gs.ecs.register::<KnownSpells>();
gs.ecs.register::<GrantsSpell>();
gs.ecs.register::<ParticleLifetime>();
gs.ecs.register::<SpawnParticleSimple>();
gs.ecs.register::<SpawnParticleBurst>();

View file

@ -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,