Implement whipping
This commit is contained in:
parent
2be2b357ad
commit
784e205357
9 changed files with 249 additions and 11 deletions
|
@ -1,3 +1,8 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use specs::prelude::*;
|
||||
use specs_derive::Component;
|
||||
|
||||
pub mod monster;
|
||||
pub mod player;
|
||||
pub mod position;
|
||||
|
@ -7,3 +12,12 @@ pub use monster::Monster;
|
|||
pub use player::Player;
|
||||
pub use position::Position;
|
||||
pub use renderable::Renderable;
|
||||
|
||||
use crate::resources::sound_output::SoundEffectHandle;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct WantsToWhip {
|
||||
pub frame: u8,
|
||||
pub last_frame: Instant,
|
||||
pub sound: Option<SoundEffectHandle>,
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@ pub mod tile_data;
|
|||
pub mod vga_color;
|
||||
|
||||
use bracket_lib::prelude::*;
|
||||
use components::{Monster, Player, Position, Renderable};
|
||||
use resources::{Clock, LevelNumber, Map, ShowDebugInfo, SoundEffects, SoundOutput, Stats};
|
||||
use components::{Monster, Player, Position, Renderable, WantsToWhip};
|
||||
use resources::{
|
||||
Clock, LevelNumber, Map, ShowDebugInfo, SoundEffects, SoundOutput, Stats, StopClock,
|
||||
};
|
||||
use specs::prelude::*;
|
||||
use state::State;
|
||||
use std::time::Instant;
|
||||
|
@ -34,6 +36,7 @@ fn main() -> BError {
|
|||
ecs.insert(sound_system);
|
||||
ecs.insert(LevelNumber(starting_level));
|
||||
ecs.insert(ShowDebugInfo(false));
|
||||
ecs.insert(StopClock(false));
|
||||
ecs.insert(Clock {
|
||||
last_ticked: Instant::now(),
|
||||
has_ticked: false,
|
||||
|
@ -56,6 +59,7 @@ fn main() -> BError {
|
|||
ecs.register::<Renderable>();
|
||||
ecs.register::<Monster>();
|
||||
ecs.register::<Player>();
|
||||
ecs.register::<WantsToWhip>();
|
||||
|
||||
let map = Map::from_level(levels::get_level(starting_level));
|
||||
map.spawn_entities(&mut ecs);
|
||||
|
|
|
@ -85,7 +85,8 @@ impl Map {
|
|||
let point = self.index_to_point2d(index);
|
||||
match tile {
|
||||
TileType::Player => {
|
||||
ecs.create_entity()
|
||||
let player_entity = ecs
|
||||
.create_entity()
|
||||
.with(Position {
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
|
@ -100,6 +101,7 @@ impl Map {
|
|||
})
|
||||
.build();
|
||||
ecs.insert(point);
|
||||
ecs.insert(player_entity);
|
||||
}
|
||||
TileType::Slow => {
|
||||
let mut rng = RandomNumberGenerator::new();
|
||||
|
|
|
@ -39,8 +39,15 @@ impl Clock {
|
|||
self.last_ticked = Instant::now();
|
||||
self.ticks += 1;
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.last_ticked = Instant::now();
|
||||
self.has_ticked = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StopClock(pub bool);
|
||||
|
||||
pub struct Stats {
|
||||
pub score: u32,
|
||||
pub gems: u32,
|
||||
|
|
|
@ -29,6 +29,8 @@ pub struct SoundEffects {
|
|||
pub pickup: SoundSamples,
|
||||
pub bad_key: SoundSamples,
|
||||
pub blocked: SoundSamples,
|
||||
pub whipping: SoundSamples,
|
||||
pub whipping_hit: SoundSamples,
|
||||
rng: RandomNumberGenerator,
|
||||
}
|
||||
|
||||
|
@ -105,6 +107,24 @@ impl SoundEffects {
|
|||
})
|
||||
.collect(),
|
||||
}),
|
||||
whipping: ss.render_sound_effect(&SoundEffect {
|
||||
sounds: vec![Sound {
|
||||
sound_type: SoundType::Tone(70),
|
||||
duration: Duration::from_secs(3),
|
||||
}],
|
||||
}),
|
||||
whipping_hit: ss.render_sound_effect(&SoundEffect {
|
||||
sounds: vec![
|
||||
Sound {
|
||||
sound_type: SoundType::Tone(400),
|
||||
duration: Duration::from_millis(20),
|
||||
},
|
||||
Sound {
|
||||
sound_type: SoundType::Tone(90),
|
||||
duration: Duration::from_secs(3),
|
||||
},
|
||||
],
|
||||
}),
|
||||
rng: RandomNumberGenerator::new(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,13 @@ use cpal::{
|
|||
traits::{DeviceTrait, HostTrait, StreamTrait},
|
||||
SampleRate, Stream,
|
||||
};
|
||||
use oddio::{Frames, Handle, Mixer};
|
||||
use oddio::{Frames, FramesSignal, Gain, Handle, Mixer, MonoToStereo, Stop};
|
||||
use rand::Rng;
|
||||
|
||||
use super::sound_effects::{SoundEffect, SoundType};
|
||||
|
||||
pub type SoundSamples = Arc<Frames<f32>>;
|
||||
pub type SoundEffectHandle = Handle<Stop<MonoToStereo<Gain<FramesSignal<f32>>>>>;
|
||||
|
||||
pub struct SoundOutput {
|
||||
mixer_handle: Handle<Mixer<[f32; 2]>>,
|
||||
|
@ -114,12 +115,12 @@ impl SoundOutput {
|
|||
oddio::Frames::from_iter(self.sample_rate.0, effect_buffer.iter().copied())
|
||||
}
|
||||
|
||||
pub fn play_sound(&mut self, samples: SoundSamples) {
|
||||
pub fn play_sound(&mut self, samples: SoundSamples) -> SoundEffectHandle {
|
||||
self.mixer_handle
|
||||
.control::<oddio::Mixer<_>, _>()
|
||||
.play(oddio::MonoToStereo::new(oddio::Gain::new(
|
||||
oddio::FramesSignal::from(samples),
|
||||
0.20,
|
||||
)));
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
|
82
src/state.rs
82
src/state.rs
|
@ -1,9 +1,10 @@
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::components::monster::damage_for_kind;
|
||||
use crate::components::{Monster, Player, Position, Renderable};
|
||||
use crate::resources::{Clock, Map, ShowDebugInfo, SoundEffects, SoundOutput, Stats};
|
||||
use crate::systems::{MapIndexingSystem, MonsterAiSystem};
|
||||
use crate::components::*;
|
||||
use crate::resources::*;
|
||||
use crate::systems::*;
|
||||
use crate::vga_color as vga;
|
||||
use crate::{constants::*, sidebar};
|
||||
use bracket_lib::prelude::*;
|
||||
use specs::prelude::*;
|
||||
|
@ -35,6 +36,8 @@ impl GameState for State {
|
|||
);
|
||||
}
|
||||
|
||||
self.draw_whip(ctx);
|
||||
|
||||
sidebar::draw(&self.ecs, ctx);
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +68,29 @@ impl State {
|
|||
self.try_move_player(0, 1);
|
||||
}
|
||||
VirtualKeyCode::N | VirtualKeyCode::End => self.try_move_player(-1, 1),
|
||||
VirtualKeyCode::W => {
|
||||
let player_entity = self.ecs.read_resource::<Entity>();
|
||||
let mut stats = self.ecs.write_resource::<Stats>();
|
||||
let positions = self.ecs.read_storage::<Position>();
|
||||
let mut wants_to_whips = self.ecs.write_storage::<WantsToWhip>();
|
||||
if let Some(_position) = positions.get(*player_entity) {
|
||||
if wants_to_whips.get(*player_entity).is_none() && stats.whips > 0 {
|
||||
let mut sound_output = self.ecs.write_resource::<SoundOutput>();
|
||||
let sound_effects = self.ecs.fetch::<SoundEffects>();
|
||||
let _ = wants_to_whips.insert(
|
||||
*player_entity,
|
||||
WantsToWhip {
|
||||
frame: 0,
|
||||
last_frame: Instant::now(),
|
||||
sound: Some(
|
||||
sound_output.play_sound(sound_effects.whipping.clone()),
|
||||
),
|
||||
},
|
||||
);
|
||||
stats.whips -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
VirtualKeyCode::S => {
|
||||
let mut sound_system = self.ecs.write_resource::<SoundOutput>();
|
||||
let sound_effects = self.ecs.fetch::<SoundEffects>();
|
||||
|
@ -94,8 +120,14 @@ impl State {
|
|||
let map = self.ecs.read_resource::<Map>();
|
||||
let mut stats = self.ecs.write_resource::<Stats>();
|
||||
let mut sound_system = self.ecs.write_resource::<SoundOutput>();
|
||||
let wants_to_whips = self.ecs.read_storage::<WantsToWhip>();
|
||||
|
||||
for (player_entity, player, pos) in (&entities, &mut players, &mut positions).join() {
|
||||
// The player shouldn't be able to move while whipping
|
||||
if let Some(_wants_to_whip) = wants_to_whips.get(player_entity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (player, pos) in (&mut players, &mut positions).join() {
|
||||
let now = Instant::now();
|
||||
if now - player.last_moved > Duration::from_secs_f32(PLAYER_STEP_PERIOD) {
|
||||
let destination = Point {
|
||||
|
@ -137,12 +169,52 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn draw_whip(&self, ctx: &mut BTerm) {
|
||||
let positions = self.ecs.read_storage::<Position>();
|
||||
let wants_to_whips = self.ecs.read_storage::<WantsToWhip>();
|
||||
let mut rng = RandomNumberGenerator::new();
|
||||
for (position, wants_to_whip) in (&positions, &wants_to_whips).join() {
|
||||
let color = RGB::named(vga::get_by_index(rng.range(1, 16)));
|
||||
let frame_data = match wants_to_whip.frame {
|
||||
0 => Some((-1, -1, '\\')),
|
||||
1 => Some((-1, 0, '─')),
|
||||
2 => Some((-1, 1, '/')),
|
||||
3 => Some((0, 1, '│')),
|
||||
4 => Some((1, 1, '\\')),
|
||||
5 => Some((1, 0, '─')),
|
||||
6 => Some((1, -1, '/')),
|
||||
7 => Some((0, -1, '│')),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(data) = frame_data {
|
||||
ctx.set(
|
||||
(position.x + MAP_X as i32) + data.0,
|
||||
(position.y + MAP_Y as i32) + data.1,
|
||||
color,
|
||||
RGB::named(vga::BLACK),
|
||||
to_cp437(data.2),
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn run_systems(&mut self) {
|
||||
let mut map_indexing_system = MapIndexingSystem {};
|
||||
map_indexing_system.run_now(&self.ecs);
|
||||
|
||||
let mut whip_system = WhipSystem {};
|
||||
whip_system.run_now(&self.ecs);
|
||||
|
||||
let mut monster_ai_system = MonsterAiSystem {};
|
||||
monster_ai_system.run_now(&self.ecs);
|
||||
|
||||
self.ecs.maintain();
|
||||
self.ecs.write_resource::<Clock>().try_tick();
|
||||
|
||||
let mut clock = self.ecs.write_resource::<Clock>();
|
||||
if !self.ecs.read_resource::<StopClock>().0 {
|
||||
clock.try_tick();
|
||||
} else {
|
||||
clock.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
pub mod map_indexing_system;
|
||||
pub mod monster_ai_system;
|
||||
pub mod whip_system;
|
||||
|
||||
pub use map_indexing_system::MapIndexingSystem;
|
||||
pub use monster_ai_system::MonsterAiSystem;
|
||||
pub use whip_system::WhipSystem;
|
||||
|
|
116
src/systems/whip_system.rs
Normal file
116
src/systems/whip_system.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
use std::time::{Duration, Instant};
|
||||
|
||||
use bracket_lib::prelude::*;
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::{
|
||||
components::{Monster, Position, WantsToWhip},
|
||||
resources::{Map, SoundEffects, SoundOutput, StopClock},
|
||||
};
|
||||
|
||||
pub struct WhipSystem {}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
impl<'a> System<'a> for WhipSystem {
|
||||
type SystemData = (
|
||||
WriteExpect<'a, Map>,
|
||||
WriteExpect<'a, StopClock>,
|
||||
WriteExpect<'a, SoundOutput>,
|
||||
ReadExpect<'a, SoundEffects>,
|
||||
ReadStorage<'a, Position>,
|
||||
WriteStorage<'a, WantsToWhip>,
|
||||
WriteStorage<'a, Monster>,
|
||||
Entities<'a>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (
|
||||
map,
|
||||
mut stop_clock,
|
||||
mut sound_output,
|
||||
sound_effects,
|
||||
positions,
|
||||
mut wants_to_whips,
|
||||
monsters,
|
||||
entities,
|
||||
) = data;
|
||||
|
||||
let mut entities_to_remove: Vec<Entity> = vec![];
|
||||
|
||||
for (entity, position, mut wants_to_whip) in
|
||||
(&entities, &positions, &mut wants_to_whips).join()
|
||||
{
|
||||
stop_clock.0 = true;
|
||||
let now = Instant::now();
|
||||
if now - wants_to_whip.last_frame > Duration::from_secs_f32(0.1) {
|
||||
let destination = match wants_to_whip.frame {
|
||||
0 => Some(Point {
|
||||
x: position.x - 1,
|
||||
y: position.y - 1,
|
||||
}),
|
||||
1 => Some(Point {
|
||||
x: position.x - 1,
|
||||
y: position.y,
|
||||
}),
|
||||
2 => Some(Point {
|
||||
x: position.x - 1,
|
||||
y: position.y + 1,
|
||||
}),
|
||||
3 => Some(Point {
|
||||
x: position.x,
|
||||
y: position.y + 1,
|
||||
}),
|
||||
4 => Some(Point {
|
||||
x: position.x + 1,
|
||||
y: position.y + 1,
|
||||
}),
|
||||
5 => Some(Point {
|
||||
x: position.x + 1,
|
||||
y: position.y,
|
||||
}),
|
||||
6 => Some(Point {
|
||||
x: position.x + 1,
|
||||
y: position.y - 1,
|
||||
}),
|
||||
7 => Some(Point {
|
||||
x: position.x,
|
||||
y: position.y - 1,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(dest) = destination {
|
||||
if map.in_bounds(dest) {
|
||||
if let Some(e) = map.get_tile_content(map.point2d_to_index(dest)) {
|
||||
if let Some(_monster) = monsters.get(e) {
|
||||
let _ = entities.delete(e);
|
||||
if let Some(sound) = &mut wants_to_whip.sound {
|
||||
sound.control::<oddio::Stop<_>, _>().stop();
|
||||
}
|
||||
wants_to_whip.sound = Some(
|
||||
sound_output.play_sound(sound_effects.whipping_hit.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if wants_to_whip.frame < 7 {
|
||||
(*wants_to_whip).frame += 1;
|
||||
wants_to_whip.last_frame = now;
|
||||
} else {
|
||||
entities_to_remove.push(entity);
|
||||
stop_clock.0 = false;
|
||||
if let Some(sound) = &mut wants_to_whip.sound {
|
||||
sound.control::<oddio::Stop<_>, _>().stop();
|
||||
wants_to_whip.sound = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for entity in entities_to_remove {
|
||||
wants_to_whips.remove(entity);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue