refactor(@projects/@magic-civilization): 🧹 p3-18 — remove dead Civ-V explicit-embark model (single source)
With Civ-VI auto-embark live (move onto water → embarked), the half-built Civ-V explicit path was a redundant second model — and dead besides (gated on the `amphibious` keyword, which no unit carries). Per owner direction (unify on one model, no two definitions), it is removed: - mc-core: drop ActionKind::Embark/Disembark (+ idx/as_str/from_str), the four embark DisabledReasons, the amphibious Embark/Disembark action-gen block, and the now-dead UnitCapability is_embarked/adjacent_water/adjacent_land fields (+ all fixtures and the amphibious action tests). - mc-turn: drop handle_embark/handle_disembark + their dispatch arms + tests. - mc-ai: drop the dead capability fields from the action-query construction. - mc-state: MapUnit::is_embarked doc now describes auto-embark (its sole writer is process_one_move). - api-gdext: the legal_actions_for/can_invoke FFI embark inputs are now vestigial (no longer affect legality); kept for ABI stability + flagged for the P5 GDScript-mirror trim. The only embark model is now the data-driven auto-embark (P1b/P2/P3). Tests: mc-core 263, mc-ai 287, mc-turn 244 green (−4 = the deleted explicit-embark tests). gdextension-api full compile deferred to the build host (local cargo resolver panics on a pre-existing thiserror/wasm feature-unification issue, unrelated to this change — no Cargo.toml touched). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
efff773c97
commit
dabcc6716a
5 changed files with 25 additions and 265 deletions
|
|
@ -6,6 +6,12 @@
|
|||
//! posture)` →
|
||||
//! `Array[Dictionary]` with `{kind, enabled, disabled_reason}` entries
|
||||
//!
|
||||
//! NOTE (p3-18): `is_embarked` / `adjacent_water` / `adjacent_land` are
|
||||
//! vestigial — embarkation is now automatic (Civ-VI: move onto water →
|
||||
//! embarked) with no explicit Embark/Disembark action, so these inputs no
|
||||
//! longer affect legality. They remain in the signature for ABI stability and
|
||||
//! are slated for removal in the P5 GDScript-mirror pass.
|
||||
//!
|
||||
//! `posture` is a Dictionary carrying archetype state fields (all optional,
|
||||
//! defaulting to false/0 if absent):
|
||||
//! { is_aiming, is_fire_arrows, is_pursuing, is_shield_wall, is_braced,
|
||||
|
|
@ -60,6 +66,11 @@ impl GdUnitActions {
|
|||
adjacent_land: bool,
|
||||
posture: Dictionary,
|
||||
) -> Array<Dictionary> {
|
||||
// p3-18 — embarkation is auto (Civ-VI): there is no explicit Embark
|
||||
// action, so these legacy embark inputs no longer affect action
|
||||
// legality. Accepted for ABI stability; the bridge + GDScript callers
|
||||
// shed them in the p3-18 P5 GDScript-mirror pass.
|
||||
let _ = (is_embarked, adjacent_water, adjacent_land);
|
||||
let cap = UnitCapability {
|
||||
unit_type: unit_type.to_string(),
|
||||
keywords: keywords
|
||||
|
|
@ -73,9 +84,6 @@ impl GdUnitActions {
|
|||
is_patrolling: false,
|
||||
is_sentrying,
|
||||
is_deployed,
|
||||
is_embarked,
|
||||
adjacent_water,
|
||||
adjacent_land,
|
||||
is_aiming: bool_from_dict(&posture, "is_aiming"),
|
||||
is_fire_arrows: bool_from_dict(&posture, "is_fire_arrows"),
|
||||
is_pursuing: bool_from_dict(&posture, "is_pursuing"),
|
||||
|
|
@ -124,6 +132,9 @@ impl GdUnitActions {
|
|||
let Some(action_kind) = ActionKind::from_str(&kind_str) else {
|
||||
return false;
|
||||
};
|
||||
// p3-18 — auto-embark: legacy embark inputs no longer affect legality
|
||||
// (see legal_actions_for). Kept for ABI stability; trimmed in P5.
|
||||
let _ = (is_embarked, adjacent_water, adjacent_land);
|
||||
let cap = UnitCapability {
|
||||
unit_type: unit_type.to_string(),
|
||||
keywords: keywords
|
||||
|
|
@ -137,9 +148,6 @@ impl GdUnitActions {
|
|||
is_patrolling: false,
|
||||
is_sentrying,
|
||||
is_deployed,
|
||||
is_embarked,
|
||||
adjacent_water,
|
||||
adjacent_land,
|
||||
is_aiming: bool_from_dict(&posture, "is_aiming"),
|
||||
is_fire_arrows: bool_from_dict(&posture, "is_fire_arrows"),
|
||||
is_pursuing: bool_from_dict(&posture, "is_pursuing"),
|
||||
|
|
|
|||
|
|
@ -235,9 +235,6 @@ fn non_motion_macro(unit: &TacticalUnit) -> Option<Action> {
|
|||
is_patrolling: unit.patrol_order.is_some(),
|
||||
is_sentrying: false,
|
||||
is_deployed: unit.is_deployed,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ pub enum ActionKind {
|
|||
PackSiege,
|
||||
DeploySiege,
|
||||
Bombard,
|
||||
Embark,
|
||||
Disembark,
|
||||
// Formation actions — parameters live in request structs queued on GameState.
|
||||
SetRallyPoint,
|
||||
ClearRallyPoint,
|
||||
|
|
@ -142,8 +140,6 @@ impl ActionKind {
|
|||
ActionKind::IssuePatrol => 14,
|
||||
ActionKind::CancelPatrol => 15,
|
||||
ActionKind::EditPatrol => 16,
|
||||
ActionKind::Embark => 12,
|
||||
ActionKind::Disembark => 13,
|
||||
ActionKind::SetRallyPoint => 17,
|
||||
ActionKind::ClearRallyPoint => 18,
|
||||
ActionKind::CommandFormation => 19,
|
||||
|
|
@ -216,8 +212,6 @@ impl ActionKind {
|
|||
ActionKind::IssuePatrol => "issue_patrol",
|
||||
ActionKind::CancelPatrol => "cancel_patrol",
|
||||
ActionKind::EditPatrol => "edit_patrol",
|
||||
ActionKind::Embark => "embark",
|
||||
ActionKind::Disembark => "disembark",
|
||||
ActionKind::SetRallyPoint => "set_rally_point",
|
||||
ActionKind::ClearRallyPoint => "clear_rally_point",
|
||||
ActionKind::CommandFormation => "command_formation",
|
||||
|
|
@ -279,8 +273,6 @@ impl ActionKind {
|
|||
"issue_patrol" => Some(ActionKind::IssuePatrol),
|
||||
"cancel_patrol" => Some(ActionKind::CancelPatrol),
|
||||
"edit_patrol" => Some(ActionKind::EditPatrol),
|
||||
"embark" => Some(ActionKind::Embark),
|
||||
"disembark" => Some(ActionKind::Disembark),
|
||||
"set_rally_point" => Some(ActionKind::SetRallyPoint),
|
||||
"clear_rally_point" => Some(ActionKind::ClearRallyPoint),
|
||||
"command_formation" => Some(ActionKind::CommandFormation),
|
||||
|
|
@ -346,10 +338,6 @@ pub enum DisabledReason {
|
|||
NotSentrying,
|
||||
AlreadyDeployed,
|
||||
NotDeployed,
|
||||
AlreadyEmbarked,
|
||||
NotEmbarked,
|
||||
NoAdjacentWater,
|
||||
NoAdjacentLand,
|
||||
// p2-53g
|
||||
NotRanged,
|
||||
AlreadyAiming,
|
||||
|
|
@ -410,10 +398,6 @@ impl DisabledReason {
|
|||
DisabledReason::NotSentrying => "disabled_reason_not_sentrying",
|
||||
DisabledReason::AlreadyDeployed => "disabled_reason_already_deployed",
|
||||
DisabledReason::NotDeployed => "disabled_reason_not_deployed",
|
||||
DisabledReason::AlreadyEmbarked => "disabled_reason_already_embarked",
|
||||
DisabledReason::NotEmbarked => "disabled_reason_not_embarked",
|
||||
DisabledReason::NoAdjacentWater => "disabled_reason_no_adjacent_water",
|
||||
DisabledReason::NoAdjacentLand => "disabled_reason_no_adjacent_land",
|
||||
DisabledReason::NotRanged => "disabled_reason_not_ranged",
|
||||
DisabledReason::AlreadyAiming => "disabled_reason_already_aiming",
|
||||
DisabledReason::AlreadyFireArrows => "disabled_reason_already_fire_arrows",
|
||||
|
|
@ -497,14 +481,6 @@ pub struct UnitCapability {
|
|||
pub is_sentrying: bool,
|
||||
/// True if a siege unit is in deployed posture (cannot move, can Bombard).
|
||||
pub is_deployed: bool,
|
||||
/// True if an amphibious unit is currently on a water tile.
|
||||
pub is_embarked: bool,
|
||||
/// True if the unit's current tile has at least one adjacent water hex.
|
||||
/// Used by the Embark gate; supplied by the bridge from grid lookup.
|
||||
pub adjacent_water: bool,
|
||||
/// True if the unit's current tile has at least one adjacent land hex.
|
||||
/// Used by the Disembark gate; supplied by the bridge from grid lookup.
|
||||
pub adjacent_land: bool,
|
||||
// p2-53g ranged posture state
|
||||
/// True if the unit is currently aiming (AimedShot pending — fires on next attack).
|
||||
pub is_aiming: bool,
|
||||
|
|
@ -732,28 +708,9 @@ pub fn legal_actions(capability: &UnitCapability) -> Vec<ActionAvailability> {
|
|||
});
|
||||
}
|
||||
|
||||
// Amphibious — embark/disembark for shore-crossing units
|
||||
if capability.keywords.iter().any(|k| k == "amphibious") {
|
||||
let is_embarked = capability.is_embarked;
|
||||
|
||||
// Embark: requires !embarked + adjacent water
|
||||
out.push(if is_embarked {
|
||||
ActionAvailability::disabled(ActionKind::Embark, DisabledReason::AlreadyEmbarked)
|
||||
} else if capability.adjacent_water {
|
||||
ActionAvailability::enabled(ActionKind::Embark)
|
||||
} else {
|
||||
ActionAvailability::disabled(ActionKind::Embark, DisabledReason::NoAdjacentWater)
|
||||
});
|
||||
|
||||
// Disembark: requires embarked + adjacent land
|
||||
out.push(if !is_embarked {
|
||||
ActionAvailability::disabled(ActionKind::Disembark, DisabledReason::NotEmbarked)
|
||||
} else if capability.adjacent_land {
|
||||
ActionAvailability::enabled(ActionKind::Disembark)
|
||||
} else {
|
||||
ActionAvailability::disabled(ActionKind::Disembark, DisabledReason::NoAdjacentLand)
|
||||
});
|
||||
}
|
||||
// p3-18 — embarkation is auto (Civ-VI): a land unit that moves onto water
|
||||
// embarks, onto land disembarks (see mc-turn process_one_move). There is no
|
||||
// explicit Embark/Disembark action, so none is generated here.
|
||||
|
||||
// p2-53g: ranged-specific actions (gate: "ranged" keyword)
|
||||
let is_ranged_unit = capability.keywords.iter().any(|k| k == "ranged");
|
||||
|
|
@ -1070,9 +1027,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1097,9 +1051,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1162,9 +1113,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1232,9 +1180,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: true,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1331,36 +1276,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
is_shield_wall: false,
|
||||
is_braced: false,
|
||||
rage_turns_remaining: 0,
|
||||
war_cry_used_this_battle: false,
|
||||
multi_turn_in_progress: false,
|
||||
is_stealthed: false,
|
||||
is_field_aura: false,
|
||||
is_ambushing: false,
|
||||
is_escorted: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn amphibious_cap(is_embarked: bool, adjacent_water: bool, adjacent_land: bool) -> UnitCapability {
|
||||
UnitCapability {
|
||||
unit_type: "melee".into(),
|
||||
keywords: vec!["amphibious".into()],
|
||||
has_movement: true,
|
||||
is_fortified: false,
|
||||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked,
|
||||
adjacent_water,
|
||||
adjacent_land,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1415,41 +1330,6 @@ mod tests {
|
|||
assert_eq!(attack.disabled_reason, Some(DisabledReason::AlreadyDeployed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amphibious_on_land_with_adjacent_water_can_embark() {
|
||||
let actions = legal_actions(&hibious_cap(false, true, false));
|
||||
let embark = actions.iter().find(|a| a.kind == ActionKind::Embark).unwrap();
|
||||
assert!(embark.enabled, "amphibious on land near water can embark");
|
||||
let disembark = actions.iter().find(|a| a.kind == ActionKind::Disembark).unwrap();
|
||||
assert!(!disembark.enabled, "not embarked means cannot disembark");
|
||||
assert_eq!(disembark.disabled_reason, Some(DisabledReason::NotEmbarked));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amphibious_on_land_no_adjacent_water_cannot_embark() {
|
||||
let actions = legal_actions(&hibious_cap(false, false, false));
|
||||
let embark = actions.iter().find(|a| a.kind == ActionKind::Embark).unwrap();
|
||||
assert!(!embark.enabled);
|
||||
assert_eq!(embark.disabled_reason, Some(DisabledReason::NoAdjacentWater));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amphibious_embarked_with_adjacent_land_can_disembark() {
|
||||
let actions = legal_actions(&hibious_cap(true, false, true));
|
||||
let disembark = actions.iter().find(|a| a.kind == ActionKind::Disembark).unwrap();
|
||||
assert!(disembark.enabled, "embarked unit next to land can disembark");
|
||||
let embark = actions.iter().find(|a| a.kind == ActionKind::Embark).unwrap();
|
||||
assert!(!embark.enabled, "already embarked");
|
||||
assert_eq!(embark.disabled_reason, Some(DisabledReason::AlreadyEmbarked));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn amphibious_embarked_no_adjacent_land_cannot_disembark() {
|
||||
let actions = legal_actions(&hibious_cap(true, false, false));
|
||||
let disembark = actions.iter().find(|a| a.kind == ActionKind::Disembark).unwrap();
|
||||
assert!(!disembark.enabled);
|
||||
assert_eq!(disembark.disabled_reason, Some(DisabledReason::NoAdjacentLand));
|
||||
}
|
||||
// p2-53g: ranged-specific action tests
|
||||
|
||||
fn ranged_cap_base(has_movement: bool) -> UnitCapability {
|
||||
|
|
@ -1461,9 +1341,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1529,9 +1406,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing,
|
||||
|
|
@ -1592,9 +1466,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1619,9 +1490,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1740,9 +1608,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1767,9 +1632,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
@ -1794,9 +1656,6 @@ mod tests {
|
|||
is_patrolling: false,
|
||||
is_sentrying: false,
|
||||
is_deployed: false,
|
||||
is_embarked: false,
|
||||
adjacent_water: false,
|
||||
adjacent_land: false,
|
||||
is_aiming: false,
|
||||
is_fire_arrows: false,
|
||||
is_pursuing: false,
|
||||
|
|
|
|||
|
|
@ -1163,8 +1163,11 @@ pub struct MapUnit {
|
|||
/// `handle_deploy_siege`, cleared by `handle_pack_siege`.
|
||||
#[serde(default)]
|
||||
pub is_deployed: bool,
|
||||
/// Amphibious unit is currently on a water tile (embarked). Defence -50%;
|
||||
/// cannot fortify. Set by `handle_embark`, cleared by `handle_disembark`.
|
||||
/// Land unit currently embarked on a water tile (p3-18, Civ-VI auto-embark).
|
||||
/// Defence is halved in combat (`mc_combat::embarked_defence_penalty`). Set
|
||||
/// automatically by `mc-turn::process_one_move` from the destination tile —
|
||||
/// moving onto water embarks, onto land disembarks; there is no explicit
|
||||
/// embark action.
|
||||
#[serde(default)]
|
||||
pub is_embarked: bool,
|
||||
/// Rally command to execute when this unit first reaches its rally hex.
|
||||
|
|
|
|||
|
|
@ -75,8 +75,6 @@ pub fn invoke(
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
ActionKind::Embark => handle_embark(state, player_idx, unit_idx),
|
||||
ActionKind::Disembark => handle_disembark(state, player_idx, unit_idx),
|
||||
// Formation commands: handled via GameState queues, not the unit-action dispatch path.
|
||||
ActionKind::SetRallyPoint
|
||||
| ActionKind::ClearRallyPoint
|
||||
|
|
@ -473,39 +471,9 @@ fn handle_pack_siege(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_embark(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::Embark)?;
|
||||
if unit.is_embarked {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::Embark,
|
||||
reason: DisabledReason::AlreadyEmbarked,
|
||||
});
|
||||
}
|
||||
unit.is_embarked = true;
|
||||
// Cannot fortify while embarked; clear any existing fortify.
|
||||
unit.is_fortified = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_disembark(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::Disembark)?;
|
||||
if !unit.is_embarked {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::Disembark,
|
||||
reason: DisabledReason::NotEmbarked,
|
||||
});
|
||||
}
|
||||
unit.is_embarked = false;
|
||||
Ok(())
|
||||
}
|
||||
// p3-18 — embarkation is auto (Civ-VI): mc-turn `process_one_move` toggles
|
||||
// `MapUnit::is_embarked` from the destination tile. There is no explicit
|
||||
// Embark/Disembark action handler.
|
||||
|
||||
// ── p2-53i: Scout action handlers ───────────────────────────────────────────
|
||||
|
||||
|
|
@ -852,79 +820,4 @@ mod tests {
|
|||
assert_eq!(err.reason, DisabledReason::SiegeMustDeployFirst);
|
||||
}
|
||||
|
||||
fn state_with_amphibious_unit(is_embarked: bool) -> (GameState, usize, usize) {
|
||||
let unit = MapUnit {
|
||||
col: 0,
|
||||
row: 0,
|
||||
hp: 30,
|
||||
max_hp: 30,
|
||||
attack: 6,
|
||||
defense: 3,
|
||||
is_fortified: false,
|
||||
is_embarked,
|
||||
unit_id: "shore_guard".into(),
|
||||
held_resources: vec![],
|
||||
patrol_order: None,
|
||||
..Default::default()
|
||||
};
|
||||
let player = PlayerState {
|
||||
player_index: 0,
|
||||
gold: 0,
|
||||
cities: vec![],
|
||||
unit_upkeep: vec![1],
|
||||
strategic_axes: Default::default(),
|
||||
scoring_weights: Default::default(),
|
||||
expansion_points: 0,
|
||||
city_improvements: Default::default(),
|
||||
city_ecology: vec![],
|
||||
tech_state: None,
|
||||
science_pool: 0,
|
||||
player_tech: None,
|
||||
science_yield: 0,
|
||||
units: vec![unit],
|
||||
city_positions: vec![],
|
||||
capital_position: None,
|
||||
culture_total: 0,
|
||||
culture_pool: Default::default(),
|
||||
arcane_lore_pop_deducted: false,
|
||||
traded_luxuries: Default::default(),
|
||||
relations: Default::default(),
|
||||
strategic_ledger: Default::default(),
|
||||
wonders_built: Default::default(),
|
||||
explored_deposits: Default::default(),
|
||||
..Default::default()
|
||||
};
|
||||
let state = GameState { turn: 0, players: vec![player], grid: None, ..Default::default() };
|
||||
(state, 0, 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn embark_sets_flag_and_clears_fortify() {
|
||||
let (mut state, pi, ui) = state_with_amphibious_unit(false);
|
||||
state.players[pi].units[ui].is_fortified = true;
|
||||
invoke(&mut state, pi, ui, ActionKind::Embark).unwrap();
|
||||
assert!(state.players[pi].units[ui].is_embarked);
|
||||
assert!(!state.players[pi].units[ui].is_fortified, "embarked unit cannot be fortified");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn embark_when_already_embarked_errors() {
|
||||
let (mut state, pi, ui) = state_with_amphibious_unit(true);
|
||||
let err = invoke(&mut state, pi, ui, ActionKind::Embark).unwrap_err();
|
||||
assert_eq!(err.reason, DisabledReason::AlreadyEmbarked);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disembark_clears_flag() {
|
||||
let (mut state, pi, ui) = state_with_amphibious_unit(true);
|
||||
invoke(&mut state, pi, ui, ActionKind::Disembark).unwrap();
|
||||
assert!(!state.players[pi].units[ui].is_embarked);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disembark_when_not_embarked_errors() {
|
||||
let (mut state, pi, ui) = state_with_amphibious_unit(false);
|
||||
let err = invoke(&mut state, pi, ui, ActionKind::Disembark).unwrap_err();
|
||||
assert_eq!(err.reason, DisabledReason::NotEmbarked);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue