59 lines
4.7 KiB
Python
59 lines
4.7 KiB
Python
"""#80 fix-landed comment + #28 machinery note."""
|
|
import base64
|
|
import json
|
|
import subprocess
|
|
import urllib.request
|
|
|
|
REPO = r"C:\git\bt411"
|
|
BASE = "https://gitea.mysticmachines.com/api/v1/repos/VWE/BT411"
|
|
|
|
out = subprocess.run(["git", "credential", "fill"],
|
|
input="protocol=https\nhost=gitea.mysticmachines.com\n\n",
|
|
capture_output=True, text=True, cwd=REPO)
|
|
cred = dict(l.split("=", 1) for l in out.stdout.strip().splitlines() if "=" in l)
|
|
AUTH = "Basic " + base64.b64encode(
|
|
(cred["username"] + ":" + cred["password"]).encode()).decode()
|
|
|
|
|
|
def call(method, path, payload=None):
|
|
data = json.dumps(payload).encode() if payload is not None else None
|
|
r = urllib.request.Request(BASE + path, data=data, method=method)
|
|
r.add_header("Authorization", AUTH)
|
|
r.add_header("Content-Type", "application/json")
|
|
return json.load(urllib.request.urlopen(r))
|
|
|
|
|
|
def comment(n, body):
|
|
marker = body.strip().splitlines()[0][:60]
|
|
for c in call("GET", "/issues/%d/comments" % n):
|
|
if marker in c.get("body", ""):
|
|
print(" #%d already has this comment - skipped" % n)
|
|
return
|
|
call("POST", "/issues/%d/comments" % n, {"body": body})
|
|
print(" commented on #%d" % n)
|
|
|
|
|
|
C80 = """**FIX LANDED 2026-07-29** (`a5fb96a`, build 4.11.625) -- the full crit system is reconstructed and bench-verified. Staying open for field confirmation per the house rule.
|
|
|
|
All three layers closed, and the third reversed a prior finding:
|
|
|
|
1. **The trigger, recovered from the un-exported gap.** The Mech message table `@0x50bdf8` names the real `Mech::TakeDamageMessageHandler @0x4a0230` (message 0x12 "TakeDamage"). Inside: the crit chance `@0x4a0164` = `clamp(0.7 * damageLevel^2 + 0.01, 0..1)`, gated on the player's `simLive` flag (novice never crits), rolled **per burst** on the current zone (skipping a burning zone). ~1% on fresh armour, ~18% at half-stripped, ~58% at 90%. The application loop also fixes two silent divergences: the engine base ignored `burstCount` entirely (multi-burst under-applied), and the binary **re-runs the cylinder lottery per burst** so bursts spray across zones.
|
|
|
|
2. **The sink.** `MechSubsystem::TakeDamage` is now the real `@0x4ac0bc` body (the address CLASSMAP had mislabeled "HandleMessage"): zone damage -> Destroyed alarm -> pin -> and for a **vital subsystem, the owner mech's graphicAlarm to level 9** -- the #28 kill machinery.
|
|
|
|
3. **The reversal.** The subsystem ctor's armour/scale copy existed but wrote through the `ReconDamageZone` proxy (struct offsets +4/+8) instead of the engine members (+0x140/+0x144) -- the floats landed on the object header and the real scales stayed zero. The 2026-07-28 "subsystem zones cannot be damaged, in the original too" finding was measuring exactly this port bug. The binary initializes the zone from resource keys `WeaponDamagePoints` (required), `CriticalHitScoreBonus` (required), and the five per-type `...DamagePoints`; the CSS now parses all seven and the ctor writes the engine's named members.
|
|
|
|
**Bench:** `[subarmor]` shows real parsed scales for every subsystem at spawn; `[critroll]` landed full-chain crits in both runs (chance roll -> weighted subsystem pick -> the subsystem's own zone driven to 1.0 -> Destroyed). Death/respawn and ammo gates un-regressed; zero crashes.
|
|
|
|
**What field verification looks like:** fight until armour strips, then watch the CRITICAL view of the secondary panel (N-key cycle) -- it should now light up as subsystems take crits, increasingly often as zones go deep. Crits are probabilistic by design (#73's lottery), so judge frequency against the curve, not single shots.
|
|
|
|
**Honest gaps, documented in `context/combat-damage.md`:** burst>1 spraying is transcribed but not yet exercised on the bench; the `damageType==0` COLLISION divert (`@0x49ffcc`) is decoded-not-reconstructed; the id-0x16 damage/kill report messages to the players (the authentic stats plumbing) are deferred to #45."""
|
|
|
|
C28 = """The kill machinery this issue asks about is now REAL (`a5fb96a`, #80): `MechSubsystem::TakeDamage @0x4ac0bc` -- on the subsystem's own zone reaching 1.0, a **vital** subsystem raises the owner mech's `graphicAlarm` to level 9 (the same fall/death level the leg-destruction path uses). The reachable path is: crit roll (`@0x4a0164`, armour-stripped zones) -> `CriticalHit` -> weighted pick of a critical subsystem -> its zone driven to 1.0 -> if vital, level 9.
|
|
|
|
Still needed to close this issue: confirm live that a vital-subsystem crit actually kills (which subsystems are `VitalSubsystem=True` in the shipped notation decides how often that can even happen), and that the death that follows is the normal death transition."""
|
|
|
|
|
|
comment(80, C80)
|
|
comment(28, C28)
|