Merge branch '25-pom-interactions' into 'dev'
Implement Pom Interactions See merge request softwaregrundprojekt/2025-2026/einzelprojekt/tutorium-moritz/bernroider-dominik/bernroider-dominik!21
This commit is contained in:
@@ -40,7 +40,7 @@ cargo run
|
|||||||
### Hidden binds (Only available in the debug build)
|
### Hidden binds (Only available in the debug build)
|
||||||
|
|
||||||
- `Shift + Enter`: Duration of the current phase is set to 3 seconds.
|
- `Shift + Enter`: Duration of the current phase is set to 3 seconds.
|
||||||
- `Left Mouse Button` on Tile: Rotate tile state.
|
- `Shift + Left Mouse Button` on Tile: Toggle tile state.
|
||||||
- `Shift + Arrow Up`: Add one berry to your inventory
|
- `Shift + Arrow Up`: Add one berry to your inventory
|
||||||
- `Shift + Arrow Down`: Remove one berry from your inventory
|
- `Shift + Arrow Down`: Remove one berry from your inventory
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::features::{
|
use crate::features::{
|
||||||
|
input::utils::mouse_to_grid,
|
||||||
phase::messages::{NextPhaseMessage, PhaseTimerPauseMessage},
|
phase::messages::{NextPhaseMessage, PhaseTimerPauseMessage},
|
||||||
pom::messages::InvalidMoveMessage,
|
pom::messages::InvalidMoveMessage,
|
||||||
shop::ui::open_shop,
|
shop::ui::open_shop,
|
||||||
@@ -7,6 +8,8 @@ use crate::prelude::*;
|
|||||||
use bevy::input::mouse::MouseButton;
|
use bevy::input::mouse::MouseButton;
|
||||||
use bevy::window::PrimaryWindow;
|
use bevy::window::PrimaryWindow;
|
||||||
|
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
pub struct InputPlugin;
|
pub struct InputPlugin;
|
||||||
|
|
||||||
impl Plugin for InputPlugin {
|
impl Plugin for InputPlugin {
|
||||||
@@ -16,11 +19,15 @@ 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)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
app.add_systems(Update, debug_click.run_if(in_state(AppState::GameScreen)));
|
||||||
|
|
||||||
app.add_message::<PhaseTimerPauseMessage>();
|
app.add_message::<PhaseTimerPauseMessage>();
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
@@ -49,20 +56,7 @@ fn move_click(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mouse_btn.just_pressed(MouseButton::Right) {
|
if mouse_btn.just_pressed(MouseButton::Right) {
|
||||||
let (cam, cam_transform) = *camera;
|
let Some((x, y)) = mouse_to_grid(window, camera, config, ui_query) else {
|
||||||
|
|
||||||
let Some(cursor_pos) = window.cursor_position() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if ui_blocks(window, cursor_pos, ui_query) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Ok(world_pos) = cam.viewport_to_world(cam_transform, cursor_pos) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Ok((x, y)) =
|
|
||||||
world_to_grid_coords(world_pos.origin, config.grid_width, config.grid_height)
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,15 +66,41 @@ 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>>,
|
||||||
window: Single<&Window, With<PrimaryWindow>>,
|
window: Single<&Window, With<PrimaryWindow>>,
|
||||||
camera: Single<(&Camera, &GlobalTransform), With<Camera2d>>,
|
camera: Single<(&Camera, &GlobalTransform), With<Camera2d>>,
|
||||||
config: Res<GameConfig>,
|
config: Res<GameConfig>,
|
||||||
phase: Res<CurrentPhase>,
|
phase: Res<CurrentPhase>,
|
||||||
ui_query: Query<(&ComputedNode, &GlobalTransform), With<Node>>,
|
ui_query: Query<(&ComputedNode, &GlobalTransform), With<Node>>,
|
||||||
// for debug
|
) {
|
||||||
grid: ResMut<Grid>,
|
match phase.0 {
|
||||||
|
Phase::Focus { .. } => return,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mouse_btn.just_pressed(MouseButton::Left) {
|
||||||
|
if keys.pressed(KeyCode::ShiftLeft) || keys.pressed(KeyCode::ShiftRight) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some((x, y)) = mouse_to_grid(window, camera, config, ui_query) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
tile_click_messages.write(TileClickMessage { x, y });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug_click(
|
||||||
|
mouse_btn: Res<ButtonInput<MouseButton>>,
|
||||||
|
keys: Res<ButtonInput<KeyCode>>,
|
||||||
|
window: Single<&Window, With<PrimaryWindow>>,
|
||||||
|
camera: Single<(&Camera, &GlobalTransform), With<Camera2d>>,
|
||||||
|
config: Res<GameConfig>,
|
||||||
|
phase: Res<CurrentPhase>,
|
||||||
|
ui_query: Query<(&ComputedNode, &GlobalTransform), With<Node>>,
|
||||||
|
grid: Res<Grid>,
|
||||||
tile_query: Query<&mut TileState>,
|
tile_query: Query<&mut TileState>,
|
||||||
) {
|
) {
|
||||||
match phase.0 {
|
match phase.0 {
|
||||||
@@ -89,38 +109,24 @@ fn interact_click(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mouse_btn.just_pressed(MouseButton::Left) {
|
if mouse_btn.just_pressed(MouseButton::Left) {
|
||||||
let (cam, cam_transform) = *camera;
|
if !keys.pressed(KeyCode::ShiftLeft) && !keys.pressed(KeyCode::ShiftRight) {
|
||||||
|
|
||||||
let Some(cursor_pos) = window.cursor_position() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if ui_blocks(window, cursor_pos, ui_query) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Ok(world_pos) = cam.viewport_to_world(cam_transform, cursor_pos) else {
|
let Some((x, y)) = mouse_to_grid(window, camera, config, ui_query) else {
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Ok((x, y)) =
|
|
||||||
world_to_grid_coords(world_pos.origin, config.grid_width, config.grid_height)
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Interact Click: ({}, {})", x, y);
|
println!("Debug Toggle Click: ({}, {})", x, y);
|
||||||
interact_messages.write(InteractStartMessage { x, y });
|
grid.map_tile_state(
|
||||||
|
(x, y),
|
||||||
if cfg!(debug_assertions) {
|
|state| match state {
|
||||||
grid.map_tile_state(
|
TileState::Unclaimed => TileState::Empty,
|
||||||
(x, y),
|
TileState::Empty => TileState::Occupied,
|
||||||
|state| match state {
|
TileState::Occupied => TileState::Unclaimed,
|
||||||
TileState::Unclaimed => TileState::Empty,
|
},
|
||||||
TileState::Empty => TileState::Occupied,
|
tile_query,
|
||||||
TileState::Occupied => TileState::Unclaimed,
|
)
|
||||||
},
|
.unwrap_or_else(|_| ());
|
||||||
tile_query,
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|_| ());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
28
src/features/input/utils.rs
Normal file
28
src/features/input/utils.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use bevy::window::PrimaryWindow;
|
||||||
|
|
||||||
|
pub fn mouse_to_grid(
|
||||||
|
window: Single<&Window, With<PrimaryWindow>>,
|
||||||
|
camera: Single<(&Camera, &GlobalTransform), With<Camera2d>>,
|
||||||
|
config: Res<GameConfig>,
|
||||||
|
ui_query: Query<(&ComputedNode, &GlobalTransform), With<Node>>,
|
||||||
|
) -> Option<(u32, u32)> {
|
||||||
|
let (cam, cam_transform) = *camera;
|
||||||
|
|
||||||
|
let Some(cursor_pos) = window.cursor_position() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
if ui_blocks(window, cursor_pos, ui_query) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let Ok(world_pos) = cam.viewport_to_world(cam_transform, cursor_pos) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let Ok(grid_pos) =
|
||||||
|
world_to_grid_coords(world_pos.origin, config.grid_width, config.grid_height)
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(grid_pos)
|
||||||
|
}
|
||||||
@@ -36,3 +36,8 @@ impl MovingState {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
pub struct InteractionTarget {
|
||||||
|
pub target: Option<(u32, u32)>,
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,33 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use components::*;
|
use components::*;
|
||||||
use messages::InvalidMoveMessage;
|
use messages::{InteractStartMessage, InvalidMoveMessage};
|
||||||
|
use std::collections::VecDeque;
|
||||||
use utils::find_path;
|
use utils::find_path;
|
||||||
|
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);
|
||||||
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(handle_move, move_pom, update_pom).run_if(in_state(AppState::GameScreen)),
|
(
|
||||||
|
handle_move,
|
||||||
|
handle_interact,
|
||||||
|
move_pom,
|
||||||
|
update_pom,
|
||||||
|
perform_interaction,
|
||||||
|
)
|
||||||
|
.run_if(in_state(AppState::GameScreen)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,6 +38,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, config: Res<Gam
|
|||||||
GridPosition { x: 0, y: 0 },
|
GridPosition { x: 0, y: 0 },
|
||||||
PathQueue::default(),
|
PathQueue::default(),
|
||||||
MovingState::default(),
|
MovingState::default(),
|
||||||
|
InteractionTarget::default(),
|
||||||
AseAnimation {
|
AseAnimation {
|
||||||
aseprite: asset_server.load("pom/pom-sleep.aseprite"),
|
aseprite: asset_server.load("pom/pom-sleep.aseprite"),
|
||||||
animation: Animation::tag("sleep-sit-start").with_repeat(AnimationRepeat::Loop),
|
animation: Animation::tag("sleep-sit-start").with_repeat(AnimationRepeat::Loop),
|
||||||
@@ -53,10 +65,13 @@ fn handle_move(
|
|||||||
mut invalid_move_messages: MessageWriter<InvalidMoveMessage>,
|
mut invalid_move_messages: MessageWriter<InvalidMoveMessage>,
|
||||||
grid: Res<Grid>,
|
grid: Res<Grid>,
|
||||||
tile_query: Query<&TileState>,
|
tile_query: Query<&TileState>,
|
||||||
mut pom_query: Query<(&GridPosition, &mut PathQueue)>,
|
mut pom_query: Query<(&GridPosition, &mut PathQueue, &mut InteractionTarget)>,
|
||||||
) {
|
) {
|
||||||
for message in move_messages.read() {
|
for message in move_messages.read() {
|
||||||
for (grid_pos, mut path_queue) in pom_query.iter_mut() {
|
for (grid_pos, mut path_queue, mut interaction_target) in pom_query.iter_mut() {
|
||||||
|
// Clear any pending interaction when moving manually
|
||||||
|
interaction_target.target = None;
|
||||||
|
|
||||||
let grid_start = (grid_pos.x, grid_pos.y);
|
let grid_start = (grid_pos.x, grid_pos.y);
|
||||||
let start = path_queue.steps.front().unwrap_or(&grid_start);
|
let start = path_queue.steps.front().unwrap_or(&grid_start);
|
||||||
let end = (message.x, message.y);
|
let end = (message.x, message.y);
|
||||||
@@ -78,6 +93,86 @@ fn handle_move(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_interact(
|
||||||
|
mut interact_messages: MessageReader<InteractStartMessage>,
|
||||||
|
mut pom_query: Query<(&GridPosition, &mut PathQueue, &mut InteractionTarget)>,
|
||||||
|
grid: Res<Grid>,
|
||||||
|
tile_query: Query<&TileState>,
|
||||||
|
) {
|
||||||
|
for message in interact_messages.read() {
|
||||||
|
for (grid_pos, mut path_queue, mut interaction_target) in pom_query.iter_mut() {
|
||||||
|
let target_pos = (message.x, message.y);
|
||||||
|
let current_pos = (grid_pos.x, grid_pos.y);
|
||||||
|
|
||||||
|
// If we are already adjacent to the target, just set the target and clear path
|
||||||
|
if manhattan_distance(current_pos.0, current_pos.1, target_pos.0, target_pos.1) == 1 {
|
||||||
|
path_queue.steps.clear();
|
||||||
|
interaction_target.target = Some(target_pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a path to an adjacent tile
|
||||||
|
let neighbors = [
|
||||||
|
(target_pos.0 as i32 + 1, target_pos.1 as i32),
|
||||||
|
(target_pos.0 as i32 - 1, target_pos.1 as i32),
|
||||||
|
(target_pos.0 as i32, target_pos.1 as i32 + 1),
|
||||||
|
(target_pos.0 as i32, target_pos.1 as i32 - 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut best_path: Option<VecDeque<(u32, u32)>> = None;
|
||||||
|
|
||||||
|
for (nx, ny) in neighbors {
|
||||||
|
if nx < 0 || ny < 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let neighbor_pos = (nx as u32, ny as u32);
|
||||||
|
|
||||||
|
if let Some(path) = find_path(current_pos, neighbor_pos, &grid, &tile_query) {
|
||||||
|
// Pick the shortest path
|
||||||
|
if best_path.as_ref().map_or(true, |p| path.len() < p.len()) {
|
||||||
|
best_path = Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(path) = best_path {
|
||||||
|
path_queue.steps = path;
|
||||||
|
interaction_target.target = Some(target_pos);
|
||||||
|
} else {
|
||||||
|
println!("Cannot reach interaction target at {:?}", target_pos);
|
||||||
|
// Don't set target if unreachable
|
||||||
|
interaction_target.target = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform_interaction(
|
||||||
|
mut pom_query: Query<(&GridPosition, &mut InteractionTarget, &PathQueue)>,
|
||||||
|
// grid: Res<Grid>,
|
||||||
|
// mut tile_query: Query<&mut TileState>,
|
||||||
|
) {
|
||||||
|
for (pos, mut target_component, path_queue) in pom_query.iter_mut() {
|
||||||
|
if let Some(target) = target_component.target {
|
||||||
|
// Wait until movement stops
|
||||||
|
if !path_queue.steps.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if manhattan_distance(pos.x, pos.y, target.0, target.1) == 1 {
|
||||||
|
println!(
|
||||||
|
"Performing interaction on tile ({}, {})",
|
||||||
|
target.0, target.1
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Implement interaction logic
|
||||||
|
}
|
||||||
|
|
||||||
|
target_component.target = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn move_pom(
|
fn move_pom(
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut query: Query<(
|
mut query: Query<(
|
||||||
@@ -153,3 +248,4 @@ fn update_pom(asset_server: Res<AssetServer>, mut query: Query<(&MovingState, &m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
123
src/features/pom/ui/context_menu.rs
Normal file
123
src/features/pom/ui/context_menu.rs
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
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,
|
||||||
|
GlobalTransform::default(),
|
||||||
|
))
|
||||||
|
.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/features/pom/ui/mod.rs
Normal file
22
src/features/pom/ui/mod.rs
Normal 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)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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::*},
|
||||||
|
|||||||
Reference in New Issue
Block a user