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 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 };
|
||||
|
@ -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<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) {
|
||||
world.insert(InputQueue::default());
|
||||
world.insert(Gameplay::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::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 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user