Scans the gauge TUs for cursor-relative draws with no MoveToAbsolute in scope. Found 6 of 29, three of them STATE-CHANGE-ONLY draws -- notably L4GraphicLamp::NotifyOfStateChange (L4LAMP.cpp:376/380), which blits a lamp using whatever cursor the view happens to hold. That is the '#48 misplaced lamp' class: lamp-shaped, on the panels that have lamps, only on damage/jam/heat state changes, and persistent. Explains why the 5-pilot rig capture was clean (no combat = no lamp state change). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0166KTsC7ADm7VXEi1HF1jNg
76 lines
3.2 KiB
Python
76 lines
3.2 KiB
Python
"""#48 audit: find gauge draws that are NOT preceded by a cursor set.
|
|
|
|
The #42 family: a draw that relies on the CURRENT cursor works by accident the
|
|
first time (the cursor happens to be right) and lands in the wrong place on a
|
|
later redraw. Draws that only fire on a STATE CHANGE -- damage, heat, jam,
|
|
alarm -- are the dangerous ones: they are rare, so the bug hides until gameplay
|
|
triggers them, and nothing erases the stray pixels afterwards.
|
|
|
|
Relative/cursor-relative draws (the risky ones) vs absolute draws (safe):
|
|
RISKY : DrawFilledRectangleToRelative, DrawBitMap*, DrawLineToRelative,
|
|
DrawPointToRelative, DrawFilledRectangleToAbsolute (uses cursor as one corner)
|
|
SAFE : anything taking explicit x,y (DrawPortBackground, NumericDisplay::Draw
|
|
which carries its own coords)
|
|
|
|
Reports each risky draw with the distance back to the nearest MoveToAbsolute /
|
|
MoveToRelative in the same function, and flags any that has none.
|
|
"""
|
|
import os
|
|
import re
|
|
|
|
FILES = [
|
|
"game/reconstructed/btl4gau2.cpp", # weapon panels + eng pages (artifacts seen here)
|
|
"game/reconstructed/btl4gau3.cpp", # pilotList, PlayerStatus, MessageBoard
|
|
"game/reconstructed/btl4gau1.cpp",
|
|
"game/reconstructed/btl4grnd.cpp",
|
|
"engine/MUNGA_L4/L4GAUGE.cpp",
|
|
"engine/MUNGA_L4/L4LAMP.cpp",
|
|
]
|
|
|
|
RISKY = re.compile(r"\.(DrawFilledRectangleToRelative|DrawBitMapOpaque|DrawBitMap|"
|
|
r"DrawLineToRelative|DrawPointToRelative|"
|
|
r"DrawFilledRectangleToAbsolute)\s*\(")
|
|
CURSOR = re.compile(r"\.(MoveToAbsolute|MoveToRelative)\s*\(")
|
|
FUNCHEAD = re.compile(r"^(?:\t|)([A-Za-z_][A-Za-z0-9_]*::[A-Za-z_][A-Za-z0-9_]*)\s*\(")
|
|
|
|
root = r"C:\git\bt411"
|
|
findings = []
|
|
for rel in FILES:
|
|
path = os.path.join(root, rel.replace("/", os.sep))
|
|
if not os.path.exists(path):
|
|
continue
|
|
lines = open(path, encoding="utf-8", errors="replace").read().splitlines()
|
|
func = "?"
|
|
func_start = 0
|
|
last_cursor = -1
|
|
for i, ln in enumerate(lines):
|
|
m = FUNCHEAD.match(ln)
|
|
if m:
|
|
func = m.group(1)
|
|
func_start = i
|
|
last_cursor = -1 # cursor state does not carry across functions
|
|
if CURSOR.search(ln):
|
|
last_cursor = i
|
|
mm = RISKY.search(ln)
|
|
if mm:
|
|
gap = (i - last_cursor) if last_cursor >= func_start else None
|
|
findings.append((rel, i + 1, func, mm.group(1), gap, ln.strip()[:78]))
|
|
|
|
print("=" * 100)
|
|
print("RISKY DRAWS WITH **NO** CURSOR SET IN THE ENCLOSING FUNCTION (top suspects)")
|
|
print("=" * 100)
|
|
none = [f for f in findings if f[4] is None]
|
|
for rel, line, func, call, gap, src in none:
|
|
print(" %-32s :%-5d %-42s %s" % (rel.split("/")[-1], line, func, call))
|
|
print(" %s" % src)
|
|
print("\n -> %d of %d risky draws have NO cursor set in scope\n" % (len(none), len(findings)))
|
|
|
|
print("=" * 100)
|
|
print("RISKY DRAWS WHOSE CURSOR SET IS FAR AWAY (>12 lines -- may be on another branch)")
|
|
print("=" * 100)
|
|
far = [f for f in findings if f[4] is not None and f[4] > 12]
|
|
for rel, line, func, call, gap, src in far:
|
|
print(" %-32s :%-5d gap=%-4d %-38s %s" % (rel.split("/")[-1], line, gap, func, call))
|
|
print("\n -> %d far-cursor draws" % len(far))
|
|
print("\ntotal risky draws scanned: %d" % len(findings))
|