feat: Add placeholder interact menu (#25)

This commit is contained in:
demenik
2025-12-01 17:49:38 +01:00
parent 15ea976442
commit a048d9e84c
6 changed files with 156 additions and 3 deletions

View File

@@ -19,6 +19,7 @@ impl Plugin for InputPlugin {
app.add_systems(Update, move_click.run_if(in_state(AppState::GameScreen))); app.add_systems(Update, move_click.run_if(in_state(AppState::GameScreen)));
app.add_message::<InteractStartMessage>(); app.add_message::<InteractStartMessage>();
app.add_message::<TileClickMessage>();
app.add_systems( app.add_systems(
Update, Update,
interact_click.run_if(in_state(AppState::GameScreen)), interact_click.run_if(in_state(AppState::GameScreen)),
@@ -65,7 +66,7 @@ fn move_click(
} }
fn interact_click( fn interact_click(
mut interact_messages: MessageWriter<InteractStartMessage>, mut tile_click_messages: MessageWriter<TileClickMessage>,
mouse_btn: Res<ButtonInput<MouseButton>>, mouse_btn: Res<ButtonInput<MouseButton>>,
keys: Res<ButtonInput<KeyCode>>, keys: Res<ButtonInput<KeyCode>>,
window: Single<&Window, With<PrimaryWindow>>, window: Single<&Window, With<PrimaryWindow>>,
@@ -88,7 +89,7 @@ fn interact_click(
}; };
println!("Interact Click: ({}, {})", x, y); println!("Interact Click: ({}, {})", x, y);
interact_messages.write(InteractStartMessage { x, y }); tile_click_messages.write(TileClickMessage { x, y });
} }
} }

View File

@@ -16,3 +16,9 @@ pub struct InteractStartMessage {
pub x: u32, pub x: u32,
pub y: u32, pub y: u32,
} }
#[derive(Message)]
pub struct TileClickMessage {
pub x: u32,
pub y: u32,
}

View File

@@ -7,12 +7,14 @@ use utils::manhattan_distance;
pub mod components; pub mod components;
pub mod messages; pub mod messages;
pub mod ui;
pub mod utils; pub mod utils;
pub struct PomPlugin; pub struct PomPlugin;
impl Plugin for PomPlugin { impl Plugin for PomPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(ui::PomUiPlugin);
app.add_systems(OnEnter(AppState::GameScreen), setup); app.add_systems(OnEnter(AppState::GameScreen), setup);
app.add_systems(OnExit(AppState::GameScreen), cleanup); app.add_systems(OnExit(AppState::GameScreen), cleanup);

View File

@@ -0,0 +1,122 @@
use crate::features::ui::utils::ui_blocks;
use crate::prelude::*;
use bevy::window::PrimaryWindow;
#[derive(Component)]
pub enum RootMarker {
ContextMenu,
}
#[derive(Component)]
pub enum ButtonType {
Interact { x: u32, y: u32 },
Cancel,
}
pub fn spawn_context_menu(
mut commands: Commands,
mut tile_click_messages: MessageReader<TileClickMessage>,
root_query: Query<Entity, With<RootMarker>>,
camera_query: Single<(&Camera, &GlobalTransform), With<Camera2d>>,
config: Res<GameConfig>,
) {
for message in tile_click_messages.read() {
// Despawn existing menu
for entity in root_query.iter() {
commands.entity(entity).try_despawn();
}
let world_pos = grid_to_world_coords(
message.x,
message.y,
Some(0.0),
config.grid_width,
config.grid_height,
);
let (camera, camera_transform) = *camera_query;
if let Ok(screen_pos) = camera.world_to_viewport(camera_transform, world_pos) {
commands
.spawn((
Node {
position_type: PositionType::Absolute,
left: px(screen_pos.x),
top: px(screen_pos.y),
padding: UiRect::all(px(5.0)),
..Node::vstack(px(5.0))
},
ZIndex(100),
BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
BorderRadius::all(px(5)),
RootMarker::ContextMenu,
))
.with_children(|parent| {
parent.spawn(button(
ButtonType::Interact {
x: message.x,
y: message.y,
},
ButtonVariant::Primary,
Node {
padding: UiRect::all(px(5)),
..default()
},
|c| text("<Interact>", 20.0, c),
));
parent.spawn(button(
ButtonType::Cancel,
ButtonVariant::Destructive,
Node {
padding: UiRect::all(px(5)),
..default()
},
|c| text("Abbrechen", 20.0, c),
));
});
}
}
}
pub fn click_outside_context_menu(
mut commands: Commands,
mouse_btn: Res<ButtonInput<MouseButton>>,
window: Single<&Window, With<PrimaryWindow>>,
ui_query: Query<(&ComputedNode, &GlobalTransform), With<Node>>,
root_query: Query<Entity, With<RootMarker>>,
) {
if mouse_btn.just_pressed(MouseButton::Left) {
let Some(cursor_pos) = window.cursor_position() else {
return;
};
if !ui_blocks(window, cursor_pos, ui_query) {
for entity in root_query.iter() {
commands.entity(entity).try_despawn();
}
}
}
}
pub fn buttons(
mut commands: Commands,
mut button_query: Query<(&Interaction, &ButtonType), (Changed<Interaction>, With<Button>)>,
mut interact_messages: MessageWriter<InteractStartMessage>,
root_query: Query<Entity, With<RootMarker>>,
) {
for (interaction, button_type) in button_query.iter_mut() {
if *interaction == Interaction::Pressed {
match button_type {
ButtonType::Interact { x, y } => {
interact_messages.write(InteractStartMessage { x: *x, y: *y });
}
ButtonType::Cancel => (),
}
for entity in root_query.iter() {
commands.entity(entity).despawn();
}
}
}
}

View File

@@ -0,0 +1,22 @@
use crate::prelude::*;
pub mod context_menu;
pub struct PomUiPlugin;
impl Plugin for PomUiPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
context_menu::spawn_context_menu.run_if(in_state(AppState::GameScreen)),
);
app.add_systems(
Update,
context_menu::click_outside_context_menu.run_if(in_state(AppState::GameScreen)),
);
app.add_systems(
Update,
context_menu::buttons.run_if(in_state(AppState::GameScreen)),
);
}
}

View File

@@ -11,7 +11,7 @@ pub use crate::features::{
phase::components::{CurrentPhase, Phase}, phase::components::{CurrentPhase, Phase},
pom::{ pom::{
components::{GridPosition, MovingState, Pom}, components::{GridPosition, MovingState, Pom},
messages::{InteractStartMessage, MoveMessage}, messages::{InteractStartMessage, MoveMessage, TileClickMessage},
}, },
savegame::components::SavegamePath, savegame::components::SavegamePath,
ui::{components::ButtonVariant, consts::*, ui::*, utils::*}, ui::{components::ButtonVariant, consts::*, ui::*, utils::*},