use crate::font::BakedFont;
use crate::generated::fonts::{FONT_CHERRY_BOMB_ONE, FONT_GALMURI};
use rand::distributions::uniform::SampleRange;
use rand::Rng;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::rect::Rect;
use sdl2::render::{Canvas, Texture, TextureCreator};
use sdl2::surface::Surface;
use sdl2::video::{Window, WindowContext};
use std::ops::DerefMut;
fn dvd_logo_offset(t: f64, screen_size_x: f64, screen_size_y: f64) -> (f64, f64) {
let offset_x = t % (screen_size_x * 2.0);
let offset_x = if offset_x > screen_size_x {
screen_size_x * 2.0 - offset_x
} else {
offset_x
};
let offset_y = t % (screen_size_y * 2.0);
let offset_y = if offset_y > screen_size_y {
screen_size_y * 2.0 - offset_y
} else {
offset_y
};
(offset_x, offset_y)
}
fn render_chars_with_rng<R>(
canvas: &mut Canvas<Window>,
texture_creator: &TextureCreator<WindowContext>,
str: impl Iterator<Item = char>,
font: impl BakedFont,
range_x: &R,
range_y: &R,
offset_x: &mut f64,
offset_y: &i32,
colour: Color,
rng: &mut impl Rng,
) where
R: SampleRange<i32> + Clone,
{
let mut rng = rng;
for c in str {
let char = font.get_char(c);
canvas
.copy(
&char.to_texture(texture_creator, colour).unwrap(),
None,
char.to_rect(
offset_x.clone() as i32 + rng.gen_range(range_x.clone()),
offset_y.clone() + rng.gen_range(range_y.clone()),
),
)
.unwrap();
*offset_x += char.advance_width;
}
}
fn render_chars_without_rng(
canvas: &mut Canvas<Window>,
texture_creator: &TextureCreator<WindowContext>,
str: impl Iterator<Item = char>,
font: impl BakedFont,
offset_x: &mut f64,
offset_y: &i32,
colour: Color,
) {
for c in str {
let char = font.get_char(c);
canvas
.copy(
&char.to_texture(texture_creator, colour).unwrap(),
None,
char.to_rect(offset_x.clone() as i32, offset_y.clone()),
)
.unwrap();
*offset_x += char.advance_width;
}
}
pub const START_UWUSPACE: f64 = 0.0;
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_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 + 27.0;
pub const PRINT_TIME: bool = true;
pub fn render(
canvas: &mut Canvas<Window>,
texture_creator: &TextureCreator<WindowContext>,
time_seconds: f64,
) {
if PRINT_TIME {
println!("Time: {time_seconds}\x1b[F");
}
let colourthing = ((time_seconds * 60.0) % 510.0).round();
let colourthing = if colourthing > 255.0 {
(255.0 - (colourthing - 255.0)) as u8
} else {
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 < SCENE_MOAR) || time_seconds >= JUST_DVD;
#[cfg(feature = "32k")]
let is_dvd = true;
if is_dvd {
#[cfg(not(feature = "32k"))]
let time_seconds = if time_seconds >= JUST_DVD {
time_seconds - JUST_DVD + 15.0
} else {
time_seconds
};
if time_seconds >= START_UWUSPACE {
let bounce_speed = 90.0;
let padding_x = 16.0;
let padding_y = 16.0;
let (offset_x, offset_y) = if time_seconds > START_BOUNCE {
let mut uwu_width = padding_x;
let mut uwu_height: f32 = padding_y;
for c in "UwU-Space".chars() {
let char = FONT_CHERRY_BOMB_ONE.get_char(c);
uwu_width += char.advance_width;
let nh = f32::from(char.height) + padding_y;
if nh > uwu_height {
uwu_height = nh;
}
}
let virtual_screen_size = (
f64::from(win_size.0) - (uwu_width + padding_x),
f64::from(win_size.1) - f64::from(uwu_height + padding_y),
);
let t = (time_seconds - START_BOUNCE) * bounce_speed;
let (offset_x, offset_y) = dvd_logo_offset(t, virtual_screen_size.0, virtual_screen_size.1);
(
(padding_x + offset_x).round() as i32,
(f64::from(padding_y) + offset_y).round() as i32,
)
} else {
(padding_x.round() as i32, padding_y.round() as i32)
};
let mut offset: f64 = 0.0;
render_chars_without_rng(
canvas,
texture_creator,
"UwU-Space".chars(),
FONT_CHERRY_BOMB_ONE,
&mut f64::from(offset_x),
&offset_y,
Color::RGB(colourthing, 64, 255 - colourthing),
);
}
}
#[cfg(not(feature = "32k"))]
let no_other_rendering = time_seconds >= JUST_DVD;
#[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 = 18.0;
let str = {
if time_seconds + 0.75 >= (SCENE_GREETINGS - SCENE_MOAR) {
"oh look a 64k rust demo"
} else {
"Have some scrollers: x3"
}
};
let str = str.chars().collect::<Vec<char>>();
let str = &str[0..((time_seconds * (f64::from(strlen) / greeting_header_duration)).floor()
as usize)
.min(str.len())];
render_chars_with_rng(
canvas,
texture_creator,
str.iter().map(|v| *v),
FONT_GALMURI,
&(-2..2),
&(-3..3),
&mut uwuoffset_x,
&24,
Color::RGB(colourthing, 64, 255 - colourthing),
&mut rng,
);
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));
render_chars_with_rng(canvas, texture_creator, line.chars(), FONT_GALMURI, &(-2..2), &(-2..2), &mut offset_x,&offset_y, Color::RGB(colourthing, 64, 255 - colourthing), &mut rng);
}
}
}
} else if is_greetings_scene {
#[cfg(not(feature = "32k"))]
{
let time_seconds = time_seconds - SCENE_GREETINGS;
// Greetings
let greeting_header_duration = 4.0;
let colour = Color::RGB(colourthing, 64, 255 - colourthing);
let mut rng = rand::thread_rng();
{
let mut i = 0.0;
let strlen = 43;
let mut uwuoffset_x = 0.0;
let str = "Greetings by UwU-Space to:";
let str = str.chars().collect::<Vec<char>>();
let str = &str[0..{
let f = time_seconds * (f64::from(strlen) / greeting_header_duration);
let f = f.floor() as usize;
f.min(str.len())
}];
for c in str {
// we dont use the abstraction funcs due to the inline uwu-space
let is_not_uwu = i <= 12.0 || i >= 22.0;
i += 1.0;
let char = if is_not_uwu {
FONT_GALMURI.get_char(*c)
} else {
FONT_CHERRY_BOMB_ONE.get_char(*c)
};
canvas
.copy(
&char.to_texture(texture_creator, colour).unwrap(),
None,
char.to_rect(
uwuoffset_x as i32 + 18 + rng.gen_range(-2..2),
(if is_not_uwu { 24 } else { 16 }) + rng.gen_range(-3..3),
),
)
.unwrap();
uwuoffset_x += char.advance_width;
}
let mut offset_y = 36;
let mut line_index = 0;
for line in [
vec!["Venty", "Erdit", "Kaede", "dui"],
vec!["Deja", "vimja", "cy", "alu"],
vec!["sashu", "expired bread", "gaben"],
vec!["Shana", "psykon", "Unlock"],
] {
offset_y += 24;
let mut offset_x = 18.0
+ ((f64::from(line_index) * 90.0) + f64::from(win_size.0)
- ((time_seconds - greeting_header_duration) * (f64::from(win_size.0) * 0.078125)));
line_index += 1;
render_chars_with_rng(
canvas,
texture_creator,
line.join(" ").chars(),
FONT_GALMURI,
&(-2..2),
&(-2..2),
&mut offset_x,
&offset_y,
colour,
&mut rng,
);
}
}
// We did most of this at mountainbytes
render_chars_with_rng(
canvas,
texture_creator,
"sorry for shit invite we have adhd".chars(),
FONT_GALMURI,
&(-2..2),
&(-2..2),
&mut 18.0,
&(win_size.1 as i32 - 96),
colour,
&mut rng,
);
render_chars_with_rng(
canvas,
texture_creator,
"we did 90% onsite at mountainbytes 2025,".chars(),
FONT_GALMURI,
&(-2..2),
&(-2..2),
&mut 18.0,
&(win_size.1 as i32 - 64),
colour,
&mut rng,
);
render_chars_with_rng(
canvas,
texture_creator,
"the rest 2 months ago".chars(),
FONT_GALMURI,
&(-2..2),
&(-2..2),
&mut 18.0,
&(win_size.1 as i32 - 32),
colour,
&mut rng,
);
}
} else {
if time_seconds >= START_SIN {
let time_seconds = time_seconds - START_SIN;
let base_sin_offset = time_seconds * 0.1;
let sin_offset = base_sin_offset - 0.75;
let mut sin_surface = Surface::new(win_size.0, win_size.1, PixelFormatEnum::RGBA32).unwrap();
let w = win_size.0;
let h = win_size.1;
let f64_w = f64::from(w);
let f64_h = f64::from(h);
let f = &mut sin_surface.deref_mut().without_lock_mut().unwrap();
let min_x_pos = (if base_sin_offset > 1.0 {
0.0
} else {
1.0 - base_sin_offset
}) * f64_w;
for x in 0..w {
let f64_x = f64::from(x);
let out_of_frame = f64_x < min_x_pos;
let sin_x = {
let mut sin_x = f64_x + (sin_offset * f64_w);
if sin_x > f64_w {
sin_x -= f64_w;
}
sin_x
};
let sin_y =
((f64::sin(sin_x * (3.141 * 2.0) / f64_w) + 1.0) * (f64_h / 2.0)).floor() as usize;
// let sin_idx = (sin_y * w as usize + x as usize) * 4;
for y in 0..h {
let idx = (y * w + x) as usize * 4;
let cx = x * 512 / w;
f[idx] = (122 - (cx / 8)) as u8;
f[idx + 1] = (255 - (cx / 2)) as u8;
f[idx + 2] = (cx / 2) as u8;
f[idx + 3] = if out_of_frame {
0
} else if sin_y < y as usize {
let v: u16 = if idx % 7 == 0 { 255 } else { 122 };
let v = if (sin_y + 3) < (y as usize) {
v
} else {
v * 2 / 3
};
v as u8
} else {
0
};
}
}
let sin_texture = Texture::from_surface(&sin_surface, texture_creator).unwrap();
canvas
.copy(&sin_texture, None, Rect::new(0, 0, win_size.0, win_size.1))
.unwrap();
}
if time_seconds >= START_COMETOCOSIN {
let time_seconds = time_seconds - START_COMETOCOSIN;
let wrap_width = f64::from(win_size.0);
let mut offset = (18.0 + (time_seconds * 32.0)) % wrap_width;
// World's shittiest marquee implementation
for char in "Come to Cosin25 :3".chars() {
let char = FONT_GALMURI.get_char(char);
canvas
.copy(
&char
.to_texture(
texture_creator,
Color::RGB(colourthing, 64, 255 - colourthing),
)
.unwrap(),
None,
char.to_rect(offset as i32, win_size.1 as i32 - 32),
)
.unwrap();
offset += char.advance_width;
if offset > wrap_width {
offset -= wrap_width;
}
}
}
}
}