use ::rltk::Point; use ::specs::prelude::*; use super::{add_effect, EffectType, Targets}; use crate::components::{ AlwaysTargetsSelf, AreaOfEffect, AttributeBonus, Confusion, Consumable, DamageOverTime, Duration, Hidden, InflictsDamage, KnownSpell, KnownSpells, MagicMapper, Name, Pools, Position, ProvidesFood, ProvidesHealing, ProvidesIdentification, ProvidesMana, ProvidesRemoveCurse, SingleActivation, Slow, SpawnParticleBurst, SpawnParticleLine, SpellTemplate, TeachesSpell, TeleportTo, TownPortal, }; use crate::effects::{aoe_tiles, entity_position, targeting}; use crate::raws::find_spell_entity; use crate::{colors, GameLog, Map, RunState}; pub fn item_trigger(creator: Option, item: Entity, targets: &Targets, ecs: &mut World) { // Check charges if let Some(c) = ecs.write_storage::().get_mut(item) { if c.charges < 1 { // Cancel let mut gamelog = ecs.fetch_mut::(); gamelog.append(format!( "{} is out of charges!", ecs.read_storage::().get(item).unwrap().name )); return; } else { c.charges -= 1; } } // Use the item via the generic system let did_something = event_trigger(creator, item, targets, ecs); // If it was a consumable, then it gets deleted if did_something { if let Some(c) = ecs.read_storage::().get(item) { if c.max_charges == 0 { ecs.entities() .delete(item) .expect("Failed to delete consumable item"); } } } } pub fn spell_trigger(creator: Option, spell: Entity, targets: &Targets, ecs: &mut World) { let mut targeting = targets.clone(); let mut self_destruct = false; if let Some(template) = ecs.read_storage::().get(spell) { let mut pools = ecs.write_storage::(); 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; } } // Handle self-targeting override if ecs.read_storage::().get(spell).is_some() { if let Some(pos) = ecs.read_storage::().get(caster) { let map = ecs.fetch::(); targeting = if let Some(aoe) = ecs.read_storage::().get(spell) { Targets::Tiles { tiles: aoe_tiles(&map, Point::new(pos.x, pos.y), aoe.radius), } } else { Targets::Tile { tile_idx: map.xy_idx(pos.x, pos.y) as i32, } } } } } if let Some(_destruct) = ecs.read_storage::().get(spell) { self_destruct = true; } } event_trigger(creator, spell, &targeting, ecs); if self_destruct { if let Some(creator) = creator { ecs.entities() .delete(creator) .expect("Unable to delete owner"); } } } pub fn trigger(creator: Option, trigger: Entity, targets: &Targets, ecs: &mut World) { // The triggering item is no longer hidden ecs.write_storage::().remove(trigger); // Use the item via the generic system let did_something = event_trigger(creator, trigger, targets, ecs); // If it was a single activation, then it gets deleted if did_something && ecs .read_storage::() .get(trigger) .is_some() { ecs.entities() .delete(trigger) .expect("Failed to delete SingleActivation tag"); } } fn event_trigger( creator: Option, entity: Entity, targets: &Targets, ecs: &mut World, ) -> bool { let mut did_something = false; let mut gamelog = ecs.fetch_mut::(); // Simple particle spawn if let Some(part) = ecs.read_storage::().get(entity) { add_effect( creator, EffectType::Particle { glyph: part.glyph, fg: part.color, bg: colors::BLACK, lifespan: part.lifetime_ms, }, targets.clone(), ); } // Line particle spawn if let Some(part) = ecs.read_storage::().get(entity) { if let Some(start_pos) = targeting::find_item_position(ecs, entity, creator) { match targets { Targets::Tile { tile_idx } => spawn_line_particles(ecs, start_pos, *tile_idx, part), Targets::Tiles { tiles } => tiles .iter() .for_each(|tile_idx| spawn_line_particles(ecs, start_pos, *tile_idx, part)), Targets::Single { target } => { if let Some(end_pos) = entity_position(ecs, *target) { spawn_line_particles(ecs, start_pos, end_pos, part); } } Targets::TargetList { targets } => { targets.iter().for_each(|target| { if let Some(end_pos) = entity_position(ecs, *target) { spawn_line_particles(ecs, start_pos, end_pos, part); } }); } } } } // Providing food if ecs.read_storage::().get(entity).is_some() { add_effect(creator, EffectType::WellFed, targets.clone()); let names = ecs.read_storage::(); gamelog.append(format!("You eat the {}.", names.get(entity).unwrap().name)); did_something = true; } // Magic mapper if ecs.read_storage::().get(entity).is_some() { let mut runstate = ecs.fetch_mut::(); gamelog.append("The map is revealed to you!"); *runstate = RunState::MagicMapReveal { row: 0 }; did_something = true; } // Remove Curse if ecs .read_storage::() .get(entity) .is_some() { let mut runstate = ecs.fetch_mut::(); *runstate = RunState::ShowRemoveCurse; did_something = true; } // Identify Item if ecs .read_storage::() .get(entity) .is_some() { let mut runstate = ecs.fetch_mut::(); *runstate = RunState::ShowIdentify; did_something = true; } // Town Portal if ecs.read_storage::().get(entity).is_some() { let map = ecs.fetch::(); if map.depth == 1 { gamelog.append("You are already in town, so the scroll does nothing."); } else { gamelog.append("You are teleported back to town!"); let mut runstate = ecs.fetch_mut::(); *runstate = RunState::TownPortal; did_something = true; } } // Healing if let Some(heal) = ecs.read_storage::().get(entity) { add_effect( creator, EffectType::Healing { amount: heal.heal_amount, }, targets.clone(), ); did_something = true; } // Mana if let Some(mana) = ecs.read_storage::().get(entity) { add_effect( creator, EffectType::Mana { amount: mana.mana_amount, }, targets.clone(), ); did_something = true; } // Damage if let Some(damage) = ecs.read_storage::().get(entity) { add_effect( creator, EffectType::Damage { amount: damage.damage, }, targets.clone(), ); did_something = true; } // Confusion if ecs.read_storage::().get(entity).is_some() { if let Some(duration) = ecs.read_storage::().get(entity) { add_effect( creator, EffectType::Confusion { turns: duration.turns, }, targets.clone(), ); did_something = true; } } // Teleport if let Some(teleport) = ecs.read_storage::().get(entity) { add_effect( creator, EffectType::TeleportTo { x: teleport.x, y: teleport.y, depth: teleport.depth, player_only: teleport.player_only, }, targets.clone(), ); did_something = true; } // Attribute Modifiers if let Some(attr) = ecs.read_storage::().get(entity) { add_effect( creator, EffectType::AttributeEffect { bonus: attr.clone(), duration: 10, name: ecs.read_storage::().get(entity).unwrap().name.clone(), }, targets.clone(), ); did_something = true; } // Learn spells if let Some(spell) = ecs.read_storage::().get(entity) { if let Some(known) = ecs.write_storage::().get_mut(creator.unwrap()) { if let Some(spell_entity) = find_spell_entity(ecs, &spell.spell) { if let Some(spell_info) = ecs.read_storage::().get(spell_entity) { let mut already_known = false; known.spells.iter().for_each(|s| { if s.display_name == spell.spell { already_known = true } }); if !already_known { known.spells.push(KnownSpell { display_name: spell.spell.clone(), mana_cost: spell_info.mana_cost, }); } } } } did_something = true; } // Slow if let Some(slow) = ecs.read_storage::().get(entity) { add_effect( creator, EffectType::Slow { initiative_penalty: slow.initiative_penalty, }, targets.clone(), ); did_something = true; } // Damage Over Time if let Some(damage) = ecs.read_storage::().get(entity) { add_effect( creator, EffectType::DamageOverTime { damage: damage.damage, }, targets.clone(), ); did_something = true; } did_something } fn spawn_line_particles(ecs: &World, start: i32, end: i32, part: &SpawnParticleLine) { use ::rltk::LineAlg; let map = ecs.fetch::(); let start_pt = Point::new(start % map.width, end / map.width); let end_pt = Point::new(end % map.width, end / map.width); let line = ::rltk::line2d(LineAlg::Bresenham, start_pt, end_pt); for pt in line.iter() { add_effect( None, EffectType::Particle { glyph: part.glyph, fg: part.color, bg: colors::BLACK, lifespan: part.lifetime_ms, }, Targets::Tile { tile_idx: map.xy_idx(pt.x, pt.y) as i32, }, ); } }