Merge branch '29-harvesting-crops' into 'dev'
Implement harvesting crops See merge request softwaregrundprojekt/2025-2026/einzelprojekt/tutorium-moritz/bernroider-dominik/bernroider-dominik!27
This commit is contained in:
@@ -4,6 +4,7 @@ use crate::prelude::*;
|
||||
pub enum InteractionAction {
|
||||
Plant(ItemType),
|
||||
Water,
|
||||
Harvest,
|
||||
}
|
||||
|
||||
impl InteractionAction {
|
||||
@@ -11,6 +12,7 @@ impl InteractionAction {
|
||||
match self {
|
||||
InteractionAction::Plant(item) => format!("Pflanze {}", item.singular(game_config)),
|
||||
InteractionAction::Water => "Gießen".into(),
|
||||
InteractionAction::Harvest => "Ernten".into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +24,7 @@ impl InteractionAction {
|
||||
match self {
|
||||
InteractionAction::Plant(item) => Some(item.get_sprite(asset_server, game_config)),
|
||||
InteractionAction::Water => None,
|
||||
InteractionAction::Harvest => Some(ItemType::Berry.get_sprite(asset_server, game_config)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +32,28 @@ impl InteractionAction {
|
||||
tile_state: &TileState,
|
||||
inventory: &Inventory,
|
||||
item_query: Query<&ItemStack>,
|
||||
game_config: &GameConfig,
|
||||
) -> Vec<InteractionAction> {
|
||||
let mut options: Vec<InteractionAction> = vec![];
|
||||
|
||||
match tile_state {
|
||||
TileState::Occupied { watered, .. } => {
|
||||
if !*watered {
|
||||
options.push(InteractionAction::Water);
|
||||
TileState::Occupied {
|
||||
watered,
|
||||
withered,
|
||||
seed,
|
||||
growth_stage,
|
||||
..
|
||||
} => {
|
||||
if *withered {
|
||||
options.push(InteractionAction::Harvest);
|
||||
} else if let ItemType::BerrySeed { name } = seed {
|
||||
if let Some(config) = game_config.berry_seeds.iter().find(|s| s.name == *name) {
|
||||
if *growth_stage >= config.growth_stages {
|
||||
options.push(InteractionAction::Harvest);
|
||||
} else if !*watered {
|
||||
options.push(InteractionAction::Water);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TileState::Empty => {
|
||||
@@ -69,6 +87,7 @@ impl InteractionAction {
|
||||
inventory: &mut Inventory,
|
||||
item_stack_query: &mut Query<&mut ItemStack>,
|
||||
commands: &mut Commands,
|
||||
game_config: &GameConfig,
|
||||
) {
|
||||
let Ok(tile_entity) = grid.get_tile(pos) else {
|
||||
println!("Error during interaction: Couldn't get tile_entity");
|
||||
@@ -123,6 +142,41 @@ impl InteractionAction {
|
||||
println!("Tile is not occupied, cannot water.");
|
||||
}
|
||||
}
|
||||
InteractionAction::Harvest => {
|
||||
if let TileState::Occupied {
|
||||
seed,
|
||||
withered,
|
||||
growth_stage,
|
||||
..
|
||||
} = &*tile_state
|
||||
{
|
||||
let mut can_harvest = *withered;
|
||||
|
||||
if !can_harvest {
|
||||
if let ItemType::BerrySeed { name } = seed {
|
||||
if let Some(config) =
|
||||
game_config.berry_seeds.iter().find(|s| s.name == *name)
|
||||
{
|
||||
if *growth_stage >= config.growth_stages {
|
||||
can_harvest = true;
|
||||
inventory.update_item_stack(
|
||||
commands,
|
||||
item_stack_query,
|
||||
ItemType::Berry,
|
||||
config.grants as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if can_harvest {
|
||||
*tile_state = TileState::Empty;
|
||||
} else {
|
||||
println!("Cannot harvest: not withered and not fully grown.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,7 @@ fn perform_interaction(
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
mut commands: Commands,
|
||||
config: Res<GameConfig>,
|
||||
) {
|
||||
for (pos, mut target_component, path_queue) in pom_query.iter_mut() {
|
||||
if let Some(target) = target_component.target {
|
||||
@@ -213,6 +214,7 @@ fn perform_interaction(
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
&config,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ pub fn spawn_context_menu(
|
||||
let Ok(tile_state) = tile_query.get(tile_entity) else {
|
||||
return;
|
||||
};
|
||||
let options = InteractionAction::list_options(tile_state, &inventory, item_query);
|
||||
let options =
|
||||
InteractionAction::list_options(tile_state, &inventory, item_query, &game_config);
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
|
||||
69
tests/common/mod.rs
Normal file
69
tests/common/mod.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use bevy::prelude::*;
|
||||
use pomomon_garden::features::config::components::{BerrySeedConfig, GameConfig};
|
||||
use pomomon_garden::features::grid::components::{Grid, Tile, TileState};
|
||||
use pomomon_garden::features::inventory::components::{Inventory, ItemStack, ItemType};
|
||||
|
||||
pub fn setup_app(
|
||||
grid_width: u32,
|
||||
grid_height: u32,
|
||||
initial_tile_states: &[(u32, u32, TileState)],
|
||||
initial_inventory: Vec<(ItemType, u32)>,
|
||||
seed_configs: Option<Vec<BerrySeedConfig>>,
|
||||
) -> App {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(MinimalPlugins);
|
||||
app.add_plugins(AssetPlugin::default());
|
||||
|
||||
// Grid Setup
|
||||
let mut grid_tiles = Vec::with_capacity(grid_width as usize);
|
||||
for x in 0..grid_width {
|
||||
let mut column = Vec::with_capacity(grid_height as usize);
|
||||
for y in 0..grid_height {
|
||||
let entity = app
|
||||
.world_mut()
|
||||
.spawn((Tile { x, y }, TileState::Unclaimed))
|
||||
.id();
|
||||
column.push(entity);
|
||||
}
|
||||
grid_tiles.push(column);
|
||||
}
|
||||
app.insert_resource(Grid {
|
||||
width: grid_width,
|
||||
height: grid_height,
|
||||
tiles: grid_tiles,
|
||||
});
|
||||
|
||||
for &(x, y, ref state) in initial_tile_states {
|
||||
if let Ok(entity) = app.world().resource::<Grid>().get_tile((x, y)) {
|
||||
*app.world_mut().get_mut::<TileState>(entity).unwrap() = state.clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Inventory Setup
|
||||
let mut inventory_items = Vec::new();
|
||||
for (item_type, amount) in initial_inventory {
|
||||
let id = app.world_mut().spawn(ItemStack { item_type, amount }).id();
|
||||
inventory_items.push(id);
|
||||
}
|
||||
app.insert_resource(Inventory {
|
||||
items: inventory_items,
|
||||
});
|
||||
|
||||
// Game Config
|
||||
let seeds = seed_configs.unwrap_or_else(|| {
|
||||
vec![BerrySeedConfig {
|
||||
name: "TestSeed".to_string(),
|
||||
cost: 1,
|
||||
grants: 1,
|
||||
slice: "seed.aseprite".to_string(),
|
||||
growth_stages: 2,
|
||||
}]
|
||||
});
|
||||
|
||||
app.insert_resource(GameConfig {
|
||||
berry_seeds: seeds,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
app
|
||||
}
|
||||
197
tests/harvest.rs
Normal file
197
tests/harvest.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
use bevy::ecs::system::RunSystemOnce;
|
||||
use pomomon_garden::features::config::components::{BerrySeedConfig, GameConfig};
|
||||
use pomomon_garden::features::grid::components::{Grid, TileState};
|
||||
use pomomon_garden::features::inventory::components::{Inventory, ItemStack, ItemType};
|
||||
use pomomon_garden::features::pom::actions::InteractionAction;
|
||||
use pomomon_garden::prelude::*;
|
||||
|
||||
mod common;
|
||||
use common::setup_app;
|
||||
|
||||
#[test]
|
||||
fn test_harvest_fully_grown() {
|
||||
let seed_type = ItemType::BerrySeed {
|
||||
name: "TestSeed".into(),
|
||||
};
|
||||
let initial_states = vec![(
|
||||
0,
|
||||
0,
|
||||
TileState::Occupied {
|
||||
seed: seed_type.clone(),
|
||||
watered: false,
|
||||
growth_stage: 2, // Max
|
||||
withered: false,
|
||||
dry_counter: 0,
|
||||
},
|
||||
)];
|
||||
|
||||
let seed_config = BerrySeedConfig {
|
||||
name: "TestSeed".into(),
|
||||
cost: 1,
|
||||
grants: 5,
|
||||
slice: "".into(),
|
||||
growth_stages: 2,
|
||||
};
|
||||
|
||||
let mut app = setup_app(1, 1, &initial_states, vec![], Some(vec![seed_config]));
|
||||
|
||||
let _ = app.world_mut().run_system_once(
|
||||
|mut commands: Commands,
|
||||
grid: Res<Grid>,
|
||||
mut tile_query: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
config: Res<GameConfig>| {
|
||||
InteractionAction::Harvest.execute(
|
||||
(0, 0),
|
||||
&grid,
|
||||
&mut tile_query,
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
&config,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Check Tile State -> Empty
|
||||
// Wait for commands to apply
|
||||
app.update();
|
||||
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile((0, 0)).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
assert!(matches!(tile_state, TileState::Empty));
|
||||
|
||||
// Check Inventory -> 5 Berries
|
||||
let inventory = app.world().resource::<Inventory>();
|
||||
assert_eq!(inventory.items.len(), 1);
|
||||
let stack_entity = inventory.items[0];
|
||||
let stack = app.world().entity(stack_entity).get::<ItemStack>().unwrap();
|
||||
assert_eq!(stack.item_type, ItemType::Berry);
|
||||
assert_eq!(stack.amount, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_harvest_withered() {
|
||||
let seed_type = ItemType::BerrySeed {
|
||||
name: "TestSeed".into(),
|
||||
};
|
||||
let initial_states = vec![(
|
||||
0,
|
||||
0,
|
||||
TileState::Occupied {
|
||||
seed: seed_type.clone(),
|
||||
watered: false,
|
||||
growth_stage: 1,
|
||||
withered: true, // Withered
|
||||
dry_counter: 0,
|
||||
},
|
||||
)];
|
||||
|
||||
let seed_config = BerrySeedConfig {
|
||||
name: "TestSeed".into(),
|
||||
cost: 1,
|
||||
grants: 5,
|
||||
slice: "".into(),
|
||||
growth_stages: 2,
|
||||
};
|
||||
|
||||
let mut app = setup_app(1, 1, &initial_states, vec![], Some(vec![seed_config]));
|
||||
|
||||
let _ = app.world_mut().run_system_once(
|
||||
|mut commands: Commands,
|
||||
grid: Res<Grid>,
|
||||
mut tile_query: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
config: Res<GameConfig>| {
|
||||
InteractionAction::Harvest.execute(
|
||||
(0, 0),
|
||||
&grid,
|
||||
&mut tile_query,
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
&config,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
app.update();
|
||||
|
||||
// Check Tile State -> Empty
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile((0, 0)).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
assert!(matches!(tile_state, TileState::Empty));
|
||||
|
||||
// Check Inventory -> Empty (no berries for withered)
|
||||
let inventory = app.world().resource::<Inventory>();
|
||||
assert!(inventory.items.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cannot_harvest_growing() {
|
||||
let seed_type = ItemType::BerrySeed {
|
||||
name: "TestSeed".into(),
|
||||
};
|
||||
let initial_states = vec![(
|
||||
0,
|
||||
0,
|
||||
TileState::Occupied {
|
||||
seed: seed_type.clone(),
|
||||
watered: false,
|
||||
growth_stage: 1, // Growing (Max is 2)
|
||||
withered: false,
|
||||
dry_counter: 0,
|
||||
},
|
||||
)];
|
||||
|
||||
let seed_config = BerrySeedConfig {
|
||||
name: "TestSeed".into(),
|
||||
cost: 1,
|
||||
grants: 5,
|
||||
slice: "".into(),
|
||||
growth_stages: 2,
|
||||
};
|
||||
|
||||
let mut app = setup_app(1, 1, &initial_states, vec![], Some(vec![seed_config]));
|
||||
|
||||
let _ = app.world_mut().run_system_once(
|
||||
|mut commands: Commands,
|
||||
grid: Res<Grid>,
|
||||
mut tile_query: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
config: Res<GameConfig>| {
|
||||
InteractionAction::Harvest.execute(
|
||||
(0, 0),
|
||||
&grid,
|
||||
&mut tile_query,
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
&config,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
app.update();
|
||||
|
||||
// Check Tile State -> Still Occupied
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile((0, 0)).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
match tile_state {
|
||||
TileState::Occupied { growth_stage, .. } => {
|
||||
assert_eq!(*growth_stage, 1);
|
||||
}
|
||||
_ => panic!("Should still be occupied"),
|
||||
}
|
||||
|
||||
// Check Inventory -> Empty
|
||||
let inventory = app.world().resource::<Inventory>();
|
||||
assert!(inventory.items.is_empty());
|
||||
}
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
use bevy::ecs::system::RunSystemOnce;
|
||||
use pomomon_garden::features::grid::components::{Grid, Tile, TileState};
|
||||
use pomomon_garden::features::inventory::components::{Inventory, ItemStack, ItemType};
|
||||
use pomomon_garden::features::pom::actions::InteractionAction;
|
||||
use pomomon_garden::prelude::*;
|
||||
|
||||
fn setup_interaction_app(
|
||||
grid_width: u32,
|
||||
grid_height: u32,
|
||||
initial_tile_states: &[(u32, u32, TileState)],
|
||||
initial_inventory: Vec<(ItemType, u32)>,
|
||||
) -> App {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(MinimalPlugins);
|
||||
app.add_plugins(AssetPlugin::default()); // Needed for asset server if used, though we mock or avoid visuals here
|
||||
|
||||
// Grid Setup
|
||||
let mut grid_tiles = Vec::with_capacity(grid_width as usize);
|
||||
for x in 0..grid_width {
|
||||
let mut column = Vec::with_capacity(grid_height as usize);
|
||||
for y in 0..grid_height {
|
||||
let entity = app
|
||||
.world_mut()
|
||||
.spawn((Tile { x, y }, TileState::Unclaimed))
|
||||
.id();
|
||||
column.push(entity);
|
||||
}
|
||||
grid_tiles.push(column);
|
||||
}
|
||||
app.world_mut().insert_resource(Grid {
|
||||
width: grid_width,
|
||||
height: grid_height,
|
||||
tiles: grid_tiles,
|
||||
});
|
||||
|
||||
for &(x, y, ref state) in initial_tile_states {
|
||||
if let Ok(entity) = app.world().resource::<Grid>().get_tile((x, y)) {
|
||||
*app.world_mut().get_mut::<TileState>(entity).unwrap() = state.clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Inventory Setup
|
||||
let mut inventory_items = Vec::new();
|
||||
for (item_type, amount) in initial_inventory {
|
||||
let id = app.world_mut().spawn(ItemStack { item_type, amount }).id();
|
||||
inventory_items.push(id);
|
||||
}
|
||||
app.world_mut().insert_resource(Inventory {
|
||||
items: inventory_items,
|
||||
});
|
||||
|
||||
app
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_plant_seed_interaction() {
|
||||
let seed_type = ItemType::BerrySeed {
|
||||
name: "TestSeed".into(),
|
||||
};
|
||||
let mut app = setup_interaction_app(
|
||||
3,
|
||||
3,
|
||||
&[(1, 1, TileState::Empty)],
|
||||
vec![(seed_type.clone(), 1)],
|
||||
);
|
||||
|
||||
// Run the interaction logic as a system or closure
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
mut tile_query: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
mut commands: Commands| {
|
||||
let action = InteractionAction::Plant(seed_type.clone());
|
||||
action.execute(
|
||||
(1, 1),
|
||||
&grid,
|
||||
&mut tile_query,
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Apply commands (despawns etc.)
|
||||
app.update();
|
||||
|
||||
// Assert Tile State
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
|
||||
if let TileState::Occupied { seed, .. } = tile_state {
|
||||
assert_eq!(
|
||||
*seed,
|
||||
ItemType::BerrySeed {
|
||||
name: "TestSeed".into()
|
||||
}
|
||||
);
|
||||
} else {
|
||||
panic!("Tile should be Occupied with seed, found {:?}", tile_state);
|
||||
}
|
||||
|
||||
// Assert Inventory Empty
|
||||
let inventory = app.world().resource::<Inventory>();
|
||||
// Item stack entity should be despawned or amount 0
|
||||
if !inventory.items.is_empty() {
|
||||
// If the entity still exists, check amount
|
||||
if let Some(entity) = inventory.items.first() {
|
||||
if let Some(stack) = app.world().entity(*entity).get::<ItemStack>() {
|
||||
assert_eq!(stack.amount, 0, "Item amount should be 0");
|
||||
} else {
|
||||
panic!(
|
||||
"Inventory items list should be empty or point to valid 0 amount entities. Found phantom entity."
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert!(inventory.items.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_plant_seed_no_inventory() {
|
||||
let seed_type = ItemType::BerrySeed {
|
||||
name: "TestSeed".into(),
|
||||
};
|
||||
let mut app = setup_interaction_app(
|
||||
3,
|
||||
3,
|
||||
&[(1, 1, TileState::Empty)],
|
||||
vec![], // Empty inventory
|
||||
);
|
||||
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
mut tile_query: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
mut commands: Commands| {
|
||||
let action = InteractionAction::Plant(seed_type.clone());
|
||||
action.execute(
|
||||
(1, 1),
|
||||
&grid,
|
||||
&mut tile_query,
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
app.update();
|
||||
|
||||
// Assert Tile State Unchanged
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
|
||||
if let TileState::Empty = tile_state {
|
||||
// Correct
|
||||
} else {
|
||||
panic!("Tile should remain Empty, found {:?}", tile_state);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_water_crop() {
|
||||
let seed_type = ItemType::BerrySeed {
|
||||
name: "TestSeed".into(),
|
||||
};
|
||||
let mut app = setup_interaction_app(
|
||||
3,
|
||||
3,
|
||||
&[(
|
||||
1,
|
||||
1,
|
||||
TileState::Occupied {
|
||||
seed: seed_type.clone(),
|
||||
watered: false,
|
||||
growth_stage: 0,
|
||||
withered: false,
|
||||
dry_counter: 0,
|
||||
},
|
||||
)],
|
||||
vec![],
|
||||
);
|
||||
|
||||
// Verify Water option is available
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
tile_query: Query<&TileState>,
|
||||
inventory: Res<Inventory>,
|
||||
item_query: Query<&ItemStack>| {
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = tile_query.get(tile_entity).unwrap();
|
||||
let options = InteractionAction::list_options(tile_state, &inventory, item_query);
|
||||
|
||||
assert!(
|
||||
options.contains(&InteractionAction::Water),
|
||||
"Water option should be available"
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Execute Water
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
mut tile_query: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
mut commands: Commands| {
|
||||
let action = InteractionAction::Water;
|
||||
action.execute(
|
||||
(1, 1),
|
||||
&grid,
|
||||
&mut tile_query,
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
app.update();
|
||||
|
||||
// Assert Tile State Watered
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
|
||||
if let TileState::Occupied { watered, .. } = tile_state {
|
||||
assert!(watered, "Tile should be watered");
|
||||
} else {
|
||||
panic!("Tile should be Occupied, found {:?}", tile_state);
|
||||
}
|
||||
|
||||
// Verify Water option is NOT available
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
tile_query: Query<&TileState>,
|
||||
inventory: Res<Inventory>,
|
||||
item_query: Query<&ItemStack>| {
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = tile_query.get(tile_entity).unwrap();
|
||||
let options = InteractionAction::list_options(tile_state, &inventory, item_query);
|
||||
|
||||
assert!(
|
||||
!options.contains(&InteractionAction::Water),
|
||||
"Water option should NOT be available"
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
128
tests/planting.rs
Normal file
128
tests/planting.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
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::features::pom::actions::InteractionAction;
|
||||
use pomomon_garden::prelude::*;
|
||||
|
||||
mod common;
|
||||
use common::setup_app;
|
||||
|
||||
#[test]
|
||||
fn test_plant_seed_interaction() {
|
||||
let seed_type = ItemType::BerrySeed {
|
||||
name: "TestSeed".into(),
|
||||
};
|
||||
let mut app = setup_app(
|
||||
3,
|
||||
3,
|
||||
&[(1, 1, TileState::Empty)],
|
||||
vec![(seed_type.clone(), 1)],
|
||||
None,
|
||||
);
|
||||
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
mut tile_query: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
mut commands: Commands,
|
||||
game_config: Res<GameConfig>| {
|
||||
let action = InteractionAction::Plant(seed_type.clone());
|
||||
action.execute(
|
||||
(1, 1),
|
||||
&grid,
|
||||
&mut tile_query,
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
&game_config,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Apply commands (despawns etc.)
|
||||
app.update();
|
||||
|
||||
// Assert Tile State
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
|
||||
if let TileState::Occupied { seed, .. } = tile_state {
|
||||
assert_eq!(
|
||||
*seed,
|
||||
ItemType::BerrySeed {
|
||||
name: "TestSeed".into()
|
||||
}
|
||||
);
|
||||
} else {
|
||||
panic!("Tile should be Occupied with seed, found {:?}", tile_state);
|
||||
}
|
||||
|
||||
// Assert Inventory Empty
|
||||
let inventory = app.world().resource::<Inventory>();
|
||||
// Item stack entity should be despawned or amount 0
|
||||
if !inventory.items.is_empty() {
|
||||
// If the entity still exists, check amount
|
||||
if let Some(entity) = inventory.items.first() {
|
||||
if let Some(stack) = app.world().entity(*entity).get::<ItemStack>() {
|
||||
assert_eq!(stack.amount, 0, "Item amount should be 0");
|
||||
} else {
|
||||
panic!(
|
||||
"Inventory items list should be empty or point to valid 0 amount entities. Found phantom entity."
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert!(inventory.items.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_plant_seed_no_inventory() {
|
||||
let seed_type = ItemType::BerrySeed {
|
||||
name: "TestSeed".into(),
|
||||
};
|
||||
let mut app = setup_app(
|
||||
3,
|
||||
3,
|
||||
&[(1, 1, TileState::Empty)],
|
||||
vec![], // Empty inventory
|
||||
None,
|
||||
);
|
||||
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
mut tile_query: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
mut commands: Commands,
|
||||
game_config: Res<GameConfig>| {
|
||||
let action = InteractionAction::Plant(seed_type.clone());
|
||||
action.execute(
|
||||
(1, 1),
|
||||
&grid,
|
||||
&mut tile_query,
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
&game_config,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
app.update();
|
||||
|
||||
// Assert Tile State Unchanged
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
|
||||
if let TileState::Empty = tile_state {
|
||||
// Correct
|
||||
} else {
|
||||
panic!("Tile should remain Empty, found {:?}", tile_state);
|
||||
}
|
||||
}
|
||||
|
||||
106
tests/watering.rs
Normal file
106
tests/watering.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
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::features::pom::actions::InteractionAction;
|
||||
use pomomon_garden::prelude::*;
|
||||
|
||||
mod common;
|
||||
use common::setup_app;
|
||||
|
||||
#[test]
|
||||
fn test_water_crop() {
|
||||
let seed_type = ItemType::BerrySeed {
|
||||
name: "TestSeed".into(),
|
||||
};
|
||||
let mut app = setup_app(
|
||||
3,
|
||||
3,
|
||||
&[(
|
||||
1,
|
||||
1,
|
||||
TileState::Occupied {
|
||||
seed: seed_type.clone(),
|
||||
watered: false,
|
||||
growth_stage: 0,
|
||||
withered: false,
|
||||
dry_counter: 0,
|
||||
},
|
||||
)],
|
||||
vec![],
|
||||
None,
|
||||
);
|
||||
|
||||
// Verify Water option is available
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
tile_query: Query<&TileState>,
|
||||
inventory: Res<Inventory>,
|
||||
item_query: Query<&ItemStack>,
|
||||
game_config: Res<GameConfig>| {
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = tile_query.get(tile_entity).unwrap();
|
||||
let options =
|
||||
InteractionAction::list_options(tile_state, &inventory, item_query, &game_config);
|
||||
|
||||
assert!(
|
||||
options.contains(&InteractionAction::Water),
|
||||
"Water option should be available"
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Execute Water
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
mut tile_query: Query<&mut TileState>,
|
||||
mut inventory: ResMut<Inventory>,
|
||||
mut item_stack_query: Query<&mut ItemStack>,
|
||||
mut commands: Commands,
|
||||
game_config: Res<GameConfig>| {
|
||||
let action = InteractionAction::Water;
|
||||
action.execute(
|
||||
(1, 1),
|
||||
&grid,
|
||||
&mut tile_query,
|
||||
&mut inventory,
|
||||
&mut item_stack_query,
|
||||
&mut commands,
|
||||
&game_config,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
app.update();
|
||||
|
||||
// Assert Tile State Watered
|
||||
let grid = app.world().resource::<Grid>();
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
|
||||
|
||||
if let TileState::Occupied { watered, .. } = tile_state {
|
||||
assert!(watered, "Tile should be watered");
|
||||
} else {
|
||||
panic!("Tile should be Occupied, found {:?}", tile_state);
|
||||
}
|
||||
|
||||
// Verify Water option is NOT available
|
||||
let _ = app.world_mut().run_system_once(
|
||||
move |grid: Res<Grid>,
|
||||
tile_query: Query<&TileState>,
|
||||
inventory: Res<Inventory>,
|
||||
item_query: Query<&ItemStack>,
|
||||
game_config: Res<GameConfig>| {
|
||||
let tile_entity = grid.get_tile((1, 1)).unwrap();
|
||||
let tile_state = tile_query.get(tile_entity).unwrap();
|
||||
let options =
|
||||
InteractionAction::list_options(tile_state, &inventory, item_query, &game_config);
|
||||
|
||||
assert!(
|
||||
!options.contains(&InteractionAction::Water),
|
||||
"Water option should NOT be available"
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user