From 1628a5d271fe102954c9ca162d4465ec135e0b38 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Mon, 27 Jul 2020 09:59:14 -0400 Subject: [PATCH] Add event system --- src/audio.rs | 33 +++++++++++++++++ src/events.rs | 23 ++++++++++++ src/main.rs | 4 ++ src/resources.rs | 10 +++++ src/systems/event_system.rs | 73 +++++++++++++++++++++++++++++++++++++ src/systems/input_system.rs | 24 ++++++++++-- src/systems/mod.rs | 2 + 7 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 src/audio.rs create mode 100644 src/events.rs create mode 100644 src/systems/event_system.rs diff --git a/src/audio.rs b/src/audio.rs new file mode 100644 index 0000000..36c51e5 --- /dev/null +++ b/src/audio.rs @@ -0,0 +1,33 @@ +use ggez::{Context, audio}; +use ggez::audio::SoundSource; +use specs::{World, WorldExt}; + +use std::collections::HashMap; + +#[derive(Default)] +pub struct AudioStore { + pub sounds: HashMap, +} + +impl AudioStore { + pub fn play_sound(&mut self, sound: &String) { + let _ = self + .sounds + .get_mut(sound) + .expect("expected sound") + .play_detached(); + } +} + +pub fn initialize_sounds(world: &mut World, context: &mut Context) { + let mut audio_store = world.write_resource::(); + let sounds = ["correct", "incorrect", "wall"]; + + for sound in sounds.iter() { + let sound_name = sound.to_string(); + let sound_path = format!("/sounds/{}.wav", sound_name); + let sound_source = audio::Source::new(context, sound_path).expect("expected sound loaded"); + + audio_store.sounds.insert(sound_name, sound_source); + } +} \ No newline at end of file diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 0000000..4446dcb --- /dev/null +++ b/src/events.rs @@ -0,0 +1,23 @@ +#[derive(Debug)] +pub enum Event { + // Fired when the player hits an obstacle like a wall + PlayerHistObstacle, + + // Fired when an entity is moved + EntityMoved(EntityMoved), + + // Fired when the box is placed on a spot + BoxPlacedOnSpot(BoxPlacedOnSpot), +} + +pub type EntityId = u32; + +#[derive(Debug)] +pub struct EntityMoved { + pub id: EntityId, +} + +#[derive(Debug)] +pub struct BoxPlacedOnSpot { + pub is_correct_spot: bool, +} diff --git a/src/main.rs b/src/main.rs index 4eedd07..047e37a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,13 +5,16 @@ use ggez::{conf, event, timer, Context, GameResult}; use specs::{RunNow, World, WorldExt}; use std::path; +mod audio; mod components; mod constants; mod entities; +mod events; mod map; mod resources; mod systems; +use crate::audio::initialize_sounds; use crate::components::*; use crate::map::*; use crate::resources::*; @@ -96,6 +99,7 @@ pub fn main() -> GameResult { .add_resource_path(path::PathBuf::from("./resources")); let (context, event_loop) = &mut context_builder.build()?; + initialize_sounds(&mut world, context); // Create the game state let game = &mut Game { world }; diff --git a/src/resources.rs b/src/resources.rs index d057064..87b17d7 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -4,6 +4,9 @@ use std::fmt; use std::fmt::Display; use std::time::Duration; +use crate::audio::AudioStore; +use crate::events::Event; + #[derive(Default)] pub struct InputQueue { pub keys_pressed: Vec, @@ -42,8 +45,15 @@ impl Display for GameplayState { } } +#[derive(Default)] +pub struct EventQueue { + pub events: Vec, +} + pub fn register_resources(world: &mut World) { world.insert(InputQueue::default()); world.insert(Gameplay::default()); world.insert(Time::default()); + world.insert(EventQueue::default()); + world.insert(AudioStore::default()); } diff --git a/src/systems/event_system.rs b/src/systems/event_system.rs new file mode 100644 index 0000000..a2d7d38 --- /dev/null +++ b/src/systems/event_system.rs @@ -0,0 +1,73 @@ +use specs::{Entities, Join, ReadStorage, System, Write}; + +use std::collections::HashMap; + +use crate::{ + audio::AudioStore, + components::*, + events::{BoxPlacedOnSpot, EntityMoved, Event}, + resources::EventQueue, +}; + +pub struct EventSystem {} + +impl<'a> System<'a> for EventSystem { + // Data + type SystemData = ( + Write<'a, EventQueue>, + Write<'a, AudioStore>, + Entities<'a>, + ReadStorage<'a, Box>, + ReadStorage<'a, BoxSpot>, + ReadStorage<'a, Position>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (mut event_queue, mut audio_store, entities, boxes, box_spots, positions) = data; + + let mut new_events = Vec::new(); + + for event in event_queue.events.drain(..) { + println!("New event: {:?}", event); + + match event { + Event::PlayerHistObstacle => { + // play sound here + audio_store.play_sound(&"wall".to_string()); + } + Event::EntityMoved(EntityMoved { id }) => { + // An entity was just moved, check if it was a box and fire + // more events if it's been moved on a spot. + if let Some(the_box) = boxes.get(entities.entity(id)) { + let box_spots_with_positions: HashMap<(u8, u8), &BoxSpot> = (&box_spots, &positions) + .join() + .map(|t| ((t.1.x, t.1.y), t.0)) + .collect(); + + if let Some(box_position) = positions.get(entities.entity(id)) { + // Check if there is a spot on this position, and if there + // is, if it's the correct or incorrect type + if let Some(box_spot) = box_spots_with_positions.get(&(box_position.x, box_position.y)) { + new_events.push(Event::BoxPlacedOnSpot(BoxPlacedOnSpot { + is_correct_spot: (box_spot.color == the_box.color), + })); + } + } + } + } + Event::BoxPlacedOnSpot(BoxPlacedOnSpot { is_correct_spot }) => { + // play sound here + let sound = if is_correct_spot { + "correct" + } else { + "inncorrect" + }; + + audio_store.play_sound(&sound.to_string()) + } + } + } + + event_queue.events.append(&mut new_events); + } +} diff --git a/src/systems/input_system.rs b/src/systems/input_system.rs index de640b3..09df8d4 100644 --- a/src/systems/input_system.rs +++ b/src/systems/input_system.rs @@ -7,13 +7,15 @@ use std::collections::HashMap; use crate::components::*; use crate::constants::*; -use crate::resources::{Gameplay, InputQueue}; +use crate::events::{EntityMoved, Event}; +use crate::resources::{EventQueue, Gameplay, InputQueue}; pub struct InputSystem {} impl<'a> System<'a> for InputSystem { // Data type SystemData = ( + Write<'a, EventQueue>, Write<'a, InputQueue>, Write<'a, Gameplay>, Entities<'a>, @@ -24,8 +26,16 @@ impl<'a> System<'a> for InputSystem { ); fn run(&mut self, data: Self::SystemData) { - let (mut input_queue, mut gameplay, entities, mut positions, players, movables, immovables) = - data; + let ( + mut events, + mut input_queue, + mut gameplay, + entities, + mut positions, + players, + movables, + immovables, + ) = data; let mut to_move = Vec::new(); @@ -76,7 +86,10 @@ impl<'a> System<'a> for InputSystem { // if it exists, we need to stop and not move anything // if it doesn't exist, we stop because we found a gap match immov.get(&pos) { - Some(_) => to_move.clear(), + Some(_) => { + to_move.clear(); + events.events.push(Event::PlayerHistObstacle {}) + } None => break, } } @@ -102,6 +115,9 @@ impl<'a> System<'a> for InputSystem { _ => (), } } + + // Fire an event for the entity that just moved + events.events.push(Event::EntityMoved(EntityMoved { id })); } } } diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 622c5e3..faa6c09 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -1,7 +1,9 @@ +mod event_system; mod gameplay_state_sytem; mod input_system; mod rendering_system; +pub use self::event_system::EventSystem; pub use self::gameplay_state_sytem::GameplayStateSystem; pub use self::input_system::InputSystem; pub use self::rendering_system::RenderingSystem;