202 lines
4.8 KiB
Rust
202 lines
4.8 KiB
Rust
#[cfg(feature = "buddy-alloc")]
|
|
mod alloc;
|
|
mod palette;
|
|
mod snake;
|
|
mod sprites;
|
|
mod wasm4;
|
|
|
|
use crate::snake::{Point, Snake};
|
|
use fastrand::Rng;
|
|
use lazy_static::lazy_static;
|
|
use std::sync::Mutex;
|
|
|
|
lazy_static! {
|
|
static ref SNAKE_GAME: Mutex<Game> = Mutex::new(Game::new());
|
|
}
|
|
|
|
enum State {
|
|
Title,
|
|
Playing,
|
|
Dead,
|
|
}
|
|
|
|
pub struct Game {
|
|
state: State,
|
|
rng: Rng,
|
|
snake: Snake,
|
|
frame_count: u32,
|
|
prev_gamepad: u8,
|
|
fruit: Point,
|
|
}
|
|
|
|
impl Game {
|
|
pub fn new() -> Self {
|
|
let rng = Rng::with_seed(235);
|
|
Self {
|
|
state: State::Title,
|
|
snake: Snake::new(),
|
|
frame_count: 0,
|
|
prev_gamepad: 0,
|
|
fruit: Point {
|
|
x: rng.i32(0..20),
|
|
y: rng.i32(0..20),
|
|
},
|
|
rng,
|
|
}
|
|
}
|
|
|
|
pub fn reset(&mut self) {
|
|
self.snake = Snake::new();
|
|
self.state = State::Playing;
|
|
}
|
|
|
|
pub fn game_input(&mut self) {
|
|
let gamepad = unsafe { *wasm4::GAMEPAD1 };
|
|
let just_pressed = gamepad & (gamepad ^ self.prev_gamepad);
|
|
|
|
if just_pressed & wasm4::BUTTON_UP != 0 {
|
|
self.snake.up();
|
|
}
|
|
|
|
if just_pressed & wasm4::BUTTON_DOWN != 0 {
|
|
self.snake.down();
|
|
}
|
|
|
|
if just_pressed & wasm4::BUTTON_LEFT != 0 {
|
|
self.snake.left();
|
|
}
|
|
|
|
if just_pressed & wasm4::BUTTON_RIGHT != 0 {
|
|
self.snake.right();
|
|
}
|
|
|
|
self.prev_gamepad = gamepad;
|
|
}
|
|
|
|
pub fn update(&mut self) {
|
|
self.frame_count += 1;
|
|
|
|
match self.state {
|
|
State::Title => self.show_title(),
|
|
State::Playing => self.step_game(),
|
|
State::Dead => self.show_score(),
|
|
}
|
|
}
|
|
|
|
pub fn show_title(&mut self) {
|
|
wasm4::text("akesi", 56, 8);
|
|
wasm4::text("Press x", 48, 128);
|
|
wasm4::text("From Within", 32, 144);
|
|
|
|
palette::set_draw_color(0x30);
|
|
|
|
wasm4::blit(
|
|
&sprites::AKESI,
|
|
48,
|
|
24,
|
|
sprites::AKESI_WIDTH,
|
|
sprites::AKESI_HEIGHT,
|
|
wasm4::BLIT_1BPP,
|
|
);
|
|
|
|
let gamepad = unsafe { *wasm4::GAMEPAD1 };
|
|
let just_pressed = gamepad & (gamepad ^ self.prev_gamepad);
|
|
|
|
if just_pressed & wasm4::BUTTON_1 != 0 {
|
|
wasm4::tone(0 | (1000 << 16), 4 | (20 << 8), 80, wasm4::TONE_TRIANGLE);
|
|
self.reset();
|
|
}
|
|
|
|
self.prev_gamepad = gamepad;
|
|
}
|
|
|
|
pub fn show_score(&mut self) {
|
|
wasm4::text("Score", 48, 48);
|
|
wasm4::text("Press x to\nplay again", 36, 128);
|
|
|
|
let mut x = 16;
|
|
let mut y = 64;
|
|
for _ in 0..self.snake.body.len() - 3 {
|
|
palette::set_draw_color(0x4320);
|
|
wasm4::blit(&sprites::FRUIT, x, y, 8, 8, wasm4::BLIT_2BPP);
|
|
x += 8;
|
|
if x > 140 {
|
|
x = 16;
|
|
y += 8;
|
|
}
|
|
}
|
|
|
|
let gamepad = unsafe { *wasm4::GAMEPAD1 };
|
|
let just_pressed = gamepad & (gamepad ^ self.prev_gamepad);
|
|
|
|
if just_pressed & wasm4::BUTTON_1 != 0 {
|
|
wasm4::tone(0 | (1000 << 16), 4 | (20 << 8), 80, wasm4::TONE_TRIANGLE);
|
|
self.reset();
|
|
}
|
|
self.prev_gamepad = gamepad;
|
|
}
|
|
|
|
pub fn step_game(&mut self) {
|
|
self.game_input();
|
|
|
|
if self.snake.is_dead() {
|
|
wasm4::tone(
|
|
460 | (140 << 16),
|
|
94 | (48 << 8) | (62 << 16),
|
|
80,
|
|
wasm4::TONE_TRIANGLE,
|
|
);
|
|
self.state = State::Dead;
|
|
}
|
|
|
|
let speed = match self.snake.body.len() {
|
|
3..=8 => 18,
|
|
9..=15 => 14,
|
|
16..=24 => 12,
|
|
25..=32 => 10,
|
|
_ => 5,
|
|
};
|
|
|
|
if self.frame_count % speed == 0 {
|
|
let dropped_pos = self.snake.update();
|
|
|
|
if self.snake.body[0] == self.fruit {
|
|
if let Some(last_pos) = dropped_pos {
|
|
self.snake.body.push(last_pos);
|
|
}
|
|
self.fruit.x = self.rng.i32(0..20);
|
|
self.fruit.y = self.rng.i32(0..20);
|
|
|
|
while self.snake.inside(&self.fruit) {
|
|
self.fruit.x = self.rng.i32(0..20);
|
|
self.fruit.y = self.rng.i32(0..20);
|
|
}
|
|
|
|
wasm4::tone(140 | (250 << 16), 12 | (6 << 8), 80, wasm4::TONE_TRIANGLE);
|
|
}
|
|
}
|
|
self.snake.draw();
|
|
|
|
palette::set_draw_color(0x4320);
|
|
wasm4::blit(
|
|
&sprites::FRUIT,
|
|
self.fruit.x * 8,
|
|
self.fruit.y * 8,
|
|
8,
|
|
8,
|
|
wasm4::BLIT_2BPP,
|
|
);
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn start() {
|
|
palette::moonlight();
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn update() {
|
|
palette::set_draw_color(0x2);
|
|
SNAKE_GAME.lock().expect("game_state").update();
|
|
}
|