fix(ui): restore staged loading progress
Correct raw 1000:531e's Pascal-order BitBlt to destination (202,328), height 13, and width (stage-1)*7 from original DAT994. Render stages 2 through 35 over DAT995, add deterministic loading screenshots, and update the C harness and evidence ledger for the corrected 237x13 source. Test Plan: - bash original/tools/test_reconstructed_c.sh - python3 original/tools/audit_reconstruction.py --require-complete - cargo test --all-targets - cargo clippy --all-targets --all-features -- -D warnings - rumdl check original/C_RECONSTRUCTION_FINAL_AUDIT.md tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md tdkpin-rs/README.md - cargo run --quiet -- --simulate loading --at 0.55 --screenshot /tmp/tdkpin-loading-mid.png - git diff --check
This commit is contained in:
+45
-2
@@ -113,6 +113,7 @@ pub struct App {
|
||||
return_screen: Screen,
|
||||
game: Option<Game>,
|
||||
setting_row: usize,
|
||||
loading_started: f64,
|
||||
loading_until: f64,
|
||||
last_help_click: f64,
|
||||
message: String,
|
||||
@@ -129,6 +130,7 @@ impl App {
|
||||
let saved = persistence.load();
|
||||
let render_target = render_target(640, 460);
|
||||
render_target.texture.set_filter(FilterMode::Nearest);
|
||||
let loading_started = get_time();
|
||||
Self {
|
||||
assets,
|
||||
persistence,
|
||||
@@ -138,7 +140,8 @@ impl App {
|
||||
return_screen: Screen::Attract,
|
||||
game: None,
|
||||
setting_row: 0,
|
||||
loading_until: get_time() + 1.1,
|
||||
loading_started,
|
||||
loading_until: loading_started + 1.1,
|
||||
last_help_click: -1.0,
|
||||
message: String::new(),
|
||||
message_until: 0.0,
|
||||
@@ -182,6 +185,14 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_simulation_loading(&mut self, steps: u64) {
|
||||
self.game = None;
|
||||
self.screen = Screen::Loading;
|
||||
let step = u32::try_from(steps).unwrap_or(u32::MAX);
|
||||
self.loading_started = get_time() - f64::from(step) / 120.0;
|
||||
self.loading_until = get_time() + 60.0;
|
||||
}
|
||||
|
||||
pub fn render_simulation(&self) {
|
||||
self.draw_logical();
|
||||
}
|
||||
@@ -431,7 +442,7 @@ impl App {
|
||||
clear_background(BLACK);
|
||||
|
||||
match self.screen {
|
||||
Screen::Loading => draw_texture(&self.assets.loading, 0.0, 0.0, WHITE),
|
||||
Screen::Loading => self.draw_loading(),
|
||||
Screen::Help => {
|
||||
let index = Language::ALL
|
||||
.iter()
|
||||
@@ -525,6 +536,28 @@ impl App {
|
||||
self.draw_intro_marquee();
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
fn draw_loading(&self) {
|
||||
draw_texture(&self.assets.loading, 0.0, 0.0, WHITE);
|
||||
let stage = Self::loading_stage_at(get_time() - self.loading_started);
|
||||
let width = i32::from(stage.saturating_sub(1)) * 7;
|
||||
draw_texture_region(
|
||||
&self.assets.loading_progress,
|
||||
202.0,
|
||||
328.0,
|
||||
width.min(237) as f32,
|
||||
13.0,
|
||||
0.0,
|
||||
0.0,
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn loading_stage_at(elapsed: f64) -> u8 {
|
||||
let progress = (elapsed / 1.1).clamp(0.0, 0.999_999);
|
||||
u8::try_from(2 + (progress * 34.0).floor() as i32).unwrap_or(35)
|
||||
}
|
||||
|
||||
fn draw_intro_marquee(&self) {
|
||||
let Some(source_x) = self.attract.marquee_source_x() else {
|
||||
return;
|
||||
@@ -1211,6 +1244,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loading_progress_covers_original_stages_two_through_thirty_five() {
|
||||
assert_eq!(App::loading_stage_at(-1.0), 2);
|
||||
assert_eq!(App::loading_stage_at(0.0), 2);
|
||||
assert_eq!(App::loading_stage_at(1.1 / 34.0), 3);
|
||||
assert_eq!(App::loading_stage_at(1.09), 35);
|
||||
assert_eq!(App::loading_stage_at(99.0), 35);
|
||||
assert_eq!(i32::from(App::loading_stage_at(99.0) - 1) * 7, 238);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attract_phases_follow_the_reconstructed_tick_quotients() {
|
||||
let at = |tick| AttractAnimation {
|
||||
|
||||
@@ -7,6 +7,7 @@ pub struct Assets {
|
||||
pub active_table: Texture2D,
|
||||
pub inactive_table: Texture2D,
|
||||
pub loading: Texture2D,
|
||||
pub loading_progress: Texture2D,
|
||||
pub help: [Texture2D; 5],
|
||||
pub media: [Texture2D; 4],
|
||||
pub diamond: [Texture2D; 9],
|
||||
@@ -29,6 +30,7 @@ impl Assets {
|
||||
let active_table = texture(include_bytes!("../assets/original/images/dat_00997.png"));
|
||||
let inactive_table = texture(include_bytes!("../assets/original/images/dat_00998.png"));
|
||||
let loading = texture(include_bytes!("../assets/original/images/dat_00995.png"));
|
||||
let loading_progress = texture(include_bytes!("../assets/original/images/dat_00994.png"));
|
||||
let help = [
|
||||
texture(include_bytes!("../assets/original/images/dat_01001.png")),
|
||||
texture(include_bytes!("../assets/original/images/dat_01002.png")),
|
||||
@@ -146,6 +148,7 @@ impl Assets {
|
||||
active_table,
|
||||
inactive_table,
|
||||
loading,
|
||||
loading_progress,
|
||||
help,
|
||||
media,
|
||||
diamond,
|
||||
|
||||
@@ -60,10 +60,10 @@ async fn run_simulation(request: Request) -> Result<(), String> {
|
||||
}
|
||||
if let Some(path) = &request.screenshot {
|
||||
let mut app = App::load().await;
|
||||
if request.scenario == Scenario::Attract {
|
||||
app.set_simulation_attract(request.target_step);
|
||||
} else {
|
||||
app.set_simulation_game(simulation.game().clone());
|
||||
match request.scenario {
|
||||
Scenario::Loading => app.set_simulation_loading(request.target_step),
|
||||
Scenario::Attract => app.set_simulation_attract(request.target_step),
|
||||
_ => app.set_simulation_game(simulation.game().clone()),
|
||||
}
|
||||
app.render_simulation();
|
||||
next_frame().await;
|
||||
|
||||
@@ -13,6 +13,7 @@ const MAX_SIMULATION_STEPS: u64 = 72_000;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Scenario {
|
||||
Loading,
|
||||
Attract,
|
||||
Autoplay,
|
||||
Launcher,
|
||||
@@ -28,6 +29,7 @@ pub enum Scenario {
|
||||
impl Scenario {
|
||||
pub const fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Loading => "loading",
|
||||
Self::Attract => "attract",
|
||||
Self::Autoplay => "autoplay",
|
||||
Self::Launcher => "launcher",
|
||||
@@ -47,7 +49,8 @@ impl Scenario {
|
||||
Self::Claw6 => Some(6),
|
||||
Self::Claw7 => Some(7),
|
||||
Self::Claw18 => Some(18),
|
||||
Self::Attract
|
||||
Self::Loading
|
||||
| Self::Attract
|
||||
| Self::Autoplay
|
||||
| Self::Launcher
|
||||
| Self::Flippers
|
||||
@@ -62,6 +65,7 @@ impl FromStr for Scenario {
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
match value {
|
||||
"loading" => Ok(Self::Loading),
|
||||
"attract" => Ok(Self::Attract),
|
||||
"autoplay" => Ok(Self::Autoplay),
|
||||
"launcher" => Ok(Self::Launcher),
|
||||
@@ -73,7 +77,7 @@ impl FromStr for Scenario {
|
||||
"claw-7" => Ok(Self::Claw7),
|
||||
"claw-18" => Ok(Self::Claw18),
|
||||
_ => Err(format!(
|
||||
"unknown scenario {value:?}; expected attract, autoplay, launcher, flippers, panel, targets, claw-1, claw-6, claw-7, or claw-18"
|
||||
"unknown scenario {value:?}; expected loading, attract, autoplay, launcher, flippers, panel, targets, claw-1, claw-6, claw-7, or claw-18"
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -157,7 +161,7 @@ fn seconds_to_step(value: &str) -> Result<u64, String> {
|
||||
}
|
||||
|
||||
pub const fn usage() -> &'static str {
|
||||
"Usage:\n tdkpin-rs\n tdkpin-rs --simulate SCENARIO [--at SECONDS | --step N] [--screenshot FILE.png] [--trace FILE.json] [--seed N]\n\nScenarios: attract, autoplay, launcher, flippers, panel, targets, claw-1, claw-6, claw-7, claw-18"
|
||||
"Usage:\n tdkpin-rs\n tdkpin-rs --simulate SCENARIO [--at SECONDS | --step N] [--screenshot FILE.png] [--trace FILE.json] [--seed N]\n\nScenarios: loading, attract, autoplay, launcher, flippers, panel, targets, claw-1, claw-6, claw-7, claw-18"
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
@@ -238,7 +242,7 @@ impl Simulation {
|
||||
} else {
|
||||
controls_for(self.scenario, self.step)
|
||||
};
|
||||
let events = if self.scenario == Scenario::Attract {
|
||||
let events = if matches!(self.scenario, Scenario::Loading | Scenario::Attract) {
|
||||
Vec::new()
|
||||
} else {
|
||||
self.game.update(SIMULATION_DT, 3, controls)
|
||||
@@ -332,7 +336,7 @@ impl Simulation {
|
||||
|
||||
fn controls_for(scenario: Scenario, step: u64) -> Controls {
|
||||
match scenario {
|
||||
Scenario::Attract => Controls::default(),
|
||||
Scenario::Loading | Scenario::Attract => Controls::default(),
|
||||
Scenario::Autoplay => unreachable!("autoplay controls need mutable simulation state"),
|
||||
Scenario::Launcher => Controls {
|
||||
launch_down: step < u64::from(SIMULATION_HZ),
|
||||
|
||||
Reference in New Issue
Block a user