Merge pull request #24 from kristoferssolo/feat/hint

This commit is contained in:
Kristofers Solo 2025-01-05 22:44:52 +02:00 committed by GitHub
commit 2a12ab8cbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 473 additions and 4 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="212.00006"
height="137.99994"
viewBox="0 0 56.091679 36.512483"
version="1.1"
id="svg1"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
sodipodi:docname="arrows.svg"
inkscape:export-filename="arrows.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="2.8284271"
inkscape:cx="77.60497"
inkscape:cy="111.5461"
inkscape:window-width="1916"
inkscape:window-height="1055"
inkscape:window-x="1920"
inkscape:window-y="21"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<rect
x="-77.781746"
y="-59.043419"
width="239.00209"
height="180.66579"
id="rect7" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(10.819751,8.4666586)">
<g
id="g8"
transform="matrix(0.99998557,0,0,1.0000004,2.6458217,-3.7369281e-6)"
inkscape:label="W">
<rect
style="fill:none;fill-opacity:1;stroke:#575279;stroke-width:1;stroke-miterlimit:10000;stroke-opacity:1;paint-order:markers stroke fill"
id="rect3"
width="8.4666662"
height="8.4666662"
x="22.225"
y="2.6527839"
rx="1.0583333"
ry="1.0583333"
transform="matrix(1.7887599,0,0,1.7887302,-32.747127,-12.317401)" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF';text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#575279;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:10000;paint-order:markers stroke fill"
x="26.458336"
y="9.7436171"
id="text3"
transform="matrix(1.7887599,0,0,1.7887302,-32.747127,-12.317401)"><tspan
sodipodi:role="line"
id="tspan3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:7.9375px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF Bold';fill:#575279;fill-opacity:1;stroke:none;stroke-width:1"
x="26.458336"
y="9.7436171">W</tspan></text>
</g>
<g
id="g1"
transform="matrix(1.7887341,0,0,1.7887309,-49.68,7.261757)"
inkscape:label="A">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF';text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#575279;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:10000;paint-order:markers stroke fill"
x="26.458336"
y="9.7436171"
id="text1"><tspan
sodipodi:role="line"
id="tspan1"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:7.9375px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF Bold';fill:#575279;fill-opacity:1;stroke:none;stroke-width:1"
x="26.458336"
y="9.7436171">A</tspan></text>
<rect
style="fill:none;fill-opacity:1;stroke:#575279;stroke-width:1;stroke-miterlimit:10000;stroke-opacity:1;paint-order:markers stroke fill"
id="rect2"
width="8.4666662"
height="8.4666662"
x="22.225"
y="2.6527839"
rx="1.0583333"
ry="1.0583333" />
</g>
<g
id="g5"
transform="matrix(0.99998557,0,0,1.0000004,2.6458214,19.579163)"
inkscape:label="S">
<rect
style="fill:none;fill-opacity:1;stroke:#575279;stroke-width:1;stroke-miterlimit:10000;stroke-opacity:1;paint-order:markers stroke fill"
id="rect5"
width="8.4666662"
height="8.4666662"
x="22.225"
y="2.6527839"
rx="1.0583333"
ry="1.0583333"
transform="matrix(1.7887599,0,0,1.7887302,-32.747127,-12.317401)" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF';text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#575279;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:10000;paint-order:markers stroke fill"
x="26.458336"
y="9.7436171"
id="text5"
transform="matrix(1.7887599,0,0,1.7887302,-32.747127,-12.317401)"><tspan
sodipodi:role="line"
id="tspan5"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:7.9375px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF Bold';fill:#575279;fill-opacity:1;stroke:none;stroke-width:1"
x="26.458336"
y="9.7436171">S</tspan></text>
</g>
<g
id="g6"
transform="matrix(0.99998557,0,0,1.0000004,22.224988,19.579163)"
inkscape:label="D">
<rect
style="fill:none;fill-opacity:1;stroke:#575279;stroke-width:1;stroke-miterlimit:10000;stroke-opacity:1;paint-order:markers stroke fill"
id="rect6"
width="8.4666662"
height="8.4666662"
x="22.225"
y="2.6527839"
rx="1.0583333"
ry="1.0583333"
transform="matrix(1.7887599,0,0,1.7887302,-32.747127,-12.317401)" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.9375px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF';text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#575279;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:10000;paint-order:markers stroke fill"
x="26.458336"
y="9.7436171"
id="text6"
transform="matrix(1.7887599,0,0,1.7887302,-32.747127,-12.317401)"><tspan
sodipodi:role="line"
id="tspan6"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:7.9375px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF Bold';fill:#575279;fill-opacity:1;stroke:none;stroke-width:1"
x="26.458336"
y="9.7436171">D</tspan></text>
<text
xml:space="preserve"
transform="matrix(0.26458715,0,0,0.26458323,-18.224937,-0.61706746)"
id="text7"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:30px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF Bold';text-align:center;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect7);display:inline;fill:#575279;stroke:#575279;stroke-width:3.77953;stroke-miterlimit:10000;paint-order:markers stroke fill" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="227.09914"
height="47.000492"
viewBox="0 0 60.086643 12.435547"
version="1.1"
id="svg1"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
sodipodi:docname="interaction.svg"
inkscape:export-filename="interaction.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="2.8284271"
inkscape:cx="67.882252"
inkscape:cy="68.766135"
inkscape:window-width="1916"
inkscape:window-height="1055"
inkscape:window-x="1920"
inkscape:window-y="21"
inkscape:window-maximized="1"
inkscape:current-layer="g3" />
<defs
id="defs1">
<rect
x="-5.4800777"
y="38.183765"
width="124.98112"
height="48.260036"
id="rect1" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(16.041967,3.3894905)">
<g
id="g8"
transform="matrix(0.99998557,0,0,1.0000004,3.7019837e-6,-3.7020056e-6)">
<g
id="g3"
transform="translate(-11.729167,-2.1527839)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.2293px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF';text-align:center;word-spacing:0px;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#575279;fill-opacity:1;stroke:none;stroke-width:1.00001;stroke-miterlimit:10000;stroke-dasharray:none;paint-order:markers stroke fill"
x="26.458336"
y="9.7436171"
id="text3"><tspan
sodipodi:role="line"
id="tspan3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF';word-spacing:0px;writing-mode:lr-tb;direction:rtl;baseline-shift:baseline;fill:#575279;fill-opacity:1;stroke:none;stroke-width:1.00001;stroke-dasharray:none"
x="26.458336"
y="9.7436171"
dy="0"
dx="0">Press[E]</tspan></text>
<text
xml:space="preserve"
transform="matrix(0.26458715,0,0,0.26458323,11.729163,2.1527876)"
id="text1"
style="font-size:30px;line-height:0px;font-family:'JetBrainsMono NF';-inkscape-font-specification:'JetBrainsMono NF';text-align:center;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect1);display:inline;fill:#575279;stroke:#575279;stroke-width:3.77953;stroke-miterlimit:10000;paint-order:markers stroke fill" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

26
src/hint/assets.rs Normal file
View 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
View 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
View 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);
}

86
src/hint/systems/check.rs Normal file
View File

@ -0,0 +1,86 @@
use bevy::prelude::*;
use hexx::Hex;
use crate::{
floor::components::{CurrentFloor, Floor, FloorYTarget},
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>>,
tranitioning: Query<Has<FloorYTarget>>,
maze_query: Query<(&MazeConfig, &Floor), 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, floor)) = 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() || tranitioning.iter().any(|x| x);
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, floor.0);
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, floor: u8) -> bool {
(*player_pos == maze_config.start_pos && floor != 1) || *player_pos == maze_config.end_pos
}

16
src/hint/systems/mod.rs Normal file
View 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)),
);
}

43
src/hint/systems/setup.rs Normal file
View File

@ -0,0 +1,43 @@
use bevy::{prelude::*, ui::Val::*};
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,
ImageNode {
image: hint_assets.arrows.clone(),
..default()
},
Node {
position_type: PositionType::Absolute,
right: Px(20.0),
bottom: Px(20.0),
..default()
},
));
commands.spawn((
Name::new("Interaction hint"),
Hint::Interaction,
Visibility::Hidden,
ImageNode {
image: hint_assets.interaction.clone(),
..default()
},
Node {
position_type: PositionType::Absolute,
right: Px(20.0),
bottom: Px(168.0),
..default()
},
));
// Add idle timer
commands.spawn(IdleTimer::default());
}

View File

@ -4,6 +4,7 @@ pub mod constants;
#[cfg(feature = "dev")]
pub mod dev_tools;
pub mod floor;
pub mod hint;
pub mod maze;
pub mod player;
pub mod screens;
@ -67,6 +68,7 @@ impl Plugin for AppPlugin {
maze::plugin,
floor::plugin,
player::plugin,
hint::plugin,
));
// Enable dev tools for dev builds.

View File

@ -1,7 +1,7 @@
use bevy::prelude::*;
use crate::theme::{palette::rose_pine::RosePineDawn, prelude::ColorScheme};
use bevy::prelude::*;
pub(super) fn generate_pill_mesh(radius: f32, half_length: f32) -> Mesh {
Mesh::from(Capsule3d {
radius,

View File

@ -1,15 +1,20 @@
//! The screen state for the main gameplay.
use crate::maze::spawn_level_command;
use crate::player::spawn_player_command;
use crate::screens::Screen;
use crate::{hint::spawn_hint_command, maze::spawn_level_command};
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
pub(super) fn plugin(app: &mut App) {
app.add_systems(
OnEnter(Screen::Gameplay),
(spawn_level_command, spawn_player_command).chain(),
(
spawn_level_command,
spawn_player_command,
spawn_hint_command,
)
.chain(),
);
app.add_systems(