Game.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #include "Game.h"
  2. #include "assets.h"
  3. #include <notification/notification.h>
  4. #include <notification/notification_messages.h>
  5. static Sprite logo = Sprite(sprite_logo, BlackOnly);
  6. static Sprite main_image = Sprite(sprite_main_image, BlackOnly);
  7. static Sprite start = Sprite(sprite_start, BlackOnly);
  8. static const NotificationSequence sequence_fail = {
  9. &message_vibro_on,
  10. &message_note_c4,
  11. &message_delay_10,
  12. &message_vibro_off,
  13. &message_sound_off,
  14. &message_delay_10,
  15. &message_vibro_on,
  16. &message_note_a3,
  17. &message_delay_10,
  18. &message_vibro_off,
  19. &message_sound_off,
  20. NULL
  21. };
  22. Game::~Game() {
  23. furi_record_close(RECORD_NOTIFICATION);
  24. delete deck;
  25. for (int i = 0; i < 4; i++)
  26. delete piles[i];
  27. delete buffer;
  28. }
  29. void Game::Render(Canvas *const canvas) {
  30. if (!buffer) return;
  31. if (had_change) {
  32. if (state == GameStateStart) {
  33. buffer->clear();
  34. buffer->draw(&logo, (Vector) {60, 30}, 0);
  35. buffer->draw(&main_image, (Vector) {110, 25}, 0);
  36. buffer->draw(&start, (Vector) {64, 55}, 0);
  37. } else if (state == GameStatePlay) {
  38. buffer->clear();
  39. if (deck)
  40. deck->Render(buffer, current_row == 0 && current_column == 0, current_row == 0 && current_column == 1);
  41. for (int i = 0; i < 7; i++) {
  42. bool selected = current_row == 1 && current_column == i;
  43. tableau[i].Render(i * 18 + 1, 25, selected, selected ? column_selection : 0, buffer);
  44. }
  45. for (int i = 0; i < 4; i++) {
  46. bool pileSelected = current_row == 0 && (current_column - 3) == i;
  47. // check_pointer(piles[i]);
  48. if (piles[i]) {
  49. piles[i]->Render(i * 18 + 55, 1, pileSelected, buffer, 22);
  50. } else {
  51. Card::RenderEmptyCard(i * 18 + 55, 1, buffer);
  52. if (pileSelected) {
  53. buffer->draw_rbox(i * 18 + 56, 2, i * 18 + 71, 23, Flip);
  54. }
  55. }
  56. }
  57. if (hand.Count() > 0) {
  58. hand.Render(current_column * 18 + 10, current_row * 25 + 15, false, 0, buffer);
  59. }
  60. }
  61. }
  62. had_change = false;
  63. buffer->render(canvas);
  64. if (state == GameStatePlay && can_auto_solve) {
  65. canvas_set_font(canvas,FontSecondary);
  66. canvas_set_color(canvas, ColorBlack);
  67. canvas_draw_frame(canvas, 8, 52, 112, 12);
  68. canvas_set_color(canvas, ColorWhite);
  69. canvas_draw_box(canvas, 9, 53, 110, 10);
  70. canvas_set_color(canvas, ColorBlack);
  71. canvas_draw_str_aligned(canvas, 64, 58, AlignCenter, AlignCenter, "Long press > to auto solve!");
  72. }
  73. }
  74. Game::Game() {
  75. deck = new Deck(1);
  76. buffer = new RenderBuffer(128, 64);
  77. notifications = static_cast<NotificationApp *>(furi_record_open(RECORD_NOTIFICATION));
  78. }
  79. void Game::NewRound() {
  80. round_start = furi_get_tick();
  81. state = GameStatePlay;
  82. had_change = true;
  83. }
  84. void Game::Reset() {
  85. can_auto_solve = false;
  86. current_column = 0;
  87. current_row = 0;
  88. column_selection = -1;
  89. hand.Clear();
  90. picked_from_column = -1;
  91. picked_from_row = -1;
  92. deck->Generate();
  93. //Reset foundations
  94. for (int i = 0; i < 4; i++) {
  95. if (piles[i]) delete piles[i];
  96. }
  97. //Populate columns
  98. for (int i = 0; i < 7; i++) {
  99. tableau[i].Reset();
  100. for (int j = 0; j <= i; j++) {
  101. Card *card = deck->Extract();
  102. if (j >= i) card->exposed = true;
  103. tableau[i].AddCard(card);
  104. }
  105. }
  106. }
  107. void Game::LongPress(InputKey key) {
  108. if (key == InputKeyOk) {
  109. if (hand.Count() == 1) {
  110. for (uint8_t i = 0; i < 4; i++) {
  111. Card *current = piles[i];
  112. if (Card::CanPlace(current, hand.TopCard())) {
  113. check_pointer(piles[i]);
  114. piles[i] = hand.Pop();
  115. had_change = true;
  116. return;
  117. }
  118. }
  119. }
  120. } else if (key == InputKeyUp) {
  121. current_row = 0;
  122. had_change = true;
  123. if (current_column == 2) current_column--;
  124. }
  125. }
  126. void Game::Update() {
  127. }
  128. void Game::Press(InputKey key) {
  129. //region Navigation
  130. if (key == InputKeyOk && state == GameStateStart) {
  131. Reset();
  132. NewRound();
  133. had_change = true;
  134. return;
  135. }
  136. if (key == InputKeyBack && state == GameStatePlay) {
  137. state = GameStateStart;
  138. had_change = true;
  139. return;
  140. }
  141. if (key == InputKeyLeft && current_column > 0) {
  142. current_column--;
  143. had_change = true;
  144. column_selection = 0;
  145. } else if (key == InputKeyRight && current_column < 6) {
  146. current_column++;
  147. column_selection = 0;
  148. if (current_row == 0 && current_column == 2) current_column++;
  149. had_change = true;
  150. }
  151. if (key == InputKeyUp && current_row == 1) {
  152. uint8_t count = tableau[current_column].Count();
  153. uint8_t first_non_flipped = tableau[current_column].FirstNonFlipped();
  154. if (hand.Count() == 0) {
  155. if ((count - column_selection - 1) > first_non_flipped && (column_selection) < count) {
  156. column_selection++;
  157. } else
  158. current_row--;
  159. } else {
  160. current_row--;
  161. }
  162. had_change = true;
  163. } else if (key == InputKeyDown && current_row == 0) {
  164. current_row++;
  165. column_selection = 0;
  166. had_change = true;
  167. } else if (key == InputKeyDown && current_row == 1 && column_selection > 0) {
  168. column_selection--;
  169. had_change = true;
  170. }
  171. //disable empty space selection
  172. if (current_row == 0 && current_column == 2) {
  173. current_column--;
  174. column_selection = 0;
  175. had_change = true;
  176. }
  177. //endregion
  178. //Pick/cycle logic
  179. if (hand.Count() == 0) {
  180. if (key == InputKeyOk) {
  181. if (current_row == 0) {
  182. //cycle deck
  183. if (current_column == 0) {
  184. deck->Cycle();
  185. had_change = true;
  186. } else if (current_column == 1) {
  187. //pick from deck
  188. Card *c = deck->GetLastWaste();
  189. if (c) {
  190. hand.AddCard(c);
  191. picked_from_column = current_column;
  192. picked_from_row = current_row;
  193. had_change = true;
  194. }
  195. }
  196. } else {
  197. //Tableau logic
  198. auto *column = &tableau[current_column];
  199. //Flip last card if it is not exposed
  200. if (column->Count() > 0) {
  201. if (!column->LastCard()->exposed) {
  202. column->Reveal();
  203. had_change = true;
  204. } else {
  205. // hand.AddCard(column->Pop());
  206. //Pick selection
  207. auto *data = column->splice(column_selection);
  208. hand.AddRange(data);
  209. data->soft_clear();
  210. delete data;
  211. column_selection = 0;
  212. had_change = true;
  213. picked_from_row = current_row;
  214. picked_from_column = current_column;
  215. }
  216. } else {
  217. ErrorMessage();
  218. }
  219. }
  220. }
  221. } else {
  222. //Place logic
  223. if (key == InputKeyOk) {
  224. //Place back to where it was picked up
  225. if (current_column == picked_from_column && current_row == picked_from_row) {
  226. //add back to tableau
  227. if (picked_from_row == 0) {
  228. FURI_LOG_D("PLACE", "reinsert top");
  229. had_change = true;
  230. deck->AddToWaste(hand.Pop());
  231. } else {
  232. FURI_LOG_D("PLACE", "reinsert tableau %i %i", hand.Count(), current_column);
  233. auto *column = &tableau[current_column];
  234. if (column) {
  235. FURI_LOG_D("PLACE", "BEFORE MERGE %i", hand.Count());
  236. column->Merge(&hand);
  237. had_change = true;
  238. FURI_LOG_D("PLACE", "AFTER MERGE");
  239. } else {
  240. FURI_LOG_E("PLACE", "TABLEAU ERROR");
  241. }
  242. }
  243. //Place to the top piles
  244. } else if (hand.Count() == 1 && current_row == 0 && current_column > 1) {
  245. FURI_LOG_D("PLACE", "place top");
  246. check_pointer(piles[current_column - 3]);
  247. Card *current = piles[current_column - 3];
  248. if (Card::CanPlace(current, hand.TopCard())) {
  249. piles[current_column - 3] = hand.Pop();
  250. had_change = true;
  251. } else {
  252. ErrorMessage();
  253. }
  254. //Place to the tableau columns
  255. } else if (current_row == 1) {
  256. FURI_LOG_D("PLACE", "place bottom");
  257. auto *column = &tableau[current_column];
  258. if (column) {
  259. if ((current_row == picked_from_row && current_column == picked_from_column) ||
  260. column->CanPlace(&hand)) {
  261. FURI_LOG_D("PLACE", "canplace");
  262. column->Merge(&hand);
  263. had_change = true;
  264. }
  265. } else {
  266. FURI_LOG_E("PLACE", "TABLEAU ERROR in bottom place");
  267. }
  268. } else {
  269. ErrorMessage();
  270. }
  271. }
  272. }
  273. CheckCanAutoSolve();
  274. }
  275. void Game::ErrorMessage() {
  276. notification_message(notifications, &sequence_fail);
  277. }
  278. void Game::CheckCanAutoSolve() {
  279. uint8_t ok = 0;
  280. for (uint8_t i = 0; i < 7; i++) {
  281. if (tableau[i].Count() == 0 || tableau[i].TopCard()->exposed)
  282. ok++;
  283. }
  284. can_auto_solve = ok == 7;
  285. if (can_auto_solve)
  286. FURI_LOG_D("Solve", "can auto solve");
  287. }