#include #include #include #include #include #include #include #include #define TAG "tarot" /* generated by fbt from .png files in images folder */ #include /* ids for all scenes used by the app */ typedef enum { AppScene_MainMenu, AppScene_About, AppScene_Game, AppScene_count } AppScene; /* ids for the 2 types of view used by the app */ typedef enum { AppView_Submenu, AppView_Popup, AppView_Widget } AppView; /* the app context struct */ typedef struct { SceneManager* scene_manager; ViewDispatcher* view_dispatcher; Submenu* submenu; // Submenu for the main menu Popup* popup; // Popup for about Widget* widget; // Widget for game } App; /* all custom events */ typedef enum { AppEvent_ShowGame, AppEvent_ShowAbout } AppEvent; /* main menu scene */ /* indices for menu items */ typedef enum { AppMenuSelection_Run, AppMenuSelection_About } AppMenuSelection; /* main menu callback - sends a custom event to the scene manager based on the menu selection */ void tarot_app_menu_callback_main_menu(void* context, uint32_t index) { FURI_LOG_T(TAG, "tarot_app_menu_callback_main_menu"); App* app = context; switch(index) { case AppMenuSelection_Run: scene_manager_handle_custom_event(app->scene_manager, AppEvent_ShowGame); break; case AppMenuSelection_About: scene_manager_handle_custom_event(app->scene_manager, AppEvent_ShowAbout); break; } } /* resets the submenu, gives it content, callbacks and selection enums */ void tarot_app_scene_on_enter_main_menu(void* context) { FURI_LOG_T(TAG, "tarot_app_scene_on_enter_main_menu"); App* app = context; submenu_reset(app->submenu); submenu_add_item( app->submenu, "Run", AppMenuSelection_Run, tarot_app_menu_callback_main_menu, app); submenu_add_item( app->submenu, "About", AppMenuSelection_About, tarot_app_menu_callback_main_menu, app); view_dispatcher_switch_to_view(app->view_dispatcher, AppView_Submenu); } /* main menu event handler - switches scene based on the event */ bool tarot_app_scene_on_event_main_menu(void* context, SceneManagerEvent event) { FURI_LOG_T(TAG, "tarot_app_scene_on_event_main_menu"); App* app = context; bool consumed = false; switch(event.type) { case SceneManagerEventTypeCustom: switch(event.event) { case AppEvent_ShowGame: scene_manager_next_scene(app->scene_manager, AppScene_Game); consumed = true; break; case AppEvent_ShowAbout: scene_manager_next_scene(app->scene_manager, AppScene_About); consumed = true; break; } break; default: // eg. SceneManagerEventTypeBack, SceneManagerEventTypeTick consumed = false; break; } return consumed; } void tarot_app_scene_on_exit_main_menu(void* context) { FURI_LOG_T(TAG, "tarot_app_scene_on_exit_main_menu"); App* app = context; submenu_reset(app->submenu); } /* About scene */ void tarot_app_scene_on_enter_about(void* context) { FURI_LOG_T(TAG, "tarot_app_scene_on_enter_about"); App* app = context; popup_reset(app->popup); popup_set_context(app->popup, app); popup_set_header(app->popup, "About", 64, 1, AlignCenter, AlignTop); popup_set_icon(app->popup, 16, 64-13, &I_github_icon); popup_set_text(app->popup, "\n\nCode: pionaiki\nArt: tihyltew\n\n /pionaiki/fz-tarot", 64, 0, AlignCenter, AlignTop); view_dispatcher_switch_to_view(app->view_dispatcher, AppView_Popup); } bool tarot_app_scene_on_event_about(void* context, SceneManagerEvent event) { FURI_LOG_T(TAG, "tarot_app_scene_on_event_about"); UNUSED(context); UNUSED(event); return false; // don't handle any events } void tarot_app_scene_on_exit_about(void* context) { FURI_LOG_T(TAG, "tarot_app_scene_on_exit_about"); App* app = context; popup_reset(app->popup); } /* Game scene */ /* ###### */ const int card_x = 23; const int card_y = 32; const int card_number = 22; int card_selected = 0; struct Card { const char name[20]; const Icon* icon; }; const struct Card card[] = { {"The Fool", &I_major_0}, {"The Magician", &I_major_1}, {"The High Priestess", &I_major_2}, {"The Empress", &I_major_3}, {"The Emperor", &I_major_4}, {"The Hierophant", &I_major_5}, {"The Lovers", &I_major_6}, {"The Chariot", &I_major_7}, {"Strength", &I_major_8}, {"The Hermit", &I_major_9}, {"Wheel of Fortune", &I_major_10}, {"Justice", &I_major_11}, {"The Hanged Man", &I_major_12}, {"Death", &I_major_13}, {"Temperance", &I_major_14}, {"The Devil", &I_major_15}, {"The Tower", &I_major_16}, {"The Star", &I_major_17}, {"The Moon", &I_major_18}, {"The Sun", &I_major_19}, {"Judgement", &I_major_20}, {"The World", &I_major_21}, {"The Fool", &I_major_0_}, {"The Magician", &I_major_1_}, {"The High Priestess", &I_major_2_}, {"The Empress", &I_major_3_}, {"The Emperor", &I_major_4_}, {"The Hierophant", &I_major_5_}, {"The Lovers", &I_major_6_}, {"The Chariot", &I_major_7_}, {"Strength", &I_major_8_}, {"The Hermit", &I_major_9_}, {"Wheel of Fortune", &I_major_10_}, {"Justice", &I_major_11_}, {"The Hanged Man", &I_major_12_}, {"Death", &I_major_13_}, {"Temperance", &I_major_14_}, {"The Devil", &I_major_15_}, {"The Tower", &I_major_16_}, {"The Star", &I_major_17_}, {"The Moon", &I_major_18_}, {"The Sun", &I_major_19_}, {"Judgement", &I_major_20_}, {"The World", &I_major_21_} }; 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 x % max; } struct Spread { int card[3]; bool selected[3]; }; struct Spread spread; void draw_tarot(void* context) { App* app = context; widget_reset(app->widget); // Set the cursor to the selected card spread.selected[0] = 0; spread.selected[1] = 0; spread.selected[2] = 0; spread.selected[card_selected] = 1; // Draw cards widget_add_icon_element(app->widget, (128-card_x)/2 - 32, 10 - 2*spread.selected[0], card[spread.card[0]].icon); widget_add_icon_element(app->widget, (128-card_x)/2, 10 - 2*spread.selected[1], card[spread.card[1]].icon); widget_add_icon_element(app->widget, (128-card_x)/2 + 32, 10 - 2*spread.selected[2], card[spread.card[2]].icon); // Draw cursor widget_add_icon_element(app->widget, (128-card_x)/2 - 34 + card_x/2 + card_selected*32, 41, &I_cursor); // Draw card name widget_add_string_element(app->widget, 64, 60, AlignCenter, AlignBottom, FontPrimary, card[spread.card[card_selected]].name); } static bool widget_input_callback(InputEvent* input_event, void* context) { App* app = context; bool consumed = false; if(input_event->type == InputTypeShort) { switch(input_event->key) { case InputKeyRight: card_selected++; if (card_selected > 2) { card_selected = 0; } consumed = true; break; case InputKeyLeft: card_selected--; if (card_selected < 0) { card_selected = 2; } consumed = true; break; case InputKeyUp: // UP //consumed = true; break; case InputKeyDown: // DOWN //consumed = true; break; default: consumed = false; break; } } if(consumed) draw_tarot(app); return consumed; } void tarot_app_scene_on_enter_game(void* context) { FURI_LOG_T(TAG, "tarot_app_scene_on_enter_game"); App* app = context; // Set random spread spread.card[0] = unbiased_rand(card_number); spread.card[1] = unbiased_rand(card_number); while (spread.card[1] == spread.card[0]) { spread.card[1] = unbiased_rand(card_number); }; spread.card[2] = unbiased_rand(card_number); while (spread.card[2] == spread.card[0] || spread.card[2] == spread.card[1]) { spread.card[2] = unbiased_rand(card_number); } // Upside down card option if (0/*settings.upside_down*/) { spread.card[0] += card_number*unbiased_rand(2); spread.card[1] += card_number*unbiased_rand(2); spread.card[2] += card_number*unbiased_rand(2); } draw_tarot(app); view_set_context(widget_get_view(app->widget), app); view_set_input_callback(widget_get_view(app->widget), widget_input_callback); view_dispatcher_switch_to_view(app->view_dispatcher, AppView_Widget); } bool tarot_app_scene_on_event_game(void* context, SceneManagerEvent event) { FURI_LOG_T(TAG, "tarot_app_scene_on_event_game"); UNUSED(context); UNUSED(event); return false; // don't handle any events } void tarot_app_scene_on_exit_game(void* context) { FURI_LOG_T(TAG, "tarot_app_scene_on_exit_game"); App* app = context; widget_reset(app->widget); } /* ###### */ /* collection of all scene on_enter handlers - in the same order as their enum */ void (*const tarot_app_scene_on_enter_handlers[])(void*) = { tarot_app_scene_on_enter_main_menu, tarot_app_scene_on_enter_about, tarot_app_scene_on_enter_game}; /* collection of all scene on event handlers - in the same order as their enum */ bool (*const tarot_app_scene_on_event_handlers[])(void*, SceneManagerEvent) = { tarot_app_scene_on_event_main_menu, tarot_app_scene_on_event_about, tarot_app_scene_on_event_game}; /* collection of all scene on exit handlers - in the same order as their enum */ void (*const tarot_app_scene_on_exit_handlers[])(void*) = { tarot_app_scene_on_exit_main_menu, tarot_app_scene_on_exit_about, tarot_app_scene_on_exit_game}; /* collection of all on_enter, on_event, on_exit handlers */ const SceneManagerHandlers tarot_app_scene_event_handlers = { .on_enter_handlers = tarot_app_scene_on_enter_handlers, .on_event_handlers = tarot_app_scene_on_event_handlers, .on_exit_handlers = tarot_app_scene_on_exit_handlers, .scene_num = AppScene_count}; /* custom event handler - passes the event to the scene manager */ bool tarot_app_scene_manager_custom_event_callback(void* context, uint32_t custom_event) { FURI_LOG_T(TAG, "tarot_app_scene_manager_custom_event_callback"); furi_assert(context); App* app = context; return scene_manager_handle_custom_event(app->scene_manager, custom_event); } /* navigation event handler - passes the event to the scene manager */ bool tarot_app_scene_manager_navigation_event_callback(void* context) { FURI_LOG_T(TAG, "tarot_app_scene_manager_navigation_event_callback"); furi_assert(context); App* app = context; return scene_manager_handle_back_event(app->scene_manager); } /* initialise the scene manager with all handlers */ void tarot_app_scene_manager_init(App* app) { FURI_LOG_T(TAG, "tarot_app_scene_manager_init"); app->scene_manager = scene_manager_alloc(&tarot_app_scene_event_handlers, app); } /* initialise the views, and initialise the view dispatcher with all views */ void tarot_app_view_dispatcher_init(App* app) { FURI_LOG_T(TAG, "tarot_app_view_dispatcher_init"); app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); // allocate each view FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init allocating views"); app->submenu = submenu_alloc(); app->popup = popup_alloc(); app->widget = widget_alloc(); // assign callback that pass events from views to the scene manager FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init setting callbacks"); view_dispatcher_set_event_callback_context(app->view_dispatcher, app); view_dispatcher_set_custom_event_callback( app->view_dispatcher, tarot_app_scene_manager_custom_event_callback); view_dispatcher_set_navigation_event_callback( app->view_dispatcher, tarot_app_scene_manager_navigation_event_callback); // add views to the dispatcher, indexed by their enum value FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init adding view menu"); view_dispatcher_add_view(app->view_dispatcher, AppView_Submenu, submenu_get_view(app->submenu)); FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init adding view popup"); view_dispatcher_add_view(app->view_dispatcher, AppView_Popup, popup_get_view(app->popup)); FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init adding view widget"); view_dispatcher_add_view(app->view_dispatcher, AppView_Widget, widget_get_view(app->widget)); } /* initialise app data, scene manager, and view dispatcher */ App* tarot_app_init() { FURI_LOG_T(TAG, "tarot_app_init"); App* app = malloc(sizeof(App)); tarot_app_scene_manager_init(app); tarot_app_view_dispatcher_init(app); return app; } /* free all app data, scene manager, and view dispatcher */ void tarot_app_free(App* app) { FURI_LOG_T(TAG, "tarot_app_free"); scene_manager_free(app->scene_manager); view_dispatcher_remove_view(app->view_dispatcher, AppView_Submenu); view_dispatcher_remove_view(app->view_dispatcher, AppView_Popup); view_dispatcher_remove_view(app->view_dispatcher, AppView_Widget); view_dispatcher_free(app->view_dispatcher); submenu_free(app->submenu); popup_free(app->popup); widget_free(app->widget); free(app); } /* go to trace log level in the dev environment */ void tarot_app_set_log_level() { #ifdef FURI_DEBUG furi_log_set_level(FuriLogLevelTrace); #else furi_log_set_level(FuriLogLevelInfo); #endif } /* entrypoint */ int32_t tarot_app(void* p) { UNUSED(p); tarot_app_set_log_level(); // create the app context struct, scene manager, and view dispatcher FURI_LOG_I(TAG, "Tarot app starting..."); App* app = tarot_app_init(); // set the scene and launch the main loop Gui* gui = furi_record_open(RECORD_GUI); view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(app->scene_manager, AppScene_MainMenu); FURI_LOG_D(TAG, "Starting dispatcher..."); view_dispatcher_run(app->view_dispatcher); // free all memory FURI_LOG_I(TAG, "Tarot app finishing..."); furi_record_close(RECORD_GUI); tarot_app_free(app); return 0; }