summaryrefslogtreecommitdiff
path: root/2026/framing/src/game.rs
diff options
context:
space:
mode:
Diffstat (limited to '2026/framing/src/game.rs')
-rw-r--r--2026/framing/src/game.rs134
1 files changed, 109 insertions, 25 deletions
diff --git a/2026/framing/src/game.rs b/2026/framing/src/game.rs
index b8fb5ce..6c2df1b 100644
--- a/2026/framing/src/game.rs
+++ b/2026/framing/src/game.rs
@@ -3,12 +3,16 @@ use std::collections::HashMap;
use teleia::*;
use teleia::renderer::UberFlags;
use wasm_bindgen::prelude::*;
+use rand::seq::SliceRandom;
use crate::assets;
const NUM_LIVES: usize = 4;
const LIFE_LOSS_FRAMES: u64 = 120;
+const SOUND_POSITIVE: &[&str] = &["booyah", "excellent", "epicwin", "wow"];
+const SOUND_NEGATIVE: &[&str] = &["boo", "aww", "itsover", "youlose", "yousuck"];
+
#[derive(PartialEq, Eq)]
enum MrColor {
Green,
@@ -146,8 +150,11 @@ fn draw_mr_armed(
}
pub enum Mode {
+ Titlescreen,
+ Intro,
BetweenGames,
InGame,
+ PostGames,
}
pub struct Game {
@@ -160,55 +167,116 @@ pub struct Game {
impl Game {
pub fn new(ctx: &context::Context, st: &mut state::State) -> Self {
Self {
- mode: Mode::BetweenGames,
+ mode: Mode::Titlescreen,
mode_started: 0,
renderer: renderer::Renderer::new(ctx, st, assets::Assets::new),
lives: Lives::new(),
}
}
+ pub fn switch(&mut self, _ctx: &context::Context, st: &mut state::State, m: Mode) {
+ self.mode = m;
+ self.mode_started = st.tick;
+ }
+ fn draw_mrworld(&mut self, ctx: &context::Context, st: &mut state::State, fadeout: Option<Tick>) -> Erm<()> {
+ self.renderer.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::TEXTURE_FLIP | UberFlags::SPRITE | UberFlags::OPACITY);
+ self.renderer.bind_texture(ctx, st, assets::Texture::Mrworld);
+ self.renderer.set_position_2d(ctx, st, Vec2::ZERO, st.render_dims);
+ self.renderer.set_vec2(ctx, st, "texture_flip", glam::Vec2::new(0.0, 1.0));
+ if let Some(t) = fadeout {
+ if t < 35 {
+ self.renderer.set_texture_offset(ctx, st, 8, 1, 2 + (t / 5).clamp(0, 5) as i32, 0);
+ self.renderer.set_f32(ctx, st, "opacity", 1.0);
+ } else if t < 70 {
+ let since = ((t - 35) as f32).clamp(0.0, 34.0);
+ let opacity = (34.0 - since) / 34.0;
+ self.renderer.set_texture_offset(ctx, st, 8, 1, 7, 0);
+ self.renderer.set_f32(ctx, st, "opacity", opacity);
+ }
+ } else {
+ self.renderer.set_texture_offset(ctx, st, 8, 1, (st.tick as i32 / 20) % 2, 0);
+ self.renderer.set_f32(ctx, st, "opacity", 1.0);
+ }
+ self.renderer.render_square(ctx, st);
+ Ok(())
+ }
+ fn play_random_sound(&mut self, ctx: &context::Context, st: &mut state::State, sounds: &[&str]) {
+ st.audio.play_sfx(sounds.choose(&mut rand::thread_rng()).unwrap());
+ }
}
impl teleia::state::Game for Game {
fn initialize_audio(&self, ctx: &context::Context, st: &state::State, actx: &audio::Context) -> HashMap<String, audio::Audio> {
HashMap::from_iter(vec![
+ ("jam2026".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/jam2026.wav"))),
+ ("wind".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/wind.wav"))),
("footsteps".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/footsteps.wav"))),
+ ("explosion".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/explosion.wav"))),
+ ("gamingtime".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/gamingtime.wav"))),
+ ("booyah".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/booyah.wav"))),
+ ("excellent".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/excellent.wav"))),
+ ("epicwin".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/epicwin.wav"))),
+ ("wow".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/wow.wav"))),
+ ("boo".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/boo.wav"))),
+ ("aww".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/aww.wav"))),
+ ("itsover".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/itsover.wav"))),
+ ("youlose".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/youlose.wav"))),
+ ("yousuck".to_owned(), audio::Audio::new(actx, include_bytes!("assets/audio/yousuck.wav"))),
])
}
fn update(&mut self, ctx: &context::Context, st: &mut state::State) -> Erm<()> {
+ let t = st.tick - self.mode_started;
+ match self.mode {
+ Mode::Intro => {
+ if t == 43 * 5 {
+ st.audio.play_sfx("explosion");
+ st.audio.play_music("jam2026", None, None);
+ }
+ if t >= 57 * 5 {
+ self.switch(ctx, st, Mode::BetweenGames);
+ }
+ },
+ _ => {},
+ }
Ok(())
}
fn render(&mut self, ctx: &context::Context, st: &mut state::State) -> Erm<()> {
let t = st.tick - self.mode_started;
match self.mode {
- Mode::BetweenGames => {
+ Mode::Titlescreen => {
self.renderer.begin_frame(ctx, st, Vec4::ZERO);
self.renderer.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::TEXTURE_FLIP | UberFlags::SPRITE);
- self.renderer.bind_texture(ctx, st, assets::Texture::Mrworld);
- self.renderer.set_texture_offset(ctx, st, 8, 1, (t / 20 % 2) as i32, 0);
+ self.renderer.bind_texture(ctx, st, assets::Texture::Titlescreen);
+ self.renderer.set_texture_offset(ctx, st, 2, 1, (t / 60 % 2) as i32, 0);
self.renderer.set_position_2d(ctx, st, Vec2::ZERO, st.render_dims);
self.renderer.set_vec2(ctx, st, "texture_flip", glam::Vec2::new(0.0, 1.0));
self.renderer.render_square(ctx, st);
- self.lives.render(ctx, st, &mut self.renderer)?;
},
- Mode::InGame => {
+ Mode::Intro => {
self.renderer.begin_frame(ctx, st, Vec4::ZERO);
- self.renderer.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::TEXTURE_FLIP | UberFlags::SPRITE | UberFlags::OPACITY);
- self.renderer.bind_texture(ctx, st, assets::Texture::Mrworld);
+ self.draw_mrworld(ctx, st, None)?;
+ self.lives.render(ctx, st, &mut self.renderer)?;
+ let f = t / 5;
+ let sx = f % 8;
+ let sy = f / 8;
+ self.renderer.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::TEXTURE_FLIP | UberFlags::SPRITE);
+ self.renderer.bind_texture(ctx, st, assets::Texture::Intro);
+ self.renderer.set_texture_offset(ctx, st, 8, 8, sx as i32, sy as i32);
self.renderer.set_position_2d(ctx, st, Vec2::ZERO, st.render_dims);
self.renderer.set_vec2(ctx, st, "texture_flip", glam::Vec2::new(0.0, 1.0));
- if t < 35 {
- self.renderer.set_texture_offset(ctx, st, 8, 1, 2 + (t / 5).clamp(0, 5) as i32, 0);
- self.renderer.set_f32(ctx, st, "opacity", 1.0);
- self.renderer.render_square(ctx, st);
- } else if t < 70 {
- let since = ((t - 35) as f32).clamp(0.0, 34.0);
- let opacity = (34.0 - since) / 34.0;
- self.renderer.set_texture_offset(ctx, st, 8, 1, 7, 0);
- self.renderer.set_f32(ctx, st, "opacity", opacity);
- self.renderer.render_square(ctx, st);
- }
- // self.lives.render(ctx, st, &mut self.renderer)?;
+ self.renderer.render_square(ctx, st);
}
+ Mode::BetweenGames => {
+ self.renderer.begin_frame(ctx, st, Vec4::ZERO);
+ self.draw_mrworld(ctx, st, None)?;
+ self.lives.render(ctx, st, &mut self.renderer)?;
+ },
+ Mode::InGame => {
+ self.renderer.begin_frame(ctx, st, Vec4::ZERO);
+ self.draw_mrworld(ctx, st, Some(t))?;
+ // self.lives.render(ctx, st, &mut self.renderer)?;
+ },
+ Mode::PostGames => {
+ },
}
Ok(())
}
@@ -217,22 +285,38 @@ impl teleia::state::Game for Game {
}
}
+#[wasm_bindgen(module = "/src/bindings.js")]
+extern {
+ fn js_test();
+}
+
+#[wasm_bindgen]
+pub fn titlescreen_click() {
+ contextualize(|ctx, st, g: &mut Game| {
+ g.switch(ctx, st, Mode::Intro);
+ st.audio.play_music("wind", None, None);
+ });
+}
+
#[wasm_bindgen]
pub fn start_game() {
contextualize(|ctx, st, g: &mut Game| {
- g.mode = Mode::InGame;
- g.mode_started = st.tick;
+ g.switch(ctx, st, Mode::InGame);
+ st.audio.mute_music(0.0);
});
}
#[wasm_bindgen]
pub fn end_game(win: bool) {
contextualize(|ctx, st, g: &mut Game| {
- g.mode = Mode::BetweenGames;
- g.mode_started = st.tick;
- if !win {
+ g.switch(ctx, st, Mode::BetweenGames);
+ st.audio.unmute_music(0.0);
+ if win {
+ g.play_random_sound(ctx, st, SOUND_POSITIVE);
+ } else {
g.lives.lose_life(ctx, st);
st.audio.play_sfx("footsteps");
+ g.play_random_sound(ctx, st, SOUND_NEGATIVE);
}
});
}