1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 12:42:30 +04:00

make arkanoid (not) great again

and small changes for other games
This commit is contained in:
MX
2022-08-07 09:59:03 +03:00
parent 2210b6b3aa
commit 044338b8f2
9 changed files with 294 additions and 365 deletions

91
.github/CODEOWNERS vendored
View File

@@ -1,91 +0,0 @@
# Who owns all the fish by default
* @skotopes @DrZlo13 @hedger
# Apps
/applications/about/ @skotopes @DrZlo13 @hedger
/applications/accessor/ @skotopes @DrZlo13 @hedger
/applications/archive/ @skotopes @DrZlo13 @hedger @nminaylov
/applications/bad_usb/ @skotopes @DrZlo13 @hedger @nminaylov
/applications/bt/ @skotopes @DrZlo13 @hedger @gornekich
/applications/cli/ @skotopes @DrZlo13 @hedger @nminaylov
/applications/crypto/ @skotopes @DrZlo13 @hedger @nminaylov
/applications/debug_tools/ @skotopes @DrZlo13 @hedger
/applications/desktop/ @skotopes @DrZlo13 @hedger @nminaylov
/applications/dialogs/ @skotopes @DrZlo13 @hedger
/applications/dolphin/ @skotopes @DrZlo13 @hedger
/applications/gpio/ @skotopes @DrZlo13 @hedger @nminaylov
/applications/gui/ @skotopes @DrZlo13 @hedger
/applications/ibutton/ @skotopes @DrZlo13 @hedger @gsurkov
/applications/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
/applications/infrared_monitor/ @skotopes @DrZlo13 @hedger @gsurkov
/applications/input/ @skotopes @DrZlo13 @hedger
/applications/lfrfid/ @skotopes @DrZlo13 @hedger
/applications/lfrfid_debug/ @skotopes @DrZlo13 @hedger
/applications/loader/ @skotopes @DrZlo13 @hedger
/applications/music_player/ @skotopes @DrZlo13 @hedger
/applications/nfc/ @skotopes @DrZlo13 @hedger @gornekich
/applications/notification/ @skotopes @DrZlo13 @hedger
/applications/power/ @skotopes @DrZlo13 @hedger
/applications/rpc/ @skotopes @DrZlo13 @hedger @nminaylov
/applications/snake_game/ @skotopes @DrZlo13 @hedger
/applications/storage/ @skotopes @DrZlo13 @hedger
/applications/storage_settings/ @skotopes @DrZlo13 @hedger
/applications/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm
/applications/system/ @skotopes @DrZlo13 @hedger
/applications/u2f/ @skotopes @DrZlo13 @hedger @nminaylov
/applications/unit_tests/ @skotopes @DrZlo13 @hedger
/applications/updater/ @skotopes @DrZlo13 @hedger
# Assets
/assets/ @skotopes @DrZlo13 @hedger
# Furi Core
/furi/ @skotopes @DrZlo13 @hedger
# Debug tools and plugins
/debug/ @skotopes @DrZlo13 @hedger
# Docker
/docker/ @skotopes @DrZlo13 @hedger @aprosvetova
/docker-compose.yml @skotopes @DrZlo13 @hedger @aprosvetova
# Documentation
/documentation/ @skotopes @DrZlo13 @hedger @aprosvetova
# Firmware targets
/firmware/ @skotopes @DrZlo13 @hedger
# Lib
/lib/FreeRTOS-Kernel/ @skotopes @DrZlo13 @hedger
/lib/FreeRTOS-glue/ @skotopes @DrZlo13 @hedger
/lib/ST25RFAL002/ @skotopes @DrZlo13 @hedger @gornekich
/lib/STM32CubeWB/ @skotopes @DrZlo13 @hedger @gornekich
/lib/app-scened-template/ @skotopes @DrZlo13 @hedger
/lib/callback-connector/ @skotopes @DrZlo13 @hedger
/lib/digital_signal/ @skotopes @DrZlo13 @hedger @gornekich
/lib/drivers/ @skotopes @DrZlo13 @hedger
/lib/fatfs/ @skotopes @DrZlo13 @hedger
/lib/flipper_format/ @skotopes @DrZlo13 @hedger
/lib/fnv1a-hash/ @skotopes @DrZlo13 @hedger
/lib/heatshrink/ @skotopes @DrZlo13 @hedger
/lib/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
/lib/libusb_stm32/ @skotopes @DrZlo13 @hedger @nminaylov
/lib/littlefs/ @skotopes @DrZlo13 @hedger
/lib/lfs_config.h @skotopes @DrZlo13 @hedger
/lib/micro-ecc/ @skotopes @DrZlo13 @hedger @nminaylov
/lib/microtar/ @skotopes @DrZlo13 @hedger
/lib/mlib/ @skotopes @DrZlo13 @hedger
/lib/nanopb/ @skotopes @DrZlo13 @hedger
/lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich
/lib/one_wire/ @skotopes @DrZlo13 @hedger
/lib/qrcode/ @skotopes @DrZlo13 @hedger
/lib/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm
/lib/toolbox/ @skotopes @DrZlo13 @hedger
/lib/u8g2/ @skotopes @DrZlo13 @hedger
/lib/update_util/ @skotopes @DrZlo13 @hedger
# Make tools
/make/ @skotopes @DrZlo13 @hedger @aprosvetova
# Helper scripts
/scripts/ @skotopes @DrZlo13 @hedger

View File

@@ -1,20 +1,19 @@
name: Bug report name: Bug report
description: File a bug reports regarding the firmware. description: File a bug reports regarding the firmware.
labels: ['bug'] labels: ["bug"]
body: body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Flipper Zero firmware. Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Flipper Zero unleashed firmware.
If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one) - type: textarea
- type: textarea
id: description id: description
attributes: attributes:
label: Describe the bug. label: Describe the bug.
description: "A clear and concise description of what the bug is." description: "A clear and concise description of what the bug is."
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: repro id: repro
attributes: attributes:
label: Reproduction label: Reproduction
@@ -26,20 +25,20 @@ body:
4. It burns 4. It burns
validations: validations:
required: true required: true
- type: input - type: input
id: target id: target
attributes: attributes:
label: Target label: Target
description: Specify the target description: Specify the target
# Target seems to be largely ignored by outside sources. # Target seems to be largely ignored by outside sources.
- type: textarea - type: textarea
id: logs id: logs
attributes: attributes:
label: Logs label: Logs
description: Attach your debug logs here description: Attach your debug logs here
render: Text render: Text
# Avoid rendering as Markdown here. # Avoid rendering as Markdown here.
- type: textarea - type: textarea
id: anything-else id: anything-else
attributes: attributes:
label: Anything else? label: Anything else?

View File

@@ -1,12 +1,11 @@
name: Enhancements name: Enhancements
description: Suggest improvements for any existing functionality within the firmware. description: Suggest improvements for any existing functionality within the firmware.
body: body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
Thank you for taking the time to fill out an issue. This template is meant for feature requests and improvements to already existing functionality. Thank you for taking the time to fill out an issue. This template is meant for feature requests and improvements to already existing functionality.
If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one) - type: textarea
- type: textarea
id: proposal id: proposal
attributes: attributes:
label: "Describe the enhancement you're suggesting." label: "Describe the enhancement you're suggesting."
@@ -14,7 +13,7 @@ body:
Feel free to describe in as much detail as you wish. Feel free to describe in as much detail as you wish.
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: anything-else id: anything-else
attributes: attributes:
label: Anything else? label: Anything else?

View File

@@ -1,13 +1,12 @@
name: Feature Request name: Feature Request
description: For feature requests regarding the firmware. description: For feature requests regarding the firmware.
labels: ['feature request'] labels: ["feature request"]
body: body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
Thank you for taking the time to fill out an issue, this template is meant for any feature suggestions. Thank you for taking the time to fill out an issue, this template is meant for any feature suggestions.
If you require help with the Flipper zero and its firmware, we ask that you join [our forum](https://forum.flipperzero.one) - type: textarea
- type: textarea
id: proposal id: proposal
attributes: attributes:
label: "Description of the feature you're suggesting." label: "Description of the feature you're suggesting."
@@ -17,7 +16,7 @@ body:
- Note whetever it is to extend existing functionality or introduce new functionality. - Note whetever it is to extend existing functionality or introduce new functionality.
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: anything-else id: anything-else
attributes: attributes:
label: Anything else? label: Anything else?

View File

@@ -1,5 +1,8 @@
blank_issues_enabled: true blank_issues_enabled: true
contact_links: contact_links:
- name: Need help? - name: Telegram
url: https://forum.flipperzero.one url: https://t.me/flipperzero_unofficial
about: For any question regarding on how to use the Flipper Zero and its firmware. about: Unofficial Telegram chat
- name: Discord
url: https://discord.gg/58D6E8BtTU
about: Unofficial Discord Community

View File

@@ -5,6 +5,6 @@ App(
entry_point="arkanoid_game_app", entry_point="arkanoid_game_app",
cdefines=["APP_ARKANOID_GAME"], cdefines=["APP_ARKANOID_GAME"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=3 * 1024,
order=30, order=30,
) )

View File

@@ -3,61 +3,53 @@
#include <input/input.h> #include <input/input.h>
#include <stdlib.h> #include <stdlib.h>
#include <gui/view.h> #include <gui/view.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#define TAG "Arkanoid" #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 pad1, 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_WIDTH 128
#define FLIPPER_LCD_HEIGHT 64 #define FLIPPER_LCD_HEIGHT 64
typedef enum { EventTypeTick, EventTypeKey } EventType; typedef enum { EventTypeTick, EventTypeKey } EventType;
typedef enum { DirectionUp, DirectionRight, DirectionDown, DirectionLeft } Direction; typedef struct {
//Brick Bounds used in collision detection
typedef enum { GameStatePlaying, GameStateGameOver } GameState; int leftBrick;
int rightBrick;
int topBrick;
int bottomBrick;
bool isHit[4][13]; //Array of if bricks are hit or not
} BrickState;
typedef struct { typedef struct {
int x; int dx; //Initial movement of ball
int y; int dy; //Initial movement of ball
} Point; int xb; //Balls starting possition
int yb; //Balls starting possition
bool released; //If the ball has been released by the player
//Ball Bounds used in collision detection
int leftBall;
int rightBall;
int topBall;
int bottomBall;
} BallState;
typedef struct { typedef struct {
GameState game_state; BallState ball_state;
BrickState brick_state;
NotificationApp* notify;
unsigned int COLUMNS; //Columns of bricks
unsigned int ROWS; //Rows of bricks
bool initialDraw; //If the inital draw has happened
int xPaddle; //X position of paddle
char text[16]; //General string buffer
bool bounced; //Used to fix double bounce glitch
int lives; //Amount of lives
int level; //Current level
unsigned int score; //Score for the game
unsigned int brickCount; //Amount of bricks hit
int tick; //Tick counter
} ArkanoidState; } ArkanoidState;
typedef struct { typedef struct {
@@ -65,128 +57,128 @@ typedef struct {
InputEvent input; InputEvent input;
} GameEvent; } GameEvent;
static const NotificationSequence sequence_short_sound = {
&message_note_c5,
&message_delay_50,
&message_sound_off,
NULL,
};
// generate number in range [min,max) // generate number in range [min,max)
int rand_range(int min, int max) { int rand_range(int min, int max) {
int number = min + rand() % (max - min); int number = min + rand() % (max - min);
return number; return number;
} }
void intro(Canvas* canvas) { void move_ball(Canvas* canvas, ArkanoidState* st) {
canvas_set_font(canvas, FontPrimary); st->tick++;
canvas_draw_str(canvas, 46, 0, "Arkanoid"); if(st->ball_state.released) {
//arduboy.tunes.tone(987, 160);
//delay(160);
//arduboy.tunes.tone(1318, 400);
//delay(2000);
}
void move_ball(Canvas* canvas) {
tick++;
if(released) {
//Move ball //Move ball
if(abs(dx) == 2) { if(abs(st->ball_state.dx) == 2) {
xb += dx / 2; st->ball_state.xb += st->ball_state.dx / 2;
// 2x speed is really 1.5 speed // 2x speed is really 1.5 speed
if(tick % 2 == 0) xb += dx / 2; if(st->tick % 2 == 0) st->ball_state.xb += st->ball_state.dx / 2;
} else { } else {
xb += dx; st->ball_state.xb += st->ball_state.dx;
} }
yb = yb + dy; st->ball_state.yb = st->ball_state.yb + st->ball_state.dy;
//Set bounds //Set bounds
leftBall = xb; st->ball_state.leftBall = st->ball_state.xb;
rightBall = xb + 2; st->ball_state.rightBall = st->ball_state.xb + 2;
topBall = yb; st->ball_state.topBall = st->ball_state.yb;
bottomBall = yb + 2; st->ball_state.bottomBall = st->ball_state.yb + 2;
//Bounce off top edge //Bounce off top edge
if(yb <= 0) { if(st->ball_state.yb <= 0) {
yb = 2; st->ball_state.yb = 2;
dy = -dy; st->ball_state.dy = -st->ball_state.dy;
// arduboy.tunes.tone(523, 250);
} }
//Lose a life if bottom edge hit //Lose a life if bottom edge hit
if(yb >= FLIPPER_LCD_HEIGHT) { if(st->ball_state.yb >= FLIPPER_LCD_HEIGHT) {
canvas_draw_frame(canvas, xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1); canvas_draw_frame(canvas, st->xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1);
xPaddle = 54; st->xPaddle = 54;
yb = 60; st->ball_state.yb = 60;
released = false; st->ball_state.released = false;
lives--; st->lives--;
// arduboy.tunes.tone(175, 250);
if(rand_range(0, 2) == 0) { if(rand_range(0, 2) == 0) {
dx = 1; st->ball_state.dx = 1;
} else { } else {
dx = -1; st->ball_state.dx = -1;
} }
} }
//Bounce off left side //Bounce off left side
if(xb <= 0) { if(st->ball_state.xb <= 0) {
xb = 2; st->ball_state.xb = 2;
dx = -dx; st->ball_state.dx = -st->ball_state.dx;
// arduboy.tunes.tone(523, 250);
} }
//Bounce off right side //Bounce off right side
if(xb >= FLIPPER_LCD_WIDTH - 2) { if(st->ball_state.xb >= FLIPPER_LCD_WIDTH - 2) {
xb = FLIPPER_LCD_WIDTH - 4; st->ball_state.xb = FLIPPER_LCD_WIDTH - 4;
dx = -dx; st->ball_state.dx = -st->ball_state.dx;
// arduboy.tunes.tone(523, 250); // arduboy.tunes.tone(523, 250);
} }
//Bounce off paddle //Bounce off paddle
if(xb + 1 >= xPaddle && xb <= xPaddle + 12 && yb + 2 >= FLIPPER_LCD_HEIGHT - 1 && if(st->ball_state.xb + 1 >= st->xPaddle && st->ball_state.xb <= st->xPaddle + 12 &&
yb <= FLIPPER_LCD_HEIGHT) { st->ball_state.yb + 2 >= FLIPPER_LCD_HEIGHT - 1 &&
dy = -dy; st->ball_state.yb <= FLIPPER_LCD_HEIGHT) {
dx = ((xb - (xPaddle + 6)) / 3); //Applies spin on the ball st->ball_state.dy = -st->ball_state.dy;
// prevent straight bounce st->ball_state.dx =
if(dx == 0) { ((st->ball_state.xb - (st->xPaddle + 6)) / 3); //Applies spin on the ball
dx = (rand_range(0, 2) == 1) ? 1 : -1; // prevent straight bounce, but not prevent roguuemaster from stealing
if(st->ball_state.dx == 0) {
st->ball_state.dx = (rand_range(0, 2) == 1) ? 1 : -1;
} }
// arduboy.tunes.tone(200, 250);
} }
//Bounce off Bricks //Bounce off Bricks
for(unsigned int row = 0; row < ROWS; row++) { for(unsigned int row = 0; row < st->ROWS; row++) {
for(unsigned int column = 0; column < COLUMNS; column++) { for(unsigned int column = 0; column < st->COLUMNS; column++) {
if(!isHit[row][column]) { if(!st->brick_state.isHit[row][column]) {
//Sets Brick bounds //Sets Brick bounds
leftBrick = 10 * column; st->brick_state.leftBrick = 10 * column;
rightBrick = 10 * column + 10; st->brick_state.rightBrick = 10 * column + 10;
topBrick = 6 * row + 1; st->brick_state.topBrick = 6 * row + 1;
bottomBrick = 6 * row + 7; st->brick_state.bottomBrick = 6 * row + 7;
//If A collison has occured //If A collison has occured
if(topBall <= bottomBrick && bottomBall >= topBrick && if(st->ball_state.topBall <= st->brick_state.bottomBrick &&
leftBall <= rightBrick && rightBall >= leftBrick) { st->ball_state.bottomBall >= st->brick_state.topBrick &&
score += (level * 10); st->ball_state.leftBall <= st->brick_state.rightBrick &&
st->ball_state.rightBall >= st->brick_state.leftBrick) {
st->score += st->level;
// Blink led when we hit some brick
notification_message(st->notify, &sequence_short_sound);
notification_message(st->notify, &sequence_blink_white_100);
brickCount++; st->brickCount++;
isHit[row][column] = true; st->brick_state.isHit[row][column] = true;
canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4); canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4);
//Vertical collision //Vertical collision
if(bottomBall > bottomBrick || topBall < topBrick) { if(st->ball_state.bottomBall > st->brick_state.bottomBrick ||
st->ball_state.topBall < st->brick_state.topBrick) {
//Only bounce once each ball move //Only bounce once each ball move
if(!bounced) { if(!st->bounced) {
dy = -dy; st->ball_state.dy = -st->ball_state.dy;
yb += dy; st->ball_state.yb += st->ball_state.dy;
bounced = true; st->bounced = true;
// arduboy.tunes.tone(261, 250);
} }
} }
//Hoizontal collision //Hoizontal collision
if(leftBall < leftBrick || rightBall > rightBrick) { if(st->ball_state.leftBall < st->brick_state.leftBrick ||
st->ball_state.rightBall > st->brick_state.rightBrick) {
//Only bounce once brick each ball move //Only bounce once brick each ball move
if(!bounced) { if(!st->bounced) {
dx = -dx; st->ball_state.dx = -st->ball_state.dx;
xb += dx; st->ball_state.xb += st->ball_state.dx;
bounced = true; st->bounced = true;
// arduboy.tunes.tone(261, 250);
} }
} }
} }
@@ -195,15 +187,15 @@ void move_ball(Canvas* canvas) {
} }
//Reset Bounce //Reset Bounce
bounced = false; st->bounced = false;
} else { } else {
//Ball follows paddle //Ball follows paddle
xb = xPaddle + 5; st->ball_state.xb = st->xPaddle + 5;
} }
} }
void draw_lives(Canvas* canvas) { void draw_lives(Canvas* canvas, ArkanoidState* arkanoid_state) {
if(lives == 3) { if(arkanoid_state->lives == 3) {
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7); canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7); canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8); canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8);
@@ -218,7 +210,7 @@ void draw_lives(Canvas* canvas) {
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 15); canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 15);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 16); canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 16);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 16); canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 16);
} else if(lives == 2) { } else if(arkanoid_state->lives == 2) {
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7); canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7); canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8); canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8);
@@ -236,98 +228,115 @@ void draw_lives(Canvas* canvas) {
} }
} }
void draw_score(Canvas* canvas) { void draw_score(Canvas* canvas, ArkanoidState* arkanoid_state) {
snprintf(text, sizeof(text), "%u", score); snprintf(arkanoid_state->text, sizeof(arkanoid_state->text), "%u", arkanoid_state->score);
canvas_draw_str_aligned(canvas, FLIPPER_LCD_WIDTH - 2, FLIPPER_LCD_HEIGHT - 6, AlignRight, AlignBottom, text); canvas_draw_str_aligned(
canvas,
FLIPPER_LCD_WIDTH - 2,
FLIPPER_LCD_HEIGHT - 6,
AlignRight,
AlignBottom,
arkanoid_state->text);
} }
void draw_ball(Canvas* canvas) { void draw_ball(Canvas* canvas, ArkanoidState* ast) {
canvas_draw_dot(canvas, xb, yb); canvas_draw_dot(canvas, ast->ball_state.xb, ast->ball_state.yb);
canvas_draw_dot(canvas, xb + 1, yb); canvas_draw_dot(canvas, ast->ball_state.xb + 1, ast->ball_state.yb);
canvas_draw_dot(canvas, xb, yb + 1); canvas_draw_dot(canvas, ast->ball_state.xb, ast->ball_state.yb + 1);
canvas_draw_dot(canvas, xb + 1, yb + 1); canvas_draw_dot(canvas, ast->ball_state.xb + 1, ast->ball_state.yb + 1);
move_ball(canvas); move_ball(canvas, ast);
} }
void draw_paddle(Canvas* canvas) { void draw_paddle(Canvas* canvas, ArkanoidState* arkanoid_state) {
canvas_draw_frame(canvas, xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1); canvas_draw_frame(canvas, arkanoid_state->xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1);
} }
void reset_level(Canvas* canvas) { void reset_level(Canvas* canvas, ArkanoidState* arkanoid_state) {
//Undraw paddle //Undraw paddle
canvas_draw_frame(canvas, xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1); canvas_draw_frame(canvas, arkanoid_state->xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1);
//Undraw ball //Undraw ball
canvas_draw_dot(canvas, xb, yb); canvas_draw_dot(canvas, arkanoid_state->ball_state.xb, arkanoid_state->ball_state.yb);
canvas_draw_dot(canvas, xb + 1, yb); canvas_draw_dot(canvas, arkanoid_state->ball_state.xb + 1, arkanoid_state->ball_state.yb);
canvas_draw_dot(canvas, xb, yb + 1); canvas_draw_dot(canvas, arkanoid_state->ball_state.xb, arkanoid_state->ball_state.yb + 1);
canvas_draw_dot(canvas, xb + 1, yb + 1); canvas_draw_dot(canvas, arkanoid_state->ball_state.xb + 1, arkanoid_state->ball_state.yb + 1);
//Alter various variables to reset the game //Alter various variables to reset the game
xPaddle = 54; arkanoid_state->xPaddle = 54;
yb = 60; arkanoid_state->ball_state.yb = 60;
brickCount = 0; arkanoid_state->brickCount = 0;
released = false; arkanoid_state->ball_state.released = false;
// Reset all brick hit states // Reset all brick hit states
for(unsigned int row = 0; row < ROWS; row++) { for(unsigned int row = 0; row < arkanoid_state->ROWS; row++) {
for(unsigned int column = 0; column < COLUMNS; column++) { for(unsigned int column = 0; column < arkanoid_state->COLUMNS; column++) {
isHit[row][column] = false; arkanoid_state->brick_state.isHit[row][column] = false;
} }
} }
} }
static void arkanoid_state_init(ArkanoidState* const arkanoid_state) { static void arkanoid_state_init(ArkanoidState* arkanoid_state) {
// Init notification
arkanoid_state->notify = furi_record_open(RECORD_NOTIFICATION);
// Set the initial game state // Set the initial game state
arkanoid_state->game_state = GameStatePlaying; arkanoid_state->COLUMNS = 13;
arkanoid_state->ROWS = 4;
arkanoid_state->ball_state.dx = -1;
arkanoid_state->ball_state.dy = -1;
arkanoid_state->bounced = false;
arkanoid_state->lives = 3;
arkanoid_state->level = 1;
arkanoid_state->score = 0;
arkanoid_state->COLUMNS = 13;
arkanoid_state->COLUMNS = 13;
// Reset initial state // Reset initial state
initialDraw = false; arkanoid_state->initialDraw = false;
} }
static void arkanoid_draw_callback(Canvas* const canvas, void* ctx) { static void arkanoid_draw_callback(Canvas* const canvas, void* ctx) {
const ArkanoidState* arkanoid_state = acquire_mutex((ValueMutex*)ctx, 25); ArkanoidState* arkanoid_state = acquire_mutex((ValueMutex*)ctx, 25);
if(arkanoid_state == NULL) { if(arkanoid_state == NULL) {
return; return;
} }
//Initial level draw //Initial level draw
if(!initialDraw) { if(!arkanoid_state->initialDraw) {
initialDraw = true; arkanoid_state->initialDraw = true;
// Set default font for text // Set default font for text
canvas_set_font(canvas, FontPrimary); canvas_set_font(canvas, FontSecondary);
//Draws the new level //Draws the new level
reset_level(canvas); reset_level(canvas, arkanoid_state);
} }
//Draws new bricks and resets their values //Draws new bricks and resets their values
for(unsigned int row = 0; row < ROWS; row++) { for(unsigned int row = 0; row < arkanoid_state->ROWS; row++) {
for(unsigned int column = 0; column < COLUMNS; column++) { for(unsigned int column = 0; column < arkanoid_state->COLUMNS; column++) {
if(!isHit[row][column]) { if(!arkanoid_state->brick_state.isHit[row][column]) {
canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4); canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4);
} }
} }
} }
if(lives > 0) { if(arkanoid_state->lives > 0) {
draw_paddle(canvas); draw_paddle(canvas, arkanoid_state);
draw_ball(canvas); draw_ball(canvas, arkanoid_state);
draw_score(canvas); draw_score(canvas, arkanoid_state);
draw_lives(canvas); draw_lives(canvas, arkanoid_state);
if(brickCount == ROWS * COLUMNS) { if(arkanoid_state->brickCount == arkanoid_state->ROWS * arkanoid_state->COLUMNS) {
level++; arkanoid_state->level++;
reset_level(canvas); reset_level(canvas, arkanoid_state);
} }
} else { } else {
reset_level(canvas); reset_level(canvas, arkanoid_state);
initialDraw = false; arkanoid_state->initialDraw = false;
start = false; arkanoid_state->lives = 3;
lives = 3; arkanoid_state->score = 0;
score = 0;
} }
release_mutex((ValueMutex*)ctx, arkanoid_state); release_mutex((ValueMutex*)ctx, arkanoid_state);
@@ -375,7 +384,7 @@ int32_t arkanoid_game_app(void* p) {
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 22); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 22);
// Open GUI and register view_port // Open GUI and register view_port
Gui* gui = furi_record_open("gui"); Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen); gui_add_view_port(gui, view_port, GuiLayerFullscreen);
GameEvent event; GameEvent event;
@@ -393,13 +402,13 @@ int32_t arkanoid_game_app(void* p) {
processing = false; processing = false;
break; break;
case InputKeyRight: case InputKeyRight:
if(xPaddle < FLIPPER_LCD_WIDTH - 12) { if(arkanoid_state->xPaddle < FLIPPER_LCD_WIDTH - 12) {
xPaddle += 8; arkanoid_state->xPaddle += 8;
} }
break; break;
case InputKeyLeft: case InputKeyLeft:
if(xPaddle > 0) { if(arkanoid_state->xPaddle > 0) {
xPaddle -= 8; arkanoid_state->xPaddle -= 8;
} }
break; break;
case InputKeyUp: case InputKeyUp:
@@ -408,24 +417,24 @@ int32_t arkanoid_game_app(void* p) {
break; break;
case InputKeyOk: case InputKeyOk:
//Release ball if FIRE pressed //Release ball if FIRE pressed
released = true; arkanoid_state->ball_state.released = true;
//Apply random direction to ball on release //Apply random direction to ball on release
if(rand_range(0, 2) == 0) { if(rand_range(0, 2) == 0) {
dx = 1; arkanoid_state->ball_state.dx = 1;
} else { } else {
dx = -1; arkanoid_state->ball_state.dx = -1;
} }
//Makes sure the ball heads upwards //Makes sure the ball heads upwards
dy = -1; arkanoid_state->ball_state.dy = -1;
break; break;
} }
} }
} }
} else { } else {
// Event timeout // Event timeout
FURI_LOG_D(TAG, "osMessageQueue: Event timeout"); FURI_LOG_D(TAG, "furi_message_queue: Event timeout");
} }
view_port_update(view_port); view_port_update(view_port);
@@ -434,7 +443,8 @@ int32_t arkanoid_game_app(void* p) {
furi_timer_free(timer); furi_timer_free(timer);
view_port_enabled_set(view_port, false); view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port); gui_remove_view_port(gui, view_port);
furi_record_close("gui"); furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
view_port_free(view_port); view_port_free(view_port);
delete_mutex(&state_mutex); delete_mutex(&state_mutex);

View File

@@ -61,6 +61,15 @@ typedef struct {
InputEvent input; InputEvent input;
} SnakeEvent; } SnakeEvent;
static const NotificationSequence sequence_short_vibro_and_sound = {
&message_vibro_on,
&message_note_c5,
&message_delay_50,
&message_sound_off,
&message_vibro_off,
NULL,
};
static void snake_game_render_callback(Canvas* const canvas, void* ctx) { static void snake_game_render_callback(Canvas* const canvas, void* ctx) {
const SnakeState* snake_state = acquire_mutex((ValueMutex*)ctx, 25); const SnakeState* snake_state = acquire_mutex((ValueMutex*)ctx, 25);
if(snake_state == NULL) { if(snake_state == NULL) {
@@ -238,7 +247,7 @@ static void snake_game_move_snake(SnakeState* const snake_state, Point const nex
snake_state->points[0] = next_step; snake_state->points[0] = next_step;
} }
static void snake_game_process_game_step(SnakeState* const snake_state) { static void snake_game_process_game_step(SnakeState* const snake_state, NotificationApp* notify) {
if(snake_state->state == GameStateGameOver) { if(snake_state->state == GameStateGameOver) {
return; return;
} }
@@ -273,10 +282,8 @@ static void snake_game_process_game_step(SnakeState* const snake_state) {
bool eatFruit = (next_step.x == snake_state->fruit.x) && (next_step.y == snake_state->fruit.y); bool eatFruit = (next_step.x == snake_state->fruit.x) && (next_step.y == snake_state->fruit.y);
if(eatFruit) { if(eatFruit) {
NotificationApp* notification = furi_record_open("notification"); notification_message(notify, &sequence_short_vibro_and_sound);
notification_message(notification, &sequence_single_vibro); notification_message(notify, &sequence_blink_white_100);
notification_message(notification, &sequence_blink_white_100);
furi_record_close("notification");
snake_state->len++; snake_state->len++;
if(snake_state->len >= MAX_SNAKE_LEN) { if(snake_state->len >= MAX_SNAKE_LEN) {
@@ -320,6 +327,8 @@ int32_t snake_game_app(void* p) {
Gui* gui = furi_record_open(RECORD_GUI); Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen); gui_add_view_port(gui, view_port, GuiLayerFullscreen);
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
SnakeEvent event; SnakeEvent event;
for(bool processing = true; processing;) { for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
@@ -354,7 +363,7 @@ int32_t snake_game_app(void* p) {
} }
} }
} else if(event.type == EventTypeTick) { } else if(event.type == EventTypeTick) {
snake_game_process_game_step(snake_state); snake_game_process_game_step(snake_state, notification);
} }
} else { } else {
// event timeout // event timeout
@@ -368,6 +377,7 @@ int32_t snake_game_app(void* p) {
view_port_enabled_set(view_port, false); view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port); gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI); furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
view_port_free(view_port); view_port_free(view_port);
furi_message_queue_free(event_queue); furi_message_queue_free(event_queue);
delete_mutex(&state_mutex); delete_mutex(&state_mutex);

View File

@@ -5,6 +5,6 @@ App(
entry_point="tetris_game_app", entry_point="tetris_game_app",
cdefines=["APP_TETRIS_GAME"], cdefines=["APP_TETRIS_GAME"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=2 * 1024,
order=20, order=20,
) )