| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- #include <furi.h>
- #include <input/input.h>
- #include <gui/gui.h>
- #include "constants.h"
- const Icon* draw_dice_frame;
- static uint16_t unbiased_rand (uint16_t max) {
- uint16_t remainder = RAND_MAX % max;
- uint16_t x;
- do {
- x = rand();
- } while (x >= RAND_MAX - remainder);
- return 1 + x % max;
- }
- static void update(State* const state) {
- if(state->app_state == SwipeLeftState) {
- for(uint8_t i = 0; i < DICE_TYPES; i++) {
- state->dices[i].x -= SWIPE_DIST;
- state->dices[i].y = DICE_Y;
- }
- if(state->dices[state->dice_index].x == DICE_X) {
- state->app_state = SelectState;
- state->dices[state->dice_index].y = DICE_Y_T;
- }
- } else if(state->app_state == SwipeRightState) {
- for(uint8_t i = 0; i < DICE_TYPES; i++) {
- state->dices[i].x += SWIPE_DIST;
- state->dices[i].y = DICE_Y;
- }
- if(state->dices[state->dice_index].x == DICE_X) {
- state->app_state = SelectState;
- state->dices[state->dice_index].y = DICE_Y_T;
- }
- } else if(state->app_state == AnimState) {
- state->anim_frame += 1;
- if(state->dice_index == 0) {
- if(state->anim_frame == 3) coin_set_start(state->roll_result); // change coin anim
- if(state->anim_frame >= MAX_COIN_FRAMES) {
- state->anim_frame = 0;
- state->app_state = AnimResultState;
- }
- } else {
- if(state->anim_frame >= MAX_DICE_FRAMES) {
- state->anim_frame = 0;
- state->app_state = AnimResultState;
- }
- }
- } else if(state->app_state == AnimResultState) {
- if(state->dice_index == 0) { // no extra animations for coin
- state->anim_frame = 0;
- state->app_state = ResultState;
- return;
- }
- state->result_pos = result_frame_pos_y[state->anim_frame];
- state->anim_frame += 1;
- // end animation
- if(state->result_pos == 0) {
- state->anim_frame = 0;
- state->app_state = ResultState;
- }
- }
- }
- static void roll(State* const state) {
- state->roll_result = 0;
- state->result_pos = result_frame_pos_y[0];
- for(uint8_t i = 0; i < MAX_DICE_COUNT; i++) {
- if(i < state->dice_count) {
- state->rolled_dices[i] = unbiased_rand(dice_types[state->dice_index].type);
- state->roll_result += state->rolled_dices[i];
- } else {
- state->rolled_dices[i] = 0;
- }
- }
- if(state->dice_index == 0) coin_set_end(state->roll_result); // change coin anim
- add_to_history(state, state->dice_index, state->dice_count, state->roll_result);
- state->app_state = AnimState;
- }
- static void draw_main_menu(const State* state, Canvas* canvas) {
- canvas_set_font(canvas, FontSecondary);
- FuriString* count = furi_string_alloc();
- furi_string_printf(count, "%01d", state->dice_count);
- // dice name
- if(isDiceNameVisible(state->app_state)) {
- canvas_draw_str_aligned(
- canvas, 63, 50, AlignCenter, AlignBottom, dice_types[state->dice_index].name);
- }
- // dice arrow buttons
- if(isDiceButtonsVisible(state->app_state)) {
- if(state->dice_index > 0) canvas_draw_icon(canvas, 44, 44, &I_ui_button_left);
- if(state->dice_index < DICE_TYPES - 1)
- canvas_draw_icon(canvas, 78, 44, &I_ui_button_right);
- }
- // dice count settings
- if(isDiceSettingsDisabled(state->app_state, state->dice_index))
- canvas_draw_icon(canvas, 48, 51, &I_ui_count_1);
- else
- canvas_draw_icon(canvas, 48, 51, &I_ui_count);
- canvas_draw_str_aligned(canvas, 58, 61, AlignCenter, AlignBottom, furi_string_get_cstr(count));
- // buttons
- if(isAnimState(state->app_state) == false) {
- canvas_draw_icon(canvas, 92, 54, &I_ui_button_roll);
- canvas_draw_icon(canvas, 0, 54, &I_ui_button_history);
- }
- if(state->app_state == AnimResultState || state->app_state == ResultState) {
- canvas_draw_icon(canvas, 0, 54, &I_ui_button_back);
- }
- furi_string_free(count);
- }
- static void draw_history(const State* state, Canvas* canvas) {
- canvas_set_font(canvas, FontSecondary);
- FuriString* hist = furi_string_alloc();
- uint8_t x = HISTORY_START_POST_X;
- uint8_t y = HISTORY_START_POST_Y;
- for(uint8_t i = 0; i < HISTORY_COL; i++) {
- // left side
- furi_string_printf(hist, "%01d.", i + 1);
- canvas_draw_str_aligned(canvas, x, y, AlignLeft, AlignBottom, furi_string_get_cstr(hist));
- if (state->history[i].index < 0) {
- furi_string_printf(hist, "--------");
- } else {
- if (state->history[i].index == 0){
- furi_string_printf(hist, state->history[i].result == 1 ? "Heads" : "Tails");
- } else {
- furi_string_printf(hist, "%01d%s: %01d", state->history[i].count, dice_types[state->history[i].index].name, state->history[i].result);
- }
- }
- canvas_draw_str_aligned(canvas, x + HISTORY_X_GAP, y, AlignLeft, AlignBottom, furi_string_get_cstr(hist));
- // right side
- uint8_t r_index = i + HISTORY_COL;
- furi_string_printf(hist, "%01d.", r_index + 1);
- canvas_draw_str_aligned(canvas, x + HISTORY_STEP_X, y, AlignLeft, AlignBottom, furi_string_get_cstr(hist));
- if (state->history[r_index].index < 0){
- furi_string_printf(hist, "--------");
- } else {
- if (state->history[r_index].index == 0){
- furi_string_printf(hist, state->history[r_index].result == 1 ? "Heads" : "Tails");
- } else {
- furi_string_printf(hist, "%01d%s: %01d", state->history[r_index].count, dice_types[state->history[r_index].index].name, state->history[r_index].result);
- }
- }
- canvas_draw_str_aligned(canvas, x + HISTORY_STEP_X + HISTORY_X_GAP, y, AlignLeft, AlignBottom, furi_string_get_cstr(hist));
- y += HISTORY_STEP_Y;
- }
- canvas_draw_icon(canvas, 0, 54, &I_ui_button_back);
- canvas_draw_icon(canvas, 75, 54, &I_ui_button_exit);
- furi_string_free(hist);
- }
- static void draw_dice(const State* state, Canvas* canvas) {
- if(isMenuState(state->app_state) == false) { // draw only selected dice
- if(state->dice_index == 0) { // coin
- draw_dice_frame = coin_frames[state->anim_frame];
- } else { // dices
- draw_dice_frame =
- dice_frames[(state->dice_index - 1) * MAX_DICE_FRAMES + state->anim_frame];
- }
- canvas_draw_icon(
- canvas,
- state->dices[state->dice_index].x,
- state->dices[state->dice_index].y,
- draw_dice_frame);
- return;
- }
- for(uint8_t i = 0; i < DICE_TYPES; i++) {
- if(state->app_state == ResultState && state->dice_index == i && state->dice_index != 0)
- continue; // draw results except coin
- if(state->dices[i].x > 128 || state->dices[i].x < -35) continue; // outside the screen
- if(i == 0) { // coin
- draw_dice_frame = coin_frames[0];
- } else { // dices
- draw_dice_frame = dice_frames[(i - 1) * MAX_DICE_FRAMES];
- }
- canvas_draw_icon(canvas, state->dices[i].x, state->dices[i].y, draw_dice_frame);
- }
- }
- static void draw_results(const State* state, Canvas* canvas) {
- canvas_set_font(canvas, FontPrimary);
- FuriString* sum = furi_string_alloc();
- furi_string_printf(sum, "%01d", state->roll_result);
- // ui frame
- if(state->app_state == AnimResultState)
- canvas_draw_icon(canvas, RESULT_BORDER_X, state->result_pos, &I_ui_result_border);
- else
- canvas_draw_icon(
- canvas, RESULT_BORDER_X, result_frame_pos_y[MAX_DICE_FRAMES - 1], &I_ui_result_border);
- // result text
- canvas_draw_str_aligned(
- canvas,
- 64,
- state->result_pos + RESULT_OFFSET,
- AlignCenter,
- AlignCenter,
- furi_string_get_cstr(sum));
- if(state->app_state == ResultState && isOneDice(state->dice_index) == false) {
- canvas_set_font(canvas, FontSecondary);
- FuriString* dices = furi_string_alloc();
- for(uint8_t i = 0; i < state->dice_count; i++) {
- furi_string_cat_printf(dices, "%01d", state->rolled_dices[i]);
- if(i != state->dice_count - 1) furi_string_cat_printf(dices, "%s", ", ");
- }
- canvas_draw_str_aligned(
- canvas, 63, 37, AlignCenter, AlignCenter, furi_string_get_cstr(dices));
- furi_string_free(dices);
- }
- furi_string_free(sum);
- }
- static void draw_callback(Canvas* canvas, void* ctx) {
- furi_assert(ctx);
- const State* state = ctx;
- furi_mutex_acquire(state->mutex, FuriWaitForever);
- if(state == NULL) {
- return;
- }
- canvas_clear(canvas);
- if (state->app_state == HistoryState) {
- draw_history(state, canvas);
- } else {
- draw_main_menu(state, canvas);
- if(isResultVisible(state->app_state, state->dice_index)) {
- draw_results(state, canvas);
- } else {
- draw_dice(state, canvas);
- }
- }
- furi_mutex_release(state->mutex);
- }
- static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
- furi_assert(event_queue);
- AppEvent event = {.type = EventTypeKey, .input = *input_event};
- furi_message_queue_put(event_queue, &event, FuriWaitForever);
- }
- static void timer_callback(FuriMessageQueue* event_queue) {
- furi_assert(event_queue);
- AppEvent event = {.type = EventTypeTick};
- furi_message_queue_put(event_queue, &event, 0);
- }
- int32_t dice_dnd_app(void* p) {
- UNUSED(p);
- FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent));
- State* state = malloc(sizeof(State));
- init(state);
- state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
- if(!state->mutex) {
- FURI_LOG_E(TAG, "cannot create mutex\r\n");
- free(state);
- return 255;
- }
- // Set callbacks
- ViewPort* view_port = view_port_alloc();
- view_port_draw_callback_set(view_port, draw_callback, state);
- view_port_input_callback_set(view_port, input_callback, event_queue);
- FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue);
- furi_timer_start(timer, furi_kernel_get_tick_frequency() * 0.2);
- // Create GUI, register view port
- Gui* gui = furi_record_open(RECORD_GUI);
- gui_add_view_port(gui, view_port, GuiLayerFullscreen);
- AppEvent event;
- for(bool processing = true; processing;) {
- FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
- furi_mutex_acquire(state->mutex, FuriWaitForever);
- if(event_status == FuriStatusOk) {
- // timer evetn
- if(event.type == EventTypeTick) {
- update(state);
- }
- // button events
- if(event.type == EventTypeKey) {
- if(event.input.type == InputTypePress) {
- // dice type
- if(isDiceButtonsVisible(state->app_state)) {
- if(event.input.key == InputKeyRight) {
- if(state->dice_index < DICE_TYPES - 1) {
- state->dice_index += 1;
- state->app_state = SwipeLeftState;
- }
- } else if(event.input.key == InputKeyLeft) {
- if(state->dice_index > 0) {
- state->dice_index -= 1;
- state->app_state = SwipeRightState;
- }
- }
- if(isOneDice(state->dice_index)) state->dice_count = 1;
- }
- // dice count
- if(isDiceSettingsDisabled(state->app_state, state->dice_index) == false &&
- isAnimState(state->app_state) == false) {
- if(event.input.key == InputKeyUp) {
- if(state->dice_index != 0) {
- state->dice_count += 1;
- if(state->dice_count > MAX_DICE_COUNT) {
- state->dice_count = 1;
- }
- }
- } else if(event.input.key == InputKeyDown) {
- state->dice_count -= 1;
- if(state->dice_count < 1) {
- state->dice_count = MAX_DICE_COUNT;
- }
- }
- }
- // roll
- if(event.input.key == InputKeyOk && isAnimState(state->app_state) == false) {
- roll(state);
- }
- }
-
- // back button handlers
- if(event.input.key == InputKeyBack){
- // switch states
- if(event.input.type == InputTypeShort) {
- if(state->app_state == SelectState){
- state->app_state = HistoryState;
- }
- else if(state->app_state == HistoryState) {
- state->app_state = SelectState;
- }
- else if(state->app_state == ResultState || state->app_state == AnimResultState) {
- state->anim_frame = 0;
- state->app_state = SelectState;
- }
- }
- // exit
- else if(event.input.type == InputTypeLong) {
- processing = false;
- }
- }
- }
- }
- view_port_update(view_port);
- furi_mutex_release(state->mutex);
- }
- // Clear
- furi_timer_free(timer);
- furi_message_queue_free(event_queue);
- view_port_enabled_set(view_port, false);
- gui_remove_view_port(gui, view_port);
- furi_record_close(RECORD_GUI);
- view_port_free(view_port);
- furi_mutex_free(state->mutex);
- free(state);
- return 0;
- }
|