Merge branch '15-farm-expansion' into 'dev'
Implement farm expansion See merge request softwaregrundprojekt/2025-2026/einzelprojekt/tutorium-moritz/bernroider-dominik/bernroider-dominik!34
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"grid_width": 12,
|
||||
"grid_height": 4,
|
||||
"grid_width": 15,
|
||||
"grid_height": 5,
|
||||
"pom_speed": 1.5,
|
||||
"shovel_base_price": 10,
|
||||
"shovel_rate": 0.5,
|
||||
"shovel_rate": 0.2,
|
||||
"berry_seeds": [
|
||||
{
|
||||
"name": "Normale Samen",
|
||||
@@ -29,4 +29,3 @@
|
||||
],
|
||||
"wonder_event_url": "wss://pomomon.farm/ws"
|
||||
}
|
||||
|
||||
|
||||
BIN
assets/shovel.aseprite
Normal file
BIN
assets/shovel.aseprite
Normal file
Binary file not shown.
@@ -13,7 +13,7 @@ pub struct CropVisual;
|
||||
#[derive(Component)]
|
||||
pub struct WaterVisual;
|
||||
|
||||
#[derive(Component, Default, Serialize, Deserialize, Clone, Debug)]
|
||||
#[derive(Component, Default, Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum TileState {
|
||||
#[default]
|
||||
Unclaimed,
|
||||
@@ -74,4 +74,18 @@ impl Grid {
|
||||
*tile_state = mapper(&*tile_state);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn count_claimed_tiles(&self, tile_query: &Query<&TileState>) -> u32 {
|
||||
self.tiles
|
||||
.iter()
|
||||
.flatten()
|
||||
.filter(|&entity| {
|
||||
if let Ok(state) = tile_query.get(*entity) {
|
||||
!matches!(state, TileState::Unclaimed)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.count() as u32
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::prelude::*;
|
||||
use components::{CropVisual, WaterVisual};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub mod components;
|
||||
pub mod consts;
|
||||
@@ -26,13 +27,28 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, config: Res<Gam
|
||||
let mut column = Vec::with_capacity(grid_height as usize);
|
||||
|
||||
for y in 0..grid_height {
|
||||
let initial_state = if x == 1 && y == 1 {
|
||||
TileState::Empty
|
||||
} else {
|
||||
TileState::Unclaimed
|
||||
};
|
||||
|
||||
let tile_entity = commands
|
||||
.spawn((
|
||||
Tile { x, y },
|
||||
TileState::Unclaimed,
|
||||
initial_state.clone(),
|
||||
AseSlice {
|
||||
name: "Unclaimed".into(),
|
||||
aseprite: asset_server.load("tiles/tile-unclaimed.aseprite"),
|
||||
name: match initial_state {
|
||||
TileState::Unclaimed => "Unclaimed",
|
||||
TileState::Empty => "Empty",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.into(),
|
||||
aseprite: asset_server.load(match initial_state {
|
||||
TileState::Unclaimed => "tiles/tile-unclaimed.aseprite",
|
||||
TileState::Empty => "tiles/tile-empty.aseprite",
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
},
|
||||
Sprite::default(),
|
||||
Transform::from_translation(grid_to_world_coords(
|
||||
@@ -88,7 +104,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 +118,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 +171,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 {
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::{features::phase::components::TimerSettings, prelude::*};
|
||||
pub enum RootMarker {
|
||||
Status,
|
||||
Settings,
|
||||
ShovelOverlay,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
|
||||
@@ -16,12 +16,18 @@ impl Plugin for HudPlugin {
|
||||
app.add_systems(OnExit(AppState::GameScreen), cleanup);
|
||||
app.add_systems(
|
||||
Update,
|
||||
(update_status, buttons, update_timer_settings).run_if(in_state(AppState::GameScreen)),
|
||||
(
|
||||
update_status,
|
||||
buttons,
|
||||
update_timer_settings,
|
||||
update_shovel_overlay_visibility,
|
||||
)
|
||||
.run_if(in_state(AppState::GameScreen)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
fn setup(mut commands: Commands, game_config: Res<GameConfig>, asset_server: Res<AssetServer>) {
|
||||
commands.spawn((
|
||||
RootMarker::Status,
|
||||
Node {
|
||||
@@ -61,6 +67,63 @@ fn setup(mut commands: Commands) {
|
||||
)
|
||||
],
|
||||
));
|
||||
|
||||
// Shovel Overlay
|
||||
commands.spawn((
|
||||
RootMarker::ShovelOverlay,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: px(20),
|
||||
left: px(0),
|
||||
right: px(0),
|
||||
width: percent(100),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
flex_direction: FlexDirection::Column,
|
||||
row_gap: px(5),
|
||||
..default()
|
||||
},
|
||||
Visibility::Hidden,
|
||||
children![(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(px(10)),
|
||||
row_gap: px(5),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.7)),
|
||||
BorderRadius::all(px(10)),
|
||||
children![
|
||||
(
|
||||
Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
align_items: AlignItems::Center,
|
||||
column_gap: px(10),
|
||||
..default()
|
||||
},
|
||||
children![
|
||||
(
|
||||
Node {
|
||||
width: px(32),
|
||||
height: px(32),
|
||||
..default()
|
||||
},
|
||||
inventory::components::ItemType::Shovel
|
||||
.get_sprite(&asset_server, &game_config),
|
||||
ImageNode::default()
|
||||
),
|
||||
text("Schaufel-Modus", 20.0, Color::WHITE)
|
||||
]
|
||||
),
|
||||
text(
|
||||
"Klicke auf ein freies Feld, um es freizuschalten.",
|
||||
14.0,
|
||||
Color::WHITE
|
||||
)
|
||||
]
|
||||
)],
|
||||
));
|
||||
}
|
||||
|
||||
fn update_status(phase_res: Res<CurrentPhase>, mut text_query: Query<(&mut Text, &TextType)>) {
|
||||
@@ -151,3 +214,21 @@ fn update_timer_settings(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_shovel_overlay_visibility(
|
||||
inventory: Res<inventory::components::Inventory>,
|
||||
item_stacks: Query<&inventory::components::ItemStack>,
|
||||
mut overlay_query: Query<(&RootMarker, &mut Visibility)>,
|
||||
) {
|
||||
let has_shovel = inventory.has_item_type(&item_stacks, inventory::components::ItemType::Shovel);
|
||||
|
||||
for (marker, mut vis) in overlay_query.iter_mut() {
|
||||
if let RootMarker::ShovelOverlay = marker {
|
||||
*vis = if has_shovel {
|
||||
Visibility::Inherited
|
||||
} else {
|
||||
Visibility::Hidden
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
tile_click_messages.write(TileClickMessage { x, y });
|
||||
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>>,
|
||||
@@ -162,9 +240,17 @@ fn shop_keybind(
|
||||
mut commands: Commands,
|
||||
game_config: Res<GameConfig>,
|
||||
asset_server: Res<AssetServer>,
|
||||
grid: Res<Grid>,
|
||||
tile_query: Query<&TileState>,
|
||||
) {
|
||||
if keys.just_pressed(KeyCode::KeyP) {
|
||||
open_shop(&mut commands, &game_config, &asset_server);
|
||||
open_shop(
|
||||
&mut commands,
|
||||
&game_config,
|
||||
&asset_server,
|
||||
&grid,
|
||||
&tile_query,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,8 +80,8 @@ impl ItemType {
|
||||
aseprite: asset_server.load("berry.aseprite"),
|
||||
},
|
||||
ItemType::Shovel => AseSlice {
|
||||
name: "Berry".into(),
|
||||
aseprite: asset_server.load("berry.aseprite"),
|
||||
name: "Shovel".into(),
|
||||
aseprite: asset_server.load("shovel.aseprite"),
|
||||
},
|
||||
ItemType::BerrySeed { name } => {
|
||||
let seed_config = game_config.berry_seeds.iter().find(|s| s.name == *name);
|
||||
@@ -102,7 +102,7 @@ impl ItemType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||
#[derive(Component, Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ItemStack {
|
||||
pub item_type: ItemType,
|
||||
pub amount: u32,
|
||||
@@ -114,12 +114,14 @@ pub struct Inventory {
|
||||
}
|
||||
|
||||
impl Inventory {
|
||||
pub fn has_item(&self, items_query: Query<&ItemStack>) -> bool {
|
||||
self.items
|
||||
.iter()
|
||||
.map(|entity| items_query.get(*entity).ok())
|
||||
.find(|option| option.is_some())
|
||||
.is_some()
|
||||
pub fn has_item_type(&self, items_query: &Query<&ItemStack>, item_type: ItemType) -> bool {
|
||||
self.items.iter().any(|&entity| {
|
||||
if let Ok(stack) = items_query.get(entity) {
|
||||
stack.item_type == item_type
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_item_stack(
|
||||
|
||||
@@ -21,12 +21,14 @@ fn buttons(
|
||||
asset_server: Res<AssetServer>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut items: Query<&mut ItemStack>,
|
||||
grid: Res<Grid>,
|
||||
tile_query: Query<&TileState>,
|
||||
) {
|
||||
for (interaction, button_type) in &mut interaction_query {
|
||||
match *interaction {
|
||||
Interaction::Pressed => match button_type {
|
||||
ButtonType::ShopOpen => {
|
||||
open_shop(&mut commands, &game_config, &asset_server);
|
||||
open_shop(&mut commands, &game_config, &asset_server, &grid, &tile_query);
|
||||
}
|
||||
ButtonType::ShopBuyItem(offer) => {
|
||||
if offer.buy(&mut inventory, &mut commands, &mut items) {
|
||||
|
||||
@@ -5,9 +5,11 @@ pub fn open_shop(
|
||||
commands: &mut Commands,
|
||||
game_config: &GameConfig,
|
||||
asset_server: &Res<AssetServer>,
|
||||
grid: &Grid,
|
||||
tile_query: &Query<&TileState>,
|
||||
) {
|
||||
// TODO: calculate tile_count
|
||||
let offers = ShopOffer::list_all(game_config, 0);
|
||||
let tile_count = grid.count_claimed_tiles(tile_query);
|
||||
let offers = ShopOffer::list_all(game_config, tile_count);
|
||||
|
||||
spawn_popup(
|
||||
commands,
|
||||
|
||||
116
tests/expansion.rs
Normal file
116
tests/expansion.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use bevy::ecs::system::RunSystemOnce;
|
||||
use pomomon_garden::features::config::components::GameConfig;
|
||||
use pomomon_garden::features::grid::components::{Grid, TileState};
|
||||
use pomomon_garden::features::inventory::components::{Inventory, ItemStack, ItemType};
|
||||
use pomomon_garden::prelude::*;
|
||||
|
||||
mod common;
|
||||
use common::setup_app;
|
||||
|
||||
#[test]
|
||||
fn test_shovel_expansion() {
|
||||
let mut app = setup_app(
|
||||
5,
|
||||
5,
|
||||
&[(2, 2, TileState::Empty)], // (2,2) is Empty, making it a "claimed" neighbor
|
||||
vec![(ItemType::Shovel, 1)], // One shovel in inventory
|
||||
None,
|
||||
);
|
||||
|
||||
let target_pos = (2, 3); // Unclaimed tile next to (2,2), not on edge of 5x5 grid
|
||||
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |
|
||||
grid: Res<Grid>,
|
||||
mut tile_states: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
mut commands: Commands,
|
||||
_game_config: Res<GameConfig> // Not directly used but required by signature
|
||||
| {
|
||||
let (x, y) = target_pos;
|
||||
|
||||
let tile_entity = match grid.get_tile((x, y)) {
|
||||
Ok(entity) => entity,
|
||||
Err(_) => panic!("Clicked outside grid at {:?}", (x,y)),
|
||||
};
|
||||
|
||||
// Mimic the edge check from interact_click
|
||||
if x == 0 || x == grid.width - 1 || y == 0 || y == grid.height - 1 {
|
||||
panic!("Should not return early due to edge check for {:?} in a 5x5 grid", target_pos);
|
||||
}
|
||||
|
||||
let neighbors = [
|
||||
(x + 1, y),
|
||||
(x.saturating_sub(1), y),
|
||||
(x, y + 1),
|
||||
(x, y.saturating_sub(1)),
|
||||
];
|
||||
|
||||
let mut has_claimed_neighbor = false;
|
||||
for (nx, ny) in neighbors.iter() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(has_claimed_neighbor, "Target tile {:?} should have a claimed neighbor", target_pos);
|
||||
|
||||
let mut tile_state = match tile_states.get_mut(tile_entity) {
|
||||
Ok(state) => state,
|
||||
Err(_) => panic!("Failed to get mutable tile state for {:?}", target_pos),
|
||||
};
|
||||
|
||||
assert!(matches!(*tile_state, TileState::Unclaimed), "Target tile {:?} should be Unclaimed initially, but was {:?}", target_pos, tile_state);
|
||||
|
||||
// Execute the expansion
|
||||
if inventory.update_item_stack(
|
||||
&mut commands,
|
||||
&mut item_stack_query,
|
||||
ItemType::Shovel,
|
||||
-1,
|
||||
) {
|
||||
*tile_state = TileState::Empty;
|
||||
} else {
|
||||
panic!("Shovel not consumed or not found in inventory");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
app.update(); // Apply commands
|
||||
|
||||
// Assert Tile State
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile(target_pos).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
|
||||
assert!(
|
||||
matches!(*tile_state, TileState::Empty),
|
||||
"Tile (1,2) should be Empty after expansion, but was {:?}",
|
||||
tile_state
|
||||
);
|
||||
|
||||
// Assert Inventory
|
||||
let inventory = app.world().resource::<Inventory>();
|
||||
let shovel_stack = inventory.items.iter().find_map(|&entity| {
|
||||
let stack = app.world().entity(entity).get::<ItemStack>()?;
|
||||
if stack.item_type == ItemType::Shovel {
|
||||
Some(stack)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
assert!(
|
||||
shovel_stack.is_none() || shovel_stack.unwrap().amount == 0,
|
||||
"Shovel should have been consumed, found {:?}",
|
||||
shovel_stack
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user