From 366c5d6543cde52623535cd441eeb85308f4f983 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Mon, 21 Aug 2023 22:43:19 +0100 Subject: [PATCH] fancy particle effects --- raws/items.json | 7 +- src/components.rs | 12 ++- src/effects/particles.rs | 150 +++++++++++++++++++++++++++++++++- src/effects/triggers.rs | 77 ++--------------- src/gui/character_creation.rs | 2 +- src/main.rs | 1 + src/raws/rawmaster.rs | 17 +++- src/saveload_system.rs | 2 + 8 files changed, 189 insertions(+), 79 deletions(-) diff --git a/raws/items.json b/raws/items.json index 2755524..328a9c0 100644 --- a/raws/items.json +++ b/raws/items.json @@ -66,7 +66,12 @@ "weight": 0.5, "value": 200, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], - "effects": { "particle": "*;#FFA500;200.0", "ranged": "10", "damage": "8d6", "aoe": "3" }, + "effects": { + "particle_burst": "*;#FFA500;#000000;600.0;#ff9595;75.0", + "ranged": "10", + "damage": "8d6", + "aoe": "3" + }, "magic": { "class": "rare", "naming": "scroll" } }, { diff --git a/src/components.rs b/src/components.rs index 1374254..a4d9220 100644 --- a/src/components.rs +++ b/src/components.rs @@ -420,12 +420,22 @@ pub struct SpawnParticleLine { } #[derive(Component, Serialize, Deserialize, Clone)] -pub struct SpawnParticleBurst { +pub struct SpawnParticleSimple { pub glyph: rltk::FontCharType, pub colour: RGB, pub lifetime_ms: f32, } +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct SpawnParticleBurst { + pub glyph: rltk::FontCharType, + pub colour: RGB, + pub lerp: RGB, + pub lifetime_ms: f32, + pub trail_colour: RGB, + pub trail_lifetime_ms: f32, +} + #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Destructible {} diff --git a/src/effects/particles.rs b/src/effects/particles.rs index 9e64d9c..4cb05cd 100644 --- a/src/effects/particles.rs +++ b/src/effects/particles.rs @@ -1,5 +1,6 @@ -use super::{EffectSpawner, EffectType}; -use crate::{Map, ParticleBuilder}; +use super::{add_effect, targeting, EffectSpawner, EffectType, Targets}; +use crate::{Map, ParticleBuilder, SpawnParticleBurst, SpawnParticleLine, SpawnParticleSimple}; +use rltk::prelude::*; use specs::prelude::*; pub fn particle_to_tile(ecs: &mut World, target: i32, effect: &EffectSpawner) { @@ -13,3 +14,148 @@ pub fn particle_to_tile(ecs: &mut World, target: i32, effect: &EffectSpawner) { } } } + +pub fn handle_simple_particles(ecs: &World, entity: Entity, target: &Targets) { + if let Some(part) = ecs.read_storage::().get(entity) { + add_effect( + None, + EffectType::Particle { + glyph: part.glyph, + fg: part.colour, + bg: RGB::named(BLACK), + lifespan: part.lifetime_ms, + delay: 0.0, + }, + target.clone(), + ); + } +} + +pub fn handle_burst_particles(ecs: &World, entity: Entity, target: &Targets) { + if let Some(part) = ecs.read_storage::().get(entity) { + if let Some(start_pos) = targeting::find_item_position(ecs, entity) { + let end_pos: i32 = get_centre(ecs, target); + spawn_line_particles( + ecs, + start_pos, + end_pos, + &SpawnParticleLine { + glyph: part.glyph, + colour: part.colour, + trail_colour: part.colour, + lifetime_ms: part.trail_lifetime_ms, // 75.0 is good here. + trail_lifetime_ms: part.trail_lifetime_ms + 25.0, + }, + ); + let map = ecs.fetch::(); + let line = line2d( + LineAlg::Bresenham, + Point::new(start_pos % map.width, start_pos / map.width), + Point::new(end_pos % map.width, end_pos / map.width), + ); + let burst_delay = line.len() as f32 * 75.0; + for i in 0..10 { + add_effect( + None, + EffectType::Particle { + glyph: part.glyph, + fg: part.colour.lerp(part.lerp, i as f32 * 0.1), + bg: RGB::named(BLACK), + lifespan: part.lifetime_ms / 10.0, // ~50-80 is good here. + delay: burst_delay + (i as f32 * part.lifetime_ms / 10.0), + }, + target.clone(), + ); + } + } + } +} + +fn get_centre(ecs: &World, target: &Targets) -> i32 { + match target { + Targets::Tile { target } => return *target as i32, + Targets::TileList { targets } => { + let map = ecs.fetch::(); + let (mut count, mut sum_x, mut sum_y) = (0, 0, 0); + for target in targets { + sum_x += *target as i32 % map.width; + sum_y += *target as i32 / map.width; + count += 1; + } + let (mean_x, mean_y) = (sum_x / count, sum_y / count); + let centre = map.xy_idx(mean_x, mean_y); + return centre as i32; + } + Targets::Entity { target } => return targeting::entity_position(ecs, *target).unwrap() as i32, + Targets::EntityList { targets } => { + let map = ecs.fetch::(); + let (mut count, mut sum_x, mut sum_y) = (0, 0, 0); + for target in targets { + if let Some(pos) = targeting::entity_position(ecs, *target) { + sum_x += pos as i32 % map.width; + sum_y += pos as i32 / map.width; + count += 1; + } + } + let (mean_x, mean_y) = (sum_x / count, sum_y / count); + let centre = map.xy_idx(mean_x, mean_y); + return centre as i32; + } + } +} + +pub fn handle_line_particles(ecs: &World, entity: Entity, target: &Targets) { + if let Some(part) = ecs.read_storage::().get(entity) { + if let Some(start_pos) = targeting::find_item_position(ecs, entity) { + match target { + Targets::Tile { target } => spawn_line_particles(ecs, start_pos, *target as i32, part), + Targets::TileList { targets } => { + targets.iter().for_each(|target| spawn_line_particles(ecs, start_pos, *target as i32, part)) + } + Targets::Entity { target } => { + if let Some(end_pos) = targeting::entity_position(ecs, *target) { + spawn_line_particles(ecs, start_pos, end_pos as i32, part); + } + } + Targets::EntityList { targets } => targets.iter().for_each(|target| { + if let Some(end_pos) = targeting::entity_position(ecs, *target) { + spawn_line_particles(ecs, start_pos, end_pos as i32, part); + } + }), + } + } + } +} + +fn spawn_line_particles(ecs: &World, start: i32, end: i32, part: &SpawnParticleLine) { + let map = ecs.fetch::(); + let start_pt = Point::new(start % map.width, start / map.width); + let end_pt = Point::new(end % map.width, end / map.width); + let line = line2d(LineAlg::Bresenham, start_pt, end_pt); + for (i, pt) in line.iter().enumerate() { + add_effect( + None, + EffectType::Particle { + glyph: part.glyph, + fg: part.colour, + bg: RGB::named(BLACK), + lifespan: part.lifetime_ms, + delay: i as f32 * part.lifetime_ms, + }, + Targets::Tile { target: map.xy_idx(pt.x, pt.y) }, + ); + if i > 0 { + add_effect( + None, + EffectType::Particle { + glyph: to_cp437('-'), + fg: part.trail_colour, + bg: RGB::named(BLACK), + lifespan: part.trail_lifetime_ms, + delay: i as f32 * part.lifetime_ms, + }, + Targets::Tile { target: map.xy_idx(line[i - 1].x, line[i - 1].y) }, + ); + } + } +} diff --git a/src/effects/triggers.rs b/src/effects/triggers.rs index 7f268b0..7889ec1 100644 --- a/src/effects/triggers.rs +++ b/src/effects/triggers.rs @@ -1,9 +1,8 @@ -use super::{add_effect, get_noncursed, spatial, targeting, EffectType, Entity, Targets, World}; +use super::{add_effect, get_noncursed, particles, spatial, targeting, EffectType, Entity, Targets, World}; use crate::{ gamelog, gui::item_colour_ecs, gui::obfuscate_name_ecs, gui::renderable_colour, Beatitude, Charges, Confusion, - Consumable, Destructible, Hidden, InflictsDamage, Item, MagicMapper, Map, Player, Prop, ProvidesHealing, - ProvidesNutrition, RandomNumberGenerator, Renderable, RunState, SingleActivation, SpawnParticleBurst, - SpawnParticleLine, BUC, + Consumable, Destructible, Hidden, InflictsDamage, Item, MagicMapper, Player, Prop, ProvidesHealing, + ProvidesNutrition, RandomNumberGenerator, Renderable, RunState, SingleActivation, BUC, }; use rltk::prelude::*; use specs::prelude::*; @@ -62,41 +61,8 @@ fn event_trigger(source: Option, entity: Entity, target: &Targets, ecs: let logger = gamelog::Logger::new(); let mut did_something = false; - // Simple particle spawn - if let Some(part) = ecs.read_storage::().get(entity) { - add_effect( - event.source, - EffectType::Particle { - glyph: part.glyph, - fg: part.colour, - bg: RGB::named(BLACK), - lifespan: part.lifetime_ms, - delay: 0.0, - }, - event.target.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 target { - Targets::Tile { target } => spawn_line_particles(ecs, start_pos, *target as i32, part), - Targets::TileList { targets } => { - targets.iter().for_each(|target| spawn_line_particles(ecs, start_pos, *target as i32, part)) - } - Targets::Entity { target } => { - if let Some(end_pos) = targeting::entity_position(ecs, *target) { - spawn_line_particles(ecs, start_pos, end_pos as i32, part); - } - } - Targets::EntityList { targets } => targets.iter().for_each(|target| { - if let Some(end_pos) = targeting::entity_position(ecs, *target) { - spawn_line_particles(ecs, start_pos, end_pos as i32, part); - } - }), - } - } - } + particles::handle_burst_particles(ecs, entity, &target); + particles::handle_line_particles(ecs, entity, &target); let (logger, restored_nutrition) = handle_restore_nutrition(ecs, &mut event, logger); let (logger, magic_mapped) = handle_magic_mapper(ecs, &mut event, logger); let (logger, healed) = handle_healing(ecs, &mut event, logger); @@ -262,36 +228,3 @@ fn get_entity_targets(target: &Targets) -> Vec { } return entities; } - -fn spawn_line_particles(ecs: &World, start: i32, end: i32, part: &SpawnParticleLine) { - let map = ecs.fetch::(); - let start_pt = Point::new(start % map.width, start / map.width); - let end_pt = Point::new(end % map.width, end / map.width); - let line = line2d(LineAlg::Bresenham, start_pt, end_pt); - for (i, pt) in line.iter().enumerate() { - add_effect( - None, - EffectType::Particle { - glyph: part.glyph, - fg: part.colour, - bg: RGB::named(BLACK), - lifespan: part.lifetime_ms, - delay: i as f32 * part.lifetime_ms, - }, - Targets::Tile { target: map.xy_idx(pt.x, pt.y) }, - ); - if i > 0 { - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('-'), - fg: part.trail_colour, - bg: RGB::named(BLACK), - lifespan: part.trail_lifetime_ms, - delay: i as f32 * part.lifetime_ms, - }, - Targets::Tile { target: map.xy_idx(line[i - 1].x, line[i - 1].y) }, - ); - } - } -} diff --git a/src/gui/character_creation.rs b/src/gui/character_creation.rs index 91773ff..13f8fe0 100644 --- a/src/gui/character_creation.rs +++ b/src/gui/character_creation.rs @@ -338,7 +338,7 @@ fn get_starting_inventory(class: Class, rng: &mut RandomNumberGenerator) -> (Vec Class::Rogue => { starting_food = "1d2+2"; equipped = vec!["equip_rapier".to_string(), "equip_body_weakleather".to_string()]; - carried = vec!["equip_dagger".to_string(), "equip_dagger".to_string()]; + carried = vec!["equip_dagger".to_string(), "equip_dagger".to_string(), "scroll_fireball".to_string()]; } Class::Wizard => { starting_food = "1d2+1"; diff --git a/src/main.rs b/src/main.rs index 0683dd4..183da8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -618,6 +618,7 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::>(); diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index a0d0f2a..41aae0a 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -30,6 +30,7 @@ macro_rules! apply_effects { "magicmapper" => $eb = $eb.with(MagicMapper {}), "digger" => $eb = $eb.with(Digger {}), "particle_line" => $eb = $eb.with(parse_particle_line(&effect.1)), + "particle_burst" => $eb = $eb.with(parse_particle_burst(&effect.1)), "particle" => $eb = $eb.with(parse_particle(&effect.1)), _ => console::log(format!("Warning: effect {} not implemented.", effect_name)), } @@ -889,11 +890,23 @@ fn parse_particle_line(n: &str) -> SpawnParticleLine { } } -fn parse_particle(n: &str) -> SpawnParticleBurst { +fn parse_particle(n: &str) -> SpawnParticleSimple { let tokens: Vec<_> = n.split(';').collect(); - SpawnParticleBurst { + SpawnParticleSimple { glyph: to_cp437(tokens[0].chars().next().unwrap()), colour: RGB::from_hex(tokens[1]).expect("Invalid RGB"), lifetime_ms: tokens[2].parse::().unwrap(), } } + +fn parse_particle_burst(n: &str) -> SpawnParticleBurst { + let tokens: Vec<_> = n.split(';').collect(); + SpawnParticleBurst { + glyph: to_cp437(tokens[0].chars().next().unwrap()), + colour: RGB::from_hex(tokens[1]).expect("Invalid RGB"), + lerp: RGB::from_hex(tokens[2]).expect("Invalid LERP RGB"), + lifetime_ms: tokens[3].parse::().unwrap(), + trail_colour: RGB::from_hex(tokens[4]).expect("Invalid trail RGB"), + trail_lifetime_ms: tokens[5].parse::().unwrap(), + } +} diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 3dc9cd4..37f52fb 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -105,6 +105,7 @@ pub fn save_game(ecs: &mut World) { Skills, SpawnParticleBurst, SpawnParticleLine, + SpawnParticleSimple, TakingTurn, Telepath, Viewshed, @@ -226,6 +227,7 @@ pub fn load_game(ecs: &mut World) { Skills, SpawnParticleBurst, SpawnParticleLine, + SpawnParticleSimple, TakingTurn, Telepath, Viewshed,