feat(mc-turn): Implement PvP combat resolution logic in the processor module

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-04-10 09:26:26 -07:00
parent f434f4283f
commit 3b3ee4dda0

View file

@ -35,11 +35,11 @@ use std::collections::HashMap;
/// Default seek range for non-rusher profiles. Units will move toward enemies
/// within this many hexes when no lair target is closer.
const DEFAULT_SEEK_RANGE: i32 = 5;
const DEFAULT_SEEK_RANGE: i32 = 40;
/// Extended seek range for rusher profiles. Rushers aggressively seek out
/// enemy units and cities from farther away.
const RUSHER_SEEK_RANGE: i32 = 8;
const RUSHER_SEEK_RANGE: i32 = 80;
// ── Config ──────────────────────────────────────────────────────────────────
@ -811,10 +811,10 @@ impl TurnProcessor {
for (ui, uc, ur, a_hp, a_atk, a_def) in &attacker_snaps {
if killed.iter().any(|&(p, u)| p == pi && u == *ui) { continue; }
// Check all other players for a defender on this tile.
// Check all other players for a defender on or adjacent to this tile.
for di in 0..n {
if di == pi { continue; }
if let Some(def_idx) = find_enemy_at(&state.players[di].units, *uc, *ur) {
if let Some(def_idx) = find_enemy_nearby(&state.players[di].units, *uc, *ur) {
if killed.iter().any(|&(p, u)| p == di && u == def_idx) { continue; }
let defender = &state.players[di].units[def_idx];
@ -1060,6 +1060,22 @@ fn find_enemy_at(units: &[MapUnit], col: i32, row: i32) -> Option<usize> {
units.iter().position(|u| u.col == col && u.row == row)
}
/// Find the nearest enemy unit on or adjacent to a tile (within 1 hex).
/// Same-tile matches are preferred; if none, the closest adjacent enemy is
/// returned. This implements the Civ5 "melee attack adjacent" mechanic.
fn find_enemy_nearby(units: &[MapUnit], col: i32, row: i32) -> Option<usize> {
// Prefer same-tile first.
if let Some(idx) = find_enemy_at(units, col, row) {
return Some(idx);
}
// Check adjacent tiles (distance <= 1).
units.iter()
.enumerate()
.filter(|(_, u)| hex_distance(u.col, u.row, col, row) <= 1)
.min_by_key(|(_, u)| hex_distance(u.col, u.row, col, row))
.map(|(i, _)| i)
}
// ── Terrain helpers ─────────────────────────────────────────────────────
/// Movement cost for a tile based on its biome. Mountains are impassable