| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- #include "GameLogic.h"
- #include "utils/Sprite.h"
- #include "assets.h"
- static Sprite logo = Sprite(sprite_logo, BlackOnly);
- static Sprite solve = Sprite(sprite_solve, BlackOnly);
- static Sprite main_image = Sprite(sprite_main_image, BlackOnly);
- static Sprite start = Sprite(sprite_start, BlackOnly);
- GameLogic::GameLogic(RenderBuffer *b, InputEventHandler *inputHandler) : buffer(b) {
- inputHandler->subscribe(this, [](void *ctx, int key, InputType type) {
- auto *inst = (GameLogic *) ctx;
- inst->Input(key, type);
- });
- }
- void GameLogic::Input(int key, InputType type) {
- if (type == InputTypeShort) {
- if (key == InputKeyBack && state == Play) {
- state = Logo;
- target[0] = 0;
- target[1] = 0;
- return;
- } else if (key == InputKeyOk) {
- if (state == Logo) {
- state = Intro;
- return;
- }
- if (state == Solve) {
- end = furi_get_tick();
- state = Finish;
- return;
- }
- if (state == Finish) {
- state = Logo;
- return;
- } else if (state == Intro) {
- for (int i = 0; i < 7; i++) {
- while ((int) tableau[i].size() < (i + 1)) {
- tableau[i].push_back(stock.pop_back());
- }
- tableau[i].peek_back()->exposed = true;
- }
- tempCard = nullptr;
- state = Play;
- startTime = furi_get_tick();
- buffer->clear();
- DrawPlayScene();
- } else if (state == Play) {
- if (selection[0] == 0 && selection[1] == 0) {
- //Cycle waste and stock
- if (hand.size() == 0) {
- if (stock.size() > 0) {
- //move from stock to waste
- waste.push_back(stock.pop_back());
- waste.peek_back()->exposed = true;
- } else {
- //move back all the card
- while (waste.size() > 0) {
- waste.peek_back()->exposed = false;
- stock.push_back(waste.pop_back());
- }
- }
- return;
- }
- return;
- } else if (selection[1] == 1 || selection[0] > 0) {
- PickAndPlace();
- }
- return;
- }
- return;
- }
- if (state == Play)
- HandleNavigation(key);
- } else if (type == InputTypeLong) {
- switch (key) {
- case InputKeyLeft:
- selection[0] = 0;
- selectedCard = 1;
- break;
- case InputKeyRight:
- selection[0] = 6;
- selectedCard = 1;
- break;
- case InputKeyUp:
- selectedCard = 1;
- selection[1] = 0;
- break;
- case InputKeyOk:
- if (CanSolve()) {
- state = Solve;
- break;
- }
- //quick place
- if (state == Play && (selection[0] == 1 || selection[1] == 1) && selectedCard == 1) {
- Card *c = selection[1] == 0 ? waste.peek_back() : tableau[selection[0]].peek_back();
- if (!c->exposed) return;
- for (int i = 0; i < 4; i++) {
- if (c->CanPlaceFoundation(foundation[i].peek_back())) {
- if (selection[1] == 0)
- foundation[i].push_back(waste.pop_back());
- else
- foundation[i].push_back(tableau[selection[0]].pop_back());
- break;
- }
- }
- }
- break;
- default:
- break;
- }
- }
- }
- void GameLogic::PickAndPlace() {
- //pick and place waste
- if (selection[0] == 1 && selection[1] == 0) {
- if (waste.size() > 0 && hand.size() == 0) {
- hand.push_back(waste.pop_back());
- target[0] = 1;
- target[1] = 0;
- } else if (hand.size() == 1 && target[0] == 1 && target[1] == 0) {
- waste.push_back(hand.pop_back());
- target[0] = 0;
- target[1] = 0;
- }
- }
- //place to foundation
- else if (selection[1] == 0 && selection[0] > 2 && hand.size() == 1) {
- uint8_t id = selection[0] - 3;
- if (hand.peek_back()->CanPlaceFoundation(foundation[id].peek_back())) {
- foundation[id].push_back(hand.pop_back());
- target[0] = selection[0];
- target[1] = selection[1];
- }
- }
- //pick and place columns
- else if (selection[1] == 1) {
- auto &tbl = tableau[selection[0]];
- if (hand.size() == 0) {
- if (tbl.peek_back() && !tbl.peek_back()->exposed) {
- tbl.peek_back()->exposed = true;
- } else {
- uint8_t count = selectedCard;
- while (count > 0) {
- hand.push_front(tbl.pop_back());
- count--;
- }
- selectedCard = 1;
- target[0] = selection[0];
- target[1] = selection[1];
- }
- } else if ((target[0] == selection[0] && target[1] == selection[1]) ||
- hand.peek_front()->CanPlaceColumn(tbl.peek_back())) {
- while (hand.size() > 0) {
- tbl.push_back(hand.pop_front());
- }
- target[0] = 0;
- target[1] = 0;
- }
- }
- }
- void GameLogic::HandleNavigation(int key) {
- if (key == InputKeyLeft && selection[0] > 0) {
- selectedCard = 1;
- selection[0]--;
- } else if (key == InputKeyRight && selection[0] < 6) {
- selectedCard = 1;
- selection[0]++;
- } else if (key == InputKeyUp && selection[1] == 1) {
- auto &tbl = tableau[selection[0]];
- uint8_t first = FirstNonFlipped(tbl);
- if (tbl.size() > 0 && first < tbl.size() - selectedCard && hand.size() == 0) {
- selectedCard++;
- } else {
- selectedCard = 1;
- selection[1]--;
- }
- } else if (key == InputKeyDown && selection[1] == 0) {
- selectedCard = 1;
- selection[1]++;
- } else if (selection[1] == 1 && selectedCard > 1) {
- selectedCard--;
- }
- if (selection[1] == 0 && selection[0] == 2) { //skip empty space
- selection[0] += key == InputKeyRight ? 1 : -1;
- }
- }
- void GameLogic::Update(float delta) {
- if(state != Finish){
- buffer->clear();
- }
- switch (state) {
- case Logo:
- Reset();
- buffer->draw(&logo, (Vector) {60, 30}, 0);
- buffer->draw(&main_image, (Vector) {115, 25}, 0);
- buffer->draw(&start, (Vector) {64, 55}, 0);
- break;
- case Intro:
- DoIntro(delta);
- dirty = true;
- break;
- case Play:
- DrawPlayScene();
- if (CanSolve()) {
- buffer->draw_rbox(25, 53, 101, 64, Black);
- buffer->draw_rbox(26, 54, 100, 63, White);
- buffer->draw(&solve, (Vector) {64, 58}, 0);
- end = furi_get_tick();
- }
- break;
- default:
- break;
- }
- buffer->swap();
- readyToRender = true;
- }
- void GameLogic::Reset() {
- stock.deleteData();
- stock.clear();
- waste.deleteData();
- waste.clear();
- tempPos = {0, 0};
- selection[0] = 0;
- selection[1] = 0;
- target[0] = 0;
- selectedCard = 1;
- target[1] = -1;
- for (int i = 0; i < 7; i++) {
- tableau[i].deleteData();
- tableau[i].clear();
- if (i < 4) {
- foundation[i].deleteData();
- foundation[i].clear();
- }
- }
- GenerateDeck();
- }
- bool GameLogic::CanSolve() {
- for (uint8_t i = 0; i < 7; i++) {
- if (tableau[i].peek_front() && !tableau[i].peek_front()->exposed)
- return false;
- }
- return state == Play;
- }
- void GameLogic::GenerateDeck() {
- int cards_count = 52;
- uint8_t cards[cards_count];
- for (int i = 0; i < cards_count; i++) cards[i] = i % 52;
- srand(DWT->CYCCNT);
- //reorder
- for (int i = 0; i < cards_count; i++) {
- int r = i + (rand() % (cards_count - i));
- uint8_t card = cards[i];
- cards[i] = cards[r];
- cards[r] = card;
- }
- //Init deck list
- for (int i = 0; i < cards_count; i++) {
- int letter = cards[i] % 13;
- int suit = cards[i] / 13;
- stock.push_back(new Card(suit, letter));
- }
- }
- GameLogic::~GameLogic() {
- stock.deleteData();
- stock.clear();
- waste.deleteData();
- waste.clear();
- hand.deleteData();
- hand.clear();
- for (int i = 0; i < 7; i++) {
- tableau[i].deleteData();
- tableau[i].clear();
- if (i < 4) {
- foundation[i].deleteData();
- foundation[i].clear();
- }
- }
- }
- Vector pos, targetPos;
- void GameLogic::DoIntro(float delta) {
- //render next after finish
- if (!buffer) return;
- buffer->clear();
- DrawPlayScene();
- if (tempCard) {
- targetPos = {
- 2.0f + (float) target[0] * 18,
- MIN(25.0f + (float) target[1] * 4, 36)
- };
- tempTime += delta * 10;
- pos = Vector::Lerp(tempPos, targetPos, MIN(tempTime, 1));
- tempCard->Render((int) pos.x, (int) pos.y, false, buffer);
- float distance = targetPos.distance(pos);
- if (distance < 0.01) {
- tempCard = nullptr;
- tableau[target[0]].push_back(stock.pop_back());
- tableau[target[0]].peek_back()->exposed = target[0] == target[1];
- if (target[0] == 6 && target[1] == 6) {
- startTime = furi_get_tick();
- state = Play;
- buffer->clear();
- DrawPlayScene();
- }
- }
- } else {
- tempTime = 0;
- tempCard = stock.peek_back();
- tempPos.x = 2;
- tempPos.y = 1;
- if (target[0] == target[1]) {
- target[0]++;
- target[1] = 0;
- } else {
- target[1]++;
- }
- }
- }
- void GameLogic::DrawPlayScene() {
- if (stock.size() > 0) {
- if (stock.size() > 1) {
- stock.peek_back()->Render(2, 1, true, buffer);
- stock.peek_back()->Render(0, 0, selection[0] == 0 && selection[1] == 0, buffer);
- } else {
- stock.peek_back()->Render(2, 1, selection[0] == 0 && selection[1] == 0, buffer);
- }
- } else
- Card::TryRender(nullptr, 2, 1, selection[0] == 1 && selection[1] == 0, buffer);
- Card::TryRender(waste.peek_back(), 20, 1, selection[0] == 1 && selection[1] == 0, buffer);
- int i;
- for (i = 0; i < 4; i++) {
- Card::TryRender(foundation[i].peek_back(), 56 + i * 18, 1, selection[0] == i + 3 && selection[1] == 0, buffer);
- }
- for (i = 0; i < 7; i++) {
- DrawColumn(2 + i * 18, 25, (selection[0] == i && selection[1] == 1) ? selectedCard : 0, i);
- }
- if (hand.size() > 0) {
- if (selection[1] == 0)
- DrawColumn(10 + selection[0] * 18, 15 + selection[1] * 25, hand.size(), -1);
- else {
- int shift = MIN((int) tableau[selection[0]].size(), 4) * 4;
- DrawColumn(10 + selection[0] * 18, 30 + shift, hand.size(), -1);
- }
- }
- }
- void GameLogic::DrawColumn(uint8_t x, uint8_t y, uint8_t selected, int8_t column) {
- UNUSED(selected);
- auto &deck = column >= 0 ? tableau[column] : hand;
- if (deck.size() == 0 && column >= 0) {
- Card::RenderEmptyCard(x, y, buffer);
- buffer->draw_rbox(x + 1, y + 1, x + 16, y + 22, Flip);
- return;
- }
- uint8_t selection = deck.size() - selected;
- // if (selected != 0) selection--;
- uint8_t loop_end = deck.size();
- uint8_t loop_start = MAX(loop_end - 4, 0);
- uint8_t position = 0;
- uint8_t first_non_flipped = FirstNonFlipped(deck);
- bool had_top = false;
- bool showDark = column >= 0;
- // Draw the first flipped and non-flipped card with adjusted visibility
- if (first_non_flipped <= loop_start && selection != first_non_flipped) {
- // Draw a card back if it is not the first card
- if (first_non_flipped > 0) {
- Card::RenderBack(x, y + position, false, buffer, 5);
- // Increment loop start index and position
- position += 4;
- loop_start++;
- had_top = true;
- }
- // Draw the front side of the first non-flipped card
- deck[first_non_flipped]->Render(x, y + position, false, buffer, deck.size() == 1 ? 22 : 9);
- position += 8;
- loop_start++; // Increment loop start index
- }
- // Draw the selected card with adjusted visibility
- if (loop_start > selection) {
- if (!had_top && first_non_flipped > 0) {
- Card::RenderBack(x, y + position, false, buffer, 5);
- position += 4;
- loop_start++;
- }
- // Draw the front side of the selected card
- deck[selection]->Render(x, y + position, showDark, buffer, 9);
- position += 8;
- loop_start++; // Increment loop start index
- }
- int height = 5;
- uint8_t i = 0;
- for (auto *card: deck) {
- if (i >= loop_start && i < loop_end) {
- height = 5;
- if ((i + 1) == loop_end) height = 22;
- else if (i == selection || i == first_non_flipped) height = 9;
- card->Render(x, y + position, i == selection && showDark, buffer, height);
- if (i == selection || i == first_non_flipped)position += 4;
- position += 4;
- }
- i++;
- }
- }
- int8_t GameLogic::FirstNonFlipped(const List<Card> &deck) {
- int8_t index = 0;
- for (auto *card: deck) {
- if (card->exposed) return index;
- index++;
- }
- return -1;
- }
|