mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-06-10 02:38:35 +00:00
Breakout - The Portapack remake game rises from the pirate's lair (#2541)
* Breakout - The Portapack remake game rises from the pirate's lair * Fixes * Added a signature
This commit is contained in:
parent
52c3760e90
commit
18bebbfb6d
29
firmware/application/external/breakout/Arial12x12.h
vendored
Normal file
29
firmware/application/external/breakout/Arial12x12.h
vendored
Normal 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__*/
|
68
firmware/application/external/breakout/SPI_TFT_ILI9341.h
vendored
Normal file
68
firmware/application/external/breakout/SPI_TFT_ILI9341.h
vendored
Normal 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__*/
|
429
firmware/application/external/breakout/breakout.cpp
vendored
Normal file
429
firmware/application/external/breakout/breakout.cpp
vendored
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
/*
|
||||||
|
* ------------------------------------------------------------
|
||||||
|
* | 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"
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH 240
|
||||||
|
#define SCREEN_HEIGHT 320
|
||||||
|
#define PADDLE_WIDTH 40
|
||||||
|
#define PADDLE_HEIGHT 10
|
||||||
|
#define BALL_SIZE 8
|
||||||
|
#define BRICK_WIDTH 20
|
||||||
|
#define BRICK_HEIGHT 10
|
||||||
|
#define BRICK_ROWS 5
|
||||||
|
#define BRICK_COLS 10
|
||||||
|
#define BRICK_GAP 2
|
||||||
|
#define GAME_AREA_TOP 50
|
||||||
|
#define GAME_AREA_BOTTOM 310
|
||||||
|
#define PADDLE_Y (GAME_AREA_BOTTOM - PADDLE_HEIGHT)
|
||||||
|
#define BALL_SPEED_INCREASE 0.1f
|
||||||
|
|
||||||
|
#define STATE_MENU 0
|
||||||
|
#define STATE_PLAYING 1
|
||||||
|
#define STATE_GAME_OVER 3
|
||||||
|
|
||||||
|
#define COLOR_BACKGROUND Black
|
||||||
|
#define COLOR_PADDLE Blue
|
||||||
|
#define COLOR_BALL White
|
||||||
|
#define COLOR_BORDER White
|
||||||
|
#define COLOR_BRICK_COLORS \
|
||||||
|
{ Red, Orange, Yellow, Green, Purple }
|
||||||
|
|
||||||
|
Ticker game_timer;
|
||||||
|
|
||||||
|
int paddle_x = (SCREEN_WIDTH - PADDLE_WIDTH) / 2;
|
||||||
|
float ball_x = SCREEN_WIDTH / 2;
|
||||||
|
float ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1;
|
||||||
|
float ball_dx = 1.5f;
|
||||||
|
float ball_dy = -2.0f;
|
||||||
|
int score = 0;
|
||||||
|
int lives = 3;
|
||||||
|
int level = 1;
|
||||||
|
int game_state = STATE_MENU;
|
||||||
|
bool initialized = false;
|
||||||
|
bool ball_attached = true;
|
||||||
|
unsigned int brick_count = 0;
|
||||||
|
|
||||||
|
bool bricks[BRICK_ROWS][BRICK_COLS];
|
||||||
|
int brick_colors[BRICK_ROWS];
|
||||||
|
|
||||||
|
extern ui::Painter painter;
|
||||||
|
|
||||||
|
void init_game();
|
||||||
|
void init_level();
|
||||||
|
void draw_screen();
|
||||||
|
void draw_bricks();
|
||||||
|
void draw_paddle();
|
||||||
|
void draw_ball();
|
||||||
|
void draw_score();
|
||||||
|
void draw_lives();
|
||||||
|
void draw_level();
|
||||||
|
void draw_borders();
|
||||||
|
void move_paddle_left();
|
||||||
|
void move_paddle_right();
|
||||||
|
void launch_ball();
|
||||||
|
void update_game();
|
||||||
|
void check_collisions();
|
||||||
|
bool check_brick_collision(int row, int col);
|
||||||
|
void handle_game_over();
|
||||||
|
void show_menu();
|
||||||
|
void show_game_over();
|
||||||
|
bool check_level_complete();
|
||||||
|
void next_level();
|
||||||
|
void reset_game();
|
||||||
|
|
||||||
|
void game_timer_check() {
|
||||||
|
if (game_state == STATE_PLAYING) {
|
||||||
|
update_game();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_game() {
|
||||||
|
claim(stdout);
|
||||||
|
set_orientation(2);
|
||||||
|
set_font((unsigned char*)Arial12x12);
|
||||||
|
|
||||||
|
paddle_x = (SCREEN_WIDTH - PADDLE_WIDTH) / 2;
|
||||||
|
score = 0;
|
||||||
|
lives = 3;
|
||||||
|
level = 1;
|
||||||
|
|
||||||
|
brick_colors[0] = Red;
|
||||||
|
brick_colors[1] = Orange;
|
||||||
|
brick_colors[2] = Yellow;
|
||||||
|
brick_colors[3] = Green;
|
||||||
|
brick_colors[4] = Purple;
|
||||||
|
|
||||||
|
init_level();
|
||||||
|
|
||||||
|
game_state = STATE_MENU;
|
||||||
|
show_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_level() {
|
||||||
|
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
|
||||||
|
ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1;
|
||||||
|
|
||||||
|
float speed_multiplier = (level == 1) ? 1.0f : 1.0f + ((level - 1) * BALL_SPEED_INCREASE);
|
||||||
|
ball_dx = (ball_dx > 0 ? 1.5f : -1.5f) * speed_multiplier;
|
||||||
|
ball_dy = -2.0f * speed_multiplier;
|
||||||
|
|
||||||
|
ball_attached = true;
|
||||||
|
|
||||||
|
brick_count = 0;
|
||||||
|
for (int row = 0; row < BRICK_ROWS; row++) {
|
||||||
|
for (int col = 0; col < BRICK_COLS; col++) {
|
||||||
|
bricks[row][col] = true;
|
||||||
|
brick_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_screen() {
|
||||||
|
cls();
|
||||||
|
background(COLOR_BACKGROUND);
|
||||||
|
|
||||||
|
draw_borders();
|
||||||
|
draw_bricks();
|
||||||
|
draw_paddle();
|
||||||
|
draw_ball();
|
||||||
|
draw_score();
|
||||||
|
draw_lives();
|
||||||
|
draw_level();
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_borders() {
|
||||||
|
rect(0, GAME_AREA_TOP - 1, SCREEN_WIDTH, GAME_AREA_TOP, COLOR_BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_bricks() {
|
||||||
|
for (int row = 0; row < BRICK_ROWS; row++) {
|
||||||
|
for (int col = 0; col < BRICK_COLS; col++) {
|
||||||
|
if (bricks[row][col]) {
|
||||||
|
int x = col * (BRICK_WIDTH + BRICK_GAP);
|
||||||
|
int y = GAME_AREA_TOP + row * (BRICK_HEIGHT + BRICK_GAP) + 5;
|
||||||
|
fillrect(x, y, x + BRICK_WIDTH, y + BRICK_HEIGHT, brick_colors[row]);
|
||||||
|
rect(x, y, x + BRICK_WIDTH, y + BRICK_HEIGHT, Black);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_paddle() {
|
||||||
|
fillrect(paddle_x, PADDLE_Y, paddle_x + PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, COLOR_PADDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_ball() {
|
||||||
|
fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_score() {
|
||||||
|
auto style = *ui::Theme::getInstance()->fg_green;
|
||||||
|
painter.draw_string({5, 10}, style, "Score: " + std::to_string(score));
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_lives() {
|
||||||
|
auto style = *ui::Theme::getInstance()->fg_red;
|
||||||
|
painter.draw_string({5, 30}, style, "Lives: " + std::to_string(lives));
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_level() {
|
||||||
|
auto style = *ui::Theme::getInstance()->fg_yellow;
|
||||||
|
painter.draw_string({80, 30}, style, "Level: " + std::to_string(level));
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_paddle_left() {
|
||||||
|
if (paddle_x > 0) {
|
||||||
|
fillrect(paddle_x, PADDLE_Y, paddle_x + PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, COLOR_BACKGROUND);
|
||||||
|
if (ball_attached) {
|
||||||
|
fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BACKGROUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
paddle_x -= 10;
|
||||||
|
if (paddle_x < 0) paddle_x = 0;
|
||||||
|
|
||||||
|
if (ball_attached) {
|
||||||
|
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_paddle();
|
||||||
|
if (ball_attached) {
|
||||||
|
draw_ball();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_paddle_right() {
|
||||||
|
if (paddle_x < SCREEN_WIDTH - PADDLE_WIDTH) {
|
||||||
|
fillrect(paddle_x, PADDLE_Y, paddle_x + PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, COLOR_BACKGROUND);
|
||||||
|
if (ball_attached) {
|
||||||
|
fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BACKGROUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
paddle_x += 10;
|
||||||
|
if (paddle_x > SCREEN_WIDTH - PADDLE_WIDTH) paddle_x = SCREEN_WIDTH - PADDLE_WIDTH;
|
||||||
|
|
||||||
|
if (ball_attached) {
|
||||||
|
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_paddle();
|
||||||
|
if (ball_attached) {
|
||||||
|
draw_ball();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void launch_ball() {
|
||||||
|
if (ball_attached) {
|
||||||
|
ball_attached = false;
|
||||||
|
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
|
||||||
|
ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1;
|
||||||
|
float speed_multiplier = (level == 1) ? 1.0f : 1.0f + ((level - 1) * BALL_SPEED_INCREASE);
|
||||||
|
ball_dx = 1.5f * speed_multiplier;
|
||||||
|
ball_dy = -2.0f * speed_multiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_game() {
|
||||||
|
if (ball_attached) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BACKGROUND);
|
||||||
|
|
||||||
|
float next_ball_y = ball_y + ball_dy;
|
||||||
|
if (next_ball_y > GAME_AREA_BOTTOM) {
|
||||||
|
lives--;
|
||||||
|
draw_lives();
|
||||||
|
if (lives <= 0) {
|
||||||
|
handle_game_over();
|
||||||
|
} else {
|
||||||
|
ball_attached = true;
|
||||||
|
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
|
||||||
|
ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1;
|
||||||
|
draw_ball();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ball_x += ball_dx;
|
||||||
|
ball_y = next_ball_y;
|
||||||
|
|
||||||
|
if (ball_x < 0) {
|
||||||
|
ball_x = 0;
|
||||||
|
ball_dx = -ball_dx;
|
||||||
|
} else if (ball_x > SCREEN_WIDTH - BALL_SIZE) {
|
||||||
|
ball_x = SCREEN_WIDTH - BALL_SIZE;
|
||||||
|
ball_dx = -ball_dx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ball_y < GAME_AREA_TOP) {
|
||||||
|
ball_y = GAME_AREA_TOP;
|
||||||
|
ball_dy = -ball_dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ball_y + BALL_SIZE >= PADDLE_Y && ball_y <= PADDLE_Y + PADDLE_HEIGHT) {
|
||||||
|
if (ball_x + BALL_SIZE >= paddle_x && ball_x <= paddle_x + PADDLE_WIDTH) {
|
||||||
|
ball_y = PADDLE_Y - BALL_SIZE;
|
||||||
|
float hit_position = (ball_x + (BALL_SIZE / 2)) - paddle_x;
|
||||||
|
float angle = (hit_position / PADDLE_WIDTH) - 0.5f;
|
||||||
|
ball_dx = angle * 4.0f;
|
||||||
|
if (ball_dx > -0.5f && ball_dx < 0.5f) {
|
||||||
|
ball_dx = (ball_dx > 0) ? 0.5f : -0.5f;
|
||||||
|
}
|
||||||
|
ball_dy = -ball_dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check_collisions();
|
||||||
|
|
||||||
|
draw_ball();
|
||||||
|
|
||||||
|
if (check_level_complete()) {
|
||||||
|
next_level();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_collisions() {
|
||||||
|
int grid_x = ball_x / (BRICK_WIDTH + BRICK_GAP);
|
||||||
|
int grid_y = (ball_y - GAME_AREA_TOP - 5) / (BRICK_HEIGHT + BRICK_GAP);
|
||||||
|
|
||||||
|
for (int row = grid_y - 1; row <= grid_y + 1; row++) {
|
||||||
|
for (int col = grid_x - 1; col <= grid_x + 1; col++) {
|
||||||
|
if (row >= 0 && row < BRICK_ROWS && col >= 0 && col < BRICK_COLS) {
|
||||||
|
if (bricks[row][col] && check_brick_collision(row, col)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_brick_collision(int row, int col) {
|
||||||
|
int brick_x = col * (BRICK_WIDTH + BRICK_GAP);
|
||||||
|
int brick_y = GAME_AREA_TOP + row * (BRICK_HEIGHT + BRICK_GAP) + 5;
|
||||||
|
|
||||||
|
if (ball_x + BALL_SIZE >= brick_x && ball_x <= brick_x + BRICK_WIDTH &&
|
||||||
|
ball_y + BALL_SIZE >= brick_y && ball_y <= brick_y + BRICK_HEIGHT) {
|
||||||
|
fillrect(brick_x, brick_y, brick_x + BRICK_WIDTH, brick_y + BRICK_HEIGHT, COLOR_BACKGROUND);
|
||||||
|
|
||||||
|
bricks[row][col] = false;
|
||||||
|
brick_count--;
|
||||||
|
|
||||||
|
score += (5 - row) * 10;
|
||||||
|
draw_score();
|
||||||
|
|
||||||
|
float center_x = brick_x + BRICK_WIDTH / 2;
|
||||||
|
float center_y = brick_y + BRICK_HEIGHT / 2;
|
||||||
|
float ball_center_x = ball_x + BALL_SIZE / 2;
|
||||||
|
float ball_center_y = ball_y + BALL_SIZE / 2;
|
||||||
|
float dx = std::abs(ball_center_x - center_x);
|
||||||
|
float dy = std::abs(ball_center_y - center_y);
|
||||||
|
|
||||||
|
if (dx * BRICK_HEIGHT > dy * BRICK_WIDTH) {
|
||||||
|
ball_dx = -ball_dx;
|
||||||
|
} else {
|
||||||
|
ball_dy = -ball_dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_level_complete() {
|
||||||
|
return brick_count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void next_level() {
|
||||||
|
level++;
|
||||||
|
init_level();
|
||||||
|
draw_screen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_game_over() {
|
||||||
|
game_state = STATE_GAME_OVER;
|
||||||
|
show_game_over();
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_menu() {
|
||||||
|
cls();
|
||||||
|
background(COLOR_BACKGROUND);
|
||||||
|
|
||||||
|
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
|
||||||
|
auto style_white = *ui::Theme::getInstance()->fg_light;
|
||||||
|
auto style_green = *ui::Theme::getInstance()->fg_green;
|
||||||
|
auto style_red = *ui::Theme::getInstance()->fg_red;
|
||||||
|
|
||||||
|
painter.draw_string({0, 40}, style_yellow, "* * * BREAKOUT * * *");
|
||||||
|
painter.draw_string({0, 70}, style_white, "========================");
|
||||||
|
painter.draw_string({0, 120}, style_green, "| ROTARY: MOVE PADDLE |");
|
||||||
|
painter.draw_string({0, 150}, style_green, "| SELECT: START/LAUNCH |");
|
||||||
|
painter.draw_string({0, 190}, style_white, "========================");
|
||||||
|
painter.draw_string({24, 230}, style_red, "* PRESS SELECT *");
|
||||||
|
}
|
||||||
|
|
||||||
|
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({72, 120}, style_red, "GAME OVER");
|
||||||
|
painter.draw_string({12, 160}, style_yellow, "FINAL SCORE: " + std::to_string(score));
|
||||||
|
painter.draw_string({0, 200}, style_green, "PRESS SELECT TO RESTART");
|
||||||
|
|
||||||
|
wait(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_game() {
|
||||||
|
level = 1;
|
||||||
|
score = 0;
|
||||||
|
lives = 3;
|
||||||
|
game_state = STATE_PLAYING;
|
||||||
|
init_level();
|
||||||
|
draw_screen();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (!initialized) {
|
||||||
|
initialized = true;
|
||||||
|
game_timer.attach(&game_timer_check, 1.0 / 60.0);
|
||||||
|
init_game();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (but_SELECT && game_state == STATE_MENU) {
|
||||||
|
game_state = STATE_PLAYING;
|
||||||
|
reset_game();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (but_SELECT && game_state == STATE_GAME_OVER) {
|
||||||
|
reset_game();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (but_SELECT && game_state == STATE_PLAYING && ball_attached) {
|
||||||
|
launch_ball();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (but_LEFT && game_state == STATE_PLAYING) {
|
||||||
|
move_paddle_left();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (but_RIGHT && game_state == STATE_PLAYING) {
|
||||||
|
move_paddle_right();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
firmware/application/external/breakout/main.cpp
vendored
Normal file
70
firmware/application/external/breakout/main.cpp
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* ------------------------------------------------------------
|
||||||
|
* | Made by RocketGod |
|
||||||
|
* | Find me at https://betaskynet.com |
|
||||||
|
* | Argh matey! |
|
||||||
|
* ------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
|
#include "ui_breakout.hpp"
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "external_app.hpp"
|
||||||
|
|
||||||
|
namespace ui::external_app::breakout {
|
||||||
|
void initialize_app(ui::NavigationView& nav) {
|
||||||
|
nav.push<BreakoutView>();
|
||||||
|
}
|
||||||
|
} // namespace ui::external_app::breakout
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
__attribute__((section(".external_app.app_breakout.application_information"), used)) application_information_t _application_information_breakout = {
|
||||||
|
/*.memory_location = */ (uint8_t*)0x00000000, // will be filled at compile time
|
||||||
|
/*.externalAppEntry = */ ui::external_app::breakout::initialize_app,
|
||||||
|
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||||
|
/*.app_version = */ VERSION_MD5,
|
||||||
|
|
||||||
|
/*.app_name = */ "Breakout",
|
||||||
|
/*.bitmap_data = */ {
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
0x80,
|
||||||
|
},
|
||||||
|
/*.icon_color = */ ui::Color::green().v,
|
||||||
|
/*.menu_location = */ app_location_t::UTILITIES,
|
||||||
|
/*.desired_menu_position = */ -1,
|
||||||
|
|
||||||
|
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||||
|
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||||
|
};
|
||||||
|
}
|
105
firmware/application/external/breakout/mbed.h
vendored
Normal file
105
firmware/application/external/breakout/mbed.h
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* ------------------------------------------------------------
|
||||||
|
* | Made by RocketGod |
|
||||||
|
* | Find me at https://betaskynet.com |
|
||||||
|
* | Argh matey! |
|
||||||
|
* ------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool but_RIGHT;
|
||||||
|
static bool but_LEFT;
|
||||||
|
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__*/
|
79
firmware/application/external/breakout/ui_breakout.cpp
vendored
Normal file
79
firmware/application/external/breakout/ui_breakout.cpp
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* ------------------------------------------------------------
|
||||||
|
* | Made by RocketGod |
|
||||||
|
* | Find me at https://betaskynet.com |
|
||||||
|
* | Argh matey! |
|
||||||
|
* ------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ui_breakout.hpp"
|
||||||
|
|
||||||
|
namespace ui::external_app::breakout {
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Weffc++"
|
||||||
|
#include "breakout.cpp"
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
BreakoutView::BreakoutView(NavigationView& nav)
|
||||||
|
: nav_{nav} {
|
||||||
|
add_children({&dummy});
|
||||||
|
game_timer.attach(&game_timer_check, 1.0 / 60.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakoutView::on_show() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakoutView::paint(Painter& painter) {
|
||||||
|
(void)painter;
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
initialized = true;
|
||||||
|
std::srand(LPC_RTC->CTIME0);
|
||||||
|
init_game();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakoutView::frame_sync() {
|
||||||
|
check_game_timer();
|
||||||
|
set_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BreakoutView::on_encoder(const EncoderEvent delta) {
|
||||||
|
if (game_state == STATE_PLAYING) {
|
||||||
|
if (delta > 0) {
|
||||||
|
move_paddle_right();
|
||||||
|
set_dirty();
|
||||||
|
} else if (delta < 0) {
|
||||||
|
move_paddle_left();
|
||||||
|
set_dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BreakoutView::on_key(const KeyEvent key) {
|
||||||
|
if (key == KeyEvent::Select) {
|
||||||
|
if (game_state == STATE_MENU) {
|
||||||
|
game_state = STATE_PLAYING;
|
||||||
|
reset_game();
|
||||||
|
} else if (game_state == STATE_PLAYING && ball_attached) {
|
||||||
|
launch_ball();
|
||||||
|
} else if (game_state == STATE_GAME_OVER) {
|
||||||
|
reset_game();
|
||||||
|
}
|
||||||
|
} else if (key == KeyEvent::Left) {
|
||||||
|
if (game_state == STATE_PLAYING) {
|
||||||
|
move_paddle_left();
|
||||||
|
}
|
||||||
|
} else if (key == KeyEvent::Right) {
|
||||||
|
if (game_state == STATE_PLAYING) {
|
||||||
|
move_paddle_right();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_dirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ui::external_app::breakout
|
53
firmware/application/external/breakout/ui_breakout.hpp
vendored
Normal file
53
firmware/application/external/breakout/ui_breakout.hpp
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* ------------------------------------------------------------
|
||||||
|
* | Made by RocketGod |
|
||||||
|
* | Find me at https://betaskynet.com |
|
||||||
|
* | Argh matey! |
|
||||||
|
* ------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __UI_BREAKOUT_H__
|
||||||
|
#define __UI_BREAKOUT_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::breakout {
|
||||||
|
|
||||||
|
class BreakoutView : public View {
|
||||||
|
public:
|
||||||
|
BreakoutView(NavigationView& nav);
|
||||||
|
void on_show() override;
|
||||||
|
|
||||||
|
std::string title() const override { return "Breakout"; };
|
||||||
|
|
||||||
|
void focus() override { dummy.focus(); };
|
||||||
|
void paint(Painter& painter) override;
|
||||||
|
void frame_sync();
|
||||||
|
bool on_encoder(const EncoderEvent event) override;
|
||||||
|
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::breakout
|
||||||
|
|
||||||
|
#endif /*__UI_BREAKOUT_H__*/
|
5
firmware/application/external/external.cmake
vendored
5
firmware/application/external/external.cmake
vendored
@ -4,6 +4,10 @@ set(EXTCPPSRC
|
|||||||
external/tetris/main.cpp
|
external/tetris/main.cpp
|
||||||
external/tetris/ui_tetris.cpp
|
external/tetris/ui_tetris.cpp
|
||||||
|
|
||||||
|
#tetris
|
||||||
|
external/breakout/main.cpp
|
||||||
|
external/breakout/ui_breakout.cpp
|
||||||
|
|
||||||
#afsk_rx
|
#afsk_rx
|
||||||
external/afsk_rx/main.cpp
|
external/afsk_rx/main.cpp
|
||||||
external/afsk_rx/ui_afsk_rx.cpp
|
external/afsk_rx/ui_afsk_rx.cpp
|
||||||
@ -187,6 +191,7 @@ set(EXTAPPLIST
|
|||||||
spainter
|
spainter
|
||||||
keyfob
|
keyfob
|
||||||
tetris
|
tetris
|
||||||
|
breakout
|
||||||
extsensors
|
extsensors
|
||||||
foxhunt_rx
|
foxhunt_rx
|
||||||
audio_test
|
audio_test
|
||||||
|
7
firmware/application/external/external.ld
vendored
7
firmware/application/external/external.ld
vendored
@ -38,6 +38,7 @@ MEMORY
|
|||||||
ram_external_app_keyfob(rwx) : org = 0xADBD0000, len = 32k
|
ram_external_app_keyfob(rwx) : org = 0xADBD0000, len = 32k
|
||||||
ram_external_app_tetris(rwx) : org = 0xADBE0000, len = 32k
|
ram_external_app_tetris(rwx) : org = 0xADBE0000, len = 32k
|
||||||
ram_external_app_extsensors(rwx) : org = 0xADBF0000, 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_foxhunt_rx(rwx) : org = 0xADC00000, len = 32k
|
||||||
ram_external_app_audio_test(rwx) : org = 0xADC10000, len = 32k
|
ram_external_app_audio_test(rwx) : org = 0xADC10000, len = 32k
|
||||||
ram_external_app_wardrivemap(rwx) : org = 0xADC20000, len = 32k
|
ram_external_app_wardrivemap(rwx) : org = 0xADC20000, len = 32k
|
||||||
@ -152,6 +153,12 @@ SECTIONS
|
|||||||
*(*ui*external_app*tetris*);
|
*(*ui*external_app*tetris*);
|
||||||
} > ram_external_app_tetris
|
} > ram_external_app_tetris
|
||||||
|
|
||||||
|
.external_app_breakout : ALIGN(4) SUBALIGN(4)
|
||||||
|
{
|
||||||
|
KEEP(*(.external_app.app_breakout.application_information));
|
||||||
|
*(*ui*external_app*breakout*);
|
||||||
|
} > ram_external_app_breakout
|
||||||
|
|
||||||
.external_app_extsensors : ALIGN(4) SUBALIGN(4)
|
.external_app_extsensors : ALIGN(4) SUBALIGN(4)
|
||||||
{
|
{
|
||||||
KEEP(*(.external_app.app_extsensors.application_information));
|
KEEP(*(.external_app.app_extsensors.application_information));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user