Add particle effects for Melee combat
This commit is contained in:
parent
c4bc637c79
commit
1dd5db42f7
@ -174,6 +174,11 @@ pub struct DefenseBonus {
|
||||
pub defense: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||
pub struct ParticleLifetime {
|
||||
pub lifetime_ms: f32,
|
||||
}
|
||||
|
||||
// Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an
|
||||
// Entity.
|
||||
|
||||
|
@ -12,6 +12,7 @@ mod map;
|
||||
mod map_indexing_system;
|
||||
mod melee_combat_system;
|
||||
mod monster_ai_system;
|
||||
mod particle_system;
|
||||
mod player;
|
||||
pub mod random_table;
|
||||
mod rect;
|
||||
@ -101,6 +102,9 @@ impl State {
|
||||
let mut item_remove = ItemRemoveSystem {};
|
||||
item_remove.run_now(&self.ecs);
|
||||
|
||||
let mut particles = particle_system::ParticleSpawnSystem {};
|
||||
particles.run_now(&self.ecs);
|
||||
|
||||
self.ecs.maintain();
|
||||
}
|
||||
}
|
||||
@ -114,6 +118,7 @@ impl GameState for State {
|
||||
}
|
||||
|
||||
ctx.cls();
|
||||
particle_system::cull_dead_particles(&mut self.ecs, ctx);
|
||||
|
||||
match newrunstate {
|
||||
RunState::MainMenu { .. } => {}
|
||||
@ -485,6 +490,7 @@ fn main() -> rltk::BError {
|
||||
MeleePowerBonus,
|
||||
DefenseBonus,
|
||||
WantsToRemoveItem,
|
||||
ParticleLifetime,
|
||||
);
|
||||
|
||||
gs.ecs.insert(SimpleMarkerAllocator::<SerializeMe>::new());
|
||||
@ -506,6 +512,7 @@ fn main() -> rltk::BError {
|
||||
menu_selection: gui::MainMenuSelection::NewGame,
|
||||
});
|
||||
gs.ecs.insert(GameLog::new("Welcome to Rusty Roguelike"));
|
||||
gs.ecs.insert(particle_system::ParticleBuilder::new());
|
||||
|
||||
rltk::main_loop(context, gs)
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::components::{
|
||||
CombatStats, DefenseBonus, Equipped, MeleePowerBonus, Name, SufferDamage, WantsToMelee,
|
||||
};
|
||||
use crate::game_log::GameLog;
|
||||
use crate::{game_log::GameLog, particle_system::ParticleBuilder, Position};
|
||||
use rltk::RGB;
|
||||
use specs::prelude::*;
|
||||
|
||||
pub struct MeleeCombatSystem {}
|
||||
@ -18,6 +19,8 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||
ReadStorage<'a, MeleePowerBonus>,
|
||||
ReadStorage<'a, DefenseBonus>,
|
||||
ReadStorage<'a, Equipped>,
|
||||
WriteExpect<'a, ParticleBuilder>,
|
||||
ReadStorage<'a, Position>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
@ -31,6 +34,8 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||
melee_power_bonuses,
|
||||
defense_bonuses,
|
||||
equipped,
|
||||
mut particle_builder,
|
||||
positions,
|
||||
) = data;
|
||||
|
||||
for (entity, wants_melee, name, stats) in
|
||||
@ -59,6 +64,17 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pos) = positions.get(wants_melee.target) {
|
||||
particle_builder.request(
|
||||
pos.x,
|
||||
pos.y,
|
||||
RGB::named(rltk::ORANGE),
|
||||
RGB::named(rltk::BLACK),
|
||||
rltk::to_cp437('‼'),
|
||||
200.0,
|
||||
);
|
||||
}
|
||||
|
||||
let damage = i32::max(
|
||||
0,
|
||||
(stats.power + offensive_bonus) - (target_stats.defense + defensive_bonus),
|
||||
|
118
src/particle_system.rs
Normal file
118
src/particle_system.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use crate::components::{ParticleLifetime, Renderable};
|
||||
use crate::Position;
|
||||
use rltk::{Rltk, RGB};
|
||||
use specs::prelude::*;
|
||||
|
||||
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("Failed to delete dead particle entity");
|
||||
}
|
||||
}
|
||||
|
||||
struct ParticleRequest {
|
||||
x: i32,
|
||||
y: i32,
|
||||
fg: RGB,
|
||||
bg: RGB,
|
||||
glyph: rltk::FontCharType,
|
||||
lifetime: f32,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ParticleBuilder {
|
||||
requests: Vec<ParticleRequest>,
|
||||
}
|
||||
|
||||
impl ParticleBuilder {
|
||||
pub fn new() -> ParticleBuilder {
|
||||
ParticleBuilder::default()
|
||||
}
|
||||
|
||||
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("Failed to insert Position of new particle");
|
||||
|
||||
renderables
|
||||
.insert(
|
||||
p,
|
||||
Renderable {
|
||||
fg: new_particle.fg,
|
||||
bg: new_particle.bg,
|
||||
glyph: new_particle.glyph,
|
||||
render_order: 0,
|
||||
},
|
||||
)
|
||||
.expect("Failed to insert Renderable of new particle");
|
||||
|
||||
particles
|
||||
.insert(
|
||||
p,
|
||||
ParticleLifetime {
|
||||
lifetime_ms: new_particle.lifetime,
|
||||
},
|
||||
)
|
||||
.expect("Failed to insert Lifetime of new particle");
|
||||
}
|
||||
|
||||
particle_builder.requests.clear();
|
||||
}
|
||||
}
|
@ -175,17 +175,8 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||
try_move_player(-1, 1, &mut gs.ecs)
|
||||
}
|
||||
|
||||
// Pick up item
|
||||
VirtualKeyCode::G => get_item(&mut gs.ecs),
|
||||
|
||||
// Show inventory
|
||||
VirtualKeyCode::I => return RunState::ShowInventory,
|
||||
|
||||
// Show item drop screen
|
||||
VirtualKeyCode::D => return RunState::ShowDropItem,
|
||||
|
||||
// Save and Quit
|
||||
VirtualKeyCode::Escape => return RunState::SaveGame,
|
||||
// Skip Turn
|
||||
VirtualKeyCode::Numpad5 | VirtualKeyCode::Space => return skip_turn(&mut gs.ecs),
|
||||
|
||||
// Level changes
|
||||
VirtualKeyCode::Period => {
|
||||
@ -194,12 +185,15 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||
}
|
||||
}
|
||||
|
||||
// Skip Turn
|
||||
VirtualKeyCode::Numpad5 | VirtualKeyCode::Space => return skip_turn(&mut gs.ecs),
|
||||
|
||||
// Remove item
|
||||
// Item management
|
||||
VirtualKeyCode::G => get_item(&mut gs.ecs),
|
||||
VirtualKeyCode::I => return RunState::ShowInventory,
|
||||
VirtualKeyCode::D => return RunState::ShowDropItem,
|
||||
VirtualKeyCode::R => return RunState::ShowRemoveItem,
|
||||
|
||||
// Save and Quit
|
||||
VirtualKeyCode::Escape => return RunState::SaveGame,
|
||||
|
||||
_ => return RunState::AwaitingInput,
|
||||
},
|
||||
}
|
||||
|
@ -74,7 +74,8 @@ pub fn save_game(ecs: &mut World) {
|
||||
Equipped,
|
||||
MeleePowerBonus,
|
||||
DefenseBonus,
|
||||
WantsToRemoveItem
|
||||
WantsToRemoveItem,
|
||||
ParticleLifetime
|
||||
);
|
||||
}
|
||||
|
||||
@ -155,7 +156,8 @@ pub fn load_game(ecs: &mut World) {
|
||||
Equipped,
|
||||
MeleePowerBonus,
|
||||
DefenseBonus,
|
||||
WantsToRemoveItem
|
||||
WantsToRemoveItem,
|
||||
ParticleLifetime
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user