use macroquad::{ audio::{PlaySoundParams, Sound, load_sound_from_bytes, play_sound, stop_sound}, prelude::{FilterMode, Image, Texture2D}, }; pub struct Assets { pub active_table: Texture2D, pub inactive_table: Texture2D, pub loading: Texture2D, pub loading_progress: Texture2D, pub highscore_background: Texture2D, pub help: [Texture2D; 5], pub player_effects: [Texture2D; 8], pub media: [Texture2D; 4], pub diamond: [Texture2D; 9], pub wheel: Texture2D, pub panel_pair_narrow_a: Texture2D, pub panel_pair_narrow_b: Texture2D, pub panel_pair_wide_a: Texture2D, pub panel_pair_wide_b: Texture2D, pub panel_target: Texture2D, pub robot: Texture2D, pub plunger: 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 loading_progress = texture(include_bytes!("../assets/original/images/dat_00994.png")); let highscore_background = texture(include_bytes!("../assets/original/images/dat_00993.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 player_effects = [ texture(include_bytes!("../assets/original/images/dat_00400.png")), texture(include_bytes!("../assets/original/images/dat_00401.png")), texture(include_bytes!("../assets/original/images/dat_00402.png")), texture(include_bytes!("../assets/original/images/dat_00403.png")), texture(include_bytes!("../assets/original/images/dat_00404.png")), texture(include_bytes!("../assets/original/images/dat_00405.png")), texture(include_bytes!("../assets/original/images/dat_00406.png")), texture(include_bytes!("../assets/original/images/dat_00407.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_bytes = include_bytes!("../assets/original/images/dat_00600.png"); let wheel = texture(wheel_bytes); let panel_pair_narrow_a = masked_atlas_pair(wheel_bytes, 0, 90, 45, 90, 45, 90); let panel_pair_narrow_b = masked_atlas_pair(wheel_bytes, 90, 90, 135, 90, 45, 90); let panel_pair_wide_a = masked_atlas_pair(wheel_bytes, 271, 90, 362, 90, 91, 90); let panel_pair_wide_b = masked_atlas_pair(wheel_bytes, 453, 90, 362, 90, 91, 90); let panel_target = masked_atlas_pair(wheel_bytes, 455, 0, 455, 17, 17, 17); let robot = texture(include_bytes!("../assets/original/images/dat_00900.png")); let plunger = 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, loading_progress, highscore_background, help, player_effects, media, diamond, wheel, panel_pair_narrow_a, panel_pair_narrow_b, panel_pair_wide_a, panel_pair_wide_b, panel_target, robot, plunger, 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, }, ); } } pub fn stop_all_sounds(&self) { for (_, sound) in &self.sounds { stop_sound(sound); } } } 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 } fn masked_atlas_pair( bytes: &[u8], image_x: usize, image_y: usize, mask_x: usize, mask_y: usize, width: usize, height: usize, ) -> Texture2D { let atlas = Image::from_file_with_format(bytes, None).expect("embedded DAT600 must decode"); let atlas_width = usize::from(atlas.width); let mut output = Image { bytes: vec![0; width * height * 4], width: u16::try_from(width).unwrap_or(0), height: u16::try_from(height).unwrap_or(0), }; for y in 0..height { for x in 0..width { let output_index = (y * width + x) * 4; let image_index = ((y + image_y) * atlas_width + image_x + x) * 4; let mask_index = ((y + mask_y) * atlas_width + mask_x + x) * 4; output.bytes[output_index..output_index + 3] .copy_from_slice(&atlas.bytes[image_index..image_index + 3]); output.bytes[output_index + 3] = u8::MAX - atlas.bytes[mask_index]; } } let texture = Texture2D::from_image(&output); texture.set_filter(FilterMode::Nearest); texture }