forked from tutorials/rust-sokoban
Add event system
This commit is contained in:
parent
51fd0614ce
commit
1628a5d271
33
src/audio.rs
Normal file
33
src/audio.rs
Normal file
@ -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<String, audio::Source>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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::<AudioStore>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
23
src/events.rs
Normal file
23
src/events.rs
Normal file
@ -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,
|
||||||
|
}
|
@ -5,13 +5,16 @@ use ggez::{conf, event, timer, Context, GameResult};
|
|||||||
use specs::{RunNow, World, WorldExt};
|
use specs::{RunNow, World, WorldExt};
|
||||||
use std::path;
|
use std::path;
|
||||||
|
|
||||||
|
mod audio;
|
||||||
mod components;
|
mod components;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod entities;
|
mod entities;
|
||||||
|
mod events;
|
||||||
mod map;
|
mod map;
|
||||||
mod resources;
|
mod resources;
|
||||||
mod systems;
|
mod systems;
|
||||||
|
|
||||||
|
use crate::audio::initialize_sounds;
|
||||||
use crate::components::*;
|
use crate::components::*;
|
||||||
use crate::map::*;
|
use crate::map::*;
|
||||||
use crate::resources::*;
|
use crate::resources::*;
|
||||||
@ -96,6 +99,7 @@ pub fn main() -> GameResult {
|
|||||||
.add_resource_path(path::PathBuf::from("./resources"));
|
.add_resource_path(path::PathBuf::from("./resources"));
|
||||||
|
|
||||||
let (context, event_loop) = &mut context_builder.build()?;
|
let (context, event_loop) = &mut context_builder.build()?;
|
||||||
|
initialize_sounds(&mut world, context);
|
||||||
|
|
||||||
// Create the game state
|
// Create the game state
|
||||||
let game = &mut Game { world };
|
let game = &mut Game { world };
|
||||||
|
@ -4,6 +4,9 @@ use std::fmt;
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::audio::AudioStore;
|
||||||
|
use crate::events::Event;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct InputQueue {
|
pub struct InputQueue {
|
||||||
pub keys_pressed: Vec<KeyCode>,
|
pub keys_pressed: Vec<KeyCode>,
|
||||||
@ -42,8 +45,15 @@ impl Display for GameplayState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EventQueue {
|
||||||
|
pub events: Vec<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register_resources(world: &mut World) {
|
pub fn register_resources(world: &mut World) {
|
||||||
world.insert(InputQueue::default());
|
world.insert(InputQueue::default());
|
||||||
world.insert(Gameplay::default());
|
world.insert(Gameplay::default());
|
||||||
world.insert(Time::default());
|
world.insert(Time::default());
|
||||||
|
world.insert(EventQueue::default());
|
||||||
|
world.insert(AudioStore::default());
|
||||||
}
|
}
|
||||||
|
73
src/systems/event_system.rs
Normal file
73
src/systems/event_system.rs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -7,13 +7,15 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use crate::components::*;
|
use crate::components::*;
|
||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::resources::{Gameplay, InputQueue};
|
use crate::events::{EntityMoved, Event};
|
||||||
|
use crate::resources::{EventQueue, Gameplay, InputQueue};
|
||||||
|
|
||||||
pub struct InputSystem {}
|
pub struct InputSystem {}
|
||||||
|
|
||||||
impl<'a> System<'a> for InputSystem {
|
impl<'a> System<'a> for InputSystem {
|
||||||
// Data
|
// Data
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
|
Write<'a, EventQueue>,
|
||||||
Write<'a, InputQueue>,
|
Write<'a, InputQueue>,
|
||||||
Write<'a, Gameplay>,
|
Write<'a, Gameplay>,
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
@ -24,8 +26,16 @@ impl<'a> System<'a> for InputSystem {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (mut input_queue, mut gameplay, entities, mut positions, players, movables, immovables) =
|
let (
|
||||||
data;
|
mut events,
|
||||||
|
mut input_queue,
|
||||||
|
mut gameplay,
|
||||||
|
entities,
|
||||||
|
mut positions,
|
||||||
|
players,
|
||||||
|
movables,
|
||||||
|
immovables,
|
||||||
|
) = data;
|
||||||
|
|
||||||
let mut to_move = Vec::new();
|
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 exists, we need to stop and not move anything
|
||||||
// if it doesn't exist, we stop because we found a gap
|
// if it doesn't exist, we stop because we found a gap
|
||||||
match immov.get(&pos) {
|
match immov.get(&pos) {
|
||||||
Some(_) => to_move.clear(),
|
Some(_) => {
|
||||||
|
to_move.clear();
|
||||||
|
events.events.push(Event::PlayerHistObstacle {})
|
||||||
|
}
|
||||||
None => break,
|
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 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
mod event_system;
|
||||||
mod gameplay_state_sytem;
|
mod gameplay_state_sytem;
|
||||||
mod input_system;
|
mod input_system;
|
||||||
mod rendering_system;
|
mod rendering_system;
|
||||||
|
|
||||||
|
pub use self::event_system::EventSystem;
|
||||||
pub use self::gameplay_state_sytem::GameplayStateSystem;
|
pub use self::gameplay_state_sytem::GameplayStateSystem;
|
||||||
pub use self::input_system::InputSystem;
|
pub use self::input_system::InputSystem;
|
||||||
pub use self::rendering_system::RenderingSystem;
|
pub use self::rendering_system::RenderingSystem;
|
||||||
|
Loading…
Reference in New Issue
Block a user