mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-09 05:49:09 -07:00
two new games & music player patch
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
### New Update
|
### New Update
|
||||||
* Fix KeeLoq Uknown behavior, patch StarLine same way
|
* Games: Snake & Tetis now shows score
|
||||||
* Fix incorrect var in protocol Scher-Khan (by @Skorpionm)
|
* Volume patch in music player (testing needed)
|
||||||
* Came Atomo working emulation algorytm!
|
* Two new games: Arkanoid & Tic Tac Toe
|
||||||
#### Previous changes
|
#### Previous changes
|
||||||
|
* Fixed KeeLoq Uknown behavior, patched StarLine same way
|
||||||
|
* Fixed incorrect var in protocol Scher-Khan (by @Skorpionm)
|
||||||
|
* Came Atomo working emulation algorithm!
|
||||||
* Updated UniRF Remix app, .txt file support
|
* Updated UniRF Remix app, .txt file support
|
||||||
* Fixed macOS Brewfile, so compiling on macOS now works
|
* Fixed macOS Brewfile, so compiling on macOS now works
|
||||||
* Updated CAME Atomo
|
* Updated CAME Atomo
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
Welcome to Flipper Zero's Custom Firmware repo!
|
Welcome to Flipper Zero's Custom Firmware repo!
|
||||||
Our goal is to make any features possible in this device without any limitations!
|
Our goal is to make any features possible in this device without any limitations!
|
||||||
|
|
||||||
Please help us realize emulation for all dynamic (rolling codes) protocols and brute-force app!
|
Please help us implement emulation for all dynamic (rolling codes) protocols and brute-force app!
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@@ -177,6 +177,8 @@ Use **`flipper-z-{target}-full-{suffix}.dfu`** to flash your device.
|
|||||||
- [WAV Player (By DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) With Fix From [Atmanos](https://github.com/at-manos)
|
- [WAV Player (By DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) With Fix From [Atmanos](https://github.com/at-manos)
|
||||||
- [Tetris (By jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
|
- [Tetris (By jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
|
||||||
- [Spectrum Analyzer (By jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer)
|
- [Spectrum Analyzer (By jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer)
|
||||||
|
- [Arkanoid (By gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
|
||||||
|
- [Tic Tac Toe (By gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
|
||||||
|
|
||||||
# Links
|
# Links
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ extern int32_t spectrum_analyzer_app(void* p);
|
|||||||
// Games
|
// Games
|
||||||
extern int32_t snake_game_app(void* p);
|
extern int32_t snake_game_app(void* p);
|
||||||
extern int32_t tetris_game_app(void *p);
|
extern int32_t tetris_game_app(void *p);
|
||||||
|
extern int32_t tictactoe_game_app(void* p);
|
||||||
|
extern int32_t arkanoid_game_app(void* p);
|
||||||
|
|
||||||
// On system start hooks declaration
|
// On system start hooks declaration
|
||||||
extern void bt_on_system_start();
|
extern void bt_on_system_start();
|
||||||
@@ -405,6 +407,23 @@ const FlipperApplication FLIPPER_GAMES[] = {
|
|||||||
.flags = FlipperApplicationFlagDefault},
|
.flags = FlipperApplicationFlagDefault},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef APP_ARKANOID_GAME
|
||||||
|
{.app = arkanoid_game_app,
|
||||||
|
.name = "Arkanoid",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = &A_Plugins_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef APP_TICTACTOE_GAME
|
||||||
|
{.app = tictactoe_game_app,
|
||||||
|
.name = "Tic Tac Toe",
|
||||||
|
.stack_size = 1024,
|
||||||
|
.icon = &A_Plugins_14,
|
||||||
|
.flags = FlipperApplicationFlagDefault},
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const size_t FLIPPER_GAMES_COUNT = COUNT_OF(FLIPPER_GAMES);
|
const size_t FLIPPER_GAMES_COUNT = COUNT_OF(FLIPPER_GAMES);
|
||||||
|
|||||||
@@ -51,9 +51,13 @@ APP_SPECTRUM_ANALYZER = 1
|
|||||||
|
|
||||||
# Plugins
|
# Plugins
|
||||||
APP_MUSIC_PLAYER = 1
|
APP_MUSIC_PLAYER = 1
|
||||||
APP_SNAKE_GAME = 1
|
|
||||||
APP_WAV_PLAYER = 1
|
APP_WAV_PLAYER = 1
|
||||||
|
|
||||||
|
# Games
|
||||||
|
APP_SNAKE_GAME = 1
|
||||||
APP_TETRIS_GAME = 1
|
APP_TETRIS_GAME = 1
|
||||||
|
APP_TICTACTOE_GAME = 1
|
||||||
|
APP_ARKANOID_GAME = 1
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
APP_ACCESSOR = 1
|
APP_ACCESSOR = 1
|
||||||
@@ -278,6 +282,18 @@ CFLAGS += -DAPP_SPECTRUM_ANALYZER
|
|||||||
SRV_GUI = 1
|
SRV_GUI = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
APP_TICTACTOE_GAME ?= 0
|
||||||
|
ifeq ($(APP_TICTACTOE_GAME),1)
|
||||||
|
CFLAGS += -DAPP_TICTACTOE_GAME
|
||||||
|
SRV_GUI = 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
APP_ARKANOID_GAME ?= 0
|
||||||
|
ifeq ($(APP_ARKANOID_GAME),1)
|
||||||
|
CFLAGS += -DAPP_ARKANOID_GAME
|
||||||
|
SRV_GUI = 1
|
||||||
|
endif
|
||||||
|
|
||||||
APP_IBUTTON ?= 0
|
APP_IBUTTON ?= 0
|
||||||
ifeq ($(APP_IBUTTON), 1)
|
ifeq ($(APP_IBUTTON), 1)
|
||||||
CFLAGS += -DAPP_IBUTTON
|
CFLAGS += -DAPP_IBUTTON
|
||||||
|
|||||||
410
applications/arkanoid/arkanoid_game.c
Normal file
410
applications/arkanoid/arkanoid_game.c
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
#include <gui/view.h>
|
||||||
|
|
||||||
|
#define TAG "Arkanoid"
|
||||||
|
|
||||||
|
unsigned int COLUMNS = 13; //Columns of bricks
|
||||||
|
unsigned int ROWS = 4; //Rows of bricks
|
||||||
|
int dx = -1; //Initial movement of ball
|
||||||
|
int dy = -1; //Initial movement of ball
|
||||||
|
int xb; //Balls starting possition
|
||||||
|
int yb; //Balls starting possition
|
||||||
|
bool released; //If the ball has been released by the player
|
||||||
|
bool paused = false; //If the game has been paused
|
||||||
|
int xPaddle; //X position of paddle
|
||||||
|
bool isHit[4][13]; //Array of if bricks are hit or not
|
||||||
|
bool bounced = false; //Used to fix double bounce glitch
|
||||||
|
int lives = 3; //Amount of lives
|
||||||
|
int level = 1; //Current level
|
||||||
|
unsigned int score = 0; //Score for the game
|
||||||
|
unsigned int brickCount; //Amount of bricks hit
|
||||||
|
int pad, pad2, pad3; //Button press buffer used to stop pause repeating
|
||||||
|
int oldpad, oldpad2, oldpad3;
|
||||||
|
char text[16]; //General string buffer
|
||||||
|
bool start = false; //If in menu or in game
|
||||||
|
bool initialDraw = false; //If the inital draw has happened
|
||||||
|
char initials[3]; //Initials used in high score
|
||||||
|
|
||||||
|
//Ball Bounds used in collision detection
|
||||||
|
int leftBall;
|
||||||
|
int rightBall;
|
||||||
|
int topBall;
|
||||||
|
int bottomBall;
|
||||||
|
|
||||||
|
//Brick Bounds used in collision detection
|
||||||
|
int leftBrick;
|
||||||
|
int rightBrick;
|
||||||
|
int topBrick;
|
||||||
|
int bottomBrick;
|
||||||
|
|
||||||
|
int tick;
|
||||||
|
|
||||||
|
#define FLIPPER_LCD_WIDTH 128
|
||||||
|
#define FLIPPER_LCD_HEIGHT 64
|
||||||
|
|
||||||
|
typedef enum { EventTypeTick, EventTypeKey } EventType;
|
||||||
|
|
||||||
|
typedef enum { DirectionUp, DirectionRight, DirectionDown, DirectionLeft } Direction;
|
||||||
|
|
||||||
|
typedef enum { GameStatePlaying, GameStateGameOver } GameState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} Point;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GameState game_state;
|
||||||
|
} ArkanoidState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
EventType type;
|
||||||
|
InputEvent input;
|
||||||
|
} GameEvent;
|
||||||
|
|
||||||
|
// generate number in range [min,max)
|
||||||
|
int rand_range(int min, int max) {
|
||||||
|
int number = min + rand() % (max - min);
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
void intro(Canvas* canvas) {
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 46, 0, "Arkanoid");
|
||||||
|
|
||||||
|
//arduboy.tunes.tone(987, 160);
|
||||||
|
//delay(160);
|
||||||
|
//arduboy.tunes.tone(1318, 400);
|
||||||
|
//delay(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_ball(Canvas* canvas) {
|
||||||
|
tick++;
|
||||||
|
if(released) {
|
||||||
|
//Move ball
|
||||||
|
if(abs(dx) == 2) {
|
||||||
|
xb += dx / 2;
|
||||||
|
// 2x speed is really 1.5 speed
|
||||||
|
if(tick % 2 == 0) xb += dx / 2;
|
||||||
|
} else {
|
||||||
|
xb += dx;
|
||||||
|
}
|
||||||
|
yb = yb + dy;
|
||||||
|
|
||||||
|
//Set bounds
|
||||||
|
leftBall = xb;
|
||||||
|
rightBall = xb + 2;
|
||||||
|
topBall = yb;
|
||||||
|
bottomBall = yb + 2;
|
||||||
|
|
||||||
|
//Bounce off top edge
|
||||||
|
if(yb <= 0) {
|
||||||
|
yb = 2;
|
||||||
|
dy = -dy;
|
||||||
|
// arduboy.tunes.tone(523, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Lose a life if bottom edge hit
|
||||||
|
if(yb >= FLIPPER_LCD_HEIGHT) {
|
||||||
|
canvas_draw_frame(canvas, xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1);
|
||||||
|
xPaddle = 54;
|
||||||
|
yb = 60;
|
||||||
|
released = false;
|
||||||
|
lives--;
|
||||||
|
|
||||||
|
sprintf(text, "LIVES:%u", lives);
|
||||||
|
canvas_draw_str(canvas, 0, 90, text);
|
||||||
|
|
||||||
|
// arduboy.tunes.tone(175, 250);
|
||||||
|
if(rand_range(0, 2) == 0) {
|
||||||
|
dx = 1;
|
||||||
|
} else {
|
||||||
|
dx = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Bounce off left side
|
||||||
|
if(xb <= 0) {
|
||||||
|
xb = 2;
|
||||||
|
dx = -dx;
|
||||||
|
// arduboy.tunes.tone(523, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Bounce off right side
|
||||||
|
if(xb >= FLIPPER_LCD_WIDTH - 2) {
|
||||||
|
xb = FLIPPER_LCD_WIDTH - 4;
|
||||||
|
dx = -dx;
|
||||||
|
// arduboy.tunes.tone(523, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Bounce off paddle
|
||||||
|
if(xb + 1 >= xPaddle && xb <= xPaddle + 12 && yb + 2 >= FLIPPER_LCD_HEIGHT - 1 &&
|
||||||
|
yb <= FLIPPER_LCD_HEIGHT) {
|
||||||
|
dy = -dy;
|
||||||
|
dx = ((xb - (xPaddle + 6)) / 3); //Applies spin on the ball
|
||||||
|
// prevent straight bounce
|
||||||
|
if(dx == 0) {
|
||||||
|
dx = (rand_range(0, 2) == 1) ? 1 : -1;
|
||||||
|
}
|
||||||
|
// arduboy.tunes.tone(200, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Bounce off Bricks
|
||||||
|
for(int row = 0; row < ROWS; row++) {
|
||||||
|
for(int column = 0; column < COLUMNS; column++) {
|
||||||
|
if(!isHit[row][column]) {
|
||||||
|
//Sets Brick bounds
|
||||||
|
leftBrick = 10 * column;
|
||||||
|
rightBrick = 10 * column + 10;
|
||||||
|
topBrick = 6 * row + 1;
|
||||||
|
bottomBrick = 6 * row + 7;
|
||||||
|
|
||||||
|
//If A collison has occured
|
||||||
|
if(topBall <= bottomBrick && bottomBall >= topBrick &&
|
||||||
|
leftBall <= rightBrick && rightBall >= leftBrick) {
|
||||||
|
// Draw score
|
||||||
|
score += (level * 10);
|
||||||
|
sprintf(text, "SCORE:%u", score);
|
||||||
|
canvas_draw_str(canvas, 80, 90, text);
|
||||||
|
|
||||||
|
brickCount++;
|
||||||
|
isHit[row][column] = true;
|
||||||
|
canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4);
|
||||||
|
|
||||||
|
//Vertical collision
|
||||||
|
if(bottomBall > bottomBrick || topBall < topBrick) {
|
||||||
|
//Only bounce once each ball move
|
||||||
|
if(!bounced) {
|
||||||
|
dy = -dy;
|
||||||
|
yb += dy;
|
||||||
|
bounced = true;
|
||||||
|
// arduboy.tunes.tone(261, 250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Hoizontal collision
|
||||||
|
if(leftBall < leftBrick || rightBall > rightBrick) {
|
||||||
|
//Only bounce once brick each ball move
|
||||||
|
if(!bounced) {
|
||||||
|
dx = -dx;
|
||||||
|
xb += dx;
|
||||||
|
bounced = true;
|
||||||
|
// arduboy.tunes.tone(261, 250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reset Bounce
|
||||||
|
bounced = false;
|
||||||
|
} else {
|
||||||
|
//Ball follows paddle
|
||||||
|
xb = xPaddle + 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_lives(Canvas* canvas) {
|
||||||
|
sprintf(text, "LIVES:%u", lives);
|
||||||
|
canvas_draw_str(canvas, 0, 90, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_ball(Canvas* canvas) {
|
||||||
|
canvas_draw_dot(canvas, xb, yb);
|
||||||
|
canvas_draw_dot(canvas, xb + 1, yb);
|
||||||
|
canvas_draw_dot(canvas, xb, yb + 1);
|
||||||
|
canvas_draw_dot(canvas, xb + 1, yb + 1);
|
||||||
|
|
||||||
|
move_ball(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_paddle(Canvas* canvas) {
|
||||||
|
canvas_draw_frame(canvas, xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_level(Canvas* canvas) {
|
||||||
|
//Undraw paddle
|
||||||
|
canvas_draw_frame(canvas, xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1);
|
||||||
|
|
||||||
|
//Undraw ball
|
||||||
|
canvas_draw_dot(canvas, xb, yb);
|
||||||
|
canvas_draw_dot(canvas, xb + 1, yb);
|
||||||
|
canvas_draw_dot(canvas, xb, yb + 1);
|
||||||
|
canvas_draw_dot(canvas, xb + 1, yb + 1);
|
||||||
|
|
||||||
|
//Alter various variables to reset the game
|
||||||
|
xPaddle = 54;
|
||||||
|
yb = 60;
|
||||||
|
brickCount = 0;
|
||||||
|
released = false;
|
||||||
|
|
||||||
|
// Reset all brick hit states
|
||||||
|
for(int row = 0; row < ROWS; row++) {
|
||||||
|
for(int column = 0; column < COLUMNS; column++) {
|
||||||
|
isHit[row][column] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arkanoid_state_init(ArkanoidState* const arkanoid_state) {
|
||||||
|
// Set the initial game state
|
||||||
|
arkanoid_state->game_state = GameStatePlaying;
|
||||||
|
|
||||||
|
// Reset initial state
|
||||||
|
initialDraw = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arkanoid_draw_callback(Canvas* const canvas, void* ctx) {
|
||||||
|
const ArkanoidState* arkanoid_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||||
|
if(arkanoid_state == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Initial level draw
|
||||||
|
if(!initialDraw) {
|
||||||
|
initialDraw = true;
|
||||||
|
|
||||||
|
// Set default font for text
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
|
||||||
|
//Draws the new level
|
||||||
|
reset_level(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Draws new bricks and resets their values
|
||||||
|
for(int row = 0; row < ROWS; row++) {
|
||||||
|
for(int column = 0; column < COLUMNS; column++) {
|
||||||
|
if(!isHit[row][column]) {
|
||||||
|
canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lives > 0) {
|
||||||
|
draw_paddle(canvas);
|
||||||
|
|
||||||
|
draw_ball(canvas);
|
||||||
|
|
||||||
|
if(brickCount == ROWS * COLUMNS) {
|
||||||
|
level++;
|
||||||
|
reset_level(canvas);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reset_level(canvas);
|
||||||
|
initialDraw = false;
|
||||||
|
start = false;
|
||||||
|
lives = 3;
|
||||||
|
score = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
release_mutex((ValueMutex*)ctx, arkanoid_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arkanoid_input_callback(InputEvent* input_event, osMessageQueueId_t event_queue) {
|
||||||
|
furi_assert(event_queue);
|
||||||
|
|
||||||
|
GameEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||||
|
osMessageQueuePut(event_queue, &event, 0, osWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arkanoid_update_timer_callback(osMessageQueueId_t event_queue) {
|
||||||
|
furi_assert(event_queue);
|
||||||
|
|
||||||
|
GameEvent event = {.type = EventTypeTick};
|
||||||
|
osMessageQueuePut(event_queue, &event, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t arkanoid_game_app(void* p) {
|
||||||
|
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(GameEvent), NULL);
|
||||||
|
|
||||||
|
ArkanoidState* arkanoid_state = malloc(sizeof(ArkanoidState));
|
||||||
|
arkanoid_state_init(arkanoid_state);
|
||||||
|
|
||||||
|
ValueMutex state_mutex;
|
||||||
|
if(!init_mutex(&state_mutex, arkanoid_state, sizeof(ArkanoidState))) {
|
||||||
|
FURI_LOG_E(TAG, "Cannot create mutex\r\n");
|
||||||
|
free(arkanoid_state);
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set system callbacks
|
||||||
|
ViewPort* view_port = view_port_alloc();
|
||||||
|
view_port_draw_callback_set(view_port, arkanoid_draw_callback, &state_mutex);
|
||||||
|
view_port_input_callback_set(view_port, arkanoid_input_callback, event_queue);
|
||||||
|
|
||||||
|
osTimerId_t timer =
|
||||||
|
osTimerNew(arkanoid_update_timer_callback, osTimerPeriodic, event_queue, NULL);
|
||||||
|
osTimerStart(timer, osKernelGetTickFreq() / 22);
|
||||||
|
|
||||||
|
// Open GUI and register view_port
|
||||||
|
Gui* gui = furi_record_open("gui");
|
||||||
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
|
GameEvent event;
|
||||||
|
for(bool processing = true; processing;) {
|
||||||
|
osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 100);
|
||||||
|
ArkanoidState* arkanoid_state = (ArkanoidState*)acquire_mutex_block(&state_mutex);
|
||||||
|
|
||||||
|
if(event_status == osOK) {
|
||||||
|
// Key events
|
||||||
|
if(event.type == EventTypeKey) {
|
||||||
|
if(event.input.type == InputTypePress || event.input.type == InputTypeLong ||
|
||||||
|
event.input.type == InputTypeRepeat) {
|
||||||
|
switch(event.input.key) {
|
||||||
|
case InputKeyBack:
|
||||||
|
processing = false;
|
||||||
|
break;
|
||||||
|
case InputKeyRight:
|
||||||
|
if(xPaddle < FLIPPER_LCD_WIDTH - 12) {
|
||||||
|
xPaddle += 8;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case InputKeyLeft:
|
||||||
|
if(xPaddle > 0) {
|
||||||
|
xPaddle -= 8;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case InputKeyUp:
|
||||||
|
break;
|
||||||
|
case InputKeyDown:
|
||||||
|
break;
|
||||||
|
case InputKeyOk:
|
||||||
|
//Release ball if FIRE pressed
|
||||||
|
released = true;
|
||||||
|
|
||||||
|
//Apply random direction to ball on release
|
||||||
|
if(rand_range(0, 2) == 0) {
|
||||||
|
dx = 1;
|
||||||
|
} else {
|
||||||
|
dx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Makes sure the ball heads upwards
|
||||||
|
dy = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Event timeout
|
||||||
|
FURI_LOG_D(TAG, "osMessageQueue: Event timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
view_port_update(view_port);
|
||||||
|
release_mutex(&state_mutex, arkanoid_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
view_port_enabled_set(view_port, false);
|
||||||
|
gui_remove_view_port(gui, view_port);
|
||||||
|
furi_record_close("gui");
|
||||||
|
view_port_free(view_port);
|
||||||
|
osMessageQueueDelete(event_queue);
|
||||||
|
furi_record_close("notification");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -253,7 +253,7 @@ MusicPlayer* music_player_alloc() {
|
|||||||
instance->model = malloc(sizeof(MusicPlayerModel));
|
instance->model = malloc(sizeof(MusicPlayerModel));
|
||||||
memset(instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
|
memset(instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
|
||||||
memset(instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
|
memset(instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE);
|
||||||
instance->model->volume = 3;
|
instance->model->volume = 1;
|
||||||
|
|
||||||
instance->model_mutex = osMutexNew(NULL);
|
instance->model_mutex = osMutexNew(NULL);
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ static int32_t music_player_worker_thread_callback(void* context) {
|
|||||||
furi_hal_speaker_stop();
|
furi_hal_speaker_stop();
|
||||||
furi_hal_speaker_start(frequency, volume);
|
furi_hal_speaker_start(frequency, volume);
|
||||||
while(instance->should_work && furi_hal_get_tick() < next_tick) {
|
while(instance->should_work && furi_hal_get_tick() < next_tick) {
|
||||||
volume *= 0.9945679;
|
volume *= 1.0000000;
|
||||||
furi_hal_speaker_set_volume(volume);
|
furi_hal_speaker_set_volume(volume);
|
||||||
furi_hal_delay_ms(2);
|
furi_hal_delay_ms(2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) {
|
|||||||
canvas_draw_box(canvas, p.x, p.y, 4, 4);
|
canvas_draw_box(canvas, p.x, p.y, 4, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show score on the game field
|
||||||
|
char buffer2[6];
|
||||||
|
snprintf(buffer2, sizeof(buffer2), "%u", snake_state->len - 7);
|
||||||
|
canvas_draw_str_aligned(canvas, 126, 8, AlignRight, AlignBottom, buffer2);
|
||||||
|
|
||||||
// Game Over banner
|
// Game Over banner
|
||||||
if(snake_state->state == GameStateGameOver) {
|
if(snake_state->state == GameStateGameOver) {
|
||||||
// Screen is 128x64 px
|
// Screen is 128x64 px
|
||||||
|
|||||||
@@ -150,6 +150,11 @@ static void tetris_game_render_callback(Canvas* const canvas, void* ctx) {
|
|||||||
tetris_game_draw_border(canvas);
|
tetris_game_draw_border(canvas);
|
||||||
tetris_game_draw_playfield(canvas, tetris_state);
|
tetris_game_draw_playfield(canvas, tetris_state);
|
||||||
|
|
||||||
|
// Show score on the game field
|
||||||
|
char buffer2[6];
|
||||||
|
snprintf(buffer2, sizeof(buffer2), "%u", tetris_state->numLines);
|
||||||
|
canvas_draw_str_aligned(canvas, 61, 8, AlignRight, AlignBottom, buffer2);
|
||||||
|
|
||||||
if(tetris_state->gameState == GameStateGameOver) {
|
if(tetris_state->gameState == GameStateGameOver) {
|
||||||
// 128 x 64
|
// 128 x 64
|
||||||
canvas_set_color(canvas, ColorWhite);
|
canvas_set_color(canvas, ColorWhite);
|
||||||
@@ -476,4 +481,4 @@ int32_t tetris_game_app() {
|
|||||||
free(tetris_state);
|
free(tetris_state);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
349
applications/tictactoe_game/tictactoe_game.c
Normal file
349
applications/tictactoe_game/tictactoe_game.c
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
#include <furi.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <notification/notification_messages.h>
|
||||||
|
#include <gui/view.h>
|
||||||
|
|
||||||
|
#define TAG "TicTacToe"
|
||||||
|
|
||||||
|
uint8_t selBoxX;
|
||||||
|
uint8_t selBoxY;
|
||||||
|
|
||||||
|
uint8_t selX = 2;
|
||||||
|
uint8_t selY = 2;
|
||||||
|
|
||||||
|
uint16_t scoreX;
|
||||||
|
uint16_t scoreO;
|
||||||
|
|
||||||
|
char player = 'X';
|
||||||
|
|
||||||
|
char field[3][3];
|
||||||
|
bool fieldx[3][3];
|
||||||
|
|
||||||
|
const uint8_t coords[3] = {6, 27, 48};
|
||||||
|
|
||||||
|
bool button_state = false;
|
||||||
|
|
||||||
|
typedef enum { EventTypeTick, EventTypeKey } EventType;
|
||||||
|
|
||||||
|
typedef enum { DirectionUp, DirectionRight, DirectionDown, DirectionLeft } Direction;
|
||||||
|
|
||||||
|
typedef enum { GameStatePlaying, GameStateGameOver } GameState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GameState game_state;
|
||||||
|
osTimerId_t timer;
|
||||||
|
} TicTacToeState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
EventType type;
|
||||||
|
InputEvent input;
|
||||||
|
} GameEvent;
|
||||||
|
|
||||||
|
void drawCross(Canvas* const canvas, uint8_t x, uint8_t y) {
|
||||||
|
canvas_draw_line(canvas, x, y, x + 9, y + 9); // top left - bottom right slash
|
||||||
|
canvas_draw_line(canvas, x + 9, y, x, y + 9); // down left - top right slash
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawCircle(Canvas* const canvas, uint8_t x, uint8_t y) {
|
||||||
|
canvas_draw_circle(canvas, x + 4, y + 5, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void player_switch() {
|
||||||
|
if(player == 'O') {
|
||||||
|
player = 'X';
|
||||||
|
} else if(player == 'X') {
|
||||||
|
player = 'O';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tictactoe_draw(Canvas* canvas) {
|
||||||
|
// Draws the game field
|
||||||
|
canvas_draw_frame(canvas, 0, 0, 64, 64); // frame
|
||||||
|
canvas_draw_line(canvas, 0, 21, 63, 21); // horizontal line
|
||||||
|
canvas_draw_line(canvas, 0, 42, 63, 42); // horizontal line
|
||||||
|
canvas_draw_line(canvas, 21, 0, 21, 63); // vertical line
|
||||||
|
canvas_draw_line(canvas, 42, 0, 42, 63); // vertical line
|
||||||
|
|
||||||
|
// Draws the game field elements (X or O)
|
||||||
|
for(uint8_t i = 0; i <= 2; i++) {
|
||||||
|
for(uint8_t j = 0; j <= 2; j++) {
|
||||||
|
if(field[i][j] == 'O') {
|
||||||
|
drawCircle(canvas, coords[i], coords[j]);
|
||||||
|
} else if(field[i][j] == 'X') {
|
||||||
|
drawCross(canvas, coords[i], coords[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draws the selection box
|
||||||
|
if(selX == 1) {
|
||||||
|
selBoxX = 1;
|
||||||
|
} else if(selX == 2) {
|
||||||
|
selBoxX = 22;
|
||||||
|
} else if(selX == 3) {
|
||||||
|
selBoxX = 43;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selY == 1) {
|
||||||
|
selBoxY = 1;
|
||||||
|
} else if(selY == 2) {
|
||||||
|
selBoxY = 22;
|
||||||
|
} else if(selY == 3) {
|
||||||
|
selBoxY = 43;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas_set_color(canvas, ColorBlack);
|
||||||
|
canvas_draw_frame(canvas, selBoxX, selBoxY, 20, 20);
|
||||||
|
canvas_draw_frame(canvas, selBoxX + 1, selBoxY + 1, 18, 18);
|
||||||
|
|
||||||
|
// Draws the sidebar
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str(canvas, 81, 10, "SCORE");
|
||||||
|
canvas_draw_str(canvas, 75, 24, "X:");
|
||||||
|
|
||||||
|
char scoreXBuffer[10];
|
||||||
|
sprintf(scoreXBuffer, "%d", scoreX);
|
||||||
|
canvas_draw_str(canvas, 88, 24, scoreXBuffer);
|
||||||
|
canvas_draw_str(canvas, 75, 35, "O:");
|
||||||
|
|
||||||
|
char scoreOBuffer[10];
|
||||||
|
sprintf(scoreOBuffer, "%d", scoreO);
|
||||||
|
canvas_draw_str(canvas, 88, 35, scoreOBuffer);
|
||||||
|
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str(canvas, 75, 46, "Player:");
|
||||||
|
|
||||||
|
if(player == 'X') {
|
||||||
|
drawCross(canvas, 93, 50);
|
||||||
|
} else if(player == 'O') {
|
||||||
|
drawCircle(canvas, 93, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_game_field() {
|
||||||
|
// Clears the game field arrays
|
||||||
|
for(uint8_t i = 0; i <= 2; i++) {
|
||||||
|
for(uint8_t j = 0; j <= 2; j++) {
|
||||||
|
field[i][j] = ' ';
|
||||||
|
fieldx[i][j] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selX = 2; // Centers the selection box on X axis
|
||||||
|
selY = 2; // Centers the selection box on Y axis
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_game_data() {
|
||||||
|
scoreO = 0;
|
||||||
|
scoreX = 0;
|
||||||
|
player = 'X';
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_win(Canvas* canvas, char player) {
|
||||||
|
// Handles the score table
|
||||||
|
if(player == 'X') {
|
||||||
|
scoreX++;
|
||||||
|
} else if(player == 'O') {
|
||||||
|
scoreO++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switches the players
|
||||||
|
player_switch();
|
||||||
|
|
||||||
|
// Draws the board with players switched
|
||||||
|
tictactoe_draw(canvas);
|
||||||
|
|
||||||
|
// Clear the game field
|
||||||
|
clear_game_field();
|
||||||
|
|
||||||
|
// Draw the new board
|
||||||
|
tictactoe_draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tictactoe_state_init(TicTacToeState* const tictactoe_state) {
|
||||||
|
// Set the initial game state
|
||||||
|
tictactoe_state->game_state = GameStatePlaying;
|
||||||
|
|
||||||
|
clear_game_field();
|
||||||
|
|
||||||
|
reset_game_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tictactoe_draw_callback(Canvas* const canvas, void* ctx) {
|
||||||
|
const TicTacToeState* tictactoe_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||||
|
if(tictactoe_state == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selX > 3) {
|
||||||
|
selX = 3;
|
||||||
|
} else if(selX < 1) {
|
||||||
|
selX = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(selY > 3) {
|
||||||
|
selY = 3;
|
||||||
|
} else if(selY < 1) {
|
||||||
|
selY = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assigns the game field elements their value (X or O) when the OK button is pressed
|
||||||
|
if(button_state) {
|
||||||
|
button_state = false;
|
||||||
|
for(uint8_t i = 0; i <= 2; i++) {
|
||||||
|
for(uint8_t j = 0; j <= 2; j++) {
|
||||||
|
if((selX == i + 1) && (selY == j + 1) && (fieldx[i][j] == false)) {
|
||||||
|
if(player == 'X') {
|
||||||
|
field[i][j] = 'X';
|
||||||
|
fieldx[i][j] = true;
|
||||||
|
player_switch();
|
||||||
|
} else if(player == 'O') {
|
||||||
|
field[i][j] = 'O';
|
||||||
|
fieldx[i][j] = true;
|
||||||
|
player_switch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the game field for winning combinations
|
||||||
|
if((field[0][0] == 'X') && (field[1][0] == 'X') && (field[2][0] == 'X')) {
|
||||||
|
draw_win(canvas, 'X');
|
||||||
|
} else if((field[0][1] == 'X') && (field[1][1] == 'X') && (field[2][1] == 'X')) {
|
||||||
|
draw_win(canvas, 'X');
|
||||||
|
} else if((field[0][2] == 'X') && (field[1][2] == 'X') && (field[2][2] == 'X')) {
|
||||||
|
draw_win(canvas, 'X');
|
||||||
|
} else if((field[0][0] == 'X') && (field[0][1] == 'X') && (field[0][2] == 'X')) {
|
||||||
|
draw_win(canvas, 'X');
|
||||||
|
} else if((field[1][0] == 'X') && (field[1][1] == 'X') && (field[1][2] == 'X')) {
|
||||||
|
draw_win(canvas, 'X');
|
||||||
|
} else if((field[2][0] == 'X') && (field[2][1] == 'X') && (field[2][2] == 'X')) {
|
||||||
|
draw_win(canvas, 'X');
|
||||||
|
} else if((field[0][0] == 'X') && (field[1][1] == 'X') && (field[2][2] == 'X')) {
|
||||||
|
draw_win(canvas, 'X');
|
||||||
|
} else if((field[2][0] == 'X') && (field[1][1] == 'X') && (field[0][2] == 'X')) {
|
||||||
|
draw_win(canvas, 'X');
|
||||||
|
} else if((field[0][0] == 'O') && (field[1][0] == 'O') && (field[2][0] == 'O')) {
|
||||||
|
draw_win(canvas, 'O');
|
||||||
|
} else if((field[0][1] == 'O') && (field[1][1] == 'O') && (field[2][1] == 'O')) {
|
||||||
|
draw_win(canvas, 'O');
|
||||||
|
} else if((field[0][2] == 'O') && (field[1][2] == 'O') && (field[2][2] == 'O')) {
|
||||||
|
draw_win(canvas, 'O');
|
||||||
|
} else if((field[0][0] == 'O') && (field[0][1] == 'O') && (field[0][2] == 'O')) {
|
||||||
|
draw_win(canvas, 'O');
|
||||||
|
} else if((field[1][0] == 'O') && (field[1][1] == 'O') && (field[1][2] == 'O')) {
|
||||||
|
draw_win(canvas, 'O');
|
||||||
|
} else if((field[2][0] == 'O') && (field[2][1] == 'O') && (field[2][2] == 'O')) {
|
||||||
|
draw_win(canvas, 'O');
|
||||||
|
} else if((field[0][0] == 'O') && (field[1][1] == 'O') && (field[2][2] == 'O')) {
|
||||||
|
draw_win(canvas, 'O');
|
||||||
|
} else if((field[2][0] == 'O') && (field[1][1] == 'O') && (field[0][2] == 'O')) {
|
||||||
|
draw_win(canvas, 'O');
|
||||||
|
} else if(
|
||||||
|
(fieldx[0][0] == true) && (fieldx[0][1] == true) && (fieldx[0][2] == true) &&
|
||||||
|
(fieldx[1][0] == true) && (fieldx[1][1] == true) && (fieldx[1][2] == true) &&
|
||||||
|
(fieldx[2][0] == true) && (fieldx[2][1] == true) && (fieldx[2][2] == true)) {
|
||||||
|
draw_win(canvas, 'T');
|
||||||
|
}
|
||||||
|
|
||||||
|
tictactoe_draw(canvas);
|
||||||
|
|
||||||
|
release_mutex((ValueMutex*)ctx, tictactoe_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tictactoe_input_callback(InputEvent* input_event, osMessageQueueId_t event_queue) {
|
||||||
|
furi_assert(event_queue);
|
||||||
|
|
||||||
|
GameEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||||
|
osMessageQueuePut(event_queue, &event, 0, osWaitForever);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tictactoe_update_timer_callback(osMessageQueueId_t event_queue) {
|
||||||
|
furi_assert(event_queue);
|
||||||
|
|
||||||
|
GameEvent event = {.type = EventTypeTick};
|
||||||
|
osMessageQueuePut(event_queue, &event, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t tictactoe_game_app(void* p) {
|
||||||
|
osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(GameEvent), NULL);
|
||||||
|
|
||||||
|
TicTacToeState* tictactoe_state = malloc(sizeof(TicTacToeState));
|
||||||
|
|
||||||
|
ValueMutex state_mutex;
|
||||||
|
if(!init_mutex(&state_mutex, tictactoe_state, sizeof(TicTacToeState))) {
|
||||||
|
FURI_LOG_E(TAG, "Cannot create mutex\r\n");
|
||||||
|
free(tictactoe_state);
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set system callbacks
|
||||||
|
ViewPort* view_port = view_port_alloc();
|
||||||
|
view_port_draw_callback_set(view_port, tictactoe_draw_callback, &state_mutex);
|
||||||
|
view_port_input_callback_set(view_port, tictactoe_input_callback, event_queue);
|
||||||
|
|
||||||
|
tictactoe_state->timer =
|
||||||
|
osTimerNew(tictactoe_update_timer_callback, osTimerPeriodic, event_queue, NULL);
|
||||||
|
osTimerStart(tictactoe_state->timer, osKernelGetTickFreq() / 22);
|
||||||
|
|
||||||
|
tictactoe_state_init(tictactoe_state);
|
||||||
|
|
||||||
|
// Open GUI and register view_port
|
||||||
|
Gui* gui = furi_record_open("gui");
|
||||||
|
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||||
|
|
||||||
|
GameEvent event;
|
||||||
|
for(bool processing = true; processing;) {
|
||||||
|
osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 100);
|
||||||
|
TicTacToeState* tictactoe_state = (TicTacToeState*)acquire_mutex_block(&state_mutex);
|
||||||
|
|
||||||
|
if(event_status == osOK) {
|
||||||
|
// Key events
|
||||||
|
if(event.type == EventTypeKey) {
|
||||||
|
if(event.input.type == InputTypePress) {
|
||||||
|
switch(event.input.key) {
|
||||||
|
case InputKeyBack:
|
||||||
|
processing = false;
|
||||||
|
break;
|
||||||
|
case InputKeyRight:
|
||||||
|
selX++;
|
||||||
|
break;
|
||||||
|
case InputKeyLeft:
|
||||||
|
selX--;
|
||||||
|
break;
|
||||||
|
case InputKeyUp:
|
||||||
|
selY--;
|
||||||
|
break;
|
||||||
|
case InputKeyDown:
|
||||||
|
selY++;
|
||||||
|
break;
|
||||||
|
case InputKeyOk:
|
||||||
|
button_state = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Event timeout
|
||||||
|
FURI_LOG_D(TAG, "osMessageQueue: Event timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
view_port_update(view_port);
|
||||||
|
release_mutex(&state_mutex, tictactoe_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
osTimerDelete(tictactoe_state->timer);
|
||||||
|
view_port_enabled_set(view_port, false);
|
||||||
|
gui_remove_view_port(gui, view_port);
|
||||||
|
furi_record_close("gui");
|
||||||
|
view_port_free(view_port);
|
||||||
|
osMessageQueueDelete(event_queue);
|
||||||
|
furi_record_close("notification");
|
||||||
|
delete_mutex(&state_mutex);
|
||||||
|
free(tictactoe_state);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user