port: the PRESS (A) plate could not be drawn at any instant -- four faults, and a misquoted number
1. --time= was silently ignored on any screen with a settle window >= 30 units: pose_at overwrote the requested instant with settle_instant. ScreenView.frozen now marks an explicit instant and skips both clamps. 2. press_start's settle window was [0,214] -- the dead stretch BEFORE the plate exists -- so its settle instant was t=107, where the element is alpha 0. The exporter now rejects intervals in which nothing is visible. title keeps [160,236], the interval the Decoder's draw stream confirmed. 3. My authored looping_focus_records entry for press_start/ptbtn00 drew a dim focus record INSTEAD of the plate's own sprite: max 0 vs max 252.5. Deleted -- an authored guess that overrides a decode with a worse answer is removed. 4. verify-capture passed --time=5.9617 for the title and it was never applied. Every title figure it has printed, including the 0.26% quoted to the Decoder, was measured at the settle instant under a note claiming t=357.7. Both rows now pose by omission and the note matches. title is 0.21% honestly; splashes unchanged at 0.01%. The boot's end artifact now contains the plate (region mean 95.7 vs 33.6). Corrects last iteration's BLOCKED row, which had the entry's effect backwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
@@ -73,73 +73,37 @@
|
||||
],
|
||||
"looping_focus_records": {
|
||||
"_": [
|
||||
"WHICH focus records the port draws unconditionally, and loops. CHOSEN, and",
|
||||
"deliberately a LOOKUP rather than a rule -- delete an entry the day the disc",
|
||||
"or a capture states it.",
|
||||
"WHICH focus records the port draws unconditionally, and loops.",
|
||||
"",
|
||||
"THE PRESS (A) PLATE GLOWS AND PULSES. Measured on the running game:",
|
||||
"`ptbtn00f`, the plate's focus record, ramps alpha 0x00->0x50 and back, and",
|
||||
"its period was timed four times at 2.12 / 2.19 / 2.34 / 2.31 s. You cannot",
|
||||
"measure a period unless the thing repeats, so THAT IT LOOPS IS MEASURED. The",
|
||||
"port drew nothing at all here, because press_start has no `buttons` and",
|
||||
"nothing is focused.",
|
||||
"🔴 EMPTY AS OF 2026-08-30, and the deletion is the finding.",
|
||||
"",
|
||||
"WHY A LOOKUP AND NOT A RULE, which is the part worth keeping. The spinning",
|
||||
"ring is a RULE in the renderer (`spin_period_units`) because it has a",
|
||||
"disc-wide check: 16 of 212 elements match its shape and all 16 are focus",
|
||||
"rings, zero false positives. The obvious rule for a pulse -- a group whose",
|
||||
"keyframes vary only in alpha and whose first and last alpha are equal -- was",
|
||||
"CENSUSED BEFORE BEING WRITTEN and it fails: 82 OF 212 ELEMENTS MATCH,",
|
||||
"including ptcopyright, palogo_sqex, ptmsg and every _eff fade-in. A renderer",
|
||||
"rule on that shape would make the copyright notice pulse.",
|
||||
"The one entry here -- `press_start/ptbtn00`, period 120 -- was AUTHORED to",
|
||||
"give the PRESS (A) plate a glow on a screen that has no buttons and focuses",
|
||||
"nothing. Measured, it did the opposite: it made the plate INVISIBLE.",
|
||||
"",
|
||||
"Narrowing to focus records leaves exactly ONE distinct element (ptbtn00f,",
|
||||
"plus its JP twin), and a rule justified by n=1 is a special case wearing a",
|
||||
"rule's clothes. So it is written here, per element, by name."
|
||||
],
|
||||
"press_start/ptbtn00": {
|
||||
"record_element": "ptbtn00f",
|
||||
"period_units": 120,
|
||||
"kind": "chosen",
|
||||
"why": [
|
||||
"120 units, and it is DECODED rather than chosen -- but it is still written",
|
||||
"here because the field is not in the pinned crate yet. DELETE THIS the day a",
|
||||
"tag exposes it and the exporter can emit it.",
|
||||
"",
|
||||
"A nested record is itself a RATC bundle with its own header, and that header's",
|
||||
"+0x08 IS THE LOOP LENGTH -- the same field `ui_header_time_disc` already tests",
|
||||
"as an animation length at the top level. A record's keyframes need not fill",
|
||||
"it, and the slack is a hold at the final pose. `ptbtn00f` is 105 UNITS OF RAMP",
|
||||
"INSIDE A 120-UNIT CYCLE, so the glow rests dark for 15 units before repeating.",
|
||||
"The five `ptbtn0Nf` records on the main menu fill their 120 exactly, which is",
|
||||
"what shows the slack belongs to this record and not to the format.",
|
||||
"",
|
||||
"Disc-wide over 1781 timed nested records: 92.3% declare exactly their last",
|
||||
"keyframe time, 7.7% declare more, and 0 DECLARE LESS. That last row is the",
|
||||
"falsifier -- a cycle cannot restart before its own last pose -- and it never",
|
||||
"fires.",
|
||||
"",
|
||||
"AND IT SURVIVES THE OBJECTION THIS ENTRY RAISED, using THIS PORT's number.",
|
||||
"Both candidate periods need the same emulator pacing factor, and the ring",
|
||||
"measures it independently: declared 120 units -> 2.177 s = 1.0885.",
|
||||
"",
|
||||
" 105 units = 1.750 s nominal, needs a factor of 1.211-1.337 -> EXCLUDES it",
|
||||
" 120 units = 2.000 s nominal, needs a factor of 1.060-1.170 -> CONTAINS it",
|
||||
"",
|
||||
"So 105 cannot reach the corpus's measured 2.12-2.34 s under any pacing the",
|
||||
"ring also satisfies. At 120 the prediction is 2.177 s. Different elements,",
|
||||
"different bundles, separate runs, tied only by both declaring 120.",
|
||||
"",
|
||||
"HISTORY, because the sequence is what makes this checkable: this entry read",
|
||||
"129 (last keyframe 105 + the since-deleted exit_ramp_units), then 105 (the",
|
||||
"group length under the corrected layout, which I shipped knowing it was 17%",
|
||||
"short of every measurement). The 123-vs-129 pair I once called unseparable",
|
||||
"STRADDLED the right answer without containing it.",
|
||||
"",
|
||||
"The ~3% between 2.177 and the corpus's 2.24 mean is four wall-clock samples of",
|
||||
"a 2 s period; the Decoder looked for a further declared hold and found none."
|
||||
]
|
||||
}
|
||||
"`_draw` treats a looping focus record the way it treats a focused button --",
|
||||
"it draws the focus record INSTEAD of the base sprite and moves on. For a",
|
||||
"focused button that is right and measured (the focused sprite covers the",
|
||||
"base at 100.0 % of base-visible pixels). For the plate it substituted a dim",
|
||||
"glow at the wrong phase for the element's own bright sprite:",
|
||||
"",
|
||||
" with the entry without it",
|
||||
" press_start t=236 max 0 max 252.5",
|
||||
" press_start t=240 max 0 max 252.5",
|
||||
" press_start t=250 max 15.3 max 252.5",
|
||||
"",
|
||||
"So the plate could not be seen at any instant, on the screen whose entire",
|
||||
"content is the plate. The element's own fade is DECODED -- alpha 255 at",
|
||||
"t=236-238 -- and this authored guess was overriding decoded data with a",
|
||||
"worse answer. That is the wrong way round, and an authored entry that",
|
||||
"suppresses a decode is deleted rather than tuned.",
|
||||
"",
|
||||
"⚠️ What is NOT claimed: that the plate has no glow. The focus record",
|
||||
"`ptbtn00f.rat` exists and may well be drawn ON TOP of the base in the real",
|
||||
"game. Drawing both would be a rendering rule nobody has measured, so the",
|
||||
"port draws what the disc's own fade says and leaves the glow out. See",
|
||||
"docs/port/BLOCKED.md -- a capture of the idle title settles it."
|
||||
]
|
||||
},
|
||||
"exit_ramp_deleted_why": [
|
||||
"DELETED 2026-08-29, and the deletion is the point.",
|
||||
|
||||
@@ -651,9 +651,33 @@ fn settle_window(elements: &[Element]) -> Option<[i64; 3]> {
|
||||
if times.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
// 🔴 A GAP IN WHICH NOTHING IS VISIBLE IS NOT A SETTLE WINDOW.
|
||||
//
|
||||
// The widest keyframe-free interval is only a settled state if the screen is
|
||||
// actually PRESENTING something across it. `press_start` is the case that
|
||||
// proves it: its keyframes are 0, 214, 236, 238, 244, so the widest gap is
|
||||
// 0..214 -- the dead stretch BEFORE the plate appears, where `ptbtn00` is
|
||||
// alpha 0 throughout. Taking its midpoint gave a settle instant of t=107,
|
||||
// and the runtime then answered every question about that screen at t=107.
|
||||
// The result was that the PRESS (A) plate could not be drawn at any instant
|
||||
// at all, including the boot's own end state, whose entire purpose is to
|
||||
// show it.
|
||||
//
|
||||
// The fix is not a tuned threshold: it is that the heuristic was reading an
|
||||
// interval where the screen is BLANK as the interval where it has arrived.
|
||||
// Rejecting those leaves `press_start` with 214..236 (22 units), which is
|
||||
// under the runtime's 30-unit bar, so it falls back to each element's own
|
||||
// hold -- which is the plate, opaque, exactly as the disc declares it.
|
||||
//
|
||||
// ⚠️ This does not disturb the windows the settle instant was measured on.
|
||||
// `title` keeps [160, 236]: elements are visible across it, and the
|
||||
// Decoder's draw stream independently found the game's clock freezing in
|
||||
// that same interval.
|
||||
let visible_at = |t: i64| elements.iter().any(|e| alpha_at(e, t) > 0);
|
||||
let (a, b) = times
|
||||
.windows(2)
|
||||
.map(|w| (w[0], w[1]))
|
||||
.filter(|(a, b)| visible_at((a + b) / 2))
|
||||
.max_by_key(|(a, b)| b - a)?;
|
||||
Some([a, b, (a + b) / 2])
|
||||
}
|
||||
|
||||
@@ -536,6 +536,14 @@ pulses on a period (`authored/timing.json` already carries a *speculative*
|
||||
what a pulse would need); or it genuinely blinks once and vanishes, in which case
|
||||
the port is right and this closes ✅.
|
||||
|
||||
⚠️ Note the port already draws a looping focus record for this element. If the
|
||||
answer is "blinks once", **that entry is wrong and should be deleted**, not kept
|
||||
as harmless — it is the only thing making the plate reappear at all.
|
||||
🔴 **Correction, 2026-08-30.** This row previously said the looping focus record
|
||||
was "the only thing making the plate reappear at all". The opposite was true: the
|
||||
entry was the only thing making it **disappear** — it drew a dim glow *instead of*
|
||||
the plate's own sprite, max 0 against max 252.5. It has been deleted, and the port
|
||||
now draws the plate from its own decoded fade. See `DECISIONS.md`.
|
||||
|
||||
⚠️ So the question narrows rather than closes. The port shows the plate at its
|
||||
declared instants and holds it. What a capture would still settle is whether the
|
||||
real plate **pulses** after that, and whether `ptbtn00f.rat` — the focus record,
|
||||
which the port now draws not at all — is a glow layered *over* the plate. Drawing
|
||||
both would be a rendering rule nobody has measured.
|
||||
|
||||
@@ -5828,3 +5828,95 @@ did not match the flag I passed, and that was visible in my first `ls`.** A
|
||||
verbatim re-run produces six correctly-named per-step shots. I do not know where
|
||||
those 410 files came from, which is itself the point: I drew a conclusion from a
|
||||
file set whose provenance I never checked.
|
||||
|
||||
## The `PRESS Ⓐ` plate: four bugs in a row, and a number I have been misquoting
|
||||
|
||||
Last iteration I filed that the plate was visible for 8 units and simply missed by
|
||||
the boot's capture instant. That was wrong in the direction that matters: **the
|
||||
plate could not be drawn at any instant at all**, and three separate faults had to
|
||||
be removed before it appeared. The fourth is a correction to figures I have quoted
|
||||
to the Decoder repeatedly.
|
||||
|
||||
### 1. `--time=` was silently ignored on half the screens
|
||||
|
||||
`pose_at` did `if holding: t = settle_instant`. The requested instant was
|
||||
**discarded** on every screen with a settle window ≥ 30 units — `title`,
|
||||
`press_start`. The flag parsed, the log printed the time asked for, and the pose
|
||||
came from somewhere else entirely.
|
||||
|
||||
`ScreenView.frozen` now marks an explicitly pinned instant and skips both clamps.
|
||||
|
||||
### 2. The settle window picked an interval where nothing is visible
|
||||
|
||||
`press_start`'s keyframes are 0, 214, 236, 238, 244. The widest keyframe-free gap
|
||||
is **0…214** — the dead stretch *before* the plate exists, where `ptbtn00` is
|
||||
alpha 0 throughout — so its settle instant was **t=107**, and every question about
|
||||
that screen was answered there.
|
||||
|
||||
🔴 **A gap in which nothing is visible is not a settled state.** The exporter now
|
||||
rejects those intervals. `press_start` becomes [214, 236] — 22 units, under the
|
||||
runtime's 30-unit bar — so it falls back to each element's own hold, which is the
|
||||
plate, opaque, as the disc declares it.
|
||||
|
||||
⚠️ It disturbs no window the settle instant was measured on: `title` keeps
|
||||
[160, 236], the interval the Decoder's draw stream independently found the game's
|
||||
clock freezing in.
|
||||
|
||||
### 3. An authored entry of mine was suppressing the decode
|
||||
|
||||
Even then the plate stayed dark, because `authored/timing.json`'s
|
||||
`looping_focus_records` entry for `press_start/ptbtn00` made `_draw` take the
|
||||
focus path — which draws the focus record **instead of** the base sprite:
|
||||
|
||||
| | with the entry | without |
|
||||
|---|---|---|
|
||||
| `press_start` t=236 | max **0** | max **252.5** |
|
||||
| t=240 | max 0 | max 252.5 |
|
||||
| t=250 | max 15.3 | max 252.5 |
|
||||
|
||||
I authored that entry to give the plate a glow. It substituted a dim glow at the
|
||||
wrong phase for the element's own bright sprite, on the screen whose entire
|
||||
content is that sprite. **Deleted** — an authored guess that overrides decoded
|
||||
data with a worse answer is removed, not tuned. The glow is not claimed either
|
||||
way; drawing both would be a rendering rule nobody has measured.
|
||||
|
||||
✅ The boot's end artifact now contains the plate: mean **95.7** in its region
|
||||
against **33.6** for the title art alone, and the overlay reports `drew 1:
|
||||
ptbtn00` where it reported `drew 0`.
|
||||
|
||||
### 4. 🔴 `verify-capture` has been measuring a different pose than it reported
|
||||
|
||||
It passed `--time=5.9617` for the title — t=357.7 units, the Decoder's refined
|
||||
sweep fit — and **that value was never applied**, because of fault 1. Every title
|
||||
figure this tool has printed, including the **0.26 %** I have quoted to the
|
||||
Decoder more than once, was measured at the **settle instant, t=198**, under a
|
||||
note claiming t=357.7.
|
||||
|
||||
Honouring the flag made it visible: t=357.7 is past the title's own group, which
|
||||
ends at t=269, so the whole screen posed at its faded-out final keyframes and the
|
||||
disagreement went to **30.97 %**. The instant was only ever meant for the `ptloop`
|
||||
leaf, which runs to t=600 and is looped separately by `loop_leaf`. Applying it to
|
||||
the whole screen was always wrong and was harmless only while it was ignored.
|
||||
|
||||
The splashes had the same shape: `--time=99` was an idiom for "settled" that
|
||||
worked only because it was discarded. Both rows now pose by omission, and the
|
||||
tool's note says what it does.
|
||||
|
||||
| | before | now |
|
||||
|---|---|---|
|
||||
| `title` | 0.26 % *(labelled t=357.7, actually t=198)* | **0.21 %** at t=198, labelled t=198 |
|
||||
| `publisher_logo` | 0.01 % | 0.01 % |
|
||||
| `developer_logos` | 0.01 % | 0.01 % |
|
||||
| `main_menu` | 0.07 % | 0.08 % |
|
||||
| `extras` | 0.19 % | 0.19 % |
|
||||
|
||||
The agreements were real; the **stated pose was not**. Corrected with the
|
||||
Decoder, since they have those numbers.
|
||||
|
||||
### The flag audit that started it
|
||||
|
||||
All 16 flags `boot.gd` parses were exercised for an observable effect after last
|
||||
iteration's two silent ones. `--pose=rest` (max 111 against the timeline),
|
||||
`--play` (enters the menu), `--no-hold` (max 255 on two screens) all pass. `--time`
|
||||
was the one that did not, and it took a screen whose content is a single late
|
||||
spike to make the failure visible.
|
||||
|
||||
@@ -241,6 +241,8 @@ func _ready() -> void:
|
||||
|
||||
if args.has("time"):
|
||||
_frozen = true
|
||||
# An explicit instant beats the settle instant -- see `ScreenView.frozen`.
|
||||
view.frozen = true
|
||||
view.time_units = float(args["time"]) * view.units_per_second
|
||||
view.queue_redraw()
|
||||
|
||||
@@ -271,6 +273,7 @@ func _ready() -> void:
|
||||
_overlay_due = 0.0
|
||||
_overlay_process(0.0)
|
||||
if overlay != null and args.has("time"):
|
||||
overlay.frozen = true
|
||||
overlay.time_units = float(args["time"]) * overlay.units_per_second
|
||||
overlay.queue_redraw()
|
||||
|
||||
|
||||
@@ -77,6 +77,26 @@ var exit_ramp_units: float = 24.0
|
||||
var settle_instant: float = -1.0
|
||||
const SETTLE_WINDOW_MIN := 30.0
|
||||
|
||||
## Set when a caller pinned an EXPLICIT instant (`--time=`), which then wins over
|
||||
## `settle_instant`.
|
||||
##
|
||||
## 🔴 Without this, `--time=` was silently ignored on every screen with a settle
|
||||
## window of 30 units or more, because `pose_at` overwrote the requested `t` with
|
||||
## `settle_instant` whenever `holding` was true. The flag parsed, the log printed
|
||||
## the time asked for, and the pose came from somewhere else.
|
||||
##
|
||||
## `press_start` is the case that exposed it. Its window is [0, 214] -- the long
|
||||
## dead stretch BEFORE the plate appears -- so its settle instant is t=107, where
|
||||
## `ptbtn00` is alpha 0. The plate's only opaque frames are t=236-238. The result
|
||||
## was that the `PRESS (A)` plate could not be rendered **at any time at all**:
|
||||
## every instant anyone asked for was answered at t=107, and the screen came back
|
||||
## empty with `ptbtn00 (transparent at rest)`.
|
||||
##
|
||||
## The settle instant is still right for a screen that has ARRIVED and is sitting
|
||||
## there, which is what it was measured for. It is not right as an answer to a
|
||||
## question about a different instant.
|
||||
var frozen := false
|
||||
|
||||
var looping_focus: Dictionary = {}
|
||||
|
||||
## Element ids whose nested `.rat` leaf the runtime actually draws.
|
||||
@@ -225,7 +245,9 @@ func pose_at(element: Dictionary, t: float) -> Dictionary:
|
||||
return frames[0] if not frames.is_empty() else element.get("rest", {})
|
||||
# While holding, stop at the hold: past it the group is ramping out, and a
|
||||
# screen that has arrived and is sitting there is not leaving.
|
||||
if holding:
|
||||
# `frozen` means a caller pinned an EXPLICIT instant and wants THAT instant,
|
||||
# not the settled pose and not a per-element clamp. Both clamps are skipped.
|
||||
if holding and not frozen:
|
||||
# One instant for the whole screen where the disc gives a wide enough
|
||||
# window; otherwise each element's own hold, which is what this port did
|
||||
# everywhere until 2026-08-29.
|
||||
|
||||
@@ -95,10 +95,36 @@ for row in "${MAP[@]}"; do
|
||||
"--shots=$OUT/$name" >"$OUT/$name.log" 2>&1 || true
|
||||
[ -f "$OUT/${name}_00_start.png" ] && cp "$OUT/${name}_00_start.png" "$OUT/$name.render.png"
|
||||
elif [ "$pose" = t357 ]; then
|
||||
godot --path port --resolution 1280x720 -- "--screen=$name" --time=5.9617 \
|
||||
# 🔴 NO `--time` HERE EITHER, and the row's note used to claim otherwise.
|
||||
#
|
||||
# It passed `--time=5.9617` (t=357.7 units, the Decoder's refined sweep fit)
|
||||
# and that value was NEVER APPLIED: `pose_at` replaced it with the screen's
|
||||
# settle instant, t=198, on every run. Every title figure this tool has ever
|
||||
# printed -- including the 0.26 % the port has quoted repeatedly -- was
|
||||
# measured at the SETTLE, under a note saying t=357.7.
|
||||
#
|
||||
# Honouring it now makes that visible: t=357.7 is PAST the title's own group,
|
||||
# which ends at t=269, so the whole screen poses at its faded-out final
|
||||
# keyframes and the disagreement goes to 30.97 %. The instant was only ever
|
||||
# meant for the `ptloop` LEAF, which runs to t=600 and is looped separately
|
||||
# by `loop_leaf` (authored/rendering.json). Applying it to the whole screen
|
||||
# was always wrong; it was harmless only while it was ignored.
|
||||
#
|
||||
# So: pose at the settle, which is what was actually being measured, and let
|
||||
# the leaf loop carry the sweeps' phase.
|
||||
godot --path port --resolution 1280x720 -- "--screen=$name" \
|
||||
"--capture=$OUT/$name.render.png" >"$OUT/$name.log" 2>&1 || true
|
||||
else
|
||||
godot --path port --resolution 1280x720 -- "--screen=$name" --time=99 \
|
||||
# NO `--time`. It used to pass `--time=99` as an idiom for "settled", and
|
||||
# that worked only because `--time` was SILENTLY IGNORED on a screen with a
|
||||
# settle window: `pose_at` overwrote the requested instant with
|
||||
# `settle_instant` whenever `holding` was true. The tool asked for t=5940
|
||||
# units and was handed the settle instant, which is the pose it actually
|
||||
# wants -- and the 0.01 % agreements on both splashes were measured through
|
||||
# that accident. Now that `--time` is honoured, asking for it explicitly
|
||||
# would pose past the end of every group, so the request is simply dropped
|
||||
# and the settled pose asked for by omission.
|
||||
godot --path port --resolution 1280x720 -- "--screen=$name" \
|
||||
"--capture=$OUT/$name.render.png" >"$OUT/$name.log" 2>&1 || true
|
||||
fi
|
||||
[ -f "$OUT/$name.render.png" ] || { printf '%-17s %s\n' "$name" "render failed"; continue; }
|
||||
@@ -109,7 +135,7 @@ for row in "${MAP[@]}"; do
|
||||
note=""
|
||||
case "$name" in
|
||||
main_menu|extras) note="rendered with authored initial focus" ;;
|
||||
title) note="posed at t=357.7, the Decoder's refined sweep fit" ;;
|
||||
title) note="posed at the SETTLE (t=198); see the note in this file" ;;
|
||||
esac
|
||||
# Where the difference lives. This comes FIRST because it is what the gamma
|
||||
# sweep has to be protected from.
|
||||
|
||||
Reference in New Issue
Block a user