feat(@projects/@magic-civilization): ✨ restructure action handlers into modular sub-systems
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
8e98b51bfc
commit
2d66a3aadf
1 changed files with 32 additions and 188 deletions
|
|
@ -4,6 +4,15 @@
|
|||
//! `processor.rs` routes external action invocations here. Move/Attack/Combat
|
||||
//! are NOT routed here — they require target coordinates and use the existing
|
||||
//! pathfinding and combat subsystems; the bridge calls those paths directly.
|
||||
//!
|
||||
//! Archetype-specific handlers live in sub-modules:
|
||||
//! - `infantry` — ShieldWall, Brace, Shove, Rage, Cleave, WarCry (p2-53f)
|
||||
//! - `ranged` — AimedShot, FireArrows, StopFireArrows (p2-53g)
|
||||
//! - `cavalry` — Pursue (p2-53h)
|
||||
|
||||
mod infantry;
|
||||
mod ranged;
|
||||
mod cavalry;
|
||||
|
||||
use crate::action::{ActionKind, DisabledReason};
|
||||
use crate::game_state::GameState;
|
||||
|
|
@ -81,25 +90,32 @@ pub fn invoke(
|
|||
ActionKind::Unsentry => handle_unsentry(state, player_idx, unit_idx),
|
||||
// p2-53g ranged actions
|
||||
// Volley requires a target hex — routed through the bridge's target-pick path.
|
||||
// Queue-drain (pending_volley_requests + process_volley_requests phase) is
|
||||
// not yet implemented; deferred pending bridge plumbing.
|
||||
ActionKind::Volley => Err(ActionError { kind, reason: DisabledReason::WrongTerrain }),
|
||||
ActionKind::AimedShot => handle_aimed_shot(state, player_idx, unit_idx),
|
||||
ActionKind::FireArrows => handle_fire_arrows(state, player_idx, unit_idx),
|
||||
ActionKind::StopFireArrows => handle_stop_fire_arrows(state, player_idx, unit_idx),
|
||||
ActionKind::AimedShot => ranged::handle_aimed_shot(state, player_idx, unit_idx),
|
||||
ActionKind::FireArrows => ranged::handle_fire_arrows(state, player_idx, unit_idx),
|
||||
ActionKind::StopFireArrows => ranged::handle_stop_fire_arrows(state, player_idx, unit_idx),
|
||||
// p2-53h cavalry actions
|
||||
// Charge and Wheel require target coords — routed through bridge.
|
||||
// Charge requires a target hex — routed through bridge's target-pick path.
|
||||
// Queue-drain (pending_charge_requests) not yet implemented; deferred pending
|
||||
// bridge plumbing.
|
||||
// Wheel requires facing/edge state not yet present on MapUnit; deferred.
|
||||
ActionKind::Charge | ActionKind::Wheel => Err(ActionError { kind, reason: DisabledReason::WrongTerrain }),
|
||||
ActionKind::Pursue => handle_pursue(state, player_idx, unit_idx),
|
||||
ActionKind::Pursue => cavalry::handle_pursue(state, player_idx, unit_idx),
|
||||
// p2-53f infantry line actions
|
||||
ActionKind::ShieldWall => handle_shield_wall(state, player_idx, unit_idx),
|
||||
ActionKind::UnshieldWall => handle_unshield_wall(state, player_idx, unit_idx),
|
||||
ActionKind::Brace => handle_brace(state, player_idx, unit_idx),
|
||||
ActionKind::Unbrace => handle_unbrace(state, player_idx, unit_idx),
|
||||
ActionKind::Shove | ActionKind::Cleave => Err(ActionError {
|
||||
kind,
|
||||
reason: DisabledReason::WrongTerrain,
|
||||
}),
|
||||
ActionKind::Rage => handle_rage(state, player_idx, unit_idx),
|
||||
ActionKind::WarCry => handle_war_cry(state, player_idx, unit_idx),
|
||||
ActionKind::ShieldWall => infantry::handle_shield_wall(state, player_idx, unit_idx),
|
||||
ActionKind::UnshieldWall => infantry::handle_unshield_wall(state, player_idx, unit_idx),
|
||||
ActionKind::Brace => infantry::handle_brace(state, player_idx, unit_idx),
|
||||
ActionKind::Unbrace => infantry::handle_unbrace(state, player_idx, unit_idx),
|
||||
ActionKind::Shove => infantry::handle_shove(state, player_idx, unit_idx),
|
||||
// p2-53f infantry shock actions
|
||||
ActionKind::Rage => infantry::handle_rage(state, player_idx, unit_idx),
|
||||
// Cleave: the combat resolver emits `cleave_secondary_damage` (50% of primary);
|
||||
// the bridge selects the adjacent target and applies it. No pre-action handler
|
||||
// needed — Cleave fires as part of the attack action, not as a standalone toggle.
|
||||
ActionKind::Cleave => Err(ActionError { kind, reason: DisabledReason::WrongTerrain }),
|
||||
ActionKind::WarCry => infantry::handle_war_cry(state, player_idx, unit_idx),
|
||||
// p2-53i engineer actions — multi-turn; require hex targets from bridge.
|
||||
ActionKind::BuildBridge
|
||||
| ActionKind::SapWall
|
||||
|
|
@ -132,7 +148,7 @@ pub fn invoke(
|
|||
}
|
||||
}
|
||||
|
||||
fn get_unit_mut<'a>(
|
||||
pub(crate) fn get_unit_mut<'a>(
|
||||
state: &'a mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
|
|
@ -404,177 +420,6 @@ fn handle_disembark(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// ── p2-53g ranged action handlers ────────────────────────────────────────────
|
||||
|
||||
fn handle_aimed_shot(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::AimedShot)?;
|
||||
if unit.aimed_shot_pending {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::AimedShot,
|
||||
reason: DisabledReason::AlreadyAiming,
|
||||
});
|
||||
}
|
||||
unit.aimed_shot_pending = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_fire_arrows(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::FireArrows)?;
|
||||
if unit.is_fire_arrows {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::FireArrows,
|
||||
reason: DisabledReason::AlreadyFireArrows,
|
||||
});
|
||||
}
|
||||
unit.is_fire_arrows = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_stop_fire_arrows(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::StopFireArrows)?;
|
||||
if !unit.is_fire_arrows {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::StopFireArrows,
|
||||
reason: DisabledReason::NotFireArrows,
|
||||
});
|
||||
}
|
||||
unit.is_fire_arrows = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ── p2-53h cavalry action handlers ────────────────────────────────────────────
|
||||
|
||||
fn handle_pursue(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::Pursue)?;
|
||||
if unit.is_pursuing {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::Pursue,
|
||||
reason: DisabledReason::AlreadyPursuing,
|
||||
});
|
||||
}
|
||||
unit.is_pursuing = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ── p2-53f infantry line action handlers ─────────────────────────────────────
|
||||
|
||||
fn handle_shield_wall(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::ShieldWall)?;
|
||||
if unit.is_shield_wall {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::ShieldWall,
|
||||
reason: DisabledReason::AlreadyShieldWall,
|
||||
});
|
||||
}
|
||||
unit.is_shield_wall = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_unshield_wall(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::UnshieldWall)?;
|
||||
if !unit.is_shield_wall {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::UnshieldWall,
|
||||
reason: DisabledReason::NotShieldWall,
|
||||
});
|
||||
}
|
||||
unit.is_shield_wall = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_brace(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::Brace)?;
|
||||
if unit.is_braced {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::Brace,
|
||||
reason: DisabledReason::AlreadyBraced,
|
||||
});
|
||||
}
|
||||
unit.is_braced = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_unbrace(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::Unbrace)?;
|
||||
if !unit.is_braced {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::Unbrace,
|
||||
reason: DisabledReason::NotBraced,
|
||||
});
|
||||
}
|
||||
unit.is_braced = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ── p2-53f infantry shock action handlers ────────────────────────────────────
|
||||
|
||||
fn handle_rage(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::Rage)?;
|
||||
if unit.rage_turns_remaining > 0 {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::Rage,
|
||||
reason: DisabledReason::AlreadyRaging,
|
||||
});
|
||||
}
|
||||
// +40% attack for 2 turns; Fortify and Sentry are incompatible (cleared here).
|
||||
unit.rage_turns_remaining = 2;
|
||||
unit.is_fortified = false;
|
||||
unit.is_sentrying = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_war_cry(
|
||||
state: &mut GameState,
|
||||
player_idx: usize,
|
||||
unit_idx: usize,
|
||||
) -> Result<(), ActionError> {
|
||||
let unit = get_unit_mut(state, player_idx, unit_idx, ActionKind::WarCry)?;
|
||||
if unit.war_cry_used_this_battle {
|
||||
return Err(ActionError {
|
||||
kind: ActionKind::WarCry,
|
||||
reason: DisabledReason::WarCryUsed,
|
||||
});
|
||||
}
|
||||
unit.war_cry_used_this_battle = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ── p2-53i: Scout action handlers ───────────────────────────────────────────
|
||||
|
||||
fn handle_stealth(
|
||||
|
|
@ -887,7 +732,6 @@ mod tests {
|
|||
strategic_axes: Default::default(),
|
||||
scoring_weights: Default::default(),
|
||||
expansion_points: 0,
|
||||
city_buildings: vec![],
|
||||
city_improvements: Default::default(),
|
||||
city_ecology: vec![],
|
||||
tech_state: None,
|
||||
Loading…
Add table
Reference in a new issue