From d89f43d1d39a49e51ae5c17c8e6d09ceede39481 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 22 Jun 2026 21:35:27 -0400 Subject: [PATCH 01/19] Update rocci-bird --- examples/rocci-bird.roc | 1389 +++++++++++++++++++-------------------- 1 file changed, 674 insertions(+), 715 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index 81ba95c..4fd06d3 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -1,155 +1,149 @@ app [main] { - w4: platform "https://github.com/lukewilliamboswell/roc-wasm4/releases/download/0.5/sZGj6cG7ted2RNohpqvQwqb7pvBd5C5zotA5XXyJZnA.tar.zst", + w4: platform "../platform/main.roc", } import w4.W4 import w4.Sprite Model : [ - TitleScreen(TitleScreenState), - Game(GameState), - GameOver(GameOverState), + TitleScreen(TitleScreenState), + Game(GameState), + GameOver(GameOverState), ] -main = { - init!: || init!(), - update!: |model| update!(model), -} +main = { init!, update! } init! : () => Model init! = || { - # Lospec palette: Candy Cloud [2-BIT] Palette - palette = { - color1: 0xe6e6c0, - color2: 0xb494b7, - color3: 0x42436e, - color4: 0x26013f, - } - W4.set_palette!(palette) - - frameCount = loadRandFromDisk!() - W4.seed_rand!(frameCount) - plants = startingPlants!() - - initTitleScreen(frameCount, plants) + # Lospec palette: Candy Cloud [2-BIT] Palette + palette = { + color1: 0xe6e6c0, + color2: 0xb494b7, + color3: 0x42436e, + color4: 0x26013f, + } + W4.set_palette!(palette) + + frame_count = load_rand_from_disk!() + W4.seed_rand!(frame_count) + plants = starting_plants!() + + init_title_screen(frame_count, plants) } update! : Model => Model update! = |model| - match model { - TitleScreen(prev) => - runTitleScreen!(updateFrameCount(prev)) - - Game(prev) => - runGame!(updateFrameCount(prev)) + match model { + TitleScreen(prev) => run_title_screen!(update_frame_count(prev)) + Game(prev) => run_game!(update_frame_count(prev)) + GameOver(prev) => run_game_over!(update_frame_count(prev)) + } + +update_frame_count = |prev| + { ..prev, frame_count: prev.frame_count + 1 } + +# TODO: switch for builtin List.append_if_ok +append_if_ok : List(a), Try(a, err) -> List(a) +append_if_ok = |items, maybe_item| + match maybe_item { + Ok(item) => items.append(item) + Err(_) => items + } + +# TODO: use the builtin once it exists +sub_saturating_u64 : U64, U64 -> U64 +sub_saturating_u64 = |x, y| + if x < y { + 0 + } else { + x - y + } - GameOver(prev) => - runGameOver!(updateFrameCount(prev)) - } +# ===== Title Screen ====================================== -updateFrameCount = |prev| { - frameCount = prev.frameCount + 1 - { ..prev, frameCount } +TitleScreenState : { + frame_count : U64, + plants : List(Plant), + rocci_idle_anim : Animation, } -appendIfOk = |items, maybe_item| - match maybe_item { - Ok(item) => List.append(items, item) - Err(_) => items - } +init_title_screen : U64, List(Plant) -> Model +init_title_screen = |frame_count, plants| + TitleScreen( + { + frame_count, + plants, + rocci_idle_anim: create_rocci_idle_anim(frame_count), + }, + ) -subSaturatingU64 : U64, U64 -> U64 -subSaturatingU64 = |x, y| - if x < y { - 0 - } else { - x - y - } +run_title_screen! : TitleScreenState => Model +run_title_screen! = |prev| { + state = { ..prev, rocci_idle_anim: update_animation(prev.frame_count, prev.rocci_idle_anim) } -# ===== Title Screen ====================================== + set_text_colors!() + W4.text!("Rocci Bird!!!", { x: 32, y: 12 }) + W4.text!("Click to start!", { x: 24, y: 72 }) + draw_ground!(ground_sprite, 0) + draw_plants!(state.plants) -TitleScreenState : { - frameCount : U64, - plants : List(Plant), - rocciIdleAnim : Animation, -} + shift = idle_shift(state.frame_count, state.rocci_idle_anim) + draw_animation!(state.rocci_idle_anim, { x: player_x, y: player_start_y_pixel + shift, flags: [] }) + gamepad = W4.get_gamepad!(Player1) + mouse = W4.get_mouse!() + + start = gamepad.button1 or gamepad.up or mouse.left -initTitleScreen : U64, List(Plant) -> Model -initTitleScreen = |frameCount, plants| - TitleScreen({ - frameCount, - plants, - rocciIdleAnim: createRocciIdleAnim(frameCount), - }) - -runTitleScreen! : TitleScreenState => Model -runTitleScreen! = |prev| { - state = { ..prev, - rocciIdleAnim: updateAnimation(prev.frameCount, prev.rocciIdleAnim), - } - setTextColors!() - W4.text!("Rocci Bird!!!", { x: 32, y: 12 }) - W4.text!("Click to start!", { x: 24, y: 72 }) - drawGround!(groundSprite, 0) - drawPlants!(plantSpriteSheet, state.plants) - - shift = idleShift(state.frameCount, state.rocciIdleAnim) - drawAnimation!(state.rocciIdleAnim, { x: playerX, y: playerStartYPixel + shift, flags: [] }) - gamepad = W4.get_gamepad!(Player1) - mouse = W4.get_mouse!() - - start = gamepad.button1 or gamepad.up or mouse.left - - if start { - initGame!(state) - } else { - TitleScreen(state) - } + if start { + init_game!(state) + } else { + TitleScreen(state) + } } # ===== Main Game ========================================= GameState : { - frameCount : U64, - score : U8, - maxScore : U8, - player : { - y : F32, - yVel : F32, - }, - lastFlap : Bool, - rocciFlapAnim : Animation, - pipes : List(Pipe), - lastPipeGenerated : U64, - plants : List(Plant), - lastPlantGenerated : U64, - groundX : I32, -} - -initGame! : TitleScreenState => Model -initGame! = |{ frameCount, plants }| { - # Seed the randomness with number of frames since the start of the game. - # This makes the game feel like it is truely randomly seeded cause players won't always start on the same frame. - saveRandToDisk!(frameCount) - W4.seed_rand!(frameCount) - W4.tone!(flapTone) - - Game({ - frameCount, - score: 0, - maxScore: 0, - player: { - y: playerStartY, - yVel: jumpSpeed, - }, - lastPipeGenerated: frameCount, - pipes: [], - plants, - lastPlantGenerated: subSaturatingU64(frameCount, 4), - lastFlap: Bool.True, - rocciFlapAnim: createRocciFlapAnim(frameCount), - groundX: 0, - }) + frame_count : U64, + score : U8, + max_score : U8, + player : { + y : F32, + y_vel : F32, + }, + last_flap : Bool, + rocci_flap_anim : Animation, + pipes : List(Pipe), + last_pipe_generated : U64, + plants : List(Plant), + last_plant_generated : U64, + ground_x : I32, +} + +init_game! : TitleScreenState => Model +init_game! = |{ frame_count, plants }| { + # Seed the randomness with number of frames since the start of the game. + # This makes the game feel like it is truely randomly seeded cause players won't always start on the same frame. + save_rand_to_disk!(frame_count) + W4.seed_rand!(frame_count) + W4.tone!(flap_tone) + + Game({ + frame_count, + score: 0, + max_score: 0, + player: { + y: player_start_y, + y_vel: jump_speed, + }, + last_pipe_generated: frame_count, + pipes: [], + plants, + last_plant_generated: sub_saturating_u64(frame_count, 4), + last_flap: True, + rocci_flap_anim: create_rocci_flap_anim(frame_count), + ground_x: 0, + }) } # Useful to throw in WolframAlpha to help calculate these: @@ -159,659 +153,624 @@ initGame! = |{ frameCount, plants }| { gravity : F32 gravity = 0.12 -jumpSpeed : F32 -jumpSpeed = -2.2 - -runGame! : GameState => Model -runGame! = |prev| { - gamepad = W4.get_gamepad!(Player1) - mouse = W4.get_mouse!() - - flap = gamepad.button1 or gamepad.up or mouse.left - - { yVel, nextAnim, playFlapSound } = - if !prev.lastFlap and flap and flapAllowed(prev.frameCount, prev.rocciFlapAnim) { - anim = prev.rocciFlapAnim - { - yVel: jumpSpeed, - nextAnim: { ..anim, index: 0, state: RunOnce }, - playFlapSound: Bool.True, - } - } else { - { - yVel: prev.player.yVel + gravity, - nextAnim: updateAnimation(prev.frameCount, prev.rocciFlapAnim), - playFlapSound: Bool.False, - } - } - - if playFlapSound { - W4.tone!(flapTone) - } else { - {} - } - - pipe = maybeGeneratePipe!(prev.lastPipeGenerated, prev.frameCount) - - lastPipeGenerated = - if Try.is_ok(pipe) { - prev.frameCount - } else { - prev.lastPipeGenerated - } - - pipes = - appendIfOk(updatePipes(prev.pipes), pipe) - - plant = maybeGeneratePlant!(prev.lastPlantGenerated, prev.frameCount) - - lastPlantGenerated = - if Try.is_ok(plant) { - prev.frameCount - } else { - prev.lastPlantGenerated - } - - plants = - appendIfOk(updatePlants(prev.plants), plant) - - gainPoint = U64.to_u8_wrap(List.count_if(prev.pipes, |{ x }| x == playerX - 2)) - y = prev.player.y + yVel - score = U8.plus_saturated(prev.score, gainPoint) - state = { ..prev, - rocciFlapAnim: nextAnim, - player: { y, yVel }, - score, - maxScore: U8.max(score, prev.maxScore), - lastFlap: flap, - lastPipeGenerated, - pipes, - lastPlantGenerated, - plants, - groundX: (prev.groundX - 1) % W4.screen_width(), - } - - if gainPoint > 0 { - W4.tone!(pointTone) - } else { - {} - } - - drawPipes!(pipeSprite, state.pipes) - drawGround!(groundSprite, state.groundX) - drawPlants!(plantSpriteSheet, state.plants) - - yPixel = - I32.min(F32.to_i32_wrap(state.player.y), 134) - - collided = playerCollided!(yPixel, state.rocciFlapAnim.index) - drawAnimation!(state.rocciFlapAnim, { x: playerX, y: yPixel, flags: [] }) - drawScore!(state.score, { x: 68, y: 4 }) - - if !collided and y < 134 { - Game(state) - } else { - W4.tone!(deathTone) - - initGameOver!(state) - } +jump_speed : F32 +jump_speed = -2.2 + +run_game! : GameState => Model +run_game! = |prev| { + gamepad = W4.get_gamepad!(Player1) + mouse = W4.get_mouse!() + + flap = gamepad.button1 or gamepad.up or mouse.left + + { y_vel, next_anim } = + if !prev.last_flap and flap and flap_allowed(prev.frame_count, prev.rocci_flap_anim) { + W4.tone!(flap_tone) + anim = prev.rocci_flap_anim + { + y_vel: jump_speed, + next_anim: { ..anim, index: 0, state: RunOnce }, + } + } else { + { + y_vel: prev.player.y_vel + gravity, + next_anim: update_animation(prev.frame_count, prev.rocci_flap_anim), + } + } + + pipe = maybe_generate_pipe!(prev.last_pipe_generated, prev.frame_count) + + last_pipe_generated = + if pipe.is_ok() { + prev.frame_count + } else { + prev.last_pipe_generated + } + + pipes = append_if_ok(update_pipes(prev.pipes), pipe) + plant = maybe_generate_plant!(prev.last_plant_generated, prev.frame_count) + + last_plant_generated = + if plant.is_ok() { + prev.frame_count + } else { + prev.last_plant_generated + } + + plants = append_if_ok(update_plants(prev.plants), plant) + gain_point = U64.to_u8_wrap(prev.pipes.count_if(|candidate_pipe| candidate_pipe.x == player_x - 2)) + y = prev.player.y + y_vel + score = U8.plus_saturated(prev.score, gain_point) + state = { + ..prev, + rocci_flap_anim: next_anim, + player: { y, y_vel }, + score, + max_score: U8.max(score, prev.max_score), + last_flap: flap, + last_pipe_generated, + pipes, + last_plant_generated, + plants, + ground_x: (prev.ground_x - 1) % W4.screen_width(), + } + + if gain_point > 0 { + W4.tone!(point_tone) + } + + draw_pipes!(state.pipes) + draw_ground!(state.ground_x) + draw_plants!(state.plants) + + y_pixel = I32.min(F32.to_i32_wrap(state.player.y), 134) + collided = player_collided!(y_pixel, state.rocci_flap_anim.index) + draw_animation!(state.rocci_flap_anim, { x: player_x, y: y_pixel, flags: [] }) + draw_score!(state.score, { x: 68, y: 4 }) + + if !collided and y < 134 { + Game(state) + } else { + W4.tone!(death_tone) + + init_game_over!(state) + } } # ===== Game Over ========================================= GameOverState : { - frameCount : U64, - score : U8, - highScore : U8, - newHighScore : Bool, - player : { - y : F32, - yVel : F32, - }, - rocciFallAnim : Animation, - highScoreAnim : Animation, - pipes : List(Pipe), - plants : List(Plant), - groundX : I32, -} - -initGameOver! : GameState => Model -initGameOver! = |{ frameCount, maxScore, score, player, pipes, plants, groundX }| { - hs = loadHighScoreFromDisk!() - newHighScore = maxScore > hs - highScore = - if newHighScore { - maxScore - } else { - hs - } - saveHighScoreToDisk!(highScore) - - GameOver({ - frameCount, - score, - highScore, - newHighScore, - player, - pipes, - plants, - rocciFallAnim: createRocciFallAnim(frameCount), - highScoreAnim: createHighScoreAnim(frameCount), - groundX, - }) -} - -runGameOver! : GameOverState => Model -runGameOver! = |prev| { - yVel = prev.player.yVel + gravity - rocciFallAnim = updateAnimation(prev.frameCount, prev.rocciFallAnim) - highScoreAnim = updateAnimation(prev.frameCount, prev.highScoreAnim) - - nextY = prev.player.y + yVel - y = if nextY > 134 { - 134 - } else { - nextY - } - - state = { ..prev, - rocciFallAnim, - highScoreAnim, - player: { y, yVel }, - } - drawPipes!(pipeSprite, state.pipes) - drawGround!(groundSprite, state.groundX) - drawPlants!(plantSpriteSheet, state.plants) - - yPixel = F32.to_i32_wrap(state.player.y) - drawAnimation!(state.rocciFallAnim, { x: playerX, y: yPixel, flags: [] }) - W4.set_shape_colors!({ border: Color4, fill: Color1 }) - W4.rect!({ x: 16, y: 52, width: 136, height: 32 }) - setTextColors!() - W4.text!("Game Over!", { x: 44, y: 56 }) - W4.text!("Right to restart", { x: 20, y: 72 }) - W4.text!("Art by Luke DeVault", { x: 4, y: 151 }) - W4.set_shape_colors!({ border: Color4, fill: Color1 }) - W4.rect!({ x: 66, y: 2, width: 28, height: 12 }) - drawScore!(state.score, { x: 68, y: 4 }) - - if state.newHighScore { - drawAnimation!(state.highScoreAnim, { x: 64, y: 0, flags: [] }) - } else { - {} - } - - W4.set_shape_colors!({ border: Color4, fill: Color1 }) - W4.rect!({ x: 54, y: 18, width: 52, height: 12 }) - setTextColors!() - W4.text!("HS:", { x: 57, y: 20 }) - drawScore!(state.highScore, { x: 80, y: 20 }) - - gamepad = W4.get_gamepad!(Player1) - mouse = W4.get_mouse!() - if mouse.right or gamepad.button2 or gamepad.right { - plants = startingPlants!() - initTitleScreen(state.frameCount, plants) - } else { - GameOver(state) - } + frame_count : U64, + score : U8, + high_score : U8, + new_high_score : Bool, + player : { + y : F32, + y_vel : F32, + }, + rocci_fall_anim : Animation, + high_score_anim : Animation, + pipes : List(Pipe), + plants : List(Plant), + ground_x : I32, +} + +init_game_over! : GameState => Model +init_game_over! = |prev| { + frame_count = prev.frame_count + max_score = prev.max_score + score = prev.score + player = prev.player + pipes = prev.pipes + plants = prev.plants + ground_x = prev.ground_x + + hs = load_high_score_from_disk!() + new_high_score = max_score > hs + high_score = + if new_high_score { + max_score + } else { + hs + } + save_high_score_to_disk!(high_score) + + GameOver( + { + frame_count, + score, + high_score, + new_high_score, + player, + pipes, + plants, + rocci_fall_anim: create_rocci_fall_anim(frame_count), + high_score_anim: create_high_score_anim(frame_count), + ground_x, + }, + ) +} + +run_game_over! : GameOverState => Model +run_game_over! = |prev| { + y_vel = prev.player.y_vel + gravity + rocci_fall_anim = update_animation(prev.frame_count, prev.rocci_fall_anim) + high_score_anim = update_animation(prev.frame_count, prev.high_score_anim) + + state = { + ..prev, + rocci_fall_anim, + high_score_anim, + player: { y: (prev.player.y + y_vel).min(134), y_vel }, + } + draw_pipes!(state.pipes) + draw_ground!(state.ground_x) + draw_plants!(state.plants) + + y_pixel = F32.to_i32_wrap(state.player.y) + draw_animation!(state.rocci_fall_anim, { x: player_x, y: y_pixel, flags: [] }) + W4.set_shape_colors!({ border: Color4, fill: Color1 }) + W4.rect!({ x: 16, y: 52, width: 136, height: 32 }) + set_text_colors!() + W4.text!("Game Over!", { x: 44, y: 56 }) + W4.text!("Right to restart", { x: 20, y: 72 }) + W4.text!("Art by Luke DeVault", { x: 4, y: 151 }) + W4.set_shape_colors!({ border: Color4, fill: Color1 }) + W4.rect!({ x: 66, y: 2, width: 28, height: 12 }) + draw_score!(state.score, { x: 68, y: 4 }) + + if state.new_high_score { + draw_animation!(state.high_score_anim, { x: 64, y: 0, flags: [] }) + } + + W4.set_shape_colors!({ border: Color4, fill: Color1 }) + W4.rect!({ x: 54, y: 18, width: 52, height: 12 }) + set_text_colors!() + W4.text!("HS:", { x: 57, y: 20 }) + draw_score!(state.high_score, { x: 80, y: 20 }) + + gamepad = W4.get_gamepad!(Player1) + mouse = W4.get_mouse!() + if mouse.right or gamepad.button2 or gamepad.right { + plants = starting_plants!() + init_title_screen(state.frame_count, plants) + } else { + GameOver(state) + } } # ===== Player ============================================ -playerStartY : F32 -playerStartY = 40 - -playerStartYPixel : I32 -playerStartYPixel = 40 - -playerX : I32 -playerX = 70 - -playerCollided! : I32, U64 => Bool -playerCollided! = |playerY, animIndex| { - if playerY >= -1 { - onScreenCollided!(playerY, animIndex) - } else { - offScreenCollided!() - } -} - -onScreenCollided! : I32, U64 => Bool -onScreenCollided! = |playerY, animIndex| { - # This is written in a kinda silly but simple way. - # It checks to ensure a few points in the sprite are all background colored. - # This must be run before drawing the player. - basePoints = [ - { x: 11, y: 2 }, - { x: 13, y: 3 }, - { x: 3, y: 5 }, - { x: 11, y: 6 }, - { x: 9, y: 8 }, - { x: 5, y: 9 }, - { x: 7, y: 10 }, - { x: 5, y: 12 }, - ] - - collisionPoints = - if animIndex == 2 { - List.append(List.append(basePoints, { x: 2, y: 1 }), { x: 7, y: 1 }) - } else if animIndex == 1 { - List.append(basePoints, { x: 2, y: 2 }) - } else { - basePoints - } - - var $collided = Bool.False - for { x, y } in collisionPoints { - if !$collided { - point = { - x: I32.to_u8_wrap(playerX + x), - y: I32.to_u8_wrap(playerY + y), - } - color = W4.get_pixel!(point) - - if color != Color1 { - $collided = Bool.True - } else { - {} - } - } else { - {} - } - } - - $collided -} - -offScreenCollided! : () => Bool -offScreenCollided! = || { - point = { - x: I32.to_u8_wrap(playerX + 13), - y: I32.to_u8_wrap(0), - } - color = W4.get_pixel!(point) - color != Color1 -} +player_start_y : F32 +player_start_y = 40 + +player_start_y_pixel : I32 +player_start_y_pixel = 40 + +player_x : I32 +player_x = 70 + +player_collided! : I32, U64 => Bool +player_collided! = |player_y, anim_index| + if player_y >= -1 { + on_screen_collided!(player_y, anim_index) + } else { + off_screen_collided!() + } + +on_screen_collided! : I32, U64 => Bool +on_screen_collided! = |player_y, anim_index| { + # This is written in a kinda silly but simple way. + # It checks to ensure a few points in the sprite are all background colored. + # This must be run before drawing the player. + base_points = [ + { x: 11, y: 2 }, + { x: 13, y: 3 }, + { x: 3, y: 5 }, + { x: 11, y: 6 }, + { x: 9, y: 8 }, + { x: 5, y: 9 }, + { x: 7, y: 10 }, + { x: 5, y: 12 }, + ] # TODO .iter() once we have iter.append() + + collision_points = + if anim_index == 2 { + base_points.append({ x: 2, y: 1 }).append({ x: 7, y: 1 }) + } else if anim_index == 1 { + base_points.append({ x: 2, y: 2 }) + } else { + base_points + } + + for { x, y } in collision_points { + if W4.get_pixel!({ + x: I32.to_u8_wrap(player_x + x), + y: I32.to_u8_wrap(player_y + y), + }) != Color1 { + return True + } + } + + False +} + +off_screen_collided! : () => Bool +off_screen_collided! = || + W4.get_pixel!({ + x: I32.to_u8_wrap(player_x + 13), + y: I32.to_u8_wrap(0), + }) != Color1 # ===== Pipes ============================================= -Pipe : { x : I32, gapStart : I32 } +Pipe : { x : I32, gap_start : I32 } -gapHeight = 40 +gap_height = 40 -drawPipes! = |sprite, pipes| { - for pipe in pipes { - drawPipe!(sprite, pipe) - } -} +draw_pipes! = |pipes| + pipes.for_each!(|pipe| draw_pipe!(pipe_sprite, pipe)) -drawPipe! : Sprite, Pipe => {} -drawPipe! = |sprite, { x, gapStart }| { - setSpriteColors!() - Sprite.blit!(sprite, { x, y: gapStart - W4.screen_height(), flags: [FlipY] }) - Sprite.blit!(sprite, { x, y: gapStart + gapHeight, flags: [] }) +draw_pipe! : Sprite, Pipe => {} +draw_pipe! = |sprite, { x, gap_start }| { + set_sprite_colors!() + Sprite.blit!(sprite, { x, y: gap_start - W4.screen_height(), flags: [FlipY] }) + Sprite.blit!(sprite, { x, y: gap_start + gap_height, flags: [] }) } -updatePipes : List(Pipe) -> List(Pipe) -updatePipes = |pipes| { - moved = List.map(pipes, |pipe| { ..pipe, x: pipe.x - 1 }) - List.drop_if(moved, |pipe| pipe.x < -20) -} +update_pipes : List(Pipe) -> List(Pipe) +update_pipes = |pipes| + pipes.iter() + .map(|pipe| { ..pipe, x: pipe.x - 1 }) + .drop_if(moved, |pipe| pipe.x < -20) + .collect() -maybeGeneratePipe! : U64, U64 => Try(Pipe, [NoPipe]) -maybeGeneratePipe! = |lastgenerated, framecount| { - if framecount - lastgenerated > 90 { - gapStart = W4.rand_between!({ start: 0, before: 16 }) - Ok({ x: W4.screen_width(), gapStart: gapStart * 5 + 10 }) - } else { - Err(NoPipe) - } -} +maybe_generate_pipe! : U64, U64 => Try(Pipe, [NoPipe]) +maybe_generate_pipe! = |last_generated, frame_count| + if frame_count - last_generated > 90 { + gap_start = W4.rand_between!({ start: 0, before: 16 }) + Ok({ x: W4.screen_width(), gap_start: gap_start * 5 + 10 }) + } else { + Err(NoPipe) + } # ===== Plants ============================================ Plant : { x : I32, type : U32 } -plantTypes = 30 +plant_types = 30 -plantY = W4.screen_height() - 22 +plant_y = W4.screen_height() - 22 -randomPlant! : I32 => Plant -randomPlant! = |x| { - type = I32.to_u32_wrap(W4.rand!()) % plantTypes - { x, type } -} +random_plant! : I32 => Plant +random_plant! = |x| + { x, type: I32.to_u32_wrap(W4.rand!()) % plant_types } -startingPlants! : () => List(Plant) -startingPlants! = || { - var $plants = List.with_capacity(20) - var $i = 0.I32 +starting_plants! : () => List(Plant) +starting_plants! = || + (0..=14).stream() + .map(|i| random_plant!(i * 12)) + .collect!() - while $i < 14 { - plant = randomPlant!($i * 12) - $plants = List.append($plants, plant) - $i = $i + 1 - } +update_plants : List(Plant) -> List(Plant) +update_plants = |plants| + plants.iter() + .map(plants, |plant| { ..plant, x: plant.x - 1 }) + .drop_if(moved, |plant| plant.x < -12) + .collect() - $plants -} +maybe_generate_plant! : U64, U64 => Try(Plant, [NoPlant]) +maybe_generate_plant! = |last_generated, frame_count| + if frame_count - last_generated > 12 { + Ok(random_plant!(W4.screen_width())) + } else { + Err(NoPlant) + } -updatePlants : List(Plant) -> List(Plant) -updatePlants = |plants| { - moved = List.map(plants, |plant| { ..plant, x: plant.x - 1 }) - List.drop_if(moved, |plant| plant.x < -12) -} +draw_plants! : List(Plant) => {} +draw_plants! = |plants| + plants.for_each!(|plant| draw_plant!(plant_sprite_sheet, plant)) -maybeGeneratePlant! : U64, U64 => Try(Plant, [NoPlant]) -maybeGeneratePlant! = |lastgenerated, framecount| { - if framecount - lastgenerated > 12 { - Ok(randomPlant!(W4.screen_width())) - } else { - Err(NoPlant) - } -} - -drawPlants! : Sprite, List(Plant) => {} -drawPlants! = |spriteSheet, plants| { - for plant in plants { - drawPlant!(spriteSheet, plant) - } -} - -drawPlant! : Sprite, Plant => {} -drawPlant! = |spriteSheet, { x, type }| { - sprite = Sprite.sub_or_crash(spriteSheet, { src_x: type * 12, src_y: 0, width: 12, height: 12 }) - setSpriteColors!() - Sprite.blit!(sprite, { x, y: plantY, flags: [] }) +draw_plant! : Sprite, Plant => {} +draw_plant! = |sprite_sheet, { x, type }| { + sprite = Sprite.sub_or_crash(sprite_sheet, { src_x: type * 12, src_y: 0, width: 12, height: 12 }) + set_sprite_colors!() + Sprite.blit!(sprite, { x, y: plant_y, flags: [] }) } # ===== Sounds ============================================ -flapTone = { - start_freq: 700, - end_freq: 870, - channel: Pulse1(Quarter), - pan: Center, - attack_time: 10, - sustain_time: 0, - decay_time: 5, - release_time: 3, - volume: 10, - peak_volume: 20, -} - -pointTone = { - start_freq: 995, - end_freq: 1000, - channel: Pulse2(Half), - pan: Center, - attack_time: 0, - sustain_time: 0, - decay_time: 10, - release_time: 10, - peak_volume: 75, - volume: 25, -} - -deathTone = { - start_freq: 170, - end_freq: 40, - channel: Noise, - pan: Center, - attack_time: 0, - sustain_time: 20, - decay_time: 40, - release_time: 0, - volume: 100, - peak_volume: 0, +flap_tone = { + start_freq: 700, + end_freq: 870, + channel: Pulse1(Quarter), + pan: Center, + attack_time: 10, + sustain_time: 0, + decay_time: 5, + release_time: 3, + volume: 10, + peak_volume: 20, +} + +point_tone = { + start_freq: 995, + end_freq: 1000, + channel: Pulse2(Half), + pan: Center, + attack_time: 0, + sustain_time: 0, + decay_time: 10, + release_time: 10, + peak_volume: 75, + volume: 25, +} + +death_tone = { + start_freq: 170, + end_freq: 40, + channel: Noise, + pan: Center, + attack_time: 0, + sustain_time: 20, + decay_time: 40, + release_time: 0, + volume: 100, + peak_volume: 0, } # ===== Drawing and Color ================================= -drawScore! : U8, { x : I32, y : I32 } => {} -drawScore! = |score, { x: baseX, y }| { - setTextColors!() - x = - if score < 10 { - baseX + 8 - } else if score < 100 { - baseX + 4 - } else { - baseX - } - W4.text!(U8.to_str(score), { x, y }) +draw_score! : U8, { x : I32, y : I32 } => {} +draw_score! = |score, { x: base_x, y }| { + set_text_colors!() + x = + if score < 10 { + base_x + 8 + } else if score < 100 { + base_x + 4 + } else { + base_x + } + W4.text!(U8.to_str(score), { x, y }) } -drawGround! : Sprite, I32 => {} -drawGround! = |sprite, x| { - setGroundColors!() - Sprite.blit!(sprite, { x, y: W4.screen_height() - 13, flags: [] }) - Sprite.blit!(sprite, { x: x + W4.screen_width(), y: W4.screen_height() - 13, flags: [] }) +draw_ground! : I32 => {} +draw_ground! = |x| { + set_ground_colors!() + Sprite.blit!(ground_sprite, { x, y: W4.screen_height() - 13, flags: [] }) + Sprite.blit!(ground_sprite, { x: x + W4.screen_width(), y: W4.screen_height() - 13, flags: [] }) } -setTextColors! : () => {} -setTextColors! = || - W4.set_text_colors!({ fg: Color4, bg: None }) +set_text_colors! : () => {} +set_text_colors! = || + W4.set_text_colors!({ fg: Color4, bg: None }) -setSpriteColors! : () => {} -setSpriteColors! = || - W4.set_draw_colors!({ primary: None, secondary: Color2, tertiary: Color3, quaternary: Color4 }) +set_sprite_colors! : () => {} +set_sprite_colors! = || + W4.set_draw_colors!({ primary: None, secondary: Color2, tertiary: Color3, quaternary: Color4 }) -setGroundColors! : () => {} -setGroundColors! = || - W4.set_draw_colors!({ primary: Color1, secondary: Color2, tertiary: Color3, quaternary: Color4 }) +set_ground_colors! : () => {} +set_ground_colors! = || + W4.set_draw_colors!({ primary: Color1, secondary: Color2, tertiary: Color3, quaternary: Color4 }) # ===== Saving and Loading ================================ # Due to limitations in randomness of wasm4 we would always get the same title screen. -# This save just a single byte of randomness from the frameCount in order to give us a bit more randomness. -saveRandToDisk! : U64 => {} -saveRandToDisk! = |frameCount| { - data = U64.to_u8_wrap(U64.bitwise_and(frameCount, 0xFF)) - highScore = loadHighScoreFromDisk!() - _ = W4.save_to_disk!([data, highScore]) - {} +# This save just a single byte of randomness from the frame_count in order to give us a bit more randomness. +save_rand_to_disk! : U64 => {} +save_rand_to_disk! = |frame_count| { + data = U64.to_u8_wrap(U64.bitwise_and(frame_count, 0xFF)) + high_score = load_high_score_from_disk!() + _ = W4.save_to_disk!([data, high_score]) + {} } -loadRandFromDisk! : () => U64 -loadRandFromDisk! = || { - data = W4.load_from_disk!() - match data { - [byte, ..] => U8.to_u64(byte) - _ => 0 - } +load_rand_from_disk! : () => U64 +load_rand_from_disk! = || { + data = W4.load_from_disk!() + match data { + [byte, ..] => U8.to_u64(byte) + _ => 0 + } } -saveHighScoreToDisk! : U8 => {} -saveHighScoreToDisk! = |highScore| { - rand = loadRandFromDisk!() - _ = W4.save_to_disk!([U64.to_u8_wrap(rand), highScore]) - {} +save_high_score_to_disk! : U8 => {} +save_high_score_to_disk! = |high_score| { + rand = load_rand_from_disk!() + _ = W4.save_to_disk!([U64.to_u8_wrap(rand), high_score]) + {} } -loadHighScoreFromDisk! : () => U8 -loadHighScoreFromDisk! = || { - data = W4.load_from_disk!() - match data { - [_, hs, ..] => hs - _ => 0 - } +load_high_score_from_disk! : () => U8 +load_high_score_from_disk! = || { + data = W4.load_from_disk!() + match data { + [_, hs, ..] => hs + _ => 0 + } } # ===== Animations ======================================== AnimationState : [Completed, RunOnce, Loop] -Animation : { - lastUpdated : U64, - index : U64, - cells : List({ frames : U64, sprite : Sprite }), - state : AnimationState, -} - -updateAnimation : U64, Animation -> Animation -updateAnimation = |frameCount, anim| { - framesPerUpdate = - match List.get(anim.cells, anim.index) { - Ok({ frames }) => frames - Err(_) => { crash "animation cell out of bounds at index: ${U64.to_str(anim.index)}" } - } - - if frameCount - anim.lastUpdated < framesPerUpdate { - anim - } else { - nextIndex = wrappedInc(anim.index, List.len(anim.cells)) - match anim.state { - Completed => - { ..anim, lastUpdated: frameCount } - - Loop => - { ..anim, index: nextIndex, lastUpdated: frameCount } - - RunOnce => - if nextIndex == 0 { - { ..anim, state: Completed, lastUpdated: frameCount } - } else { - { ..anim, index: nextIndex, lastUpdated: frameCount } - } - } - } -} - -drawAnimation! : Animation, { x : I32, y : I32, flags : List([FlipX, FlipY, Rotate]) } => {} -drawAnimation! = |anim, { x, y, flags }| - match List.get(anim.cells, anim.index) { - Ok({ sprite }) => { - setSpriteColors!() - Sprite.blit!(sprite, { x, y, flags }) - } - - Err(_) => { crash "animation cell out of bounds at index: ${U64.to_str(anim.index)}" } - } - -wrappedInc : U64, U64 -> U64 -wrappedInc = |val, count| { - next = val + 1 - if next == count { - 0 - } else { - next - } -} - -idleShift : U64, Animation -> I32 -idleShift = |frameCount, { index, lastUpdated }| - if index == 2 { - 0 - } else if index == 1 and frameCount - lastUpdated > 3 { - 0 - } else { - 1 - } - -createRocciIdleAnim : U64 -> Animation -createRocciIdleAnim = |frameCount| { - lastUpdated: frameCount, - index: 0, - state: Loop, - cells: [ - { frames: 17, sprite: Sprite.sub_or_crash(rocciSpriteSheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, - { frames: 6, sprite: Sprite.sub_or_crash(rocciSpriteSheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, - { frames: 17, sprite: Sprite.sub_or_crash(rocciSpriteSheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, - ], -} - -flapAllowed : U64, Animation -> Bool -flapAllowed = |frameCount, { index, lastUpdated }| - if index == 2 { - Bool.True - } else if index == 1 { - frameCount - lastUpdated > 6 - } else { - Bool.False - } - -createRocciFlapAnim : U64 -> Animation -createRocciFlapAnim = |frameCount| { - lastUpdated: frameCount, - index: 2, - state: Completed, - cells: [ - { frames: 6, sprite: Sprite.sub_or_crash(rocciSpriteSheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, - { frames: 12, sprite: Sprite.sub_or_crash(rocciSpriteSheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, - { frames: 1, sprite: Sprite.sub_or_crash(rocciSpriteSheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, - ], -} -createRocciFallAnim : U64 -> Animation -createRocciFallAnim = |frameCount| { - lastUpdated: frameCount, - index: 0, - state: Loop, - cells: [ - { frames: 10, sprite: Sprite.sub_or_crash(rocciSpriteSheet, { src_x: 48, src_y: 0, width: 16, height: 16 }) }, - { frames: 10, sprite: Sprite.sub_or_crash(rocciSpriteSheet, { src_x: 64, src_y: 0, width: 16, height: 16 }) }, - ], -} - -createHighScoreAnim : U64 -> Animation -createHighScoreAnim = |frameCount| { - lastUpdated: frameCount, - index: 0, - state: Loop, - cells: [ - { frames: 5, sprite: Sprite.sub_or_crash(highScoreSpriteSheet, { src_x: 0, src_y: 0, width: 32, height: 16 }) }, - { frames: 5, sprite: Sprite.sub_or_crash(highScoreSpriteSheet, { src_x: 32, src_y: 0, width: 32, height: 16 }) }, - { frames: 5, sprite: Sprite.sub_or_crash(highScoreSpriteSheet, { src_x: 64, src_y: 0, width: 32, height: 16 }) }, - { frames: 5, sprite: Sprite.sub_or_crash(highScoreSpriteSheet, { src_x: 96, src_y: 0, width: 32, height: 16 }) }, - ], +Animation : { + last_updated : U64, + index : U64, + cells : List({ frames : U64, sprite : Sprite }), + state : AnimationState, +} + +update_animation : U64, Animation -> Animation +update_animation = |frame_count, anim| { + frames_per_update = + match anim.cells.get(anim.index) { + Ok(cell) => cell.frames + Err(_) => crash "animation cell out of bounds at index: ${anim.index.to_str()}" + } + + if frame_count - anim.last_updated < frames_per_update { + anim + } else { + next_index = wrapped_inc(anim.index, anim.cells.len()) + match anim.state { + Completed => { ..anim, last_updated: frame_count } + Loop => { ..anim, index: next_index, last_updated: frame_count } + RunOnce => + if next_index == 0 { + { ..anim, state: Completed, last_updated: frame_count } + } else { + { ..anim, index: next_index, last_updated: frame_count } + } + } + } +} + +draw_animation! : Animation, { x : I32, y : I32, flags : List([FlipX, FlipY, Rotate]) } => {} +draw_animation! = |anim, { x, y, flags }| + match anim.cells.get(anim.index) { + Ok(cell) => { + set_sprite_colors!() + Sprite.blit!(cell.sprite, { x, y, flags }) + } + Err(_) => crash "animation cell out of bounds at index: ${U64.to_str(anim.index)}" + } + +wrapped_inc : U64, U64 -> U64 +wrapped_inc = |val, count| { + next = val + 1 + if next == count { + 0 + } else { + next + } +} + +idle_shift : U64, Animation -> I32 +idle_shift = |frame_count, anim| + if anim.index == 2 { + 0 + } else if anim.index == 1 and frame_count - anim.last_updated > 3 { + 0 + } else { + 1 + } + +create_rocci_idle_anim : U64 -> Animation +create_rocci_idle_anim = |frame_count| { + last_updated: frame_count, + index: 0, + state: Loop, + cells: [ + { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, + { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, + { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, + ], +} + +flap_allowed : U64, Animation -> Bool +flap_allowed = |frame_count, anim| + if anim.index == 2 { + True + } else if anim.index == 1 { + frame_count - anim.last_updated > 6 + } else { + False + } + +create_rocci_flap_anim : U64 -> Animation +create_rocci_flap_anim = |frame_count| { + last_updated: frame_count, + index: 2, + state: Completed, + cells: [ + { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, + { frames: 12, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, + { frames: 1, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, + ], +} + +create_rocci_fall_anim : U64 -> Animation +create_rocci_fall_anim = |frame_count| { + last_updated: frame_count, + index: 0, + state: Loop, + cells: [ + { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 48, src_y: 0, width: 16, height: 16 }) }, + { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 64, src_y: 0, width: 16, height: 16 }) }, + ], +} + +create_high_score_anim : U64 -> Animation +create_high_score_anim = |frame_count| { + last_updated: frame_count, + index: 0, + state: Loop, + cells: [ + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 0, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 32, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 64, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 96, src_y: 0, width: 32, height: 16 }) }, + ], } # ===== Sprites =========================================== -# Due to a compiler bug, all of these will regenerate every frame. -# That is a lot of data initialization. That said, it seems to be fast enough in practice. - -rocciSpriteSheet : Sprite -rocciSpriteSheet = - Sprite.new({ - data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x56, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x80, 0x40, 0x05, 0x56, 0x81, 0x80, 0x14, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x81, 0x40, 0x01, 0x56, 0xa5, 0xa0, 0x15, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x0a, 0x95, 0x00, 0x00, 0x0a, 0x85, 0x40, 0x00, 0x56, 0xa9, 0x00, 0x15, 0x56, 0xa5, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x06, 0x55, 0x00, 0x00, 0x06, 0x55, 0x40, 0x00, 0x06, 0xaa, 0x00, 0x15, 0x5a, 0xaa, 0x00, 0x15, 0x56, 0xaa, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x06, 0x95, 0x00, 0x00, 0x09, 0x55, 0x00, 0x15, 0x6a, 0xa9, 0x00, 0x05, 0x56, 0xa9, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x09, 0x54, 0x00, 0x05, 0x6a, 0x94, 0x00, 0x01, 0x56, 0xa4, 0x00, 0x00, 0x1a, 0x94, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x69, 0x50, 0x00, 0x00, 0x56, 0x90, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - bpp: BPP2, - width: 80, - height: 16, - }) - -groundSprite : Sprite -groundSprite = - Sprite.new({ - data: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x59, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x14, 0x51, 0x85, 0x14, 0x51, 0x49, 0x14, 0x91, 0x45, 0x18, 0x51, 0x54, 0x51, 0x49, 0x24, 0x51, 0x46, 0x14, 0x51, 0x46, 0x15, 0x14, 0x61, 0x45, 0x14, 0x92, 0x45, 0x14, 0x61, 0x46, 0x14, 0x55, 0x14, 0x51, 0x45, 0x14, 0x61, 0x51, 0x45, 0x65, 0x66, 0x56, 0x56, 0x59, 0x56, 0x59, 0x56, 0x55, 0x65, 0x65, 0x56, 0x59, 0x56, 0x59, 0x95, 0x59, 0x59, 0x55, 0x99, 0x59, 0x59, 0x55, 0x95, 0x96, 0x55, 0x99, 0x55, 0x95, 0x95, 0x59, 0x55, 0x96, 0x55, 0x59, 0x59, 0x95, 0x95, 0x96, 0x55, 0x95, 0x95, 0x99, 0x66, 0x65, 0x99, 0x65, 0x96, 0x95, 0x96, 0x59, 0x59, 0x65, 0x99, 0x65, 0xa5, 0x65, 0x96, 0x56, 0x56, 0x65, 0x99, 0x96, 0x59, 0x99, 0x66, 0x5a, 0x56, 0x59, 0x65, 0x96, 0x56, 0x59, 0x66, 0x65, 0x65, 0x66, 0x59, 0x99, 0x59, 0xa6, 0x9a, 0x5a, 0x69, 0x6a, 0x5a, 0x66, 0xa5, 0xa6, 0x9a, 0x9a, 0x6a, 0x6a, 0x5a, 0x65, 0x69, 0xa6, 0xa6, 0x9a, 0x69, 0x69, 0xa5, 0xa6, 0x9a, 0x5a, 0x96, 0x56, 0x9a, 0x6a, 0x6a, 0xa6, 0x9a, 0x9a, 0x96, 0xa9, 0xa6, 0x96, 0x9a, 0x5a, 0x69, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - bpp: BPP2, - width: 160, - height: 13, - }) - -pipeSprite : Sprite -pipeSprite = - Sprite.new({ - data: [0x0a, 0xaa, 0xaa, 0xab, 0xf0, 0x25, 0x55, 0x55, 0x55, 0x5c, 0x26, 0x96, 0x6a, 0x9a, 0xac, 0x36, 0x96, 0x6a, 0x66, 0xac, 0x36, 0x96, 0x6a, 0x9a, 0xac, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0], - bpp: BPP2, - width: 20, - height: 160, - }) - -plantSpriteSheet : Sprite -plantSpriteSheet = - Sprite.new({ - data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x02, 0xaa, 0x00, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xa0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x08, 0x00, 0x80, 0x08, 0x08, 0x80, 0x00, 0x2a, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x11, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x05, 0x14, 0x00, 0x28, 0x00, 0x51, 0x45, 0x00, 0x20, 0x01, 0x60, 0x20, 0x81, 0x60, 0x00, 0x80, 0x80, 0x0a, 0xa0, 0x16, 0x00, 0xa8, 0x00, 0x82, 0x15, 0x96, 0x02, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x02, 0xa0, 0x00, 0x00, 0x28, 0x00, 0x02, 0x20, 0x00, 0x02, 0x82, 0x00, 0x06, 0x11, 0x11, 0x02, 0xb2, 0xb0, 0x02, 0x80, 0x08, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x40, 0x15, 0x11, 0x15, 0x00, 0x00, 0x00, 0x24, 0x20, 0x80, 0x51, 0x41, 0x50, 0x00, 0x22, 0x00, 0x14, 0x41, 0x44, 0x20, 0x55, 0x60, 0x28, 0x59, 0xa0, 0x02, 0x01, 0x60, 0x20, 0x21, 0x56, 0x02, 0x02, 0x00, 0x83, 0x55, 0xd6, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x10, 0x40, 0x02, 0xa8, 0x80, 0x09, 0x5c, 0x00, 0x08, 0x20, 0x08, 0x02, 0x00, 0x00, 0x0a, 0x0a, 0x28, 0x91, 0x44, 0x66, 0x09, 0x6d, 0x5c, 0x0a, 0x08, 0x20, 0x02, 0x0b, 0x00, 0x00, 0x20, 0x00, 0x02, 0x06, 0x00, 0x0a, 0x15, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x15, 0xa0, 0x15, 0x15, 0x08, 0x00, 0x00, 0x00, 0x04, 0x86, 0x60, 0x15, 0x80, 0x48, 0x20, 0x02, 0x08, 0x50, 0x52, 0x94, 0x21, 0x55, 0x60, 0x21, 0x95, 0x60, 0x02, 0x15, 0x60, 0x80, 0x5a, 0xa8, 0x08, 0x05, 0x80, 0x81, 0x69, 0x56, 0x0a, 0xea, 0x00, 0x00, 0x28, 0x00, 0x02, 0xab, 0x80, 0x04, 0x45, 0x10, 0x0a, 0xea, 0x80, 0x03, 0x9a, 0x00, 0x28, 0xa8, 0x20, 0x02, 0x00, 0x00, 0x28, 0x08, 0xa0, 0x45, 0x19, 0x18, 0x03, 0xef, 0xeb, 0x28, 0x28, 0xa8, 0x08, 0x20, 0xc0, 0x00, 0x88, 0x00, 0x02, 0x05, 0x80, 0x26, 0x16, 0x18, 0x02, 0x00, 0x00, 0x02, 0x8a, 0x80, 0x0a, 0x15, 0x28, 0x00, 0x00, 0x08, 0x05, 0x99, 0xb0, 0x26, 0x08, 0xa0, 0x88, 0x02, 0x22, 0xa1, 0x42, 0x05, 0x0a, 0xaa, 0x80, 0x0a, 0xaa, 0x80, 0x00, 0xaa, 0x80, 0x85, 0x5a, 0x70, 0x08, 0x55, 0x80, 0x95, 0x55, 0x56, 0x2b, 0xae, 0x80, 0x00, 0x88, 0x00, 0x0a, 0xee, 0xa0, 0x11, 0x11, 0x40, 0x0b, 0xab, 0xa0, 0x0b, 0xed, 0xe0, 0xba, 0xba, 0xa0, 0x02, 0xa8, 0x00, 0x2a, 0x2a, 0xb8, 0x1a, 0x6a, 0x78, 0x29, 0xbe, 0x68, 0xae, 0xaa, 0xb8, 0x31, 0x81, 0xc0, 0x00, 0x80, 0x00, 0x02, 0x55, 0x80, 0x87, 0x18, 0x18, 0x09, 0x82, 0x80, 0x00, 0xaa, 0x00, 0x28, 0x08, 0x0a, 0x08, 0x20, 0x20, 0x05, 0x65, 0xd0, 0x08, 0x02, 0x00, 0x08, 0xaa, 0x20, 0x28, 0x8a, 0x14, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x2a, 0xa2, 0x70, 0x02, 0xaa, 0x00, 0x2a, 0xaa, 0xa8, 0x2b, 0xba, 0xa0, 0x00, 0x87, 0x00, 0x2b, 0xae, 0xa8, 0x06, 0x66, 0x80, 0x0b, 0xae, 0xb8, 0x25, 0xb9, 0x5c, 0xba, 0xea, 0xe8, 0x02, 0xe0, 0x00, 0xae, 0xae, 0xea, 0x0b, 0xaa, 0xe0, 0x96, 0x97, 0x96, 0xba, 0xba, 0xea, 0x31, 0x85, 0x70, 0x00, 0xa0, 0x00, 0x02, 0x67, 0x00, 0x87, 0x1c, 0x57, 0x21, 0xc8, 0x70, 0x00, 0x2c, 0x00, 0x08, 0x0a, 0x08, 0x28, 0xa8, 0xa8, 0x01, 0x5f, 0x54, 0x20, 0x00, 0x80, 0x28, 0x2e, 0x28, 0x22, 0x82, 0x0a, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x09, 0xc2, 0x70, 0x00, 0x9c, 0x00, 0x08, 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xce, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xac, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - bpp: BPP2, - width: 360, - height: 12, - }) - -highScoreSpriteSheet : Sprite -highScoreSpriteSheet = Sprite.new({ - data: [0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x09, 0x60, 0x25, 0x80, 0x96, 0x02, 0x58, 0x00, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x25, 0x80, 0x96, 0x02, 0x58, 0x09, 0x60, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00], - bpp: BPP2, - width: 128, - height: 16, -}) +# Note: These could be stored in separate files and then imported +# as List(U8) values using `import "..."` but this way the entire +# game can be in one source code file. + +rocci_sprite_sheet : Sprite +rocci_sprite_sheet = + Sprite.new( + { + data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x56, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x80, 0x40, 0x05, 0x56, 0x81, 0x80, 0x14, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x81, 0x40, 0x01, 0x56, 0xa5, 0xa0, 0x15, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x0a, 0x95, 0x00, 0x00, 0x0a, 0x85, 0x40, 0x00, 0x56, 0xa9, 0x00, 0x15, 0x56, 0xa5, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x06, 0x55, 0x00, 0x00, 0x06, 0x55, 0x40, 0x00, 0x06, 0xaa, 0x00, 0x15, 0x5a, 0xaa, 0x00, 0x15, 0x56, 0xaa, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x06, 0x95, 0x00, 0x00, 0x09, 0x55, 0x00, 0x15, 0x6a, 0xa9, 0x00, 0x05, 0x56, 0xa9, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x09, 0x54, 0x00, 0x05, 0x6a, 0x94, 0x00, 0x01, 0x56, 0xa4, 0x00, 0x00, 0x1a, 0x94, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x69, 0x50, 0x00, 0x00, 0x56, 0x90, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + bpp: BPP2, + width: 80, + height: 16, + }, + ) + +ground_sprite : Sprite +ground_sprite = + Sprite.new( + { + data: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x59, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x14, 0x51, 0x85, 0x14, 0x51, 0x49, 0x14, 0x91, 0x45, 0x18, 0x51, 0x54, 0x51, 0x49, 0x24, 0x51, 0x46, 0x14, 0x51, 0x46, 0x15, 0x14, 0x61, 0x45, 0x14, 0x92, 0x45, 0x14, 0x61, 0x46, 0x14, 0x55, 0x14, 0x51, 0x45, 0x14, 0x61, 0x51, 0x45, 0x65, 0x66, 0x56, 0x56, 0x59, 0x56, 0x59, 0x56, 0x55, 0x65, 0x65, 0x56, 0x59, 0x56, 0x59, 0x95, 0x59, 0x59, 0x55, 0x99, 0x59, 0x59, 0x55, 0x95, 0x96, 0x55, 0x99, 0x55, 0x95, 0x95, 0x59, 0x55, 0x96, 0x55, 0x59, 0x59, 0x95, 0x95, 0x96, 0x55, 0x95, 0x95, 0x99, 0x66, 0x65, 0x99, 0x65, 0x96, 0x95, 0x96, 0x59, 0x59, 0x65, 0x99, 0x65, 0xa5, 0x65, 0x96, 0x56, 0x56, 0x65, 0x99, 0x96, 0x59, 0x99, 0x66, 0x5a, 0x56, 0x59, 0x65, 0x96, 0x56, 0x59, 0x66, 0x65, 0x65, 0x66, 0x59, 0x99, 0x59, 0xa6, 0x9a, 0x5a, 0x69, 0x6a, 0x5a, 0x66, 0xa5, 0xa6, 0x9a, 0x9a, 0x6a, 0x6a, 0x5a, 0x65, 0x69, 0xa6, 0xa6, 0x9a, 0x69, 0x69, 0xa5, 0xa6, 0x9a, 0x5a, 0x96, 0x56, 0x9a, 0x6a, 0x6a, 0xa6, 0x9a, 0x9a, 0x96, 0xa9, 0xa6, 0x96, 0x9a, 0x5a, 0x69, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], + bpp: BPP2, + width: 160, + height: 13, + }, + ) + +pipe_sprite : Sprite +pipe_sprite = + Sprite.new( + { + data: [0x0a, 0xaa, 0xaa, 0xab, 0xf0, 0x25, 0x55, 0x55, 0x55, 0x5c, 0x26, 0x96, 0x6a, 0x9a, 0xac, 0x36, 0x96, 0x6a, 0x66, 0xac, 0x36, 0x96, 0x6a, 0x9a, 0xac, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0], + bpp: BPP2, + width: 20, + height: 160, + }, + ) + +plant_sprite_sheet : Sprite +plant_sprite_sheet = + Sprite.new( + { + data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x02, 0xaa, 0x00, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xa0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x08, 0x00, 0x80, 0x08, 0x08, 0x80, 0x00, 0x2a, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x11, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x05, 0x14, 0x00, 0x28, 0x00, 0x51, 0x45, 0x00, 0x20, 0x01, 0x60, 0x20, 0x81, 0x60, 0x00, 0x80, 0x80, 0x0a, 0xa0, 0x16, 0x00, 0xa8, 0x00, 0x82, 0x15, 0x96, 0x02, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x02, 0xa0, 0x00, 0x00, 0x28, 0x00, 0x02, 0x20, 0x00, 0x02, 0x82, 0x00, 0x06, 0x11, 0x11, 0x02, 0xb2, 0xb0, 0x02, 0x80, 0x08, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x40, 0x15, 0x11, 0x15, 0x00, 0x00, 0x00, 0x24, 0x20, 0x80, 0x51, 0x41, 0x50, 0x00, 0x22, 0x00, 0x14, 0x41, 0x44, 0x20, 0x55, 0x60, 0x28, 0x59, 0xa0, 0x02, 0x01, 0x60, 0x20, 0x21, 0x56, 0x02, 0x02, 0x00, 0x83, 0x55, 0xd6, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x10, 0x40, 0x02, 0xa8, 0x80, 0x09, 0x5c, 0x00, 0x08, 0x20, 0x08, 0x02, 0x00, 0x00, 0x0a, 0x0a, 0x28, 0x91, 0x44, 0x66, 0x09, 0x6d, 0x5c, 0x0a, 0x08, 0x20, 0x02, 0x0b, 0x00, 0x00, 0x20, 0x00, 0x02, 0x06, 0x00, 0x0a, 0x15, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x15, 0xa0, 0x15, 0x15, 0x08, 0x00, 0x00, 0x00, 0x04, 0x86, 0x60, 0x15, 0x80, 0x48, 0x20, 0x02, 0x08, 0x50, 0x52, 0x94, 0x21, 0x55, 0x60, 0x21, 0x95, 0x60, 0x02, 0x15, 0x60, 0x80, 0x5a, 0xa8, 0x08, 0x05, 0x80, 0x81, 0x69, 0x56, 0x0a, 0xea, 0x00, 0x00, 0x28, 0x00, 0x02, 0xab, 0x80, 0x04, 0x45, 0x10, 0x0a, 0xea, 0x80, 0x03, 0x9a, 0x00, 0x28, 0xa8, 0x20, 0x02, 0x00, 0x00, 0x28, 0x08, 0xa0, 0x45, 0x19, 0x18, 0x03, 0xef, 0xeb, 0x28, 0x28, 0xa8, 0x08, 0x20, 0xc0, 0x00, 0x88, 0x00, 0x02, 0x05, 0x80, 0x26, 0x16, 0x18, 0x02, 0x00, 0x00, 0x02, 0x8a, 0x80, 0x0a, 0x15, 0x28, 0x00, 0x00, 0x08, 0x05, 0x99, 0xb0, 0x26, 0x08, 0xa0, 0x88, 0x02, 0x22, 0xa1, 0x42, 0x05, 0x0a, 0xaa, 0x80, 0x0a, 0xaa, 0x80, 0x00, 0xaa, 0x80, 0x85, 0x5a, 0x70, 0x08, 0x55, 0x80, 0x95, 0x55, 0x56, 0x2b, 0xae, 0x80, 0x00, 0x88, 0x00, 0x0a, 0xee, 0xa0, 0x11, 0x11, 0x40, 0x0b, 0xab, 0xa0, 0x0b, 0xed, 0xe0, 0xba, 0xba, 0xa0, 0x02, 0xa8, 0x00, 0x2a, 0x2a, 0xb8, 0x1a, 0x6a, 0x78, 0x29, 0xbe, 0x68, 0xae, 0xaa, 0xb8, 0x31, 0x81, 0xc0, 0x00, 0x80, 0x00, 0x02, 0x55, 0x80, 0x87, 0x18, 0x18, 0x09, 0x82, 0x80, 0x00, 0xaa, 0x00, 0x28, 0x08, 0x0a, 0x08, 0x20, 0x20, 0x05, 0x65, 0xd0, 0x08, 0x02, 0x00, 0x08, 0xaa, 0x20, 0x28, 0x8a, 0x14, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x2a, 0xa2, 0x70, 0x02, 0xaa, 0x00, 0x2a, 0xaa, 0xa8, 0x2b, 0xba, 0xa0, 0x00, 0x87, 0x00, 0x2b, 0xae, 0xa8, 0x06, 0x66, 0x80, 0x0b, 0xae, 0xb8, 0x25, 0xb9, 0x5c, 0xba, 0xea, 0xe8, 0x02, 0xe0, 0x00, 0xae, 0xae, 0xea, 0x0b, 0xaa, 0xe0, 0x96, 0x97, 0x96, 0xba, 0xba, 0xea, 0x31, 0x85, 0x70, 0x00, 0xa0, 0x00, 0x02, 0x67, 0x00, 0x87, 0x1c, 0x57, 0x21, 0xc8, 0x70, 0x00, 0x2c, 0x00, 0x08, 0x0a, 0x08, 0x28, 0xa8, 0xa8, 0x01, 0x5f, 0x54, 0x20, 0x00, 0x80, 0x28, 0x2e, 0x28, 0x22, 0x82, 0x0a, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x09, 0xc2, 0x70, 0x00, 0x9c, 0x00, 0x08, 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xce, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xac, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + bpp: BPP2, + width: 360, + height: 12, + }, + ) + +high_score_sprite_sheet : Sprite +high_score_sprite_sheet = Sprite.new( + { + data: [0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x09, 0x60, 0x25, 0x80, 0x96, 0x02, 0x58, 0x00, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x25, 0x80, 0x96, 0x02, 0x58, 0x09, 0x60, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00], + bpp: BPP2, + width: 128, + height: 16, + }, +) From 8a03d01c16013ef3c4cd05d80f3b1a324830930e Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 07:26:10 -0400 Subject: [PATCH 02/19] Update wasm4 memory config and rocci-bird --- build.zig | 7 +-- examples/rocci-bird.roc | 95 +++++++++++++++++++++++------------------ platform/main.roc | 9 +++- src/allocator.zig | 2 - 4 files changed, 65 insertions(+), 48 deletions(-) diff --git a/build.zig b/build.zig index cdef5d3..f350509 100644 --- a/build.zig +++ b/build.zig @@ -15,13 +15,14 @@ pub fn build(b: *std.Build) void { // Build options exposed as the `config` module to allocator.zig / host.zig. // --------------------------------------------------------------------- - // Full mem size on WASM-4 is 58976, but we have to leave room for code + constants. - // Was u16 in the old build; widened to u32 for headroom. + // WASM-4 gives programs 58976 bytes after its reserved hardware range. The + // final module also needs room there for stack and Roc static data, so the + // default allocator arena intentionally leaves several KiB free. const mem_size = b.option( u32, "mem-size", "the amount of space reserved for dynamic memory allocation", - ) orelse 40960; + ) orelse 32768; // Enable this if you hit any sort of memory corruption. Costs performance. const zero_on_alloc = b.option( diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index 4fd06d3..ab73fbd 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -84,7 +84,7 @@ run_title_screen! = |prev| { set_text_colors!() W4.text!("Rocci Bird!!!", { x: 32, y: 12 }) W4.text!("Click to start!", { x: 24, y: 72 }) - draw_ground!(ground_sprite, 0) + draw_ground!(0) draw_plants!(state.plants) shift = idle_shift(state.frame_count, state.rocci_idle_anim) @@ -121,29 +121,34 @@ GameState : { } init_game! : TitleScreenState => Model -init_game! = |{ frame_count, plants }| { +init_game! = |state| { + frame_count = state.frame_count + plants = state.plants + # Seed the randomness with number of frames since the start of the game. # This makes the game feel like it is truely randomly seeded cause players won't always start on the same frame. save_rand_to_disk!(frame_count) W4.seed_rand!(frame_count) W4.tone!(flap_tone) - Game({ - frame_count, - score: 0, - max_score: 0, - player: { - y: player_start_y, - y_vel: jump_speed, + Game( + { + frame_count, + score: 0, + max_score: 0, + player: { + y: player_start_y, + y_vel: jump_speed, + }, + last_pipe_generated: frame_count, + pipes: [], + plants, + last_plant_generated: sub_saturating_u64(frame_count, 4), + last_flap: True, + rocci_flap_anim: create_rocci_flap_anim(frame_count), + ground_x: 0, }, - last_pipe_generated: frame_count, - pipes: [], - plants, - last_plant_generated: sub_saturating_u64(frame_count, 4), - last_flap: True, - rocci_flap_anim: create_rocci_flap_anim(frame_count), - ground_x: 0, - }) + ) } # Useful to throw in WolframAlpha to help calculate these: @@ -376,7 +381,7 @@ on_screen_collided! = |player_y, anim_index| { collision_points = if anim_index == 2 { - base_points.append({ x: 2, y: 1 }).append({ x: 7, y: 1 }) + base_points.append({ x: 2, y: 1 }).append({ x: 7, y: 1 }) } else if anim_index == 1 { base_points.append({ x: 2, y: 2 }) } else { @@ -384,10 +389,12 @@ on_screen_collided! = |player_y, anim_index| { } for { x, y } in collision_points { - if W4.get_pixel!({ - x: I32.to_u8_wrap(player_x + x), - y: I32.to_u8_wrap(player_y + y), - }) != Color1 { + if W4.get_pixel!( + { + x: I32.to_u8_wrap(player_x + x), + y: I32.to_u8_wrap(player_y + y), + }, + ) != Color1 { return True } } @@ -397,10 +404,12 @@ on_screen_collided! = |player_y, anim_index| { off_screen_collided! : () => Bool off_screen_collided! = || - W4.get_pixel!({ - x: I32.to_u8_wrap(player_x + 13), - y: I32.to_u8_wrap(0), - }) != Color1 + W4.get_pixel!( + { + x: I32.to_u8_wrap(player_x + 13), + y: I32.to_u8_wrap(0), + }, + ) != Color1 # ===== Pipes ============================================= @@ -409,7 +418,7 @@ Pipe : { x : I32, gap_start : I32 } gap_height = 40 draw_pipes! = |pipes| - pipes.for_each!(|pipe| draw_pipe!(pipe_sprite, pipe)) + pipes.for_each!(|pipe| draw_pipe!(pipe_sprite, pipe)) draw_pipe! : Sprite, Pipe => {} draw_pipe! = |sprite, { x, gap_start }| { @@ -421,9 +430,9 @@ draw_pipe! = |sprite, { x, gap_start }| { update_pipes : List(Pipe) -> List(Pipe) update_pipes = |pipes| pipes.iter() - .map(|pipe| { ..pipe, x: pipe.x - 1 }) - .drop_if(moved, |pipe| pipe.x < -20) - .collect() + .map(|pipe| { ..pipe, x: pipe.x - 1 }) + .drop_if(|pipe| pipe.x < -20) + .collect() maybe_generate_pipe! : U64, U64 => Try(Pipe, [NoPipe]) maybe_generate_pipe! = |last_generated, frame_count| @@ -444,20 +453,20 @@ plant_y = W4.screen_height() - 22 random_plant! : I32 => Plant random_plant! = |x| - { x, type: I32.to_u32_wrap(W4.rand!()) % plant_types } + { x, type: I32.to_u32_wrap(W4.rand!()) % plant_types } starting_plants! : () => List(Plant) starting_plants! = || - (0..=14).stream() - .map(|i| random_plant!(i * 12)) - .collect!() + (0..=14).stream() + .map(|i| random_plant!(i * 12)) + .collect!() update_plants : List(Plant) -> List(Plant) update_plants = |plants| - plants.iter() - .map(plants, |plant| { ..plant, x: plant.x - 1 }) - .drop_if(moved, |plant| plant.x < -12) - .collect() + plants.iter() + .map(|plant| { ..plant, x: plant.x - 1 }) + .drop_if(|plant| plant.x < -12) + .collect() maybe_generate_plant! : U64, U64 => Try(Plant, [NoPlant]) maybe_generate_plant! = |last_generated, frame_count| @@ -469,7 +478,7 @@ maybe_generate_plant! = |last_generated, frame_count| draw_plants! : List(Plant) => {} draw_plants! = |plants| - plants.for_each!(|plant| draw_plant!(plant_sprite_sheet, plant)) + plants.for_each!(|plant| draw_plant!(plant_sprite_sheet, plant)) draw_plant! : Sprite, Plant => {} draw_plant! = |sprite_sheet, { x, type }| { @@ -607,7 +616,9 @@ update_animation = |frame_count, anim| { frames_per_update = match anim.cells.get(anim.index) { Ok(cell) => cell.frames - Err(_) => crash "animation cell out of bounds at index: ${anim.index.to_str()}" + Err(_) => { + crash "animation cell out of bounds at index: ${anim.index.to_str()}" + } } if frame_count - anim.last_updated < frames_per_update { @@ -634,7 +645,9 @@ draw_animation! = |anim, { x, y, flags }| set_sprite_colors!() Sprite.blit!(cell.sprite, { x, y, flags }) } - Err(_) => crash "animation cell out of bounds at index: ${U64.to_str(anim.index)}" + Err(_) => { + crash "animation cell out of bounds at index: ${U64.to_str(anim.index)}" + } } wrapped_inc : U64, U64 -> U64 diff --git a/platform/main.roc b/platform/main.roc index 778e553..a5e18d4 100644 --- a/platform/main.roc +++ b/platform/main.roc @@ -38,14 +38,19 @@ platform "" wasm32: { inputs: ["host.wasm", app], output: Shared, - import_memory: True, + import_memory: Zeroed, minimum_memory: 65536, maximum_memory: 65536, initial_stack_size: 14752, - global_base: 6592, + global_base: wasm4_program_memory_base, }, } +# WASM-4 owns 0x0000..0x199f for registers and the framebuffer. +# Program data starts after that reserved range, rounded up to 32-byte alignment. +wasm4_reserved_memory_end = 0x19a0 +wasm4_program_memory_base = 0x19c0 + import W4 import Sprite import Host diff --git a/src/allocator.zig b/src/allocator.zig index 7061131..c3fcc32 100644 --- a/src/allocator.zig +++ b/src/allocator.zig @@ -51,8 +51,6 @@ fn blockDataPtr(c: u15) *anyopaque { } pub fn init() void { - @memset(MEM[0..], 0); - // The zeroth block is special. // Labelled as allocated, but is really just the head of the free list. BLOCKS[0] = .{ From 54350489d4572ec453ea1a47f96de094c0d3dd64 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 09:59:44 -0400 Subject: [PATCH 03/19] Add Rust Rocci Bird comparison --- examples/rocci-bird-rust/.cargo/config.toml | 14 + examples/rocci-bird-rust/.gitignore | 1 + examples/rocci-bird-rust/Cargo.lock | 7 + examples/rocci-bird-rust/Cargo.toml | 14 + examples/rocci-bird-rust/src/lib.rs | 865 ++++++++++++++++++++ examples/rocci-bird-rust/src/sprites.rs | 234 ++++++ examples/rocci-bird-rust/src/wasm4.rs | 227 +++++ examples/rocci-bird.roc | 70 +- 8 files changed, 1402 insertions(+), 30 deletions(-) create mode 100644 examples/rocci-bird-rust/.cargo/config.toml create mode 100644 examples/rocci-bird-rust/.gitignore create mode 100644 examples/rocci-bird-rust/Cargo.lock create mode 100644 examples/rocci-bird-rust/Cargo.toml create mode 100644 examples/rocci-bird-rust/src/lib.rs create mode 100644 examples/rocci-bird-rust/src/sprites.rs create mode 100644 examples/rocci-bird-rust/src/wasm4.rs diff --git a/examples/rocci-bird-rust/.cargo/config.toml b/examples/rocci-bird-rust/.cargo/config.toml new file mode 100644 index 0000000..3d2836d --- /dev/null +++ b/examples/rocci-bird-rust/.cargo/config.toml @@ -0,0 +1,14 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "link-arg=--import-memory", + "-C", "link-arg=--initial-memory=65536", + "-C", "link-arg=--max-memory=65536", + "-C", "link-arg=--allow-undefined", + "-C", "link-arg=--no-stack-first", + "-C", "link-arg=--global-base=6592", + "-C", "link-arg=-z", + "-C", "link-arg=stack-size=14752", +] diff --git a/examples/rocci-bird-rust/.gitignore b/examples/rocci-bird-rust/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/examples/rocci-bird-rust/.gitignore @@ -0,0 +1 @@ +/target diff --git a/examples/rocci-bird-rust/Cargo.lock b/examples/rocci-bird-rust/Cargo.lock new file mode 100644 index 0000000..d4292f6 --- /dev/null +++ b/examples/rocci-bird-rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "rocci-bird-rust" +version = "0.1.0" diff --git a/examples/rocci-bird-rust/Cargo.toml b/examples/rocci-bird-rust/Cargo.toml new file mode 100644 index 0000000..be7fdab --- /dev/null +++ b/examples/rocci-bird-rust/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rocci-bird-rust" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[profile.release] +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" +strip = true diff --git a/examples/rocci-bird-rust/src/lib.rs b/examples/rocci-bird-rust/src/lib.rs new file mode 100644 index 0000000..ddf1235 --- /dev/null +++ b/examples/rocci-bird-rust/src/lib.rs @@ -0,0 +1,865 @@ +#![no_std] +#![allow(static_mut_refs)] + +mod sprites; +mod wasm4; + +use core::panic::PanicInfo; +use wasm4::*; + +const SCREEN: i32 = 160; +const PLAYER_X: i32 = 70; +const PLAYER_START_Y: f32 = 40.0; +const PLAYER_START_Y_PIXEL: i32 = 40; +const GRAVITY: f32 = 0.12; +const JUMP_SPEED: f32 = -2.2; +const GAP_HEIGHT: i32 = 40; +const PLANT_TYPES: u32 = 30; +const PLANT_Y: i32 = SCREEN - 22; +const MAX_PIPES: usize = 8; +const MAX_PLANTS: usize = 32; +const PRNG_DEFAULT: u32 = 0x6d2b_79f5; + +const COLOR_NONE: u16 = 0; +const COLOR1: u16 = 1; +const COLOR2: u16 = 2; +const COLOR3: u16 = 3; +const COLOR4: u16 = 4; + +#[panic_handler] +fn panic(_: &PanicInfo) -> ! { + loop {} +} + +#[derive(Copy, Clone)] +struct Region { + src_x: u32, + src_y: u32, + width: u32, + height: u32, +} + +#[derive(Copy, Clone)] +struct Sprite { + data: &'static [u8], + bpp: u32, + stride: u32, + region: Region, +} + +const fn sprite(data: &'static [u8], bpp: u32, width: u32, height: u32) -> Sprite { + Sprite { + data, + bpp, + stride: width, + region: Region { + src_x: 0, + src_y: 0, + width, + height, + }, + } +} + +const fn sub_sprite(sprite: Sprite, src_x: u32, src_y: u32, width: u32, height: u32) -> Sprite { + Sprite { + data: sprite.data, + bpp: sprite.bpp, + stride: sprite.stride, + region: Region { + src_x: sprite.region.src_x + src_x, + src_y: sprite.region.src_y + src_y, + width, + height, + }, + } +} + +const ROCCI_SPRITE_SHEET: Sprite = sprite( + &sprites::ROCCI_SPRITE_SHEET_DATA, + sprites::ROCCI_SPRITE_SHEET_BPP, + sprites::ROCCI_SPRITE_SHEET_WIDTH, + sprites::ROCCI_SPRITE_SHEET_HEIGHT, +); +const GROUND_SPRITE: Sprite = sprite( + &sprites::GROUND_SPRITE_DATA, + sprites::GROUND_SPRITE_BPP, + sprites::GROUND_SPRITE_WIDTH, + sprites::GROUND_SPRITE_HEIGHT, +); +const PIPE_SPRITE: Sprite = sprite( + &sprites::PIPE_SPRITE_DATA, + sprites::PIPE_SPRITE_BPP, + sprites::PIPE_SPRITE_WIDTH, + sprites::PIPE_SPRITE_HEIGHT, +); +const PLANT_SPRITE_SHEET: Sprite = sprite( + &sprites::PLANT_SPRITE_SHEET_DATA, + sprites::PLANT_SPRITE_SHEET_BPP, + sprites::PLANT_SPRITE_SHEET_WIDTH, + sprites::PLANT_SPRITE_SHEET_HEIGHT, +); +const HIGH_SCORE_SPRITE_SHEET: Sprite = sprite( + &sprites::HIGH_SCORE_SPRITE_SHEET_DATA, + sprites::HIGH_SCORE_SPRITE_SHEET_BPP, + sprites::HIGH_SCORE_SPRITE_SHEET_WIDTH, + sprites::HIGH_SCORE_SPRITE_SHEET_HEIGHT, +); + +#[derive(Copy, Clone)] +struct Cell { + frames: u64, + sprite: Sprite, +} + +static IDLE_CELLS: [Cell; 3] = [ + Cell { frames: 17, sprite: sub_sprite(ROCCI_SPRITE_SHEET, 0, 0, 16, 16) }, + Cell { frames: 6, sprite: sub_sprite(ROCCI_SPRITE_SHEET, 16, 0, 16, 16) }, + Cell { frames: 17, sprite: sub_sprite(ROCCI_SPRITE_SHEET, 32, 0, 16, 16) }, +]; + +static FLAP_CELLS: [Cell; 3] = [ + Cell { frames: 6, sprite: sub_sprite(ROCCI_SPRITE_SHEET, 16, 0, 16, 16) }, + Cell { frames: 12, sprite: sub_sprite(ROCCI_SPRITE_SHEET, 32, 0, 16, 16) }, + Cell { frames: 1, sprite: sub_sprite(ROCCI_SPRITE_SHEET, 0, 0, 16, 16) }, +]; + +static FALL_CELLS: [Cell; 2] = [ + Cell { frames: 10, sprite: sub_sprite(ROCCI_SPRITE_SHEET, 48, 0, 16, 16) }, + Cell { frames: 10, sprite: sub_sprite(ROCCI_SPRITE_SHEET, 64, 0, 16, 16) }, +]; + +static HIGH_SCORE_CELLS: [Cell; 4] = [ + Cell { frames: 5, sprite: sub_sprite(HIGH_SCORE_SPRITE_SHEET, 0, 0, 32, 16) }, + Cell { frames: 5, sprite: sub_sprite(HIGH_SCORE_SPRITE_SHEET, 32, 0, 32, 16) }, + Cell { frames: 5, sprite: sub_sprite(HIGH_SCORE_SPRITE_SHEET, 64, 0, 32, 16) }, + Cell { frames: 5, sprite: sub_sprite(HIGH_SCORE_SPRITE_SHEET, 96, 0, 32, 16) }, +]; + +#[derive(Copy, Clone)] +enum AnimationState { + Completed, + RunOnce, + Loop, +} + +#[derive(Copy, Clone)] +struct Animation { + last_updated: u64, + index: usize, + cells: &'static [Cell], + state: AnimationState, +} + +#[derive(Copy, Clone)] +struct Player { + y: f32, + y_vel: f32, +} + +#[derive(Copy, Clone)] +struct Pipe { + x: i32, + gap_start: i32, +} + +#[derive(Copy, Clone)] +struct Plant { + x: i32, + kind: u32, +} + +const EMPTY_PIPE: Pipe = Pipe { x: 0, gap_start: 0 }; +const EMPTY_PLANT: Plant = Plant { x: 0, kind: 0 }; + +#[derive(Copy, Clone)] +struct Pipes { + items: [Pipe; MAX_PIPES], + len: usize, +} + +#[derive(Copy, Clone)] +struct Plants { + items: [Plant; MAX_PLANTS], + len: usize, +} + +impl Pipes { + const fn empty() -> Self { + Self { items: [EMPTY_PIPE; MAX_PIPES], len: 0 } + } + + fn push(&mut self, pipe: Pipe) { + if self.len < MAX_PIPES { + self.items[self.len] = pipe; + self.len += 1; + } + } +} + +impl Plants { + const fn empty() -> Self { + Self { items: [EMPTY_PLANT; MAX_PLANTS], len: 0 } + } + + fn push(&mut self, plant: Plant) { + if self.len < MAX_PLANTS { + self.items[self.len] = plant; + self.len += 1; + } + } +} + +#[derive(Copy, Clone)] +enum Mode { + Title, + Game, + GameOver, +} + +#[derive(Copy, Clone)] +struct State { + mode: Mode, + frame_count: u64, + plants: Plants, + rocci_idle_anim: Animation, + score: u8, + max_score: u8, + high_score: u8, + new_high_score: bool, + player: Player, + last_flap: bool, + rocci_flap_anim: Animation, + rocci_fall_anim: Animation, + high_score_anim: Animation, + pipes: Pipes, + last_pipe_generated: u64, + last_plant_generated: u64, + ground_x: i32, +} + +const INITIAL_ANIM: Animation = Animation { + last_updated: 0, + index: 0, + cells: &IDLE_CELLS, + state: AnimationState::Loop, +}; + +const INITIAL_STATE: State = State { + mode: Mode::Title, + frame_count: 0, + plants: Plants::empty(), + rocci_idle_anim: INITIAL_ANIM, + score: 0, + max_score: 0, + high_score: 0, + new_high_score: false, + player: Player { y: PLAYER_START_Y, y_vel: JUMP_SPEED }, + last_flap: false, + rocci_flap_anim: INITIAL_ANIM, + rocci_fall_anim: INITIAL_ANIM, + high_score_anim: INITIAL_ANIM, + pipes: Pipes::empty(), + last_pipe_generated: 0, + last_plant_generated: 0, + ground_x: 0, +}; + +static mut STATE: State = INITIAL_STATE; +static mut PRNG_STATE: u32 = PRNG_DEFAULT; + +#[no_mangle] +pub extern "C" fn start() { + set_palette(); + let frame_count = load_rand_from_disk(); + seed_rand(frame_count); + let plants = starting_plants(); + unsafe { + STATE = INITIAL_STATE; + init_title_screen(&mut STATE, frame_count, plants); + } +} + +#[no_mangle] +pub extern "C" fn update() { + unsafe { + STATE.frame_count = STATE.frame_count.wrapping_add(1); + match STATE.mode { + Mode::Title => run_title_screen(&mut STATE), + Mode::Game => run_game(&mut STATE), + Mode::GameOver => run_game_over(&mut STATE), + } + } +} + +fn init_title_screen(state: &mut State, frame_count: u64, plants: Plants) { + state.mode = Mode::Title; + state.frame_count = frame_count; + state.plants = plants; + state.rocci_idle_anim = create_rocci_idle_anim(frame_count); +} + +fn run_title_screen(state: &mut State) { + state.rocci_idle_anim = update_animation(state.frame_count, state.rocci_idle_anim); + + set_text_colors(); + text("Rocci Bird!!!", 32, 12); + text("Click to start!", 24, 72); + draw_ground(0); + draw_plants(state.plants); + + let shift = idle_shift(state.frame_count, state.rocci_idle_anim); + draw_animation(state.rocci_idle_anim, PLAYER_X, PLAYER_START_Y_PIXEL + shift, 0); + + if flap_pressed() { + init_game(state); + } +} + +fn init_game(state: &mut State) { + let frame_count = state.frame_count; + save_rand_to_disk(frame_count); + seed_rand(frame_count); + play_tone(FLAP_TONE); + + state.mode = Mode::Game; + state.score = 0; + state.max_score = 0; + state.player = Player { y: PLAYER_START_Y, y_vel: JUMP_SPEED }; + state.last_pipe_generated = frame_count; + state.pipes = Pipes::empty(); + state.last_plant_generated = frame_count.saturating_sub(4); + state.last_flap = true; + state.rocci_flap_anim = create_rocci_flap_anim(frame_count); + state.ground_x = 0; +} + +fn run_game(state: &mut State) { + let gamepad = unsafe { *GAMEPAD1 }; + let mouse = unsafe { *MOUSE_BUTTONS }; + let flap = (gamepad & (BUTTON_1 | BUTTON_UP)) != 0 || (mouse & MOUSE_LEFT) != 0; + + let (y_vel, next_anim) = + if !state.last_flap && flap && flap_allowed(state.frame_count, state.rocci_flap_anim) { + play_tone(FLAP_TONE); + let mut anim = state.rocci_flap_anim; + anim.index = 0; + anim.state = AnimationState::RunOnce; + (JUMP_SPEED, anim) + } else { + ( + state.player.y_vel + GRAVITY, + update_animation(state.frame_count, state.rocci_flap_anim), + ) + }; + + let previous_pipes = state.pipes; + let pipe = maybe_generate_pipe(state.last_pipe_generated, state.frame_count); + let last_pipe_generated = if pipe.is_some() { state.frame_count } else { state.last_pipe_generated }; + let mut pipes = update_pipes(previous_pipes); + if let Some(new_pipe) = pipe { + pipes.push(new_pipe); + } + + let plant = maybe_generate_plant(state.last_plant_generated, state.frame_count); + let last_plant_generated = if plant.is_some() { state.frame_count } else { state.last_plant_generated }; + let mut plants = update_plants(state.plants); + if let Some(new_plant) = plant { + plants.push(new_plant); + } + + let gain_point = count_passed_pipes(previous_pipes); + let y = state.player.y + y_vel; + let score = state.score.saturating_add(gain_point); + state.rocci_flap_anim = next_anim; + state.player = Player { y, y_vel }; + state.score = score; + if score > state.max_score { + state.max_score = score; + } + state.last_flap = flap; + state.last_pipe_generated = last_pipe_generated; + state.pipes = pipes; + state.last_plant_generated = last_plant_generated; + state.plants = plants; + state.ground_x = (state.ground_x - 1) % SCREEN; + + if gain_point > 0 { + play_tone(POINT_TONE); + } + + draw_pipes(state.pipes); + draw_ground(state.ground_x); + draw_plants(state.plants); + + let y_pixel = (state.player.y as i32).min(134); + let collided = player_collided(y_pixel, state.rocci_flap_anim.index); + draw_animation(state.rocci_flap_anim, PLAYER_X, y_pixel, 0); + draw_score(state.score, 68, 4); + + if collided || y >= 134.0 { + play_tone(DEATH_TONE); + init_game_over(state); + } +} + +fn init_game_over(state: &mut State) { + let high_score_on_disk = load_high_score_from_disk(); + state.new_high_score = state.max_score > high_score_on_disk; + state.high_score = if state.new_high_score { state.max_score } else { high_score_on_disk }; + save_high_score_to_disk(state.high_score); + state.mode = Mode::GameOver; + state.rocci_fall_anim = create_rocci_fall_anim(state.frame_count); + state.high_score_anim = create_high_score_anim(state.frame_count); +} + +fn run_game_over(state: &mut State) { + let y_vel = state.player.y_vel + GRAVITY; + state.rocci_fall_anim = update_animation(state.frame_count, state.rocci_fall_anim); + state.high_score_anim = update_animation(state.frame_count, state.high_score_anim); + state.player = Player { y: (state.player.y + y_vel).min(134.0), y_vel }; + + draw_pipes(state.pipes); + draw_ground(state.ground_x); + draw_plants(state.plants); + + draw_animation(state.rocci_fall_anim, PLAYER_X, state.player.y as i32, 0); + set_shape_colors(COLOR4, COLOR1); + rect(16, 52, 136, 32); + set_text_colors(); + text("Game Over!", 44, 56); + text("Right to restart", 20, 72); + text("Art by Luke DeVault", 4, 151); + + set_shape_colors(COLOR4, COLOR1); + rect(66, 2, 28, 12); + draw_score(state.score, 68, 4); + + if state.new_high_score { + draw_animation(state.high_score_anim, 64, 0, 0); + } + + set_shape_colors(COLOR4, COLOR1); + rect(54, 18, 52, 12); + set_text_colors(); + text("HS:", 57, 20); + draw_score(state.high_score, 80, 20); + + let gamepad = unsafe { *GAMEPAD1 }; + let mouse = unsafe { *MOUSE_BUTTONS }; + if (mouse & MOUSE_RIGHT) != 0 || (gamepad & (BUTTON_2 | BUTTON_RIGHT)) != 0 { + let plants = starting_plants(); + init_title_screen(state, state.frame_count, plants); + } +} + +fn flap_pressed() -> bool { + let gamepad = unsafe { *GAMEPAD1 }; + let mouse = unsafe { *MOUSE_BUTTONS }; + (gamepad & (BUTTON_1 | BUTTON_UP)) != 0 || (mouse & MOUSE_LEFT) != 0 +} + +fn player_collided(player_y: i32, anim_index: usize) -> bool { + if player_y >= -1 { + on_screen_collided(player_y, anim_index) + } else { + get_pixel((PLAYER_X + 13) as u8, 0) != COLOR1 as u8 + } +} + +fn on_screen_collided(player_y: i32, anim_index: usize) -> bool { + const BASE_POINTS: [(i32, i32); 8] = [ + (11, 2), + (13, 3), + (3, 5), + (11, 6), + (9, 8), + (5, 9), + (7, 10), + (5, 12), + ]; + + for (x, y) in BASE_POINTS { + if get_pixel((PLAYER_X + x) as u8, (player_y + y) as u8) != COLOR1 as u8 { + return true; + } + } + if anim_index == 2 { + get_pixel((PLAYER_X + 2) as u8, (player_y + 1) as u8) != COLOR1 as u8 + || get_pixel((PLAYER_X + 7) as u8, (player_y + 1) as u8) != COLOR1 as u8 + } else if anim_index == 1 { + get_pixel((PLAYER_X + 2) as u8, (player_y + 2) as u8) != COLOR1 as u8 + } else { + false + } +} + +fn draw_pipes(pipes: Pipes) { + let mut i = 0; + while i < pipes.len { + draw_pipe(pipes.items[i]); + i += 1; + } +} + +fn draw_pipe(pipe: Pipe) { + set_sprite_colors(); + blit_sprite(PIPE_SPRITE, pipe.x, pipe.gap_start - SCREEN, BLIT_FLIP_Y); + blit_sprite(PIPE_SPRITE, pipe.x, pipe.gap_start + GAP_HEIGHT, 0); +} + +fn update_pipes(pipes: Pipes) -> Pipes { + let mut out = Pipes::empty(); + let mut i = 0; + while i < pipes.len { + let mut pipe = pipes.items[i]; + pipe.x -= 1; + if pipe.x >= -20 { + out.push(pipe); + } + i += 1; + } + out +} + +fn maybe_generate_pipe(last_generated: u64, frame_count: u64) -> Option { + if frame_count.wrapping_sub(last_generated) > 90 { + let gap_start = rand_between(0, 16); + Some(Pipe { x: SCREEN, gap_start: gap_start * 5 + 10 }) + } else { + None + } +} + +fn count_passed_pipes(pipes: Pipes) -> u8 { + let mut count = 0; + let mut i = 0; + while i < pipes.len { + if pipes.items[i].x == PLAYER_X - 2 { + count += 1; + } + i += 1; + } + count +} + +fn random_plant(x: i32) -> Plant { + Plant { x, kind: (rand_i32() as u32) % PLANT_TYPES } +} + +fn starting_plants() -> Plants { + let mut plants = Plants::empty(); + let mut i = 0; + while i <= 14 { + plants.push(random_plant(i * 12)); + i += 1; + } + plants +} + +fn update_plants(plants: Plants) -> Plants { + let mut out = Plants::empty(); + let mut i = 0; + while i < plants.len { + let mut plant = plants.items[i]; + plant.x -= 1; + if plant.x >= -12 { + out.push(plant); + } + i += 1; + } + out +} + +fn maybe_generate_plant(last_generated: u64, frame_count: u64) -> Option { + if frame_count.wrapping_sub(last_generated) > 12 { + Some(random_plant(SCREEN)) + } else { + None + } +} + +fn draw_plants(plants: Plants) { + let mut i = 0; + while i < plants.len { + let plant = plants.items[i]; + let sprite = sub_sprite(PLANT_SPRITE_SHEET, plant.kind * 12, 0, 12, 12); + set_sprite_colors(); + blit_sprite(sprite, plant.x, PLANT_Y, 0); + i += 1; + } +} + +fn save_rand_to_disk(frame_count: u64) { + let high_score = load_high_score_from_disk(); + let data = [(frame_count & 0xff) as u8, high_score]; + unsafe { + diskw(data.as_ptr(), data.len() as u32); + } +} + +fn load_rand_from_disk() -> u64 { + let mut data = [0u8; 2]; + let got = unsafe { diskr(data.as_mut_ptr(), data.len() as u32) }; + if got >= 1 { data[0] as u64 } else { 0 } +} + +fn save_high_score_to_disk(high_score: u8) { + let rand = load_rand_from_disk() as u8; + let data = [rand, high_score]; + unsafe { + diskw(data.as_ptr(), data.len() as u32); + } +} + +fn load_high_score_from_disk() -> u8 { + let mut data = [0u8; 2]; + let got = unsafe { diskr(data.as_mut_ptr(), data.len() as u32) }; + if got >= 2 { data[1] } else { 0 } +} + +fn update_animation(frame_count: u64, mut anim: Animation) -> Animation { + let frames_per_update = anim.cells[anim.index].frames; + if frame_count.wrapping_sub(anim.last_updated) < frames_per_update { + anim + } else { + let next_index = wrapped_inc(anim.index, anim.cells.len()); + anim.last_updated = frame_count; + match anim.state { + AnimationState::Completed => anim, + AnimationState::Loop => { + anim.index = next_index; + anim + } + AnimationState::RunOnce => { + if next_index == 0 { + anim.state = AnimationState::Completed; + } else { + anim.index = next_index; + } + anim + } + } + } +} + +fn draw_animation(anim: Animation, x: i32, y: i32, flags: u32) { + set_sprite_colors(); + blit_sprite(anim.cells[anim.index].sprite, x, y, flags); +} + +fn wrapped_inc(value: usize, count: usize) -> usize { + let next = value + 1; + if next == count { 0 } else { next } +} + +fn idle_shift(frame_count: u64, anim: Animation) -> i32 { + if anim.index == 2 || (anim.index == 1 && frame_count.wrapping_sub(anim.last_updated) > 3) { + 0 + } else { + 1 + } +} + +fn create_rocci_idle_anim(frame_count: u64) -> Animation { + Animation { last_updated: frame_count, index: 0, state: AnimationState::Loop, cells: &IDLE_CELLS } +} + +fn flap_allowed(frame_count: u64, anim: Animation) -> bool { + if anim.index == 2 { + true + } else if anim.index == 1 { + frame_count.wrapping_sub(anim.last_updated) > 6 + } else { + false + } +} + +fn create_rocci_flap_anim(frame_count: u64) -> Animation { + Animation { last_updated: frame_count, index: 2, state: AnimationState::Completed, cells: &FLAP_CELLS } +} + +fn create_rocci_fall_anim(frame_count: u64) -> Animation { + Animation { last_updated: frame_count, index: 0, state: AnimationState::Loop, cells: &FALL_CELLS } +} + +fn create_high_score_anim(frame_count: u64) -> Animation { + Animation { last_updated: frame_count, index: 0, state: AnimationState::Loop, cells: &HIGH_SCORE_CELLS } +} + +fn draw_score(score: u8, base_x: i32, y: i32) { + set_text_colors(); + let x = if score < 10 { base_x + 8 } else if score < 100 { base_x + 4 } else { base_x }; + let mut buf = [0u8; 3]; + let len = if score >= 100 { + buf[0] = b'0' + score / 100; + buf[1] = b'0' + (score / 10) % 10; + buf[2] = b'0' + score % 10; + 3 + } else if score >= 10 { + buf[0] = b'0' + score / 10; + buf[1] = b'0' + score % 10; + 2 + } else { + buf[0] = b'0' + score; + 1 + }; + text(&buf[..len], x, y); +} + +fn draw_ground(x: i32) { + set_ground_colors(); + blit_sprite(GROUND_SPRITE, x, SCREEN - 13, 0); + blit_sprite(GROUND_SPRITE, x + SCREEN, SCREEN - 13, 0); +} + +fn set_palette() { + unsafe { + *PALETTE = [0xe6e6c0, 0xb494b7, 0x42436e, 0x26013f]; + } +} + +fn set_text_colors() { + set_draw_colors(COLOR4, COLOR_NONE, COLOR_NONE, COLOR_NONE); +} + +fn set_sprite_colors() { + set_draw_colors(COLOR_NONE, COLOR2, COLOR3, COLOR4); +} + +fn set_ground_colors() { + set_draw_colors(COLOR1, COLOR2, COLOR3, COLOR4); +} + +fn set_shape_colors(border: u16, fill: u16) { + set_draw_colors(fill, border, COLOR_NONE, COLOR_NONE); +} + +fn set_draw_colors(primary: u16, secondary: u16, tertiary: u16, quaternary: u16) { + unsafe { + *DRAW_COLORS = primary | (secondary << 4) | (tertiary << 8) | (quaternary << 12); + } +} + +fn blit_sprite(sprite: Sprite, x: i32, y: i32, flags: u32) { + let region = sprite.region; + blit_sub( + sprite.data, + x, + y, + region.width, + region.height, + region.src_x, + region.src_y, + sprite.stride, + sprite.bpp | flags, + ); +} + +fn get_pixel(x: u8, y: u8) -> u8 { + if x as i32 >= SCREEN || y as i32 >= SCREEN { + return 0; + } + let idx = ((SCREEN as u32 * y as u32 + x as u32) >> 2) as usize; + let shift = (x & 0x3) << 1; + let byte = unsafe { (*FRAMEBUFFER)[idx] }; + ((byte >> shift) & 0x3) + 1 +} + +#[derive(Copy, Clone)] +struct Tone { + start_freq: u16, + end_freq: u16, + channel: u8, + mode: u8, + pan: u8, + attack_time: u8, + sustain_time: u8, + decay_time: u8, + release_time: u8, + volume: u8, + peak_volume: u8, +} + +const FLAP_TONE: Tone = Tone { + start_freq: 700, + end_freq: 870, + channel: 0, + mode: 4, + pan: 0, + attack_time: 10, + sustain_time: 0, + decay_time: 5, + release_time: 3, + volume: 10, + peak_volume: 20, +}; + +const POINT_TONE: Tone = Tone { + start_freq: 995, + end_freq: 1000, + channel: 1, + mode: 8, + pan: 0, + attack_time: 0, + sustain_time: 0, + decay_time: 10, + release_time: 10, + volume: 25, + peak_volume: 75, +}; + +const DEATH_TONE: Tone = Tone { + start_freq: 170, + end_freq: 40, + channel: 3, + mode: 0, + pan: 0, + attack_time: 0, + sustain_time: 20, + decay_time: 40, + release_time: 0, + volume: 100, + peak_volume: 0, +}; + +fn play_tone(t: Tone) { + let freq = ((t.end_freq as u32) << 16) | t.start_freq as u32; + let duration = ((t.attack_time as u32) << 24) + | ((t.decay_time as u32) << 16) + | ((t.release_time as u32) << 8) + | t.sustain_time as u32; + let volume = ((t.peak_volume as u32) << 8) | t.volume as u32; + let flags = (t.pan | t.mode | t.channel) as u32; + tone(freq, duration, volume, flags); +} + +fn seed_rand(seed: u64) { + let seed = seed as u32; + unsafe { + PRNG_STATE = if seed == 0 { PRNG_DEFAULT } else { seed }; + } +} + +fn next_random_u32() -> u32 { + unsafe { + let mut x = PRNG_STATE; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + PRNG_STATE = if x == 0 { PRNG_DEFAULT } else { x }; + PRNG_STATE + } +} + +fn rand_i32() -> i32 { + next_random_u32() as i32 +} + +fn rand_between(start: i32, before: i32) -> i32 { + if start >= before { + return start; + } + let span = (before - start) as u32; + start + (next_random_u32() % span) as i32 +} diff --git a/examples/rocci-bird-rust/src/sprites.rs b/examples/rocci-bird-rust/src/sprites.rs new file mode 100644 index 0000000..e4b1074 --- /dev/null +++ b/examples/rocci-bird-rust/src/sprites.rs @@ -0,0 +1,234 @@ +// Generated from ../rocci-bird.roc sprite sheet constants. + +pub const ROCCI_SPRITE_SHEET_WIDTH: u32 = 80; +pub const ROCCI_SPRITE_SHEET_HEIGHT: u32 = 16; +pub const ROCCI_SPRITE_SHEET_BPP: u32 = 1; +pub static ROCCI_SPRITE_SHEET_DATA: [u8; 320] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x56, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x80, 0x40, 0x05, 0x56, 0x81, 0x80, 0x14, 0x00, 0x01, 0x80, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x81, 0x40, 0x01, 0x56, 0xa5, 0xa0, + 0x15, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x0a, 0x95, 0x00, 0x00, 0x0a, 0x85, 0x40, + 0x00, 0x56, 0xa9, 0x00, 0x15, 0x56, 0xa5, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x06, 0x55, 0x00, + 0x00, 0x06, 0x55, 0x40, 0x00, 0x06, 0xaa, 0x00, 0x15, 0x5a, 0xaa, 0x00, 0x15, 0x56, 0xaa, 0x00, + 0x00, 0x16, 0x95, 0x00, 0x00, 0x06, 0x95, 0x00, 0x00, 0x09, 0x55, 0x00, 0x15, 0x6a, 0xa9, 0x00, + 0x05, 0x56, 0xa9, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x09, 0x54, 0x00, + 0x05, 0x6a, 0x94, 0x00, 0x01, 0x56, 0xa4, 0x00, 0x00, 0x1a, 0x94, 0x00, 0x00, 0x16, 0x94, 0x00, + 0x00, 0x09, 0x50, 0x00, 0x00, 0x69, 0x50, 0x00, 0x00, 0x56, 0x90, 0x00, 0x00, 0x1a, 0xa0, 0x00, + 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x26, 0x40, 0x00, + 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, + 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x2a, 0x00, 0x00, + 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x05, 0x40, 0x00, + 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, + 0x00, 0xa5, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +pub const GROUND_SPRITE_WIDTH: u32 = 160; +pub const GROUND_SPRITE_HEIGHT: u32 = 13; +pub const GROUND_SPRITE_BPP: u32 = 1; +pub static GROUND_SPRITE_DATA: [u8; 520] = [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x59, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x45, 0x14, 0x51, 0x85, 0x14, 0x51, 0x49, 0x14, 0x91, 0x45, 0x18, 0x51, 0x54, 0x51, 0x49, 0x24, + 0x51, 0x46, 0x14, 0x51, 0x46, 0x15, 0x14, 0x61, 0x45, 0x14, 0x92, 0x45, 0x14, 0x61, 0x46, 0x14, + 0x55, 0x14, 0x51, 0x45, 0x14, 0x61, 0x51, 0x45, 0x65, 0x66, 0x56, 0x56, 0x59, 0x56, 0x59, 0x56, + 0x55, 0x65, 0x65, 0x56, 0x59, 0x56, 0x59, 0x95, 0x59, 0x59, 0x55, 0x99, 0x59, 0x59, 0x55, 0x95, + 0x96, 0x55, 0x99, 0x55, 0x95, 0x95, 0x59, 0x55, 0x96, 0x55, 0x59, 0x59, 0x95, 0x95, 0x96, 0x55, + 0x95, 0x95, 0x99, 0x66, 0x65, 0x99, 0x65, 0x96, 0x95, 0x96, 0x59, 0x59, 0x65, 0x99, 0x65, 0xa5, + 0x65, 0x96, 0x56, 0x56, 0x65, 0x99, 0x96, 0x59, 0x99, 0x66, 0x5a, 0x56, 0x59, 0x65, 0x96, 0x56, + 0x59, 0x66, 0x65, 0x65, 0x66, 0x59, 0x99, 0x59, 0xa6, 0x9a, 0x5a, 0x69, 0x6a, 0x5a, 0x66, 0xa5, + 0xa6, 0x9a, 0x9a, 0x6a, 0x6a, 0x5a, 0x65, 0x69, 0xa6, 0xa6, 0x9a, 0x69, 0x69, 0xa5, 0xa6, 0x9a, + 0x5a, 0x96, 0x56, 0x9a, 0x6a, 0x6a, 0xa6, 0x9a, 0x9a, 0x96, 0xa9, 0xa6, 0x96, 0x9a, 0x5a, 0x69, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, +]; + +pub const PIPE_SPRITE_WIDTH: u32 = 20; +pub const PIPE_SPRITE_HEIGHT: u32 = 160; +pub const PIPE_SPRITE_BPP: u32 = 1; +pub static PIPE_SPRITE_DATA: [u8; 800] = [ + 0x0a, 0xaa, 0xaa, 0xab, 0xf0, 0x25, 0x55, 0x55, 0x55, 0x5c, 0x26, 0x96, 0x6a, 0x9a, 0xac, 0x36, + 0x96, 0x6a, 0x66, 0xac, 0x36, 0x96, 0x6a, 0x9a, 0xac, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, + 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, + 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, + 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, + 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, + 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, + 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, + 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, + 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, + 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, + 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, + 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, + 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, + 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, + 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, + 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, + 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, + 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, + 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, + 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, + 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, + 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, +]; + +pub const PLANT_SPRITE_SHEET_WIDTH: u32 = 360; +pub const PLANT_SPRITE_SHEET_HEIGHT: u32 = 12; +pub const PLANT_SPRITE_SHEET_BPP: u32 = 1; +pub static PLANT_SPRITE_SHEET_DATA: [u8; 1080] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x40, 0x02, 0xaa, 0x00, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x00, 0x00, + 0x00, 0x0a, 0x00, 0xa0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x08, 0x00, 0x80, 0x08, + 0x08, 0x80, 0x00, 0x2a, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x41, 0x40, 0x11, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x05, 0x14, + 0x00, 0x28, 0x00, 0x51, 0x45, 0x00, 0x20, 0x01, 0x60, 0x20, 0x81, 0x60, 0x00, 0x80, 0x80, 0x0a, + 0xa0, 0x16, 0x00, 0xa8, 0x00, 0x82, 0x15, 0x96, 0x02, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x02, 0xa0, 0x00, 0x00, 0x28, 0x00, 0x02, 0x20, 0x00, + 0x02, 0x82, 0x00, 0x06, 0x11, 0x11, 0x02, 0xb2, 0xb0, 0x02, 0x80, 0x08, 0x00, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x40, 0x15, 0x11, + 0x15, 0x00, 0x00, 0x00, 0x24, 0x20, 0x80, 0x51, 0x41, 0x50, 0x00, 0x22, 0x00, 0x14, 0x41, 0x44, + 0x20, 0x55, 0x60, 0x28, 0x59, 0xa0, 0x02, 0x01, 0x60, 0x20, 0x21, 0x56, 0x02, 0x02, 0x00, 0x83, + 0x55, 0xd6, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x10, 0x40, 0x02, 0xa8, + 0x80, 0x09, 0x5c, 0x00, 0x08, 0x20, 0x08, 0x02, 0x00, 0x00, 0x0a, 0x0a, 0x28, 0x91, 0x44, 0x66, + 0x09, 0x6d, 0x5c, 0x0a, 0x08, 0x20, 0x02, 0x0b, 0x00, 0x00, 0x20, 0x00, 0x02, 0x06, 0x00, 0x0a, + 0x15, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x15, 0xa0, 0x15, 0x15, 0x08, 0x00, 0x00, 0x00, 0x04, 0x86, + 0x60, 0x15, 0x80, 0x48, 0x20, 0x02, 0x08, 0x50, 0x52, 0x94, 0x21, 0x55, 0x60, 0x21, 0x95, 0x60, + 0x02, 0x15, 0x60, 0x80, 0x5a, 0xa8, 0x08, 0x05, 0x80, 0x81, 0x69, 0x56, 0x0a, 0xea, 0x00, 0x00, + 0x28, 0x00, 0x02, 0xab, 0x80, 0x04, 0x45, 0x10, 0x0a, 0xea, 0x80, 0x03, 0x9a, 0x00, 0x28, 0xa8, + 0x20, 0x02, 0x00, 0x00, 0x28, 0x08, 0xa0, 0x45, 0x19, 0x18, 0x03, 0xef, 0xeb, 0x28, 0x28, 0xa8, + 0x08, 0x20, 0xc0, 0x00, 0x88, 0x00, 0x02, 0x05, 0x80, 0x26, 0x16, 0x18, 0x02, 0x00, 0x00, 0x02, + 0x8a, 0x80, 0x0a, 0x15, 0x28, 0x00, 0x00, 0x08, 0x05, 0x99, 0xb0, 0x26, 0x08, 0xa0, 0x88, 0x02, + 0x22, 0xa1, 0x42, 0x05, 0x0a, 0xaa, 0x80, 0x0a, 0xaa, 0x80, 0x00, 0xaa, 0x80, 0x85, 0x5a, 0x70, + 0x08, 0x55, 0x80, 0x95, 0x55, 0x56, 0x2b, 0xae, 0x80, 0x00, 0x88, 0x00, 0x0a, 0xee, 0xa0, 0x11, + 0x11, 0x40, 0x0b, 0xab, 0xa0, 0x0b, 0xed, 0xe0, 0xba, 0xba, 0xa0, 0x02, 0xa8, 0x00, 0x2a, 0x2a, + 0xb8, 0x1a, 0x6a, 0x78, 0x29, 0xbe, 0x68, 0xae, 0xaa, 0xb8, 0x31, 0x81, 0xc0, 0x00, 0x80, 0x00, + 0x02, 0x55, 0x80, 0x87, 0x18, 0x18, 0x09, 0x82, 0x80, 0x00, 0xaa, 0x00, 0x28, 0x08, 0x0a, 0x08, + 0x20, 0x20, 0x05, 0x65, 0xd0, 0x08, 0x02, 0x00, 0x08, 0xaa, 0x20, 0x28, 0x8a, 0x14, 0x02, 0x17, + 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x2a, 0xa2, 0x70, 0x02, 0xaa, 0x00, 0x2a, 0xaa, 0xa8, + 0x2b, 0xba, 0xa0, 0x00, 0x87, 0x00, 0x2b, 0xae, 0xa8, 0x06, 0x66, 0x80, 0x0b, 0xae, 0xb8, 0x25, + 0xb9, 0x5c, 0xba, 0xea, 0xe8, 0x02, 0xe0, 0x00, 0xae, 0xae, 0xea, 0x0b, 0xaa, 0xe0, 0x96, 0x97, + 0x96, 0xba, 0xba, 0xea, 0x31, 0x85, 0x70, 0x00, 0xa0, 0x00, 0x02, 0x67, 0x00, 0x87, 0x1c, 0x57, + 0x21, 0xc8, 0x70, 0x00, 0x2c, 0x00, 0x08, 0x0a, 0x08, 0x28, 0xa8, 0xa8, 0x01, 0x5f, 0x54, 0x20, + 0x00, 0x80, 0x28, 0x2e, 0x28, 0x22, 0x82, 0x0a, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, + 0x00, 0x09, 0xc2, 0x70, 0x00, 0x9c, 0x00, 0x08, 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xce, 0xac, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0xac, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +pub const HIGH_SCORE_SPRITE_SHEET_WIDTH: u32 = 128; +pub const HIGH_SCORE_SPRITE_SHEET_HEIGHT: u32 = 16; +pub const HIGH_SCORE_SPRITE_SHEET_BPP: u32 = 1; +pub static HIGH_SCORE_SPRITE_SHEET_DATA: [u8; 512] = [ + 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x00, + 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, + 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x09, 0x60, 0x25, 0x80, 0x96, 0x02, 0x58, 0x00, + 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, + 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, + 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, + 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, + 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x25, 0x80, 0x96, 0x02, 0x58, 0x09, 0x60, + 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, + 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, + 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, +]; diff --git a/examples/rocci-bird-rust/src/wasm4.rs b/examples/rocci-bird-rust/src/wasm4.rs new file mode 100644 index 0000000..7f04e61 --- /dev/null +++ b/examples/rocci-bird-rust/src/wasm4.rs @@ -0,0 +1,227 @@ +// +// WASM-4: https://wasm4.org/docs + +#![allow(unused)] + +// ┌───────────────────────────────────────────────────────────────────────────┐ +// │ │ +// │ Platform Constants │ +// │ │ +// └───────────────────────────────────────────────────────────────────────────┘ + +pub const SCREEN_SIZE: u32 = 160; +pub const FONT_SIZE: u32 = 8; + +// ┌───────────────────────────────────────────────────────────────────────────┐ +// │ │ +// │ Memory Addresses │ +// │ │ +// └───────────────────────────────────────────────────────────────────────────┘ + +pub const PALETTE: *mut [u32; 4] = 0x04 as _; +pub const DRAW_COLORS: *mut u16 = 0x14 as _; +pub const GAMEPAD1: *const u8 = 0x16 as _; +pub const GAMEPAD2: *const u8 = 0x17 as _; +pub const GAMEPAD3: *const u8 = 0x18 as _; +pub const GAMEPAD4: *const u8 = 0x19 as _; +pub const MOUSE_X: *const i16 = 0x1a as _; +pub const MOUSE_Y: *const i16 = 0x1c as _; +pub const MOUSE_BUTTONS: *const u8 = 0x1e as _; +pub const SYSTEM_FLAGS: *mut u8 = 0x1f as _; +pub const NETPLAY: *const u8 = 0x20 as _; +pub const FRAMEBUFFER: *mut [u8; 6400] = 0xa0 as _; + +pub const BUTTON_1: u8 = 1; +pub const BUTTON_2: u8 = 2; +pub const BUTTON_LEFT: u8 = 16; +pub const BUTTON_RIGHT: u8 = 32; +pub const BUTTON_UP: u8 = 64; +pub const BUTTON_DOWN: u8 = 128; + +pub const MOUSE_LEFT: u8 = 1; +pub const MOUSE_RIGHT: u8 = 2; +pub const MOUSE_MIDDLE: u8 = 4; + +pub const SYSTEM_PRESERVE_FRAMEBUFFER: u8 = 1; +pub const SYSTEM_HIDE_GAMEPAD_OVERLAY: u8 = 2; + +// ┌───────────────────────────────────────────────────────────────────────────┐ +// │ │ +// │ Drawing Functions │ +// │ │ +// └───────────────────────────────────────────────────────────────────────────┘ + +/// Copies pixels to the framebuffer. +pub fn blit(sprite: &[u8], x: i32, y: i32, width: u32, height: u32, flags: u32) { + unsafe { extern_blit(sprite.as_ptr(), x, y, width, height, flags) } +} +extern "C" { + #[link_name = "blit"] + fn extern_blit(sprite: *const u8, x: i32, y: i32, width: u32, height: u32, flags: u32); +} + +/// Copies a subregion within a larger sprite atlas to the framebuffer. +#[allow(clippy::too_many_arguments)] +pub fn blit_sub( + sprite: &[u8], + x: i32, + y: i32, + width: u32, + height: u32, + src_x: u32, + src_y: u32, + stride: u32, + flags: u32, +) { + unsafe { + extern_blit_sub( + sprite.as_ptr(), + x, + y, + width, + height, + src_x, + src_y, + stride, + flags, + ) + } +} +extern "C" { + #[link_name = "blitSub"] + fn extern_blit_sub( + sprite: *const u8, + x: i32, + y: i32, + width: u32, + height: u32, + src_x: u32, + src_y: u32, + stride: u32, + flags: u32, + ); +} + +pub const BLIT_2BPP: u32 = 1; +pub const BLIT_1BPP: u32 = 0; +pub const BLIT_FLIP_X: u32 = 2; +pub const BLIT_FLIP_Y: u32 = 4; +pub const BLIT_ROTATE: u32 = 8; + +/// Draws a line between two points. +pub fn line(x1: i32, y1: i32, x2: i32, y2: i32) { + unsafe { extern_line(x1, y1, x2, y2) } +} +extern "C" { + #[link_name = "line"] + fn extern_line(x1: i32, y1: i32, x2: i32, y2: i32); +} + +/// Draws an oval (or circle). +pub fn oval(x: i32, y: i32, width: u32, height: u32) { + unsafe { extern_oval(x, y, width, height) } +} +extern "C" { + #[link_name = "oval"] + fn extern_oval(x: i32, y: i32, width: u32, height: u32); +} + +/// Draws a rectangle. +pub fn rect(x: i32, y: i32, width: u32, height: u32) { + unsafe { extern_rect(x, y, width, height) } +} +extern "C" { + #[link_name = "rect"] + fn extern_rect(x: i32, y: i32, width: u32, height: u32); +} + +/// Draws text using the built-in system font. +pub fn text>(text: T, x: i32, y: i32) { + let text_ref = text.as_ref(); + unsafe { extern_text(text_ref.as_ptr(), text_ref.len(), x, y) } +} +extern "C" { + #[link_name = "textUtf8"] + fn extern_text(text: *const u8, length: usize, x: i32, y: i32); +} + +/// Draws a vertical line +pub fn vline(x: i32, y: i32, len: u32) { + unsafe { + extern_vline(x, y, len); + } +} + +extern "C" { + #[link_name = "vline"] + fn extern_vline(x: i32, y: i32, len: u32); +} + +/// Draws a horizontal line +pub fn hline(x: i32, y: i32, len: u32) { + unsafe { + extern_hline(x, y, len); + } +} + +extern "C" { + #[link_name = "hline"] + fn extern_hline(x: i32, y: i32, len: u32); +} + +// ┌───────────────────────────────────────────────────────────────────────────┐ +// │ │ +// │ Sound Functions │ +// │ │ +// └───────────────────────────────────────────────────────────────────────────┘ + +/// Plays a sound tone. +pub fn tone(frequency: u32, duration: u32, volume: u32, flags: u32) { + unsafe { extern_tone(frequency, duration, volume, flags) } +} +extern "C" { + #[link_name = "tone"] + fn extern_tone(frequency: u32, duration: u32, volume: u32, flags: u32); +} + +pub const TONE_PULSE1: u32 = 0; +pub const TONE_PULSE2: u32 = 1; +pub const TONE_TRIANGLE: u32 = 2; +pub const TONE_NOISE: u32 = 3; +pub const TONE_MODE1: u32 = 0; +pub const TONE_MODE2: u32 = 4; +pub const TONE_MODE3: u32 = 8; +pub const TONE_MODE4: u32 = 12; +pub const TONE_PAN_LEFT: u32 = 16; +pub const TONE_PAN_RIGHT: u32 = 32; +pub const TONE_NOTE_MODE: u32 = 64; + +// ┌───────────────────────────────────────────────────────────────────────────┐ +// │ │ +// │ Storage Functions │ +// │ │ +// └───────────────────────────────────────────────────────────────────────────┘ + +extern "C" { + /// Reads up to `size` bytes from persistent storage into the pointer `dest`. + pub fn diskr(dest: *mut u8, size: u32) -> u32; + + /// Writes up to `size` bytes from the pointer `src` into persistent storage. + pub fn diskw(src: *const u8, size: u32) -> u32; +} + +// ┌───────────────────────────────────────────────────────────────────────────┐ +// │ │ +// │ Other Functions │ +// │ │ +// └───────────────────────────────────────────────────────────────────────────┘ + +/// Prints a message to the debug console. +pub fn trace>(text: T) { + let text_ref = text.as_ref(); + unsafe { extern_trace(text_ref.as_ptr(), text_ref.len()) } +} +extern "C" { + #[link_name = "traceUtf8"] + fn extern_trace(trace: *const u8, length: usize); +} diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index ab73fbd..44262b4 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -23,6 +23,7 @@ init! = || { color4: 0x26013f, } W4.set_palette!(palette) + W4.clear_frame_buffer_each_update!() frame_count = load_rand_from_disk!() W4.seed_rand!(frame_count) @@ -368,38 +369,38 @@ on_screen_collided! = |player_y, anim_index| { # This is written in a kinda silly but simple way. # It checks to ensure a few points in the sprite are all background colored. # This must be run before drawing the player. - base_points = [ - { x: 11, y: 2 }, - { x: 13, y: 3 }, - { x: 3, y: 5 }, - { x: 11, y: 6 }, - { x: 9, y: 8 }, - { x: 5, y: 9 }, - { x: 7, y: 10 }, - { x: 5, y: 12 }, - ] # TODO .iter() once we have iter.append() - - collision_points = - if anim_index == 2 { - base_points.append({ x: 2, y: 1 }).append({ x: 7, y: 1 }) - } else if anim_index == 1 { - base_points.append({ x: 2, y: 2 }) - } else { - base_points - } - - for { x, y } in collision_points { - if W4.get_pixel!( - { - x: I32.to_u8_wrap(player_x + x), - y: I32.to_u8_wrap(player_y + y), - }, - ) != Color1 { - return True - } + if pixel_collided!(player_y, { x: 11, y: 2 }) { + return True + } + if pixel_collided!(player_y, { x: 13, y: 3 }) { + return True + } + if pixel_collided!(player_y, { x: 3, y: 5 }) { + return True + } + if pixel_collided!(player_y, { x: 11, y: 6 }) { + return True + } + if pixel_collided!(player_y, { x: 9, y: 8 }) { + return True + } + if pixel_collided!(player_y, { x: 5, y: 9 }) { + return True + } + if pixel_collided!(player_y, { x: 7, y: 10 }) { + return True + } + if pixel_collided!(player_y, { x: 5, y: 12 }) { + return True } - False + if anim_index == 2 { + pixel_collided!(player_y, { x: 2, y: 1 }) or pixel_collided!(player_y, { x: 7, y: 1 }) + } else if anim_index == 1 { + pixel_collided!(player_y, { x: 2, y: 2 }) + } else { + False + } } off_screen_collided! : () => Bool @@ -411,6 +412,15 @@ off_screen_collided! = || }, ) != Color1 +pixel_collided! : I32, { x : I32, y : I32 } => Bool +pixel_collided! = |player_y, { x, y }| + W4.get_pixel!( + { + x: I32.to_u8_wrap(player_x + x), + y: I32.to_u8_wrap(player_y + y), + }, + ) != Color1 + # ===== Pipes ============================================= Pipe : { x : I32, gap_start : I32 } From 567debe0ca931d3d6b0426f04d52b360012696a9 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 10:02:19 -0400 Subject: [PATCH 04/19] Use collision point list in Rocci Bird --- examples/rocci-bird.roc | 69 ++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index 44262b4..950bc30 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -369,38 +369,38 @@ on_screen_collided! = |player_y, anim_index| { # This is written in a kinda silly but simple way. # It checks to ensure a few points in the sprite are all background colored. # This must be run before drawing the player. - if pixel_collided!(player_y, { x: 11, y: 2 }) { - return True - } - if pixel_collided!(player_y, { x: 13, y: 3 }) { - return True - } - if pixel_collided!(player_y, { x: 3, y: 5 }) { - return True - } - if pixel_collided!(player_y, { x: 11, y: 6 }) { - return True - } - if pixel_collided!(player_y, { x: 9, y: 8 }) { - return True - } - if pixel_collided!(player_y, { x: 5, y: 9 }) { - return True - } - if pixel_collided!(player_y, { x: 7, y: 10 }) { - return True - } - if pixel_collided!(player_y, { x: 5, y: 12 }) { - return True - } + base_points = [ + { x: 11, y: 2 }, + { x: 13, y: 3 }, + { x: 3, y: 5 }, + { x: 11, y: 6 }, + { x: 9, y: 8 }, + { x: 5, y: 9 }, + { x: 7, y: 10 }, + { x: 5, y: 12 }, + ] # TODO .iter() once we have iter.append() + + collision_points = + if anim_index == 2 { + base_points.append({ x: 2, y: 1 }).append({ x: 7, y: 1 }) + } else if anim_index == 1 { + base_points.append({ x: 2, y: 2 }) + } else { + base_points + } - if anim_index == 2 { - pixel_collided!(player_y, { x: 2, y: 1 }) or pixel_collided!(player_y, { x: 7, y: 1 }) - } else if anim_index == 1 { - pixel_collided!(player_y, { x: 2, y: 2 }) - } else { - False + for { x, y } in collision_points { + if W4.get_pixel!( + { + x: I32.to_u8_wrap(player_x + x), + y: I32.to_u8_wrap(player_y + y), + }, + ) != Color1 { + return True + } } + + False } off_screen_collided! : () => Bool @@ -412,15 +412,6 @@ off_screen_collided! = || }, ) != Color1 -pixel_collided! : I32, { x : I32, y : I32 } => Bool -pixel_collided! = |player_y, { x, y }| - W4.get_pixel!( - { - x: I32.to_u8_wrap(player_x + x), - y: I32.to_u8_wrap(player_y + y), - }, - ) != Color1 - # ===== Pipes ============================================= Pipe : { x : I32, gap_start : I32 } From 826e914779caf6efd610aab692ab934a911a7e74 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 10:14:06 -0400 Subject: [PATCH 05/19] Use inputs_dir in wasm4 platform targets --- platform/main.roc | 103 ++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/platform/main.roc b/platform/main.roc index a5e18d4..b861f90 100644 --- a/platform/main.roc +++ b/platform/main.roc @@ -1,54 +1,57 @@ platform "" - requires { [Model : model] for main : { init! : () => model, update! : model => model } } - exposes [W4, Sprite, Host] - packages {} - provides { "init_for_host": init_for_host!, "update_for_host": update_for_host! } - hosted { - "host_blit": Host.blit!, - "host_blit_sub": Host.blit_sub!, - "host_disk_read": Host.disk_read!, - "host_disk_write": Host.disk_write!, - "host_get_draw_colors": Host.get_draw_colors!, - "host_get_gamepad": Host.get_gamepad!, - "host_get_mouse_buttons": Host.get_mouse_buttons!, - "host_get_mouse_x": Host.get_mouse_x!, - "host_get_mouse_y": Host.get_mouse_y!, - "host_get_netplay": Host.get_netplay!, - "host_get_palette_color": Host.get_palette_color!, - "host_get_pixel": Host.get_pixel!, - "host_hline": Host.hline!, - "host_line": Host.line!, - "host_oval": Host.oval!, - "host_rand": Host.rand!, - "host_rand_range_less_than": Host.rand_range_less_than!, - "host_rect": Host.rect!, - "host_seed_rand": Host.seed_rand!, - "host_set_draw_colors": Host.set_draw_colors!, - "host_set_hide_gamepad_overlay": Host.set_hide_gamepad_overlay!, - "host_set_palette": Host.set_palette!, - "host_set_pixel": Host.set_pixel!, - "host_set_preserve_frame_buffer": Host.set_preserve_frame_buffer!, - "host_text": Host.text!, - "host_tone": Host.tone!, - "host_trace": Host.trace!, - "host_vline": Host.vline!, - } - targets: { - inputs: "targets/", - wasm32: { - inputs: ["host.wasm", app], - output: Shared, - import_memory: Zeroed, - minimum_memory: 65536, - maximum_memory: 65536, - initial_stack_size: 14752, - global_base: wasm4_program_memory_base, - }, - } + requires { + [Model : model] for main : { init! : () => model, update! : model => model } + } + exposes [W4, Sprite, Host] + packages {} + provides { "init_for_host": init_for_host!, "update_for_host": update_for_host! } + hosted { + "host_blit": Host.blit!, + "host_blit_sub": Host.blit_sub!, + "host_disk_read": Host.disk_read!, + "host_disk_write": Host.disk_write!, + "host_get_draw_colors": Host.get_draw_colors!, + "host_get_gamepad": Host.get_gamepad!, + "host_get_mouse_buttons": Host.get_mouse_buttons!, + "host_get_mouse_x": Host.get_mouse_x!, + "host_get_mouse_y": Host.get_mouse_y!, + "host_get_netplay": Host.get_netplay!, + "host_get_palette_color": Host.get_palette_color!, + "host_get_pixel": Host.get_pixel!, + "host_hline": Host.hline!, + "host_line": Host.line!, + "host_oval": Host.oval!, + "host_rand": Host.rand!, + "host_rand_range_less_than": Host.rand_range_less_than!, + "host_rect": Host.rect!, + "host_seed_rand": Host.seed_rand!, + "host_set_draw_colors": Host.set_draw_colors!, + "host_set_hide_gamepad_overlay": Host.set_hide_gamepad_overlay!, + "host_set_palette": Host.set_palette!, + "host_set_pixel": Host.set_pixel!, + "host_set_preserve_frame_buffer": Host.set_preserve_frame_buffer!, + "host_text": Host.text!, + "host_tone": Host.tone!, + "host_trace": Host.trace!, + "host_vline": Host.vline!, + } + targets: { + inputs_dir: "targets/", + wasm32: { + inputs: ["host.wasm", app], + output: Shared, + import_memory: Zeroed, + minimum_memory: 65536, + maximum_memory: 65536, + initial_stack_size: 14752, + global_base: wasm4_program_memory_base, + }, + } # WASM-4 owns 0x0000..0x199f for registers and the framebuffer. # Program data starts after that reserved range, rounded up to 32-byte alignment. wasm4_reserved_memory_end = 0x19a0 + wasm4_program_memory_base = 0x19c0 import W4 @@ -57,12 +60,12 @@ import Host init_for_host! : () => Box(Model) init_for_host! = || { - init_fn! = main.init! - Box.box(init_fn!()) + init_fn! = main.init! + Box.box(init_fn!()) } update_for_host! : Box(Model) => Box(Model) update_for_host! = |boxed| { - update_fn! = main.update! - Box.box(update_fn!(Box.unbox(boxed))) + update_fn! = main.update! + Box.box(update_fn!(Box.unbox(boxed))) } From 1bcbadd377322cf0deb60c461b979a34eea5e784 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 10:18:47 -0400 Subject: [PATCH 06/19] Remove stale Rocci Bird iterator TODO --- examples/rocci-bird.roc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index 950bc30..796c9dc 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -378,7 +378,7 @@ on_screen_collided! = |player_y, anim_index| { { x: 5, y: 9 }, { x: 7, y: 10 }, { x: 5, y: 12 }, - ] # TODO .iter() once we have iter.append() + ] collision_points = if anim_index == 2 { From de0951586651c22158653d8b75d7d6b46b4f3fe9 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 11:19:05 -0400 Subject: [PATCH 07/19] Use compact sprite blit flags --- examples/rocci-bird.roc | 42 ++++----- platform/Sprite.roc | 195 +++++++++++++++++++++------------------- 2 files changed, 126 insertions(+), 111 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index 796c9dc..621d6d2 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -89,7 +89,7 @@ run_title_screen! = |prev| { draw_plants!(state.plants) shift = idle_shift(state.frame_count, state.rocci_idle_anim) - draw_animation!(state.rocci_idle_anim, { x: player_x, y: player_start_y_pixel + shift, flags: [] }) + draw_animation!(state.rocci_idle_anim, { x: player_x, y: player_start_y_pixel + shift, flags: Sprite.Flags.default() }) gamepad = W4.get_gamepad!(Player1) mouse = W4.get_mouse!() @@ -169,7 +169,7 @@ run_game! = |prev| { flap = gamepad.button1 or gamepad.up or mouse.left - { y_vel, next_anim } = + { y_vel, next_anim } = if !prev.last_flap and flap and flap_allowed(prev.frame_count, prev.rocci_flap_anim) { W4.tone!(flap_tone) anim = prev.rocci_flap_anim @@ -186,7 +186,7 @@ run_game! = |prev| { pipe = maybe_generate_pipe!(prev.last_pipe_generated, prev.frame_count) - last_pipe_generated = + last_pipe_generated = if pipe.is_ok() { prev.frame_count } else { @@ -196,7 +196,7 @@ run_game! = |prev| { pipes = append_if_ok(update_pipes(prev.pipes), pipe) plant = maybe_generate_plant!(prev.last_plant_generated, prev.frame_count) - last_plant_generated = + last_plant_generated = if plant.is_ok() { prev.frame_count } else { @@ -231,7 +231,7 @@ run_game! = |prev| { y_pixel = I32.min(F32.to_i32_wrap(state.player.y), 134) collided = player_collided!(y_pixel, state.rocci_flap_anim.index) - draw_animation!(state.rocci_flap_anim, { x: player_x, y: y_pixel, flags: [] }) + draw_animation!(state.rocci_flap_anim, { x: player_x, y: y_pixel, flags: Sprite.Flags.default() }) draw_score!(state.score, { x: 68, y: 4 }) if !collided and y < 134 { @@ -273,7 +273,7 @@ init_game_over! = |prev| { hs = load_high_score_from_disk!() new_high_score = max_score > hs - high_score = + high_score = if new_high_score { max_score } else { @@ -314,7 +314,7 @@ run_game_over! = |prev| { draw_plants!(state.plants) y_pixel = F32.to_i32_wrap(state.player.y) - draw_animation!(state.rocci_fall_anim, { x: player_x, y: y_pixel, flags: [] }) + draw_animation!(state.rocci_fall_anim, { x: player_x, y: y_pixel, flags: Sprite.Flags.default() }) W4.set_shape_colors!({ border: Color4, fill: Color1 }) W4.rect!({ x: 16, y: 52, width: 136, height: 32 }) set_text_colors!() @@ -326,7 +326,7 @@ run_game_over! = |prev| { draw_score!(state.score, { x: 68, y: 4 }) if state.new_high_score { - draw_animation!(state.high_score_anim, { x: 64, y: 0, flags: [] }) + draw_animation!(state.high_score_anim, { x: 64, y: 0, flags: Sprite.Flags.default() }) } W4.set_shape_colors!({ border: Color4, fill: Color1 }) @@ -380,7 +380,7 @@ on_screen_collided! = |player_y, anim_index| { { x: 5, y: 12 }, ] - collision_points = + collision_points = if anim_index == 2 { base_points.append({ x: 2, y: 1 }).append({ x: 7, y: 1 }) } else if anim_index == 1 { @@ -424,8 +424,8 @@ draw_pipes! = |pipes| draw_pipe! : Sprite, Pipe => {} draw_pipe! = |sprite, { x, gap_start }| { set_sprite_colors!() - Sprite.blit!(sprite, { x, y: gap_start - W4.screen_height(), flags: [FlipY] }) - Sprite.blit!(sprite, { x, y: gap_start + gap_height, flags: [] }) + Sprite.blit!(sprite, { x, y: gap_start - W4.screen_height(), flags: Sprite.Flags.default().flip_y() }) + Sprite.blit!(sprite, { x, y: gap_start + gap_height, flags: Sprite.Flags.default() }) } update_pipes : List(Pipe) -> List(Pipe) @@ -485,7 +485,7 @@ draw_plant! : Sprite, Plant => {} draw_plant! = |sprite_sheet, { x, type }| { sprite = Sprite.sub_or_crash(sprite_sheet, { src_x: type * 12, src_y: 0, width: 12, height: 12 }) set_sprite_colors!() - Sprite.blit!(sprite, { x, y: plant_y, flags: [] }) + Sprite.blit!(sprite, { x, y: plant_y, flags: Sprite.Flags.default() }) } # ===== Sounds ============================================ @@ -534,7 +534,7 @@ death_tone = { draw_score! : U8, { x : I32, y : I32 } => {} draw_score! = |score, { x: base_x, y }| { set_text_colors!() - x = + x = if score < 10 { base_x + 8 } else if score < 100 { @@ -548,8 +548,8 @@ draw_score! = |score, { x: base_x, y }| { draw_ground! : I32 => {} draw_ground! = |x| { set_ground_colors!() - Sprite.blit!(ground_sprite, { x, y: W4.screen_height() - 13, flags: [] }) - Sprite.blit!(ground_sprite, { x: x + W4.screen_width(), y: W4.screen_height() - 13, flags: [] }) + Sprite.blit!(ground_sprite, { x, y: W4.screen_height() - 13, flags: Sprite.Flags.default() }) + Sprite.blit!(ground_sprite, { x: x + W4.screen_width(), y: W4.screen_height() - 13, flags: Sprite.Flags.default() }) } set_text_colors! : () => {} @@ -614,7 +614,7 @@ Animation : { update_animation : U64, Animation -> Animation update_animation = |frame_count, anim| { - frames_per_update = + frames_per_update = match anim.cells.get(anim.index) { Ok(cell) => cell.frames Err(_) => { @@ -639,7 +639,7 @@ update_animation = |frame_count, anim| { } } -draw_animation! : Animation, { x : I32, y : I32, flags : List([FlipX, FlipY, Rotate]) } => {} +draw_animation! : Animation, { x : I32, y : I32, flags : Sprite.Flags } => {} draw_animation! = |anim, { x, y, flags }| match anim.cells.get(anim.index) { Ok(cell) => { @@ -736,7 +736,7 @@ create_high_score_anim = |frame_count| { # game can be in one source code file. rocci_sprite_sheet : Sprite -rocci_sprite_sheet = +rocci_sprite_sheet = Sprite.new( { data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x56, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x80, 0x40, 0x05, 0x56, 0x81, 0x80, 0x14, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x81, 0x40, 0x01, 0x56, 0xa5, 0xa0, 0x15, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x0a, 0x95, 0x00, 0x00, 0x0a, 0x85, 0x40, 0x00, 0x56, 0xa9, 0x00, 0x15, 0x56, 0xa5, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x06, 0x55, 0x00, 0x00, 0x06, 0x55, 0x40, 0x00, 0x06, 0xaa, 0x00, 0x15, 0x5a, 0xaa, 0x00, 0x15, 0x56, 0xaa, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x06, 0x95, 0x00, 0x00, 0x09, 0x55, 0x00, 0x15, 0x6a, 0xa9, 0x00, 0x05, 0x56, 0xa9, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x09, 0x54, 0x00, 0x05, 0x6a, 0x94, 0x00, 0x01, 0x56, 0xa4, 0x00, 0x00, 0x1a, 0x94, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x69, 0x50, 0x00, 0x00, 0x56, 0x90, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], @@ -747,7 +747,7 @@ rocci_sprite_sheet = ) ground_sprite : Sprite -ground_sprite = +ground_sprite = Sprite.new( { data: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x59, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x14, 0x51, 0x85, 0x14, 0x51, 0x49, 0x14, 0x91, 0x45, 0x18, 0x51, 0x54, 0x51, 0x49, 0x24, 0x51, 0x46, 0x14, 0x51, 0x46, 0x15, 0x14, 0x61, 0x45, 0x14, 0x92, 0x45, 0x14, 0x61, 0x46, 0x14, 0x55, 0x14, 0x51, 0x45, 0x14, 0x61, 0x51, 0x45, 0x65, 0x66, 0x56, 0x56, 0x59, 0x56, 0x59, 0x56, 0x55, 0x65, 0x65, 0x56, 0x59, 0x56, 0x59, 0x95, 0x59, 0x59, 0x55, 0x99, 0x59, 0x59, 0x55, 0x95, 0x96, 0x55, 0x99, 0x55, 0x95, 0x95, 0x59, 0x55, 0x96, 0x55, 0x59, 0x59, 0x95, 0x95, 0x96, 0x55, 0x95, 0x95, 0x99, 0x66, 0x65, 0x99, 0x65, 0x96, 0x95, 0x96, 0x59, 0x59, 0x65, 0x99, 0x65, 0xa5, 0x65, 0x96, 0x56, 0x56, 0x65, 0x99, 0x96, 0x59, 0x99, 0x66, 0x5a, 0x56, 0x59, 0x65, 0x96, 0x56, 0x59, 0x66, 0x65, 0x65, 0x66, 0x59, 0x99, 0x59, 0xa6, 0x9a, 0x5a, 0x69, 0x6a, 0x5a, 0x66, 0xa5, 0xa6, 0x9a, 0x9a, 0x6a, 0x6a, 0x5a, 0x65, 0x69, 0xa6, 0xa6, 0x9a, 0x69, 0x69, 0xa5, 0xa6, 0x9a, 0x5a, 0x96, 0x56, 0x9a, 0x6a, 0x6a, 0xa6, 0x9a, 0x9a, 0x96, 0xa9, 0xa6, 0x96, 0x9a, 0x5a, 0x69, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], @@ -758,7 +758,7 @@ ground_sprite = ) pipe_sprite : Sprite -pipe_sprite = +pipe_sprite = Sprite.new( { data: [0x0a, 0xaa, 0xaa, 0xab, 0xf0, 0x25, 0x55, 0x55, 0x55, 0x5c, 0x26, 0x96, 0x6a, 0x9a, 0xac, 0x36, 0x96, 0x6a, 0x66, 0xac, 0x36, 0x96, 0x6a, 0x9a, 0xac, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0], @@ -769,7 +769,7 @@ pipe_sprite = ) plant_sprite_sheet : Sprite -plant_sprite_sheet = +plant_sprite_sheet = Sprite.new( { data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x02, 0xaa, 0x00, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xa0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x08, 0x00, 0x80, 0x08, 0x08, 0x80, 0x00, 0x2a, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x11, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x05, 0x14, 0x00, 0x28, 0x00, 0x51, 0x45, 0x00, 0x20, 0x01, 0x60, 0x20, 0x81, 0x60, 0x00, 0x80, 0x80, 0x0a, 0xa0, 0x16, 0x00, 0xa8, 0x00, 0x82, 0x15, 0x96, 0x02, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x02, 0xa0, 0x00, 0x00, 0x28, 0x00, 0x02, 0x20, 0x00, 0x02, 0x82, 0x00, 0x06, 0x11, 0x11, 0x02, 0xb2, 0xb0, 0x02, 0x80, 0x08, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x40, 0x15, 0x11, 0x15, 0x00, 0x00, 0x00, 0x24, 0x20, 0x80, 0x51, 0x41, 0x50, 0x00, 0x22, 0x00, 0x14, 0x41, 0x44, 0x20, 0x55, 0x60, 0x28, 0x59, 0xa0, 0x02, 0x01, 0x60, 0x20, 0x21, 0x56, 0x02, 0x02, 0x00, 0x83, 0x55, 0xd6, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x10, 0x40, 0x02, 0xa8, 0x80, 0x09, 0x5c, 0x00, 0x08, 0x20, 0x08, 0x02, 0x00, 0x00, 0x0a, 0x0a, 0x28, 0x91, 0x44, 0x66, 0x09, 0x6d, 0x5c, 0x0a, 0x08, 0x20, 0x02, 0x0b, 0x00, 0x00, 0x20, 0x00, 0x02, 0x06, 0x00, 0x0a, 0x15, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x15, 0xa0, 0x15, 0x15, 0x08, 0x00, 0x00, 0x00, 0x04, 0x86, 0x60, 0x15, 0x80, 0x48, 0x20, 0x02, 0x08, 0x50, 0x52, 0x94, 0x21, 0x55, 0x60, 0x21, 0x95, 0x60, 0x02, 0x15, 0x60, 0x80, 0x5a, 0xa8, 0x08, 0x05, 0x80, 0x81, 0x69, 0x56, 0x0a, 0xea, 0x00, 0x00, 0x28, 0x00, 0x02, 0xab, 0x80, 0x04, 0x45, 0x10, 0x0a, 0xea, 0x80, 0x03, 0x9a, 0x00, 0x28, 0xa8, 0x20, 0x02, 0x00, 0x00, 0x28, 0x08, 0xa0, 0x45, 0x19, 0x18, 0x03, 0xef, 0xeb, 0x28, 0x28, 0xa8, 0x08, 0x20, 0xc0, 0x00, 0x88, 0x00, 0x02, 0x05, 0x80, 0x26, 0x16, 0x18, 0x02, 0x00, 0x00, 0x02, 0x8a, 0x80, 0x0a, 0x15, 0x28, 0x00, 0x00, 0x08, 0x05, 0x99, 0xb0, 0x26, 0x08, 0xa0, 0x88, 0x02, 0x22, 0xa1, 0x42, 0x05, 0x0a, 0xaa, 0x80, 0x0a, 0xaa, 0x80, 0x00, 0xaa, 0x80, 0x85, 0x5a, 0x70, 0x08, 0x55, 0x80, 0x95, 0x55, 0x56, 0x2b, 0xae, 0x80, 0x00, 0x88, 0x00, 0x0a, 0xee, 0xa0, 0x11, 0x11, 0x40, 0x0b, 0xab, 0xa0, 0x0b, 0xed, 0xe0, 0xba, 0xba, 0xa0, 0x02, 0xa8, 0x00, 0x2a, 0x2a, 0xb8, 0x1a, 0x6a, 0x78, 0x29, 0xbe, 0x68, 0xae, 0xaa, 0xb8, 0x31, 0x81, 0xc0, 0x00, 0x80, 0x00, 0x02, 0x55, 0x80, 0x87, 0x18, 0x18, 0x09, 0x82, 0x80, 0x00, 0xaa, 0x00, 0x28, 0x08, 0x0a, 0x08, 0x20, 0x20, 0x05, 0x65, 0xd0, 0x08, 0x02, 0x00, 0x08, 0xaa, 0x20, 0x28, 0x8a, 0x14, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x2a, 0xa2, 0x70, 0x02, 0xaa, 0x00, 0x2a, 0xaa, 0xa8, 0x2b, 0xba, 0xa0, 0x00, 0x87, 0x00, 0x2b, 0xae, 0xa8, 0x06, 0x66, 0x80, 0x0b, 0xae, 0xb8, 0x25, 0xb9, 0x5c, 0xba, 0xea, 0xe8, 0x02, 0xe0, 0x00, 0xae, 0xae, 0xea, 0x0b, 0xaa, 0xe0, 0x96, 0x97, 0x96, 0xba, 0xba, 0xea, 0x31, 0x85, 0x70, 0x00, 0xa0, 0x00, 0x02, 0x67, 0x00, 0x87, 0x1c, 0x57, 0x21, 0xc8, 0x70, 0x00, 0x2c, 0x00, 0x08, 0x0a, 0x08, 0x28, 0xa8, 0xa8, 0x01, 0x5f, 0x54, 0x20, 0x00, 0x80, 0x28, 0x2e, 0x28, 0x22, 0x82, 0x0a, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x09, 0xc2, 0x70, 0x00, 0x9c, 0x00, 0x08, 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xce, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xac, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], diff --git a/platform/Sprite.roc b/platform/Sprite.roc index df236e1..a21d04e 100644 --- a/platform/Sprite.roc +++ b/platform/Sprite.roc @@ -10,104 +10,119 @@ import Host ## Represents a [sprite](https://en.wikipedia.org/wiki/Sprite_(computer_graphics)) ## for drawing to the screen. Sprite := { - data : List(U8), - bpp : [BPP1, BPP2], - stride : U32, - region : { src_x : U32, src_y : U32, width : U32, height : U32 }, + data : List(U8), + bpp : [BPP1, BPP2], + stride : U32, + region : { src_x : U32, src_y : U32, width : U32, height : U32 }, }.{ - ## A subregion of a [Sprite]. - SubRegion : { src_x : U32, src_y : U32, width : U32, height : U32 } + Flags :: { bits : U32 }.{ + default : () -> Flags + default = || { bits: 0 } - ## Create a [Sprite] to be drawn or [blit](https://en.wikipedia.org/wiki/Bit_blit) - ## to the screen. - ## - ## ``` - ## fruit_sprite = Sprite.new({ - ## data: [0x00, 0xa0, 0x02, 0x00, 0x0e, 0xf0, 0x36, 0x5c, 0xd6, 0x57, 0xd5, 0x57, 0x35, 0x5c, 0x0f, 0xf0], - ## bpp: BPP2, - ## width: 8, - ## height: 8, - ## }) - ## ``` - new : { data : List(U8), bpp : [BPP1, BPP2], width : U32, height : U32 } -> Sprite - new = |{ data, bpp, width, height }| { - data, - bpp, - stride: width, - region: { - src_x: 0, - src_y: 0, - width, - height, - }, - } + flip_x : Flags -> Flags + flip_x = |{ bits }| { bits: bits.bitwise_or(2) } - ## Draw a [Sprite] to the framebuffer. - ## - ## ``` - ## Sprite.blit!(fruit_sprite, { x: 0, y: 0, flags: [FlipX, Rotate] }) - ## ``` - ## - ## [Refer to the WASM-4 docs for more information](https://wasm4.org/docs/reference/functions#blit-spriteptr-x-y-width-height-flags) - blit! : Sprite, { x : I32, y : I32, flags : List([FlipX, FlipY, Rotate]) } => {} - blit! = |sprite, { x, y, flags }| { - { src_x, src_y, width, height } = sprite.region + flip_y : Flags -> Flags + flip_y = |{ bits }| { bits: bits.bitwise_or(4) } - format = match sprite.bpp { - BPP1 => 0 - BPP2 => 1 - } + rotate : Flags -> Flags + rotate = |{ bits }| { bits: bits.bitwise_or(8) } - # Each flag occupies a distinct bit, so we can sum non-duplicate contributions. - flip_x_bit = if flags.contains(FlipX) { 2 } else { 0 } - flip_y_bit = if flags.contains(FlipY) { 4 } else { 0 } - rotate_bit = if flags.contains(Rotate) { 8 } else { 0 } - combined = format + flip_x_bit + flip_y_bit + rotate_bit + to_u32 : Flags -> U32 + to_u32 = |{ bits }| bits + } - Host.blit_sub!(sprite.data, x, y, width, height, src_x, src_y, sprite.stride, combined) - } + ## A subregion of a [Sprite]. + SubRegion : { src_x : U32, src_y : U32, width : U32, height : U32 } - ## Creates a [Sprite] referencing a subregion of the current [Sprite]. - ## This will return an error if the subregion does not fit in the current [Sprite]. - ## - ## ``` - ## sub_sprite_result = Sprite.sub(sprite, { src_x: 20, src_y: 0, width: 20, height: 20 }) - ## ``` - ## - ## Note: If your program should never generate an invalid subregion, - ## [sub_or_crash] enables avoiding the result and simpler code. - sub : Sprite, SubRegion -> Try(Sprite, [OutOfBounds]) - sub = |sprite, sub_region| { - current_region = sprite.region + ## Create a [Sprite] to be drawn or [blit](https://en.wikipedia.org/wiki/Bit_blit) + ## to the screen. + ## + ## ``` + ## fruit_sprite = Sprite.new({ + ## data: [0x00, 0xa0, 0x02, 0x00, 0x0e, 0xf0, 0x36, 0x5c, 0xd6, 0x57, 0xd5, 0x57, 0x35, 0x5c, 0x0f, 0xf0], + ## bpp: BPP2, + ## width: 8, + ## height: 8, + ## }) + ## ``` + new : { data : List(U8), bpp : [BPP1, BPP2], width : U32, height : U32 } -> Sprite + new = |{ data, bpp, width, height }| { + data, + bpp, + stride: width, + region: { + src_x: 0, + src_y: 0, + width, + height, + }, + } - out_of_bound_x = sub_region.src_x + sub_region.width > current_region.width - out_of_bound_y = sub_region.src_y + sub_region.height > current_region.height + ## Draw a [Sprite] to the framebuffer. + ## + ## ``` + ## Sprite.blit!(fruit_sprite, { x: 0, y: 0, flags: Sprite.Flags.default().flip_x().rotate() }) + ## ``` + ## + ## [Refer to the WASM-4 docs for more information](https://wasm4.org/docs/reference/functions#blit-spriteptr-x-y-width-height-flags) + blit! : Sprite, { x : I32, y : I32, flags : Flags } => {} + blit! = |sprite, { x, y, flags }| { + { src_x, src_y, width, height } = sprite.region - if out_of_bound_x or out_of_bound_y { - Err(OutOfBounds) - } else { - new_region = { - src_x: current_region.src_x + sub_region.src_x, - src_y: current_region.src_y + sub_region.src_y, - width: sub_region.width, - height: sub_region.height, - } - Ok({ ..sprite, region: new_region }) - } - } + format = match sprite.bpp { + BPP1 => 0 + BPP2 => 1 + } - ## Equivalent to the [sub] function, but will crash on error. - ## This is really useful for static sprite sheet data that needs subSprites extracted. - ## - ## ``` - ## sub_sprite = Sprite.sub_or_crash(sprite, { src_x: 20, src_y: 0, width: 20, height: 20 }) - ## ``` - ## - ## Warning: Will crash if the subregion is not contained within the sprite. - sub_or_crash : Sprite, SubRegion -> Sprite - sub_or_crash = |sprite, sub_region| - match Sprite.sub(sprite, sub_region) { - Ok(s) => s - Err(OutOfBounds) => { crash "out of bounds subregion when generating subsprite" } - } + combined = format + Flags.to_u32(flags) + + Host.blit_sub!(sprite.data, x, y, width, height, src_x, src_y, sprite.stride, combined) + } + + ## Creates a [Sprite] referencing a subregion of the current [Sprite]. + ## This will return an error if the subregion does not fit in the current [Sprite]. + ## + ## ``` + ## sub_sprite_result = Sprite.sub(sprite, { src_x: 20, src_y: 0, width: 20, height: 20 }) + ## ``` + ## + ## Note: If your program should never generate an invalid subregion, + ## [sub_or_crash] enables avoiding the result and simpler code. + sub : Sprite, SubRegion -> Try(Sprite, [OutOfBounds]) + sub = |sprite, sub_region| { + current_region = sprite.region + + out_of_bound_x = sub_region.src_x + sub_region.width > current_region.width + out_of_bound_y = sub_region.src_y + sub_region.height > current_region.height + + if out_of_bound_x or out_of_bound_y { + Err(OutOfBounds) + } else { + new_region = { + src_x: current_region.src_x + sub_region.src_x, + src_y: current_region.src_y + sub_region.src_y, + width: sub_region.width, + height: sub_region.height, + } + Ok({ ..sprite, region: new_region }) + } + } + + ## Equivalent to the [sub] function, but will crash on error. + ## This is really useful for static sprite sheet data that needs subSprites extracted. + ## + ## ``` + ## sub_sprite = Sprite.sub_or_crash(sprite, { src_x: 20, src_y: 0, width: 20, height: 20 }) + ## ``` + ## + ## Warning: Will crash if the subregion is not contained within the sprite. + sub_or_crash : Sprite, SubRegion -> Sprite + sub_or_crash = |sprite, sub_region| + match Sprite.sub(sprite, sub_region) { + Ok(s) => s + Err(OutOfBounds) => { + crash "out of bounds subregion when generating subsprite" + } + } } From 43d182436226e3ed3116fdde14c6257ab4cc2c20 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 11:23:53 -0400 Subject: [PATCH 08/19] Use explicit loops for rocci bird lists --- examples/rocci-bird.roc | 43 +++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index 621d6d2..4ad86df 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -429,11 +429,16 @@ draw_pipe! = |sprite, { x, gap_start }| { } update_pipes : List(Pipe) -> List(Pipe) -update_pipes = |pipes| - pipes.iter() - .map(|pipe| { ..pipe, x: pipe.x - 1 }) - .drop_if(|pipe| pipe.x < -20) - .collect() +update_pipes = |pipes| { + var $out = List.with_capacity(pipes.len()) + for pipe in pipes { + moved = { ..pipe, x: pipe.x - 1 } + if moved.x >= -20 { + $out = $out.append(moved) + } + } + $out +} maybe_generate_pipe! : U64, U64 => Try(Pipe, [NoPipe]) maybe_generate_pipe! = |last_generated, frame_count| @@ -457,17 +462,27 @@ random_plant! = |x| { x, type: I32.to_u32_wrap(W4.rand!()) % plant_types } starting_plants! : () => List(Plant) -starting_plants! = || - (0..=14).stream() - .map(|i| random_plant!(i * 12)) - .collect!() +starting_plants! = || { + var $plants = List.with_capacity(15) + var $i = 0 + while $i <= 14 { + $plants = $plants.append(random_plant!($i * 12)) + $i = $i + 1 + } + $plants +} update_plants : List(Plant) -> List(Plant) -update_plants = |plants| - plants.iter() - .map(|plant| { ..plant, x: plant.x - 1 }) - .drop_if(|plant| plant.x < -12) - .collect() +update_plants = |plants| { + var $out = List.with_capacity(plants.len()) + for plant in plants { + moved = { ..plant, x: plant.x - 1 } + if moved.x >= -12 { + $out = $out.append(moved) + } + } + $out +} maybe_generate_plant! : U64, U64 => Try(Plant, [NoPlant]) maybe_generate_plant! = |last_generated, frame_count| From 9fec04dfc91de0132e83ee82a700f95f1b305cb8 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 11:53:56 -0400 Subject: [PATCH 09/19] Hoist Rocci Bird animation cells --- examples/rocci-bird.roc | 54 ++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index 4ad86df..acbf479 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -691,11 +691,7 @@ create_rocci_idle_anim = |frame_count| { last_updated: frame_count, index: 0, state: Loop, - cells: [ - { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, - { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, - { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, - ], + cells: rocci_idle_cells, } flap_allowed : U64, Animation -> Bool @@ -713,11 +709,7 @@ create_rocci_flap_anim = |frame_count| { last_updated: frame_count, index: 2, state: Completed, - cells: [ - { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, - { frames: 12, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, - { frames: 1, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, - ], + cells: rocci_flap_cells, } create_rocci_fall_anim : U64 -> Animation @@ -725,10 +717,7 @@ create_rocci_fall_anim = |frame_count| { last_updated: frame_count, index: 0, state: Loop, - cells: [ - { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 48, src_y: 0, width: 16, height: 16 }) }, - { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 64, src_y: 0, width: 16, height: 16 }) }, - ], + cells: rocci_fall_cells, } create_high_score_anim : U64 -> Animation @@ -736,12 +725,7 @@ create_high_score_anim = |frame_count| { last_updated: frame_count, index: 0, state: Loop, - cells: [ - { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 0, src_y: 0, width: 32, height: 16 }) }, - { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 32, src_y: 0, width: 32, height: 16 }) }, - { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 64, src_y: 0, width: 32, height: 16 }) }, - { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 96, src_y: 0, width: 32, height: 16 }) }, - ], + cells: high_score_cells, } # ===== Sprites =========================================== @@ -803,3 +787,33 @@ high_score_sprite_sheet = Sprite.new( height: 16, }, ) + +# ===== Animation Cells =================================== + +rocci_idle_cells : List({ frames : U64, sprite : Sprite }) +rocci_idle_cells = [ + { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, + { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, + { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, +] + +rocci_flap_cells : List({ frames : U64, sprite : Sprite }) +rocci_flap_cells = [ + { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, + { frames: 12, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, + { frames: 1, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, +] + +rocci_fall_cells : List({ frames : U64, sprite : Sprite }) +rocci_fall_cells = [ + { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 48, src_y: 0, width: 16, height: 16 }) }, + { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 64, src_y: 0, width: 16, height: 16 }) }, +] + +high_score_cells : List({ frames : U64, sprite : Sprite }) +high_score_cells = [ + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 0, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 32, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 64, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 96, src_y: 0, width: 32, height: 16 }) }, +] From b8b0195634b63eb979b94db8baa1be47fa4c5d4f Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 12:34:07 -0400 Subject: [PATCH 10/19] Format Rocci Bird with updated formatter --- examples/rocci-bird.roc | 160 ++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 90 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index acbf479..abd9a2c 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -70,13 +70,11 @@ TitleScreenState : { init_title_screen : U64, List(Plant) -> Model init_title_screen = |frame_count, plants| - TitleScreen( - { - frame_count, - plants, - rocci_idle_anim: create_rocci_idle_anim(frame_count), - }, - ) + TitleScreen({ + frame_count, + plants, + rocci_idle_anim: create_rocci_idle_anim(frame_count), + }) run_title_screen! : TitleScreenState => Model run_title_screen! = |prev| { @@ -132,24 +130,22 @@ init_game! = |state| { W4.seed_rand!(frame_count) W4.tone!(flap_tone) - Game( - { - frame_count, - score: 0, - max_score: 0, - player: { - y: player_start_y, - y_vel: jump_speed, - }, - last_pipe_generated: frame_count, - pipes: [], - plants, - last_plant_generated: sub_saturating_u64(frame_count, 4), - last_flap: True, - rocci_flap_anim: create_rocci_flap_anim(frame_count), - ground_x: 0, + Game({ + frame_count, + score: 0, + max_score: 0, + player: { + y: player_start_y, + y_vel: jump_speed, }, - ) + last_pipe_generated: frame_count, + pipes: [], + plants, + last_plant_generated: sub_saturating_u64(frame_count, 4), + last_flap: True, + rocci_flap_anim: create_rocci_flap_anim(frame_count), + ground_x: 0, + }) } # Useful to throw in WolframAlpha to help calculate these: @@ -281,20 +277,18 @@ init_game_over! = |prev| { } save_high_score_to_disk!(high_score) - GameOver( - { - frame_count, - score, - high_score, - new_high_score, - player, - pipes, - plants, - rocci_fall_anim: create_rocci_fall_anim(frame_count), - high_score_anim: create_high_score_anim(frame_count), - ground_x, - }, - ) + GameOver({ + frame_count, + score, + high_score, + new_high_score, + player, + pipes, + plants, + rocci_fall_anim: create_rocci_fall_anim(frame_count), + high_score_anim: create_high_score_anim(frame_count), + ground_x, + }) } run_game_over! : GameOverState => Model @@ -390,12 +384,10 @@ on_screen_collided! = |player_y, anim_index| { } for { x, y } in collision_points { - if W4.get_pixel!( - { - x: I32.to_u8_wrap(player_x + x), - y: I32.to_u8_wrap(player_y + y), - }, - ) != Color1 { + if W4.get_pixel!({ + x: I32.to_u8_wrap(player_x + x), + y: I32.to_u8_wrap(player_y + y), + }) != Color1 { return True } } @@ -405,12 +397,10 @@ on_screen_collided! = |player_y, anim_index| { off_screen_collided! : () => Bool off_screen_collided! = || - W4.get_pixel!( - { - x: I32.to_u8_wrap(player_x + 13), - y: I32.to_u8_wrap(0), - }, - ) != Color1 + W4.get_pixel!({ + x: I32.to_u8_wrap(player_x + 13), + y: I32.to_u8_wrap(0), + }) != Color1 # ===== Pipes ============================================= @@ -736,57 +726,47 @@ create_high_score_anim = |frame_count| { rocci_sprite_sheet : Sprite rocci_sprite_sheet = - Sprite.new( - { - data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x56, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x80, 0x40, 0x05, 0x56, 0x81, 0x80, 0x14, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x81, 0x40, 0x01, 0x56, 0xa5, 0xa0, 0x15, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x0a, 0x95, 0x00, 0x00, 0x0a, 0x85, 0x40, 0x00, 0x56, 0xa9, 0x00, 0x15, 0x56, 0xa5, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x06, 0x55, 0x00, 0x00, 0x06, 0x55, 0x40, 0x00, 0x06, 0xaa, 0x00, 0x15, 0x5a, 0xaa, 0x00, 0x15, 0x56, 0xaa, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x06, 0x95, 0x00, 0x00, 0x09, 0x55, 0x00, 0x15, 0x6a, 0xa9, 0x00, 0x05, 0x56, 0xa9, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x09, 0x54, 0x00, 0x05, 0x6a, 0x94, 0x00, 0x01, 0x56, 0xa4, 0x00, 0x00, 0x1a, 0x94, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x69, 0x50, 0x00, 0x00, 0x56, 0x90, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - bpp: BPP2, - width: 80, - height: 16, - }, - ) + Sprite.new({ + data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x56, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x80, 0x40, 0x05, 0x56, 0x81, 0x80, 0x14, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x81, 0x40, 0x01, 0x56, 0xa5, 0xa0, 0x15, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x0a, 0x95, 0x00, 0x00, 0x0a, 0x85, 0x40, 0x00, 0x56, 0xa9, 0x00, 0x15, 0x56, 0xa5, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x06, 0x55, 0x00, 0x00, 0x06, 0x55, 0x40, 0x00, 0x06, 0xaa, 0x00, 0x15, 0x5a, 0xaa, 0x00, 0x15, 0x56, 0xaa, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x06, 0x95, 0x00, 0x00, 0x09, 0x55, 0x00, 0x15, 0x6a, 0xa9, 0x00, 0x05, 0x56, 0xa9, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x09, 0x54, 0x00, 0x05, 0x6a, 0x94, 0x00, 0x01, 0x56, 0xa4, 0x00, 0x00, 0x1a, 0x94, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x69, 0x50, 0x00, 0x00, 0x56, 0x90, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + bpp: BPP2, + width: 80, + height: 16, + }) ground_sprite : Sprite ground_sprite = - Sprite.new( - { - data: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x59, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x14, 0x51, 0x85, 0x14, 0x51, 0x49, 0x14, 0x91, 0x45, 0x18, 0x51, 0x54, 0x51, 0x49, 0x24, 0x51, 0x46, 0x14, 0x51, 0x46, 0x15, 0x14, 0x61, 0x45, 0x14, 0x92, 0x45, 0x14, 0x61, 0x46, 0x14, 0x55, 0x14, 0x51, 0x45, 0x14, 0x61, 0x51, 0x45, 0x65, 0x66, 0x56, 0x56, 0x59, 0x56, 0x59, 0x56, 0x55, 0x65, 0x65, 0x56, 0x59, 0x56, 0x59, 0x95, 0x59, 0x59, 0x55, 0x99, 0x59, 0x59, 0x55, 0x95, 0x96, 0x55, 0x99, 0x55, 0x95, 0x95, 0x59, 0x55, 0x96, 0x55, 0x59, 0x59, 0x95, 0x95, 0x96, 0x55, 0x95, 0x95, 0x99, 0x66, 0x65, 0x99, 0x65, 0x96, 0x95, 0x96, 0x59, 0x59, 0x65, 0x99, 0x65, 0xa5, 0x65, 0x96, 0x56, 0x56, 0x65, 0x99, 0x96, 0x59, 0x99, 0x66, 0x5a, 0x56, 0x59, 0x65, 0x96, 0x56, 0x59, 0x66, 0x65, 0x65, 0x66, 0x59, 0x99, 0x59, 0xa6, 0x9a, 0x5a, 0x69, 0x6a, 0x5a, 0x66, 0xa5, 0xa6, 0x9a, 0x9a, 0x6a, 0x6a, 0x5a, 0x65, 0x69, 0xa6, 0xa6, 0x9a, 0x69, 0x69, 0xa5, 0xa6, 0x9a, 0x5a, 0x96, 0x56, 0x9a, 0x6a, 0x6a, 0xa6, 0x9a, 0x9a, 0x96, 0xa9, 0xa6, 0x96, 0x9a, 0x5a, 0x69, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], - bpp: BPP2, - width: 160, - height: 13, - }, - ) + Sprite.new({ + data: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x59, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x14, 0x51, 0x85, 0x14, 0x51, 0x49, 0x14, 0x91, 0x45, 0x18, 0x51, 0x54, 0x51, 0x49, 0x24, 0x51, 0x46, 0x14, 0x51, 0x46, 0x15, 0x14, 0x61, 0x45, 0x14, 0x92, 0x45, 0x14, 0x61, 0x46, 0x14, 0x55, 0x14, 0x51, 0x45, 0x14, 0x61, 0x51, 0x45, 0x65, 0x66, 0x56, 0x56, 0x59, 0x56, 0x59, 0x56, 0x55, 0x65, 0x65, 0x56, 0x59, 0x56, 0x59, 0x95, 0x59, 0x59, 0x55, 0x99, 0x59, 0x59, 0x55, 0x95, 0x96, 0x55, 0x99, 0x55, 0x95, 0x95, 0x59, 0x55, 0x96, 0x55, 0x59, 0x59, 0x95, 0x95, 0x96, 0x55, 0x95, 0x95, 0x99, 0x66, 0x65, 0x99, 0x65, 0x96, 0x95, 0x96, 0x59, 0x59, 0x65, 0x99, 0x65, 0xa5, 0x65, 0x96, 0x56, 0x56, 0x65, 0x99, 0x96, 0x59, 0x99, 0x66, 0x5a, 0x56, 0x59, 0x65, 0x96, 0x56, 0x59, 0x66, 0x65, 0x65, 0x66, 0x59, 0x99, 0x59, 0xa6, 0x9a, 0x5a, 0x69, 0x6a, 0x5a, 0x66, 0xa5, 0xa6, 0x9a, 0x9a, 0x6a, 0x6a, 0x5a, 0x65, 0x69, 0xa6, 0xa6, 0x9a, 0x69, 0x69, 0xa5, 0xa6, 0x9a, 0x5a, 0x96, 0x56, 0x9a, 0x6a, 0x6a, 0xa6, 0x9a, 0x9a, 0x96, 0xa9, 0xa6, 0x96, 0x9a, 0x5a, 0x69, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], + bpp: BPP2, + width: 160, + height: 13, + }) pipe_sprite : Sprite pipe_sprite = - Sprite.new( - { - data: [0x0a, 0xaa, 0xaa, 0xab, 0xf0, 0x25, 0x55, 0x55, 0x55, 0x5c, 0x26, 0x96, 0x6a, 0x9a, 0xac, 0x36, 0x96, 0x6a, 0x66, 0xac, 0x36, 0x96, 0x6a, 0x9a, 0xac, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0], - bpp: BPP2, - width: 20, - height: 160, - }, - ) + Sprite.new({ + data: [0x0a, 0xaa, 0xaa, 0xab, 0xf0, 0x25, 0x55, 0x55, 0x55, 0x5c, 0x26, 0x96, 0x6a, 0x9a, 0xac, 0x36, 0x96, 0x6a, 0x66, 0xac, 0x36, 0x96, 0x6a, 0x9a, 0xac, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0], + bpp: BPP2, + width: 20, + height: 160, + }) plant_sprite_sheet : Sprite plant_sprite_sheet = - Sprite.new( - { - data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x02, 0xaa, 0x00, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xa0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x08, 0x00, 0x80, 0x08, 0x08, 0x80, 0x00, 0x2a, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x11, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x05, 0x14, 0x00, 0x28, 0x00, 0x51, 0x45, 0x00, 0x20, 0x01, 0x60, 0x20, 0x81, 0x60, 0x00, 0x80, 0x80, 0x0a, 0xa0, 0x16, 0x00, 0xa8, 0x00, 0x82, 0x15, 0x96, 0x02, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x02, 0xa0, 0x00, 0x00, 0x28, 0x00, 0x02, 0x20, 0x00, 0x02, 0x82, 0x00, 0x06, 0x11, 0x11, 0x02, 0xb2, 0xb0, 0x02, 0x80, 0x08, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x40, 0x15, 0x11, 0x15, 0x00, 0x00, 0x00, 0x24, 0x20, 0x80, 0x51, 0x41, 0x50, 0x00, 0x22, 0x00, 0x14, 0x41, 0x44, 0x20, 0x55, 0x60, 0x28, 0x59, 0xa0, 0x02, 0x01, 0x60, 0x20, 0x21, 0x56, 0x02, 0x02, 0x00, 0x83, 0x55, 0xd6, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x10, 0x40, 0x02, 0xa8, 0x80, 0x09, 0x5c, 0x00, 0x08, 0x20, 0x08, 0x02, 0x00, 0x00, 0x0a, 0x0a, 0x28, 0x91, 0x44, 0x66, 0x09, 0x6d, 0x5c, 0x0a, 0x08, 0x20, 0x02, 0x0b, 0x00, 0x00, 0x20, 0x00, 0x02, 0x06, 0x00, 0x0a, 0x15, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x15, 0xa0, 0x15, 0x15, 0x08, 0x00, 0x00, 0x00, 0x04, 0x86, 0x60, 0x15, 0x80, 0x48, 0x20, 0x02, 0x08, 0x50, 0x52, 0x94, 0x21, 0x55, 0x60, 0x21, 0x95, 0x60, 0x02, 0x15, 0x60, 0x80, 0x5a, 0xa8, 0x08, 0x05, 0x80, 0x81, 0x69, 0x56, 0x0a, 0xea, 0x00, 0x00, 0x28, 0x00, 0x02, 0xab, 0x80, 0x04, 0x45, 0x10, 0x0a, 0xea, 0x80, 0x03, 0x9a, 0x00, 0x28, 0xa8, 0x20, 0x02, 0x00, 0x00, 0x28, 0x08, 0xa0, 0x45, 0x19, 0x18, 0x03, 0xef, 0xeb, 0x28, 0x28, 0xa8, 0x08, 0x20, 0xc0, 0x00, 0x88, 0x00, 0x02, 0x05, 0x80, 0x26, 0x16, 0x18, 0x02, 0x00, 0x00, 0x02, 0x8a, 0x80, 0x0a, 0x15, 0x28, 0x00, 0x00, 0x08, 0x05, 0x99, 0xb0, 0x26, 0x08, 0xa0, 0x88, 0x02, 0x22, 0xa1, 0x42, 0x05, 0x0a, 0xaa, 0x80, 0x0a, 0xaa, 0x80, 0x00, 0xaa, 0x80, 0x85, 0x5a, 0x70, 0x08, 0x55, 0x80, 0x95, 0x55, 0x56, 0x2b, 0xae, 0x80, 0x00, 0x88, 0x00, 0x0a, 0xee, 0xa0, 0x11, 0x11, 0x40, 0x0b, 0xab, 0xa0, 0x0b, 0xed, 0xe0, 0xba, 0xba, 0xa0, 0x02, 0xa8, 0x00, 0x2a, 0x2a, 0xb8, 0x1a, 0x6a, 0x78, 0x29, 0xbe, 0x68, 0xae, 0xaa, 0xb8, 0x31, 0x81, 0xc0, 0x00, 0x80, 0x00, 0x02, 0x55, 0x80, 0x87, 0x18, 0x18, 0x09, 0x82, 0x80, 0x00, 0xaa, 0x00, 0x28, 0x08, 0x0a, 0x08, 0x20, 0x20, 0x05, 0x65, 0xd0, 0x08, 0x02, 0x00, 0x08, 0xaa, 0x20, 0x28, 0x8a, 0x14, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x2a, 0xa2, 0x70, 0x02, 0xaa, 0x00, 0x2a, 0xaa, 0xa8, 0x2b, 0xba, 0xa0, 0x00, 0x87, 0x00, 0x2b, 0xae, 0xa8, 0x06, 0x66, 0x80, 0x0b, 0xae, 0xb8, 0x25, 0xb9, 0x5c, 0xba, 0xea, 0xe8, 0x02, 0xe0, 0x00, 0xae, 0xae, 0xea, 0x0b, 0xaa, 0xe0, 0x96, 0x97, 0x96, 0xba, 0xba, 0xea, 0x31, 0x85, 0x70, 0x00, 0xa0, 0x00, 0x02, 0x67, 0x00, 0x87, 0x1c, 0x57, 0x21, 0xc8, 0x70, 0x00, 0x2c, 0x00, 0x08, 0x0a, 0x08, 0x28, 0xa8, 0xa8, 0x01, 0x5f, 0x54, 0x20, 0x00, 0x80, 0x28, 0x2e, 0x28, 0x22, 0x82, 0x0a, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x09, 0xc2, 0x70, 0x00, 0x9c, 0x00, 0x08, 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xce, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xac, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - bpp: BPP2, - width: 360, - height: 12, - }, - ) + Sprite.new({ + data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x02, 0xaa, 0x00, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xa0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x08, 0x00, 0x80, 0x08, 0x08, 0x80, 0x00, 0x2a, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x11, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x05, 0x14, 0x00, 0x28, 0x00, 0x51, 0x45, 0x00, 0x20, 0x01, 0x60, 0x20, 0x81, 0x60, 0x00, 0x80, 0x80, 0x0a, 0xa0, 0x16, 0x00, 0xa8, 0x00, 0x82, 0x15, 0x96, 0x02, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x02, 0xa0, 0x00, 0x00, 0x28, 0x00, 0x02, 0x20, 0x00, 0x02, 0x82, 0x00, 0x06, 0x11, 0x11, 0x02, 0xb2, 0xb0, 0x02, 0x80, 0x08, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x40, 0x15, 0x11, 0x15, 0x00, 0x00, 0x00, 0x24, 0x20, 0x80, 0x51, 0x41, 0x50, 0x00, 0x22, 0x00, 0x14, 0x41, 0x44, 0x20, 0x55, 0x60, 0x28, 0x59, 0xa0, 0x02, 0x01, 0x60, 0x20, 0x21, 0x56, 0x02, 0x02, 0x00, 0x83, 0x55, 0xd6, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x10, 0x40, 0x02, 0xa8, 0x80, 0x09, 0x5c, 0x00, 0x08, 0x20, 0x08, 0x02, 0x00, 0x00, 0x0a, 0x0a, 0x28, 0x91, 0x44, 0x66, 0x09, 0x6d, 0x5c, 0x0a, 0x08, 0x20, 0x02, 0x0b, 0x00, 0x00, 0x20, 0x00, 0x02, 0x06, 0x00, 0x0a, 0x15, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x15, 0xa0, 0x15, 0x15, 0x08, 0x00, 0x00, 0x00, 0x04, 0x86, 0x60, 0x15, 0x80, 0x48, 0x20, 0x02, 0x08, 0x50, 0x52, 0x94, 0x21, 0x55, 0x60, 0x21, 0x95, 0x60, 0x02, 0x15, 0x60, 0x80, 0x5a, 0xa8, 0x08, 0x05, 0x80, 0x81, 0x69, 0x56, 0x0a, 0xea, 0x00, 0x00, 0x28, 0x00, 0x02, 0xab, 0x80, 0x04, 0x45, 0x10, 0x0a, 0xea, 0x80, 0x03, 0x9a, 0x00, 0x28, 0xa8, 0x20, 0x02, 0x00, 0x00, 0x28, 0x08, 0xa0, 0x45, 0x19, 0x18, 0x03, 0xef, 0xeb, 0x28, 0x28, 0xa8, 0x08, 0x20, 0xc0, 0x00, 0x88, 0x00, 0x02, 0x05, 0x80, 0x26, 0x16, 0x18, 0x02, 0x00, 0x00, 0x02, 0x8a, 0x80, 0x0a, 0x15, 0x28, 0x00, 0x00, 0x08, 0x05, 0x99, 0xb0, 0x26, 0x08, 0xa0, 0x88, 0x02, 0x22, 0xa1, 0x42, 0x05, 0x0a, 0xaa, 0x80, 0x0a, 0xaa, 0x80, 0x00, 0xaa, 0x80, 0x85, 0x5a, 0x70, 0x08, 0x55, 0x80, 0x95, 0x55, 0x56, 0x2b, 0xae, 0x80, 0x00, 0x88, 0x00, 0x0a, 0xee, 0xa0, 0x11, 0x11, 0x40, 0x0b, 0xab, 0xa0, 0x0b, 0xed, 0xe0, 0xba, 0xba, 0xa0, 0x02, 0xa8, 0x00, 0x2a, 0x2a, 0xb8, 0x1a, 0x6a, 0x78, 0x29, 0xbe, 0x68, 0xae, 0xaa, 0xb8, 0x31, 0x81, 0xc0, 0x00, 0x80, 0x00, 0x02, 0x55, 0x80, 0x87, 0x18, 0x18, 0x09, 0x82, 0x80, 0x00, 0xaa, 0x00, 0x28, 0x08, 0x0a, 0x08, 0x20, 0x20, 0x05, 0x65, 0xd0, 0x08, 0x02, 0x00, 0x08, 0xaa, 0x20, 0x28, 0x8a, 0x14, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x2a, 0xa2, 0x70, 0x02, 0xaa, 0x00, 0x2a, 0xaa, 0xa8, 0x2b, 0xba, 0xa0, 0x00, 0x87, 0x00, 0x2b, 0xae, 0xa8, 0x06, 0x66, 0x80, 0x0b, 0xae, 0xb8, 0x25, 0xb9, 0x5c, 0xba, 0xea, 0xe8, 0x02, 0xe0, 0x00, 0xae, 0xae, 0xea, 0x0b, 0xaa, 0xe0, 0x96, 0x97, 0x96, 0xba, 0xba, 0xea, 0x31, 0x85, 0x70, 0x00, 0xa0, 0x00, 0x02, 0x67, 0x00, 0x87, 0x1c, 0x57, 0x21, 0xc8, 0x70, 0x00, 0x2c, 0x00, 0x08, 0x0a, 0x08, 0x28, 0xa8, 0xa8, 0x01, 0x5f, 0x54, 0x20, 0x00, 0x80, 0x28, 0x2e, 0x28, 0x22, 0x82, 0x0a, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x09, 0xc2, 0x70, 0x00, 0x9c, 0x00, 0x08, 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xce, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xac, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + bpp: BPP2, + width: 360, + height: 12, + }) high_score_sprite_sheet : Sprite -high_score_sprite_sheet = Sprite.new( - { - data: [0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x09, 0x60, 0x25, 0x80, 0x96, 0x02, 0x58, 0x00, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x25, 0x80, 0x96, 0x02, 0x58, 0x09, 0x60, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00], - bpp: BPP2, - width: 128, - height: 16, - }, -) +high_score_sprite_sheet = Sprite.new({ + data: [0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x09, 0x60, 0x25, 0x80, 0x96, 0x02, 0x58, 0x00, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x25, 0x80, 0x96, 0x02, 0x58, 0x09, 0x60, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00], + bpp: BPP2, + width: 128, + height: 16, +}) # ===== Animation Cells =================================== From d738eb6fa123d9d0b514c692c5ffae616714dca0 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 13:12:37 -0400 Subject: [PATCH 11/19] Use new Roc builtins in Rocci Bird --- examples/rocci-bird.roc | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index abd9a2c..b25184c 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -43,23 +43,6 @@ update! = |model| update_frame_count = |prev| { ..prev, frame_count: prev.frame_count + 1 } -# TODO: switch for builtin List.append_if_ok -append_if_ok : List(a), Try(a, err) -> List(a) -append_if_ok = |items, maybe_item| - match maybe_item { - Ok(item) => items.append(item) - Err(_) => items - } - -# TODO: use the builtin once it exists -sub_saturating_u64 : U64, U64 -> U64 -sub_saturating_u64 = |x, y| - if x < y { - 0 - } else { - x - y - } - # ===== Title Screen ====================================== TitleScreenState : { @@ -141,7 +124,7 @@ init_game! = |state| { last_pipe_generated: frame_count, pipes: [], plants, - last_plant_generated: sub_saturating_u64(frame_count, 4), + last_plant_generated: U64.minus_saturated(frame_count, 4), last_flap: True, rocci_flap_anim: create_rocci_flap_anim(frame_count), ground_x: 0, @@ -189,7 +172,7 @@ run_game! = |prev| { prev.last_pipe_generated } - pipes = append_if_ok(update_pipes(prev.pipes), pipe) + pipes = List.append_if_ok(update_pipes(prev.pipes), pipe) plant = maybe_generate_plant!(prev.last_plant_generated, prev.frame_count) last_plant_generated = @@ -199,7 +182,7 @@ run_game! = |prev| { prev.last_plant_generated } - plants = append_if_ok(update_plants(prev.plants), plant) + plants = List.append_if_ok(update_plants(prev.plants), plant) gain_point = U64.to_u8_wrap(prev.pipes.count_if(|candidate_pipe| candidate_pipe.x == player_x - 2)) y = prev.player.y + y_vel score = U8.plus_saturated(prev.score, gain_point) From cc9fd0ad7af56cc0974446264495931242b36e57 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 23 Jun 2026 13:56:46 -0400 Subject: [PATCH 12/19] Build wasm4 release host with ReleaseSmall --- .github/workflows/release.yml | 2 +- CONTRIBUTING.md | 4 ++-- ci/all_tests.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29feb54..9dc309e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,7 +55,7 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Build host object - run: zig build -Doptimize=ReleaseSafe + run: zig build -Doptimize=ReleaseSmall - name: Bundle platform id: bundle diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8167b67..1edf5fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -84,7 +84,7 @@ If hot reloading stops working, press `R` in the WASM-4 runtime to reload the ca Package the platform as a Roc archive with: ```shell -zig build -Doptimize=ReleaseSafe +zig build -Doptimize=ReleaseSmall ROC=roc ./bundle.sh ``` @@ -114,4 +114,4 @@ zig build -Doptimize=ReleaseSmall roc build examples/snake.roc --opt=size ``` -If the cart is too large, you can try lowering the dynamic memory space with `-Dmem-size=`. The default is `40960` bytes. +If the cart is too large, you can try lowering the dynamic memory space with `-Dmem-size=`. The default is `32768` bytes. diff --git a/ci/all_tests.sh b/ci/all_tests.sh index b43cb37..82ec831 100755 --- a/ci/all_tests.sh +++ b/ci/all_tests.sh @@ -39,7 +39,7 @@ echo "Zig: $(zig version)" if [ "$SKIP_ZIG_BUILD" = "1" ]; then echo "Skipping zig build because SKIP_ZIG_BUILD=1" else - zig build + zig build -Doptimize=ReleaseSmall fi # zig tests From e384235256ec547bb5175bd16c99489deac271a0 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 24 Jun 2026 06:35:37 -0400 Subject: [PATCH 13/19] Inline Rocci Bird animation cells --- examples/rocci-bird.roc | 54 +++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index b25184c..9d6254f 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -664,7 +664,11 @@ create_rocci_idle_anim = |frame_count| { last_updated: frame_count, index: 0, state: Loop, - cells: rocci_idle_cells, + cells: [ + { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, + { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, + { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, + ], } flap_allowed : U64, Animation -> Bool @@ -682,7 +686,11 @@ create_rocci_flap_anim = |frame_count| { last_updated: frame_count, index: 2, state: Completed, - cells: rocci_flap_cells, + cells: [ + { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, + { frames: 12, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, + { frames: 1, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, + ], } create_rocci_fall_anim : U64 -> Animation @@ -690,7 +698,10 @@ create_rocci_fall_anim = |frame_count| { last_updated: frame_count, index: 0, state: Loop, - cells: rocci_fall_cells, + cells: [ + { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 48, src_y: 0, width: 16, height: 16 }) }, + { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 64, src_y: 0, width: 16, height: 16 }) }, + ], } create_high_score_anim : U64 -> Animation @@ -698,7 +709,12 @@ create_high_score_anim = |frame_count| { last_updated: frame_count, index: 0, state: Loop, - cells: high_score_cells, + cells: [ + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 0, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 32, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 64, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 96, src_y: 0, width: 32, height: 16 }) }, + ], } # ===== Sprites =========================================== @@ -750,33 +766,3 @@ high_score_sprite_sheet = Sprite.new({ width: 128, height: 16, }) - -# ===== Animation Cells =================================== - -rocci_idle_cells : List({ frames : U64, sprite : Sprite }) -rocci_idle_cells = [ - { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, - { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, - { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, -] - -rocci_flap_cells : List({ frames : U64, sprite : Sprite }) -rocci_flap_cells = [ - { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, - { frames: 12, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, - { frames: 1, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, -] - -rocci_fall_cells : List({ frames : U64, sprite : Sprite }) -rocci_fall_cells = [ - { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 48, src_y: 0, width: 16, height: 16 }) }, - { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 64, src_y: 0, width: 16, height: 16 }) }, -] - -high_score_cells : List({ frames : U64, sprite : Sprite }) -high_score_cells = [ - { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 0, src_y: 0, width: 32, height: 16 }) }, - { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 32, src_y: 0, width: 32, height: 16 }) }, - { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 64, src_y: 0, width: 32, height: 16 }) }, - { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 96, src_y: 0, width: 32, height: 16 }) }, -] From c1f48601eee556c9b08f08f06df5664088eefcc7 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 24 Jun 2026 09:32:10 -0400 Subject: [PATCH 14/19] Keep Rocci Bird iterator collision points --- examples/rocci-bird.roc | 50 ++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index 9d6254f..2e91337 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -148,7 +148,7 @@ run_game! = |prev| { flap = gamepad.button1 or gamepad.up or mouse.left - { y_vel, next_anim } = + { y_vel, next_anim } = if !prev.last_flap and flap and flap_allowed(prev.frame_count, prev.rocci_flap_anim) { W4.tone!(flap_tone) anim = prev.rocci_flap_anim @@ -165,7 +165,7 @@ run_game! = |prev| { pipe = maybe_generate_pipe!(prev.last_pipe_generated, prev.frame_count) - last_pipe_generated = + last_pipe_generated = if pipe.is_ok() { prev.frame_count } else { @@ -175,7 +175,7 @@ run_game! = |prev| { pipes = List.append_if_ok(update_pipes(prev.pipes), pipe) plant = maybe_generate_plant!(prev.last_plant_generated, prev.frame_count) - last_plant_generated = + last_plant_generated = if plant.is_ok() { prev.frame_count } else { @@ -252,7 +252,7 @@ init_game_over! = |prev| { hs = load_high_score_from_disk!() new_high_score = max_score > hs - high_score = + high_score = if new_high_score { max_score } else { @@ -355,9 +355,9 @@ on_screen_collided! = |player_y, anim_index| { { x: 5, y: 9 }, { x: 7, y: 10 }, { x: 5, y: 12 }, - ] + ].iter() - collision_points = + collision_points = if anim_index == 2 { base_points.append({ x: 2, y: 1 }).append({ x: 7, y: 1 }) } else if anim_index == 1 { @@ -392,14 +392,11 @@ Pipe : { x : I32, gap_start : I32 } gap_height = 40 draw_pipes! = |pipes| - pipes.for_each!(|pipe| draw_pipe!(pipe_sprite, pipe)) - -draw_pipe! : Sprite, Pipe => {} -draw_pipe! = |sprite, { x, gap_start }| { - set_sprite_colors!() - Sprite.blit!(sprite, { x, y: gap_start - W4.screen_height(), flags: Sprite.Flags.default().flip_y() }) - Sprite.blit!(sprite, { x, y: gap_start + gap_height, flags: Sprite.Flags.default() }) -} + pipes.for_each!(|{ x, gap_start }| { + set_sprite_colors!() + Sprite.blit!(sprite, { x, y: gap_start - W4.screen_height(), flags: Sprite.Flags.default().flip_y() }) + Sprite.blit!(sprite, { x, y: gap_start + gap_height, flags: Sprite.Flags.default() }) + }) update_pipes : List(Pipe) -> List(Pipe) update_pipes = |pipes| { @@ -467,14 +464,11 @@ maybe_generate_plant! = |last_generated, frame_count| draw_plants! : List(Plant) => {} draw_plants! = |plants| - plants.for_each!(|plant| draw_plant!(plant_sprite_sheet, plant)) - -draw_plant! : Sprite, Plant => {} -draw_plant! = |sprite_sheet, { x, type }| { - sprite = Sprite.sub_or_crash(sprite_sheet, { src_x: type * 12, src_y: 0, width: 12, height: 12 }) - set_sprite_colors!() - Sprite.blit!(sprite, { x, y: plant_y, flags: Sprite.Flags.default() }) -} + plants.for_each!(|{ x, type }| { + sprite = Sprite.sub_or_crash(sprite_sheet, { src_x: type * 12, src_y: 0, width: 12, height: 12 }) + set_sprite_colors!() + Sprite.blit!(sprite, { x, y: plant_y, flags: Sprite.Flags.default() }) + }) # ===== Sounds ============================================ @@ -522,7 +516,7 @@ death_tone = { draw_score! : U8, { x : I32, y : I32 } => {} draw_score! = |score, { x: base_x, y }| { set_text_colors!() - x = + x = if score < 10 { base_x + 8 } else if score < 100 { @@ -602,7 +596,7 @@ Animation : { update_animation : U64, Animation -> Animation update_animation = |frame_count, anim| { - frames_per_update = + frames_per_update = match anim.cells.get(anim.index) { Ok(cell) => cell.frames Err(_) => { @@ -724,7 +718,7 @@ create_high_score_anim = |frame_count| { # game can be in one source code file. rocci_sprite_sheet : Sprite -rocci_sprite_sheet = +rocci_sprite_sheet = Sprite.new({ data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x56, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x80, 0x40, 0x05, 0x56, 0x81, 0x80, 0x14, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x81, 0x40, 0x01, 0x56, 0xa5, 0xa0, 0x15, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x0a, 0x95, 0x00, 0x00, 0x0a, 0x85, 0x40, 0x00, 0x56, 0xa9, 0x00, 0x15, 0x56, 0xa5, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x06, 0x55, 0x00, 0x00, 0x06, 0x55, 0x40, 0x00, 0x06, 0xaa, 0x00, 0x15, 0x5a, 0xaa, 0x00, 0x15, 0x56, 0xaa, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x06, 0x95, 0x00, 0x00, 0x09, 0x55, 0x00, 0x15, 0x6a, 0xa9, 0x00, 0x05, 0x56, 0xa9, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x09, 0x54, 0x00, 0x05, 0x6a, 0x94, 0x00, 0x01, 0x56, 0xa4, 0x00, 0x00, 0x1a, 0x94, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x69, 0x50, 0x00, 0x00, 0x56, 0x90, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], bpp: BPP2, @@ -733,7 +727,7 @@ rocci_sprite_sheet = }) ground_sprite : Sprite -ground_sprite = +ground_sprite = Sprite.new({ data: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x59, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x14, 0x51, 0x85, 0x14, 0x51, 0x49, 0x14, 0x91, 0x45, 0x18, 0x51, 0x54, 0x51, 0x49, 0x24, 0x51, 0x46, 0x14, 0x51, 0x46, 0x15, 0x14, 0x61, 0x45, 0x14, 0x92, 0x45, 0x14, 0x61, 0x46, 0x14, 0x55, 0x14, 0x51, 0x45, 0x14, 0x61, 0x51, 0x45, 0x65, 0x66, 0x56, 0x56, 0x59, 0x56, 0x59, 0x56, 0x55, 0x65, 0x65, 0x56, 0x59, 0x56, 0x59, 0x95, 0x59, 0x59, 0x55, 0x99, 0x59, 0x59, 0x55, 0x95, 0x96, 0x55, 0x99, 0x55, 0x95, 0x95, 0x59, 0x55, 0x96, 0x55, 0x59, 0x59, 0x95, 0x95, 0x96, 0x55, 0x95, 0x95, 0x99, 0x66, 0x65, 0x99, 0x65, 0x96, 0x95, 0x96, 0x59, 0x59, 0x65, 0x99, 0x65, 0xa5, 0x65, 0x96, 0x56, 0x56, 0x65, 0x99, 0x96, 0x59, 0x99, 0x66, 0x5a, 0x56, 0x59, 0x65, 0x96, 0x56, 0x59, 0x66, 0x65, 0x65, 0x66, 0x59, 0x99, 0x59, 0xa6, 0x9a, 0x5a, 0x69, 0x6a, 0x5a, 0x66, 0xa5, 0xa6, 0x9a, 0x9a, 0x6a, 0x6a, 0x5a, 0x65, 0x69, 0xa6, 0xa6, 0x9a, 0x69, 0x69, 0xa5, 0xa6, 0x9a, 0x5a, 0x96, 0x56, 0x9a, 0x6a, 0x6a, 0xa6, 0x9a, 0x9a, 0x96, 0xa9, 0xa6, 0x96, 0x9a, 0x5a, 0x69, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], bpp: BPP2, @@ -742,7 +736,7 @@ ground_sprite = }) pipe_sprite : Sprite -pipe_sprite = +pipe_sprite = Sprite.new({ data: [0x0a, 0xaa, 0xaa, 0xab, 0xf0, 0x25, 0x55, 0x55, 0x55, 0x5c, 0x26, 0x96, 0x6a, 0x9a, 0xac, 0x36, 0x96, 0x6a, 0x66, 0xac, 0x36, 0x96, 0x6a, 0x9a, 0xac, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0], bpp: BPP2, @@ -751,7 +745,7 @@ pipe_sprite = }) plant_sprite_sheet : Sprite -plant_sprite_sheet = +plant_sprite_sheet = Sprite.new({ data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x02, 0xaa, 0x00, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xa0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x08, 0x00, 0x80, 0x08, 0x08, 0x80, 0x00, 0x2a, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x11, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x05, 0x14, 0x00, 0x28, 0x00, 0x51, 0x45, 0x00, 0x20, 0x01, 0x60, 0x20, 0x81, 0x60, 0x00, 0x80, 0x80, 0x0a, 0xa0, 0x16, 0x00, 0xa8, 0x00, 0x82, 0x15, 0x96, 0x02, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x02, 0xa0, 0x00, 0x00, 0x28, 0x00, 0x02, 0x20, 0x00, 0x02, 0x82, 0x00, 0x06, 0x11, 0x11, 0x02, 0xb2, 0xb0, 0x02, 0x80, 0x08, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x40, 0x15, 0x11, 0x15, 0x00, 0x00, 0x00, 0x24, 0x20, 0x80, 0x51, 0x41, 0x50, 0x00, 0x22, 0x00, 0x14, 0x41, 0x44, 0x20, 0x55, 0x60, 0x28, 0x59, 0xa0, 0x02, 0x01, 0x60, 0x20, 0x21, 0x56, 0x02, 0x02, 0x00, 0x83, 0x55, 0xd6, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x10, 0x40, 0x02, 0xa8, 0x80, 0x09, 0x5c, 0x00, 0x08, 0x20, 0x08, 0x02, 0x00, 0x00, 0x0a, 0x0a, 0x28, 0x91, 0x44, 0x66, 0x09, 0x6d, 0x5c, 0x0a, 0x08, 0x20, 0x02, 0x0b, 0x00, 0x00, 0x20, 0x00, 0x02, 0x06, 0x00, 0x0a, 0x15, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x15, 0xa0, 0x15, 0x15, 0x08, 0x00, 0x00, 0x00, 0x04, 0x86, 0x60, 0x15, 0x80, 0x48, 0x20, 0x02, 0x08, 0x50, 0x52, 0x94, 0x21, 0x55, 0x60, 0x21, 0x95, 0x60, 0x02, 0x15, 0x60, 0x80, 0x5a, 0xa8, 0x08, 0x05, 0x80, 0x81, 0x69, 0x56, 0x0a, 0xea, 0x00, 0x00, 0x28, 0x00, 0x02, 0xab, 0x80, 0x04, 0x45, 0x10, 0x0a, 0xea, 0x80, 0x03, 0x9a, 0x00, 0x28, 0xa8, 0x20, 0x02, 0x00, 0x00, 0x28, 0x08, 0xa0, 0x45, 0x19, 0x18, 0x03, 0xef, 0xeb, 0x28, 0x28, 0xa8, 0x08, 0x20, 0xc0, 0x00, 0x88, 0x00, 0x02, 0x05, 0x80, 0x26, 0x16, 0x18, 0x02, 0x00, 0x00, 0x02, 0x8a, 0x80, 0x0a, 0x15, 0x28, 0x00, 0x00, 0x08, 0x05, 0x99, 0xb0, 0x26, 0x08, 0xa0, 0x88, 0x02, 0x22, 0xa1, 0x42, 0x05, 0x0a, 0xaa, 0x80, 0x0a, 0xaa, 0x80, 0x00, 0xaa, 0x80, 0x85, 0x5a, 0x70, 0x08, 0x55, 0x80, 0x95, 0x55, 0x56, 0x2b, 0xae, 0x80, 0x00, 0x88, 0x00, 0x0a, 0xee, 0xa0, 0x11, 0x11, 0x40, 0x0b, 0xab, 0xa0, 0x0b, 0xed, 0xe0, 0xba, 0xba, 0xa0, 0x02, 0xa8, 0x00, 0x2a, 0x2a, 0xb8, 0x1a, 0x6a, 0x78, 0x29, 0xbe, 0x68, 0xae, 0xaa, 0xb8, 0x31, 0x81, 0xc0, 0x00, 0x80, 0x00, 0x02, 0x55, 0x80, 0x87, 0x18, 0x18, 0x09, 0x82, 0x80, 0x00, 0xaa, 0x00, 0x28, 0x08, 0x0a, 0x08, 0x20, 0x20, 0x05, 0x65, 0xd0, 0x08, 0x02, 0x00, 0x08, 0xaa, 0x20, 0x28, 0x8a, 0x14, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x2a, 0xa2, 0x70, 0x02, 0xaa, 0x00, 0x2a, 0xaa, 0xa8, 0x2b, 0xba, 0xa0, 0x00, 0x87, 0x00, 0x2b, 0xae, 0xa8, 0x06, 0x66, 0x80, 0x0b, 0xae, 0xb8, 0x25, 0xb9, 0x5c, 0xba, 0xea, 0xe8, 0x02, 0xe0, 0x00, 0xae, 0xae, 0xea, 0x0b, 0xaa, 0xe0, 0x96, 0x97, 0x96, 0xba, 0xba, 0xea, 0x31, 0x85, 0x70, 0x00, 0xa0, 0x00, 0x02, 0x67, 0x00, 0x87, 0x1c, 0x57, 0x21, 0xc8, 0x70, 0x00, 0x2c, 0x00, 0x08, 0x0a, 0x08, 0x28, 0xa8, 0xa8, 0x01, 0x5f, 0x54, 0x20, 0x00, 0x80, 0x28, 0x2e, 0x28, 0x22, 0x82, 0x0a, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x09, 0xc2, 0x70, 0x00, 0x9c, 0x00, 0x08, 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xce, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xac, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], bpp: BPP2, From deae1505a67a61053c635622e159b5ac24e7a9f1 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 24 Jun 2026 14:52:59 -0400 Subject: [PATCH 15/19] Fix Rocci Bird sprite references --- examples/rocci-bird.roc | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/rocci-bird.roc b/examples/rocci-bird.roc index 2e91337..93dbd53 100644 --- a/examples/rocci-bird.roc +++ b/examples/rocci-bird.roc @@ -392,11 +392,13 @@ Pipe : { x : I32, gap_start : I32 } gap_height = 40 draw_pipes! = |pipes| - pipes.for_each!(|{ x, gap_start }| { - set_sprite_colors!() - Sprite.blit!(sprite, { x, y: gap_start - W4.screen_height(), flags: Sprite.Flags.default().flip_y() }) - Sprite.blit!(sprite, { x, y: gap_start + gap_height, flags: Sprite.Flags.default() }) - }) + pipes.for_each!( + |{ x, gap_start }| { + set_sprite_colors!() + Sprite.blit!(pipe_sprite, { x, y: gap_start - W4.screen_height(), flags: Sprite.Flags.default().flip_y() }) + Sprite.blit!(pipe_sprite, { x, y: gap_start + gap_height, flags: Sprite.Flags.default() }) + }, + ) update_pipes : List(Pipe) -> List(Pipe) update_pipes = |pipes| { @@ -464,11 +466,13 @@ maybe_generate_plant! = |last_generated, frame_count| draw_plants! : List(Plant) => {} draw_plants! = |plants| - plants.for_each!(|{ x, type }| { - sprite = Sprite.sub_or_crash(sprite_sheet, { src_x: type * 12, src_y: 0, width: 12, height: 12 }) - set_sprite_colors!() - Sprite.blit!(sprite, { x, y: plant_y, flags: Sprite.Flags.default() }) - }) + plants.for_each!( + |{ x, type }| { + sprite = Sprite.sub_or_crash(plant_sprite_sheet, { src_x: type * 12, src_y: 0, width: 12, height: 12 }) + set_sprite_colors!() + Sprite.blit!(sprite, { x, y: plant_y, flags: Sprite.Flags.default() }) + }, + ) # ===== Sounds ============================================ From e318f5d7b908bbd463d2a124b5a4110612cce20b Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 24 Jun 2026 20:59:06 -0400 Subject: [PATCH 16/19] Add Rocci Bird trace variant --- examples/rocci-bird-trace.roc | 785 ++++++++++++++++++++++++++++++++++ 1 file changed, 785 insertions(+) create mode 100644 examples/rocci-bird-trace.roc diff --git a/examples/rocci-bird-trace.roc b/examples/rocci-bird-trace.roc new file mode 100644 index 0000000..5869cf7 --- /dev/null +++ b/examples/rocci-bird-trace.roc @@ -0,0 +1,785 @@ +app [main] { + w4: platform "../platform/main.roc", +} + +import w4.W4 +import w4.Sprite + +Model : [ + TitleScreen(TitleScreenState), + Game(GameState), + GameOver(GameOverState), +] + +main = { init!, update! } + +init! : () => Model +init! = || { + # Lospec palette: Candy Cloud [2-BIT] Palette + palette = { + color1: 0xe6e6c0, + color2: 0xb494b7, + color3: 0x42436e, + color4: 0x26013f, + } + W4.set_palette!(palette) + W4.clear_frame_buffer_each_update!() + + frame_count = load_rand_from_disk!() + W4.seed_rand!(frame_count) + plants = starting_plants!() + + init_title_screen(frame_count, plants) +} + +update! : Model => Model +update! = |model| + match model { + TitleScreen(prev) => run_title_screen!(update_frame_count(prev)) + Game(prev) => run_game!(update_frame_count(prev)) + GameOver(prev) => run_game_over!(update_frame_count(prev)) + } + +update_frame_count = |prev| + { ..prev, frame_count: prev.frame_count + 1 } + +# ===== Title Screen ====================================== + +TitleScreenState : { + frame_count : U64, + plants : List(Plant), + rocci_idle_anim : Animation, +} + +init_title_screen : U64, List(Plant) -> Model +init_title_screen = |frame_count, plants| + TitleScreen({ + frame_count, + plants, + rocci_idle_anim: create_rocci_idle_anim(frame_count), + }) + +run_title_screen! : TitleScreenState => Model +run_title_screen! = |prev| { + state = { ..prev, rocci_idle_anim: update_animation(prev.frame_count, prev.rocci_idle_anim) } + + set_text_colors!() + W4.text!("Rocci Bird!!!", { x: 32, y: 12 }) + W4.text!("Click to start!", { x: 24, y: 72 }) + draw_ground!(0) + draw_plants!(state.plants) + + shift = idle_shift(state.frame_count, state.rocci_idle_anim) + draw_animation!(state.rocci_idle_anim, { x: player_x, y: player_start_y_pixel + shift, flags: Sprite.Flags.default() }) + gamepad = W4.get_gamepad!(Player1) + mouse = W4.get_mouse!() + + start = gamepad.button1 or gamepad.up or mouse.left + + if start { + init_game!(state) + } else { + TitleScreen(state) + } +} + +# ===== Main Game ========================================= + +GameState : { + frame_count : U64, + score : U8, + max_score : U8, + player : { + y : F32, + y_vel : F32, + }, + last_flap : Bool, + rocci_flap_anim : Animation, + pipes : List(Pipe), + last_pipe_generated : U64, + plants : List(Plant), + last_plant_generated : U64, + ground_x : I32, +} + +init_game! : TitleScreenState => Model +init_game! = |state| { + frame_count = state.frame_count + plants = state.plants + W4.trace!("init_game frame=${frame_count.to_str()}") + + # Seed the randomness with number of frames since the start of the game. + # This makes the game feel like it is truely randomly seeded cause players won't always start on the same frame. + save_rand_to_disk!(frame_count) + W4.seed_rand!(frame_count) + W4.tone!(flap_tone) + + Game({ + frame_count, + score: 0, + max_score: 0, + player: { + y: player_start_y, + y_vel: jump_speed, + }, + last_pipe_generated: frame_count, + pipes: [], + plants, + last_plant_generated: U64.minus_saturated(frame_count, 4), + last_flap: True, + rocci_flap_anim: create_rocci_flap_anim(frame_count), + ground_x: 0, + }) +} + +# Useful to throw in WolframAlpha to help calculate these: +# y = v^2 /(2a); y = -a/2*t^2 + vt; y = 20; t = 18; a > 0 +# y is max jump height in pixels. +# t is frames to reach max jump height (remember 60fps). +gravity : F32 +gravity = 0.12 + +jump_speed : F32 +jump_speed = -2.2 + +run_game! : GameState => Model +run_game! = |prev| { + gamepad = W4.get_gamepad!(Player1) + mouse = W4.get_mouse!() + + flap = gamepad.button1 or gamepad.up or mouse.left + + { y_vel, next_anim } = + if !prev.last_flap and flap and flap_allowed(prev.frame_count, prev.rocci_flap_anim) { + W4.tone!(flap_tone) + anim = prev.rocci_flap_anim + { + y_vel: jump_speed, + next_anim: { ..anim, index: 0, state: RunOnce }, + } + } else { + { + y_vel: prev.player.y_vel + gravity, + next_anim: update_animation(prev.frame_count, prev.rocci_flap_anim), + } + } + + pipe = maybe_generate_pipe!(prev.last_pipe_generated, prev.frame_count) + + last_pipe_generated = + if pipe.is_ok() { + prev.frame_count + } else { + prev.last_pipe_generated + } + + pipes = List.append_if_ok(update_pipes(prev.pipes), pipe) + plant = maybe_generate_plant!(prev.last_plant_generated, prev.frame_count) + + last_plant_generated = + if plant.is_ok() { + prev.frame_count + } else { + prev.last_plant_generated + } + + plants = List.append_if_ok(update_plants(prev.plants), plant) + gain_point = U64.to_u8_wrap(prev.pipes.count_if(|candidate_pipe| candidate_pipe.x == player_x - 2)) + y = prev.player.y + y_vel + score = U8.plus_saturated(prev.score, gain_point) + state = { + ..prev, + rocci_flap_anim: next_anim, + player: { y, y_vel }, + score, + max_score: U8.max(score, prev.max_score), + last_flap: flap, + last_pipe_generated, + pipes, + last_plant_generated, + plants, + ground_x: (prev.ground_x - 1) % W4.screen_width(), + } + + if gain_point > 0 { + W4.tone!(point_tone) + } + + draw_pipes!(state.pipes) + draw_ground!(state.ground_x) + draw_plants!(state.plants) + + y_pixel = I32.min(F32.to_i32_wrap(state.player.y), 134) + collided = player_collided!(y_pixel, state.rocci_flap_anim.index) + draw_animation!(state.rocci_flap_anim, { x: player_x, y: y_pixel, flags: Sprite.Flags.default() }) + draw_score!(state.score, { x: 68, y: 4 }) + + if !collided and y < 134 { + Game(state) + } else { + W4.tone!(death_tone) + + init_game_over!(state) + } +} + +# ===== Game Over ========================================= + +GameOverState : { + frame_count : U64, + score : U8, + high_score : U8, + new_high_score : Bool, + player : { + y : F32, + y_vel : F32, + }, + rocci_fall_anim : Animation, + high_score_anim : Animation, + pipes : List(Pipe), + plants : List(Plant), + ground_x : I32, +} + +init_game_over! : GameState => Model +init_game_over! = |prev| { + frame_count = prev.frame_count + max_score = prev.max_score + score = prev.score + player = prev.player + pipes = prev.pipes + plants = prev.plants + ground_x = prev.ground_x + + hs = load_high_score_from_disk!() + new_high_score = max_score > hs + high_score = + if new_high_score { + max_score + } else { + hs + } + save_high_score_to_disk!(high_score) + + GameOver({ + frame_count, + score, + high_score, + new_high_score, + player, + pipes, + plants, + rocci_fall_anim: create_rocci_fall_anim(frame_count), + high_score_anim: create_high_score_anim(frame_count), + ground_x, + }) +} + +run_game_over! : GameOverState => Model +run_game_over! = |prev| { + y_vel = prev.player.y_vel + gravity + rocci_fall_anim = update_animation(prev.frame_count, prev.rocci_fall_anim) + high_score_anim = update_animation(prev.frame_count, prev.high_score_anim) + + state = { + ..prev, + rocci_fall_anim, + high_score_anim, + player: { y: (prev.player.y + y_vel).min(134), y_vel }, + } + draw_pipes!(state.pipes) + draw_ground!(state.ground_x) + draw_plants!(state.plants) + + y_pixel = F32.to_i32_wrap(state.player.y) + draw_animation!(state.rocci_fall_anim, { x: player_x, y: y_pixel, flags: Sprite.Flags.default() }) + W4.set_shape_colors!({ border: Color4, fill: Color1 }) + W4.rect!({ x: 16, y: 52, width: 136, height: 32 }) + set_text_colors!() + W4.text!("Game Over!", { x: 44, y: 56 }) + W4.text!("Right to restart", { x: 20, y: 72 }) + W4.text!("Art by Luke DeVault", { x: 4, y: 151 }) + W4.set_shape_colors!({ border: Color4, fill: Color1 }) + W4.rect!({ x: 66, y: 2, width: 28, height: 12 }) + draw_score!(state.score, { x: 68, y: 4 }) + + if state.new_high_score { + draw_animation!(state.high_score_anim, { x: 64, y: 0, flags: Sprite.Flags.default() }) + } + + W4.set_shape_colors!({ border: Color4, fill: Color1 }) + W4.rect!({ x: 54, y: 18, width: 52, height: 12 }) + set_text_colors!() + W4.text!("HS:", { x: 57, y: 20 }) + draw_score!(state.high_score, { x: 80, y: 20 }) + + gamepad = W4.get_gamepad!(Player1) + mouse = W4.get_mouse!() + if mouse.right or gamepad.button2 or gamepad.right { + plants = starting_plants!() + init_title_screen(state.frame_count, plants) + } else { + GameOver(state) + } +} + +# ===== Player ============================================ + +player_start_y : F32 +player_start_y = 40 + +player_start_y_pixel : I32 +player_start_y_pixel = 40 + +player_x : I32 +player_x = 70 + +player_collided! : I32, U64 => Bool +player_collided! = |player_y, anim_index| + if player_y >= -1 { + on_screen_collided!(player_y, anim_index) + } else { + off_screen_collided!() + } + +on_screen_collided! : I32, U64 => Bool +on_screen_collided! = |player_y, anim_index| { + # This is written in a kinda silly but simple way. + # It checks to ensure a few points in the sprite are all background colored. + # This must be run before drawing the player. + base_points = [ + { x: 11, y: 2 }, + { x: 13, y: 3 }, + { x: 3, y: 5 }, + { x: 11, y: 6 }, + { x: 9, y: 8 }, + { x: 5, y: 9 }, + { x: 7, y: 10 }, + { x: 5, y: 12 }, + ].iter() + + collision_points = + if anim_index == 2 { + base_points.append({ x: 2, y: 1 }).append({ x: 7, y: 1 }) + } else if anim_index == 1 { + base_points.append({ x: 2, y: 2 }) + } else { + base_points + } + + for { x, y } in collision_points { + if W4.get_pixel!({ + x: I32.to_u8_wrap(player_x + x), + y: I32.to_u8_wrap(player_y + y), + }) != Color1 { + return True + } + } + + False +} + +off_screen_collided! : () => Bool +off_screen_collided! = || + W4.get_pixel!({ + x: I32.to_u8_wrap(player_x + 13), + y: I32.to_u8_wrap(0), + }) != Color1 + +# ===== Pipes ============================================= + +Pipe : { x : I32, gap_start : I32 } + +gap_height = 40 + +draw_pipes! = |pipes| + pipes.for_each!( + |{ x, gap_start }| { + set_sprite_colors!() + Sprite.blit!(pipe_sprite, { x, y: gap_start - W4.screen_height(), flags: Sprite.Flags.default().flip_y() }) + Sprite.blit!(pipe_sprite, { x, y: gap_start + gap_height, flags: Sprite.Flags.default() }) + }, + ) + +update_pipes : List(Pipe) -> List(Pipe) +update_pipes = |pipes| { + var $out = List.with_capacity(pipes.len()) + for pipe in pipes { + moved = { ..pipe, x: pipe.x - 1 } + if moved.x >= -20 { + $out = $out.append(moved) + } + } + $out +} + +maybe_generate_pipe! : U64, U64 => Try(Pipe, [NoPipe]) +maybe_generate_pipe! = |last_generated, frame_count| { + diff = frame_count - last_generated + should_generate = diff > 90 + diff_is_one = diff == 1 + diff_u8 = U64.to_u8_wrap(diff) + gen_text = + if should_generate { + "true" + } else { + "false" + } + one_text = + if diff_is_one { + "true" + } else { + "false" + } + W4.trace!("pipe last=${last_generated.to_str()} frame=${frame_count.to_str()} diff=${diff.to_str()} diff_u8=${diff_u8.to_str()} diff_eq_1=${one_text} gen=${gen_text}") + if should_generate { + gap_start = W4.rand_between!({ start: 0, before: 16 }) + Ok({ x: W4.screen_width(), gap_start: gap_start * 5 + 10 }) + } else { + Err(NoPipe) + } +} + +# ===== Plants ============================================ + +Plant : { x : I32, type : U32 } + +plant_types = 30 + +plant_y = W4.screen_height() - 22 + +random_plant! : I32 => Plant +random_plant! = |x| + { x, type: I32.to_u32_wrap(W4.rand!()) % plant_types } + +starting_plants! : () => List(Plant) +starting_plants! = || { + var $plants = List.with_capacity(15) + var $i = 0 + while $i <= 14 { + $plants = $plants.append(random_plant!($i * 12)) + $i = $i + 1 + } + $plants +} + +update_plants : List(Plant) -> List(Plant) +update_plants = |plants| { + var $out = List.with_capacity(plants.len()) + for plant in plants { + moved = { ..plant, x: plant.x - 1 } + if moved.x >= -12 { + $out = $out.append(moved) + } + } + $out +} + +maybe_generate_plant! : U64, U64 => Try(Plant, [NoPlant]) +maybe_generate_plant! = |last_generated, frame_count| + if frame_count - last_generated > 12 { + Ok(random_plant!(W4.screen_width())) + } else { + Err(NoPlant) + } + +draw_plants! : List(Plant) => {} +draw_plants! = |plants| + plants.for_each!( + |{ x, type }| { + sprite = Sprite.sub_or_crash(plant_sprite_sheet, { src_x: type * 12, src_y: 0, width: 12, height: 12 }) + set_sprite_colors!() + Sprite.blit!(sprite, { x, y: plant_y, flags: Sprite.Flags.default() }) + }, + ) + +# ===== Sounds ============================================ + +flap_tone = { + start_freq: 700, + end_freq: 870, + channel: Pulse1(Quarter), + pan: Center, + attack_time: 10, + sustain_time: 0, + decay_time: 5, + release_time: 3, + volume: 10, + peak_volume: 20, +} + +point_tone = { + start_freq: 995, + end_freq: 1000, + channel: Pulse2(Half), + pan: Center, + attack_time: 0, + sustain_time: 0, + decay_time: 10, + release_time: 10, + peak_volume: 75, + volume: 25, +} + +death_tone = { + start_freq: 170, + end_freq: 40, + channel: Noise, + pan: Center, + attack_time: 0, + sustain_time: 20, + decay_time: 40, + release_time: 0, + volume: 100, + peak_volume: 0, +} + +# ===== Drawing and Color ================================= + +draw_score! : U8, { x : I32, y : I32 } => {} +draw_score! = |score, { x: base_x, y }| { + set_text_colors!() + x = + if score < 10 { + base_x + 8 + } else if score < 100 { + base_x + 4 + } else { + base_x + } + W4.text!(U8.to_str(score), { x, y }) +} + +draw_ground! : I32 => {} +draw_ground! = |x| { + set_ground_colors!() + Sprite.blit!(ground_sprite, { x, y: W4.screen_height() - 13, flags: Sprite.Flags.default() }) + Sprite.blit!(ground_sprite, { x: x + W4.screen_width(), y: W4.screen_height() - 13, flags: Sprite.Flags.default() }) +} + +set_text_colors! : () => {} +set_text_colors! = || + W4.set_text_colors!({ fg: Color4, bg: None }) + +set_sprite_colors! : () => {} +set_sprite_colors! = || + W4.set_draw_colors!({ primary: None, secondary: Color2, tertiary: Color3, quaternary: Color4 }) + +set_ground_colors! : () => {} +set_ground_colors! = || + W4.set_draw_colors!({ primary: Color1, secondary: Color2, tertiary: Color3, quaternary: Color4 }) + +# ===== Saving and Loading ================================ + +# Due to limitations in randomness of wasm4 we would always get the same title screen. +# This save just a single byte of randomness from the frame_count in order to give us a bit more randomness. +save_rand_to_disk! : U64 => {} +save_rand_to_disk! = |frame_count| { + data = U64.to_u8_wrap(U64.bitwise_and(frame_count, 0xFF)) + high_score = load_high_score_from_disk!() + _ = W4.save_to_disk!([data, high_score]) + {} +} + +load_rand_from_disk! : () => U64 +load_rand_from_disk! = || { + data = W4.load_from_disk!() + match data { + [byte, ..] => U8.to_u64(byte) + _ => 0 + } +} + +save_high_score_to_disk! : U8 => {} +save_high_score_to_disk! = |high_score| { + rand = load_rand_from_disk!() + _ = W4.save_to_disk!([U64.to_u8_wrap(rand), high_score]) + {} +} + +load_high_score_from_disk! : () => U8 +load_high_score_from_disk! = || { + data = W4.load_from_disk!() + match data { + [_, hs, ..] => hs + _ => 0 + } +} + +# ===== Animations ======================================== + +AnimationState : [Completed, RunOnce, Loop] + +Animation : { + last_updated : U64, + index : U64, + cells : List({ frames : U64, sprite : Sprite }), + state : AnimationState, +} + +update_animation : U64, Animation -> Animation +update_animation = |frame_count, anim| { + frames_per_update = + match anim.cells.get(anim.index) { + Ok(cell) => cell.frames + Err(_) => { + crash "animation cell out of bounds at index: ${anim.index.to_str()}" + } + } + + if frame_count - anim.last_updated < frames_per_update { + anim + } else { + next_index = wrapped_inc(anim.index, anim.cells.len()) + match anim.state { + Completed => { ..anim, last_updated: frame_count } + Loop => { ..anim, index: next_index, last_updated: frame_count } + RunOnce => + if next_index == 0 { + { ..anim, state: Completed, last_updated: frame_count } + } else { + { ..anim, index: next_index, last_updated: frame_count } + } + } + } +} + +draw_animation! : Animation, { x : I32, y : I32, flags : Sprite.Flags } => {} +draw_animation! = |anim, { x, y, flags }| + match anim.cells.get(anim.index) { + Ok(cell) => { + set_sprite_colors!() + Sprite.blit!(cell.sprite, { x, y, flags }) + } + Err(_) => { + crash "animation cell out of bounds at index: ${U64.to_str(anim.index)}" + } + } + +wrapped_inc : U64, U64 -> U64 +wrapped_inc = |val, count| { + next = val + 1 + if next == count { + 0 + } else { + next + } +} + +idle_shift : U64, Animation -> I32 +idle_shift = |frame_count, anim| + if anim.index == 2 { + 0 + } else if anim.index == 1 and frame_count - anim.last_updated > 3 { + 0 + } else { + 1 + } + +create_rocci_idle_anim : U64 -> Animation +create_rocci_idle_anim = |frame_count| { + last_updated: frame_count, + index: 0, + state: Loop, + cells: [ + { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, + { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, + { frames: 17, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, + ], +} + +flap_allowed : U64, Animation -> Bool +flap_allowed = |frame_count, anim| + if anim.index == 2 { + True + } else if anim.index == 1 { + frame_count - anim.last_updated > 6 + } else { + False + } + +create_rocci_flap_anim : U64 -> Animation +create_rocci_flap_anim = |frame_count| { + last_updated: frame_count, + index: 2, + state: Completed, + cells: [ + { frames: 6, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 16, src_y: 0, width: 16, height: 16 }) }, + { frames: 12, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 32, src_y: 0, width: 16, height: 16 }) }, + { frames: 1, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 0, src_y: 0, width: 16, height: 16 }) }, + ], +} + +create_rocci_fall_anim : U64 -> Animation +create_rocci_fall_anim = |frame_count| { + last_updated: frame_count, + index: 0, + state: Loop, + cells: [ + { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 48, src_y: 0, width: 16, height: 16 }) }, + { frames: 10, sprite: Sprite.sub_or_crash(rocci_sprite_sheet, { src_x: 64, src_y: 0, width: 16, height: 16 }) }, + ], +} + +create_high_score_anim : U64 -> Animation +create_high_score_anim = |frame_count| { + last_updated: frame_count, + index: 0, + state: Loop, + cells: [ + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 0, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 32, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 64, src_y: 0, width: 32, height: 16 }) }, + { frames: 5, sprite: Sprite.sub_or_crash(high_score_sprite_sheet, { src_x: 96, src_y: 0, width: 32, height: 16 }) }, + ], +} + +# ===== Sprites =========================================== + +# Note: These could be stored in separate files and then imported +# as List(U8) values using `import "..."` but this way the entire +# game can be in one source code file. + +rocci_sprite_sheet : Sprite +rocci_sprite_sheet = + Sprite.new({ + data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x56, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x80, 0x40, 0x05, 0x56, 0x81, 0x80, 0x14, 0x00, 0x01, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x02, 0x85, 0x00, 0x00, 0x02, 0x81, 0x40, 0x01, 0x56, 0xa5, 0xa0, 0x15, 0x00, 0x05, 0xa0, 0x00, 0x00, 0x05, 0xa0, 0x00, 0x0a, 0x95, 0x00, 0x00, 0x0a, 0x85, 0x40, 0x00, 0x56, 0xa9, 0x00, 0x15, 0x56, 0xa5, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x06, 0x55, 0x00, 0x00, 0x06, 0x55, 0x40, 0x00, 0x06, 0xaa, 0x00, 0x15, 0x5a, 0xaa, 0x00, 0x15, 0x56, 0xaa, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x06, 0x95, 0x00, 0x00, 0x09, 0x55, 0x00, 0x15, 0x6a, 0xa9, 0x00, 0x05, 0x56, 0xa9, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x16, 0x95, 0x00, 0x00, 0x09, 0x54, 0x00, 0x05, 0x6a, 0x94, 0x00, 0x01, 0x56, 0xa4, 0x00, 0x00, 0x1a, 0x94, 0x00, 0x00, 0x16, 0x94, 0x00, 0x00, 0x09, 0x50, 0x00, 0x00, 0x69, 0x50, 0x00, 0x00, 0x56, 0x90, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x1a, 0xa0, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x29, 0x40, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x0a, 0xa0, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x0a, 0x80, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + bpp: BPP2, + width: 80, + height: 16, + }) + +ground_sprite : Sprite +ground_sprite = + Sprite.new({ + data: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x65, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x59, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x95, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45, 0x14, 0x51, 0x85, 0x14, 0x51, 0x49, 0x14, 0x91, 0x45, 0x18, 0x51, 0x54, 0x51, 0x49, 0x24, 0x51, 0x46, 0x14, 0x51, 0x46, 0x15, 0x14, 0x61, 0x45, 0x14, 0x92, 0x45, 0x14, 0x61, 0x46, 0x14, 0x55, 0x14, 0x51, 0x45, 0x14, 0x61, 0x51, 0x45, 0x65, 0x66, 0x56, 0x56, 0x59, 0x56, 0x59, 0x56, 0x55, 0x65, 0x65, 0x56, 0x59, 0x56, 0x59, 0x95, 0x59, 0x59, 0x55, 0x99, 0x59, 0x59, 0x55, 0x95, 0x96, 0x55, 0x99, 0x55, 0x95, 0x95, 0x59, 0x55, 0x96, 0x55, 0x59, 0x59, 0x95, 0x95, 0x96, 0x55, 0x95, 0x95, 0x99, 0x66, 0x65, 0x99, 0x65, 0x96, 0x95, 0x96, 0x59, 0x59, 0x65, 0x99, 0x65, 0xa5, 0x65, 0x96, 0x56, 0x56, 0x65, 0x99, 0x96, 0x59, 0x99, 0x66, 0x5a, 0x56, 0x59, 0x65, 0x96, 0x56, 0x59, 0x66, 0x65, 0x65, 0x66, 0x59, 0x99, 0x59, 0xa6, 0x9a, 0x5a, 0x69, 0x6a, 0x5a, 0x66, 0xa5, 0xa6, 0x9a, 0x9a, 0x6a, 0x6a, 0x5a, 0x65, 0x69, 0xa6, 0xa6, 0x9a, 0x69, 0x69, 0xa5, 0xa6, 0x9a, 0x5a, 0x96, 0x56, 0x9a, 0x6a, 0x6a, 0xa6, 0x9a, 0x9a, 0x96, 0xa9, 0xa6, 0x96, 0x9a, 0x5a, 0x69, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], + bpp: BPP2, + width: 160, + height: 13, + }) + +pipe_sprite : Sprite +pipe_sprite = + Sprite.new({ + data: [0x0a, 0xaa, 0xaa, 0xab, 0xf0, 0x25, 0x55, 0x55, 0x55, 0x5c, 0x26, 0x96, 0x6a, 0x9a, 0xac, 0x36, 0x96, 0x6a, 0x66, 0xac, 0x36, 0x96, 0x6a, 0x9a, 0xac, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0, 0x03, 0x65, 0x9a, 0x66, 0xc0, 0x03, 0x65, 0x9a, 0x9a, 0xc0], + bpp: BPP2, + width: 20, + height: 160, + }) + +plant_sprite_sheet : Sprite +plant_sprite_sheet = + Sprite.new({ + data: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x02, 0xaa, 0x00, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0xa0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x40, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x50, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x08, 0x00, 0x80, 0x08, 0x08, 0x80, 0x00, 0x2a, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x40, 0x11, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x05, 0x14, 0x00, 0x28, 0x00, 0x51, 0x45, 0x00, 0x20, 0x01, 0x60, 0x20, 0x81, 0x60, 0x00, 0x80, 0x80, 0x0a, 0xa0, 0x16, 0x00, 0xa8, 0x00, 0x82, 0x15, 0x96, 0x02, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x20, 0x02, 0xa0, 0x00, 0x00, 0x28, 0x00, 0x02, 0x20, 0x00, 0x02, 0x82, 0x00, 0x06, 0x11, 0x11, 0x02, 0xb2, 0xb0, 0x02, 0x80, 0x08, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x40, 0x15, 0x11, 0x15, 0x00, 0x00, 0x00, 0x24, 0x20, 0x80, 0x51, 0x41, 0x50, 0x00, 0x22, 0x00, 0x14, 0x41, 0x44, 0x20, 0x55, 0x60, 0x28, 0x59, 0xa0, 0x02, 0x01, 0x60, 0x20, 0x21, 0x56, 0x02, 0x02, 0x00, 0x83, 0x55, 0xd6, 0x02, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x10, 0x40, 0x02, 0xa8, 0x80, 0x09, 0x5c, 0x00, 0x08, 0x20, 0x08, 0x02, 0x00, 0x00, 0x0a, 0x0a, 0x28, 0x91, 0x44, 0x66, 0x09, 0x6d, 0x5c, 0x0a, 0x08, 0x20, 0x02, 0x0b, 0x00, 0x00, 0x20, 0x00, 0x02, 0x06, 0x00, 0x0a, 0x15, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x15, 0xa0, 0x15, 0x15, 0x08, 0x00, 0x00, 0x00, 0x04, 0x86, 0x60, 0x15, 0x80, 0x48, 0x20, 0x02, 0x08, 0x50, 0x52, 0x94, 0x21, 0x55, 0x60, 0x21, 0x95, 0x60, 0x02, 0x15, 0x60, 0x80, 0x5a, 0xa8, 0x08, 0x05, 0x80, 0x81, 0x69, 0x56, 0x0a, 0xea, 0x00, 0x00, 0x28, 0x00, 0x02, 0xab, 0x80, 0x04, 0x45, 0x10, 0x0a, 0xea, 0x80, 0x03, 0x9a, 0x00, 0x28, 0xa8, 0x20, 0x02, 0x00, 0x00, 0x28, 0x08, 0xa0, 0x45, 0x19, 0x18, 0x03, 0xef, 0xeb, 0x28, 0x28, 0xa8, 0x08, 0x20, 0xc0, 0x00, 0x88, 0x00, 0x02, 0x05, 0x80, 0x26, 0x16, 0x18, 0x02, 0x00, 0x00, 0x02, 0x8a, 0x80, 0x0a, 0x15, 0x28, 0x00, 0x00, 0x08, 0x05, 0x99, 0xb0, 0x26, 0x08, 0xa0, 0x88, 0x02, 0x22, 0xa1, 0x42, 0x05, 0x0a, 0xaa, 0x80, 0x0a, 0xaa, 0x80, 0x00, 0xaa, 0x80, 0x85, 0x5a, 0x70, 0x08, 0x55, 0x80, 0x95, 0x55, 0x56, 0x2b, 0xae, 0x80, 0x00, 0x88, 0x00, 0x0a, 0xee, 0xa0, 0x11, 0x11, 0x40, 0x0b, 0xab, 0xa0, 0x0b, 0xed, 0xe0, 0xba, 0xba, 0xa0, 0x02, 0xa8, 0x00, 0x2a, 0x2a, 0xb8, 0x1a, 0x6a, 0x78, 0x29, 0xbe, 0x68, 0xae, 0xaa, 0xb8, 0x31, 0x81, 0xc0, 0x00, 0x80, 0x00, 0x02, 0x55, 0x80, 0x87, 0x18, 0x18, 0x09, 0x82, 0x80, 0x00, 0xaa, 0x00, 0x28, 0x08, 0x0a, 0x08, 0x20, 0x20, 0x05, 0x65, 0xd0, 0x08, 0x02, 0x00, 0x08, 0xaa, 0x20, 0x28, 0x8a, 0x14, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x2a, 0xa2, 0x70, 0x02, 0xaa, 0x00, 0x2a, 0xaa, 0xa8, 0x2b, 0xba, 0xa0, 0x00, 0x87, 0x00, 0x2b, 0xae, 0xa8, 0x06, 0x66, 0x80, 0x0b, 0xae, 0xb8, 0x25, 0xb9, 0x5c, 0xba, 0xea, 0xe8, 0x02, 0xe0, 0x00, 0xae, 0xae, 0xea, 0x0b, 0xaa, 0xe0, 0x96, 0x97, 0x96, 0xba, 0xba, 0xea, 0x31, 0x85, 0x70, 0x00, 0xa0, 0x00, 0x02, 0x67, 0x00, 0x87, 0x1c, 0x57, 0x21, 0xc8, 0x70, 0x00, 0x2c, 0x00, 0x08, 0x0a, 0x08, 0x28, 0xa8, 0xa8, 0x01, 0x5f, 0x54, 0x20, 0x00, 0x80, 0x28, 0x2e, 0x28, 0x22, 0x82, 0x0a, 0x02, 0x17, 0x00, 0x02, 0x17, 0x00, 0x00, 0x27, 0x00, 0x09, 0xc2, 0x70, 0x00, 0x9c, 0x00, 0x08, 0x55, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xce, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x5b, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xac, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + bpp: BPP2, + width: 360, + height: 12, + }) + +high_score_sprite_sheet : Sprite +high_score_sprite_sheet = Sprite.new({ + data: [0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x09, 0x60, 0x25, 0x80, 0x96, 0x02, 0x58, 0x00, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x00, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf6, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x96, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x25, 0x80, 0x96, 0x02, 0x58, 0x09, 0x60, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x02, 0x58, 0x09, 0x60, 0x25, 0x80, 0x96, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x0a, 0x00, 0x28, 0x00, 0xa0, 0x02, 0x80, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00, 0x00, 0xa0, 0x02, 0x80, 0x0a, 0x00, 0x28, 0x00], + bpp: BPP2, + width: 128, + height: 16, +}) From 453656062f65ffc57e53b0bdf39950850680e922 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Fri, 10 Jul 2026 07:03:46 -0400 Subject: [PATCH 17/19] Declare WASM-4 cartridge exports --- platform/main.roc | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/main.roc b/platform/main.roc index b861f90..70844b4 100644 --- a/platform/main.roc +++ b/platform/main.roc @@ -40,6 +40,7 @@ platform "" wasm32: { inputs: ["host.wasm", app], output: Shared, + exports: ["start", "update"], import_memory: Zeroed, minimum_memory: 65536, maximum_memory: 65536, From 240e57747fcee81279b7243861dd69d2fed8e34a Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 14 Jul 2026 11:08:30 -0400 Subject: [PATCH 18/19] Pin CI to current roc nightly with new-compiler builtins --- .github/workflows/generate-docs.yaml | 1 + .github/workflows/release.yml | 6 ++++-- .github/workflows/tests.yaml | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate-docs.yaml b/.github/workflows/generate-docs.yaml index a02d404..a8017a4 100644 --- a/.github/workflows/generate-docs.yaml +++ b/.github/workflows/generate-docs.yaml @@ -24,6 +24,7 @@ jobs: uses: roc-lang/setup-roc@main with: version: nightly-new-compiler + nightly-tag: nightly-2026-July-14-c9147c2 - name: Build host object run: zig build - name: Generate docs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ce341d..7d8c6d2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,9 +37,10 @@ jobs: version: ${{ env.ZIG_VERSION }} - name: Install Roc - uses: roc-lang/setup-roc@v0.2.0 + uses: roc-lang/setup-roc@main with: version: ${{ env.ROC_VERSION }} + nightly-tag: nightly-2026-July-14-c9147c2 - name: Capture Roc version id: roc_version @@ -118,9 +119,10 @@ jobs: version: ${{ env.ZIG_VERSION }} - name: Install Roc - uses: roc-lang/setup-roc@v0.2.0 + uses: roc-lang/setup-roc@main with: version: ${{ env.ROC_VERSION }} + nightly-tag: nightly-2026-July-14-c9147c2 - name: Install Python uses: actions/setup-python@v5 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 51a546d..6a028df 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -25,6 +25,7 @@ jobs: uses: roc-lang/setup-roc@main with: version: nightly-new-compiler + nightly-tag: nightly-2026-July-14-c9147c2 - run: roc version From 5eb212c94ebb4a9846d21f9f5a6f9295286cf2bc Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 14 Jul 2026 11:22:39 -0400 Subject: [PATCH 19/19] Build remaining examples against the current platform --- examples/basic.roc | 2 +- examples/snake.roc | 4 ++-- examples/sound.roc | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/basic.roc b/examples/basic.roc index 5a7cc06..ba5acc2 100644 --- a/examples/basic.roc +++ b/examples/basic.roc @@ -1,4 +1,4 @@ -app [main] { w4: platform "https://github.com/lukewilliamboswell/roc-wasm4/releases/download/0.6/ADeKYHzDvyXSEZjj4wG3qRLTFRYiiEWLuVMPD5S8uBF3.tar.zst" } +app [main] { w4: platform "../platform/main.roc" } import w4.W4 diff --git a/examples/snake.roc b/examples/snake.roc index b9dec33..c1047a3 100644 --- a/examples/snake.roc +++ b/examples/snake.roc @@ -1,5 +1,5 @@ app [main] { - w4: platform "https://github.com/lukewilliamboswell/roc-wasm4/releases/download/0.6/ADeKYHzDvyXSEZjj4wG3qRLTFRYiiEWLuVMPD5S8uBF3.tar.zst", + w4: platform "../platform/main.roc", } import w4.W4 @@ -118,7 +118,7 @@ draw_game! = |model| { Sprite.blit!(model.fruit_sprite, { x: model.fruit.x * 8, y: model.fruit.y * 8, - flags: [], + flags: Sprite.Flags.default(), }) W4.set_shape_colors!({ border: blue, fill: green }) diff --git a/examples/sound.roc b/examples/sound.roc index cacb6c3..065a312 100644 --- a/examples/sound.roc +++ b/examples/sound.roc @@ -1,5 +1,5 @@ app [main] { - w4: platform "https://github.com/lukewilliamboswell/roc-wasm4/releases/download/0.6/ADeKYHzDvyXSEZjj4wG3qRLTFRYiiEWLuVMPD5S8uBF3.tar.zst", + w4: platform "../platform/main.roc", } import w4.W4 @@ -95,7 +95,7 @@ update! = |model| { Sprite.blit!(model.arrow_sprite, { x: x - 8 - 4, y: y + U64.to_i32_wrap(model.arrow_idx) * spacing, - flags: [], + flags: Sprite.Flags.default(), }) gamepad = W4.get_gamepad!(Player1)