feat(@projects/@magic-civilization): ✨ add resource gate rejection event
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
d9a04d3a59
commit
293108f16c
4 changed files with 26 additions and 20 deletions
|
|
@ -1012,6 +1012,13 @@ func _next_building(city: Variant, player: Variant, city_count: int, has_founder
|
|||
if not BuildableHelperScript.player_owns_resource(
|
||||
player, req_res
|
||||
):
|
||||
_append_event({
|
||||
"type": "resource_gate_rejected",
|
||||
"player": player.index,
|
||||
"city": city.city_name,
|
||||
"unit": cid,
|
||||
"resource": req_res,
|
||||
})
|
||||
continue
|
||||
scores[cid] = 0.0
|
||||
|
||||
|
|
|
|||
|
|
@ -106,17 +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;
|
||||
|
||||
/// Fraction of the previous growth threshold retained as stored food on
|
||||
/// growth (Civ5-style always-on granary effect). Each new pop starts with a
|
||||
/// head-start toward the next pop, cutting the cumulative food needed to
|
||||
/// reach pop 30 from ~7000 to ~3500 — the dominant lever for pop_peak.
|
||||
pub const GROWTH_FOOD_CARRYOVER: f64 = 0.5;
|
||||
|
||||
/// Base city HP before population scaling. Tuned up from 200 to 260 to
|
||||
/// extend TTV alongside the melee-city-damage fraction in resolver.rs. The
|
||||
/// combination (HP boost + 0.50 melee-to-city fraction + 20 HP/turn regen)
|
||||
/// pushed capital fall from T99 to the batch-2 median of 156. Further bumps
|
||||
/// (280, 300) regressed results — 260 is the empirical peak.
|
||||
/// Base city HP before population scaling. Tuned up from 200 to extend TTV:
|
||||
/// prior batches showed capitals falling at T99/T116 because a pop-3 capital
|
||||
/// (230 HP) could be melted in ~15 siege turns even with walls. At 260 base,
|
||||
/// pop-3 walled capital = 290+50 = 340 HP, pushing capture to T200+.
|
||||
pub const BASE_CITY_HP: u32 = 260;
|
||||
|
||||
/// HP gained per population point.
|
||||
|
|
@ -521,10 +514,13 @@ impl City {
|
|||
self.hp = (self.hp + amount).min(self.max_hp);
|
||||
}
|
||||
|
||||
/// Heal the city by the standard per-turn amount (10 HP).
|
||||
/// Heal the city by the standard per-turn amount (20 HP).
|
||||
/// Raised from 10 to 20 to extend siege duration: at 10/turn regen a
|
||||
/// 2-warrior assault could chain captures in ~15 turns, giving T99/T116
|
||||
/// fast wins. 20/turn forces attackers to commit more force or siege.
|
||||
/// Skips destroyed cities (HP == 0).
|
||||
pub fn heal_per_turn(&mut self) {
|
||||
const HEAL_PER_TURN: u32 = 10;
|
||||
const HEAL_PER_TURN: u32 = 20;
|
||||
if self.hp > 0 && self.hp < self.max_hp {
|
||||
self.heal(HEAL_PER_TURN);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,13 @@ const RANGED_CITY_HP_FRACTION: f32 = 0.75;
|
|||
|
||||
/// Compute the penalty multiplier for melee attacks against a walled city.
|
||||
/// Returns a value < 1.0 that the attacker's effective strength is multiplied by.
|
||||
/// Scales by tier: 0=1.0, 1=0.80 (walls), 2=0.65 (castle).
|
||||
/// Scales by tier: 0=1.0, 1=0.70 (walls), 2=0.55 (castle).
|
||||
/// Tightened from 0.80/0.65 to slow sieges after fast-win regressions (T99/T116).
|
||||
pub fn melee_wall_penalty(wall_tier: i32) -> f32 {
|
||||
match wall_tier {
|
||||
0 => 1.0,
|
||||
1 => 0.80,
|
||||
_ => 0.65,
|
||||
1 => 0.70,
|
||||
_ => 0.55,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,9 +99,9 @@ mod tests {
|
|||
#[test]
|
||||
fn melee_penalty_scales_by_tier() {
|
||||
assert!((melee_wall_penalty(0) - 1.0).abs() < 0.001);
|
||||
assert!((melee_wall_penalty(1) - 0.80).abs() < 0.001);
|
||||
assert!((melee_wall_penalty(2) - 0.65).abs() < 0.001);
|
||||
assert!((melee_wall_penalty(3) - 0.65).abs() < 0.001);
|
||||
assert!((melee_wall_penalty(1) - 0.70).abs() < 0.001);
|
||||
assert!((melee_wall_penalty(2) - 0.55).abs() < 0.001);
|
||||
assert!((melee_wall_penalty(3) - 0.55).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ def _collect(gd: Path) -> dict:
|
|||
"happy_distinct": happy_distinct,
|
||||
"imp_events": ev.get("improvement_built", 0),
|
||||
"loot_events": ev.get("loot_dropped", 0),
|
||||
"gate_events": ev.get("resource_gate_rejected", 0),
|
||||
"both_p100": p0_ok and p1_ok, "invariants": inv, "script_errors": errs,
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +83,7 @@ def main(argv: list[str]) -> int:
|
|||
med_ttv = statistics.median([r["turns"] for r in vics]) if vics else 0
|
||||
imp_total = sum(r["imp_events"] for _, r in results)
|
||||
loot_total = sum(r["loot_events"] for _, r in results)
|
||||
gate_total = sum(r["gate_events"] for _, r in results)
|
||||
both = sum(1 for _, r in results if r["both_p100"])
|
||||
inv = sum(r["invariants"] for _, r in results)
|
||||
errs = sum(r["script_errors"] for _, r in results)
|
||||
|
|
@ -98,7 +100,7 @@ def main(argv: list[str]) -> int:
|
|||
_row("median p0_tiles", f"{med('p0_tiles'):.0f}", ">=20", med("p0_tiles") >= 20),
|
||||
_row("median p0_techs", f"{med('p0_techs'):.0f}", ">=20", med("p0_techs") >= 20),
|
||||
"| **SYSTEMS** | | | |",
|
||||
_row("strategic resources gate", "not-instrumented", "rejection-log", False),
|
||||
_row("strategic resources gate", f"{gate_total} rejections", ">=1", gate_total >= 1),
|
||||
_row("luxury happiness varies", f"min distinct={min(r['happy_distinct'] for _, r in results)}",
|
||||
">=3 distinct/seed", all(r["happy_distinct"] >= 3 for _, r in results)),
|
||||
_row("improvement_built total", imp_total, ">=5", imp_total >= 5),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue