scorched_tanks_game_app.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include <input/input.h>
  4. #include <stdlib.h>
  5. #include <math.h>
  6. #include <notification/notification.h>
  7. #include <notification/notification_messages.h>
  8. #define SCREEN_WIDTH 128
  9. #define SCREEN_HEIGHT 64
  10. #define PLAYER_INIT_LOCATION_X 20
  11. #define PLAYER_INIT_AIM 45
  12. #define PLAYER_INIT_POWER 50
  13. #define ENEMY_INIT_LOCATION_X 108
  14. #define TANK_BARREL_LENGTH 7
  15. #define GRAVITY_FORCE (double)0.5
  16. #define MIN_GROUND_HEIGHT 35
  17. #define MAX_GROUND_HEIGHT 55
  18. #define MAX_FIRE_POWER 100
  19. #define MIN_FIRE_POWER 0
  20. #define TANK_COLLIDER_SIZE 3
  21. #define MAX_WIND 10
  22. #define MAX_PLAYER_DIFF_X 20
  23. #define MAX_ENEMY_DIFF_X 20
  24. // That's a filthy workaround but sin(player.aimAngle) breaks it all... If you're able to fix it, please do create a PR!
  25. double scorched_tanks_sin[91] = {
  26. 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.122, -0.139, -0.156, -0.174, -0.191,
  27. -0.208, -0.225, -0.242, -0.259, -0.276, -0.292, -0.309, -0.326, -0.342, -0.358, -0.375, -0.391,
  28. -0.407, -0.423, -0.438, -0.454, -0.469, -0.485, -0.500, -0.515, -0.530, -0.545, -0.559, -0.574,
  29. -0.588, -0.602, -0.616, -0.629, -0.643, -0.656, -0.669, -0.682, -0.695, -0.707, -0.719, -0.731,
  30. -0.743, -0.755, -0.766, -0.777, -0.788, -0.799, -0.809, -0.819, -0.829, -0.839, -0.848, -0.857,
  31. -0.866, -0.875, -0.883, -0.891, -0.899, -0.906, -0.914, -0.921, -0.927, -0.934, -0.940, -0.946,
  32. -0.951, -0.956, -0.961, -0.966, -0.970, -0.974, -0.978, -0.982, -0.985, -0.988, -0.990, -0.993,
  33. -0.995, -0.996, -0.998, -0.999, -0.999, -1.000, -1.000};
  34. double scorched_tanks_cos[91] = {
  35. 1.000, 1.000, 0.999, 0.999, 0.998, 0.996, 0.995, 0.993, 0.990, 0.988, 0.985, 0.982, 0.978,
  36. 0.974, 0.970, 0.966, 0.961, 0.956, 0.951, 0.946, 0.940, 0.934, 0.927, 0.921, 0.914, 0.906,
  37. 0.899, 0.891, 0.883, 0.875, 0.866, 0.857, 0.848, 0.839, 0.829, 0.819, 0.809, 0.799, 0.788,
  38. 0.777, 0.766, 0.755, 0.743, 0.731, 0.719, 0.707, 0.695, 0.682, 0.669, 0.656, 0.643, 0.629,
  39. 0.616, 0.602, 0.588, 0.574, 0.559, 0.545, 0.530, 0.515, 0.500, 0.485, 0.469, 0.454, 0.438,
  40. 0.423, 0.407, 0.391, 0.375, 0.358, 0.342, 0.326, 0.309, 0.292, 0.276, 0.259, 0.242, 0.225,
  41. 0.208, 0.191, 0.174, 0.156, 0.139, 0.122, 0.105, 0.087, 0.070, 0.052, 0.035, 0.017, 0.000};
  42. double scorched_tanks_tan[91] = {
  43. 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.123, -0.141, -0.158, -0.176,
  44. -0.194, -0.213, -0.231, -0.249, -0.268, -0.287, -0.306, -0.325, -0.344, -0.364, -0.384,
  45. -0.404, -0.424, -0.445, -0.466, -0.488, -0.510, -0.532, -0.554, -0.577, -0.601, -0.625,
  46. -0.649, -0.674, -0.700, -0.727, -0.754, -0.781, -0.810, -0.839, -0.869, -0.900, -0.932,
  47. -0.966, -1.000, -1.036, -1.072, -1.111, -1.150, -1.192, -1.235, -1.280, -1.327, -1.376,
  48. -1.428, -1.483, -1.540, -1.600, -1.664, -1.732, -1.804, -1.881, -1.963, -2.050, -2.144,
  49. -2.246, -2.356, -2.475, -2.605, -2.747, -2.904, -3.078, -3.271, -3.487, -3.732, -4.011,
  50. -4.331, -4.704, -5.144, -5.671, -6.313, -7.115, -8.144, -9.513, -11.429, -14.298, -19.077,
  51. -28.627, -57.254, -90747.269};
  52. unsigned char scorched_tanks_ground_modifiers[SCREEN_WIDTH] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  53. typedef struct
  54. {
  55. // +-----x
  56. // |
  57. // |
  58. // y
  59. uint8_t x;
  60. uint8_t y;
  61. } Point;
  62. typedef struct
  63. {
  64. // +-----x
  65. // |
  66. // |
  67. // y
  68. double x;
  69. double y;
  70. } PointDetailed;
  71. typedef struct
  72. {
  73. unsigned char locationX;
  74. unsigned char hp;
  75. int aimAngle;
  76. unsigned char firePower;
  77. } Tank;
  78. typedef struct
  79. {
  80. Point ground[SCREEN_WIDTH];
  81. Tank player;
  82. Tank enemy;
  83. bool isPlayerTurn;
  84. bool isShooting;
  85. int windSpeed;
  86. Point trajectory[SCREEN_WIDTH];
  87. unsigned char trajectoryAnimationStep;
  88. PointDetailed bulletPosition;
  89. PointDetailed bulletVector;
  90. } Game;
  91. typedef enum
  92. {
  93. EventTypeTick,
  94. EventTypeKey,
  95. } EventType;
  96. typedef struct
  97. {
  98. EventType type;
  99. InputEvent input;
  100. } ScorchedTanksEvent;
  101. int scorched_tanks_random(int min, int max)
  102. {
  103. return min + rand() % ((max + 1) - min);
  104. }
  105. void scorched_tanks_generate_ground(Game *game_state)
  106. {
  107. int lastHeight = 45;
  108. for (unsigned char a = 0; a < SCREEN_WIDTH; a++)
  109. {
  110. int diffHeight = scorched_tanks_random(-2, 3);
  111. int changeLength = scorched_tanks_random(1, 6);
  112. if (diffHeight == 0)
  113. {
  114. changeLength = 1;
  115. }
  116. for (int b = 0; b < changeLength; b++)
  117. {
  118. if (a + b < SCREEN_WIDTH)
  119. {
  120. int index = a + b;
  121. int newPoint = lastHeight + diffHeight;
  122. newPoint = newPoint < MIN_GROUND_HEIGHT ? MIN_GROUND_HEIGHT : newPoint;
  123. newPoint = newPoint > MAX_GROUND_HEIGHT ? MAX_GROUND_HEIGHT : newPoint;
  124. game_state->ground[index].x = index;
  125. game_state->ground[index].y = newPoint - scorched_tanks_ground_modifiers[a];
  126. lastHeight = newPoint;
  127. }
  128. else
  129. {
  130. a += b;
  131. break;
  132. }
  133. }
  134. a += changeLength - 1;
  135. }
  136. }
  137. void scorched_tanks_init_game(Game *game_state)
  138. {
  139. game_state->player.locationX = PLAYER_INIT_LOCATION_X + scorched_tanks_random(0, MAX_PLAYER_DIFF_X) - MAX_PLAYER_DIFF_X / 2;
  140. game_state->player.aimAngle = PLAYER_INIT_AIM;
  141. game_state->player.firePower = PLAYER_INIT_POWER;
  142. game_state->enemy.aimAngle = PLAYER_INIT_AIM;
  143. game_state->enemy.firePower = PLAYER_INIT_POWER;
  144. game_state->enemy.locationX = ENEMY_INIT_LOCATION_X + scorched_tanks_random(0, MAX_ENEMY_DIFF_X) - MAX_ENEMY_DIFF_X / 2;
  145. game_state->isPlayerTurn = true;
  146. game_state->windSpeed = scorched_tanks_random(0, MAX_WIND);
  147. for (int x = 0; x < SCREEN_WIDTH; x++)
  148. {
  149. game_state->trajectory[x].x = 0;
  150. game_state->trajectory[x].y = 0;
  151. }
  152. scorched_tanks_generate_ground(game_state);
  153. }
  154. void scorched_tanks_calculate_trajectory(Game *game_state)
  155. {
  156. if (game_state->isShooting)
  157. {
  158. game_state->bulletVector.x += ((double)game_state->windSpeed - MAX_WIND / 2) / 40;
  159. game_state->bulletVector.y += GRAVITY_FORCE;
  160. game_state->bulletPosition.x += game_state->bulletVector.x;
  161. game_state->bulletPosition.y += game_state->bulletVector.y;
  162. int totalDistanceToEnemy = 100;
  163. if (game_state->isPlayerTurn)
  164. {
  165. double distanceToEnemyX = game_state->enemy.locationX - game_state->bulletPosition.x;
  166. double distanceToEnemyY = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE - game_state->bulletPosition.y;
  167. totalDistanceToEnemy = sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY);
  168. }
  169. else
  170. {
  171. double distanceToEnemyX = game_state->player.locationX - game_state->bulletPosition.x;
  172. double distanceToEnemyY = game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE - game_state->bulletPosition.y;
  173. totalDistanceToEnemy = sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY);
  174. }
  175. if (totalDistanceToEnemy <= TANK_COLLIDER_SIZE)
  176. {
  177. game_state->isShooting = false;
  178. scorched_tanks_init_game(game_state);
  179. game_state->isPlayerTurn = !game_state->isPlayerTurn;
  180. return;
  181. }
  182. if (game_state->bulletPosition.x > SCREEN_WIDTH ||
  183. game_state->bulletPosition.y > game_state->ground[(int)round(game_state->bulletPosition.x)].y)
  184. {
  185. game_state->isShooting = false;
  186. game_state->bulletPosition.x = 0;
  187. game_state->bulletPosition.y = 0;
  188. game_state->windSpeed = scorched_tanks_random(0, MAX_WIND);
  189. game_state->isPlayerTurn = !game_state->isPlayerTurn;
  190. return;
  191. }
  192. if (game_state->bulletPosition.y > 0)
  193. {
  194. game_state->trajectory[game_state->trajectoryAnimationStep].x = round(game_state->bulletPosition.x);
  195. game_state->trajectory[game_state->trajectoryAnimationStep].y = round(game_state->bulletPosition.y);
  196. game_state->trajectoryAnimationStep++;
  197. }
  198. }
  199. }
  200. static void scorched_tanks_draw_tank(Canvas *const canvas, unsigned char x, unsigned char y, bool isPlayer)
  201. {
  202. int lineIndex = -2;
  203. if (isPlayer)
  204. {
  205. // Draw tank base
  206. canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex);
  207. lineIndex++;
  208. canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex);
  209. lineIndex++;
  210. canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex);
  211. lineIndex++;
  212. // draw turret
  213. canvas_draw_line(canvas, x - 2, y - lineIndex, x + 1, y - lineIndex);
  214. lineIndex++;
  215. canvas_draw_line(canvas, x - 2, y - lineIndex, x, y - lineIndex);
  216. lineIndex++;
  217. }
  218. else
  219. {
  220. // Draw tank base
  221. canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex);
  222. lineIndex++;
  223. canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex);
  224. lineIndex++;
  225. canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex);
  226. lineIndex++;
  227. // draw turret
  228. canvas_draw_line(canvas, x - 1, y - lineIndex, x + 2, y - lineIndex);
  229. lineIndex++;
  230. canvas_draw_line(canvas, x, y - lineIndex, x + 2, y - lineIndex);
  231. lineIndex++;
  232. }
  233. }
  234. static void scorched_tanks_render_callback(Canvas *const canvas, void *ctx)
  235. {
  236. const Game *game_state = acquire_mutex((ValueMutex *)ctx, 25);
  237. if (game_state == NULL)
  238. {
  239. return;
  240. }
  241. canvas_draw_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
  242. canvas_set_color(canvas, ColorBlack);
  243. if (game_state->isShooting)
  244. {
  245. canvas_draw_dot(canvas, game_state->bulletPosition.x, game_state->bulletPosition.y);
  246. }
  247. for (int a = 1; a < SCREEN_WIDTH; a++)
  248. {
  249. canvas_draw_line(
  250. canvas,
  251. game_state->ground[a - 1].x,
  252. game_state->ground[a - 1].y,
  253. game_state->ground[a].x,
  254. game_state->ground[a].y);
  255. if (game_state->trajectory[a].y != 0)
  256. {
  257. canvas_draw_dot(canvas, game_state->trajectory[a].x, game_state->trajectory[a].y);
  258. }
  259. }
  260. scorched_tanks_draw_tank(canvas, game_state->enemy.locationX, game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE, true);
  261. scorched_tanks_draw_tank(canvas, game_state->player.locationX, game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE, false);
  262. int aimX1 = 0;
  263. int aimY1 = 0;
  264. int aimX2 = 0;
  265. int aimY2 = 0;
  266. if (game_state->isPlayerTurn)
  267. {
  268. aimX1 = game_state->player.locationX;
  269. aimY1 = game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE;
  270. double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle];
  271. double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle];
  272. aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle;
  273. aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle;
  274. aimX1 += 2;
  275. aimX2 += 2;
  276. }
  277. else
  278. {
  279. aimX1 = game_state->enemy.locationX;
  280. aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE + 2;
  281. double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle];
  282. double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle];
  283. aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle;
  284. aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle;
  285. aimX2 = aimX1 - (aimX2 - aimX1);
  286. aimX1 -= 2;
  287. aimX2 -= 2;
  288. }
  289. canvas_draw_line(canvas, aimX1, aimY1 - 2, aimX2, aimY2 - 2);
  290. canvas_set_font(canvas, FontSecondary);
  291. char buffer2[18];
  292. snprintf(buffer2, sizeof(buffer2), "wind: %i", game_state->windSpeed - MAX_WIND / 2);
  293. canvas_draw_str(canvas, 55, 10, buffer2);
  294. if (game_state->isPlayerTurn)
  295. {
  296. canvas_draw_str(canvas, 93, 10, "player1");
  297. char buffer[12];
  298. snprintf(buffer, sizeof(buffer), "a: %u", game_state->player.aimAngle);
  299. canvas_draw_str(canvas, 2, 10, buffer);
  300. snprintf(buffer, sizeof(buffer), "p: %u", game_state->player.firePower);
  301. canvas_draw_str(canvas, 27, 10, buffer);
  302. }
  303. else
  304. {
  305. canvas_draw_str(canvas, 93, 10, "player2");
  306. char buffer[12];
  307. snprintf(buffer, sizeof(buffer), "a: %u", game_state->enemy.aimAngle);
  308. canvas_draw_str(canvas, 2, 10, buffer);
  309. snprintf(buffer, sizeof(buffer), "p: %u", game_state->enemy.firePower);
  310. canvas_draw_str(canvas, 27, 10, buffer);
  311. }
  312. release_mutex((ValueMutex *)ctx, game_state);
  313. }
  314. static void scorched_tanks_input_callback(InputEvent *input_event, FuriMessageQueue *event_queue)
  315. {
  316. furi_assert(event_queue);
  317. ScorchedTanksEvent event = {.type = EventTypeKey, .input = *input_event};
  318. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  319. }
  320. static void scorched_tanks_update_timer_callback(FuriMessageQueue *event_queue)
  321. {
  322. furi_assert(event_queue);
  323. ScorchedTanksEvent event = {.type = EventTypeTick};
  324. furi_message_queue_put(event_queue, &event, 0);
  325. }
  326. static void scorched_tanks_increase_power(Game *game_state)
  327. {
  328. if (game_state->player.firePower < MAX_FIRE_POWER && !game_state->isShooting)
  329. {
  330. if (game_state->isPlayerTurn && game_state->player.firePower < MAX_FIRE_POWER)
  331. {
  332. game_state->player.firePower++;
  333. }
  334. if (!game_state->isPlayerTurn && game_state->enemy.firePower < MAX_FIRE_POWER)
  335. {
  336. game_state->enemy.firePower++;
  337. }
  338. }
  339. }
  340. static void scorched_tanks_decrease_power(Game *game_state)
  341. {
  342. if (game_state->player.firePower > MIN_FIRE_POWER && !game_state->isShooting)
  343. {
  344. if (game_state->isPlayerTurn && game_state->player.firePower > MIN_FIRE_POWER)
  345. {
  346. game_state->player.firePower--;
  347. }
  348. if (!game_state->isPlayerTurn && game_state->enemy.firePower > MIN_FIRE_POWER)
  349. {
  350. game_state->enemy.firePower--;
  351. }
  352. }
  353. }
  354. static void scorched_tanks_aim_up(Game *game_state)
  355. {
  356. if (!game_state->isShooting)
  357. {
  358. if (game_state->isPlayerTurn && game_state->player.aimAngle < 90)
  359. {
  360. game_state->player.aimAngle++;
  361. }
  362. if (!game_state->isPlayerTurn && game_state->enemy.aimAngle < 90)
  363. {
  364. game_state->enemy.aimAngle++;
  365. }
  366. }
  367. }
  368. static void scorched_tanks_aim_down(Game *game_state)
  369. {
  370. if (!game_state->isShooting)
  371. {
  372. if (game_state->isPlayerTurn && game_state->player.aimAngle > 0)
  373. {
  374. game_state->player.aimAngle--;
  375. }
  376. if (!game_state->isPlayerTurn && game_state->enemy.aimAngle > 0)
  377. {
  378. game_state->enemy.aimAngle--;
  379. }
  380. }
  381. }
  382. const NotificationSequence sequence_long_vibro = {
  383. &message_vibro_on,
  384. &message_delay_500,
  385. &message_vibro_off,
  386. NULL,
  387. };
  388. static void scorched_tanks_fire(Game *game_state)
  389. {
  390. if (!game_state->isShooting)
  391. {
  392. if (game_state->isPlayerTurn)
  393. {
  394. double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle];
  395. double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle];
  396. unsigned char aimX1 = game_state->player.locationX;
  397. unsigned char aimY1 = game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE;
  398. int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle;
  399. int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle;
  400. game_state->bulletPosition.x = aimX2;
  401. game_state->bulletPosition.y = aimY2;
  402. game_state->bulletVector.x = scorched_tanks_cos[game_state->player.aimAngle] * ((double)game_state->player.firePower / 10);
  403. game_state->bulletVector.y = scorched_tanks_sin[game_state->player.aimAngle] * ((double)game_state->player.firePower / 10);
  404. }
  405. else
  406. {
  407. double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle];
  408. double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle];
  409. unsigned char aimX1 = game_state->enemy.locationX;
  410. unsigned char aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE;
  411. int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle;
  412. int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle;
  413. aimX2 = aimX1 - (aimX2 - aimX1);
  414. game_state->bulletPosition.x = aimX2;
  415. game_state->bulletPosition.y = aimY2;
  416. game_state->bulletVector.x = -scorched_tanks_cos[game_state->enemy.aimAngle] * ((double)game_state->enemy.firePower / 10);
  417. game_state->bulletVector.y = scorched_tanks_sin[game_state->enemy.aimAngle] * ((double)game_state->enemy.firePower / 10);
  418. }
  419. game_state->trajectoryAnimationStep = 0;
  420. for (int x = 0; x < SCREEN_WIDTH; x++)
  421. {
  422. game_state->trajectory[x].x = 0;
  423. game_state->trajectory[x].y = 0;
  424. }
  425. game_state->isShooting = true;
  426. NotificationApp *notification = furi_record_open("notification");
  427. notification_message(notification, &sequence_long_vibro);
  428. notification_message(notification, &sequence_blink_white_100);
  429. furi_record_close("notification");
  430. }
  431. }
  432. int32_t scorched_tanks_game_app(void *p)
  433. {
  434. UNUSED(p);
  435. srand(DWT->CYCCNT);
  436. FuriMessageQueue *event_queue = furi_message_queue_alloc(8, sizeof(ScorchedTanksEvent));
  437. Game *game_state = malloc(sizeof(Game));
  438. scorched_tanks_init_game(game_state);
  439. ValueMutex state_mutex;
  440. if (!init_mutex(&state_mutex, game_state, sizeof(ScorchedTanksEvent)))
  441. {
  442. FURI_LOG_E("ScorchedTanks", "cannot create mutex\r\n");
  443. free(game_state);
  444. return 255;
  445. }
  446. ViewPort *view_port = view_port_alloc();
  447. view_port_draw_callback_set(view_port, scorched_tanks_render_callback, &state_mutex);
  448. view_port_input_callback_set(view_port, scorched_tanks_input_callback, event_queue);
  449. FuriTimer *timer =
  450. furi_timer_alloc(scorched_tanks_update_timer_callback, FuriTimerTypePeriodic, event_queue);
  451. furi_timer_start(timer, 2000);
  452. // Open GUI and register view_port
  453. Gui *gui = furi_record_open(RECORD_GUI);
  454. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  455. ScorchedTanksEvent event;
  456. for (bool processing = true; processing;)
  457. {
  458. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50);
  459. if (event.type == EventTypeKey)
  460. { // && game->isPlayerTurn
  461. if (event.input.type == InputTypeRepeat || event.input.type == InputTypeShort)
  462. {
  463. switch (event.input.key)
  464. {
  465. case InputKeyUp:
  466. scorched_tanks_aim_up(game_state);
  467. break;
  468. case InputKeyDown:
  469. scorched_tanks_aim_down(game_state);
  470. break;
  471. case InputKeyRight:
  472. scorched_tanks_increase_power(game_state);
  473. break;
  474. case InputKeyLeft:
  475. scorched_tanks_decrease_power(game_state);
  476. break;
  477. case InputKeyOk:
  478. scorched_tanks_fire(game_state);
  479. break;
  480. case InputKeyBack:
  481. processing = false;
  482. break;
  483. }
  484. }
  485. }
  486. else if (event.type == EventTypeTick)
  487. {
  488. scorched_tanks_calculate_trajectory(game_state);
  489. }
  490. view_port_update(view_port);
  491. release_mutex(&state_mutex, game_state);
  492. }
  493. furi_timer_free(timer);
  494. view_port_enabled_set(view_port, false);
  495. gui_remove_view_port(gui, view_port);
  496. furi_record_close(RECORD_GUI);
  497. view_port_free(view_port);
  498. furi_message_queue_free(event_queue);
  499. delete_mutex(&state_mutex);
  500. free(game_state);
  501. return 0;
  502. }