mirror of
https://github.com/kristoferssolo/maze-ascension.git
synced 2025-10-21 19:20:34 +00:00
feat(hints): add hint systems
This commit is contained in:
parent
cbf3f7c835
commit
a224a74d05
BIN
assets/images/hints/arrows.png
Normal file
BIN
assets/images/hints/arrows.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/hints/interaction.png
Normal file
BIN
assets/images/hints/interaction.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
26
src/hint/assets.rs
Normal file
26
src/hint/assets.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Resource, Asset, Reflect, Clone)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct HintAssets {
|
||||||
|
#[dependency]
|
||||||
|
pub arrows: Handle<Image>,
|
||||||
|
#[dependency]
|
||||||
|
pub interaction: Handle<Image>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HintAssets {
|
||||||
|
pub const PATH_ARROWS: &str = "images/hints/arrows.png";
|
||||||
|
pub const PATH_INTERACTION: &str = "images/hints/interaction.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWorld for HintAssets {
|
||||||
|
fn from_world(world: &mut World) -> Self {
|
||||||
|
let assets = world.resource::<AssetServer>();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
arrows: assets.load(Self::PATH_ARROWS),
|
||||||
|
interaction: assets.load(Self::PATH_INTERACTION),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/hint/components.rs
Normal file
35
src/hint/components.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Reflect, Component, PartialEq, Eq)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub enum Hint {
|
||||||
|
Movement,
|
||||||
|
Interaction,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Reflect, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct IdleTimer {
|
||||||
|
pub timer: Timer,
|
||||||
|
pub movement_hint_visible: bool,
|
||||||
|
pub interaction_hint_visible: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdleTimer {
|
||||||
|
pub fn hide_all(&mut self) {
|
||||||
|
self.movement_hint_visible = false;
|
||||||
|
self.interaction_hint_visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for IdleTimer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
timer: Timer::new(Duration::from_secs(3), TimerMode::Once),
|
||||||
|
movement_hint_visible: false,
|
||||||
|
interaction_hint_visible: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/hint/mod.rs
Normal file
13
src/hint/mod.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
pub mod assets;
|
||||||
|
pub mod components;
|
||||||
|
mod systems;
|
||||||
|
|
||||||
|
use bevy::{ecs::system::RunSystemOnce, prelude::*};
|
||||||
|
|
||||||
|
pub(super) fn plugin(app: &mut App) {
|
||||||
|
app.add_plugins(systems::plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_hint_command(world: &mut World) {
|
||||||
|
let _ = world.run_system_once(systems::setup::setup);
|
||||||
|
}
|
||||||
84
src/hint/systems/check.rs
Normal file
84
src/hint/systems/check.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use hexx::Hex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
floor::components::CurrentFloor,
|
||||||
|
hint::components::{Hint, IdleTimer},
|
||||||
|
maze::components::MazeConfig,
|
||||||
|
player::components::{CurrentPosition, MovementTarget, Player},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn check_player_hints(
|
||||||
|
mut idle_query: Query<&mut IdleTimer>,
|
||||||
|
player_query: Query<(&CurrentPosition, &MovementTarget), With<Player>>,
|
||||||
|
maze_query: Query<&MazeConfig, With<CurrentFloor>>,
|
||||||
|
mut hint_query: Query<(&mut Visibility, &Hint)>,
|
||||||
|
time: Res<Time>,
|
||||||
|
) {
|
||||||
|
let Ok(mut idle_timer) = idle_query.get_single_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(maze_config) = maze_query.get_single() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok((player_pos, movement_target)) = player_query.get_single() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_moving = movement_target.is_some();
|
||||||
|
|
||||||
|
if is_moving {
|
||||||
|
// Reset timer and hide hints when player moves
|
||||||
|
idle_timer.timer.reset();
|
||||||
|
hide_all_hints(&mut hint_query, &mut idle_timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tick timer when player is idle
|
||||||
|
idle_timer.timer.tick(time.delta());
|
||||||
|
|
||||||
|
if idle_timer.timer.finished() {
|
||||||
|
let on_special_tile = is_on_special_tile(player_pos, maze_config);
|
||||||
|
|
||||||
|
if !idle_timer.movement_hint_visible {
|
||||||
|
set_hint_visibility(&mut hint_query, Hint::Movement, true);
|
||||||
|
idle_timer.movement_hint_visible = true;
|
||||||
|
}
|
||||||
|
if on_special_tile && !idle_timer.interaction_hint_visible {
|
||||||
|
set_hint_visibility(&mut hint_query, Hint::Interaction, true);
|
||||||
|
idle_timer.interaction_hint_visible = true;
|
||||||
|
} else if !on_special_tile && idle_timer.interaction_hint_visible {
|
||||||
|
set_hint_visibility(&mut hint_query, Hint::Interaction, false);
|
||||||
|
idle_timer.interaction_hint_visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide_all_hints(hint_query: &mut Query<(&mut Visibility, &Hint)>, idle_timer: &mut IdleTimer) {
|
||||||
|
for (mut visibility, _) in hint_query.iter_mut() {
|
||||||
|
*visibility = Visibility::Hidden;
|
||||||
|
}
|
||||||
|
idle_timer.hide_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_hint_visibility(
|
||||||
|
hint_query: &mut Query<(&mut Visibility, &Hint)>,
|
||||||
|
hint: Hint,
|
||||||
|
visible: bool,
|
||||||
|
) {
|
||||||
|
for (mut visibility, hint_type) in hint_query.iter_mut() {
|
||||||
|
if *hint_type == hint {
|
||||||
|
*visibility = if visible {
|
||||||
|
Visibility::Visible
|
||||||
|
} else {
|
||||||
|
Visibility::Hidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_on_special_tile(player_pos: &Hex, maze_config: &MazeConfig) -> bool {
|
||||||
|
*player_pos == maze_config.start_pos || *player_pos == maze_config.end_pos
|
||||||
|
}
|
||||||
16
src/hint/systems/mod.rs
Normal file
16
src/hint/systems/mod.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
mod check;
|
||||||
|
pub mod setup;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use check::check_player_hints;
|
||||||
|
|
||||||
|
use super::assets::HintAssets;
|
||||||
|
use crate::{asset_tracking::LoadResource, screens::Screen};
|
||||||
|
|
||||||
|
pub(super) fn plugin(app: &mut App) {
|
||||||
|
app.load_resource::<HintAssets>();
|
||||||
|
app.add_systems(
|
||||||
|
Update,
|
||||||
|
check_player_hints.run_if(in_state(Screen::Gameplay)),
|
||||||
|
);
|
||||||
|
}
|
||||||
23
src/hint/systems/setup.rs
Normal file
23
src/hint/systems/setup.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::hint::{
|
||||||
|
assets::HintAssets,
|
||||||
|
components::{Hint, IdleTimer},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn setup(mut commands: Commands, hint_assets: Res<HintAssets>) {
|
||||||
|
commands.spawn((
|
||||||
|
Name::new("Movement hint"),
|
||||||
|
Hint::Movement,
|
||||||
|
Visibility::Hidden,
|
||||||
|
));
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
Name::new("Interaction hint"),
|
||||||
|
Hint::Interaction,
|
||||||
|
Visibility::Hidden,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add idle timer
|
||||||
|
commands.spawn(IdleTimer::default());
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ pub mod constants;
|
|||||||
#[cfg(feature = "dev")]
|
#[cfg(feature = "dev")]
|
||||||
pub mod dev_tools;
|
pub mod dev_tools;
|
||||||
pub mod floor;
|
pub mod floor;
|
||||||
|
pub mod hint;
|
||||||
pub mod maze;
|
pub mod maze;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod screens;
|
pub mod screens;
|
||||||
@ -67,6 +68,7 @@ impl Plugin for AppPlugin {
|
|||||||
maze::plugin,
|
maze::plugin,
|
||||||
floor::plugin,
|
floor::plugin,
|
||||||
player::plugin,
|
player::plugin,
|
||||||
|
hint::plugin,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Enable dev tools for dev builds.
|
// Enable dev tools for dev builds.
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
use crate::theme::{palette::rose_pine::RosePineDawn, prelude::ColorScheme};
|
use crate::theme::{palette::rose_pine::RosePineDawn, prelude::ColorScheme};
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
pub(super) fn generate_pill_mesh(radius: f32, half_length: f32) -> Mesh {
|
pub(super) fn generate_pill_mesh(radius: f32, half_length: f32) -> Mesh {
|
||||||
Mesh::from(Capsule3d {
|
Mesh::from(Capsule3d {
|
||||||
radius,
|
radius,
|
||||||
|
|||||||
@ -1,15 +1,20 @@
|
|||||||
//! The screen state for the main gameplay.
|
//! The screen state for the main gameplay.
|
||||||
|
|
||||||
use crate::maze::spawn_level_command;
|
|
||||||
use crate::player::spawn_player_command;
|
use crate::player::spawn_player_command;
|
||||||
use crate::screens::Screen;
|
use crate::screens::Screen;
|
||||||
|
use crate::{hint::spawn_hint_command, maze::spawn_level_command};
|
||||||
|
|
||||||
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
|
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
|
||||||
|
|
||||||
pub(super) fn plugin(app: &mut App) {
|
pub(super) fn plugin(app: &mut App) {
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
OnEnter(Screen::Gameplay),
|
OnEnter(Screen::Gameplay),
|
||||||
(spawn_level_command, spawn_player_command).chain(),
|
(
|
||||||
|
spawn_level_command,
|
||||||
|
spawn_player_command,
|
||||||
|
spawn_hint_command,
|
||||||
|
)
|
||||||
|
.chain(),
|
||||||
);
|
);
|
||||||
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user