#87 root cause: the level-crossing zone-record send was missing -- observers never saw damage
The binary's effect watcher (FUN_0042aa2c) raises ForceUpdate(DamageZoneUpdateModelFlag) when a zone's damageLevel CROSSES a band-descriptor threshold (FUN_0042a5f4, master-gated on entity+0x28 & 0xc) -- that send is how every other pod's replicant learns zone levels mid-fight. The port's band hub kept only the graphic-state branch, so an observer's copy sat at 0.0 until destruction: no enemy hull darkening, no doll movement, ever (the night-10 "no armour discoloration" report). Restored the level branch with the already-reconstructed DescriptorCrossed (@0042a5f4) + the master gate on both branches (mesh swap still runs on every instance). 2-node verified: A-side armour watcher pushed 114 level changes; every replicant peak matched the master's finals to 4 decimals (dtorso 0.9321 == 0.9321). Render path cleared separately (gotcha #23 discharged): BT_ARMOR_FORCE 0/1 A/B captures prove the tint renders (hull -> charcoal at 1.0). Solo perception is the authentic economy: 2-25 pt lasers vs 68-185 pt pools. Bench: zonewalk.sh node A now carries BT_ARMOR_LOG + BT_SHOT_EVERY (the standing zone-replication receipt) + digest_walk.py; armorpx.sh is the force-pair pixel rig. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
cb5426cc7f
commit
fe48accb6b
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
# #87 pixel-verification A/B: identical solo scene, all zones' draw-op damage
|
||||
# level FORCED 0.0 vs 1.0 (BT_ARMOR_FORCE pins at the watcher, bypassing the
|
||||
# sim), numbered backbuffer captures -> pixel diff decides whether the damage
|
||||
# tint RENDERS at all (gotcha #23: a colour change that logs perfectly and
|
||||
# renders nothing).
|
||||
set -e
|
||||
cd /c/git/bt411/content
|
||||
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1 || true; sleep 2
|
||||
rm -f apxA_*.png apxB_*.png
|
||||
sed "s/^map=.*/map=grass/; s/^time=.*/time=day/; 0,/^vehicle=.*/s//vehicle=madcat/" MP.EGG > APX.EGG
|
||||
for F in 0.0 1.0; do
|
||||
TAG=A; [ "$F" = "1.0" ] && TAG=B
|
||||
rm -f "apx_$F.log"
|
||||
BT_ARMOR_FORCE=$F BT_ARMOR_LOG=1 BT_SHOT_EVERY=120 BT_SHOT_PREFIX=apx$TAG \
|
||||
BT_SPAWN_ENEMY=1 BT_GOTO=enemy BT_GOTO_STOP=55 \
|
||||
bt_launch "apx_$F.log" APX.EGG 0x03
|
||||
sleep 75
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1 || true; sleep 3
|
||||
done
|
||||
echo "=== captures:"; ls apxA_*.png apxB_*.png 2>/dev/null
|
||||
echo "=== [armor] lines force=1.0:"; grep -c "\[armor\]" apx_1.0.log || true
|
||||
grep -m2 "\[armor\]" apx_1.0.log || true
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
# Digest the instrumented zone-walk: per-zone level progressions on B (master),
|
||||
# replicant levels seen by A's armor watcher, and the per-hit damage economy.
|
||||
import re, sys, collections
|
||||
|
||||
def rd(p):
|
||||
with open(p, 'rb') as f:
|
||||
return f.read().decode('utf-8', 'replace')
|
||||
|
||||
a = rd(r'C:\git\bt411\content\zw_a.log')
|
||||
b = rd(r'C:\git\bt411\content\zw_b.log')
|
||||
|
||||
# idx -> name from A's walk lines: zone N 'name'
|
||||
names = {}
|
||||
for m in re.finditer(r"zone (\d+) '([^']+)'", a):
|
||||
names[int(m.group(1))] = m.group(2)
|
||||
|
||||
# B-side consumption: [dmghit] mech=E zone=N ... amt=X ... lvl P->Q
|
||||
zones = collections.defaultdict(lambda: {'hits': 0, 'amt': 0.0, 'first': None, 'last': 0.0})
|
||||
hitlines = re.findall(r"\[dmghit\] mech=(\S+) zone=(\d+) vital=(\d+) type=(\d+) amt=([\d.eE+-]+) burst=(\d+) lvl ([\d.eE+-]+)->([\d.eE+-]+)", b)
|
||||
mechs = collections.Counter(m[0] for m in hitlines)
|
||||
victim = mechs.most_common(1)[0][0] if mechs else None
|
||||
for mech, zi, vital, typ, amt, burst, p, q in hitlines:
|
||||
if mech != victim:
|
||||
continue
|
||||
z = zones[int(zi)]
|
||||
z['hits'] += 1
|
||||
z['amt'] += float(amt)
|
||||
if z['first'] is None:
|
||||
z['first'] = float(p)
|
||||
z['last'] = float(q)
|
||||
|
||||
print(f"victim {victim}: {sum(z['hits'] for z in zones.values())} hits into {len(zones)} zones")
|
||||
print(f"{'zone':>4} {'name':<12} {'hits':>4} {'amt/hit':>8} {'lvl first->last':>18}")
|
||||
for zi in sorted(zones, key=lambda k: -zones[k]['last']):
|
||||
z = zones[zi]
|
||||
per = z['amt'] / z['hits'] if z['hits'] else 0
|
||||
print(f"{zi:>4} {names.get(zi, '?'):<12} {z['hits']:>4} {per:>8.3f} {z['first']:>8.4f} -> {z['last']:<8.4f}")
|
||||
|
||||
# A-side replicant levels: [armor] '<mat>' zone N level P -> Q
|
||||
arm = re.findall(r"\[armor\] '[^']*' zone (\d+) level ([\d.eE+-]+) -> ([\d.eE+-]+)", a)
|
||||
if arm:
|
||||
peak = collections.defaultdict(float)
|
||||
for zi, p, q in arm:
|
||||
peak[int(zi)] = max(peak[int(zi)], float(q))
|
||||
print(f"\nA-side watcher: {len(arm)} level-change pushes; replicant peaks:")
|
||||
for zi in sorted(peak, key=lambda k: -peak[k])[:12]:
|
||||
print(f" zone {zi:>3} {names.get(zi, '?'):<12} peak {peak[zi]:.4f}")
|
||||
else:
|
||||
print("\nA-side watcher: NO level-change pushes (replicant levels never moved)")
|
||||
|
||||
# zone-repl confirmations on A
|
||||
zr = re.findall(r"\[zone-repl\] zone (\d+) '([^']+)' lvl ([\d.eE+-]+)->([\d.eE+-]+)", a)
|
||||
print(f"\n[zone-repl] on A: {len(zr)} lines")
|
||||
for zi, nm, p, q in zr[:8]:
|
||||
print(f" zone {zi} {nm} {p}->{q}")
|
||||
@@ -24,8 +24,13 @@ BT_SPIN_SELF=15 BT_DMG_LOG=1 BT_DEATH_LOG=1 BT_MP_LOG=1 BT_MATCHLOG=1 \
|
||||
bt_launch zw_b.log ZW.EGG 0x0C -net 1601
|
||||
sleep 2
|
||||
# SHOOTER second (foreground -- owns the pick focus): the walker.
|
||||
# BT_ARMOR_LOG on A: the watcher logs the REPLICANT's zone levels as A renders
|
||||
# B -- decides whether damage darkening ever reaches the observer node (#87 MP).
|
||||
# BT_SHOT_EVERY: periodic captures of A's view of B's hull (real-fight pixels).
|
||||
rm -f zwA_*.png
|
||||
BT_ZONE_WALK=6 BT_PICK_LOG=1 BT_GOTO=enemy BT_GOTO_STOP=90 BT_KEY_NOFOCUS=1 \
|
||||
BT_DMG_LOG=1 BT_RANGE_LOG=1 BT_MP_LOG=1 BT_MATCHLOG=1 \
|
||||
BT_ARMOR_LOG=1 BT_SHOT_EVERY=300 BT_SHOT_PREFIX=zwA \
|
||||
bt_launch zw_a.log ZW.EGG 0x03 -net 1501
|
||||
sleep 5
|
||||
python ../tools/btconsole.py ZW.EGG 127.0.0.1:1501 127.0.0.1:1601 > zw_relay.log 2>&1 &
|
||||
|
||||
Reference in New Issue
Block a user