| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- #include <furi.h>
- #include <gui/gui.h>
- #include "dolphin/dolphin_state.h"
- #define MAX_TRIES 3
- #define DISHES_TOTAL 3
- #define LID_POS_MAX 20
- #define TRY_TIMEOUT 10
- typedef enum {
- EventTypeTick,
- EventTypeKey,
- EventTypeDeed,
- } EventType;
- typedef struct {
- union {
- InputEvent input;
- } value;
- EventType type;
- } AppEvent;
- typedef enum {
- PlayerChoiceEvent,
- OpenLootEvent,
- WinEvent,
- LooseEvent,
- FinishedEvent,
- ExitGameEvent,
- GameEventTotal,
- } GameEventType;
- typedef enum {
- LootSkeleton,
- LootFish,
- LootShit,
- LootTotalNum,
- } LootIdEnum;
- typedef struct {
- GameEventType current_event;
- osMessageQueueId_t event_queue;
- LootIdEnum loot_list[DISHES_TOTAL];
- uint8_t cursor_pos;
- uint8_t lid_pos;
- uint8_t timeout;
- uint8_t try;
- bool selected;
- bool deed;
- } GameState;
- typedef struct {
- const Icon* f;
- const Icon* b;
- } LootGfx;
- static const Icon* letters[DISHES_TOTAL] = {&I_letterA_10x10, &I_letterB_10x10, &I_letterC_10x10};
- static const LootGfx loot[LootTotalNum] = {
- [LootSkeleton] =
- {
- .f = &I_skeleton_25x17,
- .b = &I_blackskeleton_25x17,
- },
- [LootFish] =
- {
- .f = &I_fish_25x17,
- .b = &I_blackfish_25x17,
- },
- [LootShit] =
- {
- .f = &I_shit_25x17,
- .b = &I_blackshit_25x17,
- },
- };
- static void input_callback(InputEvent* input_event, void* ctx) {
- osMessageQueueId_t event_queue = ctx;
- AppEvent event;
- event.type = EventTypeKey;
- event.value.input = *input_event;
- osMessageQueuePut(event_queue, &event, 0, osWaitForever);
- }
- static void draw_dish(Canvas* canvas, GameState* state, uint8_t x, uint8_t y, uint8_t id) {
- bool active = state->cursor_pos == id;
- bool opened = state->current_event == OpenLootEvent && active;
- canvas_set_bitmap_mode(canvas, true);
- canvas_set_color(canvas, ColorBlack);
- if(active) {
- canvas_draw_icon(canvas, x, y, &I_active_plate_48x18);
- }
- if(opened) {
- state->lid_pos = CLAMP(state->lid_pos + 1, LID_POS_MAX, 0);
- }
- uint8_t lid_pos = (y - 17) - (opened ? state->lid_pos : 0);
- canvas_draw_icon(canvas, x + 3, y - 6, &I_plate_42x19);
- canvas_set_color(canvas, ColorWhite);
- canvas_draw_icon(canvas, x + 11, y - 10, loot[state->loot_list[id]].b);
- canvas_set_color(canvas, ColorBlack);
- canvas_draw_icon(canvas, x + 11, y - 10, loot[state->loot_list[id]].f);
- canvas_set_color(canvas, ColorWhite);
- canvas_draw_icon(canvas, x + 6, lid_pos, &I_blacklid_36x27);
- canvas_set_color(canvas, ColorBlack);
- canvas_draw_icon(canvas, x + 6, lid_pos, &I_lid_36x27);
- canvas_set_bitmap_mode(canvas, false);
- canvas_draw_icon(canvas, x + 19, y + 8, letters[id]);
- }
- static void draw_dishes_scene(Canvas* canvas, GameState* state) {
- uint8_t tries_left = MAX_TRIES - state->try;
- for(size_t i = 0; i < MAX_TRIES; i++) {
- if(i < tries_left) {
- canvas_draw_disc(canvas, 5 + i * 8, 5, 2);
- } else {
- canvas_draw_circle(canvas, 5 + i * 8, 5, 2);
- }
- }
- for(size_t i = 0; i < DISHES_TOTAL; i++) {
- draw_dish(canvas, state, i * 40, i % 2 ? 26 : 44, i);
- }
- }
- static void render_callback(Canvas* canvas, void* ctx) {
- GameState* state = (GameState*)acquire_mutex((ValueMutex*)ctx, 25);
- canvas_clear(canvas);
- switch(state->current_event) {
- case WinEvent:
- canvas_draw_str(canvas, 30, 30, "Dolphin_happy.png");
- break;
- case LooseEvent:
- canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignCenter, "Try again!");
- break;
- case ExitGameEvent:
- break;
- case FinishedEvent:
- break;
- default:
- draw_dishes_scene(canvas, state);
- break;
- }
- release_mutex((ValueMutex*)ctx, state);
- }
- static void reset_lid_pos(GameState* state) {
- state->selected = false;
- state->lid_pos = 0;
- }
- void dolphin_food_deed(GameState* state) {
- furi_assert(state);
- AppEvent event;
- event.type = EventTypeDeed;
- furi_check(osMessageQueuePut(state->event_queue, &event, 0, osWaitForever) == osOK);
- }
- static void reset_loot_array(GameState* state) {
- for(size_t i = 0; i < LootTotalNum; i++) {
- state->loot_list[i] = i;
- }
- for(size_t i = 0; i < LootTotalNum; i++) {
- int temp = state->loot_list[i];
- int r_idx = rand() % LootTotalNum;
- state->loot_list[i] = state->loot_list[r_idx];
- state->loot_list[r_idx] = temp;
- }
- }
- static bool selected_is_food(GameState* state) {
- return state->loot_list[state->cursor_pos] == LootFish;
- }
- static bool tries_exceed(GameState* state) {
- return state->try == MAX_TRIES;
- }
- static bool timeout_exceed(GameState* state) {
- return state->timeout == TRY_TIMEOUT;
- }
- static void gamestate_update(GameState* state, DolphinState* dolphin_state) {
- switch(state->current_event) {
- case PlayerChoiceEvent:
- if(state->selected) {
- state->current_event = OpenLootEvent;
- }
- break;
- case OpenLootEvent:
- state->timeout = CLAMP(state->timeout + 1, TRY_TIMEOUT, 0);
- if(timeout_exceed(state)) {
- state->timeout = 0;
- state->current_event = selected_is_food(state) ? WinEvent : LooseEvent;
- state->deed = selected_is_food(state);
- }
- break;
- case LooseEvent:
- state->timeout = CLAMP(state->timeout + 1, TRY_TIMEOUT, 0);
- if(timeout_exceed(state)) {
- state->timeout = 0;
- state->current_event = FinishedEvent;
- }
- break;
- case WinEvent:
- if(state->deed) {
- dolphin_food_deed(state);
- }
- break;
- case FinishedEvent:
- reset_lid_pos(state);
- reset_loot_array(state);
- state->try++;
- state->current_event = tries_exceed(state) ? ExitGameEvent : PlayerChoiceEvent;
- break;
- default:
- break;
- }
- }
- static void food_minigame_controls(GameState* state, AppEvent* event) {
- furi_assert(state);
- furi_assert(event);
- if(event->value.input.key == InputKeyRight) {
- if(state->current_event == PlayerChoiceEvent) {
- state->cursor_pos = CLAMP(state->cursor_pos + 1, DISHES_TOTAL - 1, 0);
- }
- } else if(event->value.input.key == InputKeyLeft) {
- if(state->current_event == PlayerChoiceEvent) {
- state->cursor_pos = CLAMP(state->cursor_pos - 1, DISHES_TOTAL - 1, 0);
- }
- } else if(event->value.input.key == InputKeyOk) {
- switch(state->current_event) {
- case PlayerChoiceEvent:
- state->selected = true;
- break;
- case WinEvent:
- state->current_event = FinishedEvent;
- break;
- default:
- break;
- }
- }
- }
- int32_t food_minigame_app(void* p) {
- GameState* state = furi_alloc(sizeof(GameState));
- DolphinState* dolphin_state = dolphin_state_alloc();
- dolphin_state_load(dolphin_state);
- ValueMutex state_mutex;
- state->event_queue = osMessageQueueNew(2, sizeof(AppEvent), NULL);
- furi_check(state->event_queue);
- if(!init_mutex(&state_mutex, state, sizeof(GameState*))) {
- printf("[Food minigame] cannot create mutex\r\n");
- return 0;
- }
- ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, render_callback, &state_mutex);
- view_port_input_callback_set(view_port, input_callback, state->event_queue);
- Gui* gui = furi_record_open("gui");
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
- reset_loot_array(state);
- AppEvent event;
- while(1) {
- osStatus_t event_status = osMessageQueueGet(state->event_queue, &event, NULL, 100);
- if(event_status == osOK) {
- if(event.type == EventTypeKey && event.value.input.type == InputTypeShort) {
- food_minigame_controls(state, &event);
- if(event.value.input.key == InputKeyBack) {
- break;
- }
- } else if(event.type == EventTypeDeed) {
- dolphin_state_on_deed(dolphin_state, DolphinDeedIButtonRead);
- dolphin_state_save(dolphin_state);
- state->deed = false;
- }
- }
- if(state->current_event == ExitGameEvent) {
- break;
- }
- gamestate_update(state, dolphin_state);
- view_port_update(view_port);
- }
- gui_remove_view_port(gui, view_port);
- view_port_free(view_port);
- furi_record_close("gui");
- delete_mutex(&state_mutex);
- osMessageQueueDelete(state->event_queue);
- dolphin_state_free(dolphin_state);
- free(state);
- return 0;
- }
|