build(combat): numbers foundation — formulas→Lua hot-reload + CombatSim + a CI band #217

Merged
panda merged 2 commits from feat/combat-numbers into main 2026-08-02 00:41:33 +02:00
Owner

Summary

Phase 1 of the combat rework (design #214): the demonstrable-numbers foundation, proven on the existing HitChanceserver-only, no combat-behaviour change (identical numbers, verified by a parity test). It stands up the loop the owner asked for: edit a Lua formula → hot-reload → CombatSim → CI band, on the real shared code.

  • content/scripts/combat/formulas.lua — a pure-function Lua module (constants inline, hot-reloadable) with hitChance identical to the old static formula. The combat pipeline stays C#; only the tunable curves are Lua content.
  • FunctionModuleHost / LuaFunctionModule (Scripting) — compile-once + cache + hot-reload swap (keeps the last-good module on a bad edit); a genuinely new host capability (function module, not content registration).
  • LuaCombatFormulas behind an ICombatFormulas seam — sanitises the Lua return (NaN/±∞/out-of-range collapse to a safe guaranteed-miss, never reaching the tick), isolates a runtime error online; a fatal startup probe pre-online. CombatSystem/World/Program wire it; /reloadscripts reloads it.
  • tools/IsoMmo.CombatSim over IsoMmo.CombatSim.Core — a seeded Monte-Carlo duel harness on the SHARED Lua curve; prints a matchup table and writes a visible self-contained HTML report (win-rate + TTK). just combatsim.
  • One seeded CI regression band asserting reproducible guardrails on the harness output (same seed → same numbers; a curve edit that moves them fails CI). Measured-then-fenced around today's values — the derived-target bands land with the model rework.

Screenshots / recording

N/A — dev tooling, no game surface. Evidence: just combatsim reads the real Lua and reports

  matchup                      A win%   TTK med       p10–p90
  Even (30.0 vs 30.0)           54.6%     14.4s    12.0–16.8s
  Edge (50.0 vs 30.0)           99.7%     12.0s    10.8–14.4s
  report → combat-sim.html

How it was tested

just lint 0 warnings; just test green (GameServer 284, CombatSim.Core 4 = the bands, Scripting 39 incl. 7 function-module tests, + others); just check-docs + just lua-lint clean; whole solution builds (client + tools). New tests: FunctionModuleHostTests (load/compile/invoke/NaN/reload-swap/last-good), LuaCombatFormulasTests (parity grid + clamp/NaN/∞ guard + online error isolation), DuelHarnessBandTests (the seeded bands + exact reproducibility). Ran just combatsim (output above).

Checklist

  • just lint passes (zero warnings)
  • just test is green
  • The whole solution builds (client + tools)
  • Multi-platform preserved (MoonSharp pure-managed; .NET tool)
  • Tests added
  • Linked #214 / closes #216; DoD met (parity, hot-reload proof, guard, visible sim, reproducible CI band, gate/docs updated)

Closes #216. Refs #214.

## Summary Phase 1 of the combat rework (design #214): the **demonstrable-numbers foundation**, proven on the existing `HitChance` — **server-only, no combat-behaviour change** (identical numbers, verified by a parity test). It stands up the loop the owner asked for: *edit a Lua formula → hot-reload → CombatSim → CI band*, on the real shared code. - **`content/scripts/combat/formulas.lua`** — a pure-function Lua module (constants inline, hot-reloadable) with `hitChance` identical to the old static formula. The combat *pipeline* stays C#; only the tunable *curves* are Lua content. - **`FunctionModuleHost` / `LuaFunctionModule`** (Scripting) — compile-once + cache + hot-reload swap (keeps the last-good module on a bad edit); a genuinely new host capability (function module, not content registration). - **`LuaCombatFormulas`** behind an `ICombatFormulas` seam — sanitises the Lua return (`NaN`/±∞/out-of-range collapse to a safe guaranteed-miss, never reaching the tick), isolates a runtime error online; a **fatal startup probe** pre-online. `CombatSystem`/`World`/`Program` wire it; `/reloadscripts` reloads it. - **`tools/IsoMmo.CombatSim`** over **`IsoMmo.CombatSim.Core`** — a seeded Monte-Carlo duel harness on the SHARED Lua curve; prints a matchup table and writes a visible self-contained **HTML report** (win-rate + TTK). `just combatsim`. - **One seeded CI regression band** asserting reproducible guardrails on the harness output (same seed → same numbers; a curve edit that moves them fails CI). Measured-then-fenced around today's values — the *derived-target* bands land with the model rework. ## Screenshots / recording N/A — dev tooling, no game surface. Evidence: `just combatsim` reads the real Lua and reports ``` matchup A win% TTK med p10–p90 Even (30.0 vs 30.0) 54.6% 14.4s 12.0–16.8s Edge (50.0 vs 30.0) 99.7% 12.0s 10.8–14.4s report → combat-sim.html ``` ## How it was tested `just lint` 0 warnings; `just test` green (GameServer 284, CombatSim.Core 4 = the bands, Scripting 39 incl. 7 function-module tests, + others); `just check-docs` + `just lua-lint` clean; whole solution builds (client + tools). New tests: `FunctionModuleHostTests` (load/compile/invoke/NaN/reload-swap/last-good), `LuaCombatFormulasTests` (parity grid + clamp/NaN/∞ guard + online error isolation), `DuelHarnessBandTests` (the seeded bands + exact reproducibility). Ran `just combatsim` (output above). ## Checklist - [x] `just lint` passes (zero warnings) - [x] `just test` is green - [x] The whole solution builds (client + tools) - [x] Multi-platform preserved (MoonSharp pure-managed; .NET tool) - [x] Tests added - [x] Linked #214 / closes #216; DoD met (parity, hot-reload proof, guard, visible sim, reproducible CI band, gate/docs updated) Closes #216. Refs #214.
build(combat): numbers foundation — formulas→Lua hot-reload + CombatSim + a CI band (#216)
All checks were successful
ci / Lua content lint (pull_request) Successful in 1m18s
ci / Lint & Test (pull_request) Successful in 5m4s
8ea5cc184b
The demonstrable-numbers machine, proven on the existing HitChance (no
behaviour change). hitChance moves to content/scripts/combat/formulas.lua (a
pure-function Lua module, constants inline, hot-reloadable); a new
FunctionModuleHost compiles/caches it and swaps on reload (last-good on a bad
edit); LuaCombatFormulas reads it behind the ICombatFormulas seam and
sanitises the return (NaN/out-of-range can never reach the tick), with a
fatal startup probe. tools/IsoMmo.CombatSim (over IsoMmo.CombatSim.Core) runs
a seeded Monte-Carlo on the SHARED Lua curve and writes a visible HTML
report; a seeded CI regression band asserts reproducible guardrails on it.
Refines the content-in-Lua gate (pipeline C#, tunable curves Lua).

Refs #214. Closes #216.
Merge origin/main into feat/combat-numbers: reconcile combat formulas with run/walk + terrain-Z (World/CombatSystem ctors)
All checks were successful
ci / Lua content lint (pull_request) Successful in 10s
ci / Lint & Test (pull_request) Successful in 5m14s
1454e27513
panda merged commit f8e42eb774 into main 2026-08-02 00:41:33 +02:00
panda deleted branch feat/combat-numbers 2026-08-02 00:41:34 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo!217
No description provided.