citybloxx.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include <notification/notification.h>
  6. #include <notification/notification_messages.h>
  7. #include <math.h>
  8. #include "types.h"
  9. #include "blocks.h"
  10. #include "manipulations.h"
  11. #define TICK_VALUE 0.002
  12. #define SIN_MULTIPLIER 10
  13. #define ROTATION (sin(ticks) * SIN_MULTIPLIER)
  14. #define ROTATION_LAST (sin(ticks - TICK_VALUE) * SIN_MULTIPLIER)
  15. #define ROTATION_DIFF (ROTATION - ROTATION_LAST)
  16. #define G_CONSTANT 9.8
  17. #define BLOCK_COUNT 3
  18. bool exiting = false;
  19. bool game_over;
  20. Block* block_list;
  21. uint16_t block_list_i;
  22. int8_t balance_base;
  23. int32_t balance_sum;
  24. uint16_t balance_count;
  25. #define BALANCE_VALUE ((balance_sum / balance_count) - balance_base)
  26. const int32_t base = 80;
  27. const uint8_t house_block_height = 30;
  28. Point top;
  29. Point bottom;
  30. Point ground_point;
  31. Block falling_block;
  32. float ticks;
  33. float fall_tick;
  34. float x_velocity;
  35. uint16_t score;
  36. static void inc_tick(void* ctx) {
  37. UNUSED(ctx);
  38. ticks += TICK_VALUE;
  39. }
  40. static void fall(void* ctx) {
  41. UNUSED(ctx);
  42. line_group_translate(
  43. falling_block.lines, falling_block.count, x_velocity, G_CONSTANT * (ticks - fall_tick));
  44. Point center = line_group_get_center(falling_block.lines, falling_block.count);
  45. if(center.y >= base + house_block_height / 2) {
  46. // If we hit the ground or another block
  47. score++;
  48. if(block_list_i == BLOCK_COUNT) {
  49. free(block_list[0].lines);
  50. for(uint16_t i = 0; i < block_list_i - 1; i++) block_list[i] = block_list[i + 1];
  51. block_list_i--;
  52. }
  53. if(block_list_i == 0) {
  54. // First block ever
  55. balance_base = center.x;
  56. } else {
  57. // Checking if we are misaligned
  58. Point center_last = line_group_get_center(
  59. block_list[block_list_i - 1].lines, block_list[block_list_i - 1].count);
  60. if(abs(center_last.x - center.x) > 6) {
  61. // Playing the game over sound
  62. NotificationApp* notification_app = furi_record_open(RECORD_NOTIFICATION);
  63. notification_message(notification_app, &sequence_error);
  64. furi_record_close(RECORD_NOTIFICATION);
  65. game_over = true;
  66. }
  67. }
  68. balance_sum += center.x;
  69. balance_count++;
  70. line_group_translate(
  71. falling_block.lines,
  72. falling_block.count,
  73. 0,
  74. (base + house_block_height / 2) - center.y);
  75. block_list[block_list_i] = falling_block;
  76. falling_block.count = 0;
  77. block_list_i++;
  78. }
  79. }
  80. static void render_line(Canvas* canvas, Line line) {
  81. canvas_draw_line(canvas, line.a.x, line.a.y, line.b.x, line.b.y);
  82. }
  83. static void render_line_group(Canvas* canvas, Line* line_group, uint8_t count) {
  84. for(uint8_t i = 0; i < count; i++) render_line(canvas, line_group[i]);
  85. }
  86. static Block get_house_i() {
  87. const float rotation = ROTATION;
  88. Block house = get_house();
  89. line_group_translate(house.lines, house.count, bottom.x, bottom.y);
  90. line_group_rotate(house.lines, house.count, top, rotation);
  91. return house;
  92. }
  93. static Block get_crane_i() {
  94. const float rotation = ROTATION;
  95. Block crane = get_crane();
  96. line_group_translate(crane.lines, crane.count, top.x, top.y);
  97. line_group_rotate(crane.lines, crane.count, top, rotation);
  98. return crane;
  99. }
  100. static Block get_ground_i() {
  101. Block ground = get_ground();
  102. line_group_translate(ground.lines, ground.count, ground_point.x, ground_point.y);
  103. return ground;
  104. }
  105. static void release_block() {
  106. if(falling_block.count != 0) return;
  107. ground_point.y += 30;
  108. fall_tick = ticks;
  109. const float rotation = ROTATION;
  110. x_velocity = ROTATION_DIFF * -100; // The error here is insignificant enough
  111. falling_block = get_house_i();
  112. line_group_rotate_center(falling_block.lines, falling_block.count, -rotation);
  113. for(uint16_t i = 0; i < block_list_i; i++)
  114. line_group_translate(block_list[i].lines, block_list[i].count, 0, house_block_height);
  115. }
  116. // Screen is 128x64 px
  117. static void app_draw_callback(Canvas* canvas, void* ctx) {
  118. UNUSED(ctx);
  119. canvas_set_font(canvas, FontPrimary);
  120. if(exiting) return;
  121. if(game_over) {
  122. canvas_draw_str(canvas, 2, 70, "Game over");
  123. return;
  124. }
  125. canvas_clear(canvas);
  126. canvas_draw_frame(canvas, 0, 0, 64, 128);
  127. if(falling_block.count == 0) {
  128. Block house = get_house_i();
  129. render_line_group(canvas, house.lines, house.count);
  130. free(house.lines);
  131. }
  132. Block crane = get_crane_i();
  133. render_line_group(canvas, crane.lines, crane.count);
  134. free(crane.lines);
  135. if(ground_point.y < 128) {
  136. Block ground = get_ground_i();
  137. render_line_group(canvas, ground.lines, ground.count);
  138. free(ground.lines);
  139. }
  140. if(falling_block.count != 0)
  141. render_line_group(canvas, falling_block.lines, falling_block.count);
  142. const Point rot_base = {balance_base, base};
  143. Block* block_list_copy = malloc(sizeof(Block) * BLOCK_COUNT);
  144. const int16_t deg = BALANCE_VALUE / 4;
  145. for(uint16_t i = 0; i < block_list_i; i++) {
  146. block_list_copy[i] = block_list[i];
  147. size_t size = sizeof(Line) * HOUSE_LINE_CO;
  148. block_list_copy[i].lines = malloc(size);
  149. memcpy(block_list_copy[i].lines, block_list[i].lines, size);
  150. line_group_rotate(block_list_copy[i].lines, block_list_copy[i].count, rot_base, deg);
  151. }
  152. for(uint16_t i = 0; i < block_list_i; i++)
  153. render_line_group(canvas, block_list_copy[i].lines, block_list_copy[i].count);
  154. for(uint16_t i = 0; i < block_list_i; i++) free(block_list_copy[i].lines);
  155. free(block_list_copy);
  156. FuriString* str = furi_string_alloc();
  157. furi_string_printf(str, "%hu", score);
  158. canvas_draw_str(canvas, 4, 16, furi_string_get_cstr(str));
  159. furi_string_free(str);
  160. }
  161. static void app_input_callback(InputEvent* input_event, void* ctx) {
  162. furi_assert(ctx);
  163. FuriMessageQueue* event_queue = ctx;
  164. furi_message_queue_put(event_queue, input_event, FuriWaitForever);
  165. }
  166. static void restart() {
  167. block_list = malloc(sizeof(Block) * BLOCK_COUNT);
  168. block_list_i = 0;
  169. balance_base = 0;
  170. balance_sum = 0;
  171. balance_count = 0;
  172. top.x = 32;
  173. top.y = 0;
  174. bottom.x = top.x;
  175. bottom.y = top.y + 15;
  176. ground_point.x = 32;
  177. ground_point.y = base;
  178. falling_block.count = 0;
  179. falling_block.lines = NULL;
  180. ticks = 0;
  181. fall_tick = 0;
  182. x_velocity = 0;
  183. score = 0;
  184. game_over = false;
  185. }
  186. int32_t citybloxx_main(void* p) {
  187. UNUSED(p);
  188. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  189. // Configure view port
  190. ViewPort* view_port = view_port_alloc();
  191. view_port_draw_callback_set(view_port, app_draw_callback, view_port);
  192. view_port_input_callback_set(view_port, app_input_callback, event_queue);
  193. view_port_set_orientation(view_port, ViewPortOrientationVertical);
  194. // Register view port in GUI
  195. Gui* gui = furi_record_open(RECORD_GUI);
  196. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  197. InputEvent event;
  198. restart();
  199. FuriTimer* timer = furi_timer_alloc(inc_tick, FuriTimerTypePeriodic, NULL);
  200. furi_timer_start(timer, 1);
  201. FuriTimer* fall_timer = furi_timer_alloc(fall, FuriTimerTypePeriodic, NULL);
  202. furi_timer_start(fall_timer, 100);
  203. bool running = true;
  204. while(running) {
  205. if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
  206. if(event.type == InputTypeLong && event.key == InputKeyBack) running = false;
  207. if(event.type == InputTypePress && event.key == InputKeyOk)
  208. (game_over ? restart() : release_block());
  209. }
  210. view_port_update(view_port);
  211. }
  212. exiting = true;
  213. for(uint16_t i = 0; i < block_list_i; i++) free(block_list[i].lines);
  214. free(block_list);
  215. furi_timer_stop(timer);
  216. furi_timer_free(timer);
  217. furi_timer_stop(fall_timer);
  218. furi_timer_free(fall_timer);
  219. view_port_enabled_set(view_port, false);
  220. gui_remove_view_port(gui, view_port);
  221. view_port_free(view_port);
  222. furi_message_queue_free(event_queue);
  223. furi_record_close(RECORD_GUI);
  224. return 0;
  225. }