fix(@projects/@magic-civilization): 🐛 adjust siege AI garrison behavior

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-04-16 12:19:57 -07:00
parent 775b114537
commit a15153896d
3 changed files with 20 additions and 8 deletions

View file

@ -33,3 +33,4 @@
2026-04-16 11:40 Task #3 INSTRUMENTATION COMPLETE: c7da68a68 resource_gate_rejected event emitted from city.gd add_to_queue + mc-city QueueError::MissingResource + checklist-report.py updated (+4 lines). 7/14/13/4 line breakdown across files. (instrumentation-dev)
2026-04-16 11:34 Task #2 FAUNA (pivot): 664bf5570 city drift behavior — 35 lines wild_creature_ai.gd. Wilds step toward nearest player city with 0.2 probability when idle + no leash violation. Seed-stable RNG. Pending smoke verification.
2026-04-16 11:55 REGRESSION BATCH 2 (all changes landed): 12 PASS / 2 FAIL on 4X checklist. Seeds 1/2/3 outcomes: victory T124 / victory T189 / max_turns T300. pop_peak=32 (+12), combats=212 (+111), techs=29 (+10), tiles=59, 63 resource rejections, 2 loot_dropped, 90 improvements. FAIL: median TTV 156 (target 200-350, need +44) AND both-players-p5m4-T100 = 1/3 seeds (need 2). Very close to stop criterion. Seed 1 T124 is still too fast — remaining work is pushing seed 1 victory past T200.
2026-04-16 12:10 REGRESSION BATCH 3 (ttv-dev final siege tuning): 11 PASS / 3 FAIL. REGRESSED from batch 2 (12/2). Seeds 1/2/3: victory T75 / max_turns T300 / max_turns T300. Siege dampening went TOO far — seed 1 still fast capture (AI issue, not math), seeds 2+3 now stalemate (no captures in 300t). FAIL: victories 1/3 (33%, need 50-80%), median TTV 75, both-p5m4-T100 1/3. Root cause per ttv-dev: p1 garrison dies T69, doesn't rebuild. That's AI not combat. ACTION: revert melee_city_fraction 0.40→0.50 + spawn p1-defense-dev.

View file

@ -239,6 +239,18 @@ static func _count_own_military_at(
return total
static func _enemy_within(
pos: Vector2i, radius: int, own_idx: int
) -> bool:
var primary: Dictionary = GameState.get_primary_layer()
for u: Variant in primary.get("units", []):
if u == null or not u.is_alive() or int(u.get("owner")) == own_idx:
continue
if HexUtilsScript.hex_distance(pos, u.position) <= radius:
return true
return false
static func _enemy_military_threat(player: RefCounted) -> Dictionary:
## count=in-range(<=8), total_count=all enemy combat. threatens_city @<=5.
var count: int = 0

View file

@ -106,10 +106,10 @@ impl TileYield {
/// with two decent food tiles. Target: median p0_pop_peak ≥ 7 at T150.
pub const FOOD_PER_POP: f64 = 1.2;
/// Base city HP before population scaling. Final tune (200 → 260 → 300) to
/// stretch seed 1's T124 capital fall toward T200. Paired with 26 HP/turn
/// Base city HP before population scaling. Progression 200 → 260 → 280 to
/// stretch seed 1's T124 capital fall toward T200. Paired with 23 HP/turn
/// regen and the 0.40 melee-to-city damage fraction in resolver.rs.
pub const BASE_CITY_HP: u32 = 300;
pub const BASE_CITY_HP: u32 = 280;
/// HP gained per population point.
pub const HP_PER_POP: u32 = 10;
@ -513,13 +513,12 @@ impl City {
self.hp = (self.hp + amount).min(self.max_hp);
}
/// Heal the city by the standard per-turn amount (26 HP).
/// Progression: 10 → 20 → 26. Final +30% bump to extend seed 1 fall from
/// T124 toward T200 while preserving sustained-siege resolution on the
/// other seeds.
/// Heal the city by the standard per-turn amount (23 HP).
/// Progression: 10 → 20 → 23. +15% bump to extend seed 1 fall from T124
/// toward T200 while still letting strong sieges resolve.
/// Skips destroyed cities (HP == 0).
pub fn heal_per_turn(&mut self) {
const HEAL_PER_TURN: u32 = 26;
const HEAL_PER_TURN: u32 = 23;
if self.hp > 0 && self.hp < self.max_hp {
self.heal(HEAL_PER_TURN);
}