Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ fn debug_ui_system(
ui.label(if settings.color { "✅" } else { "❌" });
ui.end_row();

ui.label("Shape cues");
ui.label(if settings.shape { "✅" } else { "❌" });
ui.end_row();

ui.label("Sound cues");
ui.label(if settings.sound { "✅" } else { "❌" });
ui.end_row();
Expand Down Expand Up @@ -119,6 +123,14 @@ fn debug_ui_system(
});
ui.end_row();

ui.label("Shape match");
ui.label(match &engine.shapes {
Some(s) if s.is_match() => "🟢 YES",
Some(_) => "⚫ no",
None => "—",
});
ui.end_row();

ui.label("Sound match");
ui.label(match &engine.sounds {
Some(s) if s.is_match() => "🟢 YES",
Expand Down Expand Up @@ -163,6 +175,10 @@ fn debug_ui_system(
ui.label(if answer.color { "🟢" } else { "⚫" });
ui.end_row();

ui.label("Shape");
ui.label(if answer.shape { "🟢" } else { "⚫" });
ui.end_row();

ui.label("Sound");
ui.label(if answer.sound { "🟢" } else { "⚫" });
ui.end_row();
Expand Down
38 changes: 25 additions & 13 deletions src/game/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use self::{
score::Score,
},
settings::GameSettings,
tile::{Tile, TilePlugin},
tile::{Tile, TileMeshes, TilePlugin},
ui::{UiPlugin, button::GameButtonPlugin},
};

Expand All @@ -33,7 +33,12 @@ impl Plugin for GamePlugin {
}

/// Spawn the arena, the tile with its first cue, and the session entity.
fn setup_game(mut commands: Commands, settings: Res<GameSettings>) {
fn setup_game(
mut commands: Commands,
settings: Res<GameSettings>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
let edge = (config::TILE_SIZE * 3.0) + (config::TILE_SPACING * 4.0);
let bounds = Vec2::new(edge, edge);
let marker = DespawnOnExit(AppState::Game);
Expand Down Expand Up @@ -76,34 +81,42 @@ fn setup_game(mut commands: Commands, settings: Res<GameSettings>) {
));
}

// Pre-build mesh handles for every shape variant.
let tile_meshes = TileMeshes::new(&mut meshes);

// Create engine and generate the first cue up-front so the player
// sees a real cue from the start (no phantom round).
let mut engine = CueEngine::new(
settings.n,
settings.position,
settings.color,
settings.shape,
settings.sound,
);
let (first_pos, first_color, first_sound) = engine.new_cue();
let first = engine.new_cue();

let tile_pos = first.position.unwrap_or_default();
let tile_color = first.color.unwrap_or_default();
let tile_shape = first.shape.unwrap_or_default();
let tile_sound = first.sound.unwrap_or_default();

let tile_pos = first_pos.unwrap_or_default();
let tile_color = first_color.unwrap_or_default();
let tile_sound = first_sound.unwrap_or_default();
let mesh_handle = tile_meshes.get(&tile_shape);
let mat_handle = materials.add(ColorMaterial::from_color(Color::from(&tile_color)));

// Spawn tile with the first cue already applied.
commands.insert_resource(tile_meshes);

// Spawn tile with the first cue already applied (mesh-based rendering).
// Change-detection will fire on the first frame, playing the sound
// and triggering the pop animation.
commands.spawn((
Name::new("tile"),
Tile,
Sprite {
color: (&tile_color).into(),
custom_size: Some(Vec2::new(config::TILE_SIZE, config::TILE_SIZE)),
..default()
},
Mesh2d(mesh_handle),
MeshMaterial2d(mat_handle),
Transform::from_translation((&tile_pos).into()),
tile_pos,
tile_color,
tile_shape,
tile_sound,
marker.clone(),
));
Expand Down Expand Up @@ -158,7 +171,6 @@ fn spawn_pause_overlay(mut commands: Commands, asset_server: Res<AssetServer>) {
..default()
},
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.6)),
// Render on top of the game UI
GlobalZIndex(10),
children![(
Text::new("PAUSED"),
Expand Down
2 changes: 2 additions & 0 deletions src/game/session/answer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use bevy::prelude::*;
pub struct Answer {
pub position: bool,
pub color: bool,
pub shape: bool,
pub sound: bool,
}

Expand All @@ -16,6 +17,7 @@ impl Answer {
info!("reset answer");
self.position = false;
self.color = false;
self.shape = false;
self.sound = false;
}
}
31 changes: 22 additions & 9 deletions src/game/session/engine.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
use bevy::prelude::*;

use crate::game::tile::{color::TileColor, position::TilePosition, sound::TileSound};
use crate::game::tile::{
color::TileColor, position::TilePosition, shape::TileShape, sound::TileSound,
};

use super::cue::CueChain;

/// Generated cues for one round.
pub struct Cue {
pub position: Option<TilePosition>,
pub color: Option<TileColor>,
pub shape: Option<TileShape>,
pub sound: Option<TileSound>,
}

/// The n-back game engine. Owns one [`CueChain`] per enabled stimulus channel.
#[derive(Component)]
pub struct CueEngine {
n: usize,
pub positions: Option<CueChain<TilePosition>>,
pub colors: Option<CueChain<TileColor>>,
pub shapes: Option<CueChain<TileShape>>,
pub sounds: Option<CueChain<TileSound>>,
}

impl CueEngine {
pub fn new(n: usize, position: bool, color: bool, sound: bool) -> Self {
pub fn new(n: usize, position: bool, color: bool, shape: bool, sound: bool) -> Self {
CueEngine {
n,
positions: position.then(|| CueChain::with_n_back(n)),
colors: color.then(|| CueChain::with_n_back(n)),
shapes: shape.then(|| CueChain::with_n_back(n)),
sounds: sound.then(|| CueChain::with_n_back(n)),
}
}
Expand All @@ -27,17 +39,18 @@ impl CueEngine {
self.n
}

pub fn new_cue(&mut self) -> (Option<TilePosition>, Option<TileColor>, Option<TileSound>) {
let new_position = self.positions.as_mut().map(|p| p.next_cue());
let new_color = self.colors.as_mut().map(|c| c.next_cue());
let new_sound = self.sounds.as_mut().map(|s| s.next_cue());

(new_position, new_color, new_sound)
pub fn new_cue(&mut self) -> Cue {
Cue {
position: self.positions.as_mut().map(|p| p.next_cue()),
color: self.colors.as_mut().map(|c| c.next_cue()),
shape: self.shapes.as_mut().map(|s| s.next_cue()),
sound: self.sounds.as_mut().map(|s| s.next_cue()),
}
}
}

impl Default for CueEngine {
fn default() -> Self {
CueEngine::new(2, true, true, true)
CueEngine::new(2, true, true, true, true)
}
}
23 changes: 16 additions & 7 deletions src/game/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
phase::GamePhase,
score::{ScoreHistory, ScoreRecord},
settings::GameSettings,
tile::{color::TileColor, position::TilePosition, sound::TileSound},
tile::{color::TileColor, position::TilePosition, shape::TileShape, sound::TileSound},
},
state::AppState,
};
Expand Down Expand Up @@ -66,10 +66,15 @@ fn end_of_round_system(
),
With<Session>,
>,
mut tile: Single<(&mut TilePosition, &mut TileColor, &mut TileSound)>,
mut tile: Single<(
&mut TilePosition,
&mut TileColor,
&mut TileShape,
&mut TileSound,
)>,
) {
let (engine, round, score, answer, timer) = &mut *session;
let (position, color, sound) = &mut *tile;
let (position, color, shape, sound) = &mut *tile;

if !timer.just_finished() {
return;
Expand All @@ -78,19 +83,23 @@ fn end_of_round_system(
// Evaluate each cue channel
score.evaluate(&engine.positions, answer.position);
score.evaluate(&engine.colors, answer.color);
score.evaluate(&engine.shapes, answer.shape);
score.evaluate(&engine.sounds, answer.sound);

answer.reset();

// Generate next cues
let (new_position, new_color, new_sound) = engine.new_cue();
if let Some(p) = new_position {
let cue = engine.new_cue();
if let Some(p) = cue.position {
**position = p;
}
if let Some(c) = new_color {
if let Some(c) = cue.color {
**color = c;
}
if let Some(s) = new_sound {
if let Some(s) = cue.shape {
**shape = s;
}
if let Some(s) = cue.sound {
**sound = s;
}

Expand Down
2 changes: 2 additions & 0 deletions src/game/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub struct GameSettings {
pub round_time: f32,
pub position: bool,
pub color: bool,
pub shape: bool,
pub sound: bool,
}

Expand All @@ -24,6 +25,7 @@ impl Default for GameSettings {
round_time: 3.0,
position: true,
color: true,
shape: true,
sound: true,
}
}
Expand Down
Loading
Loading