1
0
Fork 0
roguelike-game/src/effects.rs

202 lines
5.8 KiB
Rust

//! The effects system
mod damage;
mod hunger;
mod movement;
mod particles;
mod targeting;
mod triggers;
use std::collections::{HashSet, VecDeque};
use std::sync::Mutex;
use ::bracket_lib::prelude::*;
use ::lazy_static::lazy_static;
use ::specs::prelude::*;
pub use targeting::*;
use crate::{spatial, AttributeBonus};
lazy_static! {
/// The shared queue of effects
pub static ref EFFECT_QUEUE: Mutex<VecDeque<EffectSpawner>> = Mutex::new(VecDeque::new());
}
/// The kind of effect to cause
pub enum EffectType {
Damage {
amount: i32,
},
Bloodstain,
Particle {
glyph: FontCharType,
fg: RGB,
bg: RGB,
lifespan: f32,
},
ParticleProjectile {
glyph: FontCharType,
fg: RGB,
bg: RGB,
lifespan: f32,
speed: f32,
path: Vec<Point>,
},
EntityDeath,
ItemUse {
item: Entity,
},
SpellUse {
spell: Entity,
},
WellFed,
Healing {
amount: i32,
},
Mana {
amount: i32,
},
Confusion {
turns: i32,
},
TriggerFire {
trigger: Entity,
},
TeleportTo {
x: i32,
y: i32,
depth: i32,
player_only: bool,
},
AttributeEffect {
bonus: AttributeBonus,
name: String,
duration: i32,
},
Slow {
initiative_penalty: f32,
},
DamageOverTime {
damage: i32,
},
}
/// Who, or what the effect should affect.
#[allow(dead_code)]
#[derive(Clone)]
pub enum Targets {
Single { target: Entity },
TargetList { targets: Vec<Entity> },
Tile { tile_idx: i32 },
Tiles { tiles: Vec<i32> },
}
/// An effect for the queue
pub struct EffectSpawner {
pub creator: Option<Entity>,
pub effect_type: EffectType,
pub targets: Targets,
dedupe: HashSet<Entity>,
}
/// Adds an effect to the queue
pub fn add_effect(creator: Option<Entity>, effect_type: EffectType, targets: Targets) {
EFFECT_QUEUE.lock().unwrap().push_back(EffectSpawner {
creator,
effect_type,
targets,
dedupe: HashSet::new(),
});
}
pub fn run_effects_queue(ecs: &mut World) {
loop {
let effect = EFFECT_QUEUE.lock().unwrap().pop_front();
if let Some(mut effect) = effect {
target_applicator(ecs, &mut effect);
} else {
break;
}
}
}
fn target_applicator(ecs: &mut World, effect: &mut EffectSpawner) {
if let EffectType::ItemUse { item } = effect.effect_type {
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 {
triggers::trigger(effect.creator, trigger, &effect.targets, ecs);
} else {
match &effect.targets.clone() {
Targets::Tile { tile_idx } => affect_tile(ecs, effect, *tile_idx),
Targets::Tiles { tiles } => tiles
.iter()
.for_each(|tile_idx| affect_tile(ecs, effect, *tile_idx)),
Targets::Single { target } => affect_entity(ecs, effect, *target),
Targets::TargetList { targets } => targets
.iter()
.for_each(|entity| affect_entity(ecs, effect, *entity)),
}
}
}
fn tile_effect_hits_entities(effect: &EffectType) -> bool {
matches!(
effect,
EffectType::Damage { .. }
| EffectType::WellFed
| EffectType::Healing { .. }
| EffectType::Confusion { .. }
| EffectType::TeleportTo { .. }
| EffectType::AttributeEffect { .. }
| EffectType::Mana { .. }
| EffectType::Slow { .. }
| EffectType::DamageOverTime { .. }
)
}
fn affect_tile(ecs: &mut World, effect: &mut EffectSpawner, tile_idx: i32) {
if tile_effect_hits_entities(&effect.effect_type) {
spatial::for_each_tile_content(tile_idx as usize, |entity| {
affect_entity(ecs, effect, entity)
});
}
match &effect.effect_type {
EffectType::Bloodstain => damage::bloodstain(ecs, tile_idx),
EffectType::Particle { .. } => particles::particle_to_tile(ecs, tile_idx, effect),
EffectType::ParticleProjectile { .. } => particles::projectile(ecs, tile_idx, effect),
_ => {}
}
}
fn affect_entity(ecs: &mut World, effect: &mut EffectSpawner, target: Entity) {
if effect.dedupe.contains(&target) {
return;
}
effect.dedupe.insert(target);
match &effect.effect_type {
EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target),
EffectType::EntityDeath => damage::death(ecs, effect, target),
EffectType::Bloodstain { .. } => {
if let Some(pos) = entity_position(ecs, target) {
damage::bloodstain(ecs, pos)
}
}
EffectType::Particle { .. } => {
if let Some(pos) = entity_position(ecs, target) {
particles::particle_to_tile(ecs, pos, effect)
}
}
EffectType::WellFed => hunger::well_fed(ecs, effect, target),
EffectType::Healing { .. } => damage::heal_damage(ecs, effect, target),
EffectType::Mana { .. } => damage::restore_mana(ecs, effect, target),
EffectType::Confusion { .. } => damage::add_confusion(ecs, effect, target),
EffectType::TeleportTo { .. } => movement::apply_teleport(ecs, effect, target),
EffectType::AttributeEffect { .. } => damage::attribute_effect(ecs, effect, target),
EffectType::Slow { .. } => damage::slow(ecs, effect, target),
EffectType::DamageOverTime { .. } => damage::damage_over_time(ecs, effect, target),
_ => {}
}
}