feat(@projects/@magic-civilization): ✨ add city food growth test via turn processor
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
8ccdea62fd
commit
560f99484b
1 changed files with 99 additions and 0 deletions
|
|
@ -1286,6 +1286,105 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
/// p2-67 Phase 11. `apply_end_turn` now runs `TurnProcessor::step`
|
||||
/// between the AI driver loop and the closing `TurnStarted` emit.
|
||||
/// Step accumulates food into `CityState.food_stored`; once the
|
||||
/// threshold is crossed, `population` grows. This test seeds a city
|
||||
/// with food_yield = 12 (well above the pop-1 maintenance of 2) so
|
||||
/// the food_stored crosses the threshold in 2 turns and population
|
||||
/// rises from 1 → 2.
|
||||
#[test]
|
||||
fn end_turn_ticks_city_food_growth_via_turn_processor() {
|
||||
use mc_city::CityState;
|
||||
let mut state = GameState::default();
|
||||
let mut ps = PlayerState::default();
|
||||
ps.player_index = 0;
|
||||
let mut city = CityState::starter();
|
||||
// Starter has population=1, food_stored=0, food_yield=4 (net +2 / turn).
|
||||
// Threshold at pop=1 is `15 + 6*0 + 0 = 15`. With a juiced food_yield
|
||||
// of 16 (net +14 / turn) we cross in 2 turns deterministically.
|
||||
city.food_yield = 16;
|
||||
ps.cities.push(city);
|
||||
ps.city_positions.push((0, 0));
|
||||
ps.city_buildings.push(Vec::new());
|
||||
ps.city_improvements.push(Vec::new());
|
||||
ps.city_ecology.push(Default::default());
|
||||
state.players.push(ps);
|
||||
// Turn 0 → state.turn becomes 1, food_stored = 14, population still 1.
|
||||
let _ = apply_action(&mut state, 0, &PlayerAction::EndTurn).unwrap();
|
||||
assert_eq!(state.turn, 1);
|
||||
assert_eq!(state.players[0].cities[0].population, 1);
|
||||
assert_eq!(state.players[0].cities[0].food_stored, 14);
|
||||
// Turn 1 → food_stored crosses 15, pop grows to 2.
|
||||
let _ = apply_action(&mut state, 0, &PlayerAction::EndTurn).unwrap();
|
||||
assert_eq!(state.turn, 2);
|
||||
assert_eq!(
|
||||
state.players[0].cities[0].population, 2,
|
||||
"city should have grown to population 2"
|
||||
);
|
||||
// food_stored after growth = 14 (pre-grow) + 14 (this turn's net) - 15 (threshold) = 13.
|
||||
// (The threshold check happens AFTER this turn's food is added but
|
||||
// BEFORE the maintenance is rebilled for the new pop — `process_city_production`
|
||||
// accumulates pre-grow.)
|
||||
assert_eq!(state.players[0].cities[0].food_stored, 13);
|
||||
}
|
||||
|
||||
/// p2-67 Phase 11. With a queue carrying a `Queueable::Unit` and
|
||||
/// enough `production_stored`, `try_spawn_unit` in `TurnProcessor::step`
|
||||
/// spawns a real unit at the city's hex. Asserts via state inspection
|
||||
/// (no `CityUnitCompleted` wire event exists today — the production
|
||||
/// path mutates `player.units` silently).
|
||||
#[test]
|
||||
fn end_turn_completes_queued_unit_via_turn_processor() {
|
||||
use mc_city::{CityState, Queueable};
|
||||
let mut state = GameState::default();
|
||||
let mut ps = PlayerState::default();
|
||||
ps.player_index = 0;
|
||||
let mut city = CityState::starter();
|
||||
// Pre-stuff production_stored above the spawn cost (4 by default
|
||||
// — see `LairCombatConfig::default`'s `unit_spawn_cost`). Anything
|
||||
// ≥ 4 lets `try_spawn_unit` trigger.
|
||||
city.production_stored = 100;
|
||||
city.queue = Some(Queueable::Unit {
|
||||
unit_id: "dwarf_warrior".into(),
|
||||
});
|
||||
city.queue_cost = Some(8);
|
||||
ps.cities.push(city);
|
||||
ps.city_positions.push((5, 5));
|
||||
ps.city_buildings.push(Vec::new());
|
||||
ps.city_improvements.push(Vec::new());
|
||||
ps.city_ecology.push(Default::default());
|
||||
state.players.push(ps);
|
||||
let unit_count_before = state.players[0].units.len();
|
||||
let _ = apply_action(&mut state, 0, &PlayerAction::EndTurn).unwrap();
|
||||
assert_eq!(state.turn, 1);
|
||||
assert!(
|
||||
state.players[0].units.len() > unit_count_before,
|
||||
"queued unit should have spawned (units before={}, after={})",
|
||||
unit_count_before,
|
||||
state.players[0].units.len()
|
||||
);
|
||||
}
|
||||
|
||||
/// p2-67 Phase 11. Unit movement_remaining refresh is now owned by
|
||||
/// `TurnProcessor::step` (DRY rule — the dispatch-level `refresh_units`
|
||||
/// call was deleted in this same patch). Asserts a fortified unit that
|
||||
/// exhausted its movement budget gets a fresh budget after EndTurn.
|
||||
#[test]
|
||||
fn end_turn_refreshes_unit_movement_via_turn_processor() {
|
||||
let mut state = make_state_with_units(vec![(0, 1, 3, 3)]);
|
||||
// make_state_with_units gives every unit `with_moves(32)` —
|
||||
// simulate a consumed budget.
|
||||
state.players[0].units[0].movement_remaining = 0;
|
||||
assert_eq!(state.players[0].units[0].base_moves, 32);
|
||||
let _ = apply_action(&mut state, 0, &PlayerAction::EndTurn).unwrap();
|
||||
assert_eq!(state.turn, 1);
|
||||
assert_eq!(
|
||||
state.players[0].units[0].movement_remaining, 32,
|
||||
"movement should refresh to base_moves after step"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noop_makes_no_state_change_and_no_events() {
|
||||
let mut state = GameState::default();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue