basic particles
This commit is contained in:
parent
a79669d55c
commit
f76b705fe6
6 changed files with 146 additions and 5 deletions
|
|
@ -97,3 +97,8 @@ pub struct WantsToUseItem {
|
|||
|
||||
#[derive(Component, Debug)]
|
||||
pub struct Consumable {}
|
||||
|
||||
#[derive(Component, Clone)]
|
||||
pub struct ParticleLifetime {
|
||||
pub lifetime_ms: f32,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use super::{
|
||||
gamelog::GameLog, CombatStats, Consumable, InBackpack, Name, Position, ProvidesHealing, WantsToDropItem,
|
||||
WantsToPickupItem, WantsToUseItem,
|
||||
gamelog::GameLog, CombatStats, Consumable, InBackpack, Name, ParticleBuilder, Position, ProvidesHealing,
|
||||
WantsToDropItem, WantsToPickupItem, WantsToUseItem, DEFAULT_PARTICLE_LIFETIME,
|
||||
};
|
||||
use specs::prelude::*;
|
||||
|
||||
|
|
@ -45,10 +45,23 @@ impl<'a> System<'a> for ItemUseSystem {
|
|||
ReadStorage<'a, Consumable>,
|
||||
ReadStorage<'a, ProvidesHealing>,
|
||||
WriteStorage<'a, CombatStats>,
|
||||
WriteExpect<'a, ParticleBuilder>,
|
||||
ReadStorage<'a, Position>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (player_entity, mut gamelog, entities, mut wants_use, names, consumables, healing, mut combat_stats) = data;
|
||||
let (
|
||||
player_entity,
|
||||
mut gamelog,
|
||||
entities,
|
||||
mut wants_use,
|
||||
names,
|
||||
consumables,
|
||||
healing,
|
||||
mut combat_stats,
|
||||
mut particle_builder,
|
||||
positions,
|
||||
) = data;
|
||||
|
||||
for (entity, use_item, stats) in (&entities, &wants_use, &mut combat_stats).join() {
|
||||
let item_heals = healing.get(use_item.item);
|
||||
|
|
@ -56,6 +69,17 @@ impl<'a> System<'a> for ItemUseSystem {
|
|||
None => {}
|
||||
Some(healer) => {
|
||||
stats.hp = i32::min(stats.max_hp, stats.hp + healer.heal_amount);
|
||||
let pos = positions.get(entity);
|
||||
if let Some(pos) = pos {
|
||||
particle_builder.request(
|
||||
pos.x,
|
||||
pos.y,
|
||||
rltk::RGB::named(rltk::GREEN),
|
||||
rltk::RGB::named(rltk::BLACK),
|
||||
rltk::to_cp437('♥'),
|
||||
DEFAULT_PARTICLE_LIFETIME,
|
||||
);
|
||||
}
|
||||
if entity == *player_entity {
|
||||
gamelog.entries.push(format!(
|
||||
"You quaff the {}, and heal {} hp.",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ mod melee_combat_system;
|
|||
use melee_combat_system::MeleeCombatSystem;
|
||||
mod inventory_system;
|
||||
use inventory_system::*;
|
||||
mod particle_system;
|
||||
use particle_system::{ParticleBuilder, DEFAULT_PARTICLE_LIFETIME};
|
||||
|
||||
// Embedded resources for use in wasm build
|
||||
rltk::embedded_resource!(TERMINAL8X8, "../resources/terminal8x8.jpg");
|
||||
|
|
@ -62,6 +64,8 @@ impl State {
|
|||
item_use_system.run_now(&self.ecs);
|
||||
let mut drop_system = ItemDropSystem {};
|
||||
drop_system.run_now(&self.ecs);
|
||||
let mut particle_system = particle_system::ParticleSpawnSystem {};
|
||||
particle_system.run_now(&self.ecs);
|
||||
self.ecs.maintain();
|
||||
}
|
||||
}
|
||||
|
|
@ -70,6 +74,7 @@ impl GameState for State {
|
|||
fn tick(&mut self, ctx: &mut Rltk) {
|
||||
// Clear screen
|
||||
ctx.cls();
|
||||
particle_system::cull_dead_particles(&mut self.ecs, ctx);
|
||||
|
||||
// Draw map and ui
|
||||
draw_map(&self.ecs, ctx);
|
||||
|
|
@ -186,6 +191,7 @@ fn main() -> rltk::BError {
|
|||
gs.ecs.register::<WantsToDropItem>();
|
||||
gs.ecs.register::<WantsToUseItem>();
|
||||
gs.ecs.register::<Consumable>();
|
||||
gs.ecs.register::<ParticleLifetime>();
|
||||
|
||||
let map = Map::new_map_rooms_and_corridors();
|
||||
let (player_x, player_y) = map.rooms[0].centre();
|
||||
|
|
@ -202,6 +208,7 @@ fn main() -> rltk::BError {
|
|||
gs.ecs.insert(player_entity);
|
||||
gs.ecs.insert(gamelog::GameLog { entries: vec!["Here's your welcome message.".to_string()] });
|
||||
gs.ecs.insert(RunState::PreRun);
|
||||
gs.ecs.insert(particle_system::ParticleBuilder::new());
|
||||
|
||||
rltk::main_loop(context, gs)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use super::{gamelog::GameLog, CombatStats, Name, SufferDamage, WantsToMelee};
|
||||
use super::{gamelog::GameLog, CombatStats, Name, ParticleBuilder, Position, SufferDamage, WantsToMelee};
|
||||
use specs::prelude::*;
|
||||
|
||||
pub struct MeleeCombatSystem {}
|
||||
|
|
@ -11,10 +11,21 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
|||
ReadStorage<'a, Name>,
|
||||
ReadStorage<'a, CombatStats>,
|
||||
WriteStorage<'a, SufferDamage>,
|
||||
WriteExpect<'a, ParticleBuilder>,
|
||||
ReadStorage<'a, Position>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (entities, mut log, mut wants_melee, names, combat_stats, mut inflict_damage) = data;
|
||||
let (
|
||||
entities,
|
||||
mut log,
|
||||
mut wants_melee,
|
||||
names,
|
||||
combat_stats,
|
||||
mut inflict_damage,
|
||||
mut particle_builder,
|
||||
positions,
|
||||
) = data;
|
||||
|
||||
for (_entity, wants_melee, name, stats) in (&entities, &wants_melee, &names, &combat_stats).join() {
|
||||
if stats.hp <= 0 {
|
||||
|
|
@ -26,6 +37,17 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
|||
}
|
||||
|
||||
let target_name = names.get(wants_melee.target).unwrap();
|
||||
let pos = positions.get(wants_melee.target);
|
||||
if let Some(pos) = pos {
|
||||
particle_builder.request(
|
||||
pos.x,
|
||||
pos.y,
|
||||
rltk::RGB::named(rltk::ORANGE),
|
||||
rltk::RGB::named(rltk::BLACK),
|
||||
rltk::to_cp437('‼'),
|
||||
150.0,
|
||||
);
|
||||
}
|
||||
let damage = i32::max(0, stats.power - target_stats.defence);
|
||||
|
||||
if damage == 0 {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ impl<'a> System<'a> for MonsterAI {
|
|||
.insert(entity, WantsToMelee { target: *player_entity })
|
||||
.expect("Unable to insert attack.");
|
||||
} else if viewshed.visible_tiles.contains(&*player_pos) {
|
||||
// If the player is visible, but the path is obstructed, this will currently search
|
||||
// the entire map (i.e. Will do a huge ASTAR to find an alternate route), and the
|
||||
// mob will follow that path until it leaves vision, then lose sight of the player
|
||||
// and stop.
|
||||
let path = rltk::a_star_search(map.xy_idx(pos.x, pos.y), map.xy_idx(player_pos.x, player_pos.y), &*map);
|
||||
if path.success && path.steps.len() > 1 {
|
||||
let mut idx = map.xy_idx(pos.x, pos.y);
|
||||
|
|
|
|||
79
src/particle_system.rs
Normal file
79
src/particle_system.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use super::{ParticleLifetime, Position, Renderable, Rltk};
|
||||
use rltk::RGB;
|
||||
use specs::prelude::*;
|
||||
|
||||
pub const DEFAULT_PARTICLE_LIFETIME: f32 = 150.0;
|
||||
|
||||
pub fn cull_dead_particles(ecs: &mut World, ctx: &Rltk) {
|
||||
let mut dead_particles: Vec<Entity> = Vec::new();
|
||||
{
|
||||
// Age out particles
|
||||
let mut particles = ecs.write_storage::<ParticleLifetime>();
|
||||
let entities = ecs.entities();
|
||||
for (entity, mut particle) in (&entities, &mut particles).join() {
|
||||
particle.lifetime_ms -= ctx.frame_time_ms;
|
||||
if particle.lifetime_ms < 0.0 {
|
||||
dead_particles.push(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
for dead in dead_particles.iter() {
|
||||
ecs.delete_entity(*dead).expect("Particle will not die");
|
||||
}
|
||||
}
|
||||
|
||||
struct ParticleRequest {
|
||||
x: i32,
|
||||
y: i32,
|
||||
fg: RGB,
|
||||
bg: RGB,
|
||||
glyph: rltk::FontCharType,
|
||||
lifetime: f32,
|
||||
}
|
||||
|
||||
pub struct ParticleBuilder {
|
||||
requests: Vec<ParticleRequest>,
|
||||
}
|
||||
|
||||
impl ParticleBuilder {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> ParticleBuilder {
|
||||
ParticleBuilder { requests: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn request(&mut self, x: i32, y: i32, fg: RGB, bg: RGB, glyph: rltk::FontCharType, lifetime: f32) {
|
||||
self.requests.push(ParticleRequest { x, y, fg, bg, glyph, lifetime });
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ParticleSpawnSystem {}
|
||||
|
||||
impl<'a> System<'a> for ParticleSpawnSystem {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
WriteStorage<'a, Position>,
|
||||
WriteStorage<'a, Renderable>,
|
||||
WriteStorage<'a, ParticleLifetime>,
|
||||
WriteExpect<'a, ParticleBuilder>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (entities, mut positions, mut renderables, mut particles, mut particle_builder) = data;
|
||||
|
||||
for new_particle in particle_builder.requests.iter() {
|
||||
let p = entities.create();
|
||||
positions.insert(p, Position { x: new_particle.x, y: new_particle.y }).expect("Could not insert position");
|
||||
renderables
|
||||
.insert(
|
||||
p,
|
||||
Renderable { fg: new_particle.fg, bg: new_particle.bg, glyph: new_particle.glyph, render_order: 0 },
|
||||
)
|
||||
.expect("Could not insert renderables");
|
||||
particles
|
||||
.insert(p, ParticleLifetime { lifetime_ms: new_particle.lifetime })
|
||||
.expect("Could not insert lifetime");
|
||||
}
|
||||
particle_builder.requests.clear();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue