scene_game.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. #include "scene_game.h"
  2. #include "app.h"
  3. #include "game.h"
  4. #include "app_gameplay.h"
  5. #include "wave/scene_management.h"
  6. #include "wave/calc.h"
  7. #include "wave/data_structures/list.h"
  8. #include "wave/exception_manager.h"
  9. #include "racso_zero_icons.h"
  10. #include <gui/gui.h>
  11. #include <furi.h>
  12. #include <string.h>
  13. #include <stdlib.h>
  14. #include <stdio.h>
  15. #define VISUAL_CARD_WIDTH 23
  16. #define VISUAL_CARD_HEIGHT 13
  17. #define VISUAL_ICON_WIDTH 9
  18. #define VISUAL_ICON_HEIGHT 9
  19. #define VISUAL_ICON_BIG_WIDTH 17
  20. #define VISUAL_CARD_SPACING 0
  21. #define VISUAL_CARDS_ROWS 1
  22. #define VISUAL_CARDS_COLUMNS 5
  23. #define VISUAL_PLAYER_CARDS_X 7
  24. #define VISUAL_PLAYER_CARDS_Y 63
  25. typedef enum {
  26. CardDrawMode_Solid,
  27. CardDrawMode_Filled,
  28. CardDrawMode_Dotted,
  29. CardDrawMode_Dashed,
  30. CardDrawMode_NoBorder,
  31. } CardDrawMode;
  32. typedef enum { CardDrawAlign_TopLeft, CardDrawAlign_Center } CardDrawAlign;
  33. // Suit selector component.
  34. typedef struct SuitSelector {
  35. int selectedSuitIndex;
  36. bool selectionConfirmed;
  37. bool isEnabled;
  38. } SuitSelector;
  39. static SuitSelector suitSelector;
  40. void suit_selector_set_enabled(SuitSelector* suitSelector, bool isEnabled) {
  41. suitSelector->isEnabled = isEnabled;
  42. suitSelector->selectedSuitIndex = 0;
  43. suitSelector->selectionConfirmed = false;
  44. }
  45. bool suit_selector_is_enabled(SuitSelector* suitSelector) {
  46. return suitSelector->isEnabled;
  47. }
  48. void suit_selector_set_index(SuitSelector* suitSelector, int index) {
  49. suitSelector->selectedSuitIndex = index;
  50. }
  51. int suit_selector_get_index(SuitSelector* suitSelector) {
  52. return suitSelector->selectedSuitIndex;
  53. }
  54. void suit_selector_confirm_selection(SuitSelector* suitSelector) {
  55. if(suit_selector_get_index(suitSelector) > 0) suitSelector->selectionConfirmed = true;
  56. }
  57. bool suit_selector_is_selection_confirmed(SuitSelector* suitSelector) {
  58. return suitSelector->selectionConfirmed;
  59. }
  60. void draw_suit_selector(Canvas* canvas, SuitSelector* suitSelector) {
  61. if(!suit_selector_is_enabled(suitSelector)) return;
  62. int w = 50, h = VISUAL_ICON_HEIGHT + 8, spacing = 2;
  63. int x = 64 - w / 2, y = 32 - h / 2;
  64. // Frame:
  65. canvas_set_color(canvas, ColorWhite);
  66. canvas_draw_box(canvas, x, y, w, h); // White margin
  67. canvas_set_color(canvas, ColorBlack);
  68. canvas_draw_frame(canvas, x + 1, y + 1, w - 2, h - 2); // Black border
  69. // 1 px white padding goes here.
  70. // Icons:
  71. // Highlighting an icon uses 1 px around the icon. Therefore, the icons are drawn with an additional 1 px margin.
  72. x += 3 + 1, y += 3 + 1; // Skips margin, border and padding + additional 1 px margin
  73. const Icon* icons[4] = {&I_s1, &I_s2, &I_s3, &I_s4};
  74. canvas_set_color(canvas, ColorBlack);
  75. for(int i = 0; i < 4; i++)
  76. canvas_draw_icon(canvas, x + i * (VISUAL_ICON_WIDTH + spacing), y, icons[i]);
  77. // Highlight selection:
  78. if(suit_selector_get_index(suitSelector) > 0) {
  79. canvas_set_color(canvas, ColorXOR);
  80. canvas_draw_box(
  81. canvas,
  82. x - 1 + (suit_selector_get_index(suitSelector) - 1) * (VISUAL_ICON_WIDTH + spacing),
  83. y - 1,
  84. VISUAL_ICON_WIDTH + 2,
  85. VISUAL_ICON_HEIGHT + 2);
  86. }
  87. }
  88. void suit_selector_handle_input(SuitSelector* suitSelector, InputKey key, InputType type) {
  89. if(!suit_selector_is_enabled(suitSelector) ||
  90. suit_selector_is_selection_confirmed(suitSelector))
  91. return;
  92. if(type != InputTypePress) return;
  93. if(key == InputKeyLeft || key == InputKeyUp)
  94. suitSelector->selectedSuitIndex = wrap_single(suitSelector->selectedSuitIndex - 1, 1, 4);
  95. if(key == InputKeyRight || key == InputKeyDown)
  96. suitSelector->selectedSuitIndex = wrap_single(suitSelector->selectedSuitIndex + 1, 1, 4);
  97. if(key == InputKeyOk) suit_selector_confirm_selection(suitSelector);
  98. }
  99. // End of suit selector.
  100. void screen_printer_print(char* string, Canvas* canvas, int x, int y) {
  101. char* start = string;
  102. char* end;
  103. char lineBuffer[200];
  104. while((end = strchr(start, '\n')) != NULL) {
  105. strncpy(lineBuffer, start, end - start);
  106. lineBuffer[end - start] = '\0';
  107. canvas_draw_str_aligned(canvas, x, y, AlignLeft, AlignTop, lineBuffer);
  108. y += 10;
  109. start = end + 1;
  110. }
  111. }
  112. const Icon* get_player_icon(int playerIndex) {
  113. throw_exception_if(playerIndex < 1 || playerIndex > NUMBER_OF_PLAYERS, "Invalid player index");
  114. static const Icon* playerIcons[NUMBER_OF_PLAYERS] = {
  115. &I_player1, &I_player2, &I_player3, &I_player4};
  116. return playerIcons[playerIndex - 1];
  117. }
  118. char* get_card_suit_name(CardSuit suit) {
  119. switch(suit) {
  120. case CardSuit_Spades:
  121. return "Spades";
  122. case CardSuit_Hearts:
  123. return "Hearts";
  124. case CardSuit_Diamonds:
  125. return "Diamonds";
  126. case CardSuit_Clubs:
  127. return "Clubs";
  128. default:
  129. return "???";
  130. }
  131. }
  132. void draw_rectangle_dashed(Canvas* canvas, int x, int y, int w, int h, Color color) {
  133. canvas_set_color(canvas, color);
  134. // dot, dot, space, dot, dot, space, ...
  135. for(int i = 0; i < w; i++) {
  136. if(i % 6 < 5) canvas_draw_dot(canvas, x + i, y);
  137. if(i % 6 < 5) canvas_draw_dot(canvas, x + i, y + h - 1);
  138. }
  139. for(int i = 0; i < h; i++) {
  140. if(i % 6 < 5) canvas_draw_dot(canvas, x, y + i);
  141. if(i % 6 < 5) canvas_draw_dot(canvas, x + w - 1, y + i);
  142. }
  143. }
  144. void draw_rectangle_dotted(Canvas* canvas, int x, int y, int w, int h, Color color) {
  145. canvas_set_color(canvas, color);
  146. for(int i = 0; i < w; i++) {
  147. if(i % 2 == 0) canvas_draw_dot(canvas, x + i, y);
  148. if(i % 2 == 0) canvas_draw_dot(canvas, x + i, y + h - 1);
  149. }
  150. for(int i = 0; i < h; i++) {
  151. if(i % 2 == 0) canvas_draw_dot(canvas, x, y + i);
  152. if(i % 2 == 0) canvas_draw_dot(canvas, x + w - 1, y + i);
  153. }
  154. }
  155. void draw_card_content(Canvas* canvas, int x, int y, Card card) {
  156. if(card.action == ActionType_ChangeSuit || card.action == ActionType_ChangeSuitPlus4) {
  157. int iconX = x - VISUAL_ICON_BIG_WIDTH / 2;
  158. int iconY = y - VISUAL_ICON_HEIGHT / 2;
  159. const Icon* icon = card.action == ActionType_ChangeSuit ? &I_joker : &I_joker4;
  160. canvas_draw_icon(canvas, iconX, iconY, icon);
  161. return;
  162. }
  163. // Draw left part: number or action
  164. if(card.number >= 0) {
  165. char numberStr[11];
  166. snprintf(numberStr, 11, "%d", card.number);
  167. canvas_set_font(canvas, FontKeyboard);
  168. canvas_draw_str_aligned(canvas, x - 4, y + 1, AlignCenter, AlignCenter, numberStr);
  169. } else {
  170. const Icon* icon = NULL;
  171. switch(card.action) {
  172. case ActionType_Skip:
  173. icon = &I_s;
  174. break;
  175. case ActionType_Invert:
  176. icon = &I_r;
  177. break;
  178. case ActionType_Plus2:
  179. icon = &I_p2;
  180. break;
  181. default:
  182. throw_exception("Invalid action type");
  183. }
  184. canvas_draw_icon(canvas, x - 9, y - 4, icon);
  185. }
  186. // Draw right part: suit
  187. const Icon* icon = NULL;
  188. switch(card.suit) {
  189. case CardSuit_Spades:
  190. icon = &I_s1;
  191. break;
  192. case CardSuit_Hearts:
  193. icon = &I_s2;
  194. break;
  195. case CardSuit_Diamonds:
  196. icon = &I_s3;
  197. break;
  198. case CardSuit_Clubs:
  199. icon = &I_s4;
  200. break;
  201. default:
  202. throw_exception("Invalid card suit");
  203. }
  204. canvas_draw_icon(canvas, x + 1, y - 4, icon);
  205. }
  206. void draw_card(Canvas* canvas, Card card, int x, int y, CardDrawAlign align, CardDrawMode mode) {
  207. const int cardWidth = 23, cardHeight = 13;
  208. if(align == CardDrawAlign_Center) x -= cardWidth / 2, y -= cardHeight / 2;
  209. int cardCenterX = x + cardWidth / 2;
  210. int cardCenterY = y + cardHeight / 2;
  211. canvas_set_color(canvas, ColorBlack);
  212. draw_card_content(canvas, cardCenterX, cardCenterY, card);
  213. if(mode == CardDrawMode_Solid) canvas_draw_frame(canvas, x, y, cardWidth, cardHeight);
  214. if(mode == CardDrawMode_Filled) {
  215. canvas_set_color(canvas, ColorXOR);
  216. canvas_draw_box(canvas, x, y, cardWidth, cardHeight);
  217. }
  218. if(mode == CardDrawMode_Dotted)
  219. draw_rectangle_dotted(canvas, x, y, cardWidth, cardHeight, ColorBlack);
  220. if(mode == CardDrawMode_Dashed)
  221. draw_rectangle_dashed(canvas, x, y, cardWidth, cardHeight, ColorBlack);
  222. }
  223. void draw_my_cards_numbering(
  224. Canvas* canvas,
  225. int x,
  226. int y,
  227. int cardsInHandCount,
  228. int selectedHandIndex) {
  229. char numberingStr[16];
  230. snprintf(numberingStr, 16, "%d/%d", selectedHandIndex + 1, cardsInHandCount);
  231. canvas_set_color(canvas, ColorBlack);
  232. canvas_set_font(canvas, FontKeyboard);
  233. canvas_draw_str_aligned(canvas, x, y, AlignLeft, AlignBottom, numberingStr);
  234. }
  235. void draw_my_cards(
  236. Canvas* canvas,
  237. GameState* game,
  238. int x,
  239. int y,
  240. int playerIndex,
  241. int selectedHandIndex,
  242. bool showSelection) {
  243. List* cardsInHand = list_alloc(MAX_HAND_SIZE, sizeof(int));
  244. game_get_player_hand(game, playerIndex, cardsInHand);
  245. int cardsInHandCount = list_count(cardsInHand);
  246. int cardsPerPage = VISUAL_CARDS_ROWS * VISUAL_CARDS_COLUMNS;
  247. int currentPage = selectedHandIndex / cardsPerPage;
  248. int currentPageStartIndex = currentPage * cardsPerPage;
  249. int pageSlotIndex = 0;
  250. for(int handIndex = currentPageStartIndex; handIndex < currentPageStartIndex + cardsPerPage;
  251. handIndex++) {
  252. if(handIndex >= list_count(cardsInHand)) break;
  253. int cardX = x + (pageSlotIndex / VISUAL_CARDS_COLUMNS) *
  254. (VISUAL_CARD_WIDTH - 1 + VISUAL_CARD_SPACING);
  255. int cardY = y - VISUAL_CARD_HEIGHT -
  256. (pageSlotIndex % VISUAL_CARDS_COLUMNS) *
  257. (VISUAL_CARD_HEIGHT - 1 + VISUAL_CARD_SPACING);
  258. int cardInSlot;
  259. list_get_at(cardsInHand, handIndex, &cardInSlot);
  260. CardDrawMode drawMode = CardDrawMode_NoBorder;
  261. if(showSelection) {
  262. bool isValidCard = game_is_card_valid_to_play(game, cardInSlot);
  263. bool isSelected = handIndex == selectedHandIndex;
  264. if(isValidCard && isSelected)
  265. drawMode = CardDrawMode_Filled;
  266. else if(isSelected)
  267. drawMode = CardDrawMode_Solid;
  268. else if(isValidCard)
  269. drawMode = CardDrawMode_Dotted;
  270. else
  271. drawMode = CardDrawMode_NoBorder;
  272. }
  273. draw_card(canvas, AllCardsData[cardInSlot], cardX, cardY, CardDrawAlign_TopLeft, drawMode);
  274. const int ARROW_ICON_HEIGHT = 6, ARROW_ICON_WIDTH = 5;
  275. int numberOfPages = (cardsInHandCount - 1) / cardsPerPage + 1;
  276. if(numberOfPages > 1 &&
  277. (pageSlotIndex == cardsPerPage - 1 || handIndex == cardsInHandCount - 1))
  278. canvas_draw_icon(canvas, cardX - ARROW_ICON_WIDTH - 2, cardY, &I_arrow_up);
  279. if(numberOfPages > 1 && pageSlotIndex == 0)
  280. canvas_draw_icon_ex(
  281. canvas,
  282. cardX + VISUAL_CARD_WIDTH + 2,
  283. cardY + VISUAL_CARD_HEIGHT - ARROW_ICON_HEIGHT,
  284. &I_arrow_up,
  285. IconRotation180);
  286. pageSlotIndex++;
  287. }
  288. if(showSelection && cardsInHandCount > 0)
  289. draw_my_cards_numbering(
  290. canvas, x + VISUAL_CARD_WIDTH + 12, y - 2, cardsInHandCount, selectedHandIndex);
  291. list_free(cardsInHand);
  292. }
  293. void draw_player_card_count(
  294. Canvas* canvas,
  295. GameState* game,
  296. int playerIndex,
  297. int x,
  298. int y,
  299. bool isCurrentTurn) {
  300. int cardCount = 0;
  301. for(int i = 0; i < NUMBER_OF_CARDS; i++)
  302. if(game_get_card_location(game, i) == playerIndex) cardCount++;
  303. char cardCountStr[16];
  304. snprintf(cardCountStr, 16, "%d", cardCount);
  305. canvas_set_color(canvas, ColorBlack);
  306. canvas_set_font(canvas, FontPrimary);
  307. int boxX = x, boxY = y;
  308. const Icon* playerIcon = get_player_icon(playerIndex);
  309. canvas_draw_icon(
  310. canvas,
  311. x + VISUAL_CARD_WIDTH + 1,
  312. y + (VISUAL_CARD_HEIGHT - VISUAL_ICON_HEIGHT) / 2,
  313. playerIcon);
  314. canvas_set_font(canvas, FontPrimary);
  315. if(isCurrentTurn)
  316. canvas_draw_box(canvas, boxX, y, VISUAL_CARD_WIDTH, VISUAL_CARD_HEIGHT);
  317. else
  318. draw_rectangle_dotted(
  319. canvas, boxX, boxY, VISUAL_CARD_WIDTH, VISUAL_CARD_HEIGHT, ColorBlack);
  320. canvas_set_color(canvas, ColorXOR);
  321. canvas_draw_str_aligned(
  322. canvas,
  323. boxX + VISUAL_CARD_WIDTH / 2,
  324. boxY + VISUAL_CARD_HEIGHT / 2,
  325. AlignCenter,
  326. AlignCenter,
  327. cardCountStr);
  328. }
  329. void draw_forced_suit(Canvas* canvas, CardSuit suit, int x, int y, CardDrawMode drawMode) {
  330. if(suit == CardSuit_None) return;
  331. canvas_set_color(canvas, ColorBlack);
  332. const Icon* icon = NULL;
  333. switch(suit) {
  334. case CardSuit_Spades:
  335. icon = &I_s1;
  336. break;
  337. case CardSuit_Hearts:
  338. icon = &I_s2;
  339. break;
  340. case CardSuit_Diamonds:
  341. icon = &I_s3;
  342. break;
  343. case CardSuit_Clubs:
  344. icon = &I_s4;
  345. break;
  346. default:
  347. throw_exception("Invalid card suit");
  348. }
  349. canvas_draw_icon(canvas, x, y, icon);
  350. if(drawMode == CardDrawMode_Filled) {
  351. canvas_set_color(canvas, ColorXOR);
  352. canvas_draw_box(canvas, x - 1, y - 1, 11, 11);
  353. }
  354. }
  355. void draw_winner_screen(Canvas* canvas, int winner) {
  356. if(winner == 0) return;
  357. int w = 91, h = 31;
  358. int x = 64 - w / 2, y = 32 - h / 2;
  359. // draw a black border with a 1px white margin
  360. canvas_set_color(canvas, ColorWhite);
  361. canvas_draw_box(canvas, x, y, w, h);
  362. canvas_set_color(canvas, ColorBlack);
  363. canvas_draw_frame(canvas, x + 1, y + 1, w - 2, h - 2);
  364. canvas_set_color(canvas, ColorBlack);
  365. canvas_set_font(canvas, FontPrimary);
  366. char* winnerStr = winner == PLAYER_NUMBER ? "You win!" : "Opponent wins!";
  367. canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, winnerStr);
  368. canvas_draw_icon(canvas, 64 - VISUAL_ICON_WIDTH / 2, 34, get_player_icon(winner));
  369. }
  370. void draw_game(Canvas* canvas, AppContext* context) {
  371. AppGameplayState* gameplay = context->gameplay;
  372. GameState* game = gameplay_get_game(gameplay);
  373. int topCard = game_get_top_card(game);
  374. int currentTurn = game_get_player_turn(game);
  375. draw_player_card_count(canvas, game, 2, 128 - VISUAL_CARD_WIDTH - 10, 1, currentTurn == 2);
  376. draw_player_card_count(
  377. canvas, game, 3, 128 - VISUAL_CARD_WIDTH - 10, 1 + 12, currentTurn == 3);
  378. draw_player_card_count(
  379. canvas, game, 4, 128 - VISUAL_CARD_WIDTH - 10, 1 + 24, currentTurn == 4);
  380. draw_player_card_count(
  381. canvas, game, 1, 128 - VISUAL_CARD_WIDTH - 10, 63 - VISUAL_CARD_HEIGHT, currentTurn == 1);
  382. const Icon* directionIcon = game_get_direction(game) == 1 ? &I_arrow_cw : &I_arrow_ccw;
  383. canvas_draw_icon(canvas, 84, 1, directionIcon);
  384. // Draw top card or player card
  385. int mainCardX = 63, mainCardY = 31;
  386. int forcedSuitX = mainCardX - VISUAL_ICON_WIDTH / 2,
  387. forcedSuitY = mainCardY + VISUAL_CARD_HEIGHT / 2 + 3;
  388. if(game_get_card_to_play(game) == CARD_NONE) {
  389. draw_card(
  390. canvas,
  391. AllCardsData[topCard],
  392. mainCardX,
  393. mainCardY,
  394. CardDrawAlign_Center,
  395. CardDrawMode_Solid);
  396. draw_forced_suit(
  397. canvas, game_get_forced_suit(game), forcedSuitX, forcedSuitY, CardDrawMode_NoBorder);
  398. } else {
  399. draw_card(
  400. canvas,
  401. AllCardsData[game_get_card_to_play(game)],
  402. mainCardX,
  403. mainCardY,
  404. CardDrawAlign_Center,
  405. CardDrawMode_Filled);
  406. draw_forced_suit(
  407. canvas,
  408. game_get_forced_suit_to_play(game),
  409. forcedSuitX,
  410. forcedSuitY,
  411. CardDrawMode_Filled);
  412. }
  413. int selectedHandIndex = gameplay_selection_get_hand_index(gameplay);
  414. draw_my_cards(
  415. canvas,
  416. game,
  417. VISUAL_PLAYER_CARDS_X,
  418. VISUAL_PLAYER_CARDS_Y,
  419. 1,
  420. selectedHandIndex,
  421. currentTurn == PLAYER_NUMBER);
  422. draw_suit_selector(canvas, &suitSelector);
  423. draw_winner_screen(canvas, game_get_winner(game));
  424. }
  425. void game_transition_callback(int from, int to, void* context) {
  426. AppContext* app = (AppContext*)context;
  427. UNUSED(from);
  428. if(to == SceneType_Game) {
  429. gameplay_reset(app->gameplay);
  430. suit_selector_set_enabled(&suitSelector, false);
  431. }
  432. }
  433. void game_render_callback(Canvas* const canvas, void* context) {
  434. //AppContext* app = (AppContext*)context;
  435. //AppGameplayState* gameplay = app->gameplay;
  436. canvas_clear(canvas);
  437. draw_game(canvas, context);
  438. }
  439. void ai_play_card(GameState* game, int cardIndex) {
  440. CardSuit forcedSuit = CardSuit_None;
  441. Card card = AllCardsData[cardIndex];
  442. if(card.action == ActionType_ChangeSuit || card.action == ActionType_ChangeSuitPlus4)
  443. forcedSuit = rand() % 4 + 1;
  444. game_set_card_to_play(game, cardIndex, forcedSuit);
  445. }
  446. void ai_play_turn(GameState* game) {
  447. int playerIndex = game_get_player_turn(game);
  448. int randomCard = rand() % NUMBER_OF_CARDS;
  449. int cardIndex = randomCard;
  450. while(true) {
  451. if(game_get_card_location(game, cardIndex) == playerIndex &&
  452. game_is_card_valid_to_play(game, cardIndex)) {
  453. FURI_LOG_D("AI", "AI player %d selected card %d", playerIndex, cardIndex);
  454. ai_play_card(game, cardIndex);
  455. return;
  456. }
  457. cardIndex = (cardIndex + 1) % NUMBER_OF_CARDS;
  458. if(cardIndex == randomCard) {
  459. FURI_LOG_D("AI", "AI player %d has no cards to play", playerIndex);
  460. int drawnCard = game_step_draw_card(game, playerIndex);
  461. FURI_LOG_D("AI", "AI player %d drew card %d", playerIndex, drawnCard);
  462. if(game_is_card_valid_to_play(game, cardIndex)) {
  463. ai_play_card(game, cardIndex);
  464. return;
  465. } else {
  466. FURI_LOG_D("AI", "AI player %d skipped turn", playerIndex);
  467. game_step_next_player(game);
  468. return;
  469. }
  470. }
  471. }
  472. }
  473. void game_handle_input(InputKey key, InputType type, void* context) {
  474. AppContext* app = (AppContext*)context;
  475. AppGameplayState* gameplay = app->gameplay;
  476. GameState* game = gameplay_get_game(gameplay);
  477. if(type != InputTypePress) return;
  478. if(key == InputKeyBack) {
  479. scene_manager_set_scene(app->sceneManager, SceneType_Menu);
  480. return;
  481. }
  482. // State: displaying winner
  483. if(game_get_winner(game) != 0) return;
  484. // State: displaying the card that is being played
  485. if(game_get_card_to_play(game) != CARD_NONE) {
  486. if(key == InputKeyOk) game_apply_card_to_play(game);
  487. return;
  488. }
  489. // State: player is selecting a suit for a joker
  490. if(suit_selector_is_enabled(&suitSelector)) {
  491. suit_selector_handle_input(&suitSelector, key, type);
  492. if(suit_selector_is_selection_confirmed(&suitSelector)) {
  493. int selectedCard = gameplay_get_selected_card(gameplay);
  494. game_set_card_to_play(game, selectedCard, suit_selector_get_index(&suitSelector));
  495. suit_selector_set_enabled(&suitSelector, false);
  496. }
  497. return;
  498. }
  499. // States: players select card.
  500. // If the player has no valid cards to play, a special flow is done.
  501. if(game_get_player_hand_valid_cards_count(game, game_get_player_turn(game)) == 0) {
  502. FURI_LOG_D(
  503. "GAME",
  504. "Player %d has no valid cards to play. Drawing card.",
  505. game_get_player_turn(game));
  506. int card = game_step_draw_card(game, game_get_player_turn(game));
  507. if(!game_is_card_valid_to_play(game, card)) {
  508. FURI_LOG_D("GAME", "Card cannot be played. Skipping turn.");
  509. game_step_next_player(game);
  510. }
  511. return;
  512. }
  513. // State: player is selecting a card to play
  514. if(game_get_player_turn(game) == PLAYER_NUMBER) {
  515. if(key == InputKeyOk) {
  516. int selectedCard = gameplay_get_selected_card(gameplay);
  517. if(game_is_card_valid_to_play(game, selectedCard)) {
  518. bool isJoker = AllCardsData[selectedCard].action == ActionType_ChangeSuit ||
  519. AllCardsData[selectedCard].action == ActionType_ChangeSuitPlus4;
  520. if(isJoker)
  521. suit_selector_set_enabled(&suitSelector, true);
  522. else
  523. game_set_card_to_play(game, selectedCard, CardSuit_None);
  524. }
  525. return;
  526. }
  527. if(key == InputKeyLeft || key == InputKeyUp) {
  528. gameplay_selection_delta(gameplay, 1);
  529. return;
  530. }
  531. if(key == InputKeyRight || key == InputKeyDown) {
  532. gameplay_selection_delta(gameplay, -1);
  533. return;
  534. }
  535. return;
  536. }
  537. // State: CPU is playing
  538. if(game_get_player_turn(game) != PLAYER_NUMBER) {
  539. if(key == InputKeyOk) {
  540. ai_play_turn(game);
  541. return;
  542. }
  543. }
  544. }
  545. void game_tick_callback(void* context) {
  546. AppContext* app = (AppContext*)context;
  547. UNUSED(app);
  548. }