Let multiplier countdown timeline play between HUD events

This commit is contained in:
chazmm committed 2026-09-08 18:54:32 +01:00
1 parent d70de2625d
commit 553c4ddc93
3 files changed
+80 -11

No files matched your search

+39 -1
View File
@@ -38,6 +38,9 @@ fn main() -> Result<(), String> {
serde_json::from_value(json["shapes"].clone()).map_err(|e| e.to_string())?;
let mut max_draws = 0;
let mut saw_native_shadow_pair = false;
let mut glow_alphas = std::collections::BTreeSet::new();
let mut flicker_alphas = std::collections::BTreeSet::new();
let mut previous_timer_frame = None;
for tick in 0..1800 {
let new_trick = tick % 180 == 10;
if new_trick {
@@ -54,7 +57,11 @@ fn main() -> Result<(), String> {
input.clean = tick % 360 == 10;
input.sketchy = !input.clean;
input.line_time = 8.0;
input.multiplier = if input.multiplier == 1.5 { 2.0 } else { 1.5 };
input.multiplier = match (tick / 180) % 3 {
0 => 1.5,
1 => 2.0,
_ => 3.0,
};
}
input.line_time = (input.line_time - 1.0 / 60.0).max(0.0);
input.sequence_timer = input.line_time as i32;
@@ -69,6 +76,34 @@ fn main() -> Result<(), String> {
}
}
let draws = apt_scene::draw(&runtime.bindings.movie, &runtime.vm, &shapes)?;
for (&id, instance) in &runtime.bindings.movie.instances {
let name = instance.placement.as_ref().map(|p| p.name.as_str());
if name == Some("multiTimer_mc") && (20..40).contains(&tick) {
if let Some(previous) = previous_timer_frame {
if instance.frame != previous + 1 {
return Err(
"Line timer was re-seeked instead of playing consecutive frames".into(),
);
}
}
previous_timer_frame = Some(instance.frame);
}
if input.multiplier == 3.0 {
let alpha = runtime.vm.get(id, "_alpha").number();
if name == Some("mBlueGlow") {
if !(80.0..100.0).contains(&alpha) {
return Err("Blue glow outside authored flicker range".into());
}
glow_alphas.insert(alpha.to_bits());
}
if name == Some("mFlicker") {
if !(90.0..100.0).contains(&alpha) {
return Err("Multiplier flicker outside authored range".into());
}
flicker_alphas.insert(alpha.to_bits());
}
}
}
max_draws = max_draws.max(draws.len());
saw_native_shadow_pair |= draws.windows(2).any(|pair| {
pair[0].texture.contains("futurashadow")
@@ -88,6 +123,9 @@ fn main() -> Result<(), String> {
if !saw_native_shadow_pair {
return Err("Missing native black shadow / heavy foreground pair".into());
}
if glow_alphas.len() < 4 || flicker_alphas.len() < 4 {
return Err("Original x3 blue glow/flicker clips did not animate".into());
}
println!(
"Original HUD action audit passed 1800 authored frames; {} object slots, {} display instances",
runtime.vm.objects.len(),
+14 -7
View File
@@ -236,13 +236,20 @@ impl Runtime {
.call_method(self.controller, method, vec![], &mut self.bindings)?;
self.drain()?;
}
self.vm.call_method(
self.controller,
"UpdateLineDisplay",
vec![],
&mut self.bindings,
)?;
self.drain()?;
// The movie handles this as event 11 (0196CC), not onEnterFrame.
// Re-seeking the 501-frame multiTimer_mc on every tick pins it to
// the same whole-second sample instead of letting gotoAndPlay run.
if previous.line_time.trunc() != self.bindings.input.line_time.trunc()
|| previous.line_score != self.bindings.input.line_score
{
self.vm.call_method(
self.controller,
"UpdateLineDisplay",
vec![],
&mut self.bindings,
)?;
self.drain()?;
}
self.bindings.movie.advance(&mut self.vm)?;
self.drain()?;
self.vm
+27 -3
View File
@@ -98,8 +98,8 @@ The copied build and launcher are in ignored `logs/scoring/build`. The launcher
uses this worktree's HUD cache and the existing owned asset installation and
starts paused. It checks required paths before running and retains failures on
screen. The executable SHA256 is
`6853a0035cb3a8693681e574f1fae5829f2caec694657536b92217c6d8f7c63b`.
The launcher now selects `skate3rust-hud-score-fix.exe`.
`59c7c876d289c58e9661f669274fa4faa4e50a7bf51085461bcf33f2fc1bd809`.
The launcher now selects `skate3rust-hud-animation.exe`.
The missing-HUD startup defect is fixed: HUD setup now depends on presentation
setup, so Bevy applies the deferred camera spawn before the HUD queries it.
@@ -188,7 +188,31 @@ preparation order. An asset-only regression test checks that an unchanged
target emits no Modified event and a resize invalidates both image and material.
The test uses no window, renderer or gameplay systems.
## Native parity gaps
## Multiplier timeline playback
The host previously called `UpdateLineDisplay` every fixed tick. The shipped
action at 019C39 computes `floor(GetLineTimeRemaining() * 60) - 30`, clamps
it, and seeks `multiTimer_mc` with `gotoAndPlay` at 019CF4. Since the native
binding returns whole seconds, repeated calls pin the 501-frame timer to the
same frame for a second, then jump it forward. The ActionScript dispatches
this method for event11 (0196CC) and ScreenShow, rather than onEnterFrame.
The adapter now dispatches when the exposed timer sample or line score changes,
allowing the authored timeline to advance between samples. This is an adapter
event policy; the entire native HUD event producer is not yet ported.
Original x3 effect clips are retained: character59 changes mBlueGlow alpha
with `80 + Math.random() * 20` every four frames; character70 changes mFlicker
with `90 + Math.random() * 10`. Original multiplier labels select the glow
and background timelines (characters66 and77); no replacement particles,
colors or universal x3 effect were added to lower multiplier levels.
The supplied-data movie audit now visits x1.5, x2 and x3 over 1,800 frames,
asserts consecutive timer frames between events, checks both flicker ranges
and changing alpha values, and reaches 40 authored draw batches. The scoring
flow audit still passes landing persistence, line expiry and cancellation.
No game or GPU capture was launched.
## Remaining native parity gaps
These are implementation gaps, not merely missing gameplay validation: