High-DPI sizing and a resizable client allowed the 640x460 canvas to be placed inside a much taller window, producing the large internal black band captured in the gameplay recording. Open an aspect-correct fixed 960x690 client with high-DPI framebuffer scaling disabled, giving every platform a predictable 1.5x canvas. Use exact active-table patches for the cyan score fields and derive transparent numeral glyphs from the recovered Win16 bitmap font. Fill the complete display slots, move the KBytes readout clear of its border, and place shortcuts on a readable footer so no dynamic text is clipped or left over an inactive panel. Test Plan: - `cargo fmt -- --check` -- passed - `cargo test --all-targets` -- passed - `cargo clippy --all-targets -- -D warnings` -- passed - launched the Linux window and confirmed a 960x690 client with no letterbox, full-width cyan fields, recovered score digits, and readable footer -- passed - `git diff --check` -- passed
203 lines
7.3 KiB
Rust
203 lines
7.3 KiB
Rust
use macroquad::{
|
|
audio::{PlaySoundParams, Sound, load_sound_from_bytes, play_sound},
|
|
prelude::{FilterMode, Image, Texture2D},
|
|
};
|
|
|
|
pub struct Assets {
|
|
pub active_table: Texture2D,
|
|
pub inactive_table: Texture2D,
|
|
pub loading: Texture2D,
|
|
pub help: [Texture2D; 5],
|
|
pub media: [Texture2D; 4],
|
|
pub diamond: [Texture2D; 9],
|
|
pub wheel: Texture2D,
|
|
pub robot: Texture2D,
|
|
pub magnet: Texture2D,
|
|
pub ball: Texture2D,
|
|
pub digits: Texture2D,
|
|
sounds: Vec<(u16, Sound)>,
|
|
}
|
|
|
|
impl Assets {
|
|
#[allow(clippy::too_many_lines)]
|
|
pub async fn load() -> Self {
|
|
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 help = [
|
|
texture(include_bytes!("../assets/original/images/dat_01001.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_01002.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_01003.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_01004.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_01005.png")),
|
|
];
|
|
let media = [
|
|
texture(include_bytes!("../assets/original/images/dat_00701.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00702.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00703.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00704.png")),
|
|
];
|
|
let diamond = [
|
|
texture(include_bytes!("../assets/original/images/dat_00801.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00802.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00803.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00804.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00805.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00806.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00807.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00808.png")),
|
|
texture(include_bytes!("../assets/original/images/dat_00809.png")),
|
|
];
|
|
let wheel = texture(include_bytes!("../assets/original/images/dat_00600.png"));
|
|
let robot = texture(include_bytes!("../assets/original/images/dat_00900.png"));
|
|
let magnet = texture(include_bytes!("../assets/original/images/dat_00901.png"));
|
|
let ball = masked_texture(
|
|
include_bytes!("../assets/original/images/bitmap_00101.png"),
|
|
include_bytes!("../assets/original/images/bitmap_00102.png"),
|
|
);
|
|
let digits =
|
|
monochrome_texture(include_bytes!("../assets/original/images/bitmap_00500.png"));
|
|
|
|
let sound_bytes: [(u16, &[u8]); 16] = [
|
|
(
|
|
2001,
|
|
include_bytes!("../assets/original/audio/wav_02001.wav"),
|
|
),
|
|
(
|
|
2002,
|
|
include_bytes!("../assets/original/audio/wav_02002.wav"),
|
|
),
|
|
(
|
|
2004,
|
|
include_bytes!("../assets/original/audio/wav_02004.wav"),
|
|
),
|
|
(
|
|
2006,
|
|
include_bytes!("../assets/original/audio/wav_02006.wav"),
|
|
),
|
|
(
|
|
2007,
|
|
include_bytes!("../assets/original/audio/wav_02007.wav"),
|
|
),
|
|
(
|
|
2008,
|
|
include_bytes!("../assets/original/audio/wav_02008.wav"),
|
|
),
|
|
(
|
|
2011,
|
|
include_bytes!("../assets/original/audio/wav_02011.wav"),
|
|
),
|
|
(
|
|
2012,
|
|
include_bytes!("../assets/original/audio/wav_02012.wav"),
|
|
),
|
|
(
|
|
2013,
|
|
include_bytes!("../assets/original/audio/wav_02013.wav"),
|
|
),
|
|
(
|
|
2015,
|
|
include_bytes!("../assets/original/audio/wav_02015.wav"),
|
|
),
|
|
(
|
|
2016,
|
|
include_bytes!("../assets/original/audio/wav_02016.wav"),
|
|
),
|
|
(
|
|
2017,
|
|
include_bytes!("../assets/original/audio/wav_02017.wav"),
|
|
),
|
|
(
|
|
2019,
|
|
include_bytes!("../assets/original/audio/wav_02019.wav"),
|
|
),
|
|
(
|
|
2020,
|
|
include_bytes!("../assets/original/audio/wav_02020.wav"),
|
|
),
|
|
(
|
|
2021,
|
|
include_bytes!("../assets/original/audio/wav_02021.wav"),
|
|
),
|
|
(
|
|
2022,
|
|
include_bytes!("../assets/original/audio/wav_02022.wav"),
|
|
),
|
|
];
|
|
let mut sounds = Vec::with_capacity(sound_bytes.len());
|
|
for (id, bytes) in sound_bytes {
|
|
if let Ok(sound) = load_sound_from_bytes(bytes).await {
|
|
sounds.push((id, sound));
|
|
}
|
|
}
|
|
|
|
Self {
|
|
active_table,
|
|
inactive_table,
|
|
loading,
|
|
help,
|
|
media,
|
|
diamond,
|
|
wheel,
|
|
robot,
|
|
magnet,
|
|
ball,
|
|
digits,
|
|
sounds,
|
|
}
|
|
}
|
|
|
|
pub fn play(&self, id: u16, enabled: bool) {
|
|
if !enabled {
|
|
return;
|
|
}
|
|
if let Some((_, sound)) = self.sounds.iter().find(|(sound_id, _)| *sound_id == id) {
|
|
play_sound(
|
|
sound,
|
|
PlaySoundParams {
|
|
looped: false,
|
|
volume: 0.72,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn texture(bytes: &[u8]) -> Texture2D {
|
|
let texture = Texture2D::from_file_with_format(bytes, None);
|
|
texture.set_filter(FilterMode::Nearest);
|
|
texture
|
|
}
|
|
|
|
fn masked_texture(color_bytes: &[u8], mask_bytes: &[u8]) -> Texture2D {
|
|
let mut color =
|
|
Image::from_file_with_format(color_bytes, None).expect("embedded color bitmap must decode");
|
|
let mask =
|
|
Image::from_file_with_format(mask_bytes, None).expect("embedded mask bitmap must decode");
|
|
assert_eq!((color.width, color.height), (mask.width, mask.height));
|
|
|
|
let (color_pixels, color_remainder) = color.bytes.as_chunks_mut::<4>();
|
|
let (mask_pixels, mask_remainder) = mask.bytes.as_chunks::<4>();
|
|
assert!(color_remainder.is_empty() && mask_remainder.is_empty());
|
|
for (pixel, mask_pixel) in color_pixels.iter_mut().zip(mask_pixels) {
|
|
pixel[3] = u8::MAX - mask_pixel[0];
|
|
}
|
|
let texture = Texture2D::from_image(&color);
|
|
texture.set_filter(FilterMode::Nearest);
|
|
texture
|
|
}
|
|
|
|
fn monochrome_texture(bytes: &[u8]) -> Texture2D {
|
|
let mut image =
|
|
Image::from_file_with_format(bytes, None).expect("embedded monochrome bitmap must decode");
|
|
let (pixels, remainder) = image.bytes.as_chunks_mut::<4>();
|
|
assert!(remainder.is_empty());
|
|
for pixel in pixels {
|
|
pixel[3] = u8::MAX - pixel[0];
|
|
pixel[..3].fill(u8::MAX);
|
|
}
|
|
let texture = Texture2D::from_image(&image);
|
|
texture.set_filter(FilterMode::Nearest);
|
|
texture
|
|
}
|