feat(simulator): Implement ClearRally building action handler and update processor logic for Minecraft turn-based simulator

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-05-01 18:52:57 -07:00
parent 54d3cae1b0
commit b89ff67315
2 changed files with 85 additions and 0 deletions

View file

@ -43,3 +43,87 @@ pub fn drain_pending_building_actions(state: &mut GameState) {
let _ = invoke(state, &req); let _ = invoke(state, &req);
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::game_state::{BuildingRallyPoint, PlayerState, RallyCommand};
fn state_with_rally(building_id: &str) -> GameState {
let mut player = PlayerState::default();
player.rally_points.push(BuildingRallyPoint {
city_index: 0,
building_id: building_id.to_string(),
hex: (3, 5),
command: RallyCommand::Defend,
});
let mut state = GameState::default();
state.players.push(player);
state
}
#[test]
fn clear_rally_removes_matching_entry() {
let mut state = state_with_rally("barracks");
assert_eq!(state.players[0].rally_points.len(), 1);
state.pending_building_actions.push(BuildingActionRequest {
player_idx: 0,
city_idx: 0,
building_id: "barracks".to_string(),
kind: BuildingActionKind::ClearRally,
});
drain_pending_building_actions(&mut state);
assert!(state.pending_building_actions.is_empty(), "queue drained");
assert!(state.players[0].rally_points.is_empty(), "rally point removed");
}
#[test]
fn clear_rally_leaves_other_buildings_untouched() {
let mut state = state_with_rally("barracks");
state.players[0].rally_points.push(BuildingRallyPoint {
city_index: 0,
building_id: "watchtower".to_string(),
hex: (1, 2),
command: RallyCommand::Defend,
});
assert_eq!(state.players[0].rally_points.len(), 2);
state.pending_building_actions.push(BuildingActionRequest {
player_idx: 0,
city_idx: 0,
building_id: "barracks".to_string(),
kind: BuildingActionKind::ClearRally,
});
drain_pending_building_actions(&mut state);
assert_eq!(state.players[0].rally_points.len(), 1);
assert_eq!(state.players[0].rally_points[0].building_id, "watchtower");
}
#[test]
fn stubbed_actions_return_not_yet_implemented() {
let mut state = GameState::default();
state.players.push(PlayerState::default());
let req = BuildingActionRequest {
player_idx: 0,
city_idx: 0,
building_id: "barracks".to_string(),
kind: BuildingActionKind::GarrisonIn,
};
assert_eq!(invoke(&mut state, &req), Err(BuildingActionError::NotYetImplemented));
}
#[test]
fn out_of_range_player_returns_error() {
let mut state = GameState::default(); // no players
let req = BuildingActionRequest {
player_idx: 0,
city_idx: 0,
building_id: "barracks".to_string(),
kind: BuildingActionKind::ClearRally,
};
assert_eq!(invoke(&mut state, &req), Err(BuildingActionError::PlayerOutOfRange));
}
}

View file

@ -307,6 +307,7 @@ impl TurnProcessor {
// Phase 4f: drain formation requests from GDScript (rally, command, shape, split, auto-join). // Phase 4f: drain formation requests from GDScript (rally, command, shape, split, auto-join).
Self::drain_formation_requests(state); Self::drain_formation_requests(state);
crate::building_action_handlers::drain_pending_building_actions(state);
// Phase 4g: recompute formation membership from adjacency. // Phase 4g: recompute formation membership from adjacency.
Self::aggregate_formations(state); Self::aggregate_formations(state);