From 03a90aba440b46a374c5b1dc0af35f7e9dd8cd46 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Thu, 20 Jan 2022 19:41:16 -0500 Subject: [PATCH] Complete Section 5.21 --- src/effects/damage.rs | 2 +- src/effects/targeting.rs | 27 ++++++++++++++++ src/effects/triggers.rs | 67 ++++++++++++++++++++++++++++++++++++++-- src/raws/rawmaster.rs | 20 ++++++++++++ 4 files changed, 113 insertions(+), 3 deletions(-) diff --git a/src/effects/damage.rs b/src/effects/damage.rs index 67de0c6..de521bd 100644 --- a/src/effects/damage.rs +++ b/src/effects/damage.rs @@ -47,7 +47,7 @@ pub fn death(ecs: &mut World, effect: &EffectSpawner, target: Entity) { let mut pools = ecs.write_storage::(); let attributes = ecs.read_storage::(); - let map = ecs.fetch_mut::(); + let map = ecs.fetch::(); if let Some(pos) = entity_position(ecs, target) { crate::spatial::remove_entity(target, pos as usize); diff --git a/src/effects/targeting.rs b/src/effects/targeting.rs index f3cdbf9..88792ae 100644 --- a/src/effects/targeting.rs +++ b/src/effects/targeting.rs @@ -22,3 +22,30 @@ pub fn aoe_tiles(map: &Map, target: ::rltk::Point, radius: i32) -> Vec { } result } + +pub fn find_item_position(ecs: &World, target: Entity) -> Option { + let positions = ecs.read_storage::(); + let map = ecs.fetch::(); + + // Easy - it has a position + if let Some(pos) = positions.get(target) { + return Some(map.xy_idx(pos.x, pos.y) as i32); + } + + // Maybe it is carried? + if let Some(carried) = ecs.read_storage::().get(target) { + if let Some(pos) = positions.get(carried.owner) { + return Some(map.xy_idx(pos.x, pos.y) as i32); + } + } + + // Maybe it is equipped? + if let Some(equipped) = ecs.read_storage::().get(target) { + if let Some(pos) = positions.get(equipped.owner) { + return Some(map.xy_idx(pos.x, pos.y) as i32); + } + } + + // No idea - give up + None +} diff --git a/src/effects/triggers.rs b/src/effects/triggers.rs index 5b50525..f7d6a4c 100644 --- a/src/effects/triggers.rs +++ b/src/effects/triggers.rs @@ -3,9 +3,11 @@ use ::specs::prelude::*; use super::{add_effect, EffectType, Targets}; use crate::components::{ Confusion, Consumable, Hidden, InflictsDamage, MagicMapper, Name, ProvidesFood, - ProvidesHealing, SingleActivation, TeleportTo, TownPortal, + ProvidesHealing, SingleActivation, SpawnParticleBurst, SpawnParticleLine, TeleportTo, + TownPortal, }; -use crate::{GameLog, Map, RunState}; +use crate::effects::{entity_position, targeting}; +use crate::{colors, GameLog, Map, RunState}; pub fn item_trigger(creator: Option, item: Entity, targets: &Targets, ecs: &mut World) { // Use the item via the generic system @@ -48,6 +50,44 @@ fn event_trigger( 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) { + 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()); @@ -137,3 +177,26 @@ fn event_trigger( did_something } + +fn spawn_line_particles(ecs: &World, start: i32, end: i32, part: &SpawnParticleLine) { + use ::rltk::{LineAlg, Point}; + + 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, + }, + ); + } +} diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index f422386..809823f 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -268,6 +268,24 @@ pub fn string_to_slot(slot: &str) -> EquipmentSlot { } } +fn parse_particle_line(n: &str) -> SpawnParticleLine { + let tokens: Vec<_> = n.split(';').collect(); + SpawnParticleLine { + glyph: rltk::to_cp437(tokens[0].chars().next().unwrap()), + color: RGB::from_hex(tokens[1]).expect("Invalid hex rgb color"), + lifetime_ms: tokens[2].parse::().unwrap(), + } +} + +fn parse_particle(n: &str) -> SpawnParticleBurst { + let tokens: Vec<_> = n.split(';').collect(); + SpawnParticleBurst { + glyph: rltk::to_cp437(tokens[0].chars().next().unwrap()), + color: RGB::from_hex(tokens[1]).expect("Invalid hex rgb color"), + lifetime_ms: tokens[2].parse::().unwrap(), + } +} + macro_rules! apply_effects { ($effects:expr, $eb:expr) => { for effect in $effects.iter() { @@ -302,6 +320,8 @@ macro_rules! apply_effects { "town_portal" => $eb = $eb.with(TownPortal {}), "food" => $eb = $eb.with(ProvidesFood {}), "single_activation" => $eb = $eb.with(SingleActivation {}), + "particle_line" => $eb = $eb.with(parse_particle_line(&effect.1)), + "particle" => $eb = $eb.with(parse_particle(&effect.1)), _ => { ::rltk::console::log(format!( "Warning: consumable effect {} not implemented.",