GameLogic.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. #include "GameLogic.h"
  2. #include "utils/Sprite.h"
  3. #include "assets.h"
  4. static Sprite logo = Sprite(sprite_logo, BlackOnly);
  5. static Sprite solve = Sprite(sprite_solve, BlackOnly);
  6. static Sprite main_image = Sprite(sprite_main_image, BlackOnly);
  7. static Sprite start = Sprite(sprite_start, BlackOnly);
  8. GameLogic::GameLogic(RenderBuffer *b, InputEventHandler *inputHandler) : buffer(b) {
  9. inputHandler->subscribe(this, [](void *ctx, int key, InputType type) {
  10. auto *inst = (GameLogic *) ctx;
  11. inst->Input(key, type);
  12. });
  13. }
  14. void GameLogic::Input(int key, InputType type) {
  15. if (type == InputTypeShort) {
  16. if (key == InputKeyBack && state == Play) {
  17. state = Logo;
  18. target[0] = 0;
  19. target[1] = 0;
  20. return;
  21. } else if (key == InputKeyOk) {
  22. if (state == Logo) {
  23. state = Intro;
  24. return;
  25. }
  26. if (state == Solve) {
  27. end = furi_get_tick();
  28. state = Finish;
  29. return;
  30. }
  31. if (state == Finish) {
  32. state = Logo;
  33. return;
  34. } else if (state == Intro) {
  35. for (int i = 0; i < 7; i++) {
  36. while ((int) tableau[i].size() < (i + 1)) {
  37. tableau[i].push_back(stock.pop_back());
  38. }
  39. tableau[i].peek_back()->exposed = true;
  40. }
  41. tempCard = nullptr;
  42. state = Play;
  43. startTime = furi_get_tick();
  44. buffer->clear();
  45. DrawPlayScene();
  46. } else if (state == Play) {
  47. if (selection[0] == 0 && selection[1] == 0) {
  48. //Cycle waste and stock
  49. if (hand.size() == 0) {
  50. if (stock.size() > 0) {
  51. //move from stock to waste
  52. waste.push_back(stock.pop_back());
  53. waste.peek_back()->exposed = true;
  54. } else {
  55. //move back all the card
  56. while (waste.size() > 0) {
  57. waste.peek_back()->exposed = false;
  58. stock.push_back(waste.pop_back());
  59. }
  60. }
  61. return;
  62. }
  63. return;
  64. } else if (selection[1] == 1 || selection[0] > 0) {
  65. PickAndPlace();
  66. }
  67. return;
  68. }
  69. return;
  70. }
  71. if (state == Play)
  72. HandleNavigation(key);
  73. } else if (type == InputTypeLong) {
  74. switch (key) {
  75. case InputKeyLeft:
  76. selection[0] = 0;
  77. selectedCard = 1;
  78. break;
  79. case InputKeyRight:
  80. selection[0] = 6;
  81. selectedCard = 1;
  82. break;
  83. case InputKeyUp:
  84. selectedCard = 1;
  85. selection[1] = 0;
  86. break;
  87. case InputKeyOk:
  88. if (CanSolve()) {
  89. state = Solve;
  90. break;
  91. }
  92. //quick place
  93. if (state == Play && (selection[0] == 1 || selection[1] == 1) && selectedCard == 1) {
  94. Card *c = selection[1] == 0 ? waste.peek_back() : tableau[selection[0]].peek_back();
  95. if (!c->exposed) return;
  96. for (int i = 0; i < 4; i++) {
  97. if (c->CanPlaceFoundation(foundation[i].peek_back())) {
  98. if (selection[1] == 0)
  99. foundation[i].push_back(waste.pop_back());
  100. else
  101. foundation[i].push_back(tableau[selection[0]].pop_back());
  102. break;
  103. }
  104. }
  105. }
  106. break;
  107. default:
  108. break;
  109. }
  110. }
  111. }
  112. void GameLogic::PickAndPlace() {
  113. //pick and place waste
  114. if (selection[0] == 1 && selection[1] == 0) {
  115. if (waste.size() > 0 && hand.size() == 0) {
  116. hand.push_back(waste.pop_back());
  117. target[0] = 1;
  118. target[1] = 0;
  119. } else if (hand.size() == 1 && target[0] == 1 && target[1] == 0) {
  120. waste.push_back(hand.pop_back());
  121. target[0] = 0;
  122. target[1] = 0;
  123. }
  124. }
  125. //place to foundation
  126. else if (selection[1] == 0 && selection[0] > 2 && hand.size() == 1) {
  127. uint8_t id = selection[0] - 3;
  128. if (hand.peek_back()->CanPlaceFoundation(foundation[id].peek_back())) {
  129. foundation[id].push_back(hand.pop_back());
  130. target[0] = selection[0];
  131. target[1] = selection[1];
  132. }
  133. }
  134. //pick and place columns
  135. else if (selection[1] == 1) {
  136. auto &tbl = tableau[selection[0]];
  137. if (hand.size() == 0) {
  138. if (tbl.peek_back() && !tbl.peek_back()->exposed) {
  139. tbl.peek_back()->exposed = true;
  140. } else {
  141. uint8_t count = selectedCard;
  142. while (count > 0) {
  143. hand.push_front(tbl.pop_back());
  144. count--;
  145. }
  146. selectedCard = 1;
  147. target[0] = selection[0];
  148. target[1] = selection[1];
  149. }
  150. } else if ((target[0] == selection[0] && target[1] == selection[1]) ||
  151. hand.peek_front()->CanPlaceColumn(tbl.peek_back())) {
  152. while (hand.size() > 0) {
  153. tbl.push_back(hand.pop_front());
  154. }
  155. target[0] = 0;
  156. target[1] = 0;
  157. }
  158. }
  159. }
  160. void GameLogic::HandleNavigation(int key) {
  161. if (key == InputKeyLeft && selection[0] > 0) {
  162. selectedCard = 1;
  163. selection[0]--;
  164. } else if (key == InputKeyRight && selection[0] < 6) {
  165. selectedCard = 1;
  166. selection[0]++;
  167. } else if (key == InputKeyUp && selection[1] == 1) {
  168. auto &tbl = tableau[selection[0]];
  169. uint8_t first = FirstNonFlipped(tbl);
  170. if (tbl.size() > 0 && first < tbl.size() - selectedCard && hand.size() == 0) {
  171. selectedCard++;
  172. } else {
  173. selectedCard = 1;
  174. selection[1]--;
  175. }
  176. } else if (key == InputKeyDown && selection[1] == 0) {
  177. selectedCard = 1;
  178. selection[1]++;
  179. } else if (selection[1] == 1 && selectedCard > 1) {
  180. selectedCard--;
  181. }
  182. if (selection[1] == 0 && selection[0] == 2) { //skip empty space
  183. selection[0] += key == InputKeyRight ? 1 : -1;
  184. }
  185. }
  186. void GameLogic::Update(float delta) {
  187. if(state != Finish){
  188. buffer->clear();
  189. }
  190. switch (state) {
  191. case Logo:
  192. Reset();
  193. buffer->draw(&logo, (Vector) {60, 30}, 0);
  194. buffer->draw(&main_image, (Vector) {115, 25}, 0);
  195. buffer->draw(&start, (Vector) {64, 55}, 0);
  196. break;
  197. case Intro:
  198. DoIntro(delta);
  199. dirty = true;
  200. break;
  201. case Play:
  202. DrawPlayScene();
  203. if (CanSolve()) {
  204. buffer->draw_rbox(25, 53, 101, 64, Black);
  205. buffer->draw_rbox(26, 54, 100, 63, White);
  206. buffer->draw(&solve, (Vector) {64, 58}, 0);
  207. end = furi_get_tick();
  208. }
  209. break;
  210. default:
  211. break;
  212. }
  213. buffer->swap();
  214. readyToRender = true;
  215. }
  216. void GameLogic::Reset() {
  217. stock.deleteData();
  218. stock.clear();
  219. waste.deleteData();
  220. waste.clear();
  221. tempPos = {0, 0};
  222. selection[0] = 0;
  223. selection[1] = 0;
  224. target[0] = 0;
  225. selectedCard = 1;
  226. target[1] = -1;
  227. for (int i = 0; i < 7; i++) {
  228. tableau[i].deleteData();
  229. tableau[i].clear();
  230. if (i < 4) {
  231. foundation[i].deleteData();
  232. foundation[i].clear();
  233. }
  234. }
  235. GenerateDeck();
  236. }
  237. bool GameLogic::CanSolve() {
  238. for (uint8_t i = 0; i < 7; i++) {
  239. if (tableau[i].peek_front() && !tableau[i].peek_front()->exposed)
  240. return false;
  241. }
  242. return state == Play;
  243. }
  244. void GameLogic::GenerateDeck() {
  245. int cards_count = 52;
  246. uint8_t cards[cards_count];
  247. for (int i = 0; i < cards_count; i++) cards[i] = i % 52;
  248. srand(DWT->CYCCNT);
  249. //reorder
  250. for (int i = 0; i < cards_count; i++) {
  251. int r = i + (rand() % (cards_count - i));
  252. uint8_t card = cards[i];
  253. cards[i] = cards[r];
  254. cards[r] = card;
  255. }
  256. //Init deck list
  257. for (int i = 0; i < cards_count; i++) {
  258. int letter = cards[i] % 13;
  259. int suit = cards[i] / 13;
  260. stock.push_back(new Card(suit, letter));
  261. }
  262. }
  263. GameLogic::~GameLogic() {
  264. stock.deleteData();
  265. stock.clear();
  266. waste.deleteData();
  267. waste.clear();
  268. hand.deleteData();
  269. hand.clear();
  270. for (int i = 0; i < 7; i++) {
  271. tableau[i].deleteData();
  272. tableau[i].clear();
  273. if (i < 4) {
  274. foundation[i].deleteData();
  275. foundation[i].clear();
  276. }
  277. }
  278. }
  279. Vector pos, targetPos;
  280. void GameLogic::DoIntro(float delta) {
  281. //render next after finish
  282. if (!buffer) return;
  283. buffer->clear();
  284. DrawPlayScene();
  285. if (tempCard) {
  286. targetPos = {
  287. 2.0f + (float) target[0] * 18,
  288. MIN(25.0f + (float) target[1] * 4, 36)
  289. };
  290. tempTime += delta * 10;
  291. pos = Vector::Lerp(tempPos, targetPos, MIN(tempTime, 1));
  292. tempCard->Render((int) pos.x, (int) pos.y, false, buffer);
  293. float distance = targetPos.distance(pos);
  294. if (distance < 0.01) {
  295. tempCard = nullptr;
  296. tableau[target[0]].push_back(stock.pop_back());
  297. tableau[target[0]].peek_back()->exposed = target[0] == target[1];
  298. if (target[0] == 6 && target[1] == 6) {
  299. startTime = furi_get_tick();
  300. state = Play;
  301. buffer->clear();
  302. DrawPlayScene();
  303. }
  304. }
  305. } else {
  306. tempTime = 0;
  307. tempCard = stock.peek_back();
  308. tempPos.x = 2;
  309. tempPos.y = 1;
  310. if (target[0] == target[1]) {
  311. target[0]++;
  312. target[1] = 0;
  313. } else {
  314. target[1]++;
  315. }
  316. }
  317. }
  318. void GameLogic::DrawPlayScene() {
  319. if (stock.size() > 0) {
  320. if (stock.size() > 1) {
  321. stock.peek_back()->Render(2, 1, true, buffer);
  322. stock.peek_back()->Render(0, 0, selection[0] == 0 && selection[1] == 0, buffer);
  323. } else {
  324. stock.peek_back()->Render(2, 1, selection[0] == 0 && selection[1] == 0, buffer);
  325. }
  326. } else
  327. Card::TryRender(nullptr, 2, 1, selection[0] == 1 && selection[1] == 0, buffer);
  328. Card::TryRender(waste.peek_back(), 20, 1, selection[0] == 1 && selection[1] == 0, buffer);
  329. int i;
  330. for (i = 0; i < 4; i++) {
  331. Card::TryRender(foundation[i].peek_back(), 56 + i * 18, 1, selection[0] == i + 3 && selection[1] == 0, buffer);
  332. }
  333. for (i = 0; i < 7; i++) {
  334. DrawColumn(2 + i * 18, 25, (selection[0] == i && selection[1] == 1) ? selectedCard : 0, i);
  335. }
  336. if (hand.size() > 0) {
  337. if (selection[1] == 0)
  338. DrawColumn(10 + selection[0] * 18, 15 + selection[1] * 25, hand.size(), -1);
  339. else {
  340. int shift = MIN((int) tableau[selection[0]].size(), 4) * 4;
  341. DrawColumn(10 + selection[0] * 18, 30 + shift, hand.size(), -1);
  342. }
  343. }
  344. }
  345. void GameLogic::DrawColumn(uint8_t x, uint8_t y, uint8_t selected, int8_t column) {
  346. UNUSED(selected);
  347. auto &deck = column >= 0 ? tableau[column] : hand;
  348. if (deck.size() == 0 && column >= 0) {
  349. Card::RenderEmptyCard(x, y, buffer);
  350. buffer->draw_rbox(x + 1, y + 1, x + 16, y + 22, Flip);
  351. return;
  352. }
  353. uint8_t selection = deck.size() - selected;
  354. // if (selected != 0) selection--;
  355. uint8_t loop_end = deck.size();
  356. uint8_t loop_start = MAX(loop_end - 4, 0);
  357. uint8_t position = 0;
  358. uint8_t first_non_flipped = FirstNonFlipped(deck);
  359. bool had_top = false;
  360. bool showDark = column >= 0;
  361. // Draw the first flipped and non-flipped card with adjusted visibility
  362. if (first_non_flipped <= loop_start && selection != first_non_flipped) {
  363. // Draw a card back if it is not the first card
  364. if (first_non_flipped > 0) {
  365. Card::RenderBack(x, y + position, false, buffer, 5);
  366. // Increment loop start index and position
  367. position += 4;
  368. loop_start++;
  369. had_top = true;
  370. }
  371. // Draw the front side of the first non-flipped card
  372. deck[first_non_flipped]->Render(x, y + position, false, buffer, deck.size() == 1 ? 22 : 9);
  373. position += 8;
  374. loop_start++; // Increment loop start index
  375. }
  376. // Draw the selected card with adjusted visibility
  377. if (loop_start > selection) {
  378. if (!had_top && first_non_flipped > 0) {
  379. Card::RenderBack(x, y + position, false, buffer, 5);
  380. position += 4;
  381. loop_start++;
  382. }
  383. // Draw the front side of the selected card
  384. deck[selection]->Render(x, y + position, showDark, buffer, 9);
  385. position += 8;
  386. loop_start++; // Increment loop start index
  387. }
  388. int height = 5;
  389. uint8_t i = 0;
  390. for (auto *card: deck) {
  391. if (i >= loop_start && i < loop_end) {
  392. height = 5;
  393. if ((i + 1) == loop_end) height = 22;
  394. else if (i == selection || i == first_non_flipped) height = 9;
  395. card->Render(x, y + position, i == selection && showDark, buffer, height);
  396. if (i == selection || i == first_non_flipped)position += 4;
  397. position += 4;
  398. }
  399. i++;
  400. }
  401. }
  402. int8_t GameLogic::FirstNonFlipped(const List<Card> &deck) {
  403. int8_t index = 0;
  404. for (auto *card: deck) {
  405. if (card->exposed) return index;
  406. index++;
  407. }
  408. return -1;
  409. }