Setup parts of spell system

This commit is contained in:
Timothy Warren 2022-01-25 09:58:30 -05:00
parent 0353c658aa
commit 969ecf7fef
10 changed files with 186 additions and 25 deletions

View File

@ -401,3 +401,25 @@ pub struct Duration {
pub struct StatusEffect { pub struct StatusEffect {
pub target: Entity, pub target: Entity,
} }
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct KnownSpell {
pub display_name: String,
pub mana_cost: i32,
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct KnownSpells {
pub spells: Vec<KnownSpell>,
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct SpellTemplate {
pub mana_cost: i32,
}
#[derive(Component, Debug, ConvertSaveload, Clone)]
pub struct WantsToCastSpell {
pub spell: Entity,
pub target: Option<Point>,
}

View File

@ -12,7 +12,8 @@ use tooltip::draw_tooltips;
use crate::components::{ use crate::components::{
Attribute, Attributes, Consumable, CursedItem, Duration, Equipped, HungerClock, HungerState, Attribute, Attributes, Consumable, CursedItem, Duration, Equipped, HungerClock, HungerState,
InBackpack, MagicItem, MagicItemClass, Name, ObfuscatedName, Pools, StatusEffect, Viewshed, InBackpack, KnownSpells, MagicItem, MagicItemClass, Name, ObfuscatedName, Pools, StatusEffect,
Viewshed,
}; };
use crate::game_log::GameLog; use crate::game_log::GameLog;
use crate::{camera, colors, Map, MasterDungeonMap, State}; use crate::{camera, colors, Map, MasterDungeonMap, State};
@ -243,6 +244,24 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
} }
} }
// Spells
y += 1;
let known_spells_storage = ecs.read_storage::<KnownSpells>();
let known_spells = &known_spells_storage.get(*player_entity).unwrap().spells;
let mut index = 1;
for spell in known_spells.iter() {
ctx.print_color(50, y, colors::CYAN, colors::BLACK, &format!("^{}", index));
ctx.print_color(
53,
y,
colors::CYAN,
colors::BLACK,
&format!("{} ({})", spell.display_name, spell.mana_cost),
);
index += 1;
y += 1;
}
// Status // Status
y = 44; y = 44;
let hunger = ecs.read_storage::<HungerClock>(); let hunger = ecs.read_storage::<HungerClock>();

View File

@ -108,6 +108,7 @@ fn init_state() -> State {
InflictsDamage, InflictsDamage,
Initiative, Initiative,
Item, Item,
KnownSpells,
LightSource, LightSource,
LootTable, LootTable,
MagicItem, MagicItem,
@ -136,12 +137,14 @@ fn init_state() -> State {
Skills, Skills,
SpawnParticleBurst, SpawnParticleBurst,
SpawnParticleLine, SpawnParticleLine,
SpellTemplate,
StatusEffect, StatusEffect,
TeleportTo, TeleportTo,
TownPortal, TownPortal,
Vendor, Vendor,
Viewshed, Viewshed,
WantsToApproach, WantsToApproach,
WantsToCastSpell,
WantsToDropItem, WantsToDropItem,
WantsToFlee, WantsToFlee,
WantsToMelee, WantsToMelee,

View File

@ -9,7 +9,7 @@ use crate::components::{
}; };
use crate::game_log::GameLog; use crate::game_log::GameLog;
use crate::raws::{self, Reaction, RAWS}; use crate::raws::{self, Reaction, RAWS};
use crate::{spatial, Map, RunState, State, TileType, VendorMode}; use crate::{spatial, Map, RunState, State, TileType, VendorMode, WantsToCastSpell};
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState { pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState {
let mut positions = ecs.write_storage::<Position>(); let mut positions = ecs.write_storage::<Position>();
@ -298,10 +298,53 @@ fn use_consumable_hotkey(gs: &mut State, key: i32) -> RunState {
RunState::Ticking RunState::Ticking
} }
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { fn use_spell_hotkey(gs: &mut State, key: i32) -> RunState {
// Hotkeys use crate::components::{KnownSpells, Ranged};
if ctx.shift && ctx.key.is_some() { use crate::raws::find_spell_entity;
let key: Option<i32> = match ctx.key.unwrap() {
let player_entity = gs.ecs.fetch::<Entity>();
let known_spells_storage = gs.ecs.read_storage::<KnownSpells>();
let known_spells = &known_spells_storage.get(*player_entity).unwrap().spells;
if (key as usize) < known_spells.len() {
let pools = gs.ecs.read_storage::<Pools>();
let player_pools = pools.get(*player_entity).unwrap();
if player_pools.mana.current >= known_spells[key as usize].mana_cost {
if let Some(spell_entity) =
find_spell_entity(&gs.ecs, &known_spells[key as usize].display_name)
{
if let Some(ranged) = gs.ecs.read_storage::<Ranged>().get(spell_entity) {
return RunState::ShowTargeting {
range: ranged.range,
item: spell_entity,
};
}
let mut intent = gs.ecs.write_storage::<WantsToCastSpell>();
intent
.insert(
*player_entity,
WantsToCastSpell {
spell: spell_entity,
target: None,
},
)
.expect("Unable to insert intent to cast spell");
return RunState::Ticking;
}
} else {
let mut gamelog = gs.ecs.fetch_mut::<GameLog>();
gamelog.append("You don't have enough mana to cast that!");
}
}
RunState::Ticking
}
fn get_number_key(ctx: &mut Rltk) -> Option<i32> {
match ctx.key {
Some(key) => match key {
VirtualKeyCode::Key1 => Some(1), VirtualKeyCode::Key1 => Some(1),
VirtualKeyCode::Key2 => Some(2), VirtualKeyCode::Key2 => Some(2),
VirtualKeyCode::Key3 => Some(3), VirtualKeyCode::Key3 => Some(3),
@ -312,11 +355,25 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
VirtualKeyCode::Key8 => Some(8), VirtualKeyCode::Key8 => Some(8),
VirtualKeyCode::Key9 => Some(9), VirtualKeyCode::Key9 => Some(9),
_ => None, _ => None,
}; },
None => None,
}
}
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
// Hotkeys
if ctx.shift && ctx.key.is_some() {
let key: Option<i32> = get_number_key(ctx);
if let Some(key) = key { if let Some(key) = key {
return use_consumable_hotkey(gs, key - 1); return use_consumable_hotkey(gs, key - 1);
} }
} }
if ctx.control && ctx.key.is_some() {
let key: Option<i32> = get_number_key(ctx);
if let Some(key) = key {
return use_spell_hotkey(gs, key - 1);
}
}
// Mac OS is special when it comes to the numpad. Instead of reporting // Mac OS is special when it comes to the numpad. Instead of reporting
// the keys as Numpad-specific numbers, it reports the number row scan // the keys as Numpad-specific numbers, it reports the number row scan

View File

@ -5,6 +5,7 @@ mod mob_structs;
mod prop_structs; mod prop_structs;
mod rawmaster; mod rawmaster;
mod spawn_table_structs; mod spawn_table_structs;
mod spell_structs;
use std::sync::Mutex; use std::sync::Mutex;
@ -17,8 +18,9 @@ use mob_structs::*;
use prop_structs::*; use prop_structs::*;
pub use rawmaster::*; pub use rawmaster::*;
use spawn_table_structs::*; use spawn_table_structs::*;
pub use spell_structs::Spell;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Default, Debug)]
pub struct Raws { pub struct Raws {
pub items: Vec<Item>, pub items: Vec<Item>,
pub mobs: Vec<Mob>, pub mobs: Vec<Mob>,
@ -26,6 +28,7 @@ pub struct Raws {
pub spawn_table: Vec<SpawnTableEntry>, pub spawn_table: Vec<SpawnTableEntry>,
pub loot_tables: Vec<LootTable>, pub loot_tables: Vec<LootTable>,
pub faction_table: Vec<FactionInfo>, pub faction_table: Vec<FactionInfo>,
pub spells: Vec<Spell>,
} }
embedded_resource!(RAW_FILE, "../raws/spawns.json"); embedded_resource!(RAW_FILE, "../raws/spawns.json");

View File

@ -8,7 +8,7 @@ pub struct FactionInfo {
pub responses: HashMap<String, String>, pub responses: HashMap<String, String>,
} }
#[derive(PartialEq, Eq, Hash, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum Reaction { pub enum Reaction {
Ignore, Ignore,
Attack, Attack,

View File

@ -40,6 +40,7 @@ pub enum SpawnType {
Carried { by: Entity }, Carried { by: Entity },
} }
#[derive(Debug, Default)]
pub struct RawMaster { pub struct RawMaster {
raws: Raws, raws: Raws,
item_index: HashMap<String, usize>, item_index: HashMap<String, usize>,
@ -47,25 +48,12 @@ pub struct RawMaster {
prop_index: HashMap<String, usize>, prop_index: HashMap<String, usize>,
loot_index: HashMap<String, usize>, loot_index: HashMap<String, usize>,
faction_index: HashMap<String, HashMap<String, Reaction>>, faction_index: HashMap<String, HashMap<String, Reaction>>,
spell_index: HashMap<String, usize>,
} }
impl RawMaster { impl RawMaster {
pub fn empty() -> RawMaster { pub fn empty() -> RawMaster {
RawMaster { RawMaster::default()
raws: Raws {
items: Vec::new(),
mobs: Vec::new(),
props: Vec::new(),
spawn_table: Vec::new(),
loot_tables: Vec::new(),
faction_table: Vec::new(),
},
item_index: HashMap::new(),
mob_index: HashMap::new(),
prop_index: HashMap::new(),
loot_index: HashMap::new(),
faction_index: HashMap::new(),
}
} }
pub fn load(&mut self, raws: Raws) { pub fn load(&mut self, raws: Raws) {
@ -130,6 +118,10 @@ impl RawMaster {
} }
self.faction_index.insert(faction.name.clone(), reactions); self.faction_index.insert(faction.name.clone(), reactions);
} }
for (i, spell) in self.raws.spells.iter().enumerate() {
self.spell_index.insert(spell.name.clone(), i);
}
} }
} }
@ -722,6 +714,26 @@ pub fn spawn_named_entity(
None None
} }
pub fn spawn_named_spell(raws: &RawMaster, ecs: &mut World, key: &str) -> Option<Entity> {
if raws.spell_index.contains_key(key) {
let spell_template = &raws.raws.spells[raws.spell_index[key]];
let mut eb = ecs
.create_entity()
.marked::<SimpleMarker<SerializeMe>>()
.with(SpellTemplate {
mana_cost: spell_template.mana_cost,
})
.with(Name::from(*spell_template.name));
apply_effects!(spell_template.effects, eb);
return Some(eb.build());
}
None
}
pub fn get_spawn_table_for_depth(raws: &RawMaster, depth: i32) -> RandomTable { pub fn get_spawn_table_for_depth(raws: &RawMaster, depth: i32) -> RandomTable {
use super::SpawnTableEntry; use super::SpawnTableEntry;
@ -761,3 +773,24 @@ pub fn get_item_drop(
None None
} }
pub fn spawn_all_spells(ecs: &mut World) {
let raws = &RAWS.lock().unwrap();
for spell in raws.raws.spells.iter() {
spawn_named_spell(raws, ecs, &spell.name);
}
}
pub fn find_spell_entity(ecs: &World, name: &str) -> Option<Entity> {
let names = ecs.read_storage::<Name>();
let spell_templates = ecs.read_storage::<SpellTemplate>();
let entities = ecs.entities();
for (entity, sname, _template) in (&entities, &names, &spell_templates).join() {
if name == sname.name {
return Some(entity);
}
}
None
}

View File

@ -0,0 +1,9 @@
use std::collections::HashMap;
use ::serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct Spell {
pub name: String,
pub effects: HashMap<String, String>,
}

View File

@ -87,6 +87,7 @@ pub fn save_game(ecs: &mut World) {
InflictsDamage, InflictsDamage,
Initiative, Initiative,
Item, Item,
KnownSpells,
LightSource, LightSource,
LootTable, LootTable,
MagicItem, MagicItem,
@ -114,12 +115,14 @@ pub fn save_game(ecs: &mut World) {
Skills, Skills,
SpawnParticleBurst, SpawnParticleBurst,
SpawnParticleLine, SpawnParticleLine,
SpellTemplate,
StatusEffect, StatusEffect,
TeleportTo, TeleportTo,
TownPortal, TownPortal,
Vendor, Vendor,
Viewshed, Viewshed,
WantsToApproach, WantsToApproach,
WantsToCastSpell,
WantsToDropItem, WantsToDropItem,
WantsToFlee, WantsToFlee,
WantsToMelee, WantsToMelee,
@ -210,6 +213,7 @@ pub fn load_game(ecs: &mut World) {
InflictsDamage, InflictsDamage,
Initiative, Initiative,
Item, Item,
KnownSpells,
LightSource, LightSource,
LootTable, LootTable,
MagicItem, MagicItem,
@ -237,12 +241,14 @@ pub fn load_game(ecs: &mut World) {
Skills, Skills,
SpawnParticleBurst, SpawnParticleBurst,
SpawnParticleLine, SpawnParticleLine,
SpellTemplate,
StatusEffect, StatusEffect,
TeleportTo, TeleportTo,
TownPortal, TownPortal,
Vendor, Vendor,
Viewshed, Viewshed,
WantsToApproach, WantsToApproach,
WantsToCastSpell,
WantsToDropItem, WantsToDropItem,
WantsToFlee, WantsToFlee,
WantsToMelee, WantsToMelee,

View File

@ -7,11 +7,14 @@ use ::specs::saveload::{MarkedBuilder, SimpleMarker};
use crate::components::*; use crate::components::*;
use crate::gamesystem::{mana_at_level, player_hp_at_level}; use crate::gamesystem::{mana_at_level, player_hp_at_level};
use crate::random_table::RandomTable; use crate::random_table::RandomTable;
use crate::raws::{get_spawn_table_for_depth, spawn_named_entity, SpawnType, RAWS}; use crate::raws::{
get_spawn_table_for_depth, spawn_all_spells, spawn_named_entity, SpawnType, RAWS,
};
use crate::{colors, Map, MasterDungeonMap, Rect, TileType}; use crate::{colors, Map, MasterDungeonMap, Rect, TileType};
/// Spawns the player and returns their entity object /// Spawns the player and returns their entity object
pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
spawn_all_spells(ecs);
let player = ecs let player = ecs
.create_entity() .create_entity()
.with(Position { .with(Position {
@ -55,6 +58,12 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
.with(Initiative { current: 0 }) .with(Initiative { current: 0 })
.with(Faction::from("Player")) .with(Faction::from("Player"))
.with(EquipmentChanged {}) .with(EquipmentChanged {})
.with(KnownSpells {
spells: vec![KnownSpell {
display_name: "Zap".to_string(),
mana_cost: 1,
}],
})
.marked::<SimpleMarker<SerializeMe>>() .marked::<SimpleMarker<SerializeMe>>()
.build(); .build();