* Create the Shopping Cart Lock app

Will demonstrate tomorrow. Don't merge until I do 😁

* Fixes for HTotoo's comments 😎

* Improved audio the best I can.

If nobody has any ideas to further improve high frequencies of the audio, the hardware may not be capable. I still need to check with line-out to better speaker to make sure it's not just the speaker, but it shouldn't be.

* Compared against baseband_api.cpp - matched some things better but still playback seems to be missing higher fq sounds

* renamed wav files to a more specific / less generic name

* indentation + using variables instead of litteral names for wav files to use

* indentation

* Made a Snake game - enjoy

* Code formatting. I always forget.

* move to keep sort order

* Update external.ld

Sorry I should have also asked if there was any reason that address ranges 0xADDA0000--0xADDD0000 were skipped in external.ld.  I assumed there wasn't so I changed it to be consecutive using the same 0x10000 step as the other modules.  If there is any reason to skip them then we should add a comment to note it.  Of course these are all just temporary address values used for linking and get overwritten by a kludgy "search & replace" during the build process.

Resolves enhancement request #764

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
Co-authored-by: Mark Thompson <129641948+NotherNgineer@users.noreply.github.com>
This commit is contained in:
RocketGod 2025-03-08 06:33:27 -08:00 committed by GitHub
parent 1df318355b
commit eb50b790c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 629 additions and 2 deletions

View File

@ -4,10 +4,14 @@ set(EXTCPPSRC
external/tetris/main.cpp
external/tetris/ui_tetris.cpp
#tetris
#breakout
external/breakout/main.cpp
external/breakout/ui_breakout.cpp
#snake
external/snake/main.cpp
external/snake/ui_snake.cpp
#afsk_rx
external/afsk_rx/main.cpp
external/afsk_rx/ui_afsk_rx.cpp
@ -192,6 +196,7 @@ set(EXTAPPLIST
keyfob
tetris
breakout
snake
extsensors
foxhunt_rx
audio_test

View File

@ -38,7 +38,6 @@ MEMORY
ram_external_app_keyfob(rwx) : org = 0xADBD0000, len = 32k
ram_external_app_tetris(rwx) : org = 0xADBE0000, len = 32k
ram_external_app_extsensors(rwx) : org = 0xADBF0000, len = 32k
ram_external_app_breakout(rwx) : org = 0xADE00000, len = 32k
ram_external_app_foxhunt_rx(rwx) : org = 0xADC00000, len = 32k
ram_external_app_audio_test(rwx) : org = 0xADC10000, len = 32k
ram_external_app_wardrivemap(rwx) : org = 0xADC20000, len = 32k
@ -65,6 +64,8 @@ MEMORY
ram_external_app_view_wav(rwx) : org = 0xADD70000, len = 32k
ram_external_app_sd_wipe(rwx) : org = 0xADD80000, len = 32k
ram_external_app_playlist_editor(rwx) : org = 0xADD90000, len = 32k
ram_external_app_breakout(rwx) : org = 0xADDA0000, len = 32k
ram_external_app_snake(rwx) : org = 0xADDB0000, len = 32k
}
SECTIONS
@ -159,6 +160,12 @@ SECTIONS
*(*ui*external_app*breakout*);
} > ram_external_app_breakout
.external_app_snake : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_snake.application_information));
*(*ui*external_app*snake*);
} > ram_external_app_snake
.external_app_extsensors : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_extsensors.application_information));

View File

@ -0,0 +1,29 @@
/*
* 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__*/

View File

@ -0,0 +1,68 @@
/*
* ------------------------------------------------------------
* | 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__*/

View File

@ -0,0 +1,67 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#include "ui.hpp"
#include "ui_snake.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::snake {
void initialize_app(ui::NavigationView& nav) {
nav.push<SnakeView>();
}
} // namespace ui::external_app::snake
extern "C" {
__attribute__((section(".external_app.app_snake.application_information"), used)) application_information_t _application_information_snake = {
(uint8_t*)0x00000000,
ui::external_app::snake::initialize_app,
CURRENT_HEADER_VERSION,
VERSION_MD5,
"Snake",
{
0x00,
0x00,
0x7E,
0x42,
0x42,
0x42,
0x7E,
0x00,
0x00,
0x7E,
0x42,
0x42,
0x42,
0x7E,
0x00,
0x00,
0x00,
0x7E,
0x42,
0x42,
0x42,
0x7E,
0x00,
0x00,
0x7E,
0x42,
0x42,
0x42,
0x7E,
0x00,
0x00,
},
ui::Color::green().v,
app_location_t::GAMES,
-1,
{0, 0, 0, 0},
0x00000000,
};
}

View File

@ -0,0 +1,98 @@
#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__*/

View File

@ -0,0 +1,234 @@
/*
* ------------------------------------------------------------
* | 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();
}
}
}

View File

@ -0,0 +1,71 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#include "ui_snake.hpp"
namespace ui::external_app::snake {
#include "snake.cpp"
SnakeView::SnakeView(NavigationView& nav)
: nav_{nav} {
add_children({&dummy});
game_timer.attach(&game_timer_check, 1.0 / 5.0);
}
void SnakeView::on_show() {
}
void SnakeView::paint(Painter& painter) {
(void)painter;
if (!initialized) {
initialized = true;
std::srand(LPC_RTC->CTIME0);
init_game();
}
}
void SnakeView::frame_sync() {
check_game_timer();
set_dirty();
}
bool SnakeView::on_key(const KeyEvent key) {
if (key == KeyEvent::Select) {
if (game_state == STATE_MENU || game_state == STATE_GAME_OVER) {
game_state = STATE_PLAYING;
init_game();
}
} else if (game_state == STATE_PLAYING) {
if (key == KeyEvent::Left) {
if (snake_dx == 0) {
snake_dx = -1;
snake_dy = 0;
}
} else if (key == KeyEvent::Right) {
if (snake_dx == 0) {
snake_dx = 1;
snake_dy = 0;
}
} else if (key == KeyEvent::Up) {
if (snake_dy == 0) {
snake_dx = 0;
snake_dy = -1;
}
} else if (key == KeyEvent::Down) {
if (snake_dy == 0) {
snake_dx = 0;
snake_dy = 1;
}
}
}
set_dirty();
return true;
}
} // namespace ui::external_app::snake

View File

@ -0,0 +1,48 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#ifndef __UI_SNAKE_H__
#define __UI_SNAKE_H__
#include "ui_navigation.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include "irq_controls.hpp"
#include "random.hpp"
#include "lpc43xx_cpp.hpp"
#include "limits.h"
#include "ui_widget.hpp"
namespace ui::external_app::snake {
class SnakeView : public View {
public:
SnakeView(NavigationView& nav);
void on_show() override;
std::string title() const override { return "Snake"; };
void focus() override { dummy.focus(); };
void paint(Painter& painter) override;
void frame_sync();
bool on_key(KeyEvent key) override;
private:
bool initialized = false;
NavigationView& nav_;
Button dummy{
{240, 0, 0, 0},
""};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->frame_sync();
}};
};
} // namespace ui::external_app::snake
#endif /*__UI_SNAKE_H__*/