mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-04-09 01:34:47 +00:00
Combine cpp, move helpers to hpp (#2584)
This commit is contained in:
parent
c5b7326d4b
commit
139ade0670
29
firmware/application/external/snake/Arial12x12.h
vendored
29
firmware/application/external/snake/Arial12x12.h
vendored
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2024 Mark Thompson
|
|
||||||
*
|
|
||||||
* This file is part of PortaPack.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// dummy include file to avoid changing original source
|
|
||||||
|
|
||||||
#ifndef __UI_Arial12x12_H__
|
|
||||||
#define __UI_Arial12x12_H__
|
|
||||||
|
|
||||||
#define Arial12x12 (0)
|
|
||||||
|
|
||||||
#endif /*__UI_Arial12x12_H__*/
|
|
@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* | Made by RocketGod |
|
|
||||||
* | Find me at https://betaskynet.com |
|
|
||||||
* | Argh matey! |
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __UI_SPI_TFT_ILI9341_H__
|
|
||||||
#define __UI_SPI_TFT_ILI9341_H__
|
|
||||||
|
|
||||||
ui::Painter painter;
|
|
||||||
|
|
||||||
static int bg_color;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
White,
|
|
||||||
Blue,
|
|
||||||
Yellow,
|
|
||||||
Purple,
|
|
||||||
Green,
|
|
||||||
Red,
|
|
||||||
Maroon,
|
|
||||||
Orange,
|
|
||||||
Black,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Color pp_colors[] = {
|
|
||||||
Color::white(),
|
|
||||||
Color::blue(),
|
|
||||||
Color::yellow(),
|
|
||||||
Color::purple(),
|
|
||||||
Color::green(),
|
|
||||||
Color::red(),
|
|
||||||
Color::magenta(),
|
|
||||||
Color::orange(),
|
|
||||||
Color::black(),
|
|
||||||
};
|
|
||||||
|
|
||||||
static void claim(__FILE* x) {
|
|
||||||
(void)x;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void cls() {
|
|
||||||
painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black());
|
|
||||||
};
|
|
||||||
|
|
||||||
static void background(int color) {
|
|
||||||
bg_color = color;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void set_orientation(int x) {
|
|
||||||
(void)x;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void set_font(unsigned char* x) {
|
|
||||||
(void)x;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void fillrect(int x1, int y1, int x2, int y2, int color) {
|
|
||||||
painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
|
|
||||||
};
|
|
||||||
|
|
||||||
static void rect(int x1, int y1, int x2, int y2, int color) {
|
|
||||||
painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /*__UI_SPI_TFT_ILI9341_H__*/
|
|
2
firmware/application/external/snake/main.cpp
vendored
2
firmware/application/external/snake/main.cpp
vendored
@ -65,4 +65,4 @@ __attribute__((section(".external_app.app_snake.application_information"), used)
|
|||||||
{0, 0, 0, 0},
|
{0, 0, 0, 0},
|
||||||
0x00000000,
|
0x00000000,
|
||||||
};
|
};
|
||||||
}
|
} // namespace ui::external_app::snake
|
98
firmware/application/external/snake/mbed.h
vendored
98
firmware/application/external/snake/mbed.h
vendored
@ -1,98 +0,0 @@
|
|||||||
#ifndef __UI_mbed_H__
|
|
||||||
#define __UI_mbed_H__
|
|
||||||
|
|
||||||
using Callback = void (*)(void);
|
|
||||||
|
|
||||||
#define wait_us(x) (void)0
|
|
||||||
#define wait(x) chThdSleepMilliseconds(x * 1000)
|
|
||||||
#define PullUp 1
|
|
||||||
|
|
||||||
#include "ui_navigation.hpp"
|
|
||||||
|
|
||||||
enum {
|
|
||||||
dp0,
|
|
||||||
dp1,
|
|
||||||
dp2,
|
|
||||||
dp3,
|
|
||||||
dp4,
|
|
||||||
dp5,
|
|
||||||
dp6,
|
|
||||||
dp7,
|
|
||||||
dp8,
|
|
||||||
dp9,
|
|
||||||
dp10,
|
|
||||||
dp11,
|
|
||||||
dp12,
|
|
||||||
dp13,
|
|
||||||
dp14,
|
|
||||||
dp15,
|
|
||||||
dp16,
|
|
||||||
dp17,
|
|
||||||
dp18,
|
|
||||||
dp19,
|
|
||||||
dp20,
|
|
||||||
dp21,
|
|
||||||
dp22,
|
|
||||||
dp23,
|
|
||||||
dp24,
|
|
||||||
dp25,
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
||||||
static bool but_RIGHT;
|
|
||||||
static bool but_LEFT;
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
static bool but_SELECT;
|
|
||||||
|
|
||||||
class Timer {
|
|
||||||
public:
|
|
||||||
Timer() { (void)0; };
|
|
||||||
void reset() { (void)0; };
|
|
||||||
void start() { (void)0; }
|
|
||||||
uint32_t read_ms() { return 1000; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
static Callback game_update_callback;
|
|
||||||
static uint32_t game_update_timeout;
|
|
||||||
static uint32_t game_update_counter;
|
|
||||||
|
|
||||||
static void check_game_timer() {
|
|
||||||
if (game_update_callback) {
|
|
||||||
if (++game_update_counter >= game_update_timeout) {
|
|
||||||
game_update_counter = 0;
|
|
||||||
game_update_callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Ticker {
|
|
||||||
public:
|
|
||||||
Ticker() { (void)0; };
|
|
||||||
void attach(Callback func, double delay_sec) {
|
|
||||||
game_update_callback = func;
|
|
||||||
game_update_timeout = delay_sec * 60;
|
|
||||||
}
|
|
||||||
void detach() {
|
|
||||||
game_update_callback = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
static Callback button_callback;
|
|
||||||
|
|
||||||
class InterruptIn {
|
|
||||||
public:
|
|
||||||
InterruptIn(int reg) {
|
|
||||||
(void)reg;
|
|
||||||
};
|
|
||||||
void fall(Callback func) { button_callback = func; };
|
|
||||||
void mode(int v) { (void)v; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /*__UI_mbed_H__*/
|
|
234
firmware/application/external/snake/snake.cpp
vendored
234
firmware/application/external/snake/snake.cpp
vendored
@ -1,234 +0,0 @@
|
|||||||
/*
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* | Made by RocketGod |
|
|
||||||
* | Find me at https://betaskynet.com |
|
|
||||||
* | Argh matey! |
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "mbed.h"
|
|
||||||
#include "SPI_TFT_ILI9341.h"
|
|
||||||
#include "Arial12x12.h"
|
|
||||||
#include "ui.hpp"
|
|
||||||
#include "random.hpp"
|
|
||||||
|
|
||||||
extern int game_state;
|
|
||||||
|
|
||||||
#define SCREEN_WIDTH 240
|
|
||||||
#define SCREEN_HEIGHT 320
|
|
||||||
#define SNAKE_SIZE 10
|
|
||||||
#define INFO_BAR_HEIGHT 25
|
|
||||||
#define GAME_AREA_TOP (INFO_BAR_HEIGHT + 1)
|
|
||||||
#define GAME_AREA_HEIGHT (SCREEN_HEIGHT - INFO_BAR_HEIGHT - 2)
|
|
||||||
#define GRID_WIDTH ((SCREEN_WIDTH - 2) / SNAKE_SIZE)
|
|
||||||
#define GRID_HEIGHT (GAME_AREA_HEIGHT / SNAKE_SIZE)
|
|
||||||
#define STATE_MENU 0
|
|
||||||
#define STATE_PLAYING 1
|
|
||||||
#define STATE_GAME_OVER 2
|
|
||||||
|
|
||||||
#define COLOR_BACKGROUND Black
|
|
||||||
#define COLOR_SNAKE Green
|
|
||||||
#define COLOR_FOOD Red
|
|
||||||
#define COLOR_BORDER White
|
|
||||||
|
|
||||||
Ticker game_timer;
|
|
||||||
|
|
||||||
int snake_x[GRID_WIDTH * GRID_HEIGHT];
|
|
||||||
int snake_y[GRID_WIDTH * GRID_HEIGHT];
|
|
||||||
int snake_length = 1;
|
|
||||||
int snake_dx = 1, snake_dy = 0;
|
|
||||||
int food_x, food_y;
|
|
||||||
int score = 0;
|
|
||||||
int game_state = STATE_MENU;
|
|
||||||
bool initialized = false;
|
|
||||||
|
|
||||||
extern ui::Painter painter;
|
|
||||||
|
|
||||||
void init_game();
|
|
||||||
void update_game();
|
|
||||||
void draw_screen();
|
|
||||||
void draw_snake();
|
|
||||||
void draw_full_snake();
|
|
||||||
void erase_tail(int x, int y);
|
|
||||||
void draw_food();
|
|
||||||
void erase_food();
|
|
||||||
void draw_score();
|
|
||||||
void draw_borders();
|
|
||||||
void spawn_food();
|
|
||||||
bool check_collision();
|
|
||||||
void show_menu();
|
|
||||||
void show_game_over();
|
|
||||||
|
|
||||||
void game_timer_check() {
|
|
||||||
if (game_state == STATE_PLAYING) {
|
|
||||||
update_game();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_game() {
|
|
||||||
claim(stdout);
|
|
||||||
set_orientation(2);
|
|
||||||
set_font((unsigned char*)Arial12x12);
|
|
||||||
snake_x[0] = GRID_WIDTH / 2;
|
|
||||||
snake_y[0] = GRID_HEIGHT / 2;
|
|
||||||
snake_length = 1;
|
|
||||||
snake_dx = 1;
|
|
||||||
snake_dy = 0;
|
|
||||||
score = 0;
|
|
||||||
spawn_food();
|
|
||||||
if (game_state == STATE_MENU) {
|
|
||||||
show_menu();
|
|
||||||
} else if (game_state == STATE_PLAYING) {
|
|
||||||
draw_screen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void spawn_food() {
|
|
||||||
bool valid;
|
|
||||||
do {
|
|
||||||
food_x = rand() % GRID_WIDTH;
|
|
||||||
food_y = rand() % GRID_HEIGHT;
|
|
||||||
valid = true;
|
|
||||||
for (int i = 0; i < snake_length; i++) {
|
|
||||||
if (snake_x[i] == food_x && snake_y[i] == food_y) {
|
|
||||||
valid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (!valid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_game() {
|
|
||||||
int new_x = snake_x[0] + snake_dx;
|
|
||||||
int new_y = snake_y[0] + snake_dy;
|
|
||||||
bool ate_food = (new_x == food_x && new_y == food_y);
|
|
||||||
|
|
||||||
int tail_x = snake_x[snake_length - 1];
|
|
||||||
int tail_y = snake_y[snake_length - 1];
|
|
||||||
|
|
||||||
for (int i = snake_length - 1; i > 0; i--) {
|
|
||||||
snake_x[i] = snake_x[i - 1];
|
|
||||||
snake_y[i] = snake_y[i - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
snake_x[0] = new_x;
|
|
||||||
snake_y[0] = new_y;
|
|
||||||
|
|
||||||
if (ate_food) {
|
|
||||||
snake_x[snake_length] = tail_x;
|
|
||||||
snake_y[snake_length] = tail_y;
|
|
||||||
snake_length++;
|
|
||||||
score += 10;
|
|
||||||
spawn_food();
|
|
||||||
draw_food();
|
|
||||||
} else {
|
|
||||||
erase_tail(tail_x, tail_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_snake();
|
|
||||||
draw_score();
|
|
||||||
|
|
||||||
if (check_collision()) {
|
|
||||||
draw_borders();
|
|
||||||
game_state = STATE_GAME_OVER;
|
|
||||||
show_game_over();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check_collision() {
|
|
||||||
if (snake_x[0] < 0 || snake_x[0] >= GRID_WIDTH || snake_y[0] < 0 || snake_y[0] >= GRID_HEIGHT) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (int i = 1; i < snake_length; i++) {
|
|
||||||
if (snake_x[0] == snake_x[i] && snake_y[0] == snake_y[i]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_screen() {
|
|
||||||
cls();
|
|
||||||
background(COLOR_BACKGROUND);
|
|
||||||
draw_borders();
|
|
||||||
draw_full_snake();
|
|
||||||
draw_food();
|
|
||||||
draw_score();
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_snake() {
|
|
||||||
fillrect(1 + snake_x[0] * SNAKE_SIZE, GAME_AREA_TOP + snake_y[0] * SNAKE_SIZE,
|
|
||||||
1 + snake_x[0] * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + snake_y[0] * SNAKE_SIZE + SNAKE_SIZE, COLOR_SNAKE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_full_snake() {
|
|
||||||
for (int i = 0; i < snake_length; i++) {
|
|
||||||
fillrect(1 + snake_x[i] * SNAKE_SIZE, GAME_AREA_TOP + snake_y[i] * SNAKE_SIZE,
|
|
||||||
1 + snake_x[i] * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + snake_y[i] * SNAKE_SIZE + SNAKE_SIZE, COLOR_SNAKE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void erase_tail(int x, int y) {
|
|
||||||
fillrect(1 + x * SNAKE_SIZE, GAME_AREA_TOP + y * SNAKE_SIZE,
|
|
||||||
1 + x * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + y * SNAKE_SIZE + SNAKE_SIZE, COLOR_BACKGROUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_food() {
|
|
||||||
fillrect(1 + food_x * SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE,
|
|
||||||
1 + food_x * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE + SNAKE_SIZE, COLOR_FOOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void erase_food() {
|
|
||||||
fillrect(1 + food_x * SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE,
|
|
||||||
1 + food_x * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE + SNAKE_SIZE, COLOR_BACKGROUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_score() {
|
|
||||||
auto style = *ui::Theme::getInstance()->fg_blue;
|
|
||||||
painter.draw_string({5, 5}, style, "Score: " + std::to_string(score));
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_borders() {
|
|
||||||
rect(0, GAME_AREA_TOP - 1, SCREEN_WIDTH, GAME_AREA_TOP, COLOR_BORDER);
|
|
||||||
rect(0, GAME_AREA_TOP, SCREEN_WIDTH, SCREEN_HEIGHT, COLOR_BORDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_menu() {
|
|
||||||
cls();
|
|
||||||
background(COLOR_BACKGROUND);
|
|
||||||
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
|
|
||||||
auto style_green = *ui::Theme::getInstance()->fg_green;
|
|
||||||
auto style_blue = *ui::Theme::getInstance()->fg_blue;
|
|
||||||
painter.draw_string({50, 40}, style_yellow, "* * * SNAKE * * *");
|
|
||||||
painter.draw_string({0, 120}, style_blue, "USE THE D-PAD TO MOVE");
|
|
||||||
painter.draw_string({0, 150}, style_blue, "EAT THE RED SQUARES TO GROW");
|
|
||||||
painter.draw_string({0, 180}, style_blue, "DON'T HIT THE WALLS OR SELF");
|
|
||||||
painter.draw_string({15, 240}, style_green, "** PRESS SELECT TO START **");
|
|
||||||
}
|
|
||||||
|
|
||||||
void show_game_over() {
|
|
||||||
cls();
|
|
||||||
background(COLOR_BACKGROUND);
|
|
||||||
auto style_red = *ui::Theme::getInstance()->fg_red;
|
|
||||||
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
|
|
||||||
auto style_green = *ui::Theme::getInstance()->fg_green;
|
|
||||||
painter.draw_string({75, 90}, style_red, "GAME OVER");
|
|
||||||
painter.draw_string({74, 150}, style_yellow, "SCORE: " + std::to_string(score));
|
|
||||||
painter.draw_string({20, 220}, style_green, "PRESS SELECT TO RESTART");
|
|
||||||
wait(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
if (!initialized) {
|
|
||||||
initialized = true;
|
|
||||||
game_timer.attach(&game_timer_check, 1.0 / 5.0);
|
|
||||||
init_game();
|
|
||||||
}
|
|
||||||
while (1) {
|
|
||||||
if (but_SELECT && (game_state == STATE_MENU || game_state == STATE_GAME_OVER)) {
|
|
||||||
game_state = STATE_PLAYING;
|
|
||||||
init_game();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
225
firmware/application/external/snake/ui_snake.cpp
vendored
225
firmware/application/external/snake/ui_snake.cpp
vendored
@ -10,7 +10,230 @@
|
|||||||
|
|
||||||
namespace ui::external_app::snake {
|
namespace ui::external_app::snake {
|
||||||
|
|
||||||
#include "snake.cpp"
|
Ticker game_timer;
|
||||||
|
|
||||||
|
int snake_x[GRID_WIDTH * GRID_HEIGHT];
|
||||||
|
int snake_y[GRID_WIDTH * GRID_HEIGHT];
|
||||||
|
int snake_length = 1;
|
||||||
|
int snake_dx = 1;
|
||||||
|
int snake_dy = 0;
|
||||||
|
int food_x, food_y;
|
||||||
|
int score = 0;
|
||||||
|
int game_state = STATE_MENU;
|
||||||
|
bool initialized = false;
|
||||||
|
|
||||||
|
const Color pp_colors[] = {
|
||||||
|
Color::white(),
|
||||||
|
Color::blue(),
|
||||||
|
Color::yellow(),
|
||||||
|
Color::purple(),
|
||||||
|
Color::green(),
|
||||||
|
Color::red(),
|
||||||
|
Color::magenta(),
|
||||||
|
Color::orange(),
|
||||||
|
Color::black(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Painter painter;
|
||||||
|
|
||||||
|
bool but_RIGHT = false;
|
||||||
|
bool but_LEFT = false;
|
||||||
|
bool but_SELECT = false;
|
||||||
|
|
||||||
|
static Callback game_update_callback = nullptr;
|
||||||
|
static uint32_t game_update_timeout = 0;
|
||||||
|
static uint32_t game_update_counter = 0;
|
||||||
|
|
||||||
|
void cls() {
|
||||||
|
painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black());
|
||||||
|
}
|
||||||
|
|
||||||
|
void background(int color) {
|
||||||
|
(void)color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillrect(int x1, int y1, int x2, int y2, int color) {
|
||||||
|
painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rect(int x1, int y1, int x2, int y2, int color) {
|
||||||
|
painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_game_timer() {
|
||||||
|
if (game_update_callback) {
|
||||||
|
if (++game_update_counter >= game_update_timeout) {
|
||||||
|
game_update_counter = 0;
|
||||||
|
game_update_callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ticker::attach(Callback func, double delay_sec) {
|
||||||
|
game_update_callback = func;
|
||||||
|
game_update_timeout = delay_sec * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ticker::detach() {
|
||||||
|
game_update_callback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_timer_check() {
|
||||||
|
if (game_state == STATE_PLAYING) {
|
||||||
|
update_game();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_game() {
|
||||||
|
snake_x[0] = GRID_WIDTH / 2;
|
||||||
|
snake_y[0] = GRID_HEIGHT / 2;
|
||||||
|
snake_length = 1;
|
||||||
|
snake_dx = 1;
|
||||||
|
snake_dy = 0;
|
||||||
|
score = 0;
|
||||||
|
spawn_food();
|
||||||
|
if (game_state == STATE_MENU) {
|
||||||
|
show_menu();
|
||||||
|
} else if (game_state == STATE_PLAYING) {
|
||||||
|
draw_screen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void spawn_food() {
|
||||||
|
bool valid;
|
||||||
|
do {
|
||||||
|
food_x = rand() % GRID_WIDTH;
|
||||||
|
food_y = rand() % GRID_HEIGHT;
|
||||||
|
valid = true;
|
||||||
|
for (int i = 0; i < snake_length; i++) {
|
||||||
|
if (snake_x[i] == food_x && snake_y[i] == food_y) {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_game() {
|
||||||
|
int new_x = snake_x[0] + snake_dx;
|
||||||
|
int new_y = snake_y[0] + snake_dy;
|
||||||
|
bool ate_food = (new_x == food_x && new_y == food_y);
|
||||||
|
|
||||||
|
int tail_x = snake_x[snake_length - 1];
|
||||||
|
int tail_y = snake_y[snake_length - 1];
|
||||||
|
|
||||||
|
for (int i = snake_length - 1; i > 0; i--) {
|
||||||
|
snake_x[i] = snake_x[i - 1];
|
||||||
|
snake_y[i] = snake_y[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
snake_x[0] = new_x;
|
||||||
|
snake_y[0] = new_y;
|
||||||
|
|
||||||
|
if (ate_food) {
|
||||||
|
snake_x[snake_length] = tail_x;
|
||||||
|
snake_y[snake_length] = tail_y;
|
||||||
|
snake_length++;
|
||||||
|
score += 10;
|
||||||
|
spawn_food();
|
||||||
|
draw_food();
|
||||||
|
} else {
|
||||||
|
erase_tail(tail_x, tail_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_snake();
|
||||||
|
draw_score();
|
||||||
|
|
||||||
|
if (check_collision()) {
|
||||||
|
draw_borders();
|
||||||
|
game_state = STATE_GAME_OVER;
|
||||||
|
show_game_over();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_collision() {
|
||||||
|
if (snake_x[0] < 0 || snake_x[0] >= GRID_WIDTH || snake_y[0] < 0 || snake_y[0] >= GRID_HEIGHT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < snake_length; i++) {
|
||||||
|
if (snake_x[0] == snake_x[i] && snake_y[0] == snake_y[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_screen() {
|
||||||
|
cls();
|
||||||
|
background(COLOR_BACKGROUND);
|
||||||
|
draw_borders();
|
||||||
|
draw_full_snake();
|
||||||
|
draw_food();
|
||||||
|
draw_score();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_snake() {
|
||||||
|
fillrect(1 + snake_x[0] * SNAKE_SIZE, GAME_AREA_TOP + snake_y[0] * SNAKE_SIZE,
|
||||||
|
1 + snake_x[0] * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + snake_y[0] * SNAKE_SIZE + SNAKE_SIZE, COLOR_SNAKE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_full_snake() {
|
||||||
|
for (int i = 0; i < snake_length; i++) {
|
||||||
|
fillrect(1 + snake_x[i] * SNAKE_SIZE, GAME_AREA_TOP + snake_y[i] * SNAKE_SIZE,
|
||||||
|
1 + snake_x[i] * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + snake_y[i] * SNAKE_SIZE + SNAKE_SIZE, COLOR_SNAKE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase_tail(int x, int y) {
|
||||||
|
fillrect(1 + x * SNAKE_SIZE, GAME_AREA_TOP + y * SNAKE_SIZE,
|
||||||
|
1 + x * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + y * SNAKE_SIZE + SNAKE_SIZE, COLOR_BACKGROUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_food() {
|
||||||
|
fillrect(1 + food_x * SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE,
|
||||||
|
1 + food_x * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE + SNAKE_SIZE, COLOR_FOOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase_food() {
|
||||||
|
fillrect(1 + food_x * SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE,
|
||||||
|
1 + food_x * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE + SNAKE_SIZE, COLOR_BACKGROUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_score() {
|
||||||
|
auto style = *ui::Theme::getInstance()->fg_blue;
|
||||||
|
painter.draw_string({5, 5}, style, "Score: " + std::to_string(score));
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_borders() {
|
||||||
|
rect(0, GAME_AREA_TOP - 1, SCREEN_WIDTH, GAME_AREA_TOP, COLOR_BORDER);
|
||||||
|
rect(0, GAME_AREA_TOP, SCREEN_WIDTH, SCREEN_HEIGHT, COLOR_BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_menu() {
|
||||||
|
cls();
|
||||||
|
background(COLOR_BACKGROUND);
|
||||||
|
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
|
||||||
|
auto style_green = *ui::Theme::getInstance()->fg_green;
|
||||||
|
auto style_blue = *ui::Theme::getInstance()->fg_blue;
|
||||||
|
painter.draw_string({50, 40}, style_yellow, "* * * SNAKE * * *");
|
||||||
|
painter.draw_string({0, 120}, style_blue, "USE THE D-PAD TO MOVE");
|
||||||
|
painter.draw_string({0, 150}, style_blue, "EAT THE RED SQUARES TO GROW");
|
||||||
|
painter.draw_string({0, 180}, style_blue, "DON'T HIT THE WALLS OR SELF");
|
||||||
|
painter.draw_string({15, 240}, style_green, "** PRESS SELECT TO START **");
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_game_over() {
|
||||||
|
cls();
|
||||||
|
background(COLOR_BACKGROUND);
|
||||||
|
auto style_red = *ui::Theme::getInstance()->fg_red;
|
||||||
|
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
|
||||||
|
auto style_green = *ui::Theme::getInstance()->fg_green;
|
||||||
|
painter.draw_string({75, 90}, style_red, "GAME OVER");
|
||||||
|
painter.draw_string({74, 150}, style_yellow, "SCORE: " + std::to_string(score));
|
||||||
|
painter.draw_string({20, 220}, style_green, "PRESS SELECT TO RESTART");
|
||||||
|
wait(1);
|
||||||
|
}
|
||||||
|
|
||||||
SnakeView::SnakeView(NavigationView& nav)
|
SnakeView::SnakeView(NavigationView& nav)
|
||||||
: nav_{nav} {
|
: nav_{nav} {
|
||||||
|
92
firmware/application/external/snake/ui_snake.hpp
vendored
92
firmware/application/external/snake/ui_snake.hpp
vendored
@ -9,23 +9,105 @@
|
|||||||
#ifndef __UI_SNAKE_H__
|
#ifndef __UI_SNAKE_H__
|
||||||
#define __UI_SNAKE_H__
|
#define __UI_SNAKE_H__
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
#include "ui_navigation.hpp"
|
#include "ui_navigation.hpp"
|
||||||
#include "event_m0.hpp"
|
#include "event_m0.hpp"
|
||||||
#include "message.hpp"
|
#include "message.hpp"
|
||||||
#include "irq_controls.hpp"
|
#include "irq_controls.hpp"
|
||||||
#include "random.hpp"
|
#include "random.hpp"
|
||||||
#include "lpc43xx_cpp.hpp"
|
#include "lpc43xx_cpp.hpp"
|
||||||
#include "limits.h"
|
|
||||||
#include "ui_widget.hpp"
|
#include "ui_widget.hpp"
|
||||||
|
|
||||||
namespace ui::external_app::snake {
|
namespace ui::external_app::snake {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
White,
|
||||||
|
Blue,
|
||||||
|
Yellow,
|
||||||
|
Purple,
|
||||||
|
Green,
|
||||||
|
Red,
|
||||||
|
Maroon,
|
||||||
|
Orange,
|
||||||
|
Black,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const Color pp_colors[];
|
||||||
|
extern Painter painter;
|
||||||
|
extern bool but_RIGHT;
|
||||||
|
extern bool but_LEFT;
|
||||||
|
extern bool but_SELECT;
|
||||||
|
|
||||||
|
void cls();
|
||||||
|
void background(int color);
|
||||||
|
void fillrect(int x1, int y1, int x2, int y2, int color);
|
||||||
|
void rect(int x1, int y1, int x2, int y2, int color);
|
||||||
|
|
||||||
|
#define wait(x) chThdSleepMilliseconds(x * 1000)
|
||||||
|
|
||||||
|
using Callback = void (*)(void);
|
||||||
|
|
||||||
|
class Ticker {
|
||||||
|
public:
|
||||||
|
Ticker() = default;
|
||||||
|
void attach(Callback func, double delay_sec);
|
||||||
|
void detach();
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 240
|
||||||
|
#define SCREEN_HEIGHT 320
|
||||||
|
#define SNAKE_SIZE 10
|
||||||
|
#define INFO_BAR_HEIGHT 25
|
||||||
|
#define GAME_AREA_TOP (INFO_BAR_HEIGHT + 1)
|
||||||
|
#define GAME_AREA_HEIGHT (SCREEN_HEIGHT - INFO_BAR_HEIGHT - 2)
|
||||||
|
#define GRID_WIDTH ((SCREEN_WIDTH - 2) / SNAKE_SIZE)
|
||||||
|
#define GRID_HEIGHT (GAME_AREA_HEIGHT / SNAKE_SIZE)
|
||||||
|
#define STATE_MENU 0
|
||||||
|
#define STATE_PLAYING 1
|
||||||
|
#define STATE_GAME_OVER 2
|
||||||
|
|
||||||
|
#define COLOR_BACKGROUND Black
|
||||||
|
#define COLOR_SNAKE Green
|
||||||
|
#define COLOR_FOOD Red
|
||||||
|
#define COLOR_BORDER White
|
||||||
|
|
||||||
|
extern Ticker game_timer;
|
||||||
|
|
||||||
|
extern int snake_x[GRID_WIDTH * GRID_HEIGHT];
|
||||||
|
extern int snake_y[GRID_WIDTH * GRID_HEIGHT];
|
||||||
|
extern int snake_length;
|
||||||
|
extern int snake_dx;
|
||||||
|
extern int snake_dy;
|
||||||
|
extern int food_x;
|
||||||
|
extern int food_y;
|
||||||
|
extern int score;
|
||||||
|
extern int game_state;
|
||||||
|
extern bool initialized;
|
||||||
|
|
||||||
|
void game_timer_check();
|
||||||
|
void init_game();
|
||||||
|
void update_game();
|
||||||
|
void draw_screen();
|
||||||
|
void draw_snake();
|
||||||
|
void draw_full_snake();
|
||||||
|
void erase_tail(int x, int y);
|
||||||
|
void draw_food();
|
||||||
|
void erase_food();
|
||||||
|
void draw_score();
|
||||||
|
void draw_borders();
|
||||||
|
void spawn_food();
|
||||||
|
bool check_collision();
|
||||||
|
void show_menu();
|
||||||
|
void show_game_over();
|
||||||
|
|
||||||
class SnakeView : public View {
|
class SnakeView : public View {
|
||||||
public:
|
public:
|
||||||
SnakeView(NavigationView& nav);
|
SnakeView(NavigationView& nav);
|
||||||
void on_show() override;
|
void on_show() override;
|
||||||
std::string title() const override { return "Snake"; };
|
|
||||||
void focus() override { dummy.focus(); };
|
std::string title() const override { return "Snake"; }
|
||||||
|
|
||||||
|
void focus() override { dummy.focus(); }
|
||||||
void paint(Painter& painter) override;
|
void paint(Painter& painter) override;
|
||||||
void frame_sync();
|
void frame_sync();
|
||||||
bool on_key(KeyEvent key) override;
|
bool on_key(KeyEvent key) override;
|
||||||
@ -33,9 +115,11 @@ class SnakeView : public View {
|
|||||||
private:
|
private:
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
NavigationView& nav_;
|
NavigationView& nav_;
|
||||||
|
|
||||||
Button dummy{
|
Button dummy{
|
||||||
{240, 0, 0, 0},
|
{240, 0, 0, 0},
|
||||||
""};
|
""};
|
||||||
|
|
||||||
MessageHandlerRegistration message_handler_frame_sync{
|
MessageHandlerRegistration message_handler_frame_sync{
|
||||||
Message::ID::DisplayFrameSync,
|
Message::ID::DisplayFrameSync,
|
||||||
[this](const Message* const) {
|
[this](const Message* const) {
|
||||||
@ -45,4 +129,4 @@ class SnakeView : public View {
|
|||||||
|
|
||||||
} // namespace ui::external_app::snake
|
} // namespace ui::external_app::snake
|
||||||
|
|
||||||
#endif /*__UI_SNAKE_H__*/
|
#endif /* __UI_SNAKE_H__ */
|
Loading…
x
Reference in New Issue
Block a user