feat: Implement shovel interaction and tile highlighting (#15)

This commit is contained in:
demenik
2025-12-09 17:30:55 +01:00
parent bde09ec5f2
commit 8f01e0bc80
2 changed files with 133 additions and 4 deletions

View File

@@ -1,5 +1,6 @@
use crate::prelude::*;
use components::{CropVisual, WaterVisual};
use std::collections::HashSet;
pub mod components;
pub mod consts;
@@ -88,7 +89,10 @@ fn cleanup(mut commands: Commands, tile_query: Query<Entity, With<Tile>>) {
}
fn update_tiles(
mut query: Query<(&TileState, &mut AseSlice, &Children), (With<Tile>, Without<CropVisual>)>,
mut query: Query<
(&TileState, &mut AseSlice, &Children, &Tile),
(With<Tile>, Without<CropVisual>),
>,
mut crop_query: Query<
(&mut Visibility, &mut Transform, &mut AseSlice),
(With<CropVisual>, Without<WaterVisual>, Without<Tile>),
@@ -99,8 +103,27 @@ fn update_tiles(
>,
asset_server: Res<AssetServer>,
game_config: Res<GameConfig>,
inventory: Res<Inventory>,
item_stacks: Query<&ItemStack>,
grid: Res<Grid>,
mut sprite_query: Query<&mut Sprite, With<Tile>>,
) {
for (state, mut slice, children) in &mut query {
let has_shovel = inventory.has_item_type(&item_stacks, ItemType::Shovel);
let owned_tiles: HashSet<(u32, u32)> = query
.iter()
.filter_map(|(state, _, _, tile)| {
if !matches!(state, TileState::Unclaimed) {
Some((tile.x, tile.y))
} else {
None
}
})
.collect();
for (state, mut slice, children, tile) in &mut query {
let entity = grid.get_tile((tile.x, tile.y)).unwrap(); // Get entity for sprite query
slice.name = match state {
TileState::Unclaimed => "Unclaimed",
TileState::Empty => "Empty",
@@ -133,6 +156,34 @@ fn update_tiles(
_ => Vec3::ONE,
};
let mut is_highlighted = false;
if has_shovel && matches!(state, TileState::Unclaimed) {
// Check if not on edge
if tile.x > 0 && tile.x < grid.width - 1 && tile.y > 0 && tile.y < grid.height - 1 {
// Check neighbors
let neighbors = [
(tile.x + 1, tile.y),
(tile.x.saturating_sub(1), tile.y),
(tile.x, tile.y + 1),
(tile.x, tile.y.saturating_sub(1)),
];
for n in neighbors.iter() {
if owned_tiles.contains(n) {
is_highlighted = true;
break;
}
}
}
}
if let Ok(mut sprite) = sprite_query.get_mut(entity) {
if is_highlighted {
sprite.color = Color::srgb(0.3, 1.0, 0.3); // Green tint
} else {
sprite.color = Color::WHITE;
}
}
for child in children.iter() {
if let Ok((mut visibility, mut transform, mut sprite)) = crop_query.get_mut(child) {
*visibility = match state {

View File

@@ -53,7 +53,13 @@ fn move_click(
config: Res<GameConfig>,
phase: Res<CurrentPhase>,
ui_query: Query<(&ComputedNode, &GlobalTransform), With<Node>>,
inventory: Res<Inventory>,
item_stacks: Query<&ItemStack>,
) {
if inventory.has_item_type(&item_stacks, ItemType::Shovel) {
return; // Block movement if player has a shovel
}
match phase.0 {
Phase::Focus { .. } => return,
_ => {}
@@ -78,12 +84,25 @@ fn interact_click(
config: Res<GameConfig>,
phase: Res<CurrentPhase>,
ui_query: Query<(&ComputedNode, &GlobalTransform), With<Node>>,
mut commands: Commands,
mut inventory: ResMut<Inventory>,
mut item_stacks: Query<&mut ItemStack>,
grid: Res<Grid>,
mut tile_states: Query<&mut TileState>,
) {
match phase.0 {
Phase::Focus { .. } => return,
_ => {}
}
let has_shovel = inventory.items.iter().any(|&entity| {
if let Ok(stack) = item_stacks.get(entity) {
stack.item_type == ItemType::Shovel
} else {
false
}
});
if mouse_btn.just_pressed(MouseButton::Left) {
if keys.pressed(KeyCode::ShiftLeft) || keys.pressed(KeyCode::ShiftRight) {
return;
@@ -92,10 +111,69 @@ fn interact_click(
return;
};
if has_shovel {
// Shovel interaction logic
let tile_entity = match grid.get_tile((x, y)) {
Ok(entity) => entity,
Err(_) => return, // Clicked outside grid
};
// Before getting mutable tile_state, check neighbors with immutable borrow
let mut has_claimed_neighbor = false;
// Check if not on edge first for early exit to avoid checking neighbors outside grid.
if x == 0 || x == grid.width - 1 || y == 0 || y == grid.height - 1 {
return;
}
let neighbors = [
(x + 1, y),
(x.saturating_sub(1), y),
(x, y + 1),
(x, y.saturating_sub(1)),
];
for (nx, ny) in neighbors.iter() {
// Ensure neighbor coordinates are within grid boundaries before attempting to get tile
if *nx < grid.width && *ny < grid.height {
if let Ok(neighbor_entity) = grid.get_tile((*nx, *ny)) {
if let Ok(neighbor_state) = tile_states.get(neighbor_entity) {
if !matches!(*neighbor_state, TileState::Unclaimed) {
has_claimed_neighbor = true;
break;
}
}
}
}
}
if !has_claimed_neighbor {
return; // No claimed neighbor, cannot unlock
}
// Now get mutable tile_state, after all immutable neighbor checks are done
let mut tile_state = match tile_states.get_mut(tile_entity) {
Ok(state) => state,
Err(_) => return, // Should not happen
};
// Check if unclaimed AFTER determining neighbor status
if !matches!(*tile_state, TileState::Unclaimed) {
return;
}
if has_claimed_neighbor {
// This check is redundant due to early return above, but kept for clarity
// Unlock tile
*tile_state = TileState::Empty;
inventory.update_item_stack(&mut commands, &mut item_stacks, ItemType::Shovel, -1);
}
return; // Consume click event, prevent normal tile_click_messages
} else {
// Normal interaction
tile_click_messages.write(TileClickMessage { x, y });
}
}
}
fn debug_click(
mouse_btn: Res<ButtonInput<MouseButton>>,
keys: Res<ButtonInput<KeyCode>>,