From 761a3172bf4c15aed3836e4d1cd6f815b651b0e0 Mon Sep 17 00:00:00 2001 From: LLLL Colonq Date: Sat, 29 Aug 2026 04:06:51 -0400 Subject: Fix --- .../games_submissions/asrael_io/source/src/anim.rs | 42 -- .../asrael_io/source/src/bullet.rs | 118 ---- .../asrael_io/source/src/color.rs | 66 --- .../asrael_io/source/src/enemy.rs | 174 ------ 2026/games_submissions/asrael_io/source/src/gfx.rs | 113 ---- .../games_submissions/asrael_io/source/src/main.rs | 642 --------------------- .../games_submissions/asrael_io/source/src/math.rs | 13 - .../asrael_io/source/src/player.rs | 96 --- 2026/games_submissions/asrael_io/source/src/rng.rs | 35 -- 2026/games_submissions/asrael_io/source/src/sfx.rs | 302 ---------- .../asrael_io/source/src/sprite.rs | 89 --- .../asrael_io/source/src/starfield.rs | 59 -- 12 files changed, 1749 deletions(-) delete mode 100644 2026/games_submissions/asrael_io/source/src/anim.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/bullet.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/color.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/enemy.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/gfx.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/main.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/math.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/player.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/rng.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/sfx.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/sprite.rs delete mode 100644 2026/games_submissions/asrael_io/source/src/starfield.rs (limited to '2026/games_submissions/asrael_io/source/src') diff --git a/2026/games_submissions/asrael_io/source/src/anim.rs b/2026/games_submissions/asrael_io/source/src/anim.rs deleted file mode 100644 index 427e22e..0000000 --- a/2026/games_submissions/asrael_io/source/src/anim.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::color::Palette; -use crate::sprite::Sprite; - -use std::rc::Rc; - -use glam::Vec2; - -const FRAME_TICKS: u32 = 6; - -pub struct Anim { - center: Vec2, - frames: Rc>, - tick: u32, -} - -impl Anim { - pub fn new(frames: Rc>, center: Vec2) -> Self { - Self { - center, - frames, - tick: 0, - } - } - - pub fn done(&self) -> bool { - self.tick >= FRAME_TICKS * self.frames.len() as u32 - } - - pub fn update(&mut self) { - self.tick += 1; - } - - pub fn draw(&self, frame: &mut [u32], palette: &Palette) { - let i = (self.tick / FRAME_TICKS) as usize; - let Some(sprite) = self.frames.get(i) else { - return; - }; - - let pos = self.center - sprite.size.as_vec2() / 2.0; - sprite.draw_at(frame, palette, pos.round().as_ivec2()); - } -} diff --git a/2026/games_submissions/asrael_io/source/src/bullet.rs b/2026/games_submissions/asrael_io/source/src/bullet.rs deleted file mode 100644 index ac44f25..0000000 --- a/2026/games_submissions/asrael_io/source/src/bullet.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::color::Palette; -use crate::color::db32::{LIGHT_RED, LIME}; -use crate::math::aabb; -use crate::sprite::Sprite; -use crate::{GAME_H, GAME_W}; - -use glam::Vec2; - -const ENEMY_BULLET_W: i32 = 2; -const ENEMY_BULLET_H: i32 = 6; -const PLAYER_BULLET_W: i32 = 3; -const PLAYER_BULLET_H: i32 = 6; -const ENEMY_SHOT_SPEED: f32 = 1.5; -const PLAYER_SHOT_SPEED: f32 = 4.0; - -#[rustfmt::skip] -const ENEMY_BULLET_PIXELS: [u8; (ENEMY_BULLET_W * ENEMY_BULLET_H) as usize] = [ - LIME, LIME, - LIME, LIME, - LIME, LIME, - LIME, LIME, - LIME, LIME, - LIME, LIME, -]; - -#[rustfmt::skip] -const PLAYER_BULLET_PIXELS: [u8; (PLAYER_BULLET_W * PLAYER_BULLET_H) as usize] = [ - 0, LIGHT_RED, 0, - LIGHT_RED, LIGHT_RED, LIGHT_RED, - 0, 0, 0, - 0, LIGHT_RED, 0, - 0, LIGHT_RED, 0, - 0, LIGHT_RED, 0, -]; - -pub struct Bullet { - angle: Option, - pos: Vec2, - vel: Vec2, - sprite: Sprite, -} - -impl Bullet { - fn new( - pos: Vec2, - vel: Vec2, - angle: Option, - width: i32, - height: i32, - pixels: &[u8], - ) -> Self { - let sprite = Sprite::new(width, height, pixels.to_vec()); - - Self { - angle, - pos, - vel, - sprite, - } - } - - pub fn aimed(from: Vec2, target: Vec2) -> Self { - let dir = (target - from).normalize_or_zero(); - let vel = dir * ENEMY_SHOT_SPEED; - let pos = from + Vec2::new(-(ENEMY_BULLET_W as f32) / 2.0, 0.0); - let angle = (-dir.x).atan2(dir.y); - - Self::new( - pos, - vel, - Some(angle), - ENEMY_BULLET_W, - ENEMY_BULLET_H, - &ENEMY_BULLET_PIXELS, - ) - } - - pub fn fired(muzzle_pos: Vec2) -> Self { - let pos = - muzzle_pos + Vec2::new(-(PLAYER_BULLET_W as f32) / 2.0, -(PLAYER_BULLET_H as f32)); - - Self::new( - pos, - Vec2::new(0.0, -PLAYER_SHOT_SPEED), - None, - PLAYER_BULLET_W, - PLAYER_BULLET_H, - &PLAYER_BULLET_PIXELS, - ) - } - - pub fn hits(&self, pos: Vec2, size: Vec2) -> bool { - aabb(self.pos, self.sprite.size.as_vec2(), pos, size) - } - - pub fn offscreen(&self) -> bool { - self.pos.x + self.sprite.size.x as f32 <= 0.0 - || self.pos.x >= GAME_W as f32 - || self.pos.y + self.sprite.size.y as f32 <= 0.0 - || self.pos.y >= GAME_H as f32 - } - - pub fn update(&mut self) { - self.pos += self.vel; - } - - pub fn draw(&self, frame: &mut [u32], palette: &Palette, a: f32) { - let p = self.pos - self.vel * (1.0 - a); - - match self.angle { - Some(angle) => { - let center = p + self.sprite.size.as_vec2() / 2.0; - self.sprite.draw_rotated(frame, palette, center, angle, 0); - } - None => self.sprite.draw_at(frame, palette, p.round().as_ivec2()), - } - } -} diff --git a/2026/games_submissions/asrael_io/source/src/color.rs b/2026/games_submissions/asrael_io/source/src/color.rs deleted file mode 100644 index 1db0786..0000000 --- a/2026/games_submissions/asrael_io/source/src/color.rs +++ /dev/null @@ -1,66 +0,0 @@ -pub type Color = u32; - -#[allow(dead_code)] -pub mod db32 { - pub const BLACK: u8 = 0; - pub const DARK_PURPLE: u8 = 1; - pub const MAROON: u8 = 2; - pub const DARK_BROWN: u8 = 3; - pub const BROWN: u8 = 4; - pub const ORANGE: u8 = 5; - pub const TAN: u8 = 6; - pub const PEACH: u8 = 7; - pub const YELLOW: u8 = 8; - pub const LIME: u8 = 9; - pub const GREEN: u8 = 10; - pub const SEA_GREEN: u8 = 11; - pub const DARK_GREEN: u8 = 12; - pub const OLIVE: u8 = 13; - pub const CHARCOAL: u8 = 14; - pub const NAVY: u8 = 15; - pub const STEEL_BLUE: u8 = 16; - pub const INDIGO: u8 = 17; - pub const BLUE: u8 = 18; - pub const CYAN: u8 = 19; - pub const PALE_BLUE: u8 = 20; - pub const WHITE: u8 = 21; - pub const GRAY: u8 = 22; - pub const STONE: u8 = 23; - pub const DIM_GRAY: u8 = 24; - pub const SLATE: u8 = 25; - pub const PURPLE: u8 = 26; - pub const RED: u8 = 27; - pub const LIGHT_RED: u8 = 28; - pub const PINK: u8 = 29; - pub const MOSS: u8 = 30; - pub const KHAKI: u8 = 31; -} - -const fn from_rgb(r: u8, g: u8, b: u8) -> Color { - (r as u32) << 16 | (g as u32) << 8 | b as u32 -} - -#[derive(Clone, Copy)] -pub struct Palette([Color; 256]); - -impl Default for Palette { - fn default() -> Self { - Self([0; 256]) - } -} - -impl Palette { - pub fn at(&self, index: u8) -> Color { - self.0[index as usize] - } - - pub fn from_ase(src: &[aseprite::Color]) -> Self { - let mut out = [0u32; 256]; - - for (dst, s) in out.iter_mut().zip(src) { - *dst = from_rgb(s.r, s.g, s.b); - } - - Palette(out) - } -} diff --git a/2026/games_submissions/asrael_io/source/src/enemy.rs b/2026/games_submissions/asrael_io/source/src/enemy.rs deleted file mode 100644 index 46eabc0..0000000 --- a/2026/games_submissions/asrael_io/source/src/enemy.rs +++ /dev/null @@ -1,174 +0,0 @@ -use crate::color::Palette; -use crate::color::db32::{LIGHT_RED, PINK, PURPLE}; -use crate::gfx; -use crate::math; -use crate::rng::Rng; -use crate::sprite::Sprite; -use crate::{GAME_H, GAME_W}; - -use std::f32::consts::TAU; - -use aseprite::AsepriteFile; -use glam::Vec2; - -const ROT_STEPS: f32 = 16.0; - -#[derive(Default)] -enum State { - Dive { - path: [Vec2; 4], - fired: u32, - shots: u32, - t: f32, - }, - #[default] - Formation, -} - -pub struct Enemy { - base: Vec2, - flash: u32, - hp: u32, - pos: Vec2, - step: Vec2, - sprite: Sprite, - state: State, -} - -impl Enemy { - pub fn new(sprites: &AsepriteFile, layer: &str, base: Vec2, hp: u32) -> Self { - let pos = base; - let step = Vec2::ZERO; - let sprite = Sprite::from_ase(sprites, layer); - let state = State::default(); - - Self { - base, - flash: 0, - hp, - pos, - step, - sprite, - state, - } - } - - pub fn damage(&mut self) -> bool { - self.hp = self.hp.saturating_sub(1); - self.flash = 12; - self.hp == 0 - } - - pub fn center(&self) -> Vec2 { - self.pos + self.size() / 2.0 - } - - pub fn pos(&self) -> Vec2 { - self.pos - } - - pub fn size(&self) -> Vec2 { - self.sprite.size.as_vec2() - } - - pub fn start_dive(&mut self, player_x: f32, shots: u32, rng: &mut Rng) { - if matches!(self.state, State::Dive { .. }) { - return; - } - - let side = if self.pos.x < 70.0 { - 1.0 - } else if self.pos.x > GAME_W as f32 - 70.0 { - -1.0 - } else if rng.chance(0.5) { - 1.0 - } else { - -1.0 - }; - - let min = Vec2::new(4.0, 4.0); - let max = Vec2::new(GAME_W as f32 - 20.0, GAME_H as f32 - 20.0); - let swing = (self.pos + Vec2::new(side * 70.0, -40.0)).clamp(min, max); - let plunge = Vec2::new(player_x, GAME_H as f32 - 20.0).clamp(min, max); - - let path = [self.pos, swing, plunge, self.base]; - - self.state = State::Dive { - path, - fired: 0, - shots, - t: 0.0, - }; - } - - pub fn update(&mut self, sway: f32, player: Vec2) -> Option { - let before = self.pos; - let mut shot = None; - - self.flash = self.flash.saturating_sub(1); - - match &mut self.state { - State::Formation => { - let target = self.base.x + sway; - self.pos.x += (target - self.pos.x) * 0.2; - } - - State::Dive { - path, - fired, - shots, - t, - } => { - *t += 1.0 / 240.0; - - if *t >= 1.0 { - self.pos = self.base; - self.state = State::Formation; - } else { - self.pos = math::bezier(*path, *t); - - let center = self.pos + self.sprite.size.as_vec2() / 2.0; - let facing = self.step.normalize_or_zero(); - let to_player = (player - center).normalize_or_zero(); - - let next = 0.25 + 0.5 * *fired as f32 / *shots as f32; - if *fired < *shots && *t > next && facing.dot(to_player) > 0.8 { - *fired += 1; - shot = Some(center); - } - } - } - } - - self.step = self.pos - before; - shot - } - - pub fn draw(&self, frame: &mut [u32], palette: &Palette, a: f32, tick: u32) { - let pos = self.pos - self.step * (1.0 - a); - let tint = if self.flash > 0 && (tick / 2).is_multiple_of(2) { - LIGHT_RED - } else { - 0 - }; - - if matches!(self.state, State::Dive { .. }) && self.step.length_squared() > 0.001 { - let dir = self.step.normalize(); - let angle = (-dir.x).atan2(dir.y); - let angle = (angle * ROT_STEPS / TAU).round() * (TAU / ROT_STEPS); - let center = pos + self.sprite.size.as_vec2() / 2.0; - self.sprite - .draw_rotated(frame, palette, center, angle, tint); - - let half = self.sprite.size.y as f32 / 2.0; - for i in 0..3 { - let tail = center - dir * (half + 1.0 + i as f32 * 2.0); - let c = [PURPLE, PINK][(tick / 4 + i) as usize % 2]; - gfx::blit(frame, palette, &[c], tail.round().as_ivec2(), 1, 0); - } - } else { - self.sprite - .draw_tinted(frame, palette, pos.round().as_ivec2(), tint); - } - } -} diff --git a/2026/games_submissions/asrael_io/source/src/gfx.rs b/2026/games_submissions/asrael_io/source/src/gfx.rs deleted file mode 100644 index 4f515c1..0000000 --- a/2026/games_submissions/asrael_io/source/src/gfx.rs +++ /dev/null @@ -1,113 +0,0 @@ -use crate::color::Palette; -use crate::{GAME_H, GAME_W}; - -use embedded_graphics::Drawable; -use embedded_graphics::Pixel; -use embedded_graphics::draw_target::DrawTarget; -use embedded_graphics::geometry::{OriginDimensions, Point, Size}; -use embedded_graphics::mono_font::{MonoFont, MonoTextStyle}; -use embedded_graphics::pixelcolor::{Rgb888, RgbColor}; -use embedded_graphics::text::{Baseline, Text}; -use glam::{IVec2, Vec2}; - -pub struct Frame<'a>(pub &'a mut [u32]); - -impl OriginDimensions for Frame<'_> { - fn size(&self) -> Size { - Size::new(GAME_W, GAME_H) - } -} - -impl DrawTarget for Frame<'_> { - type Color = Rgb888; - type Error = core::convert::Infallible; - - fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> - where - I: IntoIterator>, - { - for Pixel(p, color) in pixels { - if p.x < 0 || p.x >= GAME_W as i32 || p.y < 0 || p.y >= GAME_H as i32 { - continue; - } - - self.0[(p.y * GAME_W as i32 + p.x) as usize] = - (color.r() as u32) << 16 | (color.g() as u32) << 8 | color.b() as u32; - } - - Ok(()) - } -} - -pub fn draw_text(frame: &mut [u32], font: &MonoFont, text: &str, pos: IVec2, color: u32) { - let color = Rgb888::new((color >> 16) as u8, (color >> 8) as u8, color as u8); - let style = MonoTextStyle::new(font, color); - - let _ = Text::with_baseline(text, Point::new(pos.x, pos.y), style, Baseline::Top) - .draw(&mut Frame(frame)); -} - -pub fn blit( - frame: &mut [u32], - palette: &Palette, - pixels: &[u8], - origin: IVec2, - width: i32, - tint: u8, -) { - for (i, &px) in pixels.iter().enumerate() { - if px == 0 { - continue; - } - - let sx = origin.x + i as i32 % width; - let sy = origin.y + i as i32 / width; - if sx < 0 || sx >= GAME_W as i32 || sy < 0 || sy >= GAME_H as i32 { - continue; - } - - let px = if tint != 0 { tint } else { px }; - frame[(sy * GAME_W as i32 + sx) as usize] = palette.at(px); - } -} - -pub fn blit_rotated( - frame: &mut [u32], - palette: &Palette, - pixels: &[u8], - center: Vec2, - width: i32, - angle: f32, - tint: u8, -) { - let height = pixels.len() as i32 / width; - let half = Vec2::new(width as f32, height as f32) / 2.0; - let (sin, cos) = angle.sin_cos(); - let r = half.length().ceil() as i32; - - for dy in -r..=r { - for dx in -r..=r { - let x = dx as f32 + 0.5; - let y = dy as f32 + 0.5; - let sx = (cos * x + sin * y + half.x).floor() as i32; - let sy = (-sin * x + cos * y + half.y).floor() as i32; - if sx < 0 || sx >= width || sy < 0 || sy >= height { - continue; - } - - let px = pixels[(sy * width + sx) as usize]; - if px == 0 { - continue; - } - - let fx = center.x.round() as i32 + dx; - let fy = center.y.round() as i32 + dy; - if fx < 0 || fx >= GAME_W as i32 || fy < 0 || fy >= GAME_H as i32 { - continue; - } - - let px = if tint != 0 { tint } else { px }; - frame[(fy * GAME_W as i32 + fx) as usize] = palette.at(px); - } - } -} diff --git a/2026/games_submissions/asrael_io/source/src/main.rs b/2026/games_submissions/asrael_io/source/src/main.rs deleted file mode 100644 index 7e57f8b..0000000 --- a/2026/games_submissions/asrael_io/source/src/main.rs +++ /dev/null @@ -1,642 +0,0 @@ -mod anim; -mod bullet; -mod color; -mod enemy; -mod gfx; -mod math; -mod player; -mod rng; -mod sfx; -mod sprite; -mod starfield; - -use anim::Anim; -use bullet::Bullet; -use color::Palette; -use color::db32::{BLACK, CYAN, LIGHT_RED, LIME, RED, WHITE, YELLOW}; -use enemy::Enemy; -use math::aabb; -use player::Player; -use rng::Rng; -use sfx::Sfx; -use sprite::Sprite; -use starfield::Starfield; - -use std::f32::consts::TAU; -use std::num::NonZeroU32; -use std::rc::Rc; -use std::sync::Arc; -use std::time::Duration; - -use aseprite::AsepriteFile; -use embedded_graphics::mono_font::ascii::{FONT_6X10, FONT_10X20}; -use glam::{IVec2, Vec2}; -use softbuffer::{Context, Surface}; -use web_time::Instant; -use winit::application::ApplicationHandler; -use winit::event::{ElementState, KeyEvent, MouseButton, WindowEvent}; -use winit::event_loop::{ActiveEventLoop, EventLoop}; -use winit::keyboard::{KeyCode, PhysicalKey}; -use winit::window::{Window, WindowId}; - -const MAX_FRAME: Duration = Duration::from_millis(100); -const TICK: Duration = Duration::from_nanos(16_666_667); - -const PLAYER_MOVES: [(Action, IVec2); 4] = [ - (Action::Up, IVec2::new(0, -1)), - (Action::Down, IVec2::new(0, 1)), - (Action::Left, IVec2::new(-1, 0)), - (Action::Right, IVec2::new(1, 0)), -]; - -pub(crate) const GAME_W: u32 = 240; -pub(crate) const GAME_H: u32 = 160; - -const SPRITES: &[u8] = include_bytes!("../assets/sprites.aseprite"); - -const DONE_HOLD: u32 = 120; - -const WIN_CYCLE: [u8; 4] = [LIME, WHITE, CYAN, YELLOW]; -const OVER_CYCLE: [u8; 2] = [RED, LIGHT_RED]; - -#[derive(Default)] -struct Cj2k26 { - accumulator: Duration, - anims: Vec, - cursor: Option, - difficulty: f32, - dive_timer: u32, - done_timer: u32, - enemies: Vec, - enemy_bullets: Vec, - explosion_frames: Rc>, - frame: Vec, - input: u32, - interacted: bool, - last: Option, - state: State, - palette: Palette, - player: Player, - player_bullets: Vec, - rng: Rng, - sfx: Option, - starfield: Starfield, - surface: Option, Arc>>, - tick: u32, - volley_timer: u32, - window: Option>, -} - -#[derive(Clone, Copy)] -enum Action { - Up, - Down, - Left, - Right, - Fire, -} - -#[derive(Default)] -enum State { - #[default] - Waiting, - Playing, - Win, - GameOver, -} - -#[cfg(target_arch = "wasm32")] -mod jam { - use wasm_bindgen::prelude::wasm_bindgen; - - #[wasm_bindgen] - extern "C" { - #[wasm_bindgen(js_name = jamShouldStart)] - pub fn should_start() -> bool; - - #[wasm_bindgen(js_name = jamGetDifficulty)] - pub fn difficulty() -> f32; - - #[wasm_bindgen(js_name = jamStarted)] - pub fn started(verb: &str); - - #[wasm_bindgen(js_name = jamDone)] - pub fn done(win: bool); - } -} - -#[cfg(not(target_arch = "wasm32"))] -mod jam { - pub fn should_start() -> bool { - true - } - - pub fn difficulty() -> f32 { - 0.0 - } - - pub fn started(_verb: &str) {} - - pub fn done(_win: bool) {} -} - -impl Cj2k26 { - fn has_action(&self, action: Action) -> bool { - self.input & (1 << action as u32) != 0 - } - - fn unlock_audio(&mut self) { - if let Some(sfx) = &mut self.sfx { - sfx.resume(); - } - } - - fn update(&mut self) { - self.tick = self.tick.wrapping_add(1); - - if matches!(self.state, State::Waiting) && jam::should_start() { - jam::started("Shoot!"); - self.difficulty = jam::difficulty().min(7.0); - self.state = State::Playing; - } - - if self.input != 0 { - self.interacted = true; - } - - let playing = matches!(self.state, State::Playing); - - self.starfield.update(); - - if playing { - let player_dir = PLAYER_MOVES - .iter() - .filter(|(a, _)| self.has_action(*a)) - .fold(IVec2::ZERO, |acc, (_, v)| acc + *v); - - if player_dir != IVec2::ZERO { - self.cursor = None; - } - - let mut mouse = Vec2::ZERO; - if let Some(cursor) = self.cursor { - let target = cursor - self.player.size() / 2.0; - mouse = target - self.player.pos(); - } - - self.player.update(player_dir, mouse); - } - - let player_center = self.player.pos() + self.player.size() / 2.0; - - if playing && self.interacted && !self.enemies.is_empty() { - let dive_after = (90.0 - self.difficulty * 6.0).max(45.0) as u32; - self.dive_timer += 1; - if self.dive_timer >= dive_after { - self.dive_timer = 0; - - let i = self.rng.range(self.enemies.len() as u32) as usize; - let shots = (2.0 + self.difficulty / 4.0).min(5.0) as u32; - self.enemies[i].start_dive(player_center.x, shots, &mut self.rng); - } - - let volley_after = (70.0 - self.difficulty * 4.0).max(30.0) as u32; - self.volley_timer += 1; - if self.volley_timer >= volley_after { - self.volley_timer = 0; - - let volley = (2.0 + self.difficulty / 3.0).min(5.0) as u32; - for _ in 0..volley { - let i = self.rng.range(self.enemies.len() as u32) as usize; - let enemy = &self.enemies[i]; - let muzzle = enemy.center() + Vec2::new(0.0, enemy.size().y / 2.0); - let spread = Vec2::new((self.rng.f32() - 0.5) * 60.0, 0.0); - - self.enemy_bullets - .push(Bullet::aimed(muzzle, player_center + spread)); - } - - if let Some(sfx) = &self.sfx { - sfx.enemy_shoot(); - } - } - } - - let seconds = self.tick as f32 / 60.0; - let sway = 12.0 * (seconds * TAU / 3.0).sin(); - for enemy in &mut self.enemies { - if let Some(muzzle_pos) = enemy.update(sway, player_center) - && playing - { - self.enemy_bullets - .push(Bullet::aimed(muzzle_pos, self.player.pos())); - if let Some(sfx) = &self.sfx { - sfx.enemy_shoot(); - } - } - } - - if playing - && self.has_action(Action::Fire) - && let Some(muzzle_pos) = self.player.try_fire() - { - if let Some(sfx) = &self.sfx { - sfx.shoot(); - } - - self.player_bullets.push(Bullet::fired(muzzle_pos)); - } - - for bullet in self - .player_bullets - .iter_mut() - .chain(&mut self.enemy_bullets) - { - bullet.update(); - } - - self.player_bullets.retain(|b| !b.offscreen()); - self.enemy_bullets.retain(|b| !b.offscreen()); - - self.player_bullets.retain(|b| { - match self.enemies.iter().position(|e| b.hits(e.pos(), e.size())) { - Some(i) => { - if self.enemies[i].damage() { - let enemy = self.enemies.swap_remove(i); - self.anims - .push(Anim::new(self.explosion_frames.clone(), enemy.center())); - if let Some(sfx) = &self.sfx { - sfx.explode(); - } - } else if let Some(sfx) = &self.sfx { - sfx.hit(); - } - false - } - None => true, - } - }); - - if playing { - let p_pos = self.player.pos(); - let p_size = self.player.size(); - - let mut player_hit = false; - self.enemy_bullets.retain(|b| { - !b.hits(p_pos, p_size) || { - player_hit = true; - false - } - }); - - player_hit |= self - .enemies - .iter() - .any(|e| aabb(p_pos, p_size, e.pos(), e.size())); - - if player_hit { - self.state = State::GameOver; - self.done_timer = DONE_HOLD; - self.anims.push(Anim::new( - self.explosion_frames.clone(), - p_pos + p_size / 2.0, - )); - if let Some(sfx) = &self.sfx { - sfx.explode(); - sfx.lose(); - } - } - } - - if matches!(self.state, State::Playing) && self.enemies.is_empty() { - self.state = State::Win; - self.done_timer = DONE_HOLD; - if let Some(sfx) = &self.sfx { - sfx.win(); - } - } - - if matches!(self.state, State::Win) { - self.player - .win_anim(DONE_HOLD.saturating_sub(self.done_timer)); - } - - if self.done_timer > 0 { - self.done_timer -= 1; - if self.done_timer == 0 { - jam::done(matches!(self.state, State::Win)); - } - } - - for anim in &mut self.anims { - anim.update(); - } - self.anims.retain(|a| !a.done()); - } - - fn draw(&mut self, a: f32) { - self.frame.fill(self.palette.at(BLACK)); - - self.starfield.draw(&mut self.frame, &self.palette, a); - - if !matches!(self.state, State::GameOver) { - self.player - .draw(&mut self.frame, &self.palette, a, self.tick); - } - - for enemy in &self.enemies { - enemy.draw(&mut self.frame, &self.palette, a, self.tick); - } - - for bullet in self.player_bullets.iter().chain(&self.enemy_bullets) { - bullet.draw(&mut self.frame, &self.palette, a); - } - - for anim in &self.anims { - anim.draw(&mut self.frame, &self.palette); - } - - let cycle = (self.tick / 8) as usize; - let banner = match self.state { - State::Win => Some(("YOU WIN", WIN_CYCLE[cycle % WIN_CYCLE.len()])), - State::GameOver => Some(("GAME OVER", OVER_CYCLE[cycle % OVER_CYCLE.len()])), - _ => None, - }; - - if let Some((text, color)) = banner { - let w = text.len() as i32 * FONT_10X20.character_size.width as i32; - let pos = IVec2::new((GAME_W as i32 - w) / 2, (GAME_H as i32 - 20) / 2); - gfx::draw_text( - &mut self.frame, - &FONT_10X20, - text, - pos, - self.palette.at(color), - ); - } - - if !self.interacted && matches!(self.state, State::Waiting | State::Playing) { - let lines = ["Mouse or WASD to Move", "Left Click or Space to Fire"]; - for (i, hint) in lines.iter().enumerate() { - let w = hint.len() as i32 * FONT_6X10.character_size.width as i32; - let y = GAME_H as i32 / 2 + 20 + i as i32 * 12; - let pos = IVec2::new((GAME_W as i32 - w) / 2, y); - gfx::draw_text( - &mut self.frame, - &FONT_6X10, - hint, - pos, - self.palette.at(WHITE), - ); - } - } - - self.present(); - } - - fn present(&mut self) { - let Some(window) = self.window.as_ref() else { - return; - }; - let Some(surface) = self.surface.as_mut() else { - return; - }; - - let size = window.inner_size(); - let (w, h) = (size.width as usize, size.height as usize); - let scale = (w / GAME_W as usize).min(h / GAME_H as usize); - if scale == 0 { - return; - } - - let Ok(mut buffer) = surface.buffer_mut() else { - return; - }; - if buffer.len() != w * h { - return; - } - let out_w = GAME_W as usize * scale; - let x0 = w.saturating_sub(out_w) / 2; - let y0 = h.saturating_sub(GAME_H as usize * scale) / 2; - - buffer.fill(0); - - let mut row = vec![0u32; out_w]; - for gy in 0..GAME_H as usize { - let src = &self.frame[gy * GAME_W as usize..(gy + 1) * GAME_W as usize]; - for (i, &c) in src.iter().enumerate() { - row[i * scale..(i + 1) * scale].fill(c); - } - - for sy in 0..scale { - let start = (y0 + gy * scale + sy) * w + x0; - buffer[start..start + out_w].copy_from_slice(&row); - } - } - - let _ = buffer.present(); - } -} - -impl ApplicationHandler for Cj2k26 { - fn resumed(&mut self, event_loop: &ActiveEventLoop) { - let attributes = Window::default_attributes().with_title("cj2k26"); - - #[cfg(not(target_arch = "wasm32"))] - let attributes = { - use winit::dpi::LogicalSize; - - let size = LogicalSize::new(GAME_W * 4, GAME_H * 4); - attributes.with_inner_size(size).with_min_inner_size(size) - }; - - #[cfg(target_arch = "wasm32")] - let attributes = { - use winit::platform::web::WindowAttributesExtWebSys; - attributes.with_append(true) - }; - - let window = Arc::new( - event_loop - .create_window(attributes) - .expect("failed to create window"), - ); - window.set_cursor_visible(false); - - #[cfg(not(target_arch = "wasm32"))] - { - use winit::window::CursorGrabMode; - let _ = window - .set_cursor_grab(CursorGrabMode::Confined) - .or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked)); - } - - let context = Context::new(window.clone()).expect("failed to create softbuffer context"); - let mut surface = Surface::new(&context, window.clone()).expect("failed to create surface"); - - let inner = window.inner_size(); - if let (Some(w), Some(h)) = (NonZeroU32::new(inner.width), NonZeroU32::new(inner.height)) { - let _ = surface.resize(w, h); - } - - self.frame = vec![0; (GAME_W * GAME_H) as usize]; - self.surface = Some(surface); - - let sprites = AsepriteFile::from_reader(SPRITES).expect("failed to read sprites aseprite!"); - - self.enemies = (0..8) - .map(|i| { - let hp = if i % 3 == 0 { 2 } else { 1 }; - Enemy::new( - &sprites, - "enemy", - Vec2::new(24.0 + i as f32 * 24.0, 24.0), - hp, - ) - }) - .collect(); - - self.explosion_frames = Rc::new(Sprite::frames_from_ase(&sprites, "explosion")); - self.palette = Palette::from_ase(sprites.palette()); - self.player = Player::new(&sprites, 2.0); - self.rng = Rng::default(); - self.sfx = Some(Sfx::new()); - self.starfield = Starfield::new(60, &mut self.rng); - self.window = Some(window.clone()); - - window.request_redraw(); - } - - fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { - match event { - WindowEvent::CloseRequested => event_loop.exit(), - - WindowEvent::Resized(size) => { - if let Some(surface) = self.surface.as_mut() - && let (Some(w), Some(h)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - let _ = surface.resize(w, h); - } - } - - WindowEvent::RedrawRequested => { - let now = Instant::now(); - let dt = self - .last - .replace(now) - .map(|prev| (now - prev).min(MAX_FRAME)) - .unwrap_or_default(); - self.accumulator += dt; - - while self.accumulator >= TICK { - self.update(); - self.accumulator -= TICK; - } - - let alpha = self.accumulator.as_secs_f32() / TICK.as_secs_f32(); - self.draw(alpha); - - #[cfg(not(target_arch = "wasm32"))] - std::thread::sleep(TICK.saturating_sub(now.elapsed())); - - if let Some(window) = self.window.as_ref() { - window.request_redraw(); - } - } - - WindowEvent::KeyboardInput { - event: - KeyEvent { - physical_key: PhysicalKey::Code(code), - state, - .. - }, - .. - } => { - self.unlock_audio(); - - if let Some(action) = bind_key(code) { - let bit = 1 << action as u32; - - match state { - ElementState::Pressed => self.input |= bit, - ElementState::Released => self.input &= !bit, - } - } - } - - WindowEvent::CursorMoved { position, .. } => { - if let Some(window) = self.window.as_ref() { - let size = window.inner_size(); - let (w, h) = (size.width as i32, size.height as i32); - let scale = (w / GAME_W as i32).min(h / GAME_H as i32); - - if scale > 0 { - let x0 = (w - GAME_W as i32 * scale) / 2; - let y0 = (h - GAME_H as i32 * scale) / 2; - let gx = (position.x as i32 - x0) as f32 / scale as f32; - let gy = (position.y as i32 - y0) as f32 / scale as f32; - - self.cursor = Some(Vec2::new(gx, gy)); - if matches!(self.state, State::Playing) { - self.interacted = true; - } - } - } - } - - WindowEvent::MouseInput { - state, - button: MouseButton::Left, - .. - } => { - self.unlock_audio(); - - let bit = 1 << Action::Fire as u32; - match state { - ElementState::Pressed => self.input |= bit, - ElementState::Released => self.input &= !bit, - } - } - - WindowEvent::MouseInput { .. } | WindowEvent::KeyboardInput { .. } => { - self.unlock_audio(); - } - - _ => {} - } - } -} - -fn bind_key(code: KeyCode) -> Option { - match code { - KeyCode::KeyW | KeyCode::ArrowUp => Some(Action::Up), - KeyCode::KeyS | KeyCode::ArrowDown => Some(Action::Down), - KeyCode::KeyA | KeyCode::ArrowLeft => Some(Action::Left), - KeyCode::KeyD | KeyCode::ArrowRight => Some(Action::Right), - KeyCode::Space => Some(Action::Fire), - - _ => None, - } -} - -fn main() { - let event_loop = EventLoop::new().expect("failed to create event loop"); - - #[cfg(not(target_arch = "wasm32"))] - { - env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); - event_loop - .run_app(&mut Cj2k26::default()) - .expect("failed to run app"); - } - - #[cfg(target_arch = "wasm32")] - { - use winit::platform::web::EventLoopExtWebSys; - - console_error_panic_hook::set_once(); - event_loop.spawn_app(Cj2k26::default()); - } -} diff --git a/2026/games_submissions/asrael_io/source/src/math.rs b/2026/games_submissions/asrael_io/source/src/math.rs deleted file mode 100644 index 371f165..0000000 --- a/2026/games_submissions/asrael_io/source/src/math.rs +++ /dev/null @@ -1,13 +0,0 @@ -use glam::Vec2; - -pub fn aabb(a_pos: Vec2, a_size: Vec2, b_pos: Vec2, b_size: Vec2) -> bool { - a_pos.x < b_pos.x + b_size.x - && b_pos.x < a_pos.x + a_size.x - && a_pos.y < b_pos.y + b_size.y - && b_pos.y < a_pos.y + a_size.y -} - -pub fn bezier(p: [Vec2; 4], t: f32) -> Vec2 { - let u = 1.0 - t; - u * u * u * p[0] + 3.0 * u * u * t * p[1] + 3.0 * u * t * t * p[2] + t * t * t * p[3] -} diff --git a/2026/games_submissions/asrael_io/source/src/player.rs b/2026/games_submissions/asrael_io/source/src/player.rs deleted file mode 100644 index 637160b..0000000 --- a/2026/games_submissions/asrael_io/source/src/player.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::color::Palette; -use crate::color::db32::{ORANGE, YELLOW}; -use crate::gfx; -use crate::sprite::Sprite; -use crate::{GAME_H, GAME_W}; - -use aseprite::AsepriteFile; -use glam::{IVec2, Vec2}; - -const FIRE_COOLDOWN: u32 = 12; -const MOUSE_MAX: f32 = 2.0; - -#[derive(Default)] -pub struct Player { - cooldown: u32, - pos: Vec2, - step: Vec2, - speed: f32, - sprite: Sprite, -} - -impl Player { - pub fn new(sprites: &AsepriteFile, speed: f32) -> Self { - let cooldown = 0; - let pos = Vec2::new(GAME_W as f32 / 2.0, GAME_H as f32 / 2.0); - let step = Vec2::ZERO; - let sprite = Sprite::from_ase(sprites, "player"); - - Self { - cooldown, - pos, - step, - speed, - sprite, - } - } - - pub fn win_anim(&mut self, t: u32) { - let before = self.pos; - - if t < 40 { - self.pos.y += 0.3; - } else { - self.pos.y -= ((t - 40) as f32 * 0.25).min(8.0); - } - - self.step = self.pos - before; - } - - pub fn pos(&self) -> Vec2 { - self.pos - } - - pub fn size(&self) -> Vec2 { - self.sprite.size.as_vec2() - } - - pub fn try_fire(&mut self) -> Option { - (self.cooldown == 0).then(|| { - self.cooldown = FIRE_COOLDOWN; - self.pos + Vec2::new(self.sprite.size.x as f32 / 2.0, 0.0) - }) - } - - pub fn update(&mut self, direction: IVec2, mouse: Vec2) { - let before = self.pos; - - self.cooldown = self.cooldown.saturating_sub(1); - - let dir = direction.as_vec2().normalize_or_zero(); - self.pos += dir * self.speed + mouse.clamp_length_max(MOUSE_MAX); - - let w = self.sprite.size.x as f32; - let h = self.sprite.size.y as f32; - - self.pos.x = self.pos.x.clamp(0.0, GAME_W as f32 - w); - self.pos.y = self.pos.y.clamp(0.0, GAME_H as f32 - h); - self.step = self.pos - before; - } - - pub fn draw(&self, frame: &mut [u32], palette: &Palette, a: f32, tick: u32) { - let pos = (self.pos - self.step * (1.0 - a)).round().as_ivec2(); - self.sprite.draw_at(frame, palette, pos); - - if self.step.length_squared() > 0.01 { - let flame = if (tick / 4).is_multiple_of(2) { - ORANGE - } else { - YELLOW - }; - let exhaust = [flame, flame]; - let nozzle = pos + IVec2::new(self.sprite.size.x / 2, self.sprite.size.y); - gfx::blit(frame, palette, &exhaust, nozzle, 1, 0); - } - } -} diff --git a/2026/games_submissions/asrael_io/source/src/rng.rs b/2026/games_submissions/asrael_io/source/src/rng.rs deleted file mode 100644 index 6e46de0..0000000 --- a/2026/games_submissions/asrael_io/source/src/rng.rs +++ /dev/null @@ -1,35 +0,0 @@ -pub struct Rng(u32); - -impl Rng { - pub fn new(seed: u32) -> Self { - let mut rng = Self(seed.max(1)); - rng.next(); - rng - } - - pub fn next(&mut self) -> u32 { - self.0 ^= self.0 << 13; - self.0 ^= self.0 >> 17; - self.0 ^= self.0 << 5; - - self.0 - } - - pub fn range(&mut self, n: u32) -> u32 { - self.next() % n - } - - pub fn f32(&mut self) -> f32 { - self.next() as f32 / u32::MAX as f32 - } - - pub fn chance(&mut self, p: f32) -> bool { - self.f32() < p - } -} - -impl Default for Rng { - fn default() -> Self { - Self::new(u32::from_be_bytes(*b"CLNK")) - } -} diff --git a/2026/games_submissions/asrael_io/source/src/sfx.rs b/2026/games_submissions/asrael_io/source/src/sfx.rs deleted file mode 100644 index a1367df..0000000 --- a/2026/games_submissions/asrael_io/source/src/sfx.rs +++ /dev/null @@ -1,302 +0,0 @@ -use std::sync::mpsc::{Receiver, Sender, channel}; - -use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; -use cpal::{Device, OutputCallbackInfo, Stream, SupportedStreamConfig}; -use resid::{ChipModel, PAL_CLOCK, SamplingMethod, Sid}; - -const GATE: u8 = 0x01; -const TRIANGLE: u8 = 0x10; -const SAWTOOTH: u8 = 0x20; -const PULSE: u8 = 0x40; -const NOISE: u8 = 0x80; - -enum Sound { - EnemyShoot, - Explosion, - Hit, - Lose, - Shoot, - Win, -} - -struct Pending { - config: SupportedStreamConfig, - device: Device, - rx: Receiver, - sid: Sid, -} - -pub struct Sfx { - pending: Option, - stream: Option, - tx: Sender, -} - -impl Sfx { - pub fn new() -> Self { - let device = cpal::default_host() - .default_output_device() - .expect("no audio output device"); - - let config = device - .default_output_config() - .expect("no default audio config"); - - let mut sid = Sid::new(ChipModel::Mos6581); - let (tx, rx) = channel::(); - - sid.set_sampling_parameters(SamplingMethod::ResampleFast, PAL_CLOCK, config.sample_rate()); - Self::set_volume(&mut sid, 0x0F); - Self::set_envelope(&mut sid, 0, 0x06, 0x00); - Self::set_envelope(&mut sid, 1, 0x06, 0x00); - Self::set_envelope(&mut sid, 2, 0x08, 0xA9); - - Self { - pending: Some(Pending { - config, - device, - rx, - sid, - }), - stream: None, - tx, - } - } - - pub fn resume(&mut self) { - if let Some(stream) = &self.stream { - let _ = stream.play(); - return; - } - - let Some(Pending { - config, - device, - rx, - mut sid, - }) = self.pending.take() - else { - return; - }; - - let channels = config.channels() as usize; - let sample_rate = config.sample_rate(); - let mut scratch: Vec = Vec::new(); - - let mut enemy_shoot_hz: f32 = 0.0; - let mut explosion_hz: f32 = 0.0; - let mut hit_t: u32 = 0; - let mut lose_hz: f32 = 0.0; - let mut lose_t: u32 = 0; - let mut lose_wob: f32 = 0.0; - let mut shoot_hz: f32 = 0.0; - let mut win_t: u32 = 0; - - let stream = device - .build_output_stream( - config.into(), - move |data: &mut [f32], _: &OutputCallbackInfo| { - while let Ok(sound) = rx.try_recv() { - match sound { - Sound::EnemyShoot => { - enemy_shoot_hz = 520.0; - Self::set_freq(&mut sid, 1, enemy_shoot_hz); - Self::gate_on(&mut sid, 1, TRIANGLE); - } - - Sound::Explosion => { - hit_t = 0; - Self::set_envelope(&mut sid, 2, 0x08, 0xA9); - - explosion_hz = 2500.0; - Self::set_freq(&mut sid, 2, explosion_hz); - Self::gate_on(&mut sid, 2, NOISE); - } - - Sound::Hit => { - Self::set_envelope(&mut sid, 2, 0x05, 0x00); - - hit_t = 45; - Self::set_freq(&mut sid, 2, 2500.0); - Self::gate_on(&mut sid, 2, NOISE); - } - - Sound::Lose => { - enemy_shoot_hz = 0.0; - Self::set_envelope(&mut sid, 1, 0x00, 0xC6); - Self::set_pulse_width(&mut sid, 1, 0x0800); - - lose_hz = 392.0; - lose_t = 300; - lose_wob = 0.0; - Self::set_freq(&mut sid, 1, lose_hz); - Self::gate_on(&mut sid, 1, PULSE); - } - - Sound::Shoot => { - shoot_hz = 1760.0; - Self::set_freq(&mut sid, 0, shoot_hz); - Self::gate_on(&mut sid, 0, SAWTOOTH); - } - - Sound::Win => { - shoot_hz = 0.0; - Self::set_envelope(&mut sid, 0, 0x00, 0xA9); - Self::set_pulse_width(&mut sid, 0, 0x0800); - - win_t = 300; - Self::set_freq(&mut sid, 0, 987.77); - Self::gate_on(&mut sid, 0, PULSE); - } - } - } - - let frames = data.len() / channels; - if scratch.len() < frames { - scratch.resize(frames, 0); - } - let out = &mut scratch[..frames]; - - for block in out.chunks_mut(64) { - Self::sweep(&mut sid, &mut shoot_hz, 0.975, 220.0, 0, SAWTOOTH); - Self::sweep(&mut sid, &mut enemy_shoot_hz, 0.97, 200.0, 1, TRIANGLE); - Self::sweep(&mut sid, &mut explosion_hz, 0.985, 150.0, 2, NOISE); - - if hit_t > 0 { - hit_t -= 1; - if hit_t == 0 { - Self::gate_off(&mut sid, 2, NOISE); - } - } - - if win_t > 0 { - win_t -= 1; - - if win_t == 245 || win_t == 190 { - let hz = if win_t == 245 { 1318.5 } else { 1661.2 }; - Self::set_freq(&mut sid, 0, hz); - Self::gate_on(&mut sid, 0, PULSE); - } - - if win_t == 0 { - Self::gate_off(&mut sid, 0, PULSE); - } - } - - if lose_t > 0 { - lose_t -= 1; - lose_hz *= 0.995; - lose_wob += 0.35; - - let hz = lose_hz * (1.0 + 0.05 * lose_wob.sin()); - Self::set_freq(&mut sid, 1, hz); - - if lose_t == 0 { - Self::gate_off(&mut sid, 1, PULSE); - } - } - - Self::fill(&mut sid, block, sample_rate); - } - - for (frame, &s) in data.chunks_mut(channels).zip(out.iter()) { - frame.fill(s as f32 / 32768.0); - } - }, - move |err| log::error!("audio stream error: {err}"), - None, - ) - .expect("failed to build audio stream"); - - stream.play().expect("failed to start audio stream"); - self.stream = Some(stream); - } - - pub fn enemy_shoot(&self) { - let _ = self.tx.send(Sound::EnemyShoot); - } - - pub fn explode(&self) { - let _ = self.tx.send(Sound::Explosion); - } - - pub fn hit(&self) { - let _ = self.tx.send(Sound::Hit); - } - - pub fn lose(&self) { - let _ = self.tx.send(Sound::Lose); - } - - pub fn shoot(&self) { - let _ = self.tx.send(Sound::Shoot); - } - - pub fn win(&self) { - let _ = self.tx.send(Sound::Win); - } - - fn fill(sid: &mut Sid, out: &mut [i16], sample_rate: u32) { - let budget = PAL_CLOCK / sample_rate + 1; - let mut i = 0; - - while i < out.len() { - let target = (out.len() - i) as u32; - let (n, _) = sid.sample(target * budget, &mut out[i..], 1); - - if n == 0 { - break; - } - - i += n; - } - } - - fn gate_off(sid: &mut Sid, voice: u8, wave: u8) { - sid.write(voice * 7 + 4, wave); - } - - fn gate_on(sid: &mut Sid, voice: u8, wave: u8) { - sid.write(voice * 7 + 4, wave); - sid.write(voice * 7 + 4, wave | GATE); - } - - fn set_envelope(sid: &mut Sid, voice: u8, attack_decay: u8, sustain_release: u8) { - let base = voice * 7; - - sid.write(base + 5, attack_decay); - sid.write(base + 6, sustain_release); - } - - fn set_freq(sid: &mut Sid, voice: u8, hz: f32) { - let base = voice * 7; - let freq = (hz * 16_777_216.0 / PAL_CLOCK as f32) as u16; - - sid.write(base, freq as u8); - sid.write(base + 1, (freq >> 8) as u8); - } - - fn set_pulse_width(sid: &mut Sid, voice: u8, width: u16) { - let base = voice * 7; - - sid.write(base + 2, width as u8); - sid.write(base + 3, (width >> 8) as u8); - } - - fn set_volume(sid: &mut Sid, volume: u8) { - sid.write(0x18, volume); - } - - fn sweep(sid: &mut Sid, hz: &mut f32, rate: f32, floor: f32, voice: u8, wave: u8) { - if *hz > 0.0 { - *hz *= rate; - - if *hz < floor { - *hz = 0.0; - Self::gate_off(sid, voice, wave); - } else { - Self::set_freq(sid, voice, *hz); - } - } - } -} diff --git a/2026/games_submissions/asrael_io/source/src/sprite.rs b/2026/games_submissions/asrael_io/source/src/sprite.rs deleted file mode 100644 index 2bfb104..0000000 --- a/2026/games_submissions/asrael_io/source/src/sprite.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::color::Palette; -use crate::gfx; - -use aseprite::{AsepriteFile, CelKind}; -use glam::{IVec2, Vec2}; - -#[derive(Clone, Default)] -pub struct Sprite { - pixels: Vec, - pub size: IVec2, -} - -impl Sprite { - pub fn new(width: i32, height: i32, pixels: Vec) -> Self { - Self { - pixels, - size: IVec2::new(width, height), - } - } - - pub fn from_ase(file: &AsepriteFile, layer: &str) -> Self { - let index = file - .layers() - .iter() - .position(|l| l.name == layer) - .unwrap_or_else(|| panic!("no layer named {layer}")); - let cel = file.cel(file.layer_ref(index).unwrap(), 0).unwrap(); - - let (CelKind::Raw { pixels, .. } | CelKind::Compressed { pixels, .. }) = &cel.kind else { - panic!("layer {layer} has no pixel cel"); - }; - - Self::new( - pixels.width as i32, - pixels.height as i32, - pixels.data.clone(), - ) - } - - pub fn frames_from_ase(file: &AsepriteFile, layer: &str) -> Vec { - let index = file - .layers() - .iter() - .position(|l| l.name == layer) - .unwrap_or_else(|| panic!("no layer named {layer}")); - let layer_ref = file.layer_ref(index).unwrap(); - - (0..file.frames().len()) - .filter_map(|f| file.resolve_cel(layer_ref, f)) - .filter_map(|cel| match &cel.kind { - CelKind::Raw { pixels, .. } | CelKind::Compressed { pixels, .. } => { - Some(Self::new( - pixels.width as i32, - pixels.height as i32, - pixels.data.clone(), - )) - } - _ => None, - }) - .collect() - } - - pub fn draw_at(&self, frame: &mut [u32], palette: &Palette, pos: IVec2) { - gfx::blit(frame, palette, &self.pixels, pos, self.size.x, 0); - } - - pub fn draw_tinted(&self, frame: &mut [u32], palette: &Palette, pos: IVec2, tint: u8) { - gfx::blit(frame, palette, &self.pixels, pos, self.size.x, tint); - } - - pub fn draw_rotated( - &self, - frame: &mut [u32], - palette: &Palette, - center: Vec2, - angle: f32, - tint: u8, - ) { - gfx::blit_rotated( - frame, - palette, - &self.pixels, - center, - self.size.x, - angle, - tint, - ); - } -} diff --git a/2026/games_submissions/asrael_io/source/src/starfield.rs b/2026/games_submissions/asrael_io/source/src/starfield.rs deleted file mode 100644 index ab7a199..0000000 --- a/2026/games_submissions/asrael_io/source/src/starfield.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::color::Palette; -use crate::color::db32::{BLUE, CYAN, GRAY, LIGHT_RED, LIME, PALE_BLUE, WHITE, YELLOW}; -use crate::rng::Rng; -use crate::{GAME_H, GAME_W}; - -const STAR_COLORS: [u8; 8] = [WHITE, GRAY, PALE_BLUE, CYAN, BLUE, YELLOW, LIGHT_RED, LIME]; - -struct Star { - color: u8, - speed: f32, - x: i32, - y: f32, -} - -#[derive(Default)] -pub struct Starfield { - stars: Vec, -} - -impl Starfield { - pub fn new(count: usize, rng: &mut Rng) -> Self { - let stars = (0..count) - .map(|i| { - let speed = match i % 3 { - 0 => 0.5, - 1 => 1.0, - _ => 1.5, - }; - - Star { - color: STAR_COLORS[rng.range(STAR_COLORS.len() as u32) as usize], - speed, - x: rng.range(GAME_W) as i32, - y: rng.range(GAME_H) as f32, - } - }) - .collect(); - - Self { stars } - } - - pub fn update(&mut self) { - for star in &mut self.stars { - star.y += star.speed; - if star.y >= GAME_H as f32 { - star.y -= GAME_H as f32; - } - } - } - - pub fn draw(&self, frame: &mut [u32], palette: &Palette, a: f32) { - for star in &self.stars { - let y = star.y - star.speed * (1.0 - a); - let y = (y as i32).rem_euclid(GAME_H as i32); - - frame[(y * GAME_W as i32 + star.x) as usize] = palette.at(star.color); - } - } -} -- cgit v1.3.1