dice_app.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #include <furi.h>
  2. #include <input/input.h>
  3. #include <gui/gui.h>
  4. #include "constants.h"
  5. const Icon* draw_dice_frame;
  6. static void update(State* const state) {
  7. if(state->app_state == SwipeLeftState) {
  8. for(uint8_t i = 0; i < DICE_TYPES; i++) {
  9. state->dices[i].x -= SWIPE_DIST;
  10. state->dices[i].y = DICE_Y;
  11. }
  12. if(state->dices[state->dice_index].x == DICE_X) {
  13. state->app_state = SelectState;
  14. state->dices[state->dice_index].y = DICE_Y_T;
  15. }
  16. } else if(state->app_state == SwipeRightState) {
  17. for(uint8_t i = 0; i < DICE_TYPES; i++) {
  18. state->dices[i].x += SWIPE_DIST;
  19. state->dices[i].y = DICE_Y;
  20. }
  21. if(state->dices[state->dice_index].x == DICE_X) {
  22. state->app_state = SelectState;
  23. state->dices[state->dice_index].y = DICE_Y_T;
  24. }
  25. } else if(state->app_state == AnimState) {
  26. state->anim_frame += 1;
  27. if(state->dice_index == 0) {
  28. if(state->anim_frame == 3) coin_set_start(state->roll_result); // change coin anim
  29. if(state->anim_frame >= MAX_COIN_FRAMES) {
  30. state->anim_frame = 0;
  31. state->app_state = ResultState;
  32. }
  33. } else {
  34. if(state->anim_frame >= MAX_DICE_FRAMES) {
  35. state->anim_frame = 0;
  36. state->app_state = ResultState;
  37. }
  38. }
  39. }
  40. }
  41. static void roll(State* const state) {
  42. state->roll_result = 0;
  43. for(uint8_t i = 0; i < MAX_DICE_COUNT; i++) {
  44. if(i < state->dice_count) {
  45. state->rolled_dices[i] = (rand() % dice_types[state->dice_index].type) + 1;
  46. state->roll_result += state->rolled_dices[i];
  47. } else {
  48. state->rolled_dices[i] = 0;
  49. }
  50. }
  51. if(state->dice_index == 0) coin_set_end(state->roll_result); // change coin anim
  52. state->app_state = AnimState;
  53. }
  54. static void draw_ui(const State* state, Canvas* canvas) {
  55. canvas_set_font(canvas, FontSecondary);
  56. FuriString* count = furi_string_alloc();
  57. furi_string_printf(count, "%01d", state->dice_count);
  58. // dice name and arrows
  59. if(state->app_state != SwipeLeftState && state->app_state != SwipeRightState) {
  60. canvas_draw_str_aligned(
  61. canvas, 63, 50, AlignCenter, AlignBottom, dice_types[state->dice_index].name);
  62. if(state->dice_index > 0) canvas_draw_icon(canvas, 45, 44, &I_ui_button_left);
  63. if(state->dice_index < DICE_TYPES - 1)
  64. canvas_draw_icon(canvas, 78, 44, &I_ui_button_right);
  65. }
  66. // dice settings
  67. if(state->dice_index == 0)
  68. canvas_draw_icon(canvas, 48, 51, &I_ui_count_1);
  69. else
  70. canvas_draw_icon(canvas, 48, 51, &I_ui_count);
  71. canvas_draw_str_aligned(canvas, 58, 61, AlignCenter, AlignBottom, furi_string_get_cstr(count));
  72. // buttons
  73. canvas_draw_icon(canvas, 92, 54, &I_ui_button_roll);
  74. canvas_draw_icon(canvas, 0, 54, &I_ui_button_exit);
  75. furi_string_free(count);
  76. }
  77. static void draw_dice(const State* state, Canvas* canvas) {
  78. for(uint8_t i = 0; i < DICE_TYPES; i++) {
  79. if(state->app_state == ResultState && state->dice_index == i && state->dice_index != 0)
  80. continue; // draw results except coin
  81. if(state->dices[i].x > 128 || state->dices[i].x < -35) continue; // outside the screen
  82. if(i == state->dice_index) { // draw dice with animation
  83. if(i == 0) { // coin
  84. draw_dice_frame = coin_frames[state->anim_frame];
  85. } else { // dices
  86. draw_dice_frame = dice_frames[(i - 1) * MAX_DICE_FRAMES + state->anim_frame];
  87. }
  88. } else { // draw first dice frame
  89. if(i == 0) { // coin
  90. draw_dice_frame = coin_frames[0];
  91. } else { // dices
  92. draw_dice_frame = dice_frames[(i - 1) * MAX_DICE_FRAMES];
  93. }
  94. }
  95. canvas_draw_icon(canvas, state->dices[i].x, state->dices[i].y, draw_dice_frame);
  96. }
  97. }
  98. static void draw_results(const State* state, Canvas* canvas) {
  99. if(state->app_state != ResultState) return;
  100. if(state->dice_index == 0) return; // skip for coin
  101. canvas_set_font(canvas, FontPrimary);
  102. FuriString* sum = furi_string_alloc();
  103. furi_string_printf(sum, "%01d", state->roll_result);
  104. // result text
  105. canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignCenter, furi_string_get_cstr(sum));
  106. // ui frame
  107. if(state->roll_result > 99)
  108. canvas_draw_icon(canvas, 52, 26, &I_ui_result_3);
  109. else if(state->roll_result > 9)
  110. canvas_draw_icon(canvas, 56, 26, &I_ui_result_2);
  111. else
  112. canvas_draw_icon(canvas, 58, 26, &I_ui_result_1);
  113. furi_string_free(sum);
  114. }
  115. static void draw_callback(Canvas* canvas, void* ctx) {
  116. furi_assert(ctx);
  117. const State* state = ctx;
  118. furi_mutex_acquire(state->mutex, FuriWaitForever);
  119. canvas_clear(canvas);
  120. draw_ui(state, canvas);
  121. draw_dice(state, canvas);
  122. draw_results(state, canvas);
  123. furi_mutex_release(state->mutex);
  124. }
  125. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  126. furi_assert(event_queue);
  127. AppEvent event = {.type = EventTypeKey, .input = *input_event};
  128. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  129. }
  130. static void timer_callback(FuriMessageQueue* event_queue) {
  131. furi_assert(event_queue);
  132. AppEvent event = {.type = EventTypeTick};
  133. furi_message_queue_put(event_queue, &event, 0);
  134. }
  135. int32_t dice_tool_app(void* p) {
  136. UNUSED(p);
  137. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
  138. FURI_LOG_E(TAG, ">>> Started...\r\n");
  139. State* state = malloc(sizeof(State));
  140. init(state);
  141. state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  142. if(!state->mutex) {
  143. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  144. free(state);
  145. return 255;
  146. }
  147. // Set callbacks
  148. ViewPort* view_port = view_port_alloc();
  149. view_port_draw_callback_set(view_port, draw_callback, state);
  150. view_port_input_callback_set(view_port, input_callback, event_queue);
  151. FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue);
  152. furi_timer_start(timer, furi_kernel_get_tick_frequency() * 0.2);
  153. // Create GUI, register view port
  154. Gui* gui = furi_record_open(RECORD_GUI);
  155. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  156. AppEvent event;
  157. for(bool processing = true; processing;) {
  158. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  159. furi_mutex_acquire(state->mutex, FuriWaitForever);
  160. if(event_status == FuriStatusOk) {
  161. // timer evetn
  162. if(event.type == EventTypeTick) {
  163. update(state);
  164. }
  165. // button events
  166. if(event.type == EventTypeKey) {
  167. if(event.input.type == InputTypePress) {
  168. // lock input while animations
  169. if(state->app_state == SelectState || state->app_state == ResultState) {
  170. // input
  171. if(event.input.key == InputKeyUp) {
  172. if(state->dice_index != 0) {
  173. state->dice_count += 1;
  174. if(state->dice_count > MAX_DICE_COUNT) {
  175. state->dice_count = MAX_DICE_COUNT;
  176. }
  177. }
  178. } else if(event.input.key == InputKeyDown) {
  179. state->dice_count -= 1;
  180. if(state->dice_count < 1) {
  181. state->dice_count = 1;
  182. }
  183. } else if(event.input.key == InputKeyRight) {
  184. if(state->dice_index < DICE_TYPES - 1) {
  185. state->dice_index += 1;
  186. state->app_state = SwipeLeftState;
  187. }
  188. } else if(event.input.key == InputKeyLeft) {
  189. if(state->dice_index > 0) {
  190. state->dice_index -= 1;
  191. state->app_state = SwipeRightState;
  192. if(state->dice_index == 0) state->dice_count = 1;
  193. }
  194. } else if(event.input.key == InputKeyOk) {
  195. roll(state);
  196. }
  197. }
  198. // quit from app
  199. if(event.input.key == InputKeyBack) {
  200. processing = false;
  201. }
  202. }
  203. }
  204. }
  205. view_port_update(view_port);
  206. furi_mutex_release(state->mutex);
  207. }
  208. // Clear
  209. furi_timer_free(timer);
  210. furi_message_queue_free(event_queue);
  211. view_port_enabled_set(view_port, false);
  212. gui_remove_view_port(gui, view_port);
  213. furi_record_close(RECORD_GUI);
  214. view_port_free(view_port);
  215. furi_mutex_free(state->mutex);
  216. free(state);
  217. return 0;
  218. }