use crate::spatial; use rltk::prelude::*; use specs::prelude::*; use std::collections::VecDeque; use std::sync::Mutex; mod damage; mod particles; mod targeting; lazy_static! { pub static ref EFFECT_QUEUE: Mutex> = Mutex::new(VecDeque::new()); } pub enum EffectType { Damage { amount: i32 }, Bloodstain, Particle { glyph: FontCharType, fg: RGB, bg: RGB, lifespan: f32, delay: f32 }, EntityDeath, } #[derive(Clone)] pub enum Targets { Entity { target: Entity }, EntityList { targets: Vec }, Tile { target: usize }, TileList { targets: Vec }, } pub struct EffectSpawner { pub source: Option, pub effect_type: EffectType, pub target: Targets, } /// Adds an effect to the effects queue pub fn add_effect(source: Option, effect_type: EffectType, target: Targets) { let mut lock = EFFECT_QUEUE.lock().unwrap(); lock.push_back(EffectSpawner { source, effect_type, target }); } /// Iterates through the effects queue, applying each effect to their target. pub fn run_effects_queue(ecs: &mut World) { loop { let effect: Option = EFFECT_QUEUE.lock().unwrap().pop_front(); if let Some(effect) = effect { target_applicator(ecs, &effect); } else { break; } } } /// Applies an effect to the correct target(s). fn target_applicator(ecs: &mut World, effect: &EffectSpawner) { match &effect.target { Targets::Tile { target } => affect_tile(ecs, effect, *target), Targets::TileList { targets } => targets.iter().for_each(|target| affect_tile(ecs, effect, *target)), Targets::Entity { target } => affect_entity(ecs, effect, *target), Targets::EntityList { targets } => targets.iter().for_each(|target| affect_entity(ecs, effect, *target)), } } /// Checks if a given effect affects entities or not. fn tile_effect_hits_entities(effect: &EffectType) -> bool { match effect { EffectType::Damage { .. } => true, _ => false, } } /// Runs an effect on a given tile index fn affect_tile(ecs: &mut World, effect: &EffectSpawner, target: usize) { if tile_effect_hits_entities(&effect.effect_type) { spatial::for_each_tile_content(target, |entity| { affect_entity(ecs, effect, entity); }); } match &effect.effect_type { EffectType::Bloodstain => damage::bloodstain(ecs, target), EffectType::Particle { .. } => particles::particle_to_tile(ecs, target as i32, &effect), _ => {} } // Run the effect } /// Runs an effect on a given entity fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) { match &effect.effect_type { EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target), EffectType::Bloodstain { .. } => { if let Some(pos) = targeting::entity_position(ecs, target) { damage::bloodstain(ecs, pos) } } EffectType::Particle { .. } => { if let Some(pos) = targeting::entity_position(ecs, target) { particles::particle_to_tile(ecs, pos as i32, &effect) } } EffectType::EntityDeath => damage::entity_death(ecs, effect, target), _ => {} } }