Implement system to use spells
This commit is contained in:
parent
969ecf7fef
commit
e6a01791bf
@ -35,6 +35,9 @@ pub enum EffectType {
|
|||||||
ItemUse {
|
ItemUse {
|
||||||
item: Entity,
|
item: Entity,
|
||||||
},
|
},
|
||||||
|
SpellUse {
|
||||||
|
spell: Entity,
|
||||||
|
},
|
||||||
WellFed,
|
WellFed,
|
||||||
Healing {
|
Healing {
|
||||||
amount: i32,
|
amount: i32,
|
||||||
@ -98,6 +101,8 @@ pub fn run_effects_queue(ecs: &mut World) {
|
|||||||
fn target_applicator(ecs: &mut World, effect: &EffectSpawner) {
|
fn target_applicator(ecs: &mut World, effect: &EffectSpawner) {
|
||||||
if let EffectType::ItemUse { item } = effect.effect_type {
|
if let EffectType::ItemUse { item } = effect.effect_type {
|
||||||
triggers::item_trigger(effect.creator, item, &effect.targets, ecs);
|
triggers::item_trigger(effect.creator, item, &effect.targets, ecs);
|
||||||
|
} else if let EffectType::SpellUse { spell } = effect.effect_type {
|
||||||
|
triggers::spell_trigger(effect.creator, spell, &effect.targets, ecs);
|
||||||
} else if let EffectType::TriggerFire { trigger } = effect.effect_type {
|
} else if let EffectType::TriggerFire { trigger } = effect.effect_type {
|
||||||
triggers::trigger(effect.creator, trigger, &effect.targets, ecs);
|
triggers::trigger(effect.creator, trigger, &effect.targets, ecs);
|
||||||
} else {
|
} else {
|
||||||
|
@ -23,7 +23,7 @@ pub fn aoe_tiles(map: &Map, target: ::rltk::Point, radius: i32) -> Vec<i32> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_item_position(ecs: &World, target: Entity) -> Option<i32> {
|
pub fn find_item_position(ecs: &World, target: Entity, creator: Option<Entity>) -> Option<i32> {
|
||||||
let positions = ecs.read_storage::<Position>();
|
let positions = ecs.read_storage::<Position>();
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
|
|
||||||
@ -46,6 +46,13 @@ pub fn find_item_position(ecs: &World, target: Entity) -> Option<i32> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maybe the creator has a position?
|
||||||
|
if let Some(creator) = creator {
|
||||||
|
if let Some(pos) = positions.get(creator) {
|
||||||
|
return Some(map.xy_idx(pos.x, pos.y) as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// No idea - give up
|
// No idea - give up
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ use ::specs::prelude::*;
|
|||||||
use super::{add_effect, EffectType, Targets};
|
use super::{add_effect, EffectType, Targets};
|
||||||
use crate::components::{
|
use crate::components::{
|
||||||
AttributeBonus, Confusion, Consumable, Duration, Hidden, InflictsDamage, MagicMapper, Name,
|
AttributeBonus, Confusion, Consumable, Duration, Hidden, InflictsDamage, MagicMapper, Name,
|
||||||
ProvidesFood, ProvidesHealing, ProvidesRemoveCurse, SingleActivation, SpawnParticleBurst,
|
Pools, ProvidesFood, ProvidesHealing, ProvidesRemoveCurse, SingleActivation,
|
||||||
SpawnParticleLine, TeleportTo, TownPortal,
|
SpawnParticleBurst, SpawnParticleLine, SpellTemplate, TeleportTo, TownPortal,
|
||||||
};
|
};
|
||||||
use crate::effects::{entity_position, targeting};
|
use crate::effects::{entity_position, targeting};
|
||||||
use crate::{colors, GameLog, Map, RunState};
|
use crate::{colors, GameLog, Map, RunState};
|
||||||
@ -41,6 +41,21 @@ pub fn item_trigger(creator: Option<Entity>, item: Entity, targets: &Targets, ec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn spell_trigger(creator: Option<Entity>, spell: Entity, targets: &Targets, ecs: &mut World) {
|
||||||
|
if let Some(template) = ecs.read_storage::<SpellTemplate>().get(spell) {
|
||||||
|
let mut pools = ecs.write_storage::<Pools>();
|
||||||
|
if let Some(caster) = creator {
|
||||||
|
if let Some(pool) = pools.get_mut(caster) {
|
||||||
|
if template.mana_cost <= pool.mana.current {
|
||||||
|
pool.mana.current -= template.mana_cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event_trigger(creator, spell, targets, ecs);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn trigger(creator: Option<Entity>, trigger: Entity, targets: &Targets, ecs: &mut World) {
|
pub fn trigger(creator: Option<Entity>, trigger: Entity, targets: &Targets, ecs: &mut World) {
|
||||||
// The triggering item is no longer hidden
|
// The triggering item is no longer hidden
|
||||||
ecs.write_storage::<Hidden>().remove(trigger);
|
ecs.write_storage::<Hidden>().remove(trigger);
|
||||||
@ -86,7 +101,7 @@ fn event_trigger(
|
|||||||
|
|
||||||
// Line particle spawn
|
// Line particle spawn
|
||||||
if let Some(part) = ecs.read_storage::<SpawnParticleLine>().get(entity) {
|
if let Some(part) = ecs.read_storage::<SpawnParticleLine>().get(entity) {
|
||||||
if let Some(start_pos) = targeting::find_item_position(ecs, entity) {
|
if let Some(start_pos) = targeting::find_item_position(ecs, entity, creator) {
|
||||||
match targets {
|
match targets {
|
||||||
Targets::Tile { tile_idx } => spawn_line_particles(ecs, start_pos, *tile_idx, part),
|
Targets::Tile { tile_idx } => spawn_line_particles(ecs, start_pos, *tile_idx, part),
|
||||||
Targets::Tiles { tiles } => tiles
|
Targets::Tiles { tiles } => tiles
|
||||||
|
@ -11,7 +11,7 @@ pub use drop_system::ItemDropSystem;
|
|||||||
pub use identification_system::ItemIdentificationSystem;
|
pub use identification_system::ItemIdentificationSystem;
|
||||||
pub use remove_system::ItemRemoveSystem;
|
pub use remove_system::ItemRemoveSystem;
|
||||||
pub use use_equip::ItemEquipOnUse;
|
pub use use_equip::ItemEquipOnUse;
|
||||||
pub use use_system::ItemUseSystem;
|
pub use use_system::{ItemUseSystem, SpellUseSystem};
|
||||||
|
|
||||||
use crate::components::{MagicItem, Name, ObfuscatedName};
|
use crate::components::{MagicItem, Name, ObfuscatedName};
|
||||||
use crate::MasterDungeonMap;
|
use crate::MasterDungeonMap;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use ::specs::prelude::*;
|
use ::specs::prelude::*;
|
||||||
|
|
||||||
use crate::components::{AreaOfEffect, EquipmentChanged, IdentifiedItem, Name, WantsToUseItem};
|
use crate::components::{
|
||||||
|
AreaOfEffect, EquipmentChanged, IdentifiedItem, Name, WantsToCastSpell, WantsToUseItem,
|
||||||
|
};
|
||||||
use crate::effects::{add_effect, aoe_tiles, EffectType, Targets};
|
use crate::effects::{add_effect, aoe_tiles, EffectType, Targets};
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
@ -75,3 +77,76 @@ impl<'a> System<'a> for ItemUseSystem {
|
|||||||
wants_use.clear();
|
wants_use.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SpellUseSystem {}
|
||||||
|
|
||||||
|
impl<'a> System<'a> for SpellUseSystem {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
type SystemData = (
|
||||||
|
ReadExpect<'a, Entity>,
|
||||||
|
WriteExpect<'a, Map>,
|
||||||
|
Entities<'a>,
|
||||||
|
WriteStorage<'a, WantsToCastSpell>,
|
||||||
|
ReadStorage<'a, Name>,
|
||||||
|
ReadStorage<'a, AreaOfEffect>,
|
||||||
|
WriteStorage<'a, EquipmentChanged>,
|
||||||
|
WriteStorage<'a, IdentifiedItem>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
|
let (
|
||||||
|
player_entity,
|
||||||
|
map,
|
||||||
|
entities,
|
||||||
|
mut wants_use,
|
||||||
|
names,
|
||||||
|
aoe,
|
||||||
|
mut dirty,
|
||||||
|
mut identified_item,
|
||||||
|
) = data;
|
||||||
|
|
||||||
|
for (entity, useitem) in (&entities, &wants_use).join() {
|
||||||
|
dirty
|
||||||
|
.insert(entity, EquipmentChanged {})
|
||||||
|
.expect("Unable to insert EquipmentChanged tag");
|
||||||
|
|
||||||
|
// Identify
|
||||||
|
if entity == *player_entity {
|
||||||
|
identified_item
|
||||||
|
.insert(
|
||||||
|
entity,
|
||||||
|
IdentifiedItem {
|
||||||
|
name: names.get(useitem.spell).unwrap().name.clone(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("Unable to insert item identification");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the effects system
|
||||||
|
add_effect(
|
||||||
|
Some(entity),
|
||||||
|
EffectType::SpellUse {
|
||||||
|
spell: useitem.spell,
|
||||||
|
},
|
||||||
|
match useitem.target {
|
||||||
|
None => Targets::Single {
|
||||||
|
target: *player_entity,
|
||||||
|
},
|
||||||
|
Some(target) => {
|
||||||
|
if let Some(aoe) = aoe.get(useitem.spell) {
|
||||||
|
Targets::Tiles {
|
||||||
|
tiles: aoe_tiles(&*map, target, aoe.radius),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Targets::Tile {
|
||||||
|
tile_idx: map.xy_idx(target.x, target.y) as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
wants_use.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -342,9 +342,10 @@ fn use_spell_hotkey(gs: &mut State, key: i32) -> RunState {
|
|||||||
RunState::Ticking
|
RunState::Ticking
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_number_key(ctx: &mut Rltk) -> Option<i32> {
|
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||||
match ctx.key {
|
// Hotkeys
|
||||||
Some(key) => match key {
|
if (ctx.shift || ctx.control) && ctx.key.is_some() {
|
||||||
|
let key: Option<i32> = match ctx.key.unwrap() {
|
||||||
VirtualKeyCode::Key1 => Some(1),
|
VirtualKeyCode::Key1 => Some(1),
|
||||||
VirtualKeyCode::Key2 => Some(2),
|
VirtualKeyCode::Key2 => Some(2),
|
||||||
VirtualKeyCode::Key3 => Some(3),
|
VirtualKeyCode::Key3 => Some(3),
|
||||||
@ -355,25 +356,16 @@ fn get_number_key(ctx: &mut Rltk) -> Option<i32> {
|
|||||||
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 {
|
||||||
|
if ctx.shift {
|
||||||
return use_consumable_hotkey(gs, key - 1);
|
return use_consumable_hotkey(gs, key - 1);
|
||||||
}
|
} else if ctx.control {
|
||||||
}
|
|
||||||
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);
|
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
|
||||||
|
@ -724,7 +724,7 @@ pub fn spawn_named_spell(raws: &RawMaster, ecs: &mut World, key: &str) -> Option
|
|||||||
.with(SpellTemplate {
|
.with(SpellTemplate {
|
||||||
mana_cost: spell_template.mana_cost,
|
mana_cost: spell_template.mana_cost,
|
||||||
})
|
})
|
||||||
.with(Name::from(*spell_template.name));
|
.with(Name::from(spell_template.name.clone()));
|
||||||
|
|
||||||
apply_effects!(spell_template.effects, eb);
|
apply_effects!(spell_template.effects, eb);
|
||||||
|
|
||||||
|
@ -5,5 +5,6 @@ use ::serde::Deserialize;
|
|||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Spell {
|
pub struct Spell {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub mana_cost: i32,
|
||||||
pub effects: HashMap<String, String>,
|
pub effects: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
@ -51,13 +51,13 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
|
|||||||
gold: 0.,
|
gold: 0.,
|
||||||
god_mode: false,
|
god_mode: false,
|
||||||
})
|
})
|
||||||
|
.with(EquipmentChanged {})
|
||||||
.with(LightSource {
|
.with(LightSource {
|
||||||
color: colors::TORCH_LIGHT,
|
color: colors::TORCH_LIGHT,
|
||||||
range: 8,
|
range: 8,
|
||||||
})
|
})
|
||||||
.with(Initiative { current: 0 })
|
.with(Initiative { current: 0 })
|
||||||
.with(Faction::from("Player"))
|
.with(Faction::from("Player"))
|
||||||
.with(EquipmentChanged {})
|
|
||||||
.with(KnownSpells {
|
.with(KnownSpells {
|
||||||
spells: vec![KnownSpell {
|
spells: vec![KnownSpell {
|
||||||
display_name: "Zap".to_string(),
|
display_name: "Zap".to_string(),
|
||||||
@ -195,15 +195,15 @@ pub fn spawn_region(
|
|||||||
|
|
||||||
/// Spawns a named entity (name in tuple.1) at the location in (tuple.0)
|
/// Spawns a named entity (name in tuple.1) at the location in (tuple.0)
|
||||||
pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) {
|
pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) {
|
||||||
let map = ecs.fetch::<Map>();
|
let width: usize;
|
||||||
let width = map.width as usize;
|
{
|
||||||
|
width = ecs.fetch::<Map>().width as usize;
|
||||||
|
}
|
||||||
|
|
||||||
let (idx, name) = *spawn;
|
let (idx, name) = *spawn;
|
||||||
let x = (*idx % width) as i32;
|
let x = (*idx % width) as i32;
|
||||||
let y = (*idx / width) as i32;
|
let y = (*idx / width) as i32;
|
||||||
|
|
||||||
// Drop this map reference to make the borrow checker happy
|
|
||||||
std::mem::drop(map);
|
|
||||||
|
|
||||||
let item_result = spawn_named_entity(
|
let item_result = spawn_named_entity(
|
||||||
&RAWS.lock().unwrap(),
|
&RAWS.lock().unwrap(),
|
||||||
ecs,
|
ecs,
|
||||||
@ -214,7 +214,9 @@ pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !name.is_empty() {
|
||||||
::rltk::console::log(format!("WARNING: We don't know how to spawn [{}]!", name));
|
::rltk::console::log(format!("WARNING: We don't know how to spawn [{}]!", name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_town_portal(ecs: &mut World) {
|
pub fn spawn_town_portal(ecs: &mut World) {
|
||||||
|
20
src/state.rs
20
src/state.rs
@ -8,7 +8,7 @@ use crate::gui::{self, show_cheat_mode, CheatMenuResult, MainMenuSelection};
|
|||||||
use crate::hunger_system::HungerSystem;
|
use crate::hunger_system::HungerSystem;
|
||||||
use crate::inventory_system::{
|
use crate::inventory_system::{
|
||||||
ItemCollectionSystem, ItemDropSystem, ItemEquipOnUse, ItemIdentificationSystem,
|
ItemCollectionSystem, ItemDropSystem, ItemEquipOnUse, ItemIdentificationSystem,
|
||||||
ItemRemoveSystem, ItemUseSystem,
|
ItemRemoveSystem, ItemUseSystem, SpellUseSystem,
|
||||||
};
|
};
|
||||||
use crate::lighting_system::LightingSystem;
|
use crate::lighting_system::LightingSystem;
|
||||||
use crate::map::{self, *};
|
use crate::map::{self, *};
|
||||||
@ -133,6 +133,9 @@ impl State {
|
|||||||
let mut itemuse = ItemUseSystem {};
|
let mut itemuse = ItemUseSystem {};
|
||||||
itemuse.run_now(&self.ecs);
|
itemuse.run_now(&self.ecs);
|
||||||
|
|
||||||
|
let mut spelluse = SpellUseSystem {};
|
||||||
|
spelluse.run_now(&self.ecs);
|
||||||
|
|
||||||
let mut item_id = ItemIdentificationSystem {};
|
let mut item_id = ItemIdentificationSystem {};
|
||||||
item_id.run_now(&self.ecs);
|
item_id.run_now(&self.ecs);
|
||||||
|
|
||||||
@ -348,6 +351,20 @@ impl GameState for State {
|
|||||||
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput,
|
||||||
gui::ItemMenuResult::NoResponse => {}
|
gui::ItemMenuResult::NoResponse => {}
|
||||||
gui::ItemMenuResult::Selected => {
|
gui::ItemMenuResult::Selected => {
|
||||||
|
if self.ecs.read_storage::<SpellTemplate>().get(item).is_some() {
|
||||||
|
let mut intent = self.ecs.write_storage::<WantsToCastSpell>();
|
||||||
|
intent
|
||||||
|
.insert(
|
||||||
|
*self.ecs.fetch::<Entity>(),
|
||||||
|
WantsToCastSpell {
|
||||||
|
spell: item,
|
||||||
|
target: result.1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("failed to add intent to cast spell");
|
||||||
|
|
||||||
|
newrunstate = RunState::Ticking;
|
||||||
|
} else {
|
||||||
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
|
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
|
||||||
|
|
||||||
intent
|
intent
|
||||||
@ -364,6 +381,7 @@ impl GameState for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RunState::MainMenu { .. } => match gui::main_menu(self, ctx) {
|
RunState::MainMenu { .. } => match gui::main_menu(self, ctx) {
|
||||||
gui::MainMenuResult::NoSelection { selected } => {
|
gui::MainMenuResult::NoSelection { selected } => {
|
||||||
newrunstate = RunState::MainMenu {
|
newrunstate = RunState::MainMenu {
|
||||||
|
Loading…
Reference in New Issue
Block a user