feat: Add watering (#27)

This commit is contained in:
demenik
2025-12-02 15:46:48 +01:00
parent d07dcc3e7c
commit 4df968af2e
7 changed files with 159 additions and 5 deletions

View File

@@ -10,6 +10,9 @@ pub struct Tile {
#[derive(Component)]
pub struct CropVisual;
#[derive(Component)]
pub struct WaterVisual;
#[derive(Component, Default, Serialize, Deserialize, Clone, Debug)]
pub enum TileState {
#[default]
@@ -17,6 +20,7 @@ pub enum TileState {
Empty,
Occupied {
seed: ItemType,
watered: bool,
},
}

View File

@@ -1,5 +1,5 @@
use crate::prelude::*;
use components::CropVisual;
use components::{CropVisual, WaterVisual};
pub mod components;
pub mod consts;
@@ -55,6 +55,17 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, config: Res<Gam
Visibility::Hidden,
ZIndex(1),
));
parent.spawn((
WaterVisual,
AseSlice {
name: "Water".into(),
aseprite: asset_server.load("crop.aseprite"),
},
Sprite::default(),
Transform::default(),
Visibility::Hidden,
ZIndex(2),
));
})
.id();
column.push(tile_entity);
@@ -78,7 +89,8 @@ fn cleanup(mut commands: Commands, tile_query: Query<Entity, With<Tile>>) {
fn update_tiles(
mut query: Query<(&TileState, &mut AseSlice, &Children)>,
mut crop_query: Query<&mut Visibility, With<CropVisual>>,
mut crop_query: Query<&mut Visibility, (With<CropVisual>, Without<WaterVisual>)>,
mut water_query: Query<&mut Visibility, (With<WaterVisual>, Without<CropVisual>)>,
asset_server: Res<AssetServer>,
) {
for (state, mut slice, children) in &mut query {
@@ -102,6 +114,12 @@ fn update_tiles(
_ => Visibility::Hidden,
};
}
if let Ok(mut visibility) = water_query.get_mut(child) {
*visibility = match state {
TileState::Occupied { watered: true, .. } => Visibility::Visible,
_ => Visibility::Hidden,
};
}
}
}
}

View File

@@ -125,6 +125,7 @@ fn debug_click(
seed: ItemType::BerrySeed {
name: "Debug".into(),
},
watered: false,
},
TileState::Occupied { .. } => TileState::Unclaimed,
},

View File

@@ -157,8 +157,27 @@ fn handle_continue(
mut phase_res: ResMut<CurrentPhase>,
mut session_tracker: ResMut<SessionTracker>,
settings: Res<TimerSettings>,
mut tile_query: Query<&mut TileState>,
) {
for _ in messages.read() {
let entering_break = if let Phase::Finished { completed_phase } = &phase_res.0 {
matches!(**completed_phase, Phase::Focus { .. })
} else {
false
};
next_phase(&mut phase_res, &mut session_tracker, &settings);
if entering_break {
println!("Resetting watered state for all crops.");
for mut state in tile_query.iter_mut() {
if let TileState::Occupied { seed, .. } = &*state {
*state = TileState::Occupied {
seed: seed.clone(),
watered: false,
};
}
}
}
}
}

View File

@@ -3,12 +3,14 @@ use crate::prelude::*;
#[derive(Clone, Debug, PartialEq)]
pub enum InteractionAction {
Plant(ItemType),
Water,
}
impl InteractionAction {
pub fn get_name(&self, game_config: &GameConfig) -> String {
match self {
InteractionAction::Plant(item) => format!("Pflanze {}", item.singular(game_config)),
InteractionAction::Water => "Gießen".into(),
}
}
@@ -19,6 +21,7 @@ impl InteractionAction {
) -> Option<AseSlice> {
match self {
InteractionAction::Plant(item) => Some(item.get_sprite(asset_server, game_config)),
InteractionAction::Water => None,
}
}
@@ -48,6 +51,15 @@ impl InteractionAction {
}
}
match tile_state {
TileState::Occupied { watered, .. } => {
if !*watered {
options.push(InteractionAction::Water);
}
}
_ => (),
}
options
}
@@ -81,6 +93,7 @@ impl InteractionAction {
println!("Planting {:?}", seed_type);
*tile_state = TileState::Occupied {
seed: seed_type.clone(),
watered: false,
};
} else {
println!("No {:?} in inventory!", seed_type);
@@ -89,6 +102,17 @@ impl InteractionAction {
println!("Tile is not empty, cannot plant.");
}
}
InteractionAction::Water => {
if let TileState::Occupied { seed, .. } = &*tile_state {
println!("Watering {:?}", seed);
*tile_state = TileState::Occupied {
seed: seed.clone(),
watered: true,
};
} else {
println!("Tile is not occupied, cannot water.");
}
}
}
}
}

View File

@@ -91,7 +91,7 @@ fn test_plant_seed_interaction() {
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 {
if let TileState::Occupied { seed, .. } = tile_state {
assert_eq!(
*seed,
ItemType::BerrySeed {
@@ -164,3 +164,89 @@ fn test_plant_seed_no_inventory() {
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,
},
)],
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"
);
},
);
}

View File

@@ -91,8 +91,9 @@ fn test_find_path_simple() {
fn test_find_path_around_obstacle() {
let obstacle: TileState = TileState::Occupied {
seed: ItemType::BerrySeed {
name: "test".into(),
name: "Test".into(),
},
watered: false,
};
let obstacles = vec![
(2, 2, obstacle.clone()),
@@ -142,8 +143,9 @@ fn test_find_path_around_obstacle() {
fn test_find_path_no_path() {
let obstacle: TileState = TileState::Occupied {
seed: ItemType::BerrySeed {
name: "test".into(),
name: "Test".into(),
},
watered: false,
};
let obstacles = vec![
(2, 0, obstacle.clone()),