port: implement the decoded leaf composition -- and it does not close the 1.82%
The Decoder decoded the rule I refused to guess: draw the leaf on its own timeline, do NOT multiply the parent's alpha in. Multiplying is refuted rather than unsupported -- at the fitted time the parent has expired, so leaf x parent predicts zero for both quads and the sweeps would be invisible. They are drawn. Implemented: `_draw_leaf` runs the leaf unclamped, like the spinning ring and for the same reason -- held at its own rest.t the leaf sits at x=1521, entirely off the right edge, so `holding` would delete the sweeps rather than settle them. AND IT CHANGES NOTHING MEASURABLE. The title is still 1.82% against the oracle: 1.82 at t=261, 1.81 at t=355, 1.79 at t=420. At t=355 my interpolation puts the leaf's top-left at x ~ -324, off-screen left, where the Decoder's model puts the quad's CENTRE at 981. Those cannot both be right, and it is not something to tune away -- it is a disagreement about how the leaf's keyframes become a placed quad, most likely in the pivot and the rotation about it. Handed back with both numbers. So: the exporter no longer drops the data, the composition rule is implemented as decoded, and the port's largest oracle gap is exactly where it was. Fixing the export was necessary and not sufficient. TWO FLAGGED ELEMENTS DELIBERATELY NOT DRAWN, in authored/rendering.json with reasons. title_jp/ptlogo_eff2 (parent 125%, leaf 100%) is the same shape and is the element DECISIONS has recorded since P1 as the largest render disagreement -- but the Decoder said plainly "I have not tested it", and drawing it would extend a decode past the case it was fitted on. pgloading_loop5's leaf is scale (0,0), and scale-0 is one of the three historical failures this corpus names. Neither can be adjudicated here: title_jp has no oracle capture, and verify-screen compares against a renderer that draws no leaves at all, so ANY leaf drawing increases that divergence whether right or wrong. Its max went 155 -> 232 when they were drawn, and that number is not evidence in either direction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
@@ -182,6 +182,9 @@ func _ready() -> void:
|
||||
# own logic on purpose -- see `looping_focus` there for the census that says
|
||||
# this cannot be a rule.
|
||||
_looping = timing.get("looping_focus_records", {})
|
||||
# Which decoded rules apply where. See `authored/rendering.json`.
|
||||
var rendering: Variant = export_tree.authored("rendering.json")
|
||||
_draw_leaf_for = [] if rendering == null else rendering.get("draw_leaf_for", [])
|
||||
# `--no-hold` plays a screen's groups PAST their rest instead of clamping each
|
||||
# element at its own `rest.t`. A diagnostic, not a mode: `rest.t` is the last
|
||||
# HOLD keyframe before the exit, not the settled state, and the only way to
|
||||
@@ -197,6 +200,7 @@ func _ready() -> void:
|
||||
viewport.add_child(view)
|
||||
|
||||
view.looping_focus = _looping_for(name)
|
||||
view.draw_leaf_for = _draw_leaf_for
|
||||
if not view.load_screen(export_tree, name):
|
||||
push_error(export_tree.error)
|
||||
get_tree().quit(2)
|
||||
@@ -260,6 +264,8 @@ var _play := false
|
||||
var _pending: Variant = null
|
||||
## `authored/timing.json` `looping_focus_records`, keyed `<screen>/<element>`.
|
||||
var _looping: Dictionary = {}
|
||||
## `authored/rendering.json` `draw_leaf_for`.
|
||||
var _draw_leaf_for: Array = []
|
||||
var _script: PackedStringArray = PackedStringArray()
|
||||
var _shots := ""
|
||||
## `--skip-at=SECONDS`: when to send a synthetic (A) during a movie, or 0.
|
||||
@@ -360,6 +366,7 @@ func _advance() -> void:
|
||||
view.holding = true
|
||||
view.time_units = 0.0
|
||||
view.looping_focus = _looping_for(name)
|
||||
view.draw_leaf_for = _draw_leaf_for
|
||||
if not view.load_screen(view.tree, name):
|
||||
push_error(view.tree.error)
|
||||
get_tree().quit(2)
|
||||
@@ -562,6 +569,7 @@ func _menu_arrive() -> void:
|
||||
view.holding = true
|
||||
view.time_units = 0.0
|
||||
view.looping_focus = _looping_for(name)
|
||||
view.draw_leaf_for = _draw_leaf_for
|
||||
if not view.load_screen(view.tree, name):
|
||||
push_error(view.tree.error)
|
||||
get_tree().quit(2)
|
||||
@@ -876,6 +884,7 @@ func _raise_overlay(name: String) -> void:
|
||||
overlay.units_per_second = view.units_per_second
|
||||
overlay.exit_ramp_units = view.exit_ramp_units
|
||||
overlay.looping_focus = _looping_for(name)
|
||||
overlay.draw_leaf_for = _draw_leaf_for
|
||||
overlay.holding = true
|
||||
overlay.time_units = 0.0
|
||||
if not overlay.load_screen(view.tree, name):
|
||||
|
||||
@@ -58,6 +58,17 @@ var exit_ramp_units: float = 24.0
|
||||
## rule's clothes.
|
||||
var looping_focus: Dictionary = {}
|
||||
|
||||
## Element ids whose nested `.rat` leaf the runtime actually draws.
|
||||
##
|
||||
## The exporter flags `leaf_carries_geometry` on 15 elements -- a census fact.
|
||||
## This is narrower on purpose: it is the subset the DECODE covers, and it comes
|
||||
## from `authored/rendering.json` with its reasons. Two elements are flagged and
|
||||
## deliberately not drawn (`title_jp/ptlogo_eff2`, `pgloading_loop5`), because
|
||||
## drawing them would extend a decode past the case it was fitted on and neither
|
||||
## can be adjudicated here -- `title_jp` has no oracle capture, and the
|
||||
## consistency harness compares against a renderer that draws no leaves at all.
|
||||
var draw_leaf_for: Array = []
|
||||
|
||||
## While true the screen holds at `rest` and never plays its exit. The
|
||||
## sequencer clears it to send the screen away.
|
||||
var holding: bool = true
|
||||
@@ -99,6 +110,8 @@ func _load_textures() -> void:
|
||||
var paths: Array = [element.get("sprite", ""), element.get("focus_sprite", "")]
|
||||
# The focus record's own elements carry their own sprites -- the ring is
|
||||
# only reachable this way.
|
||||
for fe: Dictionary in element.get("leaf", {}).get("elements", []):
|
||||
paths.append(fe.get("sprite", ""))
|
||||
for fe: Dictionary in element.get("focus", {}).get("elements", []):
|
||||
paths.append(fe.get("sprite", ""))
|
||||
for rel: String in paths:
|
||||
@@ -367,6 +380,54 @@ static func _rot_of(pose: Dictionary) -> float:
|
||||
## The label is 13 px larger per axis than the base and sits at (-7,-7), which
|
||||
## keeps the two concentric; drawing it at the base position pushes it 7 px
|
||||
## down-right and off-centre.
|
||||
## Draw an element's nested `.rat` leaf INSTEAD of the element itself.
|
||||
##
|
||||
## Only when the exporter flagged `leaf_carries_geometry` -- 15 elements, where
|
||||
## the leaf's scale or rotation differs from the parent's. Everywhere else the
|
||||
## leaf duplicates the parent and the parent wins, which is what this port has
|
||||
## always done and which `screen.rs` documents for base records.
|
||||
##
|
||||
## ⚠️ **The leaf runs on its OWN timeline and the parent's alpha is NOT
|
||||
## multiplied in.** That is decoded, not assumed, and multiplying is refuted
|
||||
## rather than merely unsupported: the game's own composed alpha is observable in
|
||||
## the per-draw capture's vertex colours (`C3FFFFFF` / `B6FFFFFF` = 195 and 182),
|
||||
## and fitting only those two numbers against the two leaf ramps gives one
|
||||
## consistent time, t=355 -- leaf A 194.8 against 195, leaf B 182.2 against 182.
|
||||
## At t=355 the PARENT has expired: its group returns to 0 at t=250 and holds
|
||||
## there, so `leaf x parent / 255` predicts zero for both quads and the sweeps
|
||||
## would be invisible. They are drawn.
|
||||
##
|
||||
## The check that matters was PREDICTED, not fitted: no x entered it, and the
|
||||
## same t=355 places the quad centres at 981 and 478 against 992.0 and 467.2
|
||||
## measured off the capture -- ~11 px on quads travelling 1 560 and 1 950 px.
|
||||
##
|
||||
## ❔ Every observation behind this has parent alpha 0, so "the leaf wins" and
|
||||
## "the parent is ignored because it draws nothing" are NOT separated. A capture
|
||||
## during t=100...238 would separate them.
|
||||
func _draw_leaf(element: Dictionary) -> void:
|
||||
for fe: Dictionary in element.get("leaf", {}).get("elements", []):
|
||||
var rel: String = fe.get("sprite", "")
|
||||
if rel == "":
|
||||
continue
|
||||
var tex: Texture2D = textures.get(rel)
|
||||
if tex == null:
|
||||
skipped.append("%s (leaf sprite failed to load)" % fe.get("id", ""))
|
||||
continue
|
||||
# UNCLAMPED, like the spinning ring and for the same reason: a sweep that
|
||||
# crosses the frame does not stop because the screen has arrived, and
|
||||
# `ORACLE-CAPTURES.md` says these two "move continuously". Held at its own
|
||||
# `rest.t` the leaf sits at x=1521 -- entirely off the right edge -- so
|
||||
# `holding` would delete the sweeps rather than settle them.
|
||||
var was := holding
|
||||
holding = false
|
||||
var pose := pose_at(fe, time_units)
|
||||
holding = was
|
||||
var pivot := _vec(fe.get("pivot", [0, 0]))
|
||||
_draw_quad(tex, placement(pose, pivot, tex.get_size()), modulate_of(pose),
|
||||
pivot, _vec(pose.get("pos", [0, 0])), _rot_of(pose))
|
||||
drawn.append(fe.get("id", ""))
|
||||
|
||||
|
||||
func _draw_focus(element: Dictionary) -> void:
|
||||
var focus: Dictionary = element.get("focus", {})
|
||||
var parent_id := String(element.get("id", ""))
|
||||
@@ -443,6 +504,13 @@ func _draw() -> void:
|
||||
var pivot := _vec(element.get("pivot", [0, 0]))
|
||||
var pos := _vec(pose.get("pos", [0, 0]))
|
||||
var rot := _rot_of(pose)
|
||||
# An element whose LEAF carries the geometry draws the leaf instead of
|
||||
# itself: the parent is a container whose own record has identity scale
|
||||
# and rotation. See `_draw_leaf`.
|
||||
if element.get("leaf_carries_geometry", false) \
|
||||
and draw_leaf_for.has(String(element.get("id", ""))):
|
||||
_draw_leaf(element)
|
||||
continue
|
||||
# A focused button draws its own record instead of its base sprite -- and
|
||||
# so does an element the authored table says always shows it, which is
|
||||
# how the PRESS (A) plate gets its glow on a screen that has no buttons
|
||||
|
||||
Reference in New Issue
Block a user