| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- #include <stdlib.h>
- #include <dolphin/dolphin.h>
- #include <furi.h>
- #include <gui/canvas_i.h>
- #include "defines.h"
- #include "common/ui.h"
- #include "solitaire_icons.h"
- #include <notification/notification.h>
- #include <notification/notification_messages.h>
- void init(GameState* game_state);
- const NotificationSequence sequence_fail = {
- &message_vibro_on,
- &message_note_c4,
- &message_delay_10,
- &message_vibro_off,
- &message_sound_off,
- &message_delay_10,
- &message_vibro_on,
- &message_note_a3,
- &message_delay_10,
- &message_vibro_off,
- &message_sound_off,
- NULL,
- };
- int8_t columns[7][3] = {
- {1, 1, 25},
- {19, 1, 25},
- {37, 1, 25},
- {55, 1, 25},
- {73, 1, 25},
- {91, 1, 25},
- {109, 1, 25},
- };
- bool can_place_card(Card where, Card what) {
- bool a_black = where.pip == 0 || where.pip == 3;
- bool b_black = what.pip == 0 || what.pip == 3;
- if(a_black == b_black) return false;
- int8_t a_letter = (int8_t)where.character;
- int8_t b_letter = (int8_t)what.character;
- if(a_letter == 12) a_letter = -1;
- if(b_letter == 12) b_letter = -1;
- return (a_letter - 1) == b_letter;
- }
- static void draw_scene(Canvas* const canvas, const GameState* game_state) {
- if(game_state->had_change) {
- int deckIndex = game_state->deck.index;
- if(game_state->dragging_deck) deckIndex--;
- if((game_state->deck.index < (game_state->deck.card_count - 1) ||
- game_state->deck.index == -1) &&
- game_state->deck.card_count > 0) {
- draw_card_back_at(columns[0][0], columns[0][1], canvas);
- if(game_state->selectRow == 0 && game_state->selectColumn == 0) {
- draw_rounded_box(
- canvas,
- columns[0][0] + 1,
- columns[0][1] + 1,
- CARD_WIDTH - 2,
- CARD_HEIGHT - 2,
- Inverse);
- }
- } else
- draw_card_space(
- columns[0][0],
- columns[0][1],
- game_state->selectRow == 0 && game_state->selectColumn == 0,
- canvas);
- //deck side
- if(deckIndex >= 0) {
- Card c = game_state->deck.cards[deckIndex];
- draw_card_at_colored(
- columns[1][0],
- columns[1][1],
- c.pip,
- c.character,
- game_state->selectRow == 0 && game_state->selectColumn == 1,
- canvas);
- } else
- draw_card_space(
- columns[1][0],
- columns[1][1],
- game_state->selectRow == 0 && game_state->selectColumn == 1,
- canvas);
- for(uint8_t i = 0; i < 4; i++) {
- Card current = game_state->top_cards[i];
- bool selected = game_state->selectRow == 0 && game_state->selectColumn == (i + 3);
- if(current.disabled) {
- draw_card_space(columns[i + 3][0], columns[i + 3][1], selected, canvas);
- } else {
- draw_card_at(
- columns[i + 3][0], columns[i + 3][1], current.pip, current.character, canvas);
- if(selected) {
- draw_rounded_box(
- canvas,
- columns[i + 3][0],
- columns[i + 3][1],
- CARD_WIDTH,
- CARD_HEIGHT,
- Inverse);
- }
- }
- }
- for(uint8_t i = 0; i < 7; i++) {
- bool selected = game_state->selectRow == 1 && game_state->selectColumn == i;
- int8_t index = (game_state->bottom_columns[i].index - 1 - game_state->selected_card);
- if(index < 0) index = 0;
- draw_hand_column(
- game_state->bottom_columns[i],
- columns[i][0],
- columns[i][2],
- selected ? index : -1,
- canvas);
- }
- int8_t pos[2] = {
- columns[game_state->selectColumn][0],
- columns[game_state->selectColumn][game_state->selectRow + 1]};
- /* draw_icon_clip(canvas, &I_card_graphics, pos[0] + CARD_HALF_WIDTH, pos[1] + CARD_HALF_HEIGHT, 30, 5, 5, 5,
- Filled);*/
- if(game_state->dragging_hand.index > 0) {
- draw_hand_column(
- game_state->dragging_hand,
- pos[0] + CARD_HALF_WIDTH + 3,
- pos[1] + CARD_HALF_HEIGHT + 3,
- -1,
- canvas);
- }
- clone_buffer(get_buffer(canvas), game_state->animation.buffer);
- } else {
- clone_buffer(game_state->animation.buffer, get_buffer(canvas));
- }
- }
- static void draw_animation(Canvas* const canvas, const GameState* game_state) {
- if(!game_state->animation.started) {
- draw_scene(canvas, game_state);
- } else {
- clone_buffer(game_state->animation.buffer, get_buffer(canvas));
- draw_card_at(
- (int8_t)game_state->animation.x,
- (int8_t)game_state->animation.y,
- game_state->animation.card.pip,
- game_state->animation.card.character,
- canvas);
- }
- clone_buffer(get_buffer(canvas), game_state->animation.buffer);
- }
- static void render_callback(Canvas* const canvas, void* ctx) {
- const GameState* game_state = ctx;
- furi_mutex_acquire(game_state->mutex, 25);
- if(game_state == NULL) {
- return;
- }
- switch(game_state->state) {
- case GameStateAnimate:
- draw_animation(canvas, game_state);
- break;
- case GameStateStart:
- canvas_draw_icon(canvas, 0, 0, &I_solitaire_main);
- break;
- case GameStatePlay:
- draw_scene(canvas, game_state);
- break;
- default:
- break;
- }
- furi_mutex_release(game_state->mutex);
- }
- void remove_drag(GameState* game_state) {
- if(game_state->dragging_deck) {
- remove_from_deck(game_state->deck.index, &(game_state->deck));
- game_state->dragging_deck = false;
- } else if(game_state->dragging_column < 7) {
- game_state->dragging_column = 8;
- }
- game_state->dragging_hand.index = 0;
- }
- bool handleInput(GameState* game_state) {
- Hand currentHand = game_state->bottom_columns[game_state->selectColumn];
- switch(game_state->input) {
- case InputKeyUp:
- if(game_state->selectRow > 0) {
- int first = first_non_flipped_card(currentHand);
- first = currentHand.index - first;
- if((first - 1) > game_state->selected_card && game_state->dragging_hand.index == 0 &&
- !game_state->longPress) {
- game_state->selected_card++;
- } else {
- game_state->selectRow--;
- game_state->selected_card = 0;
- }
- }
- break;
- case InputKeyDown:
- if(game_state->selectRow < 1) {
- game_state->selectRow++;
- game_state->selected_card = 0;
- } else {
- if(game_state->selected_card > 0) {
- if(game_state->longPress)
- game_state->selected_card = 0;
- else
- game_state->selected_card--;
- }
- }
- break;
- case InputKeyRight:
- if(game_state->selectColumn < 6) {
- game_state->selectColumn++;
- game_state->selected_card = 0;
- }
- break;
- case InputKeyLeft:
- if(game_state->selectColumn > 0) {
- game_state->selectColumn--;
- game_state->selected_card = 0;
- }
- break;
- case InputKeyOk:
- return true;
- break;
- default:
- break;
- }
- if(game_state->selectRow == 0 && game_state->selectColumn == 2) {
- if(game_state->input == InputKeyRight)
- game_state->selectColumn++;
- else
- game_state->selectColumn--;
- }
- if(game_state->dragging_hand.index > 0) game_state->selected_card = 0;
- return false;
- }
- bool place_on_top(Card* where, Card what) {
- if(where->disabled && what.character == 12) {
- where->disabled = what.disabled;
- where->pip = what.pip;
- where->character = what.character;
- return true;
- } else if(where->pip == what.pip) {
- int8_t a_letter = (int8_t)where->character;
- int8_t b_letter = (int8_t)what.character;
- if(a_letter == 12) a_letter = -1;
- if(b_letter == 12) b_letter = -1;
- if(where->disabled && b_letter != -1) return false;
- if((a_letter + 1) == b_letter) {
- where->disabled = what.disabled;
- where->pip = what.pip;
- where->character = what.character;
- return true;
- }
- }
- return false;
- }
- void tick(GameState* game_state, NotificationApp* notification) {
- game_state->last_tick = furi_get_tick();
- uint8_t row = game_state->selectRow;
- uint8_t column = game_state->selectColumn;
- if(game_state->state != GameStatePlay && game_state->state != GameStateAnimate) return;
- bool wasAction = false;
- if(game_state->state == GameStatePlay) {
- if(game_state->top_cards[0].character == 11 && game_state->top_cards[1].character == 11 &&
- game_state->top_cards[2].character == 11 && game_state->top_cards[3].character == 11) {
- game_state->state = GameStateAnimate;
- game_state->had_change = true;
- dolphin_deed(DolphinDeedPluginGameWin);
- return;
- }
- }
- if(handleInput(game_state)) {
- if(game_state->state == GameStatePlay) {
- if(game_state->longPress && game_state->dragging_hand.index == 1) {
- for(uint8_t i = 0; i < 4; i++) {
- if(place_on_top(
- &(game_state->top_cards[i]), game_state->dragging_hand.cards[0])) {
- remove_drag(game_state);
- wasAction = true;
- break;
- }
- }
- } else {
- if(row == 0 && column == 0 && game_state->dragging_hand.index == 0) {
- FURI_LOG_D(APP_NAME, "Drawing card");
- game_state->deck.index++;
- wasAction = true;
- if(game_state->deck.index >= (game_state->deck.card_count))
- game_state->deck.index = -1;
- }
- //pick/place from deck
- else if(row == 0 && column == 1) {
- //place
- if(game_state->dragging_deck) {
- wasAction = true;
- game_state->dragging_deck = false;
- game_state->dragging_hand.index = 0;
- }
- //pick
- else {
- if(game_state->dragging_hand.index == 0 && game_state->deck.index >= 0) {
- wasAction = true;
- game_state->dragging_deck = true;
- add_to_hand(
- &(game_state->dragging_hand),
- game_state->deck.cards[game_state->deck.index]);
- }
- }
- }
- //place on top row
- else if(row == 0 && game_state->dragging_hand.index == 1) {
- column -= 3;
- Card currCard = game_state->dragging_hand.cards[0];
- wasAction = place_on_top(&(game_state->top_cards[column]), currCard);
- if(wasAction) remove_drag(game_state);
- }
- //pick/place from bottom
- else if(row == 1) {
- Hand* curr_hand = &(game_state->bottom_columns[column]);
- //pick up
- if(game_state->dragging_hand.index == 0) {
- Card curr_card = curr_hand->cards[curr_hand->index - 1];
- if(curr_card.flipped) {
- curr_hand->cards[curr_hand->index - 1].flipped = false;
- wasAction = true;
- } else {
- if(curr_hand->index > 0) {
- extract_hand_region(
- curr_hand,
- &(game_state->dragging_hand),
- curr_hand->index - game_state->selected_card - 1);
- game_state->selected_card = 0;
- game_state->dragging_column = column;
- wasAction = true;
- }
- }
- }
- //place
- else {
- Card first = game_state->dragging_hand.cards[0];
- if(game_state->dragging_column == column ||
- (curr_hand->index == 0 && first.character == 11) ||
- can_place_card(curr_hand->cards[curr_hand->index - 1], first)) {
- add_hand_region(curr_hand, &(game_state->dragging_hand));
- remove_drag(game_state);
- wasAction = true;
- }
- }
- }
- }
- if(!wasAction) {
- notification_message(notification, &sequence_fail);
- }
- }
- }
- if(game_state->state == GameStateAnimate) {
- if(game_state->animation.started && !game_state->longPress &&
- game_state->input == InputKeyOk) {
- init(game_state);
- game_state->state = GameStateStart;
- }
- game_state->animation.started = true;
- if(game_state->animation.x < -20 || game_state->animation.x > 128) {
- game_state->animation.deck++;
- if(game_state->animation.deck > 3) game_state->animation.deck = 0;
- int8_t cardIndex = 11 - game_state->animation.indexes[game_state->animation.deck];
- if(game_state->animation.indexes[0] == 13 && game_state->animation.indexes[1] == 13 &&
- game_state->animation.indexes[2] == 13 && game_state->animation.indexes[3] == 13) {
- init(game_state);
- game_state->state = GameStateStart;
- return;
- }
- if(cardIndex == -1) cardIndex = 12;
- game_state->animation.card = (Card){
- game_state->top_cards[game_state->animation.deck].pip, cardIndex, false, false};
- game_state->animation.indexes[game_state->animation.deck]++;
- game_state->animation.vx = -(rand() % 3 + 1) * (rand() % 2 == 1 ? 1 : -1);
- game_state->animation.vy = (rand() % 3 + 1);
- game_state->animation.x = columns[game_state->animation.deck + 3][0];
- game_state->animation.y = columns[game_state->animation.deck + 3][1];
- }
- game_state->animation.x += game_state->animation.vx;
- game_state->animation.y -= game_state->animation.vy;
- game_state->animation.vy -= 1;
- if(game_state->animation.vy < -10) game_state->animation.vy = -10;
- if(game_state->animation.y > 41) {
- game_state->animation.y = 41;
- game_state->animation.vy = -(game_state->animation.vy * 0.7f);
- }
- }
- }
- void init(GameState* game_state) {
- dolphin_deed(DolphinDeedPluginGameStart);
- game_state->selectColumn = 0;
- game_state->selected_card = 0;
- game_state->selectRow = 0;
- generate_deck(&(game_state->deck), 1);
- shuffle_deck(&(game_state->deck));
- game_state->dragging_deck = false;
- game_state->animation.started = false;
- game_state->animation.deck = -1;
- game_state->animation.x = -21;
- game_state->state = GameStatePlay;
- game_state->dragging_column = 8;
- for(uint8_t i = 0; i < 7; i++) {
- free_hand(&(game_state->bottom_columns[i]));
- init_hand(&(game_state->bottom_columns[i]), 21);
- game_state->bottom_columns[i].index = 0;
- for(uint8_t j = 0; j <= i; j++) {
- Card cur = remove_from_deck(0, &(game_state->deck));
- cur.flipped = i != j;
- add_to_hand(&(game_state->bottom_columns[i]), cur);
- }
- }
- for(uint8_t i = 0; i < 4; i++) {
- game_state->animation.indexes[i] = 0;
- game_state->top_cards[i] = (Card){0, 0, true, false};
- }
- game_state->deck.index = -1;
- }
- void init_start(GameState* game_state) {
- game_state->input = InputKeyMAX;
- for(uint8_t i = 0; i < 7; i++)
- init_hand(&(game_state->bottom_columns[i]), 21);
- init_hand(&(game_state->dragging_hand), 13);
- game_state->animation.buffer = make_buffer();
- }
- static void input_callback(InputEvent* input_event, void* ctx) {
- furi_assert(ctx);
- FuriMessageQueue* event_queue = ctx;
- AppEvent event = {.type = EventTypeKey, .input = *input_event};
- furi_message_queue_put(event_queue, &event, FuriWaitForever);
- }
- static void update_timer_callback(void* ctx) {
- furi_assert(ctx);
- FuriMessageQueue* event_queue = ctx;
- AppEvent event = {.type = EventTypeTick};
- furi_message_queue_put(event_queue, &event, 0);
- }
- int32_t solitaire_app(void* p) {
- UNUSED(p);
- int32_t return_code = 0;
- FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
- GameState* game_state = malloc(sizeof(GameState));
- init_start(game_state);
- set_card_graphics(&I_card_graphics);
- game_state->state = GameStateStart;
- game_state->processing = true;
- game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- if(!game_state->mutex) {
- FURI_LOG_E(APP_NAME, "cannot create mutex\r\n");
- return_code = 255;
- goto free_and_exit;
- }
- NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
- notification_message_block(notification, &sequence_display_backlight_enforce_on);
- 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);
- FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue);
- furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30);
- Gui* gui = furi_record_open("gui");
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
- AppEvent event;
- for(bool processing = true; processing;) {
- FuriStatus event_status = furi_message_queue_get(event_queue, &event, 150);
- furi_mutex_acquire(game_state->mutex, FuriWaitForever);
- game_state->had_change = false;
- if(event_status == FuriStatusOk) {
- if(event.type == EventTypeKey) {
- game_state->had_change = true;
- if(event.input.type == InputTypeLong) {
- game_state->longPress = true;
- switch(event.input.key) {
- case InputKeyUp:
- case InputKeyDown:
- case InputKeyRight:
- case InputKeyLeft:
- case InputKeyOk:
- game_state->input = event.input.key;
- break;
- case InputKeyBack:
- processing = false;
- return_code = 1;
- default:
- break;
- }
- } else if(event.input.type == InputTypePress) {
- game_state->longPress = false;
- switch(event.input.key) {
- case InputKeyUp:
- case InputKeyDown:
- case InputKeyRight:
- case InputKeyLeft:
- case InputKeyOk:
- if(event.input.key == InputKeyOk && game_state->state == GameStateStart) {
- game_state->state = GameStatePlay;
- init(game_state);
- } else {
- game_state->input = event.input.key;
- }
- break;
- case InputKeyBack:
- init(game_state);
- processing = false;
- return_code = 1;
- break;
- default:
- break;
- }
- }
- } else if(event.type == EventTypeTick) {
- tick(game_state, notification);
- processing = game_state->processing;
- game_state->input = InputKeyMAX;
- }
- }
- furi_mutex_release(game_state->mutex);
- view_port_update(view_port);
- }
- notification_message_block(notification, &sequence_display_backlight_enforce_auto);
- 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(game_state->mutex);
- free_and_exit:
- free(game_state->animation.buffer);
- ui_cleanup();
- for(uint8_t i = 0; i < 7; i++)
- free_hand(&(game_state->bottom_columns[i]));
- free(game_state->deck.cards);
- free(game_state);
- furi_message_queue_free(event_queue);
- return return_code;
- }
|