From 5dc410b7ae80ea629dc754912b3ddcb5815d27c2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 03:09:33 +0100 Subject: [PATCH] Add some new games --- applications/external/4inrow/4inrow.c | 320 ++++++++ applications/external/4inrow/4inrow_10px.png | Bin 0 -> 200 bytes applications/external/4inrow/application.fam | 13 + applications/external/reversi/LICENSE | 21 + applications/external/reversi/application.fam | 15 + applications/external/reversi/game_reversi.c | 356 +++++++++ .../external/reversi/game_reversi.png | Bin 0 -> 235 bytes applications/external/reversi/reversi.c | 168 ++++ applications/external/reversi/reversi.h | 21 + applications/external/rootoflife/LICENSE | 21 + .../external/rootoflife/application.fam | 14 + .../rootoflife/images/place_error.png | Bin 0 -> 159 bytes .../external/rootoflife/images/place_ok.png | Bin 0 -> 163 bytes .../rootoflife/images/root_reroll.png | Bin 0 -> 174 bytes .../external/rootoflife/images/score.png | Bin 0 -> 192 bytes .../external/rootoflife/images/tree.png | Bin 0 -> 165 bytes .../rootoflife/roots_of_life_10px.png | Bin 0 -> 1679 bytes .../external/rootoflife/roots_of_life_game.c | 750 ++++++++++++++++++ applications/external/scorched_tanks/LICENSE | 674 ++++++++++++++++ .../external/scorched_tanks/application.fam | 12 + .../scorched_tanks/scorchedTanks_10px.png | Bin 0 -> 614 bytes .../scorched_tanks/scorched_tanks_game_app.c | 546 +++++++++++++ applications/external/simonsays/LICENSE | 674 ++++++++++++++++ .../external/simonsays/application.fam | 15 + .../simonsays/images/Connected_62x31.png | Bin 0 -> 3765 bytes .../simonsays/images/Cry_dolph_55x52.png | Bin 0 -> 3898 bytes .../simonsays/images/DolphinLeft_56x48.png | Bin 0 -> 1416 bytes .../simonsays/images/DolphinMafia_115x62.png | Bin 0 -> 2504 bytes .../images/DolphinNiceLeft_96x59.png | Bin 0 -> 2459 bytes .../images/DolphinNiceRight_96x59.png | Bin 0 -> 1066 bytes .../simonsays/images/DolphinRight_56x48.png | Bin 0 -> 601 bytes .../simonsays/images/DolphinTalking_59x63.png | Bin 0 -> 1177 bytes .../simonsays/images/DolphinWait_61x59.png | Bin 0 -> 2023 bytes .../simonsays/images/WarningDolphin_45x42.png | Bin 0 -> 1139 bytes .../external/simonsays/images/board.png | Bin 0 -> 826 bytes .../external/simonsays/images/down.png | Bin 0 -> 954 bytes .../external/simonsays/images/left.png | Bin 0 -> 921 bytes .../external/simonsays/images/right.png | Bin 0 -> 948 bytes applications/external/simonsays/images/up.png | Bin 0 -> 867 bytes applications/external/simonsays/simon_says.c | 666 ++++++++++++++++ .../external/simonsays/simon_says.png | Bin 0 -> 133 bytes .../{snake_game => snake_2}/application.fam | 9 +- applications/external/snake_2/snake_10px.png | Bin 0 -> 178 bytes .../snake_game.c => snake_2/snake_20.c} | 161 +++- .../external/snake_game/snake_10px.png | Bin 158 -> 0 bytes applications/external/t_rex_runner/LICENSE | 674 ++++++++++++++++ .../external/t_rex_runner/application.fam | 13 + .../external/t_rex_runner/assets/Cactus.png | Bin 0 -> 110 bytes .../external/t_rex_runner/assets/Dino.png | Bin 0 -> 4339 bytes .../external/t_rex_runner/assets/DinoRun0.png | Bin 0 -> 149 bytes .../external/t_rex_runner/assets/DinoRun1.png | Bin 0 -> 152 bytes .../t_rex_runner/assets/HorizonLine0.png | Bin 0 -> 5289 bytes .../external/t_rex_runner/trexrunner.c | 271 +++++++ .../external/t_rex_runner/trexrunner_icon.png | Bin 0 -> 110 bytes .../uncut_assets/HorizonLine0.png | Bin 0 -> 203 bytes .../uncut_assets/HorizonLine1.png | Bin 0 -> 272 bytes 56 files changed, 5390 insertions(+), 24 deletions(-) create mode 100644 applications/external/4inrow/4inrow.c create mode 100644 applications/external/4inrow/4inrow_10px.png create mode 100644 applications/external/4inrow/application.fam create mode 100644 applications/external/reversi/LICENSE create mode 100644 applications/external/reversi/application.fam create mode 100644 applications/external/reversi/game_reversi.c create mode 100644 applications/external/reversi/game_reversi.png create mode 100644 applications/external/reversi/reversi.c create mode 100644 applications/external/reversi/reversi.h create mode 100644 applications/external/rootoflife/LICENSE create mode 100644 applications/external/rootoflife/application.fam create mode 100644 applications/external/rootoflife/images/place_error.png create mode 100644 applications/external/rootoflife/images/place_ok.png create mode 100644 applications/external/rootoflife/images/root_reroll.png create mode 100644 applications/external/rootoflife/images/score.png create mode 100644 applications/external/rootoflife/images/tree.png create mode 100644 applications/external/rootoflife/roots_of_life_10px.png create mode 100644 applications/external/rootoflife/roots_of_life_game.c create mode 100644 applications/external/scorched_tanks/LICENSE create mode 100644 applications/external/scorched_tanks/application.fam create mode 100644 applications/external/scorched_tanks/scorchedTanks_10px.png create mode 100644 applications/external/scorched_tanks/scorched_tanks_game_app.c create mode 100644 applications/external/simonsays/LICENSE create mode 100644 applications/external/simonsays/application.fam create mode 100644 applications/external/simonsays/images/Connected_62x31.png create mode 100644 applications/external/simonsays/images/Cry_dolph_55x52.png create mode 100644 applications/external/simonsays/images/DolphinLeft_56x48.png create mode 100644 applications/external/simonsays/images/DolphinMafia_115x62.png create mode 100644 applications/external/simonsays/images/DolphinNiceLeft_96x59.png create mode 100644 applications/external/simonsays/images/DolphinNiceRight_96x59.png create mode 100644 applications/external/simonsays/images/DolphinRight_56x48.png create mode 100644 applications/external/simonsays/images/DolphinTalking_59x63.png create mode 100644 applications/external/simonsays/images/DolphinWait_61x59.png create mode 100644 applications/external/simonsays/images/WarningDolphin_45x42.png create mode 100644 applications/external/simonsays/images/board.png create mode 100644 applications/external/simonsays/images/down.png create mode 100644 applications/external/simonsays/images/left.png create mode 100644 applications/external/simonsays/images/right.png create mode 100644 applications/external/simonsays/images/up.png create mode 100644 applications/external/simonsays/simon_says.c create mode 100644 applications/external/simonsays/simon_says.png rename applications/external/{snake_game => snake_2}/application.fam (55%) create mode 100644 applications/external/snake_2/snake_10px.png rename applications/external/{snake_game/snake_game.c => snake_2/snake_20.c} (61%) delete mode 100644 applications/external/snake_game/snake_10px.png create mode 100644 applications/external/t_rex_runner/LICENSE create mode 100644 applications/external/t_rex_runner/application.fam create mode 100644 applications/external/t_rex_runner/assets/Cactus.png create mode 100644 applications/external/t_rex_runner/assets/Dino.png create mode 100644 applications/external/t_rex_runner/assets/DinoRun0.png create mode 100644 applications/external/t_rex_runner/assets/DinoRun1.png create mode 100644 applications/external/t_rex_runner/assets/HorizonLine0.png create mode 100644 applications/external/t_rex_runner/trexrunner.c create mode 100644 applications/external/t_rex_runner/trexrunner_icon.png create mode 100644 applications/external/t_rex_runner/uncut_assets/HorizonLine0.png create mode 100644 applications/external/t_rex_runner/uncut_assets/HorizonLine1.png diff --git a/applications/external/4inrow/4inrow.c b/applications/external/4inrow/4inrow.c new file mode 100644 index 000000000..e9b7b6c69 --- /dev/null +++ b/applications/external/4inrow/4inrow.c @@ -0,0 +1,320 @@ +#include +#include +#include +#include +#include +#include + +static int matrix[6][7] = {0}; +static int cursorx = 3; +static int cursory = 5; +static int player = 1; +static int scoreX = 0; +static int scoreO = 0; + +typedef struct { + FuriMutex* mutex; +} FourInRowState; + +void init() { + for(size_t i = 0; i < 6; i++) { + for(size_t j = 0; j < 7; j++) { + matrix[i][j] = 0; + } + } + cursorx = 3; + cursory = 5; + player = 1; +} + +const NotificationSequence end = { + &message_vibro_on, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_vibro_off, + NULL, +}; + +void intToStr(int num, char* str) { + int i = 0, sign = 0; + + if(num < 0) { + num = -num; + sign = 1; + } + + do { + str[i++] = num % 10 + '0'; + num /= 10; + } while(num > 0); + + if(sign) { + str[i++] = '-'; + } + + str[i] = '\0'; + + // Reverse the string + int j, len = i; + char temp; + for(j = 0; j < len / 2; j++) { + temp = str[j]; + str[j] = str[len - j - 1]; + str[len - j - 1] = temp; + } +} + +int next_height(int x) { + if(matrix[0][x] != 0) { + return -1; + } + for(size_t y = 1; y < 6; y++) { + if(matrix[y][x] != 0) { + return y - 1; + } + } + return 5; +} + +int wincheck() { + for(size_t y = 0; y <= 2; y++) { + for(size_t x = 0; x <= 6; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y + 1][x] && + matrix[y][x] == matrix[y + 2][x] && matrix[y][x] == matrix[y + 3][x]) { + return matrix[y][x]; + } + } + } + + for(size_t y = 0; y <= 5; y++) { + for(size_t x = 0; x <= 3; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y][x + 1] && + matrix[y][x] == matrix[y][x + 2] && matrix[y][x] == matrix[y][x + 3]) { + return matrix[y][x]; + } + } + } + + for(size_t y = 0; y <= 2; y++) { + for(size_t x = 0; x <= 3; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y + 1][x + 1] && + matrix[y][x] == matrix[y + 2][x + 2] && matrix[y][x] == matrix[y + 3][x + 3]) { + return matrix[y][x]; + } + } + } + + for(size_t y = 3; y <= 5; y++) { + for(size_t x = 0; x <= 3; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y - 1][x + 1] && + matrix[y][x] == matrix[y - 2][x + 2] && matrix[y][x] == matrix[y - 3][x + 3]) { + return matrix[y][x]; + } + } + } + + bool tf = true; + for(size_t y = 0; y < 6; y++) { + for(size_t x = 0; x < 7; x++) { + if(matrix[y][x] == 0) { + tf = false; + } + } + } + if(tf) { + return 0; + } + + return -1; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const FourInRowState* fourinrow_state = ctx; + + furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever); + canvas_clear(canvas); + + if(wincheck() != -1) { + canvas_set_font(canvas, FontPrimary); + + if(wincheck() == 0) { + canvas_draw_str(canvas, 30, 35, "Draw! O_o"); + } + if(wincheck() == 1) { + canvas_draw_str(canvas, 30, 35, "Player X win!"); + } + if(wincheck() == 2) { + canvas_draw_str(canvas, 30, 35, "Player O win!"); + } + + furi_mutex_release(fourinrow_state->mutex); + + return; + } + + for(size_t i = 0; i < 6; i++) { + for(size_t j = 0; j < 7; j++) { + char el[2]; + switch(matrix[i][j]) { + case 0: + strcpy(el, "_\0"); + break; + + case 1: + strcpy(el, "X\0"); + break; + + case 2: + strcpy(el, "O\0"); + break; + } + canvas_draw_str(canvas, j * 10 + 10, i * 10 + 10, el); + } + } + canvas_draw_str(canvas, cursorx * 10 + 8, cursory * 10 + 10, "[ ]"); + + if(player == 1) { + canvas_draw_str(canvas, 80, 10, "Turn: X"); + } + if(player == 2) { + canvas_draw_str(canvas, 80, 10, "Turn: O"); + } + char scX[1]; + intToStr(scoreX, scX); + char scO[1]; + intToStr(scoreO, scO); + + canvas_draw_str(canvas, 80, 20, "X:"); + canvas_draw_str(canvas, 90, 20, scX); + + canvas_draw_str(canvas, 80, 30, "O:"); + canvas_draw_str(canvas, 90, 30, scO); + + furi_mutex_release(fourinrow_state->mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t four_in_row_app(void* p) { + UNUSED(p); + + // Текущее событие типа InputEvent + InputEvent event; + // Очередь событий на 8 элементов размера InputEvent + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + FourInRowState* fourinrow_state = malloc(sizeof(FourInRowState)); + + fourinrow_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex + if(!fourinrow_state->mutex) { + FURI_LOG_E("4inRow", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(fourinrow_state); + return 255; + } + + // dolphin_deed(DolphinDeedPluginGameStart); + + // Создаем новый view port + ViewPort* view_port = view_port_alloc(); + // Создаем callback отрисовки, без контекста + view_port_draw_callback_set(view_port, draw_callback, fourinrow_state); + // Создаем callback нажатий на клавиши, в качестве контекста передаем + // нашу очередь сообщений, чтоб запихивать в неё эти события + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Создаем GUI приложения + Gui* gui = furi_record_open(RECORD_GUI); + // Подключаем view port к GUI в полноэкранном режиме + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notification, &sequence_display_backlight_enforce_on); + + // Бесконечный цикл обработки очереди событий + while(1) { + // Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста) + // и проверяем, что у нас получилось это сделать + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever); + // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения + if(wincheck() != -1) { + notification_message(notification, &end); + furi_delay_ms(1000); + if(wincheck() == 1) { + scoreX++; + } + if(wincheck() == 2) { + scoreO++; + } + init(); + } + + if(event.type == InputTypePress) { + if(event.key == InputKeyOk) { + int nh = next_height(cursorx); + if(nh != -1) { + matrix[nh][cursorx] = player; + player = 3 - player; + } + } + if(event.key == InputKeyUp) { + //cursory--; + } + if(event.key == InputKeyDown) { + //cursory++; + } + if(event.key == InputKeyLeft) { + if(cursorx > 0) { + cursorx--; + } + } + if(event.key == InputKeyRight) { + if(cursorx < 6) { + cursorx++; + } + } + if(event.key == InputKeyBack) { + break; + } + } + view_port_update(view_port); + furi_mutex_release(fourinrow_state->mutex); + } + + // Clear notification + notification_message_block(notification, &sequence_display_backlight_enforce_auto); + furi_record_close(RECORD_NOTIFICATION); + + // Специальная очистка памяти, занимаемой очередью + furi_message_queue_free(event_queue); + + // Чистим созданные объекты, связанные с интерфейсом + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_mutex_free(fourinrow_state->mutex); + furi_record_close(RECORD_GUI); + free(fourinrow_state); + + return 0; +} diff --git a/applications/external/4inrow/4inrow_10px.png b/applications/external/4inrow/4inrow_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..ef3e18f20d04c696f5cb2ae5757168b216888dd1 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ih{XE z)7O>#Ig5xmyIQ8VV=Pcevcxr_#5q4VH#M(>!MP|ku_QG`p**uBL&4qCHz2%`PaLSo z$ +#include +#include +#include +#include +#include "reversi.h" + +#define FRAME_LEFT 3 +#define FRAME_TOP 3 +#define FRAME_CELL_SIZE 7 + +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/game_reversi.save" + +typedef enum { AppScreenGame, AppScreenMenu } AppScreen; + +typedef struct { + GameState game; + AppScreen screen; + uint8_t selected_menu_item; + FuriMutex* mutex; +} AppState; + +#define MENU_ITEMS_COUNT 2 +static const char* popup_menu_strings[] = {"Resume", "New Game"}; + +static void draw_menu(Canvas* const canvas, const AppState* app_state); +static void gray_canvas(Canvas* const canvas); + +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +static void draw_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const AppState* app_state = ctx; + furi_mutex_acquire(app_state->mutex, FuriWaitForever); + + const GameState* game_state = &app_state->game; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + for(uint8_t i = 0; i <= BOARD_SIZE; i++) { + canvas_draw_line( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * i, + FRAME_TOP, + FRAME_LEFT + FRAME_CELL_SIZE * i, + FRAME_TOP + FRAME_CELL_SIZE * BOARD_SIZE); + canvas_draw_line( + canvas, + FRAME_LEFT, + FRAME_TOP + FRAME_CELL_SIZE * i, + FRAME_LEFT + FRAME_CELL_SIZE * BOARD_SIZE, + FRAME_TOP + FRAME_CELL_SIZE * i); + } + // + // draw cursor + canvas_set_color(canvas, ColorWhite); + canvas_draw_frame( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * game_state->cursor_x, + FRAME_TOP + FRAME_CELL_SIZE * game_state->cursor_y, + FRAME_CELL_SIZE + 1, + FRAME_CELL_SIZE + 1); + + canvas_set_color(canvas, ColorBlack); + // draw pieces + int blacks = 0, whites = 0; + const int radius = FRAME_CELL_SIZE >> 1; + for(uint8_t i = 0; i < BOARD_SIZE; i++) { + for(uint8_t j = 0; j < BOARD_SIZE; j++) { + if(!game_state->board[i][j]) { + continue; + } + if(game_state->board[i][j] == BLACK) { + canvas_draw_disc( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * i + radius + 1, + FRAME_TOP + FRAME_CELL_SIZE * j + radius + 1, + radius); + blacks++; + } else { + canvas_draw_circle( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * i + radius + 1, + FRAME_TOP + FRAME_CELL_SIZE * j + radius + 1, + radius); + whites++; + } + } + } + + canvas_set_font(canvas, FontPrimary); + // draw score + char score_str[25]; + memset(score_str, 0, sizeof(score_str)); + snprintf(score_str, sizeof(score_str), "%d - %d", whites, blacks); + + canvas_draw_str_aligned(canvas, 70, 3, AlignLeft, AlignTop, score_str); + + canvas_set_font(canvas, FontSecondary); + if(game_state->is_game_over) { + canvas_draw_str_aligned(canvas, 70, 20, AlignLeft, AlignTop, "Game over"); + + canvas_draw_str_aligned( + canvas, + 70, + FRAME_TOP + FRAME_CELL_SIZE * BOARD_SIZE, + AlignLeft, + AlignBottom, + "Press OK"); + + canvas_set_font(canvas, FontPrimary); + + if(whites == blacks) { + canvas_draw_str_aligned(canvas, 70, 30, AlignLeft, AlignTop, "DRAW"); + } else if( + ((game_state->human_color == WHITE) && whites > blacks) || + ((game_state->human_color == BLACK) && blacks > whites)) { + canvas_draw_str_aligned(canvas, 70, 30, AlignLeft, AlignTop, "YOU WIN"); + } else { + canvas_draw_str_aligned(canvas, 70, 30, AlignLeft, AlignTop, "YOU LOSE"); + } + } else if(game_state->current_player == game_state->human_color) { + canvas_draw_str_aligned(canvas, 70, 12, AlignLeft, AlignTop, "Your turn"); + } else { + canvas_draw_str_aligned(canvas, 70, 12, AlignLeft, AlignTop, "Computer turn"); + } + + if(app_state->screen == AppScreenMenu) { + draw_menu(canvas, app_state); + } + + furi_mutex_release(app_state->mutex); +} + +static void draw_menu(Canvas* const canvas, const AppState* app_state) { + gray_canvas(canvas); + canvas_set_color(canvas, ColorWhite); + canvas_draw_rbox(canvas, 28, 16, 72, 32, 4); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 28, 16, 72, 32, 4); + + for(int i = 0; i < MENU_ITEMS_COUNT; i++) { + if(i == app_state->selected_menu_item) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 34, 20 + 12 * i, 60, 12); + } + + canvas_set_color(canvas, i == app_state->selected_menu_item ? ColorWhite : ColorBlack); + canvas_draw_str_aligned( + canvas, 64, 26 + 12 * i, AlignCenter, AlignCenter, popup_menu_strings[i]); + } +} + +static void gray_canvas(Canvas* const canvas) { + canvas_set_color(canvas, ColorWhite); + for(int x = 0; x < 128; x += 2) { + for(int y = 0; y < 64; y++) { + canvas_draw_dot(canvas, x + (y % 2 == 1 ? 0 : 1), y); + } + } +} + +bool load_game(GameState* game_state) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* file = storage_file_alloc(storage); + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) { + bytes_readed = storage_file_read(file, game_state, sizeof(GameState)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); + + return bytes_readed == sizeof(GameState); +} + +void save_game(const GameState* game_state) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { + return; + } + } + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, game_state, sizeof(GameState)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +bool handle_key_game(GameState* game_state, InputKey key) { + switch(key) { + case InputKeyBack: + save_game(game_state); + return false; + break; + case InputKeyOk: + if(game_state->is_game_over) { + init_game(game_state); + save_game(game_state); + } else { + human_move(game_state); + } + break; + case InputKeyUp: + if(game_state->cursor_y > 0) { + game_state->cursor_y--; + } else { + game_state->cursor_y = BOARD_SIZE - 1; + } + break; + case InputKeyDown: + if(game_state->cursor_y < BOARD_SIZE - 1) { + game_state->cursor_y++; + } else { + game_state->cursor_y = 0; + } + break; + case InputKeyLeft: + if(game_state->cursor_x > 0) { + game_state->cursor_x--; + } else { + game_state->cursor_x = BOARD_SIZE - 1; + } + break; + case InputKeyRight: + if(game_state->cursor_x < BOARD_SIZE - 1) { + game_state->cursor_x++; + } else { + game_state->cursor_x = 0; + } + break; + default: + break; + } + return true; +} + +bool handle_key_menu(AppState* app_state, InputKey key) { + switch(key) { + case InputKeyUp: + if(app_state->selected_menu_item > 0) { + app_state->selected_menu_item--; + } + break; + case InputKeyDown: + if(app_state->selected_menu_item < MENU_ITEMS_COUNT - 1) { + app_state->selected_menu_item++; + } + break; + case InputKeyOk: + if(app_state->selected_menu_item == 1) { + // new game + init_game(&app_state->game); + save_game(&app_state->game); + } + app_state->screen = AppScreenGame; + break; + default: + break; + } + return true; +} + +// returns `true` if the event loop should keep going +bool handle_key(AppState* app_state, InputKey key) { + GameState* game_state = &app_state->game; + + switch(app_state->screen) { + case AppScreenGame: + return handle_key_game(game_state, key); + break; + case AppScreenMenu: + return handle_key_menu(app_state, key); + break; + } + return true; +} + +int32_t game_reversi_app() { + AppState app_state; + app_state.screen = AppScreenGame; + if(!load_game(&app_state.game)) { + init_game(&app_state.game); + } + + app_state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!app_state.mutex) { + return 255; + } + InputEvent input; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, &app_state); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + bool is_finished = false; + + while(!is_finished) { + // check if it's computer's turn + if(!app_state.game.is_game_over && + (app_state.game.current_player != app_state.game.human_color)) { + computer_move(&app_state.game); + } + FuriStatus event_status = furi_message_queue_get(event_queue, &input, FuriWaitForever); + if(event_status == FuriStatusOk) { + // handle only press event, ignore repeat/release events + + if(input.type == InputTypeLong && input.key == InputKeyOk && + app_state.screen == AppScreenGame) { + furi_mutex_acquire(app_state.mutex, FuriWaitForever); + app_state.selected_menu_item = 0; + app_state.screen = AppScreenMenu; + view_port_update(view_port); + furi_mutex_release(app_state.mutex); + continue; + } + if(input.type != InputTypePress) continue; + + furi_mutex_acquire(app_state.mutex, FuriWaitForever); + is_finished = !handle_key(&app_state, input.key); + view_port_update(view_port); + furi_mutex_release(app_state.mutex); + } + } + + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + + view_port_free(view_port); + + furi_message_queue_free(event_queue); + + furi_mutex_free(app_state.mutex); + + return 0; +} diff --git a/applications/external/reversi/game_reversi.png b/applications/external/reversi/game_reversi.png new file mode 100644 index 0000000000000000000000000000000000000000..3a321471b96c16186e80b43c8659d1b26b5d5bd5 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4v7|ftIx;Y9?C1WI$O`0Jq(*qA z`T8 zhmtY(m4zHeNhYV)vz~NWa`wf$W0ST$_|x(0y3*^~dgFLrS=B#57F+v3HhQ}HxvX= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && board[r][c] == opponent) { + int k = 2; + while(true) { + r += i; + c += j; + if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break; + if(board[r][c] == player) return true; + if(board[r][c] == 0) break; + k++; + } + } + } + } + return false; +} + +// Check if the game is over by checking if there are no more moves left for +// either player +bool is_game_over(int8_t board[BOARD_SIZE][BOARD_SIZE]) { + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(is_legal_move(board, i, j, BLACK) || is_legal_move(board, i, j, WHITE)) { + return false; + } + } + } + return true; +} + +bool has_legal_moves(int8_t board[BOARD_SIZE][BOARD_SIZE], int8_t player_color) { + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(is_legal_move(board, i, j, player_color)) { + return true; + } + } + } + return false; +} + +// Calculate the heuristic value of the current board. This function can +// be replaced with a more complex evaluation function that takes into +// account factors such as mobility, piece square tables, etc. +int heuristic(int8_t board[BOARD_SIZE][BOARD_SIZE]) { + int white = 0, black = 0; + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(board[i][j] == 1) white++; + if(board[i][j] == -1) black++; + } + } + return white - black; +} + +// Make a move on the board and capture any opponent pieces +void make_move(GameState* state, int x, int y, int player) { + state->board[x][y] = player; + int opponent = -player; + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + if(i == 0 && j == 0) continue; + int r = x + i, c = y + j; + if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && + state->board[r][c] == opponent) { + int k = 2; + while(true) { + r += i; + c += j; + if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break; + if(state->board[r][c] == player) { + r -= i; + c -= j; + while(r != x || c != y) { + state->board[r][c] = player; + r -= i; + c -= j; + } + break; + } + if(state->board[r][c] == 0) break; + k++; + } + } + } + } + state->is_game_over = is_game_over(state->board); +} + +void init_game(GameState* state) { + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + state->board[i][j] = 0; + } + } + + // Place the initial pieces + int mid = BOARD_SIZE / 2; + state->board[mid - 1][mid - 1] = WHITE; + state->board[mid][mid] = WHITE; + state->board[mid - 1][mid] = BLACK; + state->board[mid][mid - 1] = BLACK; + + state->cursor_x = mid - 1; + state->cursor_y = mid + 1; + + // Set up turn order + state->human_color = WHITE; + state->current_player = WHITE; + + state->is_game_over = false; +} + +void human_move(GameState* game_state) { + if(game_state->current_player != game_state->human_color) { + return; + } + + if(is_legal_move( + game_state->board, + game_state->cursor_x, + game_state->cursor_y, + game_state->current_player)) { + make_move( + game_state, game_state->cursor_x, game_state->cursor_y, game_state->current_player); + game_state->current_player = -game_state->current_player; + } +} + +void computer_move(GameState* game_state) { + if(game_state->current_player == game_state->human_color) { + return; + } + int best_row = -1, best_col = -1, best_score = -1000000; + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(!is_legal_move(game_state->board, i, j, game_state->current_player)) { + continue; + } + int score = heuristic(game_state->board); + if(score > best_score) { + best_score = score; + best_row = i; + best_col = j; + } + } + } + if(best_row != -1) { + make_move(game_state, best_row, best_col, game_state->current_player); + } + if(has_legal_moves(game_state->board, game_state->human_color)) { + game_state->current_player = -game_state->current_player; + } +} diff --git a/applications/external/reversi/reversi.h b/applications/external/reversi/reversi.h new file mode 100644 index 000000000..ee4588838 --- /dev/null +++ b/applications/external/reversi/reversi.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#define BLACK 1 +#define WHITE -1 +#define BOARD_SIZE 8 + +typedef struct { + int8_t board[BOARD_SIZE][BOARD_SIZE]; + int8_t current_player; + int8_t human_color; + uint8_t cursor_x; + uint8_t cursor_y; + uint8_t is_game_over; +} GameState; + +void init_game(GameState* state); +void computer_move(GameState* game_state); +void human_move(GameState* game_state); diff --git a/applications/external/rootoflife/LICENSE b/applications/external/rootoflife/LICENSE new file mode 100644 index 000000000..765f2a64b --- /dev/null +++ b/applications/external/rootoflife/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Kirill Korepanov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/rootoflife/application.fam b/applications/external/rootoflife/application.fam new file mode 100644 index 000000000..8dfe98599 --- /dev/null +++ b/applications/external/rootoflife/application.fam @@ -0,0 +1,14 @@ +App( + appid="roots_of_life", + name="Roots of Life", + apptype=FlipperAppType.EXTERNAL, + entry_point="roots_of_life_game_app", + cdefines=["APP_ROOTS_OF_LIFE_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=30, + fap_icon="roots_of_life_10px.png", + fap_category="Games", + fap_icon_assets="images", + fap_icon_assets_symbol="roots_of_life_game", +) diff --git a/applications/external/rootoflife/images/place_error.png b/applications/external/rootoflife/images/place_error.png new file mode 100644 index 0000000000000000000000000000000000000000..9032c65322a0a85f7364c67737cdc56a4b721fbb GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4v7|ftIx;Y9?C1WI$O`0h7I;J! zGcfQS0b$0e+I-SLK}Sy)#}JL+CQSej~LyBFO(}d#0NeWUo ze@cWM7FqmT&{@$(aMMf4v7|ftIx;Y9?C1WI$O`0h7I;J! zGcfQS0b$0e+I-SLL03-~#}JL+wdWoA7zB7+E?)clzDF>4%9PgqnM(dEp3SnE@@vmG z{#9AC6gTe~ HDWM4fl&>_% literal 0 HcmV?d00001 diff --git a/applications/external/rootoflife/images/root_reroll.png b/applications/external/rootoflife/images/root_reroll.png new file mode 100644 index 0000000000000000000000000000000000000000..996b5ff8110079f1df73192e97512fad71417ac9 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4v7|ftIx;Y9?C1WI$O`0h7I;J! zGcfQS0b$0e+I-SL!2nMe#}JL+-U*(33$yQE#-Ur_Jv4$-pWrDz0u?uVU&0j5v{#Uc(CKE4{j(G3H Se^x-V89ZJ6T-G@yGywn@BsNa~ literal 0 HcmV?d00001 diff --git a/applications/external/rootoflife/images/score.png b/applications/external/rootoflife/images/score.png new file mode 100644 index 0000000000000000000000000000000000000000..73bf140d4db41eafb435c50e99c24713dc4485f4 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^AT~D#8<2F%laT;YEa{HEjtmSN`?>!lvI6;>1s;*b z3=DinK$vl=HlH+5Fv-)!F+?M{bb_N$g8~n8^#A|qsoBboh5UY=(+!g6HD?%YUM?Md zR9WdKla2K~#?7-%ntYahuKD-u)g8{0B3!23xmJ8`xnQLD9M?Ohoxf~3wuD^RH2*cL k3;&#@4oWM0&x9UfZL9HS;(TiJ6lfcRr>mdKI;Vst0L7_2=Kufz literal 0 HcmV?d00001 diff --git a/applications/external/rootoflife/images/tree.png b/applications/external/rootoflife/images/tree.png new file mode 100644 index 0000000000000000000000000000000000000000..4f424270ca65e9fd59ecc276244a6da07f2c7c8f GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4v7|ftIx;Y9?C1WI$O`0h7I;J! zGcfQS0b$0e+I-SLL3d9V#}JL+-X2CS1_ci0zyFu}uDv;>g>Ti)buRo$*1t|lx;$Dm zxxb>qaew&9=L>&}2yV(qx;M4a<=vy6yDx;qcAT7{@;o>swrQvE@9Rg*{D4L>c)I$z JtaD0e0ssaIHrfCH literal 0 HcmV?d00001 diff --git a/applications/external/rootoflife/roots_of_life_10px.png b/applications/external/rootoflife/roots_of_life_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..49d72986ddd0ed5bd1b7fccab89e796b1ef23339 GIT binary patch literal 1679 zcmcIlOOM-B6m|uz+Nmm`@(@CmFz#j$WBdBXkJ!`9f`q99lBk-3ijby2&pR9%MKg4kFYkhvQtw@fv(_+S z>ITdBPDz%me~&0Hm2p7X5B?-9rB^(>misi%zO zJZ*8_n-*$q$|7Q{AaVXm7zezcJy|F!4OoNZ%$1;OSVp$2=`CDwG}|%oQ>fw~G=X~1 z2LZ{+(ZD)=k-G*q1A&Rn-W! z81m^@7uYvA-6_&n*iYIv;1oD!8FDPW9U#-9ahS<_UMad6*un2gm_g}=<@d44UMW|-P#ia%Luk?Ku0)F>vp^~zhj+=| z(R5B|`&|a4hZKJT%XmBQla$pukm>(yF7;mGGqQWQ^rIF2f0SOPm3B$oVK6vVsR%|b zM$Dur>c8tXes{+>n!;hm70cOhh0Sa{vlysT7`_jz|MoHr?zLv4)|%gZ@A~>#;fa$= z2Y>wd`TO6!_2O%Li{^c&Zr*;Kw*PY8zV=i0;rzAH$B%!q{;gNj(o6f#{_ynKhtpSY s9lUw<(_#Jl(dgWz=fD2!cYflL(XR`CK4dN2DE +#include +#include +#include +#include +#include +#include + +#include "roots_of_life_game_icons.h" + +#define TAG "RootsOfLife" + +// Flipper +#define FLIPPER_LCD_WIDTH 128 +#define FLIPPER_LCD_HEIGHT 64 + +// General +#define GROUND_HEIGHT 10 +#define CELL_SIZE 3 +#define FIELD_START_X 0 +#define FIELD_START_Y (GROUND_HEIGHT + 1) +#define CELLS_X (FLIPPER_LCD_WIDTH / CELL_SIZE) +#define CELLS_Y ((FLIPPER_LCD_HEIGHT - GROUND_HEIGHT) / CELL_SIZE) +#define CELLS_TOTAL (CELLS_Y * CELLS_X) +#define CELL(Y, X) (Y * CELLS_X + X) + +// Root Spawn +#define ROOT_SIZE_X 7 +#define ROOT_SIZE_Y 7 +#define ROOT(Y, X) ((Y)*ROOT_SIZE_X + (X)) + +#define SPAWN_DIRECTIONS 2 +#define GROW_STEPS 4 +#define GROW_SAME_DIRECTION_CHANCE 70 +#define RANDOM_GROW_ATTEMPTS 4 +#define RANDOM_GROW_CHANCE 50 + +// UI +#define BLINK_PERIOD 12 +#define BLINK_HIDE_FRAMES 5 +#define TREE_HEIGHT 10 +#define PICKUP_FREQUENCY 10 + +// Game +#define REROLLS_MAX 5 +#define SCORE_FACTOR 10 + +#define PICKUPS_MIN 1 +#define PICKUPS_MAX 5 +#define PICKUPS_POINTS_FACTOR 10 + +typedef enum { EventTypeTick, EventTypeKey } EventType; + +typedef enum { + R_NONE = 0, + R_UP = 0b1000, + R_DOWN = 0b0100, + R_LEFT = 0b0010, + R_RIGHT = 0b0001 +} Direction; + +typedef enum { StageStart, StageRun, StageOver } GameStage; + +typedef struct { + bool initialDraw; + + GameStage stage; + int tick; + + bool* filledCells; + char* cells; + bool* pickups; + int collectedPickups; + + bool* filledRootBase; + char* rootBase; + + int rootSizeX; + int rootSizeY; + bool* filledRoot; + char* root; + + int pX, pY; + + int rerolls; + int score; + FuriMutex* mutex; +} GameState; + +typedef struct { + EventType type; + InputEvent input; +} GameEvent; + +static Direction rand_dir() { + int r = rand() % 4; + return 1 << r; +} + +static Direction reverse_dir(Direction dir) { + switch(dir) { + case R_UP: + return R_DOWN; + case R_DOWN: + return R_UP; + case R_LEFT: + return R_RIGHT; + case R_RIGHT: + return R_LEFT; + + default: + return R_NONE; + } +} + +static int rand_range(int min, int max) { + return min + rand() % (max - min); +} +static bool rand_chance(int chance) { + return (rand() % 100) < chance; +} + +static bool has_intersection(char cellA, char cellB) { + return cellA & cellB; +} + +static int root_index(GameState* state, int y, int x) { + return y * state->rootSizeX + x; +} + +static void set_cell(GameState* state, int y, int x, char cellRoot) { + int c = CELL(y, x); + state->filledCells[c] = true; + state->cells[c] = cellRoot; +} + +static void game_state_init(GameState* state) { + state->initialDraw = false; + state->tick = 0; + + // Init field arrays + state->filledCells = (bool*)malloc(CELLS_TOTAL * sizeof(bool)); + state->cells = (char*)malloc(CELLS_TOTAL * sizeof(char)); + state->pickups = (bool*)malloc(CELLS_TOTAL * sizeof(char)); + + state->rootBase = (char*)malloc(ROOT_SIZE_X * ROOT_SIZE_Y * sizeof(char)); + state->filledRootBase = (bool*)malloc(ROOT_SIZE_X * ROOT_SIZE_Y * sizeof(bool)); + state->root = NULL; + state->filledRoot = NULL; + + for(int i = 0; i < CELLS_TOTAL; i++) { + state->filledCells[i] = false; + state->cells[i] = R_NONE; + state->pickups[i] = false; + } +} + +static void free_root(GameState* state) { + if(state->root) free(state->root); + if(state->filledRoot) free(state->filledRoot); +} + +static void game_state_free(GameState* state) { + free(state->filledCells); + free(state->cells); + free(state->pickups); + + free(state->rootBase); + free(state->filledRootBase); + + free_root(state); +} + +/*static bool has_root(GameState* state, int x, int y) { + return x >= 0 && x < ROOT_SIZE_X && y >= 0 && y < ROOT_SIZE_Y && + state->filledRootBase[ROOT(y, x)]; +}*/ + +static void generate_new_root(GameState* state) { + for(int i = 0; i < ROOT_SIZE_X * ROOT_SIZE_Y; i++) { + state->filledRootBase[i] = false; + state->rootBase[i] = R_NONE; + } + + int cX = ROOT_SIZE_X / 2; + int cY = ROOT_SIZE_Y / 2; + int c = ROOT(cY, cX); + state->filledRootBase[c] = true; + + for(int i = 0; i < SPAWN_DIRECTIONS; i++) { + int pX = cX, pY = cY; + Direction oldDir = rand_dir(); + for(int g = 0; g < GROW_STEPS; g++) { + Direction dir = rand_chance(GROW_SAME_DIRECTION_CHANCE) ? oldDir : rand_dir(); + oldDir = dir; + + int nX = pX - (dir & R_LEFT ? 1 : 0) + (dir & R_RIGHT ? 1 : 0); + int nY = pY - (dir & R_UP ? 1 : 0) + (dir & R_DOWN ? 1 : 0); + if(nX < 0 || nY < 0 || nX >= ROOT_SIZE_X || nY >= ROOT_SIZE_Y) continue; + + int n = ROOT(nY, nX); + state->filledRootBase[n] = true; + + // Connect points + int p = ROOT(pY, pX); + state->rootBase[p] |= dir; + state->rootBase[n] |= reverse_dir(dir); + + // Grow from new point + pX = nX; + pY = nY; + } + } + + for(int y = 0; y < ROOT_SIZE_Y; y++) { + for(int x = 0; x < ROOT_SIZE_X; x++) { + int c = ROOT(y, x); + if(!state->filledRootBase[c]) continue; + + /* + if(has_root(state, x - 1, y)) state->rootBase[c] |= R_LEFT; + if(has_root(state, x + 1, y)) state->rootBase[c] |= R_RIGHT; + if(has_root(state, x, y - 1)) state->rootBase[c] |= R_UP; + if(has_root(state, x, y + 1)) state->rootBase[c] |= R_DOWN; + */ + + for(int r = 0; r < RANDOM_GROW_ATTEMPTS; r++) { + if(!rand_chance(RANDOM_GROW_CHANCE)) continue; + state->rootBase[c] |= rand_dir(); + } + } + } + + // Copy root to real root + int minX = cX, maxX = cX, minY = cY, maxY = cY; + for(int y = 0; y < ROOT_SIZE_Y; y++) { + for(int x = 0; x < ROOT_SIZE_X; x++) { + int r = ROOT(y, x); + if(!state->filledRootBase[r]) continue; + + minX = MIN(minX, x); + maxX = MAX(maxX, x); + minY = MIN(minY, y); + maxY = MAX(maxY, y); + } + } + + // Clone to real root + state->rootSizeX = maxX - minX + 1; + state->rootSizeY = maxY - minY + 1; + free_root(state); + + state->root = (char*)malloc(state->rootSizeX * state->rootSizeY * sizeof(char)); + state->filledRoot = (bool*)malloc(state->rootSizeX * state->rootSizeY * sizeof(bool)); + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int c = root_index(state, y, x); + int r = ROOT(y + minY, x + minX); + state->filledRoot[c] = state->filledRootBase[r]; + state->root[c] = state->rootBase[r]; + } + } +} + +static bool in_borders(int x, int y) { + return x >= 0 && y >= 0 && x < CELLS_X && y < CELLS_Y; +} +static char get_cell(GameState* state, int x, int y) { + if(!in_borders(x, y)) return R_NONE; + return state->cells[CELL(y, x)]; +} + +static bool get_filled_cell(GameState* state, int x, int y) { + if(!in_borders(x, y)) return false; + return state->filledCells[CELL(y, x)]; +} + +static bool can_place_root(GameState* state) { + bool hasConnection = false; + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int r = root_index(state, y, x); + if(!state->filledRoot[r]) { + continue; + } + char root = state->root[r]; + + int rY = y + state->pY; + int rX = x + state->pX; + + // Check if colliding + if(get_filled_cell(state, rX, rY)) { + char cell = get_cell(state, rX, rY); + if(has_intersection(cell, root)) { + return false; + } + hasConnection = true; + } + + // Check neighbours + hasConnection |= (root & R_RIGHT) && (get_cell(state, rX + 1, rY) & R_LEFT); + hasConnection |= (root & R_LEFT) && (get_cell(state, rX - 1, rY) & R_RIGHT); + hasConnection |= (root & R_UP) && (get_cell(state, rX, rY - 1) & R_DOWN); + hasConnection |= (root & R_DOWN) && (get_cell(state, rX, rY + 1) & R_UP); + } + } + + return hasConnection; +} + +static bool try_place_root(GameState* state) { + if(!can_place_root(state)) return false; + + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int r = root_index(state, y, x); + if(!state->filledRoot[r]) continue; + + int rY = y + state->pY; + int rX = x + state->pX; + + // Root may be out of borders in rare cases (after new cpawn changed its size), just ignore that part + if(in_borders(rX, rY)) { + int c = CELL(rY, rX); + + state->filledCells[c] = true; + state->cells[c] |= state->root[r]; + } + } + } + + return true; +} + +static void reset_level(GameState* state) { + state->stage = StageStart; + state->tick = 0; + + for(int i = 0; i < CELLS_TOTAL; i++) { + state->filledCells[i] = false; + state->cells[i] = R_NONE; + } + + generate_new_root(state); + + // Starting cells + int midX = CELLS_X / 2; + set_cell(state, 0, midX, R_UP | R_DOWN); + set_cell(state, 1, midX, R_UP | R_DOWN | R_LEFT | R_RIGHT); + set_cell(state, 1, midX - 1, R_RIGHT | R_DOWN); + set_cell(state, 1, midX + 1, R_LEFT | R_DOWN); + set_cell(state, 2, midX, R_UP); + + state->pX = midX; + state->pY = 4; + + state->rerolls = REROLLS_MAX; + state->score = 0; + + state->collectedPickups = 0; + for(int i = 0, n = rand_range(PICKUPS_MIN, PICKUPS_MAX); i < n; i++) { + int x = rand_range(0, CELLS_X); + int y = rand_range(0, CELLS_Y); + state->pickups[CELL(y, x)] = true; + } +} + +static void recalculate_score(GameState* state) { + int score = 0; + for(int i = 0; i < CELLS_TOTAL; i++) { + if(state->filledCells[i]) score++; + } + + for(int i = 0; i < CELLS_TOTAL; i++) { + if(!state->pickups[i] || !state->filledCells[i]) continue; + + state->pickups[i] = false; + state->collectedPickups++; + state->rerolls++; + } + + state->score = (score + state->collectedPickups * PICKUPS_POINTS_FACTOR) * SCORE_FACTOR; +} + +static void draw_root_cell(Canvas* canvas, char root, int y, int x, bool isHidden) { + int posX = FIELD_START_X + x * CELL_SIZE + 1, posY = FIELD_START_Y + y * CELL_SIZE + 1; + canvas_draw_dot(canvas, posX, posY); + + if(isHidden) { + canvas_set_color(canvas, ColorXOR); + } + + if(root & R_UP) canvas_draw_dot(canvas, posX, posY - 1); + if(root & R_DOWN) canvas_draw_dot(canvas, posX, posY + 1); + if(root & R_LEFT) canvas_draw_dot(canvas, posX - 1, posY); + if(root & R_RIGHT) canvas_draw_dot(canvas, posX + 1, posY); + + if(isHidden) { + canvas_set_color(canvas, ColorBlack); + } +} + +static void draw_placed_roots(Canvas* canvas, GameState* state) { + for(int y = 0; y < CELLS_Y; y++) { + for(int x = 0; x < CELLS_X; x++) { + int c = CELL(y, x); + if(!state->filledCells[c]) continue; + draw_root_cell(canvas, state->cells[c], y, x, false); + } + } +} + +static void draw_pickup(Canvas* canvas, GameState* state, int y, int x) { + int posX = FIELD_START_X + x * CELL_SIZE + 1, posY = FIELD_START_Y + y * CELL_SIZE + 1; + + int stage = state->tick / PICKUP_FREQUENCY; + + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX + 1, posY); + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX, posY + 1); + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX - 1, posY); + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX, posY - 1); +} + +static void draw_pickups(Canvas* canvas, GameState* state) { + for(int y = 0; y < CELLS_Y; y++) { + for(int x = 0; x < CELLS_X; x++) { + int c = CELL(y, x); + if(!state->pickups[c]) continue; + draw_pickup(canvas, state, y, x); + } + } +} + +static void draw_active_root(Canvas* canvas, GameState* state) { + bool isHidden = (state->tick % BLINK_PERIOD) < BLINK_HIDE_FRAMES; + + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int c = root_index(state, y, x); + if(!state->filledRoot[c]) continue; + + int realX = x + state->pX; + int realY = y + state->pY; + draw_root_cell(canvas, state->root[c], realY, realX, isHidden); + } + } +} + +#if DRAW_DEBUG +static void draw_generated_root(Canvas* canvas, GameState* state) { + bool isHidden = (state->tick % BLINK_PERIOD) < BLINK_HIDE_FRAMES; + + for(int y = 0; y < ROOT_SIZE_Y; y++) { + for(int x = 0; x < ROOT_SIZE_X; x++) { + int c = ROOT(y, x); + if(!state->filledRootBase[c]) continue; + + int realX = x + 1; + int realY = y + 1; + draw_root_cell(canvas, state->rootBase[c], realY, realX, isHidden); + } + } +} +#endif + +static void draw_ground(Canvas* canvas, GameState* state) { + canvas_draw_line(canvas, 0, GROUND_HEIGHT, FLIPPER_LCD_WIDTH, GROUND_HEIGHT); + UNUSED(state); +} + +static void draw_tree(Canvas* canvas, GameState* state) { + canvas_draw_icon(canvas, FLIPPER_LCD_WIDTH / 2 - 5, GROUND_HEIGHT - TREE_HEIGHT, &I_tree); + UNUSED(state); +} + +static void draw_placement(Canvas* canvas, GameState* state) { + bool canPlace = can_place_root(state); + canvas_draw_icon(canvas, FLIPPER_LCD_WIDTH - 10, 0, canPlace ? &I_place_ok : &I_place_error); +} + +static void draw_rerolls(Canvas* canvas, GameState* state) { + UNUSED(canvas); + UNUSED(state); + + canvas_draw_icon(canvas, 0, 0, &I_root_reroll); + + // Ugh + FuriString* tmp_string = furi_string_alloc(); + furi_string_printf(tmp_string, "%d", MAX(0, state->rerolls)); + canvas_draw_str(canvas, 11, 9, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); +} + +static void draw_score(Canvas* canvas, GameState* state) { + UNUSED(canvas); + UNUSED(state); + + int x = FLIPPER_LCD_WIDTH / 2 + 15; + canvas_draw_icon(canvas, x, 0, &I_score); + + // Ugh + FuriString* tmp_string = furi_string_alloc(); + furi_string_printf(tmp_string, "%d", MAX(0, state->score)); + canvas_draw_str(canvas, x + 11, 9, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); +} + +static void draw_gui(Canvas* canvas, GameState* state) { + draw_ground(canvas, state); + draw_tree(canvas, state); + draw_placement(canvas, state); + draw_rerolls(canvas, state); + draw_score(canvas, state); +} + +static void draw_center_box(Canvas* canvas, int w2, int h2, int margin) { + int x = FLIPPER_LCD_WIDTH / 2 - w2; + int y = FLIPPER_LCD_HEIGHT / 2 - h2; + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, x - margin - 1, y - margin - 1, (w2 + margin + 1) * 2, (h2 + margin + 1) * 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, x - margin, y - margin, (w2 + margin) * 2, (h2 + margin) * 2); +} + +static void draw_start_ui(Canvas* canvas, GameState* state) { + int w2 = 40; + int margin = 3; + int h2 = 10; + draw_center_box(canvas, w2, h2, margin); + + int x = FLIPPER_LCD_WIDTH / 2 - w2; + int y = FLIPPER_LCD_HEIGHT / 2 - h2; + canvas_draw_str(canvas, x + 1, y + 9, " Grow your roots "); + canvas_draw_str(canvas, x + 1, y + 18, "Press [OK] to start"); + + UNUSED(state); +} + +static void draw_end_ui(Canvas* canvas, GameState* state) { + int w2 = 46; + int margin = 3; + int h2 = 15; + draw_center_box(canvas, w2, h2, margin); + + int x = FLIPPER_LCD_WIDTH / 2 - w2; + int y = FLIPPER_LCD_HEIGHT / 2 - h2; + + canvas_draw_str(canvas, x + 1, y + 9, " Game Over "); + + FuriString* tmp_string = furi_string_alloc(); + furi_string_printf(tmp_string, "You've got %d points", MAX(0, state->score)); + canvas_draw_str(canvas, x + 1, y + 19, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); + + canvas_draw_str(canvas, x + 2, y + 29, "Press [OK] to restart"); + + int h = 13, w = 54; + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, FLIPPER_LCD_HEIGHT - h, w + 1, h + 1); + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 0, FLIPPER_LCD_HEIGHT - h, w, h); + canvas_draw_str(canvas, 2, FLIPPER_LCD_HEIGHT - 3, "by @Xorboo"); + UNUSED(state); +} + +static void roots_draw_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + GameState* state = ctx; + furi_mutex_acquire(state->mutex, FuriWaitForever); + + if(!state->initialDraw) { + state->initialDraw = true; + + canvas_set_font(canvas, FontSecondary); + reset_level(state); + } + + state->tick++; + + draw_gui(canvas, state); + draw_placed_roots(canvas, state); + draw_pickups(canvas, state); + + switch(state->stage) { + case StageStart: + draw_start_ui(canvas, state); + break; + + case StageRun: + draw_active_root(canvas, state); +#if DRAW_DEBUG + draw_generated_root(canvas, state); +#endif + break; + + case StageOver: + draw_end_ui(canvas, state); + break; + } + + furi_mutex_release(state->mutex); +} + +static void roots_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void roots_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void ProcessStartInput(GameState* state, InputKey key) { + if(key == InputKeyOk) { + state->stage = StageRun; + } +} + +static void ProcessRunInput(GameState* state, InputKey key) { + switch(key) { + case InputKeyRight: + state->pX = MIN(state->pX + 1, CELLS_X - state->rootSizeX); + break; + case InputKeyLeft: + state->pX = MAX(state->pX - 1, 0); + break; + case InputKeyUp: + state->pY = MAX(state->pY - 1, 0); + break; + case InputKeyDown: + state->pY = MIN(state->pY + 1, CELLS_Y - state->rootSizeY); + break; + case InputKeyOk: { + bool rootPlaced = try_place_root(state); + if(rootPlaced) { + recalculate_score(state); + generate_new_root(state); + } else { + state->rerolls--; + if(state->rerolls >= 0) { + generate_new_root(state); + } else { + state->stage = StageOver; + } + } + break; + } + default: + break; + } +} + +static void ProcessOverInput(GameState* state, InputKey key) { + if(key == InputKeyOk) { + state->stage = StageStart; + reset_level(state); + } +} + +int32_t roots_of_life_game_app(void* p) { + FURI_LOG_D(TAG, "Starting game..."); + + UNUSED(p); + int32_t return_code = 0; + + // Set random seed from interrR_UPts + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); + + GameState* state = malloc(sizeof(GameState)); + game_state_init(state); + + state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!state->mutex) { + FURI_LOG_E(TAG, "Cannot create mutex\r\n"); + return_code = 255; + goto free_and_exit; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, roots_draw_callback, state); + view_port_input_callback_set(view_port, roots_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(roots_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 22); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FURI_LOG_D(TAG, "Entering game loop..."); + GameEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + furi_mutex_acquire(state->mutex, FuriWaitForever); + + if(event_status == FuriStatusOk) { + // Key events + if(event.type == EventTypeKey) { + //FURI_LOG_D(TAG, "Got key: %d", event.input.key); + if(event.input.type == InputTypePress || event.input.type == InputTypeLong || + event.input.type == InputTypeRepeat) { + if(event.input.key == InputKeyBack) { + processing = false; + } + + switch(state->stage) { + case StageStart: + ProcessStartInput(state, event.input.key); + break; + case StageRun: + ProcessRunInput(state, event.input.key); + break; + case StageOver: + ProcessOverInput(state, event.input.key); + break; + } + } + } + } + + view_port_update(view_port); + furi_mutex_release(state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + view_port_free(view_port); + furi_mutex_free(state->mutex); +free_and_exit: + furi_message_queue_free(event_queue); + //FURI_LOG_D(TAG, "Quitting game..."); + game_state_free(state); + free(state); + + return return_code; +} \ No newline at end of file diff --git a/applications/external/scorched_tanks/LICENSE b/applications/external/scorched_tanks/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/scorched_tanks/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 of the License, 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. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/scorched_tanks/application.fam b/applications/external/scorched_tanks/application.fam new file mode 100644 index 000000000..c32a07bf2 --- /dev/null +++ b/applications/external/scorched_tanks/application.fam @@ -0,0 +1,12 @@ +App( + appid="scorched_tanks", + name="Scorched Tanks", + apptype=FlipperAppType.EXTERNAL, + entry_point="scorched_tanks_game_app", + cdefines=["APP_SCORCHED_TANKS_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=100, + fap_icon="scorchedTanks_10px.png", + fap_category="Games", +) diff --git a/applications/external/scorched_tanks/scorchedTanks_10px.png b/applications/external/scorched_tanks/scorchedTanks_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..6e1ae4c04b973c4015b6c4bb84ee6e806c4cde04 GIT binary patch literal 614 zcmV-s0-61ZP)EX>4Tx04R}tkv&MmKpe$iQ$;Bi5j%)DWT;LSM5Q`P6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<`9$B7gyeFoUSXOg)ia%)oPe-NVP%y9m$nKKJM7Q}QMQd?N82(+!JwgE+cr z>74h8L#!kz#OK8023?T&k?XR{Z=8z`3p_JqWK#3QA!4!E!Ey()lA#h$6NeR5qkJLj zvch?bvs$UK);;+PgL!Qw&2^e1h+_!}Bq2gZ4P{hdAwsK0iis5M$2|PQjz38*nOtQs zax9<<6_Voz|AXJ%nuV!JHz^ncx?gPjV-)Dw1)6o+{yw(t<_X|`2ClTWzuEw1KS{5* zweS(pzYSbmw>5bWxZDATo^;8O9LY~pC=`JAGy0|+Fn9|D*4*A&`#607GSt=b4RCM> zj1?(+-Q(T8oxS~grq$mM6zy{8hfi!q00006VoOIv00000008+zyMF)x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru<_Z!C4+Jw!Hq!tA02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{002HoL_t&-(~Xcx3IH$+0yXsiU!I-{g>=zVz(I@#P@qa6 z+pYuxs05OvaWdDv_pck&lzjc|*yeeqF3;)U6GWsW10ayR(*OVf07*qoM6N<$g4ARE A`Tzg` literal 0 HcmV?d00001 diff --git a/applications/external/scorched_tanks/scorched_tanks_game_app.c b/applications/external/scorched_tanks/scorched_tanks_game_app.c new file mode 100644 index 000000000..6ebcf9030 --- /dev/null +++ b/applications/external/scorched_tanks/scorched_tanks_game_app.c @@ -0,0 +1,546 @@ +#include +#include +#include +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define PLAYER_INIT_LOCATION_X 20 +#define PLAYER_INIT_AIM 45 +#define PLAYER_INIT_POWER 50 +#define ENEMY_INIT_LOCATION_X 108 +#define TANK_BARREL_LENGTH 8 +#define GRAVITY_FORCE (double)0.5 +#define MIN_GROUND_HEIGHT 35 +#define MAX_GROUND_HEIGHT 55 +#define MAX_FIRE_POWER 100 +#define MIN_FIRE_POWER 0 +#define TANK_COLLIDER_SIZE 3 +#define MAX_WIND 10 +#define MAX_PLAYER_DIFF_X 20 +#define MAX_ENEMY_DIFF_X 20 + +// That's a filthy workaround but sin(player.aimAngle) breaks it all... If you're able to fix it, please do create a PR! +double scorched_tanks_sin[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.122, -0.139, -0.156, -0.174, -0.191, + -0.208, -0.225, -0.242, -0.259, -0.276, -0.292, -0.309, -0.326, -0.342, -0.358, -0.375, -0.391, + -0.407, -0.423, -0.438, -0.454, -0.469, -0.485, -0.500, -0.515, -0.530, -0.545, -0.559, -0.574, + -0.588, -0.602, -0.616, -0.629, -0.643, -0.656, -0.669, -0.682, -0.695, -0.707, -0.719, -0.731, + -0.743, -0.755, -0.766, -0.777, -0.788, -0.799, -0.809, -0.819, -0.829, -0.839, -0.848, -0.857, + -0.866, -0.875, -0.883, -0.891, -0.899, -0.906, -0.914, -0.921, -0.927, -0.934, -0.940, -0.946, + -0.951, -0.956, -0.961, -0.966, -0.970, -0.974, -0.978, -0.982, -0.985, -0.988, -0.990, -0.993, + -0.995, -0.996, -0.998, -0.999, -0.999, -1.000, -1.000}; +double scorched_tanks_cos[91] = { + 1.000, 1.000, 0.999, 0.999, 0.998, 0.996, 0.995, 0.993, 0.990, 0.988, 0.985, 0.982, 0.978, + 0.974, 0.970, 0.966, 0.961, 0.956, 0.951, 0.946, 0.940, 0.934, 0.927, 0.921, 0.914, 0.906, + 0.899, 0.891, 0.883, 0.875, 0.866, 0.857, 0.848, 0.839, 0.829, 0.819, 0.809, 0.799, 0.788, + 0.777, 0.766, 0.755, 0.743, 0.731, 0.719, 0.707, 0.695, 0.682, 0.669, 0.656, 0.643, 0.629, + 0.616, 0.602, 0.588, 0.574, 0.559, 0.545, 0.530, 0.515, 0.500, 0.485, 0.469, 0.454, 0.438, + 0.423, 0.407, 0.391, 0.375, 0.358, 0.342, 0.326, 0.309, 0.292, 0.276, 0.259, 0.242, 0.225, + 0.208, 0.191, 0.174, 0.156, 0.139, 0.122, 0.105, 0.087, 0.070, 0.052, 0.035, 0.017, 0.000}; +double scorched_tanks_tan[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.123, -0.141, -0.158, -0.176, + -0.194, -0.213, -0.231, -0.249, -0.268, -0.287, -0.306, -0.325, -0.344, -0.364, -0.384, + -0.404, -0.424, -0.445, -0.466, -0.488, -0.510, -0.532, -0.554, -0.577, -0.601, -0.625, + -0.649, -0.674, -0.700, -0.727, -0.754, -0.781, -0.810, -0.839, -0.869, -0.900, -0.932, + -0.966, -1.000, -1.036, -1.072, -1.111, -1.150, -1.192, -1.235, -1.280, -1.327, -1.376, + -1.428, -1.483, -1.540, -1.600, -1.664, -1.732, -1.804, -1.881, -1.963, -2.050, -2.144, + -2.246, -2.356, -2.475, -2.605, -2.747, -2.904, -3.078, -3.271, -3.487, -3.732, -4.011, + -4.331, -4.704, -5.144, -5.671, -6.313, -7.115, -8.144, -9.513, -11.429, -14.298, -19.077, + -28.627, -57.254, -90747.269}; +uint8_t scorched_tanks_ground_modifiers[SCREEN_WIDTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 26, 24, 22, 20, + 18, 16, 14, 12, 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +typedef struct { + // +-----x + // | + // | + // y + uint8_t x; + uint8_t y; +} Point; + +typedef struct { + // +-----x + // | + // | + // y + double x; + double y; +} PointDetailed; + +typedef struct { + uint8_t locationX; + uint8_t hp; + int aimAngle; + uint8_t firePower; +} Tank; + +typedef struct { + Point ground[SCREEN_WIDTH]; + Tank player; + Tank enemy; + bool isPlayerTurn; + bool isShooting; + int windSpeed; + Point trajectory[SCREEN_WIDTH]; + uint8_t trajectoryAnimationStep; + PointDetailed bulletPosition; + PointDetailed bulletVector; + FuriMutex* mutex; +} Game; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} ScorchedTanksEvent; + +int scorched_tanks_random(int min, int max) { + return min + rand() % ((max + 1) - min); +} + +void scorched_tanks_generate_ground(Game* game_state) { + int lastHeight = 45; + + for(uint8_t a = 0; a < SCREEN_WIDTH; a++) { + int diffHeight = scorched_tanks_random(-2, 3); + int changeLength = scorched_tanks_random(1, 6); + + if(diffHeight == 0) { + changeLength = 1; + } + + for(int b = 0; b < changeLength; b++) { + if(a + b < SCREEN_WIDTH) { + int index = a + b; + int newPoint = lastHeight + diffHeight; + newPoint = newPoint < MIN_GROUND_HEIGHT ? MIN_GROUND_HEIGHT : newPoint; + newPoint = newPoint > MAX_GROUND_HEIGHT ? MAX_GROUND_HEIGHT : newPoint; + game_state->ground[index].x = index; + game_state->ground[index].y = newPoint - scorched_tanks_ground_modifiers[a]; + lastHeight = newPoint; + } else { + a += b; + break; + } + } + + a += changeLength - 1; + } +} + +void scorched_tanks_init_game(Game* game_state) { + game_state->player.locationX = PLAYER_INIT_LOCATION_X + + scorched_tanks_random(0, MAX_PLAYER_DIFF_X) - + MAX_PLAYER_DIFF_X / 2; + game_state->player.aimAngle = PLAYER_INIT_AIM; + game_state->player.firePower = PLAYER_INIT_POWER; + game_state->enemy.aimAngle = PLAYER_INIT_AIM; + game_state->enemy.firePower = PLAYER_INIT_POWER; + game_state->enemy.locationX = + ENEMY_INIT_LOCATION_X + scorched_tanks_random(0, MAX_ENEMY_DIFF_X) - MAX_ENEMY_DIFF_X / 2; + game_state->isPlayerTurn = true; + + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + scorched_tanks_generate_ground(game_state); +} + +void scorched_tanks_calculate_trajectory(Game* game_state) { + if(game_state->isShooting) { + game_state->bulletVector.x += ((double)game_state->windSpeed - MAX_WIND / 2) / 40; + game_state->bulletVector.y += GRAVITY_FORCE; + + game_state->bulletPosition.x += game_state->bulletVector.x; + game_state->bulletPosition.y += game_state->bulletVector.y; + + int totalDistanceToEnemy = 100; + + if(game_state->isPlayerTurn) { + double distanceToEnemyX = game_state->enemy.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->enemy.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } else { + double distanceToEnemyX = game_state->player.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->player.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } + + if(totalDistanceToEnemy <= TANK_COLLIDER_SIZE) { + game_state->isShooting = false; + scorched_tanks_init_game(game_state); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.x > SCREEN_WIDTH || + game_state->bulletPosition.y > + game_state->ground[(int)round(game_state->bulletPosition.x)].y) { + game_state->isShooting = false; + game_state->bulletPosition.x = 0; + game_state->bulletPosition.y = 0; + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.y > 0) { + game_state->trajectory[game_state->trajectoryAnimationStep].x = + round(game_state->bulletPosition.x); + game_state->trajectory[game_state->trajectoryAnimationStep].y = + round(game_state->bulletPosition.y); + game_state->trajectoryAnimationStep++; + } + } +} + +static void scorched_tanks_draw_tank(Canvas* const canvas, uint8_t x, uint8_t y, bool isPlayer) { + uint8_t lineIndex = 0; + + if(isPlayer) { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + + // draw turret + canvas_draw_line(canvas, x - 2, y - lineIndex, x + 1, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 2, y - lineIndex, x, y - lineIndex); + lineIndex++; + } else { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + + // draw turret + canvas_draw_line(canvas, x - 1, y - lineIndex, x + 2, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x, y - lineIndex, x + 2, y - lineIndex); + lineIndex++; + } +} + +static void scorched_tanks_render_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const Game* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + canvas_draw_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + canvas_set_color(canvas, ColorBlack); + + if(game_state->isShooting) { + canvas_draw_dot(canvas, game_state->bulletPosition.x, game_state->bulletPosition.y); + } + + for(int a = 1; a < SCREEN_WIDTH; a++) { + canvas_draw_line( + canvas, + game_state->ground[a - 1].x, + game_state->ground[a - 1].y, + game_state->ground[a].x, + game_state->ground[a].y); + + if(game_state->trajectory[a].y != 0) { + canvas_draw_dot(canvas, game_state->trajectory[a].x, game_state->trajectory[a].y); + } + } + + scorched_tanks_draw_tank( + canvas, + game_state->enemy.locationX, + game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE, + true); + + scorched_tanks_draw_tank( + canvas, + game_state->player.locationX, + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE, + false); + + int aimX1 = 0; + int aimY1 = 0; + int aimX2 = 0; + int aimY2 = 0; + + if(game_state->isPlayerTurn) { + aimX1 = game_state->player.locationX; + aimY1 = game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX1 += 1; + aimX2 += 1; + } else { + aimX1 = game_state->enemy.locationX; + aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX2 = aimX1 - (aimX2 - aimX1); + + aimX1 -= 1; + aimX2 -= 1; + } + + canvas_draw_line(canvas, aimX1, aimY1 - 3, aimX2, aimY2 - 3); + + canvas_set_font(canvas, FontSecondary); + + char buffer2[18]; + snprintf(buffer2, sizeof(buffer2), "wind: %i", game_state->windSpeed - MAX_WIND / 2); + canvas_draw_str(canvas, 55, 10, buffer2); + + if(game_state->isPlayerTurn) { + canvas_draw_str(canvas, 93, 10, "player1"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->player.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->player.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } else { + canvas_draw_str(canvas, 93, 10, "player2"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->enemy.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->enemy.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } + + furi_mutex_release(game_state->mutex); +} + +static void scorched_tanks_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void scorched_tanks_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void scorched_tanks_increase_power(Game* game_state) { + if(game_state->player.firePower < MAX_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower < MAX_FIRE_POWER) { + game_state->player.firePower++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower < MAX_FIRE_POWER) { + game_state->enemy.firePower++; + } + } +} + +static void scorched_tanks_decrease_power(Game* game_state) { + if(game_state->player.firePower > MIN_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower > MIN_FIRE_POWER) { + game_state->player.firePower--; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower > MIN_FIRE_POWER) { + game_state->enemy.firePower--; + } + } +} + +static void scorched_tanks_aim_up(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.aimAngle < 90) { + game_state->player.aimAngle++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.aimAngle < 90) { + game_state->enemy.aimAngle++; + } + } +} + +static void scorched_tanks_aim_down(Game* game_state) { + if(game_state->player.aimAngle > 0 && !game_state->isShooting) { + if(game_state->isPlayerTurn) { + game_state->player.aimAngle--; + } else { + game_state->enemy.aimAngle--; + } + } +} + +const NotificationSequence sequence_long_vibro = { + &message_vibro_on, + &message_delay_500, + &message_vibro_off, + NULL, +}; + +static void scorched_tanks_fire(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn) { + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + uint8_t aimX1 = game_state->player.locationX; + uint8_t aimY1 = + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = scorched_tanks_cos[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + } else { + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + uint8_t aimX1 = game_state->enemy.locationX; + uint8_t aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + aimX2 = aimX1 - (aimX2 - aimX1); + + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = -scorched_tanks_cos[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + } + + game_state->trajectoryAnimationStep = 0; + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + game_state->isShooting = true; + + NotificationApp* notification = furi_record_open("notification"); + notification_message(notification, &sequence_long_vibro); + notification_message(notification, &sequence_blink_white_100); + furi_record_close("notification"); + } +} + +int32_t scorched_tanks_game_app(void* p) { + UNUSED(p); + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(ScorchedTanksEvent)); + + Game* game_state = malloc(sizeof(Game)); + scorched_tanks_init_game(game_state); + + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { + FURI_LOG_E("ScorchedTanks", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(game_state); + return 255; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, scorched_tanks_render_callback, game_state); + view_port_input_callback_set(view_port, scorched_tanks_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(scorched_tanks_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, 2000); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + ScorchedTanksEvent event; + for(bool processing = true; processing;) { + furi_message_queue_get(event_queue, &event, 50); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(event.type == EventTypeKey) { // && game->isPlayerTurn + if(event.input.type == InputTypeRepeat || event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + scorched_tanks_aim_up(game_state); + break; + case InputKeyDown: + scorched_tanks_aim_down(game_state); + break; + case InputKeyRight: + scorched_tanks_increase_power(game_state); + break; + case InputKeyLeft: + scorched_tanks_decrease_power(game_state); + break; + case InputKeyOk: + scorched_tanks_fire(game_state); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + scorched_tanks_calculate_trajectory(game_state); + } + + view_port_update(view_port); + furi_mutex_release(game_state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(game_state->mutex); + free(game_state); + + return 0; +} diff --git a/applications/external/simonsays/LICENSE b/applications/external/simonsays/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/simonsays/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 of the License, 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. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/simonsays/application.fam b/applications/external/simonsays/application.fam new file mode 100644 index 000000000..8165eb22b --- /dev/null +++ b/applications/external/simonsays/application.fam @@ -0,0 +1,15 @@ +App( + appid="simon_says", # Must be unique + name="Simon Says", # Displayed in UI + apptype=FlipperAppType.EXTERNAL, + entry_point="simon_says_app_entry", + stack_size=2 * 1024, + fap_category="Games", + # Optional values + # fap_version=(0, 1), # (major, minor) + fap_icon="simon_says.png", # 10x10 1-bit PNG + fap_description="A Simon Says Game", + fap_author="SimplyMinimal,ShehabAttia96", + fap_weburl="https://github.com/SimplyMinimal/FlipperZero-SimonSays", + fap_icon_assets="images", # Image assets to compile for this application +) diff --git a/applications/external/simonsays/images/Connected_62x31.png b/applications/external/simonsays/images/Connected_62x31.png new file mode 100644 index 0000000000000000000000000000000000000000..eeaf660b12ea4647154c8d616b468a3098203356 GIT binary patch literal 3765 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 literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinMafia_115x62.png b/applications/external/simonsays/images/DolphinMafia_115x62.png new file mode 100644 index 0000000000000000000000000000000000000000..66fdb40ff2651916faed4a2ae1d564cafdbf7bcb GIT binary patch literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinNiceLeft_96x59.png b/applications/external/simonsays/images/DolphinNiceLeft_96x59.png new file mode 100644 index 0000000000000000000000000000000000000000..a299d3630239b4486e249cc501872bed5996df3b GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1HVZ`K~#8N)t2F6 zBOwTcn|uFv&P{g+A+EZhxHaDoPgG!>+U`A`&*x7nkHB!heHqV5>*uGU)#2vyVsDH}AVsy~ezoEL*Kpbe!+iXUd?~Rwtzo6LrLj)?o;W z%B}S*E^b1t5`5Cx0{z`8Oesd$bKHNRqlaG6PD{IGuR*vKQq03T%4+akggQEz{fhB2 zkVWib2;?%WTF?p3547%+^O>KQHRH=tcKLr$mMokh|q`fz5XzF(Mj!9;jWZ>LNB{745!LW$pD zkadJ}O#GL$L~Z>7gVYy}SQ)m4MI`o7H;6j^7HZ&?>gY2!p)=#O&x04p+kp$E=eQP@#}!k|J2nC}-w9XXHK)ky)? zWi|(UNpSEHpL=IDPQiMlBeznh7JPrj3U{`$Mmf7v$R4KZ4yb?TsX&5lDBPjK>aC)* z+yP>cwL=w1ycy~aM_HYqf7$W{+1^+Ss^Ce3gNuqN9m$B8+)qUj0$sJ@{B+6{erZg9c6DntWH_-f%8n1_=>N5 zLBQ(NkXs#`$1LCaf`ID8{8B28@w~)t#2{v}pB=foCw`qg1|*K~@fFj4I?Bd=oONQt zEejU)z!>HGWH}wNGE;piDz9ommiS5)b*I&P6RHXka*`#xG{|;kv#d@lusTV)+7J6l k;Zb_aMAVv9B7c7V17g|vS5K+|GXMYp07*qoM6N<$f|p|O7XSbN literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinRight_56x48.png b/applications/external/simonsays/images/DolphinRight_56x48.png new file mode 100644 index 0000000000000000000000000000000000000000..a0fcba21838c09095daae8551025b8e2f8030a90 GIT binary patch literal 601 zcmV-f0;c_mP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0q#jeK~!i%&6Haf zgD?mMwfo<*rQRHhfCPr0+Hl$M=O-~xWa1riV$rM9qJP`7MI@V}uDQTO}5Xl9QS% nZe`HUO%wHVx9HqBw{80Z5rL-iUB=Na00000NkvXXu0mjfU|j(! literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinTalking_59x63.png b/applications/external/simonsays/images/DolphinTalking_59x63.png new file mode 100644 index 0000000000000000000000000000000000000000..46f559f65f11194c94c15bb7f195a1d72ef2a295 GIT binary patch literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinWait_61x59.png b/applications/external/simonsays/images/DolphinWait_61x59.png new file mode 100644 index 0000000000000000000000000000000000000000..423e079199b00df0d910981caf8944cbaf8ee67e GIT binary patch literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/WarningDolphin_45x42.png b/applications/external/simonsays/images/WarningDolphin_45x42.png new file mode 100644 index 0000000000000000000000000000000000000000..d766ffbb444db1739f2ccd030e506e8bada11ee8 GIT binary patch literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/board.png b/applications/external/simonsays/images/board.png new file mode 100644 index 0000000000000000000000000000000000000000..7640db5946908dfced504134d72fc6bad481f55b GIT binary patch literal 826 zcmV-A1I7G_P)KtS*B?{IK%KR-Wze}9jUkH5daZ*OleFE0=f z5Km7}udlCPUtgb}pF~7NBqSv6?(QQaBRV=dCnqO4IXUg^?IX*~ zHd19_~QlfI^JY$IVs^?Hr zpE*W(SHf;L~KfQf`yD`fR5J0xEm6%3hd4wL#L`75b}( zTb&z<#5$d%vFshBm5Vie?)j734R_-}T6FeWxcX9loNLyF5xa>Tn}^Z3`brMhMywJe z#UzhYM&WR9d2QOJn#;Ho#T^d+uIzAB7+{osZX;l`A%bWe$YR96Q7L1?a@NJcR(#V- zwEQz*-TQZ>lmA1>*cX7MGXOHtVgjWGYb)HbPG8|pE*Re2Y8xb%P~*Vj#7^azos`17 zaE<%o+}+F12lL`I`!#&m^cF7MW2Sx7qPh3ab`A2Dpw-z9*C|0rO6n|ldtBNn zc&fZUJIbrV^~x*D8gDU2+>87*ou;Z<$1g|gKpb^b=Dvm_aSO6hG4>nu0~ZV%^ebg~ z14~RdVSL^Ep;yF4%cFkFkGR9bR`PeWZ$AuO_OV6fdyfrcPSFy^(^2?WN;pz)FJ5?_ zGjNKE6H*<1uO&reW18adQ?1=nlPx#1ZP1_K>z@;j|==^1poj5%uq~JMgRZ*&(F`0kdRRV zDVqm3=V2p__fz;0dlK(OBPn_kSilYV#j$?tD2hJEe!}zk!I2zJ#DJq?I1ciKt8efk zsujog;MGjIRqO8wU3G2Z!Mv2b#256m>#9Qvozt`S*MZk-R(?(BMD?~h;3PB&1vQMQ z&vu=t4lAX2?cJH?isH|1)zvDMkuf)o40pB>OLWJcu2GrSH`Tx&4Nr8pcgA5{mAt_8;!;ED{M}#Vw6PRR0 zJH9$$)qbdwodUB}COhS3i!A!cr>x?#M}y^z8ex(ZPRXf=i0kx}#0&7oXqc6+qXn>Q zOm)44DO|mVM7Z#DbT`;8oDIN~uS~w>($$e~2gJM2DP=xVJkA>(&q%$$hwjFa1@k$r zbyU7XnC1Y&SDIxGIfl;PrtF#SY*B^0G&=uKsE?qOO6wP_3yvV^j`l-ZrS)^t(E_`j z;dt#*Y5godUR6nxsZob1IAs`Is{Gl1L9|okqS}#In&_f3C8qZ!nxd`=7jr{*@C1ng z=E_f!yr8pPWVEo;@kdjRkrXlR_?;mN``hC&@@k`%V!-R_FdS?b30dBg{}(zpdU?l~ c{%Mr@1JGzPQsxqccK`qY07*qoM6N<$f~Y5<(EtDd literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/left.png b/applications/external/simonsays/images/left.png new file mode 100644 index 0000000000000000000000000000000000000000..1eb6b58ee05def3c5e0fac6c7a8300a487fdd540 GIT binary patch literal 921 zcmV;K17`e*P)Px#1ZP1_K>z@;j|==^1poj5%uq~JMgRZ*&(F`0kdRx zK~zXf)s|~_(=ZH%9Ux(3tQ2PnD}j}*<+9NY1}k?Mm;e8tkYu^BBzNKP3Ep$sW664w z*pe^qzg6M;SxK5rmlZolRJA(+iCX({vg$clsJ|YpTaJ6)d*3wP_qW*5)*O;i5k60h zr&>f3*7w&g#8PyBy zH{V&Hw=55~aP`N~uZB?Xf$uK-@*Gj8@cSVefe|){>9)cB7!W(lyjK- zf}JRu->A7Nhz&&(&<7_feHln{^#^bf6_PYm!)=Nlv`Dzi3ve(+&mTsoR!UhuJK-L8 z@)pcPrksN^TXQq!EY%C1mk!@`v7y`#Fyo})#A>}Ht~-F{`%8)<)nG5VP1UIN1URDh z-fsHrKM~a z&eKhx;?jjIuumO;p3AnNWAW`q7qJTc(lC+Ju(z? zs}83?o-H+Qvbcnv7jAAP<}COzV%DX5!npgs;2KfVNzo vvLhCPx#1ZP1_K>z@;j|==^1poj5%uq~JMgRZ*&(F`0kdRV~azOuQVbdXR(y4jd9;@yd2D%0*8Cvsr zi#dGZcp-z+7JkB9;^_m`=D~x1=GfubEIV$R<4(chd-KM)PGgCU`$feD3| z^quPTLs1y~@^E3;&+7Ce$pvQm&l)-_B^Jtkq0Lsl#+^c$0s11V&svLlm{11V(1GMUUV zkV959C89dYPCtR9qyQ+KzH8DVYyqg;))YSOQBcYqHNU}@o14p9>=&QNM{~WD#1Q*J zVJmOeqIsPmT?HoU9~WwdxdX5MaVL^bFR=o4(OZZprRpxdR-UKGaB zkyp9rOin*JN>4{rq?7*49H>`3-oZ(byMXP+fNva`Wl#224&jlb)*Shl!4xyKxhsqu z`R;!9PT>PE7i72^|!gs5V(Ux+;DkfYV{XaHG$p-7sJC_Ukv{_isCn% WhcZ%O?_L)G0000KtS*B?^IM&1qB5V5D=fApF~7NPft%98ykOr zf4{%KBqStnZ*T7I?i?H(IyyQ(KR+!kEni<>EG#T5D=R4}DK<7XC@3heudgR3CnY5% zkB^TgCMFIJ4lge+3JMD2~83_pq=H})f zA0Of2;Rpx_Dk>^FJ3H;|?NCrqIXO8!Jv~Q9M>I4v4-XHpu&{7&aKOO84JNt}00009 za7bBm000ie000ie0hKEb8vp5@T+hkj%i zhNyAK!E^S-N6i=v$}yGj7jr_`S06>lod4r|DH^xgHyLAJKa{0ve zZPWpi-QJTdf)qs>?!2aUHA=&#MCUGm zI@2kUC_Ze0!|cNJ`eQRCsa#I#k^psiVBrupHv$0E1rvWWX8?Gj#Rl~&X&P5bxMP{V zrIuVU_&VvDH@C76lB;amfuc$6T3bh(FrS*rUBOkjaMT6;zT=PgxNxfO8@Nn$8E&um zi$9t4QHqW>1yj6-eaWqPT9TehHb+UGQJ-1RFXKb$|<6_by z2i0&;^Obg}Hb;k9DTuRGia4lN4vKSPpRgRL;ZP>U7)_q8Oi tRHn-NuWI#_<#<^m%W?V1i{U?Le*q}3GEzALUP}M~002ovPDHLkV1iojbSeM< literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/simon_says.c b/applications/external/simonsays/simon_says.c new file mode 100644 index 000000000..f537cb364 --- /dev/null +++ b/applications/external/simonsays/simon_says.c @@ -0,0 +1,666 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Header-file for boolean data-type. +#include +#include + +/* generated by fbt from .png files in images folder */ +#include + +#define TAG "Simon" // Used for logging +#define DEBUG_MSG 1 +#define SCREEN_XRES 128 +#define SCREEN_YRES 64 +#define BOARD_X 72 // Used for board placement +#define BOARD_Y 8 +#define GAME_START_LIVES 3 +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/game_simon_says.save" + +// Define Notes +// Shamelessly stolen from Ocarina application +// https://github.com/invalidna-me/flipperzero-ocarina +#define NOTE_UP 587.33f +#define NOTE_LEFT 493.88f +#define NOTE_RIGHT 440.00f +#define NOTE_DOWN 349.23 +#define NOTE_OK 293.66f + +/* ============================ Data structures ============================= */ + +typedef enum game_state { preloading, mainMenu, inGame, gameOver, gameVictory } game_state; + +typedef enum difficulty_mode { normal, hard } difficulty_mode; + +typedef enum shape_names { up, down, left, right, number_of_shapes } Direction; + +typedef enum currently_playing { simon, player } currently_playing; + +typedef struct { + /* Game state. */ + enum game_state gameState; // This is the current game state + bool gameover; /* if true then switch to the game over state */ + bool is_wrong_direction; /* Is the last direction wrong? */ + enum currently_playing activePlayer; // This is used to track who is playing at the moment + uint32_t lives; /* Number of lives in the current game. */ + + enum difficulty_mode difficultyMode; // This is the difficulty mode for the current game + bool sound_enabled; // This is the sound enabled flag for the current game + float volume; // This is the volume for the current game + + /* Handle Score */ + int currentScore; // This is the score for the current + int highScore; /* Highscore. Shown on Game Over Screen */ + bool is_new_highscore; /* Is the last score a new highscore? */ + + /* Handle Shape Display */ + uint32_t numberOfMillisecondsBeforeShapeDisappears; // This defines the speed of the game + enum shape_names simonMoves[1000]; // Store the sequence of shapes that Simon plays + enum shape_names selectedShape; // This is used to track the shape that the player has selected + bool set_board_neutral; // This is used to track if the board should be neutral or not + int moveIndex; // This is used to track the current move in the sequence + + uint32_t last_button_press_tick; + NotificationApp* notification; + FuriMutex* mutex; +} SimonData; + +/* ============================== Sequences ============================== */ + +const NotificationSequence sequence_wrong_move = { + &message_red_255, + + &message_vibro_on, + // &message_note_g5, // Play sound but currently disabled + &message_delay_25, + // &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_player_submit_move = { + &message_vibro_on, + // &message_note_g5, // Play sound but currently disabled. Need On/Off menu setting + &message_delay_10, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + + // &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_up = { + // &message_vibro_on, + &message_note_g4, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_down = { + // &message_vibro_on, + &message_note_c3, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_left = { + // &message_vibro_on, + &message_note_e3, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_right = { + // &message_vibro_on, + &message_note_g3, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +// Indicate that it's Simon's turn +const NotificationSequence sequence_simon_is_playing = { + &message_red_255, + &message_do_not_reset, + NULL, +}; + +// Indicate that it's the Player's turn +const NotificationSequence sequence_player_is_playing = { + &message_red_0, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_cleanup = { + &message_red_0, + &message_green_0, + &message_blue_0, + &message_sound_off, + &message_vibro_off, + NULL, +}; + +/* ============================ 2D drawing ================================== */ + +/* Display remaining lives in the center of the board */ +void draw_remaining_lives(Canvas* canvas, const SimonData* simon_state) { + // Convert score to string + // int length = snprintf(NULL, 0, "%lu", simon_state->lives); + // char* str_lives_remaining = malloc(length + 1); + // snprintf(str_lives_remaining, length + 1, "%lu", simon_state->lives); + + // TODO: Make it a Simon Says icon on top right + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + int x = SCREEN_XRES - 6; + int lives = simon_state->lives; + while(lives--) { + canvas_draw_str(canvas, x, 8, "*"); + x -= 7; + } +} + +void draw_current_score(Canvas* canvas, const SimonData* simon_data) { + /* Draw Game Score. */ + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontSecondary); + char str_score[32]; + snprintf(str_score, sizeof(str_score), "%i", simon_data->currentScore); + canvas_draw_str_aligned(canvas, SCREEN_XRES / 2 + 4, 2, AlignCenter, AlignTop, str_score); +} + +void play_sound_up(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_UP, app->volume); + } +} + +void play_sound_down(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_DOWN, app->volume); + } +} + +void play_sound_left(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_LEFT, app->volume); + } +} + +void play_sound_right(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_RIGHT, app->volume); + } +} + +void stop_sound() { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } +} + +/* Main Render Function */ +void simon_draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const SimonData* simon_state = ctx; + furi_mutex_acquire(simon_state->mutex, FuriWaitForever); + + canvas_clear(canvas); + + // ######################### Main Menu ######################### + // Show Main Menu + if(simon_state->gameState == mainMenu) { + // Draw border frame + canvas_draw_frame(canvas, 1, 1, SCREEN_XRES - 1, SCREEN_YRES - 1); // Border + + // Draw Simon text banner + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, + SCREEN_XRES / 2, + SCREEN_YRES / 2 - 4, + AlignCenter, + AlignCenter, + "Welcome to Simon Says"); + + // Display Press OK to start below title + canvas_set_color(canvas, ColorXOR); + canvas_draw_str_aligned( + canvas, + SCREEN_XRES / 2, + SCREEN_YRES / 2 + 10, + AlignCenter, + AlignCenter, + "Press OK to start"); + } + + // ######################### in Game ######################### + //@todo Render Callback + // We're in an active game + if(simon_state->gameState == inGame) { + // Draw Current Score + draw_current_score(canvas, simon_state); + + // Draw Lives + draw_remaining_lives(canvas, simon_state); + + // Draw Simon Pose + if(simon_state->activePlayer == player) { + // Player's turn + canvas_draw_icon(canvas, 0, 4, &I_DolphinWait_61x59); + } else { + // Simon's turn + canvas_draw_icon(canvas, 0, 4, &I_DolphinTalking_59x63); + } + + if(simon_state->set_board_neutral) { + // Draw Neutral Board + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_board); // Draw Board + + // Stop Sound TODO: Move this to a better place + //@todo Sound + stop_sound(); + } else { + switch(simon_state->selectedShape) { + case up: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_up); // Draw Up + play_sound_up(simon_state); + break; + case down: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_down); // Draw Down + play_sound_down(simon_state); + break; + case left: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_left); // Draw Left + play_sound_left(simon_state); + break; + case right: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_right); // Draw Right + play_sound_right(simon_state); + break; + default: + if(DEBUG_MSG) + FURI_LOG_E( + TAG, "Invalid shape: %d", simon_state->simonMoves[simon_state->moveIndex]); + break; + } + } + } + + // ######################### Game Over ######################### + if(simon_state->gameState == gameOver) { + stop_sound(); //TODO: Make a game over sequence + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontPrimary); + + // TODO: if new highscore, display blinking "New High Score" + // Display High Score Text + if(simon_state->is_new_highscore) { + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, 6, AlignCenter, AlignTop, "New High Score!"); + } else { + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, 6, AlignCenter, AlignTop, "High Score"); + } + + // Convert highscore to string + int length = snprintf(NULL, 0, "%i", simon_state->highScore); + char* str_high_score = malloc(length + 1); + snprintf(str_high_score, length + 1, "%i", simon_state->highScore); + + // Display High Score + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, 22, AlignCenter, AlignCenter, str_high_score); + free(str_high_score); + + // Display Game Over + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, SCREEN_YRES / 2 + 2, AlignCenter, AlignCenter, "GAME OVER"); + + // Display Press OK to restart below title + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + SCREEN_XRES / 2, + SCREEN_YRES / 2 + 15, + AlignCenter, + AlignCenter, + "Press OK to restart"); + } + + // ######################### Victory ######################### + //Player Beat Simon beyond limit! A word record holder here! + //TODO + + //release the mutex + furi_mutex_release(simon_state->mutex); +} + +/* ======================== Input Handling ============================== */ + +void simon_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +/* ======================== Simon Game Engine ======================== */ + +bool load_game(SimonData* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* file = storage_file_alloc(storage); + + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) { + if(storage_file_size(file) > sizeof(SimonData)) { + storage_simply_remove(storage, SAVING_FILENAME); + FURI_LOG_E( + TAG, "Error: file is larger than the data structure! The file has been deleted."); + } else { + bytes_readed = storage_file_read(file, app, sizeof(SimonData)); + } + storage_file_close(file); + storage_file_free(file); + } + + furi_record_close(RECORD_STORAGE); + return bytes_readed == sizeof(SimonData); +} + +void save_game(SimonData* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { + return; + } + } + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, app, sizeof(SimonData)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +int getRandomIntInRange(int lower, int upper) { + return (rand() % (upper - lower + 1)) + lower; +} + +void play_sound_sequence_correct() { + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_success); +} + +void play_sound_wrong_move() { + //TODO: play wrong sound: Try sequence_audiovisual_alert + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_error); +} + +/* Restart game and give player a chance to try again on same sequence */ +// @todo restartGame +void resetGame(SimonData* app) { + app->moveIndex = 0; + app->numberOfMillisecondsBeforeShapeDisappears = 500; + app->activePlayer = simon; + app->is_wrong_direction = false; + app->last_button_press_tick = 0; + app->set_board_neutral = true; + app->activePlayer = simon; +} + +/* Set gameover state */ +void game_over(SimonData* app) { + if(app->is_new_highscore) save_game(app); // Save highscore but only on change + app->gameover = true; + app->lives = GAME_START_LIVES; // Show 3 lives in game over screen to match new game start + app->gameState = gameOver; +} + +/* Called after gameover to restart the game. This function + * also calls restart_game(). */ +void restart_game_after_gameover(SimonData* app) { + app->volume = 1.0f; //TODO: make this a setting + app->gameState = inGame; + app->gameover = false; + app->currentScore = 0; + app->is_new_highscore = false; + app->lives = GAME_START_LIVES; + app->simonMoves[0] = rand() % number_of_shapes; + resetGame(app); +} + +void addNewSimonMove(int addAtIndex, SimonData* app) { + app->simonMoves[addAtIndex] = getRandomIntInRange(0, 3); +} + +void startNewRound(SimonData* app) { + addNewSimonMove(app->currentScore, app); + app->moveIndex = 0; + app->activePlayer = simon; +} + +void onPlayerAnsweredCorrect(SimonData* app) { + app->moveIndex++; +} + +void onPlayerAnsweredWrong(SimonData* app) { + if(app->lives > 0) { + app->lives--; + + // Play the wrong sound + if(app->sound_enabled) { + play_sound_wrong_move(); + } + resetGame(app); + } else { + // The player has no lives left + // Game over + game_over(app); + //TODO: Play unique game over sound + } +} + +bool isRoundComplete(SimonData* app) { + return app->moveIndex == app->currentScore; +} + +enum shape_names getCurrentSimonMove(SimonData* app) { + return app->simonMoves[app->moveIndex]; +} + +void onPlayerSelectedShapeCallback(enum shape_names shape, SimonData* app) { + if(shape == getCurrentSimonMove(app)) { + onPlayerAnsweredCorrect(app); + } else { + onPlayerAnsweredWrong(app); + } +} + +//@todo gametick +void game_tick(SimonData* simon_state) { + if(simon_state->gameState == inGame) { + if(simon_state->activePlayer == simon) { + // ############### Simon Turn ############### + notification_message(simon_state->notification, &sequence_simon_is_playing); + + //@todo Gameplay + if(simon_state->set_board_neutral) { + if(simon_state->moveIndex < simon_state->currentScore) { + simon_state->selectedShape = getCurrentSimonMove(simon_state); + simon_state->set_board_neutral = false; + simon_state->moveIndex++; + } else { + simon_state->activePlayer = player; + simon_state->set_board_neutral = true; + simon_state->moveIndex = 0; + } + } else { + simon_state->set_board_neutral = true; + } + } else { + // ############### Player Turn ############### + notification_message(simon_state->notification, &sequence_player_is_playing); + + // It's Player's Turn + if(isRoundComplete(simon_state)) { + simon_state->activePlayer = simon; + simon_state->currentScore++; + // app->numberOfMillisecondsBeforeShapeDisappears -= 50; + //TODO: Hacky way of handling highscore by subtracting 1 to account for the first move + if(simon_state->currentScore - 1 > simon_state->highScore) { + simon_state->highScore = simon_state->currentScore - 1; + simon_state->is_new_highscore = true; + } + if(simon_state->sound_enabled) { + play_sound_sequence_correct(); + } + startNewRound(simon_state); + } + } + } +} + +/* ======================== Main Entry Point ============================== */ + +int32_t simon_says_app_entry(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + SimonData* simon_state = malloc(sizeof(SimonData)); + + simon_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!simon_state->mutex) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + free(simon_state); + return -1; + } + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, simon_draw_callback, simon_state); + view_port_input_callback_set(view_port, simon_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + simon_state->notification = notification; + + InputEvent input; + + // Show Main Menu Screen + //load_game(simon_state); + restart_game_after_gameover(simon_state); + simon_state->gameState = mainMenu; + + while(true) { + game_tick(simon_state); + + FuriStatus q_status = furi_message_queue_get( + event_queue, &input, simon_state->numberOfMillisecondsBeforeShapeDisappears); + furi_mutex_acquire(simon_state->mutex, FuriWaitForever); + + if(q_status == FuriStatusOk) { + //FURI_LOG_D(TAG, "Got input event: %d", input.key); + //break out of the loop if the back key is pressed + if(input.key == InputKeyBack && input.type == InputTypeLong) { + // Save high score before quitting + //if(simon_state->is_new_highscore) { + // save_game(simon_state); + //} + break; + } + + //@todo Set Game States + if(input.key == InputKeyOk && simon_state->gameState != inGame) { + restart_game_after_gameover(simon_state); + // Set Simon Board state + startNewRound(simon_state); + view_port_update(view_port); + } + + // Keep LED on if it is Simon's turn + if(simon_state->activePlayer == player) { + notification_message(notification, &sequence_player_is_playing); + + if(input.type == InputTypePress) { + simon_state->set_board_neutral = false; + + switch(input.key) { + case InputKeyUp: + simon_state->selectedShape = up; + onPlayerSelectedShapeCallback(up, simon_state); + break; + case InputKeyDown: + simon_state->selectedShape = down; + onPlayerSelectedShapeCallback(down, simon_state); + break; + case InputKeyLeft: + simon_state->selectedShape = left; + onPlayerSelectedShapeCallback(left, simon_state); + break; + case InputKeyRight: + simon_state->selectedShape = right; + onPlayerSelectedShapeCallback(right, simon_state); + break; + default: + simon_state->set_board_neutral = true; + break; + } + } else { + //FURI_LOG_D(TAG, "Input type is not short"); + simon_state->set_board_neutral = true; + } + } + } + + // @todo Animation Loop for debug + // if(simon_state->gameState == inGame && simon_state->activePlayer == simon) { + // simon_state->currentScore++; + // simon_state->set_board_neutral = !simon_state->set_board_neutral; + // } + + view_port_update(view_port); + furi_mutex_release(simon_state->mutex); + } + + stop_sound(); + notification_message(notification, &sequence_cleanup); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(simon_state->mutex); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + free(simon_state); + + return 0; +} diff --git a/applications/external/simonsays/simon_says.png b/applications/external/simonsays/simon_says.png new file mode 100644 index 0000000000000000000000000000000000000000..6fe9ba3e6f2acf28e94bdb001606248d1ea0e96a GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>vG;Uw4ABTq z{`3F;|9Unh24x>bWo6~Crv}9VB1+PHmpuX$cYRzE-FvoC;dSRGspU=+R=$usl%-_$ f+E7G@tC(San8)KerT;B~MlpE0`njxgN@xNAKO-r( literal 0 HcmV?d00001 diff --git a/applications/external/snake_game/application.fam b/applications/external/snake_2/application.fam similarity index 55% rename from applications/external/snake_game/application.fam rename to applications/external/snake_2/application.fam index 1e1ee48a6..f35642b7c 100644 --- a/applications/external/snake_game/application.fam +++ b/applications/external/snake_2/application.fam @@ -1,11 +1,12 @@ App( - appid="snake", - name="Snake Game", + appid="snake20", + name="Snake 2.0", apptype=FlipperAppType.EXTERNAL, - entry_point="snake_game_app", + entry_point="snake_20_app", + cdefines=["APP_SNAKE_20"], requires=["gui"], stack_size=1 * 1024, - order=210, + order=30, fap_icon="snake_10px.png", fap_category="Games", ) diff --git a/applications/external/snake_2/snake_10px.png b/applications/external/snake_2/snake_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..125cfb3341b00f9c3cf93a6ae88fb2042722bbf0 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ihk44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfQS24TkI`72U@g6^I!jv*SswY`CS3ysNL|NBE6XVjK0w?j96T5q6LB$mUSx$ok&E|(OC&e_JdFCDjd Uc|mutex, FuriWaitForever); + // Before the function is called, the state is set with the canvas_reset(canvas) + // Frame canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -105,6 +111,9 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { f.x = f.x * 4 + 1; f.y = f.y * 4 + 1; canvas_draw_rframe(canvas, f.x, f.y, 6, 6, 2); + canvas_draw_dot(canvas, f.x + 3, f.y - 1); + canvas_draw_dot(canvas, f.x + 4, f.y - 2); + //canvas_draw_dot(canvas,f.x+4,f.y-3); // Snake for(uint16_t i = 0; i < snake_state->len; i++) { @@ -112,24 +121,70 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { p.x = p.x * 4 + 2; p.y = p.y * 4 + 2; canvas_draw_box(canvas, p.x, p.y, 4, 4); + if(i == 0) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, p.x + 1, p.y + 1, 2, 2); + canvas_set_color(canvas, ColorBlack); + } } - // Game Over banner - if(snake_state->state == GameStateGameOver) { + // Pause and GameOver banner + if(snake_state->state == GameStatePause || snake_state->state == GameStateGameOver) { // Screen is 128x64 px canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 34, 20, 62, 24); + canvas_draw_box(canvas, 33, 19, 64, 26); canvas_set_color(canvas, ColorBlack); canvas_draw_frame(canvas, 34, 20, 62, 24); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 37, 31, "Game Over"); + if(snake_state->state == GameStateGameOver) { + canvas_draw_str_aligned(canvas, 65, 31, AlignCenter, AlignBottom, "Game Over"); + } + if(snake_state->state == GameStatePause) { + canvas_draw_str_aligned(canvas, 65, 31, AlignCenter, AlignBottom, "Pause"); + } canvas_set_font(canvas, FontSecondary); - char buffer[12]; + char buffer[20]; snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U); - canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); + canvas_draw_str_aligned(canvas, 65, 41, AlignCenter, AlignBottom, buffer); + + // Painting "back"-symbol, Help message for Exit App, ProgressBar + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 25, 2, 81, 11); + canvas_draw_box(canvas, 28, 54, 73, 9); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, 65, 10, AlignCenter, AlignBottom, "Hold to Exit App"); + snprintf( + buffer, sizeof(buffer), "Complete: %-5.1f%%", (double)((snake_state->len - 7U) / 4.58)); + canvas_draw_str_aligned(canvas, 65, 62, AlignCenter, AlignBottom, buffer); + { + canvas_draw_dot(canvas, x_back_symbol + 0, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 1, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 2, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 3, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 4, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 5, y_back_symbol - 1); + canvas_draw_dot(canvas, x_back_symbol + 6, y_back_symbol - 2); + canvas_draw_dot(canvas, x_back_symbol + 6, y_back_symbol - 3); + canvas_draw_dot(canvas, x_back_symbol + 5, y_back_symbol - 4); + canvas_draw_dot(canvas, x_back_symbol + 4, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 3, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 2, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 1, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 0, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 2, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 3, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 2, y_back_symbol - 6); + canvas_draw_dot(canvas, x_back_symbol - 2, y_back_symbol - 4); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 6); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 4); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 7); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 3); + } } furi_mutex_release(snake_state->mutex); @@ -297,6 +352,7 @@ static void if(eatFruit) { snake_state->len++; if(snake_state->len >= MAX_SNAKE_LEN) { + //You win!!! snake_state->state = GameStateGameOver; notification_message_block(notification, &sequence_fail); return; @@ -308,10 +364,11 @@ static void if(eatFruit) { snake_state->fruit = snake_game_get_new_fruit(snake_state); notification_message(notification, &sequence_eat); + notification_message(notification, &sequence_blink_red_100); } } -int32_t snake_game_app(void* p) { +int32_t snake_20_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); @@ -320,7 +377,6 @@ int32_t snake_game_app(void* p) { snake_game_init_game(snake_state); snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!snake_state->mutex) { FURI_LOG_E("SnakeGame", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); @@ -343,6 +399,8 @@ int32_t snake_game_app(void* p) { notification_message_block(notification, &sequence_display_backlight_enforce_on); + // dolphin_deed(DolphinDeedPluginGameStart); + SnakeEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); @@ -350,26 +408,83 @@ int32_t snake_game_app(void* p) { furi_mutex_acquire(snake_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { - // press events if(event.type == EventTypeKey) { + // press events if(event.input.type == InputTypePress) { switch(event.input.key) { case InputKeyUp: - snake_state->nextMovement = DirectionUp; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionUp; + } break; case InputKeyDown: - snake_state->nextMovement = DirectionDown; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionDown; + } break; case InputKeyRight: - snake_state->nextMovement = DirectionRight; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionRight; + } break; case InputKeyLeft: - snake_state->nextMovement = DirectionLeft; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionLeft; + } break; case InputKeyOk: if(snake_state->state == GameStateGameOver) { snake_game_init_game(snake_state); } + if(snake_state->state == GameStatePause) { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + snake_state->state = GameStateLife; + } + break; + case InputKeyBack: + if(snake_state->state == GameStateLife) { + furi_timer_stop(timer); + snake_state->state = GameStatePause; + break; + } + if(snake_state->state == GameStatePause) { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + snake_state->state = GameStateLife; + break; + } + if(snake_state->state == GameStateGameOver) { + snake_game_init_game(snake_state); + } + default: + break; + } + } + //LongPress Events + if(event.input.type == InputTypeLong) { + switch(event.input.key) { + case InputKeyUp: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionUp; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } + break; + case InputKeyDown: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionDown; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } + break; + case InputKeyRight: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionRight; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } + break; + case InputKeyLeft: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionLeft; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } break; case InputKeyBack: processing = false; @@ -378,6 +493,12 @@ int32_t snake_game_app(void* p) { break; } } + //ReleaseKey Event + if(event.input.type == InputTypeRelease) { + if(snake_state->state != GameStatePause) { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + } + } } else if(event.type == EventTypeTick) { snake_game_process_game_step(snake_state, notification); } @@ -389,8 +510,8 @@ int32_t snake_game_app(void* p) { furi_mutex_release(snake_state->mutex); } - // Return backlight to normal state - notification_message(notification, &sequence_display_backlight_enforce_auto); + // Wait for all notifications to be played and return backlight to normal state + notification_message_block(notification, &sequence_display_backlight_enforce_auto); furi_timer_free(timer); view_port_enabled_set(view_port, false); diff --git a/applications/external/snake_game/snake_10px.png b/applications/external/snake_game/snake_10px.png deleted file mode 100644 index 52d9fa7e0e1b884774a6e58abb1965b1b1905767..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxc>kDAIJlX?eOh zhE&W+PDn^dVNp_G*rbrd!ONE5$kL$2+HH6!MA=j6#)(e`V#>-4Tp0`o%bUNP1L~43 zag8Vm&QB{TPb^AhaL6gmODsst%q!6^$V=Bv&QD2A{^~3#2UN)5>FVdQ&MBb@0GSOf APyhe` diff --git a/applications/external/t_rex_runner/LICENSE b/applications/external/t_rex_runner/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/t_rex_runner/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 of the License, 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. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/t_rex_runner/application.fam b/applications/external/t_rex_runner/application.fam new file mode 100644 index 000000000..88623b9c4 --- /dev/null +++ b/applications/external/t_rex_runner/application.fam @@ -0,0 +1,13 @@ +App( + appid="t_rex_runner", + name="T-Rex runner", + apptype=FlipperAppType.EXTERNAL, + entry_point="trexrunner_app", + cdefines=["APP_TREXRUNNER"], + requires=["gui"], + stack_size=8 * 1024, + fap_category="Games", + fap_icon="trexrunner_icon.png", + fap_icon_assets="assets", + order=36, +) diff --git a/applications/external/t_rex_runner/assets/Cactus.png b/applications/external/t_rex_runner/assets/Cactus.png new file mode 100644 index 0000000000000000000000000000000000000000..0b832932760cca3d8844bca7960926960db06ecc GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIaRt)<|NmclbN*c*OWD)K zF@)oK@{ihvhl`mHyRx-)^6&^tNJz;@@W~`paEP!dGOU=$qhxGhwg{-4!PC{xWt~$( F69C)u9Nqu` literal 0 HcmV?d00001 diff --git a/applications/external/t_rex_runner/assets/Dino.png b/applications/external/t_rex_runner/assets/Dino.png new file mode 100644 index 0000000000000000000000000000000000000000..9e33092d6e97d0bb70365d5b88d902e21173d5df GIT binary patch literal 4339 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 xFg#-aBQIfhqCVk=<$vbG^M53A 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 literal 0 HcmV?d00001 diff --git a/applications/external/t_rex_runner/trexrunner.c b/applications/external/t_rex_runner/trexrunner.c new file mode 100644 index 000000000..d7db91fad --- /dev/null +++ b/applications/external/t_rex_runner/trexrunner.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "t_rex_runner_icons.h" + +#define DINO_START_X 10 +#define DINO_START_Y 34 // 64 - 22 - BACKGROUND_H / 2 - 2 + +#define FPS 20 + +#define DINO_RUNNING_MS_PER_FRAME 500 + +#define GRAVITY 60 +#define JUMP_SPEED 30 + +#define CACTUS_W 10 +#define CACTUS_H 10 +#define START_x_speed 25 + +#define BACKGROUND_W 128 +#define BACKGROUND_H 12 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + FuriTimer* timer; + uint32_t last_tick; + const Icon* dino_icon; + int dino_frame_ms; + FuriMutex* mutex; + + // Dino info + float y_position; + float y_speed; + int y_acceleration; + float x_speed; + + // Cactus info + int cactus_position; + int has_cactus; + + // Horizontal line + int background_position; + + int lost; + + int score; +} GameState; + +static void timer_callback(void* ctx) { + GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(game_state == NULL) { + return; + } + + uint32_t ticks_elapsed = furi_get_tick() - game_state->last_tick; + game_state->last_tick = furi_get_tick(); + int delta_time_ms = ticks_elapsed * 1000 / furi_kernel_get_tick_frequency(); + + // dino update + game_state->dino_frame_ms += delta_time_ms; + // TODO: switch by dino state + if(game_state->dino_frame_ms >= DINO_RUNNING_MS_PER_FRAME) { + if(game_state->dino_icon == &I_DinoRun0) { + game_state->dino_icon = &I_DinoRun1; + } else { + game_state->dino_icon = &I_DinoRun0; + } + game_state->dino_frame_ms = 0; + } + + // Compute dino dynamics + game_state->y_acceleration = game_state->y_acceleration - GRAVITY * delta_time_ms / 1000; + game_state->y_speed = game_state->y_speed + game_state->y_acceleration * delta_time_ms / 1000; + game_state->y_position = game_state->y_position - game_state->y_speed * delta_time_ms / 1000; + + // Touch ground + if(game_state->y_position >= DINO_START_Y) { + game_state->y_acceleration = 0; + game_state->y_speed = 0; + game_state->y_position = DINO_START_Y; + } + + // Update Cactus state + if(game_state->has_cactus) { + game_state->cactus_position = + game_state->cactus_position - game_state->x_speed * delta_time_ms / 1000; + if(game_state->cactus_position <= 0) { + game_state->has_cactus = 0; + game_state->score = game_state->score + 1; + } + } + // Create cactus (not random) + else { + game_state->has_cactus = 1; + game_state->cactus_position = 120; + } + + // Move horizontal line + if(game_state->background_position <= -BACKGROUND_W) + game_state->background_position += BACKGROUND_W; + game_state->background_position = + game_state->background_position - game_state->x_speed * delta_time_ms / 1000; + + // Lose condition + if((game_state->y_position + 22 >= (64 - CACTUS_H)) && + ((DINO_START_X + 20) >= game_state->cactus_position) && + (DINO_START_X <= (game_state->cactus_position + CACTUS_W))) + game_state->lost = 1; + + furi_mutex_release(game_state->mutex); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(game_state == NULL) { + return; + } + + char score_string[12]; + if(!game_state->lost) { + // Show Ground + canvas_draw_icon( + canvas, game_state->background_position, 64 - BACKGROUND_H, &I_HorizonLine0); + canvas_draw_icon( + canvas, + game_state->background_position + BACKGROUND_W, + 64 - BACKGROUND_H, + &I_HorizonLine0); + + // Show DINO + canvas_draw_icon(canvas, DINO_START_X, game_state->y_position, game_state->dino_icon); + + // Show cactus + if(game_state->has_cactus) + //canvas_draw_triangle(canvas, game_state->cactus_position, 64 - BACKGROUND_H + CACTUS_W, CACTUS_W, CACTUS_H, CanvasDirectionBottomToTop); + canvas_draw_icon( + canvas, + game_state->cactus_position, + 64 - BACKGROUND_H / 2 - CACTUS_H - 2, + &I_Cactus); + + // Show score + if(game_state->score == 0) canvas_set_font(canvas, FontSecondary); + snprintf(score_string, 12, "Score: %d", game_state->score); + canvas_draw_str_aligned(canvas, 85, 5, AlignLeft, AlignTop, score_string); + + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, "You lost :c"); + } + + furi_mutex_release(game_state->mutex); +} + +static void game_state_init(GameState* const game_state) { + game_state->last_tick = furi_get_tick(); + game_state->dino_frame_ms = 0; + game_state->dino_icon = &I_Dino; + game_state->y_acceleration = game_state->y_speed = 0; + game_state->y_position = DINO_START_Y; + game_state->has_cactus = 0; + game_state->background_position = 0; + game_state->lost = 0; + game_state->x_speed = START_x_speed; + game_state->score = 0; + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); +} + +int32_t trexrunner_app() { + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + GameState* game_state = malloc(sizeof(GameState)); + game_state_init(game_state); + + if(!game_state->mutex) { + FURI_LOG_E("T-rex runner", "cannot create mutex\r\n"); + free(game_state); + return 255; + } + // BEGIN IMPLEMENTATION + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, game_state); + view_port_input_callback_set(view_port, input_callback, event_queue); + game_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, game_state); + + furi_timer_start(game_state->timer, (uint32_t)furi_kernel_get_tick_frequency() / FPS); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing && !game_state->lost;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyLeft: + break; + case InputKeyRight: + break; + case InputKeyOk: + if(game_state->y_position == DINO_START_Y) + game_state->y_speed = JUMP_SPEED; + break; + case InputKeyMAX: + break; + case InputKeyBack: + // Exit the app + processing = false; + break; + } + } + } + } else { + // event timeout + ; + } + if(game_state->lost) { + furi_message_queue_get( + event_queue, &event, 1500); //Sleep to show the "you lost" message + } + view_port_update(view_port); + furi_mutex_release(game_state->mutex); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(game_state->mutex); + furi_timer_free(game_state->timer); + free(game_state); + + return 0; +} diff --git a/applications/external/t_rex_runner/trexrunner_icon.png b/applications/external/t_rex_runner/trexrunner_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0b832932760cca3d8844bca7960926960db06ecc GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIaRt)<|NmclbN*c*OWD)K zF@)oK@{ihvhl`mHyRx-)^6&^tNJz;@@W~`paEP!dGOU=$qhxGhwg{-4!PC{xWt~$( F69C)u9Nqu` literal 0 HcmV?d00001 diff --git a/applications/external/t_rex_runner/uncut_assets/HorizonLine0.png b/applications/external/t_rex_runner/uncut_assets/HorizonLine0.png new file mode 100644 index 0000000000000000000000000000000000000000..b326cbb7f470f4d0796e18cbe8babfd065303422 GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0y~yV2S{;c^H|2WQ#^%IFMos@Ck7R(%-*-&pmM_9>{9& zba4#PIG_B(-}Zok(3wSQ5_cYcJuj#D#b552Ro}85vfC{ce0l00Ec^5CKf4}l(qHCB z`wJ%iRCx67zkI;YDvrDSYUTgxeJ=F>Pw4rsap?Jfl9R zTq{#`%DM6hk$)2UKOR2v?s1ullm7p5m5=8OE;H=iqdDtX^Y(K`N<9fa_k_4qS!W_|n# zLio)(|A#oUp8u;q|F0g^EOa3MkAcDd0Rut6|DQn~T{kv6=b!&i4!~;G0eMQy`e%<6 z+i;VPqbY93lSF`;82-Rb`TZXe>>$=ZApVQ+H;B0Z{|8ha8tVOEa_9fYIQ490km z8rA=i#2=&kaX;84hmjoc5vJhpc?_>Yfju$@lx!LJ!5)KWocp*PiO|cBoZvshO#=Xk WcPDMu%7Baj0000