Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f939312640 |
@@ -14,6 +14,7 @@ board = esp32doit-devkit-v1
|
|||||||
framework = arduino
|
framework = arduino
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
|
#monitor_port = /dev/cu.usbserial-2120
|
||||||
#upload_flags = -b 115200
|
#upload_flags = -b 115200
|
||||||
|
|
||||||
monitor_flags =
|
monitor_flags =
|
||||||
@@ -46,6 +47,7 @@ build_flags =
|
|||||||
-DLOAD_GLCD=1
|
-DLOAD_GLCD=1
|
||||||
-DLOAD_GFXFF=1
|
-DLOAD_GFXFF=1
|
||||||
-DSPI_FREQUENCY=40000000
|
-DSPI_FREQUENCY=40000000
|
||||||
|
-DDISABLE_ALL_LIBRARY_WARNINGS=1
|
||||||
|
|
||||||
[env:mainOTA]
|
[env:mainOTA]
|
||||||
extends = env:main
|
extends = env:main
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <json11.hpp>
|
#include <json11.hpp>
|
||||||
|
|
||||||
#include "gif_player.h"
|
#include "gif_player.h"
|
||||||
|
#include "flappy.h"
|
||||||
|
|
||||||
using namespace json11;
|
using namespace json11;
|
||||||
|
|
||||||
@@ -311,7 +312,12 @@ void DisplayTask::run() {
|
|||||||
tft_.fillScreen(TFT_BLACK);
|
tft_.fillScreen(TFT_BLACK);
|
||||||
tft_.setTextSize(2);
|
tft_.setTextSize(2);
|
||||||
tft_.setTextDatum(TC_DATUM);
|
tft_.setTextDatum(TC_DATUM);
|
||||||
tft_.drawString("Merry Christmas!", center, 10);
|
if (isChristmas()){
|
||||||
|
tft_.drawString("Merry Christmas!", center, 10);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tft_.drawString("Credits", center, 10);
|
||||||
|
}
|
||||||
tft_.setTextSize(1);
|
tft_.setTextSize(1);
|
||||||
tft_.drawString("Designed and handmade", center, 50);
|
tft_.drawString("Designed and handmade", center, 50);
|
||||||
tft_.drawString("by Scott Bezek", center, 60);
|
tft_.drawString("by Scott Bezek", center, 60);
|
||||||
@@ -364,6 +370,28 @@ void DisplayTask::run() {
|
|||||||
tft_.fillScreen(TFT_BLACK);
|
tft_.fillScreen(TFT_BLACK);
|
||||||
delay(200);
|
delay(200);
|
||||||
}
|
}
|
||||||
|
if (left_button){
|
||||||
|
main_task_.setOtaEnabled(false);
|
||||||
|
state = State::PLAY_FLAPPY;
|
||||||
|
tft_.fillScreen(TFT_BLACK);
|
||||||
|
tft_.setTextSize(2);
|
||||||
|
tft_.setTextDatum(TC_DATUM);
|
||||||
|
tft_.drawString("Lets Flap!", tft_.width()/2, 10);
|
||||||
|
tft_.setTextSize(1);
|
||||||
|
delay(200);
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case State::PLAY_FLAPPY:
|
||||||
|
if (right_button) {
|
||||||
|
// Exit credits
|
||||||
|
main_task_.setOtaEnabled(false);
|
||||||
|
state = State::CHOOSE_GIF;
|
||||||
|
tft_.fillScreen(TFT_BLACK);
|
||||||
|
delay(200);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
game_loop(left_button);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ enum class State {
|
|||||||
CHOOSE_GIF,
|
CHOOSE_GIF,
|
||||||
PLAY_GIF,
|
PLAY_GIF,
|
||||||
SHOW_CREDITS,
|
SHOW_CREDITS,
|
||||||
|
PLAY_FLAPPY,
|
||||||
};
|
};
|
||||||
|
|
||||||
class DisplayTask : public Task<DisplayTask>, public Logger {
|
class DisplayTask : public Task<DisplayTask>, public Logger {
|
||||||
|
|||||||
351
Firmware/src/flappy.cpp
Normal file
351
Firmware/src/flappy.cpp
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
#include "flappy.h"
|
||||||
|
|
||||||
|
// By Ponticelli Domenico.
|
||||||
|
// 12NOV2020 EEPROM Working now, Modified by Zontex
|
||||||
|
// https://github.com/pcelli85/M5Stack_FlappyBird_game
|
||||||
|
|
||||||
|
// #include <EEPROM.h>
|
||||||
|
// #include <M5StickC.h>
|
||||||
|
TFT_eSPI tft_;
|
||||||
|
int address = 0;
|
||||||
|
int maxScore = 0; // EEPROM.readInt(address);
|
||||||
|
const int buttonPin = 2;
|
||||||
|
// background
|
||||||
|
const unsigned int BCKGRDCOL = tft_.color565(138, 235, 244);
|
||||||
|
// bird
|
||||||
|
const unsigned int BIRDCOL = tft_.color565(255, 254, 174);
|
||||||
|
// pipe
|
||||||
|
const unsigned int PIPECOL = tft_.color565(99, 255, 78);
|
||||||
|
// pipe highlight
|
||||||
|
const unsigned int PIPEHIGHCOL = tft_.color565(250, 255, 250);
|
||||||
|
// pipe seam
|
||||||
|
const unsigned int PIPESEAMCOL = tft_.color565(0, 0, 0);
|
||||||
|
// floor
|
||||||
|
const unsigned int FLOORCOL = tft_.color565(246, 240, 163);
|
||||||
|
// grass (col2 is the stripe color)
|
||||||
|
const unsigned int GRASSCOL = tft_.color565(141, 225, 87);
|
||||||
|
const unsigned int GRASSCOL2 = tft_.color565(156, 239, 88);
|
||||||
|
|
||||||
|
// bird sprite
|
||||||
|
// bird sprite colors (Cx name for values to keep the array readable)
|
||||||
|
#define C0 BCKGRDCOL
|
||||||
|
#define C1 tft_.color565(195, 165, 75)
|
||||||
|
#define C2 BIRDCOL
|
||||||
|
#define C3 TFT_WHITE
|
||||||
|
#define C4 TFT_RED
|
||||||
|
#define C5 tft_.color565(251, 216, 114)
|
||||||
|
|
||||||
|
static unsigned int birdcol[] = {
|
||||||
|
C0, C0, C1, C1, C1, C1, C1, C0, C0, C0, C1, C1, C1, C1, C1, C0, C0, C1, C2,
|
||||||
|
C2, C2, C1, C3, C1, C0, C1, C2, C2, C2, C1, C3, C1, C0, C2, C2, C2, C2, C1,
|
||||||
|
C3, C1, C0, C2, C2, C2, C2, C1, C3, C1, C1, C1, C1, C2, C2, C3, C1, C1, C1,
|
||||||
|
C1, C1, C2, C2, C3, C1, C1, C1, C2, C2, C2, C2, C2, C4, C4, C1, C2, C2, C2,
|
||||||
|
C2, C2, C4, C4, C1, C2, C2, C2, C1, C5, C4, C0, C1, C2, C2, C2, C1, C5, C4,
|
||||||
|
C0, C0, C1, C2, C1, C5, C5, C5, C0, C0, C1, C2, C1, C5, C5, C5, C0, C0, C0,
|
||||||
|
C1, C5, C5, C5, C0, C0, C0, C0, C1, C5, C5, C5, C0, C0};
|
||||||
|
|
||||||
|
// bird structure
|
||||||
|
static struct BIRD
|
||||||
|
{
|
||||||
|
long x, y, old_y;
|
||||||
|
long col;
|
||||||
|
float vel_y;
|
||||||
|
} bird;
|
||||||
|
|
||||||
|
// pipe structure
|
||||||
|
static struct PIPES
|
||||||
|
{
|
||||||
|
long x, gap_y;
|
||||||
|
long col;
|
||||||
|
} pipes;
|
||||||
|
|
||||||
|
// score
|
||||||
|
int score;
|
||||||
|
// temporary x and y var
|
||||||
|
static short tmpx, tmpy;
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// draw pixel
|
||||||
|
// ---------------
|
||||||
|
// faster drawPixel method by inlining calls and using setAddrWindow and
|
||||||
|
// pushColor using macro to force inlining
|
||||||
|
#define drawPixel(a, b, c) \
|
||||||
|
tft_.setAddrWindow(a, b, a, b); \
|
||||||
|
tft_.pushColor(c)
|
||||||
|
// ---------------
|
||||||
|
// game loop
|
||||||
|
// ---------------
|
||||||
|
double delta, old_time, next_game_tick, current_time;
|
||||||
|
FlpState state;
|
||||||
|
unsigned char GAMEH = TFTH - FLOORH;
|
||||||
|
long grassx = TFTW;
|
||||||
|
int loops;
|
||||||
|
// passed pipe flag to count score
|
||||||
|
bool passed_pipe = false;
|
||||||
|
// temp var for setAddrWindow
|
||||||
|
unsigned char px;
|
||||||
|
|
||||||
|
void game_loop(bool button)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case FlpState::START_FLAPPY:
|
||||||
|
{
|
||||||
|
tft_.fillScreen(TFT_BLACK);
|
||||||
|
tft_.fillRect(0, TFTH2 - 10, TFTW, 1, TFT_WHITE);
|
||||||
|
tft_.fillRect(0, TFTH2 + 15, TFTW, 1, TFT_WHITE);
|
||||||
|
tft_.setTextColor(TFT_WHITE);
|
||||||
|
tft_.setTextSize(1);
|
||||||
|
// half width - num char * char width in pixels
|
||||||
|
tft_.setCursor(TFTW2 - 15, TFTH2 - 6);
|
||||||
|
tft_.println("FLAPPY");
|
||||||
|
tft_.setTextSize(1);
|
||||||
|
tft_.setCursor(TFTW2 - 15, TFTH2 + 6);
|
||||||
|
tft_.println("-BIRD-");
|
||||||
|
tft_.setTextSize(1);
|
||||||
|
tft_.setCursor(TFTW2 - 40, TFTH2 + 21);
|
||||||
|
tft_.println("please press home");
|
||||||
|
if (button)
|
||||||
|
{
|
||||||
|
state = FlpState::INIT_FLAPPY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlpState::INIT_FLAPPY:
|
||||||
|
{
|
||||||
|
// clear screen
|
||||||
|
tft_.fillScreen(BCKGRDCOL);
|
||||||
|
// reset score
|
||||||
|
score = 0;
|
||||||
|
// init bird
|
||||||
|
bird.x = 30;
|
||||||
|
bird.y = bird.old_y = TFTH2 - BIRDH;
|
||||||
|
bird.vel_y = -JUMP_FORCE;
|
||||||
|
tmpx = tmpy = 0;
|
||||||
|
// generate new random seed for the pipe gape
|
||||||
|
randomSeed(analogRead(0));
|
||||||
|
// init pipe
|
||||||
|
pipes.x = 0;
|
||||||
|
pipes.gap_y = random(20, TFTH - 60);
|
||||||
|
// ===============
|
||||||
|
// prepare game variables
|
||||||
|
// draw floor
|
||||||
|
// ===============
|
||||||
|
// instead of calculating the distance of the floor from the screen height
|
||||||
|
// each time store it in a variable
|
||||||
|
|
||||||
|
// draw the floor once, we will not overwrite on this area in-game
|
||||||
|
// black line
|
||||||
|
tft_.drawFastHLine(0, GAMEH, TFTW, TFT_BLACK);
|
||||||
|
// grass and stripe
|
||||||
|
tft_.fillRect(0, GAMEH + 1, TFTW2, GRASSH, GRASSCOL);
|
||||||
|
tft_.fillRect(TFTW2, GAMEH + 1, TFTW2, GRASSH, GRASSCOL2);
|
||||||
|
// black line
|
||||||
|
tft_.drawFastHLine(0, GAMEH + GRASSH, TFTW, TFT_BLACK);
|
||||||
|
// mud
|
||||||
|
tft_.fillRect(0, GAMEH + GRASSH + 1, TFTW, FLOORH - GRASSH, FLOORCOL);
|
||||||
|
// grass x position (for stripe animation)
|
||||||
|
// game loop time variables
|
||||||
|
|
||||||
|
next_game_tick = current_time = millis();
|
||||||
|
|
||||||
|
loops = 0;
|
||||||
|
state = FlpState::RUN_FLAPPY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlpState::RUN_FLAPPY:
|
||||||
|
{
|
||||||
|
if (millis() > next_game_tick && loops < MAX_FRAMESKIP)
|
||||||
|
{
|
||||||
|
if (button)
|
||||||
|
{
|
||||||
|
// while(digitalRead(M5_BUTTON_HOME) == LOW);
|
||||||
|
if (bird.y > BIRDH2 * 0.5)
|
||||||
|
bird.vel_y = -JUMP_FORCE;
|
||||||
|
// else zero velocity
|
||||||
|
else
|
||||||
|
bird.vel_y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============
|
||||||
|
// update
|
||||||
|
// ===============
|
||||||
|
// calculate delta time
|
||||||
|
// ---------------
|
||||||
|
old_time = current_time;
|
||||||
|
current_time = millis();
|
||||||
|
delta = (current_time - old_time) / 1000;
|
||||||
|
|
||||||
|
// bird
|
||||||
|
// ---------------
|
||||||
|
bird.vel_y += GRAVITY * delta;
|
||||||
|
bird.y += bird.vel_y;
|
||||||
|
|
||||||
|
// pipe
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
pipes.x -= SPEED;
|
||||||
|
// if pipe reached edge of the screen reset its position and gap
|
||||||
|
if (pipes.x < -PIPEW)
|
||||||
|
{
|
||||||
|
pipes.x = TFTW;
|
||||||
|
pipes.gap_y = random(10, GAMEH - (10 + GAPHEIGHT));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
next_game_tick += SKIP_TICKS;
|
||||||
|
loops++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============
|
||||||
|
// draw
|
||||||
|
// ===============
|
||||||
|
// pipe
|
||||||
|
// ---------------
|
||||||
|
// we save cycles if we avoid drawing the pipe when outside the screen
|
||||||
|
|
||||||
|
if (pipes.x >= 0 && pipes.x < TFTW)
|
||||||
|
{
|
||||||
|
// pipe color
|
||||||
|
tft_.drawFastVLine(pipes.x + 3, 0, pipes.gap_y, PIPECOL);
|
||||||
|
tft_.drawFastVLine(pipes.x + 3, pipes.gap_y + GAPHEIGHT + 1,
|
||||||
|
GAMEH - (pipes.gap_y + GAPHEIGHT + 1),
|
||||||
|
PIPECOL);
|
||||||
|
// highlight
|
||||||
|
tft_.drawFastVLine(pipes.x, 0, pipes.gap_y, PIPEHIGHCOL);
|
||||||
|
tft_.drawFastVLine(pipes.x, pipes.gap_y + GAPHEIGHT + 1,
|
||||||
|
GAMEH - (pipes.gap_y + GAPHEIGHT + 1),
|
||||||
|
PIPEHIGHCOL);
|
||||||
|
// bottom and top border of pipe
|
||||||
|
drawPixel(pipes.x, pipes.gap_y, PIPESEAMCOL);
|
||||||
|
drawPixel(pipes.x, pipes.gap_y + GAPHEIGHT, PIPESEAMCOL);
|
||||||
|
// pipe seam
|
||||||
|
drawPixel(pipes.x, pipes.gap_y - 6, PIPESEAMCOL);
|
||||||
|
drawPixel(pipes.x, pipes.gap_y + GAPHEIGHT + 6, PIPESEAMCOL);
|
||||||
|
drawPixel(pipes.x + 3, pipes.gap_y - 6, PIPESEAMCOL);
|
||||||
|
drawPixel(pipes.x + 3, pipes.gap_y + GAPHEIGHT + 6, PIPESEAMCOL);
|
||||||
|
}
|
||||||
|
#if 1
|
||||||
|
// erase behind pipe
|
||||||
|
if (pipes.x <= TFTW)
|
||||||
|
tft_.drawFastVLine(pipes.x + PIPEW, 0, GAMEH, BCKGRDCOL);
|
||||||
|
// tft_.drawFastVLine(pipes.x, 0, GAMEH, BCKGRDCOL);
|
||||||
|
// PIPECOL
|
||||||
|
#endif
|
||||||
|
// bird
|
||||||
|
// ---------------
|
||||||
|
tmpx = BIRDW - 1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
px = bird.x + tmpx + BIRDW;
|
||||||
|
// clear bird at previous position stored in old_y
|
||||||
|
// we can't just erase the pixels before and after current position
|
||||||
|
// because of the non-linear bird movement (it would leave 'dirty'
|
||||||
|
// pixels)
|
||||||
|
tmpy = BIRDH - 1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
drawPixel(px, bird.old_y + tmpy, BCKGRDCOL);
|
||||||
|
} while (tmpy--);
|
||||||
|
// draw bird sprite at new position
|
||||||
|
tmpy = BIRDH - 1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
drawPixel(px, bird.y + tmpy, birdcol[tmpx + (tmpy * BIRDW)]);
|
||||||
|
} while (tmpy--);
|
||||||
|
} while (tmpx--);
|
||||||
|
// save position to erase bird on next draw
|
||||||
|
bird.old_y = bird.y;
|
||||||
|
|
||||||
|
// grass stripes
|
||||||
|
// ---------------
|
||||||
|
grassx -= SPEED;
|
||||||
|
if (grassx < 0)
|
||||||
|
grassx = TFTW;
|
||||||
|
tft_.drawFastVLine(grassx % TFTW, GAMEH + 1, GRASSH - 1, GRASSCOL);
|
||||||
|
tft_.drawFastVLine((grassx + 64) % TFTW, GAMEH + 1, GRASSH - 1,
|
||||||
|
GRASSCOL2);
|
||||||
|
|
||||||
|
// ===============
|
||||||
|
// collision
|
||||||
|
// ===============
|
||||||
|
// if the bird hit the ground game over
|
||||||
|
if (bird.y > GAMEH - BIRDH)
|
||||||
|
break;
|
||||||
|
// checking for bird collision with pipe
|
||||||
|
if (bird.x + BIRDW >= pipes.x - BIRDW2 &&
|
||||||
|
bird.x <= pipes.x + PIPEW - BIRDW)
|
||||||
|
{
|
||||||
|
// bird entered a pipe, check for collision
|
||||||
|
if (bird.y < pipes.gap_y ||
|
||||||
|
bird.y + BIRDH > pipes.gap_y + GAPHEIGHT)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
passed_pipe = true;
|
||||||
|
}
|
||||||
|
// if bird has passed the pipe increase score
|
||||||
|
else if (bird.x > pipes.x + PIPEW - BIRDW && passed_pipe)
|
||||||
|
{
|
||||||
|
passed_pipe = false;
|
||||||
|
// erase score with background color
|
||||||
|
tft_.setTextColor(BCKGRDCOL);
|
||||||
|
tft_.setCursor(TFTW2, 4);
|
||||||
|
tft_.print(score);
|
||||||
|
// set text color back to white for new score
|
||||||
|
tft_.setTextColor(TFT_WHITE);
|
||||||
|
// increase score since we successfully passed a pipe
|
||||||
|
score++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update score
|
||||||
|
// ---------------
|
||||||
|
tft_.setCursor(2, 4);
|
||||||
|
tft_.print(score);
|
||||||
|
delay(2000);
|
||||||
|
state = FlpState::GAME_OVER;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FlpState::GAME_OVER:
|
||||||
|
{
|
||||||
|
tft_.fillScreen(TFT_BLACK);
|
||||||
|
// maxScore = EEPROM.readInt(address);
|
||||||
|
|
||||||
|
if (score > maxScore)
|
||||||
|
{
|
||||||
|
// EEPROM.writeInt(address, score);
|
||||||
|
// EEPROM.commit();
|
||||||
|
maxScore = score;
|
||||||
|
tft_.setTextColor(TFT_RED);
|
||||||
|
tft_.setTextSize(1);
|
||||||
|
tft_.setCursor(0, TFTH2 - 16);
|
||||||
|
tft_.println("NEW HIGHSCORE");
|
||||||
|
}
|
||||||
|
|
||||||
|
tft_.setTextColor(TFT_WHITE);
|
||||||
|
tft_.setTextSize(1);
|
||||||
|
// half width - num char * char width in pixels
|
||||||
|
tft_.setCursor(TFTW2 - 25, TFTH2 - 6);
|
||||||
|
tft_.println("GAME OVER");
|
||||||
|
tft_.setTextSize(1);
|
||||||
|
tft_.setCursor(1, 10);
|
||||||
|
tft_.print("score: ");
|
||||||
|
tft_.print(score);
|
||||||
|
tft_.setCursor(5, TFTH2 + 6);
|
||||||
|
tft_.println("press button");
|
||||||
|
tft_.setCursor(1, 21);
|
||||||
|
tft_.print("Max Score:");
|
||||||
|
tft_.print(maxScore);
|
||||||
|
if (button)
|
||||||
|
{
|
||||||
|
state = FlpState::INIT_FLAPPY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
Firmware/src/flappy.h
Normal file
39
Firmware/src/flappy.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <TFT_eSPI.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TFTW 240 // screen width
|
||||||
|
#define TFTH 135 // screen height
|
||||||
|
#define TFTW2 120 // half screen width
|
||||||
|
#define TFTH2 67 // half screen height
|
||||||
|
// game constant
|
||||||
|
#define SPEED 1
|
||||||
|
#define GRAVITY 9.8
|
||||||
|
#define JUMP_FORCE 2.15
|
||||||
|
#define SKIP_TICKS 20.0 // 1000 / 50fps
|
||||||
|
#define MAX_FRAMESKIP 5
|
||||||
|
// bird size
|
||||||
|
#define BIRDW 8 // bird width
|
||||||
|
#define BIRDH 8 // bird height
|
||||||
|
#define BIRDW2 4 // half width
|
||||||
|
#define BIRDH2 4 // half height
|
||||||
|
// pipe size
|
||||||
|
#define PIPEW 15 // pipe width
|
||||||
|
#define GAPHEIGHT 30 // pipe gap height
|
||||||
|
// floor size
|
||||||
|
#define FLOORH 20 // floor height (from bottom of the screen)
|
||||||
|
// grass size
|
||||||
|
#define GRASSH 4 // grass height (inside floor, starts at floor y)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum class FlpState {
|
||||||
|
START_FLAPPY,
|
||||||
|
INIT_FLAPPY,
|
||||||
|
RUN_FLAPPY,
|
||||||
|
GAME_OVER,
|
||||||
|
};
|
||||||
|
|
||||||
|
void game_loop(bool button);
|
||||||
Reference in New Issue
Block a user