diff options
feat: More text, looping music optional, additional cosin invite screen
-rw-r--r-- | Cargo.toml | 6 | ||||
-rwxr-xr-x | build-entry | 4 | ||||
-rw-r--r-- | build.rs | 2 | ||||
-rw-r--r-- | src/main.rs | 9 | ||||
-rw-r--r-- | src/music.rs | 26 | ||||
-rw-r--r-- | src/render.rs | 89 |
6 files changed, 124 insertions, 12 deletions
@@ -32,3 +32,9 @@ music = [] 32k = [] # Use Stereo Audio stereo = ["music"] +# Use Logical Size; forces internal render resolution of 512x256 +logical = [] +# Launch in fullscreen +fullscreen = [] +# Loop the music +loop_music = ["music"] diff --git a/build-entry b/build-entry new file mode 100755 index 0000000..228bc4f --- /dev/null +++ b/build-entry @@ -0,0 +1,4 @@ +#!/bin/sh +set -eax +RUSTFLAGS="-Zlocation-detail=none" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features="optimize_for_size" -Z build-std-features=panic_immediate_abort -r --features logical --features resizable +upx --brute target/release/cosin-2025-invite-deck @@ -191,7 +191,7 @@ use crate::font::BakedFont; FontMetadata { name: "Galmuri", font: { Font::try_from_vec(fs::read("assets/fonts/Galmuri11.ttf")?).unwrap() }, - charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz29053: %,/", + charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz219053: %,/-.:#@", scale: Scale::uniform(20.0), }, FontMetadata { diff --git a/src/main.rs b/src/main.rs index 24dcf8d..9f4ddc0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,6 +48,7 @@ pub fn main() { pcm: mmc2r_to_pcm( &mut MmC2r::new(include_bytes!("../uwudhd.mod"), spec.freq as isize).unwrap(), ), + playing: true, } }) .unwrap(); @@ -58,15 +59,21 @@ pub fn main() { let video_subsystem = sdl_context.video().unwrap(); - let mut builder = video_subsystem.window("cosin25-invite.rs", 512, 256); + let mut builder = video_subsystem.window("cosin25-invite.rs", 1280 / 2, 800 / 2); builder.position_centered(); + #[cfg(feature = "fullscreen")] + builder.fullscreen_desktop(); #[cfg(feature = "resizable")] builder.resizable(); let window = &mut builder.build().unwrap(); + window.set_minimum_size(1280 / 2, 800 / 2).unwrap(); let mut canvas = window.clone().into_canvas().build().unwrap(); let texture_creator = canvas.texture_creator(); + #[cfg(feature = "logical")] + canvas.set_logical_size(1280 / 2, 800 / 2).unwrap(); + render::render(&mut canvas, &texture_creator, 0.001); canvas.present(); diff --git a/src/music.rs b/src/music.rs index 888de77..7cae5eb 100644 --- a/src/music.rs +++ b/src/music.rs @@ -8,6 +8,7 @@ use crate::vendored::micromod::MmC2r; pub struct Music { pub pcm: Vec<i16>, pub offset: usize, + pub playing: bool, } #[cfg(all(feature = "music", not(feature = "32k")))] @@ -15,13 +16,24 @@ impl AudioCallback for Music { type Channel = f32; fn callback(&mut self, out_f: &mut [f32]) { - let len = out_f.len(); - let slice = &self.pcm[self.offset..(self.offset + len).min(self.pcm.len() - 1)]; - let len = slice.len(); - pcm_to_f32pcm(&slice, out_f, len); - self.offset += len; - if self.offset > self.pcm.len() { - self.offset = 0; + if self.playing { + let len = out_f.len(); + let end_of_slice = (self.offset + len).min(self.pcm.len() - 1); + let slice = &self.pcm[self.offset..end_of_slice]; + let len = slice.len(); + pcm_to_f32pcm(&slice, out_f, len); + self.offset += len; + if end_of_slice == self.pcm.len() - 1 { + self.offset = 0; + #[cfg(not(feature = "loop_music"))] + { + self.playing = false; + } + } + } else { + for i in 0..out_f.len() - 1 { + out_f[i] = 0.0; + } } } } diff --git a/src/render.rs b/src/render.rs index daf3658..d37545a 100644 --- a/src/render.rs +++ b/src/render.rs @@ -31,7 +31,9 @@ pub const START_BOUNCE: f64 = 0.5; pub const START_SIN: f64 = 12.0; pub const START_COMETOCOSIN: f64 = 18.0; #[cfg(not(feature = "32k"))] -pub const SCENE_GREETINGS: f64 = 30.0; +pub const SCENE_MOAR: f64 = 30.0; +#[cfg(not(feature = "32k"))] +pub const SCENE_GREETINGS: f64 = SCENE_MOAR + 33.0; #[cfg(not(feature = "32k"))] pub const JUST_DVD: f64 = SCENE_GREETINGS + 28.0; @@ -52,13 +54,17 @@ pub fn render( colourthing as u8 }; + #[cfg(not(feature = "logical"))] let win_size = canvas.window().drawable_size(); + #[cfg(feature = "logical")] + let win_size = canvas.logical_size(); canvas.set_draw_color(Color::RGB(12, 12, 12)); canvas.clear(); #[cfg(not(feature = "32k"))] - let is_dvd = time_seconds < SCENE_GREETINGS || time_seconds >= JUST_DVD; + let is_dvd = + (time_seconds < SCENE_GREETINGS && time_seconds < SCENE_MOAR) || time_seconds >= JUST_DVD; #[cfg(feature = "32k")] let is_dvd = true; @@ -122,11 +128,88 @@ pub fn render( #[cfg(feature = "32k")] let no_other_rendering = false; #[cfg(not(feature = "32k"))] + let is_moar_scene = time_seconds >= SCENE_MOAR && time_seconds < SCENE_GREETINGS; + #[cfg(feature = "32k")] + let is_moar_scene = false; + #[cfg(not(feature = "32k"))] let is_greetings_scene = time_seconds >= SCENE_GREETINGS; #[cfg(feature = "32k")] let is_greetings_scene = false; if no_other_rendering { // + } else if is_moar_scene { + #[cfg(not(feature = "32k"))] + { + let time_seconds = time_seconds - SCENE_MOAR; + // Greetings + let greeting_header_duration = 2.0; + { + let mut rng = rand::thread_rng(); + + let mut i = 0.0; + let strlen = 19; + let mut uwuoffset_x = 0.0; + for c in "Have some scrollers".chars() { + let char: crate::font::RenderableCharacter = FONT_GALMURI.get_char(c); + canvas + .copy( + &char + .to_texture( + texture_creator, + Color::RGB(colourthing, 64, 255 - colourthing), + ) + .unwrap(), + None, + char.to_rect( + uwuoffset_x as i32 + 18 + rng.gen_range(-2..2), + (24) + rng.gen_range(-3..3), + ), + ) + .unwrap(); + uwuoffset_x += char.advance_width; + i += 1.0; + if i > (time_seconds * (f64::from(strlen) / greeting_header_duration)).floor() { + break; + } + } + let mut offset_y = 36; + for line in [ + "Pls come to CoSin 2025 Credits", + " May 29th - June 1st 2025 UwU :3 ceemos", + " Villa Ritter 30CHF, paid on-site memdmp", + " Biel/Bienne https://cosin.ch fence", + " Switzerland /dev/urandom", + "", + "", + "", + " Matrix: #cosin:fairydust.space Mastodon: @CoSin@mastodon.social" + ] { + offset_y += 24; + let mut offset_x: f64 = f64::from(win_size.0) + - ((time_seconds - greeting_header_duration) * (f64::from(win_size.0) * 0.078125)); + + for c in line.chars() { + let char = FONT_GALMURI.get_char(c); + canvas + .copy( + &char + .to_texture( + texture_creator, + Color::RGB(colourthing, 64, 255 - colourthing), + ) + .unwrap(), + None, + char.to_rect( + offset_x as i32 + rng.gen_range(-2..2), + offset_y + rng.gen_range(-2..2), + ), + ) + .unwrap(); + offset_x += char.advance_width; + } + } + } + } } else if is_greetings_scene { #[cfg(not(feature = "32k"))] { @@ -317,7 +400,7 @@ pub fn render( f[idx + 3] = if out_of_frame { 0 } else if sin_y < y as usize { - let v: u16 = if idx % 5 == 0 { 255 } else { 122 }; + let v: u16 = if idx % 7 == 0 { 255 } else { 122 }; let v = if (sin_y + 3) < (y as usize) { v } else { |