tarot.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include <gui/icon_i.h>
  4. #include <gui/view_dispatcher.h>
  5. #include <gui/scene_manager.h>
  6. #include <gui/modules/submenu.h>
  7. #include <gui/modules/popup.h>
  8. #include <gui/modules/widget.h>
  9. #define TAG "tarot"
  10. /* generated by fbt from .png files in images folder */
  11. #include <tarot_icons.h>
  12. /* ids for all scenes used by the app */
  13. typedef enum {
  14. AppScene_MainMenu,
  15. AppScene_About,
  16. AppScene_Game,
  17. AppScene_count
  18. } AppScene;
  19. /* ids for the 2 types of view used by the app */
  20. typedef enum { AppView_Submenu, AppView_Popup, AppView_Widget } AppView;
  21. /* the app context struct */
  22. typedef struct {
  23. SceneManager* scene_manager;
  24. ViewDispatcher* view_dispatcher;
  25. Submenu* submenu; // Submenu for the main menu
  26. Popup* popup; // Popup for about
  27. Widget* widget; // Widget for game
  28. } App;
  29. /* all custom events */
  30. typedef enum { AppEvent_ShowGame, AppEvent_ShowAbout } AppEvent;
  31. /* main menu scene */
  32. /* indices for menu items */
  33. typedef enum { AppMenuSelection_Run, AppMenuSelection_About } AppMenuSelection;
  34. /* main menu callback - sends a custom event to the scene manager based on the menu selection */
  35. void tarot_app_menu_callback_main_menu(void* context, uint32_t index) {
  36. FURI_LOG_T(TAG, "tarot_app_menu_callback_main_menu");
  37. App* app = context;
  38. switch(index) {
  39. case AppMenuSelection_Run:
  40. scene_manager_handle_custom_event(app->scene_manager, AppEvent_ShowGame);
  41. break;
  42. case AppMenuSelection_About:
  43. scene_manager_handle_custom_event(app->scene_manager, AppEvent_ShowAbout);
  44. break;
  45. }
  46. }
  47. /* resets the submenu, gives it content, callbacks and selection enums */
  48. void tarot_app_scene_on_enter_main_menu(void* context) {
  49. FURI_LOG_T(TAG, "tarot_app_scene_on_enter_main_menu");
  50. App* app = context;
  51. submenu_reset(app->submenu);
  52. submenu_add_item(
  53. app->submenu,
  54. "Run",
  55. AppMenuSelection_Run,
  56. tarot_app_menu_callback_main_menu,
  57. app);
  58. submenu_add_item(
  59. app->submenu,
  60. "About",
  61. AppMenuSelection_About,
  62. tarot_app_menu_callback_main_menu,
  63. app);
  64. view_dispatcher_switch_to_view(app->view_dispatcher, AppView_Submenu);
  65. }
  66. /* main menu event handler - switches scene based on the event */
  67. bool tarot_app_scene_on_event_main_menu(void* context, SceneManagerEvent event) {
  68. FURI_LOG_T(TAG, "tarot_app_scene_on_event_main_menu");
  69. App* app = context;
  70. bool consumed = false;
  71. switch(event.type) {
  72. case SceneManagerEventTypeCustom:
  73. switch(event.event) {
  74. case AppEvent_ShowGame:
  75. scene_manager_next_scene(app->scene_manager, AppScene_Game);
  76. consumed = true;
  77. break;
  78. case AppEvent_ShowAbout:
  79. scene_manager_next_scene(app->scene_manager, AppScene_About);
  80. consumed = true;
  81. break;
  82. }
  83. break;
  84. default: // eg. SceneManagerEventTypeBack, SceneManagerEventTypeTick
  85. consumed = false;
  86. break;
  87. }
  88. return consumed;
  89. }
  90. void tarot_app_scene_on_exit_main_menu(void* context) {
  91. FURI_LOG_T(TAG, "tarot_app_scene_on_exit_main_menu");
  92. App* app = context;
  93. submenu_reset(app->submenu);
  94. }
  95. /* About scene */
  96. void tarot_app_scene_on_enter_about(void* context) {
  97. FURI_LOG_T(TAG, "tarot_app_scene_on_enter_about");
  98. App* app = context;
  99. popup_reset(app->popup);
  100. popup_set_context(app->popup, app);
  101. popup_set_header(app->popup, "About", 64, 1, AlignCenter, AlignTop);
  102. popup_set_icon(app->popup, 16, 64-13, &I_github_icon);
  103. popup_set_text(app->popup, "\n\nCode: pionaiki\nArt: tihyltew\n\n /pionaiki/fz-tarot", 64, 0, AlignCenter, AlignTop);
  104. view_dispatcher_switch_to_view(app->view_dispatcher, AppView_Popup);
  105. }
  106. bool tarot_app_scene_on_event_about(void* context, SceneManagerEvent event) {
  107. FURI_LOG_T(TAG, "tarot_app_scene_on_event_about");
  108. UNUSED(context);
  109. UNUSED(event);
  110. return false; // don't handle any events
  111. }
  112. void tarot_app_scene_on_exit_about(void* context) {
  113. FURI_LOG_T(TAG, "tarot_app_scene_on_exit_about");
  114. App* app = context;
  115. popup_reset(app->popup);
  116. }
  117. /* Game scene */
  118. /* ###### */
  119. const int card_x = 23;
  120. const int card_y = 32;
  121. const int card_number = 22;
  122. int card_selected = 0;
  123. struct Card {
  124. const char name[20];
  125. const Icon* icon;
  126. };
  127. const struct Card card[] = {
  128. {"The Fool", &I_major_0},
  129. {"The Magician", &I_major_1},
  130. {"The High Priestess", &I_major_2},
  131. {"The Empress", &I_major_3},
  132. {"The Emperor", &I_major_4},
  133. {"The Hierophant", &I_major_5},
  134. {"The Lovers", &I_major_6},
  135. {"The Chariot", &I_major_7},
  136. {"Strength", &I_major_8},
  137. {"The Hermit", &I_major_9},
  138. {"Wheel of Fortune", &I_major_10},
  139. {"Justice", &I_major_11},
  140. {"The Hanged Man", &I_major_12},
  141. {"Death", &I_major_13},
  142. {"Temperance", &I_major_14},
  143. {"The Devil", &I_major_15},
  144. {"The Tower", &I_major_16},
  145. {"The Star", &I_major_17},
  146. {"The Moon", &I_major_18},
  147. {"The Sun", &I_major_19},
  148. {"Judgement", &I_major_20},
  149. {"The World", &I_major_21},
  150. {"The Fool", &I_major_0_},
  151. {"The Magician", &I_major_1_},
  152. {"The High Priestess", &I_major_2_},
  153. {"The Empress", &I_major_3_},
  154. {"The Emperor", &I_major_4_},
  155. {"The Hierophant", &I_major_5_},
  156. {"The Lovers", &I_major_6_},
  157. {"The Chariot", &I_major_7_},
  158. {"Strength", &I_major_8_},
  159. {"The Hermit", &I_major_9_},
  160. {"Wheel of Fortune", &I_major_10_},
  161. {"Justice", &I_major_11_},
  162. {"The Hanged Man", &I_major_12_},
  163. {"Death", &I_major_13_},
  164. {"Temperance", &I_major_14_},
  165. {"The Devil", &I_major_15_},
  166. {"The Tower", &I_major_16_},
  167. {"The Star", &I_major_17_},
  168. {"The Moon", &I_major_18_},
  169. {"The Sun", &I_major_19_},
  170. {"Judgement", &I_major_20_},
  171. {"The World", &I_major_21_}
  172. };
  173. static uint16_t unbiased_rand (uint16_t max) {
  174. uint16_t remainder = RAND_MAX % max;
  175. uint16_t x;
  176. do {
  177. x = rand();
  178. } while (x >= RAND_MAX - remainder);
  179. return x % max;
  180. }
  181. struct Spread {
  182. int card[3];
  183. bool selected[3];
  184. };
  185. struct Spread spread;
  186. void draw_tarot(void* context) {
  187. App* app = context;
  188. widget_reset(app->widget);
  189. // Set the cursor to the selected card
  190. spread.selected[0] = 0;
  191. spread.selected[1] = 0;
  192. spread.selected[2] = 0;
  193. spread.selected[card_selected] = 1;
  194. // Draw cards
  195. widget_add_icon_element(app->widget, (128-card_x)/2 - 32, 10 - 2*spread.selected[0], card[spread.card[0]].icon);
  196. widget_add_icon_element(app->widget, (128-card_x)/2, 10 - 2*spread.selected[1], card[spread.card[1]].icon);
  197. widget_add_icon_element(app->widget, (128-card_x)/2 + 32, 10 - 2*spread.selected[2], card[spread.card[2]].icon);
  198. // Draw cursor
  199. widget_add_icon_element(app->widget, (128-card_x)/2 - 34 + card_x/2 + card_selected*32, 41, &I_cursor);
  200. // Draw card name
  201. widget_add_string_element(app->widget, 64, 60, AlignCenter, AlignBottom, FontPrimary, card[spread.card[card_selected]].name);
  202. }
  203. static bool widget_input_callback(InputEvent* input_event, void* context) {
  204. App* app = context;
  205. bool consumed = false;
  206. if(input_event->type == InputTypeShort) {
  207. switch(input_event->key) {
  208. case InputKeyRight:
  209. card_selected++;
  210. if (card_selected > 2) {
  211. card_selected = 0;
  212. }
  213. consumed = true;
  214. break;
  215. case InputKeyLeft:
  216. card_selected--;
  217. if (card_selected < 0) {
  218. card_selected = 2;
  219. }
  220. consumed = true;
  221. break;
  222. case InputKeyUp:
  223. // UP
  224. //consumed = true;
  225. break;
  226. case InputKeyDown:
  227. // DOWN
  228. //consumed = true;
  229. break;
  230. default:
  231. consumed = false;
  232. break;
  233. }
  234. }
  235. if(consumed) draw_tarot(app);
  236. return consumed;
  237. }
  238. void tarot_app_scene_on_enter_game(void* context) {
  239. FURI_LOG_T(TAG, "tarot_app_scene_on_enter_game");
  240. App* app = context;
  241. // Set random spread
  242. spread.card[0] = unbiased_rand(card_number);
  243. spread.card[1] = unbiased_rand(card_number);
  244. while (spread.card[1] == spread.card[0]) {
  245. spread.card[1] = unbiased_rand(card_number);
  246. };
  247. spread.card[2] = unbiased_rand(card_number);
  248. while (spread.card[2] == spread.card[0] || spread.card[2] == spread.card[1]) {
  249. spread.card[2] = unbiased_rand(card_number);
  250. }
  251. // Upside down card option
  252. if (0/*settings.upside_down*/) {
  253. spread.card[0] += card_number*unbiased_rand(2);
  254. spread.card[1] += card_number*unbiased_rand(2);
  255. spread.card[2] += card_number*unbiased_rand(2);
  256. }
  257. draw_tarot(app);
  258. view_set_context(widget_get_view(app->widget), app);
  259. view_set_input_callback(widget_get_view(app->widget), widget_input_callback);
  260. view_dispatcher_switch_to_view(app->view_dispatcher, AppView_Widget);
  261. }
  262. bool tarot_app_scene_on_event_game(void* context, SceneManagerEvent event) {
  263. FURI_LOG_T(TAG, "tarot_app_scene_on_event_game");
  264. UNUSED(context);
  265. UNUSED(event);
  266. return false; // don't handle any events
  267. }
  268. void tarot_app_scene_on_exit_game(void* context) {
  269. FURI_LOG_T(TAG, "tarot_app_scene_on_exit_game");
  270. App* app = context;
  271. widget_reset(app->widget);
  272. }
  273. /* ###### */
  274. /* collection of all scene on_enter handlers - in the same order as their enum */
  275. void (*const tarot_app_scene_on_enter_handlers[])(void*) = {
  276. tarot_app_scene_on_enter_main_menu,
  277. tarot_app_scene_on_enter_about,
  278. tarot_app_scene_on_enter_game};
  279. /* collection of all scene on event handlers - in the same order as their enum */
  280. bool (*const tarot_app_scene_on_event_handlers[])(void*, SceneManagerEvent) = {
  281. tarot_app_scene_on_event_main_menu,
  282. tarot_app_scene_on_event_about,
  283. tarot_app_scene_on_event_game};
  284. /* collection of all scene on exit handlers - in the same order as their enum */
  285. void (*const tarot_app_scene_on_exit_handlers[])(void*) = {
  286. tarot_app_scene_on_exit_main_menu,
  287. tarot_app_scene_on_exit_about,
  288. tarot_app_scene_on_exit_game};
  289. /* collection of all on_enter, on_event, on_exit handlers */
  290. const SceneManagerHandlers tarot_app_scene_event_handlers = {
  291. .on_enter_handlers = tarot_app_scene_on_enter_handlers,
  292. .on_event_handlers = tarot_app_scene_on_event_handlers,
  293. .on_exit_handlers = tarot_app_scene_on_exit_handlers,
  294. .scene_num = AppScene_count};
  295. /* custom event handler - passes the event to the scene manager */
  296. bool tarot_app_scene_manager_custom_event_callback(void* context, uint32_t custom_event) {
  297. FURI_LOG_T(TAG, "tarot_app_scene_manager_custom_event_callback");
  298. furi_assert(context);
  299. App* app = context;
  300. return scene_manager_handle_custom_event(app->scene_manager, custom_event);
  301. }
  302. /* navigation event handler - passes the event to the scene manager */
  303. bool tarot_app_scene_manager_navigation_event_callback(void* context) {
  304. FURI_LOG_T(TAG, "tarot_app_scene_manager_navigation_event_callback");
  305. furi_assert(context);
  306. App* app = context;
  307. return scene_manager_handle_back_event(app->scene_manager);
  308. }
  309. /* initialise the scene manager with all handlers */
  310. void tarot_app_scene_manager_init(App* app) {
  311. FURI_LOG_T(TAG, "tarot_app_scene_manager_init");
  312. app->scene_manager = scene_manager_alloc(&tarot_app_scene_event_handlers, app);
  313. }
  314. /* initialise the views, and initialise the view dispatcher with all views */
  315. void tarot_app_view_dispatcher_init(App* app) {
  316. FURI_LOG_T(TAG, "tarot_app_view_dispatcher_init");
  317. app->view_dispatcher = view_dispatcher_alloc();
  318. view_dispatcher_enable_queue(app->view_dispatcher);
  319. // allocate each view
  320. FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init allocating views");
  321. app->submenu = submenu_alloc();
  322. app->popup = popup_alloc();
  323. app->widget = widget_alloc();
  324. // assign callback that pass events from views to the scene manager
  325. FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init setting callbacks");
  326. view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
  327. view_dispatcher_set_custom_event_callback(
  328. app->view_dispatcher, tarot_app_scene_manager_custom_event_callback);
  329. view_dispatcher_set_navigation_event_callback(
  330. app->view_dispatcher, tarot_app_scene_manager_navigation_event_callback);
  331. // add views to the dispatcher, indexed by their enum value
  332. FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init adding view menu");
  333. view_dispatcher_add_view(app->view_dispatcher, AppView_Submenu, submenu_get_view(app->submenu));
  334. FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init adding view popup");
  335. view_dispatcher_add_view(app->view_dispatcher, AppView_Popup, popup_get_view(app->popup));
  336. FURI_LOG_D(TAG, "tarot_app_view_dispatcher_init adding view widget");
  337. view_dispatcher_add_view(app->view_dispatcher, AppView_Widget, widget_get_view(app->widget));
  338. }
  339. /* initialise app data, scene manager, and view dispatcher */
  340. App* tarot_app_init() {
  341. FURI_LOG_T(TAG, "tarot_app_init");
  342. App* app = malloc(sizeof(App));
  343. tarot_app_scene_manager_init(app);
  344. tarot_app_view_dispatcher_init(app);
  345. return app;
  346. }
  347. /* free all app data, scene manager, and view dispatcher */
  348. void tarot_app_free(App* app) {
  349. FURI_LOG_T(TAG, "tarot_app_free");
  350. scene_manager_free(app->scene_manager);
  351. view_dispatcher_remove_view(app->view_dispatcher, AppView_Submenu);
  352. view_dispatcher_remove_view(app->view_dispatcher, AppView_Popup);
  353. view_dispatcher_remove_view(app->view_dispatcher, AppView_Widget);
  354. view_dispatcher_free(app->view_dispatcher);
  355. submenu_free(app->submenu);
  356. popup_free(app->popup);
  357. widget_free(app->widget);
  358. free(app);
  359. }
  360. /* go to trace log level in the dev environment */
  361. void tarot_app_set_log_level() {
  362. #ifdef FURI_DEBUG
  363. furi_log_set_level(FuriLogLevelTrace);
  364. #else
  365. furi_log_set_level(FuriLogLevelInfo);
  366. #endif
  367. }
  368. /* entrypoint */
  369. int32_t tarot_app(void* p) {
  370. UNUSED(p);
  371. tarot_app_set_log_level();
  372. // create the app context struct, scene manager, and view dispatcher
  373. FURI_LOG_I(TAG, "Tarot app starting...");
  374. App* app = tarot_app_init();
  375. // set the scene and launch the main loop
  376. Gui* gui = furi_record_open(RECORD_GUI);
  377. view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
  378. scene_manager_next_scene(app->scene_manager, AppScene_MainMenu);
  379. FURI_LOG_D(TAG, "Starting dispatcher...");
  380. view_dispatcher_run(app->view_dispatcher);
  381. // free all memory
  382. FURI_LOG_I(TAG, "Tarot app finishing...");
  383. furi_record_close(RECORD_GUI);
  384. tarot_app_free(app);
  385. return 0;
  386. }