diff --git a/README.md b/README.md new file mode 100644 index 00000000000..b5693a0535e --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Flipper Zero Color Guessing Game +
+ + +
+ +## What this is? +As a web developer I enjoy guessing colours by HEX Code. This game is targeted at other Devs and graphic designers
+that also enjoy this. +

+ +### Mode 1 +The LED will display a color and you must try and guess it by adjusting the HEX values on the screen. A timer will show
+how fast you were. Three levels of difficulty are available. Vibro hints given to help you find the solution. + +### Mode 2 +You can define a color using the HEX code on-screen and the LED will display this color + + +## How to install on Flipper Zero +- If you do not have one, download a firmware
+- Plug your Flipper Zero in via USB.
+- Copy the contents of this folder into the applications_user folder of your firmware.
+ +Then run the command: + ``` +.\fbt launch_app APPSRC=applications_user/color_guess + ``` +The application will be compiled and copied onto your device. + +## Licensing +This code is open-source and may be used for whatever you want to do with it. \ No newline at end of file diff --git a/application.fam b/application.fam new file mode 100644 index 00000000000..f24d51ce275 --- /dev/null +++ b/application.fam @@ -0,0 +1,19 @@ +App( + appid="color_guess", + name="Color Guess", + apptype=FlipperAppType.EXTERNAL, + entry_point="color_guess_app", + cdefines=["APP_COLOR_GUESS"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=10, + fap_icon="color_guess_10px.png", + fap_icon_assets="icons", + fap_version="1.1", + fap_category="Games", + fap_author="Leedave", + fap_description="Color Guessing Game", + fap_weburl="https://github.com/leedave/Leeds-Flipper-Zero-Applications", +) \ No newline at end of file diff --git a/assets/flipper_logo_orange.png b/assets/flipper_logo_orange.png new file mode 100644 index 00000000000..82148e819d0 Binary files /dev/null and b/assets/flipper_logo_orange.png differ diff --git a/assets/preview.jpg b/assets/preview.jpg new file mode 100644 index 00000000000..3bef5d4bd38 Binary files /dev/null and b/assets/preview.jpg differ diff --git a/changelog.md b/changelog.md new file mode 100644 index 00000000000..c4d4c1b0ad2 --- /dev/null +++ b/changelog.md @@ -0,0 +1,7 @@ +## v1.1 + +Added GFX to start screen + +## v1.0 + +First release to Application Catalog \ No newline at end of file diff --git a/color_guess.c b/color_guess.c new file mode 100644 index 00000000000..90c387c36b2 --- /dev/null +++ b/color_guess.c @@ -0,0 +1,137 @@ +#include "color_guess.h" +#include "helpers/digits.h" + +bool color_guess_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + ColorGuess* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +void color_guess_tick_event_callback(void* context) { + furi_assert(context); + ColorGuess* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +//leave app if back button pressed +bool color_guess_navigation_event_callback(void* context) { + furi_assert(context); + ColorGuess* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +ColorGuess* color_guess_app_alloc() { + ColorGuess* app = malloc(sizeof(ColorGuess)); + app->gui = furi_record_open(RECORD_GUI); + app->notification = furi_record_open(RECORD_NOTIFICATION); + app->error = false; + + // Set Defaults if no config exists + app->haptic = 1; + app->led = 1; + app->save_settings = 1; + + // Load configs + color_guess_read_settings(app); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_display_backlight_on); + + //Scene additions + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&color_guess_scene_handlers, app); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, color_guess_navigation_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, color_guess_tick_event_callback, 100); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, color_guess_custom_event_callback); + app->submenu = submenu_alloc(); + + view_dispatcher_add_view( + app->view_dispatcher, ColorGuessViewIdMenu, submenu_get_view(app->submenu)); + app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + ColorGuessViewIdSettings, + variable_item_list_get_view(app->variable_item_list)); + app->color_guess_startscreen = color_guess_startscreen_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + ColorGuessViewIdStartscreen, + color_guess_startscreen_get_view(app->color_guess_startscreen)); + app->color_guess_color_set = color_guess_color_set_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + ColorGuessViewIdColorSet, + color_guess_color_set_get_view(app->color_guess_color_set)); + app->color_guess_play = color_guess_play_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + ColorGuessViewIdPlay, + color_guess_play_get_view(app->color_guess_play)); + + //End Scene Additions + + return app; +} + +void color_guess_app_free(ColorGuess* app) { + furi_assert(app); + + // Scene manager + scene_manager_free(app->scene_manager); + + // View Dispatcher + view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdMenu); + view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdStartscreen); + view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdColorSet); + view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdPlay); + view_dispatcher_remove_view(app->view_dispatcher, ColorGuessViewIdSettings); + submenu_free(app->submenu); + + view_dispatcher_free(app->view_dispatcher); + + // GUI + furi_record_close(RECORD_GUI); + + app->view_port = NULL; + app->gui = NULL; + app->notification = NULL; + + //Remove whatever is left + free(app); +} + +int32_t color_guess_app(void* p) { + UNUSED(p); + ColorGuess* app = color_guess_app_alloc(); + if(app->error) { + return 255; + } + + /* //This exits if run in RM FW + if(!furi_hal_region_is_provisioned()) { + color_guess_app_free(app); + return 1; + }*/ + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + scene_manager_next_scene(app->scene_manager, ColorGuessSceneStartscreen); + + furi_hal_power_suppress_charge_enter(); + + view_dispatcher_run(app->view_dispatcher); + + color_guess_save_settings(app); + + furi_hal_power_suppress_charge_exit(); + + color_guess_app_free(app); + + return 0; +} diff --git a/color_guess.h b/color_guess.h new file mode 100644 index 00000000000..a6c1dac4580 --- /dev/null +++ b/color_guess.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "helpers/color_guess_custom_event.h" +#include "scenes/color_guess_scene.h" +#include "views/color_guess_color_set.h" +#include "views/color_guess_play.h" +#include "views/color_guess_startscreen.h" +#include "helpers/color_guess_storage.h" + +#define TAG "Color_Guess" + +typedef struct { + Gui* gui; + NotificationApp* notification; + ViewPort* view_port; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + VariableItemList* variable_item_list; + SceneManager* scene_manager; + ColorGuessColorSet* color_guess_color_set; + ColorGuessPlay* color_guess_play; + ColorGuessStartscreen* color_guess_startscreen; + Submenu* color_guess_settings; + bool error; + uint32_t haptic; + //uint32_t speaker; + uint32_t led; + uint32_t save_settings; +} ColorGuess; + +typedef enum { + ColorGuessViewIdStartscreen, + ColorGuessViewIdMenu, + ColorGuessViewIdPlay, + ColorGuessViewIdColorSet, + ColorGuessViewIdSettings, +} ColorGuessViewId; + +typedef enum { + ColorGuessHapticOff, + ColorGuessHapticOn, +} ColorGuessHapticState; + +typedef enum { + ColorGuessSpeakerOff, + ColorGuessSpeakerOn, +} ColorGuessSpeakerState; + +typedef enum { + ColorGuessLedOff, + ColorGuessLedOn, +} ColorGuessLedState; \ No newline at end of file diff --git a/color_guess_10px.png b/color_guess_10px.png new file mode 100644 index 00000000000..207c2921ee5 Binary files /dev/null and b/color_guess_10px.png differ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000000..e87feb0fba3 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,20 @@ +## Color Guessing Game + +Targeted at Web Developers, Graphical Designers and other nerds. +This app will let your devices LED glow in a random color. Your job is to guess the Hex Code of the color. + +## Features +- 3 Difficulties +- Optional haptic feedback helps you guess +- Percentage calculation to show how close you are +- Practice mode that lets you define colors yourself + +## How HEX color codes work + +Example #FF44CC +- Each digit is a number from 0 - F +- One digit represents 0 - 15, two digits represent 0 - 255 +- Each colors intensity is defined by two digits (black to full color) +- The first color is red (example: FF) +- The second color is green (example: 44) +- The third color is blue (example: CC) \ No newline at end of file diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 00000000000..5cc5cd98c06 --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1,3 @@ +## v1.0 + +First release to Application Catalog \ No newline at end of file diff --git a/helpers/color_guess_colors.h b/helpers/color_guess_colors.h new file mode 100644 index 00000000000..7d852db01e9 --- /dev/null +++ b/helpers/color_guess_colors.h @@ -0,0 +1,21 @@ +#pragma once + +int colorsEasy[] = { + 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff, 0xffffff, 0x500000, + 0x005000, 0x000050, 0x505000, 0x500050, 0x005050, 0x505050, 0x0000c0, 0x010101, + 0xcccccc, 0xcc00cc, 0x00cccc, 0xcccc00, 0xcc0000, 0x00cc00, 0x0000cc, 0x000000, +}; + +int colorsNormal[] = { + 0xa04a00, 0x308030, 0xc03030, 0x00e090, 0x0000ad, 0xaf00af, 0xbb3030, 0xcccc00, + 0xcc8000, 0x0080f0, 0x009020, 0x902050, 0xbc00ff, 0xff6a00, 0xc5c5c5, 0xafafc0, + 0xcece00, 0xcf6500, 0x2b2b00, 0x55ee11, 0xff33ff, 0x2266ff, 0x530053, 0x3399ff, + 0xff0033, 0x99ff22, 0xab00ab, 0x55ff55, 0x9999ff, 0xe500e5, +}; + +int colorsHard[] = { + 0x94275d, 0xb4f73e, 0xc833fd, 0x813f00, 0xb77b51, 0xe2b739, 0x378b3a, 0x373e8b, 0x8b3785, + 0x8b4137, 0xffbdb5, 0x3a3aa7, 0x37a6bd, 0xbd4737, 0x621308, 0x086238, 0x2d4137, 0x711761, + 0xdc26bc, 0xdc266e, 0x26dc81, 0x8d4500, 0xb8c22b, 0x2bc2a0, 0x9064c1, 0x732bc2, 0x5610a3, + 0xa31034, 0xe50c41, 0x6d001a, 0x159bbc, 0x32bc15, 0x53e60c, +}; \ No newline at end of file diff --git a/helpers/color_guess_custom_event.h b/helpers/color_guess_custom_event.h new file mode 100644 index 00000000000..90e75947996 --- /dev/null +++ b/helpers/color_guess_custom_event.h @@ -0,0 +1,22 @@ +#pragma once + +typedef enum { + ColorGuessCustomEventStartscreenUp, + ColorGuessCustomEventStartscreenDown, + ColorGuessCustomEventStartscreenLeft, + ColorGuessCustomEventStartscreenRight, + ColorGuessCustomEventStartscreenOk, + ColorGuessCustomEventStartscreenBack, + ColorGuessCustomEventColorSetUp, + ColorGuessCustomEventColorSetDown, + ColorGuessCustomEventColorSetLeft, + ColorGuessCustomEventColorSetRight, + ColorGuessCustomEventColorSetOk, + ColorGuessCustomEventColorSetBack, + ColorGuessCustomEventPlayUp, + ColorGuessCustomEventPlayDown, + ColorGuessCustomEventPlayLeft, + ColorGuessCustomEventPlayRight, + ColorGuessCustomEventPlayOk, + ColorGuessCustomEventPlayBack, +} ColorGuessCustomEvent; \ No newline at end of file diff --git a/helpers/color_guess_haptic.c b/helpers/color_guess_haptic.c new file mode 100644 index 00000000000..231f8afed90 --- /dev/null +++ b/helpers/color_guess_haptic.c @@ -0,0 +1,35 @@ +#include "color_guess_haptic.h" +#include "../color_guess.h" + +void color_guess_play_happy_bump(void* context) { + ColorGuess* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 20); + notification_message(app->notification, &sequence_reset_vibro); +} + +void color_guess_play_bad_bump(void* context) { + ColorGuess* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + notification_message(app->notification, &sequence_reset_vibro); +} + +void color_guess_play_long_bump(void* context) { + ColorGuess* app = context; + if(app->haptic != 1) { + return; + } + for(int i = 0; i < 4; i++) { + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 50); + notification_message(app->notification, &sequence_reset_vibro); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + } +} diff --git a/helpers/color_guess_haptic.h b/helpers/color_guess_haptic.h new file mode 100644 index 00000000000..15862034559 --- /dev/null +++ b/helpers/color_guess_haptic.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +void color_guess_play_happy_bump(void* context); + +void color_guess_play_bad_bump(void* context); + +void color_guess_play_long_bump(void* context); diff --git a/helpers/color_guess_led.c b/helpers/color_guess_led.c new file mode 100644 index 00000000000..46d58e0a24d --- /dev/null +++ b/helpers/color_guess_led.c @@ -0,0 +1,39 @@ +#include "color_guess_led.h" +#include "../color_guess.h" + +void color_guess_led_set_rgb(void* context, int red, int green, int blue) { + ColorGuess* app = context; + if(app->led != 1) { + return; + } + NotificationMessage notification_led_message_1; + notification_led_message_1.type = NotificationMessageTypeLedRed; + NotificationMessage notification_led_message_2; + notification_led_message_2.type = NotificationMessageTypeLedGreen; + NotificationMessage notification_led_message_3; + notification_led_message_3.type = NotificationMessageTypeLedBlue; + + notification_led_message_1.data.led.value = red; + notification_led_message_2.data.led.value = green; + notification_led_message_3.data.led.value = blue; + const NotificationSequence notification_sequence = { + ¬ification_led_message_1, + ¬ification_led_message_2, + ¬ification_led_message_3, + &message_do_not_reset, + NULL, + }; + notification_message(app->notification, ¬ification_sequence); + furi_thread_flags_wait( + 0, FuriFlagWaitAny, 10); //Delay, prevent removal from RAM before LED value set +} + +void color_guess_led_reset(void* context) { + ColorGuess* app = context; + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + + furi_thread_flags_wait( + 0, FuriFlagWaitAny, 300); //Delay, prevent removal from RAM before LED value set +} diff --git a/helpers/color_guess_led.h b/helpers/color_guess_led.h new file mode 100644 index 00000000000..513a67e39f4 --- /dev/null +++ b/helpers/color_guess_led.h @@ -0,0 +1,5 @@ +#pragma once + +void color_guess_led_set_rgb(void* context, int red, int green, int blue); + +void color_guess_led_reset(void* context); diff --git a/helpers/color_guess_storage.c b/helpers/color_guess_storage.c new file mode 100644 index 00000000000..71432599212 --- /dev/null +++ b/helpers/color_guess_storage.c @@ -0,0 +1,114 @@ +#include "color_guess_storage.h" + +static Storage* color_guess_open_storage() { + return furi_record_open(RECORD_STORAGE); +} + +static void color_guess_close_storage() { + furi_record_close(RECORD_STORAGE); +} + +static void color_guess_close_config_file(FlipperFormat* file) { + if(file == NULL) return; + flipper_format_file_close(file); + flipper_format_free(file); +} + +void color_guess_save_settings(void* context) { + ColorGuess* app = context; + if(app->save_settings == 0) { + return; + } + + FURI_LOG_D(TAG, "Saving Settings"); + Storage* storage = color_guess_open_storage(); + FlipperFormat* fff_file = flipper_format_file_alloc(storage); + + // Overwrite wont work, so delete first + if(storage_file_exists(storage, COLOR_GUESS_SETTINGS_SAVE_PATH)) { + storage_simply_remove(storage, COLOR_GUESS_SETTINGS_SAVE_PATH); + } + + // Open File, create if not exists + if(!storage_common_stat(storage, COLOR_GUESS_SETTINGS_SAVE_PATH, NULL) == FSE_OK) { + FURI_LOG_D( + TAG, "Config file %s is not found. Will create new.", COLOR_GUESS_SETTINGS_SAVE_PATH); + if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) { + FURI_LOG_D( + TAG, "Directory %s doesn't exist. Will create new.", CONFIG_FILE_DIRECTORY_PATH); + if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { + FURI_LOG_E(TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); + } + } + } + + if(!flipper_format_file_open_new(fff_file, COLOR_GUESS_SETTINGS_SAVE_PATH)) { + //totp_close_config_file(fff_file); + FURI_LOG_E(TAG, "Error creating new file %s", COLOR_GUESS_SETTINGS_SAVE_PATH); + color_guess_close_storage(); + return; + } + + // Store Settings + flipper_format_write_header_cstr( + fff_file, COLOR_GUESS_SETTINGS_HEADER, COLOR_GUESS_SETTINGS_FILE_VERSION); + flipper_format_write_uint32(fff_file, COLOR_GUESS_SETTINGS_KEY_HAPTIC, &app->haptic, 1); + flipper_format_write_uint32(fff_file, COLOR_GUESS_SETTINGS_KEY_LED, &app->led, 1); + flipper_format_write_uint32( + fff_file, COLOR_GUESS_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1); + + if(!flipper_format_rewind(fff_file)) { + color_guess_close_config_file(fff_file); + FURI_LOG_E(TAG, "Rewind error"); + color_guess_close_storage(); + return; + } + + color_guess_close_config_file(fff_file); + color_guess_close_storage(); +} + +void color_guess_read_settings(void* context) { + ColorGuess* app = context; + Storage* storage = color_guess_open_storage(); + FlipperFormat* fff_file = flipper_format_file_alloc(storage); + + if(storage_common_stat(storage, COLOR_GUESS_SETTINGS_SAVE_PATH, NULL) != FSE_OK) { + color_guess_close_config_file(fff_file); + color_guess_close_storage(); + return; + } + uint32_t file_version; + FuriString* temp_str = furi_string_alloc(); + + if(!flipper_format_file_open_existing(fff_file, COLOR_GUESS_SETTINGS_SAVE_PATH)) { + FURI_LOG_E(TAG, "Cannot open file %s", COLOR_GUESS_SETTINGS_SAVE_PATH); + color_guess_close_config_file(fff_file); + color_guess_close_storage(); + return; + } + + if(!flipper_format_read_header(fff_file, temp_str, &file_version)) { + FURI_LOG_E(TAG, "Missing Header Data"); + color_guess_close_config_file(fff_file); + color_guess_close_storage(); + return; + } + + if(file_version < COLOR_GUESS_SETTINGS_FILE_VERSION) { + FURI_LOG_I(TAG, "old config version, will be removed."); + color_guess_close_config_file(fff_file); + color_guess_close_storage(); + return; + } + + flipper_format_read_uint32(fff_file, COLOR_GUESS_SETTINGS_KEY_HAPTIC, &app->haptic, 1); + flipper_format_read_uint32(fff_file, COLOR_GUESS_SETTINGS_KEY_LED, &app->led, 1); + flipper_format_read_uint32( + fff_file, COLOR_GUESS_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1); + + flipper_format_rewind(fff_file); + + color_guess_close_config_file(fff_file); + color_guess_close_storage(); +} \ No newline at end of file diff --git a/helpers/color_guess_storage.h b/helpers/color_guess_storage.h new file mode 100644 index 00000000000..305d13db184 --- /dev/null +++ b/helpers/color_guess_storage.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include +#include "../color_guess.h" + +#define COLOR_GUESS_SETTINGS_FILE_VERSION 1 +#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/color_guess") +#define COLOR_GUESS_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/color_guess.conf" +#define COLOR_GUESS_SETTINGS_SAVE_PATH_TMP COLOR_GUESS_SETTINGS_SAVE_PATH ".tmp" +#define COLOR_GUESS_SETTINGS_HEADER "Color Guess Config File" +#define COLOR_GUESS_SETTINGS_KEY_HAPTIC "Haptic" +#define COLOR_GUESS_SETTINGS_KEY_LED "Led" +#define COLOR_GUESS_SETTINGS_KEY_SPEAKER "Speaker" +#define COLOR_GUESS_SETTINGS_KEY_SAVE_SETTINGS "SaveSettings" + +void color_guess_save_settings(void* context); +void color_guess_read_settings(void* context); \ No newline at end of file diff --git a/helpers/digits.h b/helpers/digits.h new file mode 100644 index 00000000000..b4144d11c6f --- /dev/null +++ b/helpers/digits.h @@ -0,0 +1,23 @@ +//#pragma once + +#include +#include "color_guess_icons.h" + +const Icon* digits[17] = { + &I_digit_0_10x14, + &I_digit_1_10x14, + &I_digit_2_10x14, + &I_digit_3_10x14, + &I_digit_4_10x14, + &I_digit_5_10x14, + &I_digit_6_10x14, + &I_digit_7_10x14, + &I_digit_8_10x14, + &I_digit_9_10x14, + &I_digit_a_10x14, + &I_digit_b_10x14, + &I_digit_c_10x14, + &I_digit_d_10x14, + &I_digit_e_10x14, + &I_digit_f_10x14, + &I_digit_x_10x14}; diff --git a/icons/ButtonCenter_7x7.png b/icons/ButtonCenter_7x7.png new file mode 100644 index 00000000000..a66461b227b Binary files /dev/null and b/icons/ButtonCenter_7x7.png differ diff --git a/icons/ButtonDown_10x5.png b/icons/ButtonDown_10x5.png new file mode 100644 index 00000000000..b492b926c45 Binary files /dev/null and b/icons/ButtonDown_10x5.png differ diff --git a/icons/ButtonUp_10x5.png b/icons/ButtonUp_10x5.png new file mode 100644 index 00000000000..5da99d01ee9 Binary files /dev/null and b/icons/ButtonUp_10x5.png differ diff --git a/icons/digit_0_10x14.png b/icons/digit_0_10x14.png new file mode 100644 index 00000000000..a874b194034 Binary files /dev/null and b/icons/digit_0_10x14.png differ diff --git a/icons/digit_1_10x14.png b/icons/digit_1_10x14.png new file mode 100644 index 00000000000..2f18aaa9bd2 Binary files /dev/null and b/icons/digit_1_10x14.png differ diff --git a/icons/digit_2_10x14.png b/icons/digit_2_10x14.png new file mode 100644 index 00000000000..05e72d69305 Binary files /dev/null and b/icons/digit_2_10x14.png differ diff --git a/icons/digit_3_10x14.png b/icons/digit_3_10x14.png new file mode 100644 index 00000000000..f791ae25bea Binary files /dev/null and b/icons/digit_3_10x14.png differ diff --git a/icons/digit_4_10x14.png b/icons/digit_4_10x14.png new file mode 100644 index 00000000000..ab98aa77f25 Binary files /dev/null and b/icons/digit_4_10x14.png differ diff --git a/icons/digit_5_10x14.png b/icons/digit_5_10x14.png new file mode 100644 index 00000000000..4305f46dd2a Binary files /dev/null and b/icons/digit_5_10x14.png differ diff --git a/icons/digit_6_10x14.png b/icons/digit_6_10x14.png new file mode 100644 index 00000000000..b170f29a77d Binary files /dev/null and b/icons/digit_6_10x14.png differ diff --git a/icons/digit_7_10x14.png b/icons/digit_7_10x14.png new file mode 100644 index 00000000000..9f38be8376e Binary files /dev/null and b/icons/digit_7_10x14.png differ diff --git a/icons/digit_8_10x14.png b/icons/digit_8_10x14.png new file mode 100644 index 00000000000..464f9229350 Binary files /dev/null and b/icons/digit_8_10x14.png differ diff --git a/icons/digit_9_10x14.png b/icons/digit_9_10x14.png new file mode 100644 index 00000000000..13f1563089e Binary files /dev/null and b/icons/digit_9_10x14.png differ diff --git a/icons/digit_a_10x14.png b/icons/digit_a_10x14.png new file mode 100644 index 00000000000..5359cc453e7 Binary files /dev/null and b/icons/digit_a_10x14.png differ diff --git a/icons/digit_b_10x14.png b/icons/digit_b_10x14.png new file mode 100644 index 00000000000..502da3d4e36 Binary files /dev/null and b/icons/digit_b_10x14.png differ diff --git a/icons/digit_c_10x14.png b/icons/digit_c_10x14.png new file mode 100644 index 00000000000..a5c35eff9a3 Binary files /dev/null and b/icons/digit_c_10x14.png differ diff --git a/icons/digit_d_10x14.png b/icons/digit_d_10x14.png new file mode 100644 index 00000000000..e149214b968 Binary files /dev/null and b/icons/digit_d_10x14.png differ diff --git a/icons/digit_e_10x14.png b/icons/digit_e_10x14.png new file mode 100644 index 00000000000..88e31f2f400 Binary files /dev/null and b/icons/digit_e_10x14.png differ diff --git a/icons/digit_f_10x14.png b/icons/digit_f_10x14.png new file mode 100644 index 00000000000..a5088380388 Binary files /dev/null and b/icons/digit_f_10x14.png differ diff --git a/icons/digit_x_10x14.png b/icons/digit_x_10x14.png new file mode 100644 index 00000000000..8d45ea022ac Binary files /dev/null and b/icons/digit_x_10x14.png differ diff --git a/icons/start_dolph_49x55.png b/icons/start_dolph_49x55.png new file mode 100644 index 00000000000..89a29a33384 Binary files /dev/null and b/icons/start_dolph_49x55.png differ diff --git a/scenes/color_guess_scene.c b/scenes/color_guess_scene.c new file mode 100644 index 00000000000..14745a10e81 --- /dev/null +++ b/scenes/color_guess_scene.c @@ -0,0 +1,30 @@ +#include "color_guess_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const color_guess_on_enter_handlers[])(void*) = { +#include "color_guess_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const color_guess_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "color_guess_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const color_guess_on_exit_handlers[])(void* context) = { +#include "color_guess_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers color_guess_scene_handlers = { + .on_enter_handlers = color_guess_on_enter_handlers, + .on_event_handlers = color_guess_on_event_handlers, + .on_exit_handlers = color_guess_on_exit_handlers, + .scene_num = ColorGuessSceneNum, +}; diff --git a/scenes/color_guess_scene.h b/scenes/color_guess_scene.h new file mode 100644 index 00000000000..aaa07b1a24b --- /dev/null +++ b/scenes/color_guess_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) ColorGuessScene##id, +typedef enum { +#include "color_guess_scene_config.h" + ColorGuessSceneNum, +} ColorGuessScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers color_guess_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "color_guess_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "color_guess_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "color_guess_scene_config.h" +#undef ADD_SCENE diff --git a/scenes/color_guess_scene_color_set.c b/scenes/color_guess_scene_color_set.c new file mode 100644 index 00000000000..3c478c30a56 --- /dev/null +++ b/scenes/color_guess_scene_color_set.c @@ -0,0 +1,51 @@ +#include "../color_guess.h" +#include "../helpers/color_guess_custom_event.h" +#include "../views/color_guess_color_set.h" + +void color_guess_color_set_callback(ColorGuessCustomEvent event, void* context) { + furi_assert(context); + ColorGuess* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void color_guess_scene_color_set_on_enter(void* context) { + furi_assert(context); + ColorGuess* app = context; + color_guess_color_set_set_callback( + app->color_guess_color_set, color_guess_color_set_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, ColorGuessViewIdColorSet); +} + +bool color_guess_scene_color_set_on_event(void* context, SceneManagerEvent event) { + ColorGuess* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case ColorGuessCustomEventColorSetLeft: + case ColorGuessCustomEventColorSetRight: + break; + case ColorGuessCustomEventColorSetUp: + case ColorGuessCustomEventColorSetDown: + break; + case ColorGuessCustomEventColorSetBack: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, ColorGuessSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void color_guess_scene_color_set_on_exit(void* context) { + ColorGuess* app = context; + UNUSED(app); +} diff --git a/scenes/color_guess_scene_config.h b/scenes/color_guess_scene_config.h new file mode 100644 index 00000000000..5efe884134d --- /dev/null +++ b/scenes/color_guess_scene_config.h @@ -0,0 +1,5 @@ +ADD_SCENE(color_guess, startscreen, Startscreen) +ADD_SCENE(color_guess, menu, Menu) +ADD_SCENE(color_guess, color_set, ColorSet) +ADD_SCENE(color_guess, play, Play) +ADD_SCENE(color_guess, settings, Settings) \ No newline at end of file diff --git a/scenes/color_guess_scene_menu.c b/scenes/color_guess_scene_menu.c new file mode 100644 index 00000000000..8a47527a977 --- /dev/null +++ b/scenes/color_guess_scene_menu.c @@ -0,0 +1,69 @@ +#include "../color_guess.h" + +enum SubmenuIndex { + SubmenuIndexPlay = 10, + SubmenuIndexColorSet, + SubmenuIndexSettings, +}; + +void color_guess_scene_menu_submenu_callback(void* context, uint32_t index) { + ColorGuess* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void color_guess_scene_menu_on_enter(void* context) { + ColorGuess* app = context; + + submenu_add_item( + app->submenu, "Play Game", SubmenuIndexPlay, color_guess_scene_menu_submenu_callback, app); + submenu_add_item( + app->submenu, + "Set Color", + SubmenuIndexColorSet, + color_guess_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Settings", + SubmenuIndexSettings, + color_guess_scene_menu_submenu_callback, + app); + + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, ColorGuessSceneMenu)); + view_dispatcher_switch_to_view(app->view_dispatcher, ColorGuessViewIdMenu); +} + +bool color_guess_scene_menu_on_event(void* context, SceneManagerEvent event) { + ColorGuess* app = context; + UNUSED(app); + if(event.type == SceneManagerEventTypeBack) { + //exit app + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + return true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexColorSet) { + scene_manager_set_scene_state( + app->scene_manager, ColorGuessSceneMenu, SubmenuIndexColorSet); + scene_manager_next_scene(app->scene_manager, ColorGuessSceneColorSet); + return true; + } else if(event.event == SubmenuIndexPlay) { + scene_manager_set_scene_state( + app->scene_manager, ColorGuessSceneMenu, SubmenuIndexPlay); + scene_manager_next_scene(app->scene_manager, ColorGuessScenePlay); + return true; + } else if(event.event == SubmenuIndexSettings) { + scene_manager_set_scene_state( + app->scene_manager, ColorGuessSceneMenu, SubmenuIndexSettings); + scene_manager_next_scene(app->scene_manager, ColorGuessSceneSettings); + return true; + } + } + return false; +} + +void color_guess_scene_menu_on_exit(void* context) { + ColorGuess* app = context; + submenu_reset(app->submenu); +} \ No newline at end of file diff --git a/scenes/color_guess_scene_play.c b/scenes/color_guess_scene_play.c new file mode 100644 index 00000000000..f45c5c1d7c7 --- /dev/null +++ b/scenes/color_guess_scene_play.c @@ -0,0 +1,50 @@ +#include "../color_guess.h" +#include "../helpers/color_guess_custom_event.h" +#include "../views/color_guess_play.h" + +void color_guess_play_callback(ColorGuessCustomEvent event, void* context) { + furi_assert(context); + ColorGuess* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void color_guess_scene_play_on_enter(void* context) { + furi_assert(context); + ColorGuess* app = context; + color_guess_play_set_callback(app->color_guess_play, color_guess_play_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, ColorGuessViewIdPlay); +} + +bool color_guess_scene_play_on_event(void* context, SceneManagerEvent event) { + ColorGuess* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case ColorGuessCustomEventPlayLeft: + case ColorGuessCustomEventPlayRight: + break; + case ColorGuessCustomEventPlayUp: + case ColorGuessCustomEventPlayDown: + break; + case ColorGuessCustomEventPlayBack: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, ColorGuessSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void color_guess_scene_play_on_exit(void* context) { + ColorGuess* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/scenes/color_guess_scene_settings.c b/scenes/color_guess_scene_settings.c new file mode 100644 index 00000000000..d23fa3fb352 --- /dev/null +++ b/scenes/color_guess_scene_settings.c @@ -0,0 +1,116 @@ +#include "../color_guess.h" +#include + +enum SettingsIndex { + SettingsIndexHaptic = 10, + SettingsIndexValue1, + SettingsIndexValue2, +}; + +const char* const haptic_text[2] = { + "OFF", + "ON", +}; +const uint32_t haptic_value[2] = { + ColorGuessHapticOff, + ColorGuessHapticOn, +}; + +/* Speaker currently not used +const char* const speaker_text[2] = { + "OFF", + "ON", +}; +const uint32_t speaker_value[2] = { + ColorGuessSpeakerOff, + ColorGuessSpeakerOn, +}; +*/ + +/* Game doesn't make sense with LED off, but the setting is there */ +const char* const led_text[2] = { + "OFF", + "ON", +}; +const uint32_t led_value[2] = { + ColorGuessLedOff, + ColorGuessLedOn, +}; + +static void color_guess_scene_settings_set_haptic(VariableItem* item) { + ColorGuess* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, haptic_text[index]); + app->haptic = haptic_value[index]; +} + +/* +static void color_guess_scene_settings_set_speaker(VariableItem* item) { + ColorGuess* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, speaker_text[index]); + app->speaker = speaker_value[index]; +} +*/ + +static void color_guess_scene_settings_set_led(VariableItem* item) { + ColorGuess* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, led_text[index]); + app->led = led_value[index]; +} + +void color_guess_scene_settings_submenu_callback(void* context, uint32_t index) { + ColorGuess* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void color_guess_scene_settings_on_enter(void* context) { + ColorGuess* app = context; + VariableItem* item; + uint8_t value_index; + + // Vibro on/off + item = variable_item_list_add( + app->variable_item_list, "Vibro/Haptic:", 2, color_guess_scene_settings_set_haptic, app); + value_index = value_index_uint32(app->haptic, haptic_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, haptic_text[value_index]); + + // Sound on/off + /* + item = variable_item_list_add( + app->variable_item_list, + "Sound:", + 2, + color_guess_scene_settings_set_speaker, + app); + value_index = value_index_uint32(app->speaker, speaker_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, speaker_text[value_index]);*/ + + // LED Effects on/off + item = variable_item_list_add( + app->variable_item_list, "LED FX:", 2, color_guess_scene_settings_set_led, app); + value_index = value_index_uint32(app->led, led_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, led_text[value_index]); + + view_dispatcher_switch_to_view(app->view_dispatcher, ColorGuessViewIdSettings); +} + +bool color_guess_scene_settings_on_event(void* context, SceneManagerEvent event) { + ColorGuess* app = context; + UNUSED(app); + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + } + return consumed; +} + +void color_guess_scene_settings_on_exit(void* context) { + ColorGuess* app = context; + variable_item_list_set_selected_item(app->variable_item_list, 0); + variable_item_list_reset(app->variable_item_list); +} \ No newline at end of file diff --git a/scenes/color_guess_scene_startscreen.c b/scenes/color_guess_scene_startscreen.c new file mode 100644 index 00000000000..cceba24c663 --- /dev/null +++ b/scenes/color_guess_scene_startscreen.c @@ -0,0 +1,53 @@ +#include "../color_guess.h" + +void color_guess_scene_startscreen_callback(ColorGuessCustomEvent event, void* context) { + furi_assert(context); + ColorGuess* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void color_guess_scene_startscreen_on_enter(void* context) { + furi_assert(context); + ColorGuess* app = context; + color_guess_startscreen_set_callback( + app->color_guess_startscreen, color_guess_scene_startscreen_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, ColorGuessViewIdStartscreen); +} + +bool color_guess_scene_startscreen_on_event(void* context, SceneManagerEvent event) { + ColorGuess* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case ColorGuessCustomEventStartscreenLeft: + case ColorGuessCustomEventStartscreenRight: + break; + case ColorGuessCustomEventStartscreenUp: + case ColorGuessCustomEventStartscreenDown: + break; + case ColorGuessCustomEventStartscreenOk: + scene_manager_next_scene(app->scene_manager, ColorGuessSceneMenu); + consumed = true; + break; + case ColorGuessCustomEventStartscreenBack: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, ColorGuessSceneStartscreen)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void color_guess_scene_startscreen_on_exit(void* context) { + ColorGuess* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/screenshots/color_guess_1.png b/screenshots/color_guess_1.png new file mode 100644 index 00000000000..608b9dcf2ee Binary files /dev/null and b/screenshots/color_guess_1.png differ diff --git a/screenshots/color_guess_2.png b/screenshots/color_guess_2.png new file mode 100644 index 00000000000..cf696738f44 Binary files /dev/null and b/screenshots/color_guess_2.png differ diff --git a/screenshots/color_guess_3.png b/screenshots/color_guess_3.png new file mode 100644 index 00000000000..0364e57de12 Binary files /dev/null and b/screenshots/color_guess_3.png differ diff --git a/views/color_guess_color_set.c b/views/color_guess_color_set.c new file mode 100644 index 00000000000..ef41d8ce92f --- /dev/null +++ b/views/color_guess_color_set.c @@ -0,0 +1,184 @@ +#include "../color_guess.h" +#include "color_guess_icons.h" +#include "../helpers/color_guess_led.h" +#include +#include +#include +#include +#include + +struct ColorGuessColorSet { + View* view; + ColorGuessColorSetCallback callback; + void* context; +}; + +typedef struct { + ColorGuessColorSetStatus status; + int cursorpos; + int digit[6]; +} ColorGuessColorSetModel; + +void color_guess_color_set_set_callback( + ColorGuessColorSet* instance, + ColorGuessColorSetCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void color_guess_color_set_draw(Canvas* canvas, ColorGuessColorSetModel* model) { + const int cursorOffset = 30; + const int newCursorPos = (model->cursorpos * 12) + cursorOffset; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 5, 7, "Set a custom color on LED"); + + canvas_draw_icon(canvas, newCursorPos, 18, &I_ButtonUp_10x5); + canvas_draw_icon(canvas, newCursorPos, 41, &I_ButtonDown_10x5); + canvas_draw_icon(canvas, 18, 25, digits[16]); + canvas_draw_icon(canvas, 30, 25, digits[model->digit[0]]); + canvas_draw_icon(canvas, 42, 25, digits[model->digit[1]]); + canvas_draw_icon(canvas, 54, 25, digits[model->digit[2]]); + canvas_draw_icon(canvas, 66, 25, digits[model->digit[3]]); + canvas_draw_icon(canvas, 78, 25, digits[model->digit[4]]); + canvas_draw_icon(canvas, 90, 25, digits[model->digit[5]]); + elements_button_right(canvas, "See your color here"); +} + +static void color_guess_color_set_model_init(ColorGuessColorSetModel* const model) { + model->cursorpos = 0; + for(int i = 0; i < 6; i++) { + model->digit[i] = 0; + } +} + +void color_guess_color_set_set_led(void* context, ColorGuessColorSetModel* model) { + furi_assert(context); + ColorGuess* app = context; + color_guess_led_set_rgb( + app, + (model->digit[0] * 16) + model->digit[1], + (model->digit[2] * 16) + model->digit[3], + (model->digit[4] * 16) + model->digit[5]); +} + +bool color_guess_color_set_input(InputEvent* event, void* context) { + furi_assert(context); + ColorGuessColorSet* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + ColorGuessColorSetModel * model, + { + UNUSED(model); + instance->callback(ColorGuessCustomEventColorSetBack, instance->context); + }, + true); + break; + case InputKeyLeft: + with_view_model( + instance->view, + ColorGuessColorSetModel * model, + { + model->cursorpos--; + if(model->cursorpos < 0) { + model->cursorpos = 5; + } + }, + true); + break; + case InputKeyRight: + with_view_model( + instance->view, + ColorGuessColorSetModel * model, + { + model->cursorpos++; + if(model->cursorpos > 5) { + model->cursorpos = 0; + } + }, + true); + break; + case InputKeyUp: + with_view_model( + instance->view, + ColorGuessColorSetModel * model, + { + model->digit[model->cursorpos]++; + if(model->digit[model->cursorpos] > 15) { + model->digit[model->cursorpos] = 0; + } + color_guess_color_set_set_led(instance->context, model); + }, + true); + break; + case InputKeyDown: + with_view_model( + instance->view, + ColorGuessColorSetModel * model, + { + model->digit[model->cursorpos]--; + if(model->digit[model->cursorpos] < 0) { + model->digit[model->cursorpos] = 15; + } + color_guess_color_set_set_led(instance->context, model); + }, + true); + break; + case InputKeyOk: + case InputKeyMAX: + break; + } + } + + return true; +} + +void color_guess_color_set_exit(void* context) { + furi_assert(context); +} + +void color_guess_color_set_enter(void* context) { + furi_assert(context); + dolphin_deed(DolphinDeedPluginStart); + // ColorGuessColorSet* instance = context; +} + +ColorGuessColorSet* color_guess_color_set_alloc() { + ColorGuessColorSet* instance = malloc(sizeof(ColorGuessColorSet)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(ColorGuessColorSetModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)color_guess_color_set_draw); + view_set_input_callback(instance->view, color_guess_color_set_input); + //view_set_enter_callback(instance->view, color_guess_color_set_enter); + //view_set_exit_callback(instance->view, color_guess_color_set_exit); + + with_view_model( + instance->view, + ColorGuessColorSetModel * model, + { color_guess_color_set_model_init(model); }, + true); + + return instance; +} + +void color_guess_color_set_free(ColorGuessColorSet* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* color_guess_color_set_get_view(ColorGuessColorSet* instance) { + furi_assert(instance); + + return instance->view; +} diff --git a/views/color_guess_color_set.h b/views/color_guess_color_set.h new file mode 100644 index 00000000000..c430c679a97 --- /dev/null +++ b/views/color_guess_color_set.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "../helpers/color_guess_custom_event.h" + +typedef struct ColorGuessColorSet ColorGuessColorSet; + +typedef void (*ColorGuessColorSetCallback)(ColorGuessCustomEvent event, void* context); + +typedef enum { + ColorGuessColorSetStatusStart, + ColorGuessColorSetStatusIDLE, +} ColorGuessColorSetStatus; + +void color_guess_color_set_set_callback( + ColorGuessColorSet* instance, + ColorGuessColorSetCallback callback, + void* context); + +ColorGuessColorSet* color_guess_color_set_alloc(); + +void color_guess_color_set_free(ColorGuessColorSet* color_guess_static); + +View* color_guess_color_set_get_view(ColorGuessColorSet* color_guess_static); diff --git a/views/color_guess_play.c b/views/color_guess_play.c new file mode 100644 index 00000000000..4fe1f6f67f0 --- /dev/null +++ b/views/color_guess_play.c @@ -0,0 +1,333 @@ +#include "../color_guess.h" +#include "color_guess_icons.h" +#include "../helpers/color_guess_colors.h" +#include "../helpers/color_guess_haptic.h" +#include "../helpers/color_guess_led.h" +#include +#include +#include +#include +#include + +struct ColorGuessPlay { + View* view; + ColorGuessPlayCallback callback; + void* context; +}; + +typedef struct { + ColorGuessPlayStatus status; + int cursorpos; + int digit[6]; + int color; + int time_spent; + int timestamp_start; + int prev_closeness; + int closeness; + int difficulty; + int success; +} ColorGuessPlayModel; + +void color_guess_play_set_callback( + ColorGuessPlay* instance, + ColorGuessPlayCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void play_haptic(void* context, ColorGuessPlayModel* model) { + ColorGuess* app = context; + if(model->success == 1) { + color_guess_play_long_bump(app); + } else if(model->closeness > model->prev_closeness) { + color_guess_play_happy_bump(app); + } else if(model->closeness < model->prev_closeness) { + color_guess_play_bad_bump(app); + } +} + +void color_guess_play_new_round(void* context, ColorGuessPlayModel* model) { + furi_assert(context); + ColorGuess* app = context; + //Reset timer + FuriHalRtcDateTime date_time; + furi_hal_rtc_get_datetime(&date_time); + model->timestamp_start = furi_hal_rtc_datetime_to_timestamp(&date_time); + model->success = 0; + model->closeness = 0; + model->prev_closeness = 0; + + if(model->difficulty == 0) { + model->color = colorsEasy[rand() % ARR_SIZE(colorsEasy)]; + } else if(model->difficulty == 1) { + model->color = colorsNormal[rand() % ARR_SIZE(colorsNormal)]; + } else if(model->difficulty == 2) { + model->color = colorsHard[rand() % ARR_SIZE(colorsHard)]; + } + + color_guess_led_set_rgb( + app, ((model->color >> 16) & 0xFF), ((model->color >> 8) & 0xFF), ((model->color) & 0xFF)); +} + +void color_guess_play_calculate_closeness(void* context, ColorGuessPlayModel* model) { + furi_assert(context); + ColorGuess* app = context; + UNUSED(app); + int userRed = (model->digit[0] * 16) + model->digit[1]; + int userGreen = (model->digit[2] * 16) + model->digit[3]; + int userBlue = (model->digit[4] * 16) + model->digit[5]; + int ledRed = ((model->color >> 16) & 0xFF); + int ledGreen = ((model->color >> 8) & 0xFF); + int ledBlue = ((model->color) & 0xFF); + + int distanceRed = abs(ledRed - userRed); + int distanceGreen = abs(ledGreen - userGreen); + int distanceBlue = abs(ledBlue - userBlue); + float percentageRed = 100 - ((distanceRed / 255.0) * + 100); //make sure one number is float, otherwise C will calc wrong + float percentageGreen = 100 - ((distanceGreen / 255.0) * 100); + float percentageBlue = 100 - ((distanceBlue / 255.0) * 100); + if(percentageRed == 100 && percentageGreen == 100 && percentageBlue == 100) { + model->success = 1; + dolphin_deed(DolphinDeedPluginGameWin); + } + float fullPercentage = (percentageRed + percentageGreen + percentageBlue) / 3; + model->prev_closeness = model->closeness; + model->closeness = round(fullPercentage); +} + +void parse_time_str(char* buffer, int32_t sec) { + snprintf( + buffer, + TIMER_LENGHT, + TIMER_FORMAT, + (sec % (60 * 60)) / 60, // minute + sec % 60); // second +} + +void drawDifficulty(Canvas* canvas, ColorGuessPlayModel* model) { + UNUSED(model); + char* strDifficulty = malloc(7); + if(model->difficulty == 0) { + strcpy(strDifficulty, "Easy"); + } else if(model->difficulty == 1) { + strcpy(strDifficulty, "Medium"); + } else if(model->difficulty == 2) { + strcpy(strDifficulty, "Hard"); + } + canvas_draw_box(canvas, 0, 52, 47, 12); + canvas_invert_color(canvas); + canvas_draw_icon(canvas, 2, 54, &I_ButtonCenter_7x7); + canvas_draw_str_aligned(canvas, 11, 54, AlignLeft, AlignTop, strDifficulty); + canvas_invert_color(canvas); + free(strDifficulty); + furi_thread_flags_wait(0, FuriFlagWaitAny, 10); +} + +void color_guess_play_draw(Canvas* canvas, ColorGuessPlayModel* model) { + char timer_string[TIMER_LENGHT]; + if(model->success == 1) { + parse_time_str(timer_string, model->time_spent); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "You won!!"); + canvas_set_font(canvas, FontSecondary); + elements_button_center(canvas, "New Round"); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 64, 16, AlignCenter, AlignTop, timer_string); + + canvas_draw_icon(canvas, 18, 32, digits[16]); + canvas_draw_icon(canvas, 30, 32, digits[model->digit[0]]); + canvas_draw_icon(canvas, 42, 32, digits[model->digit[1]]); + canvas_draw_icon(canvas, 54, 32, digits[model->digit[2]]); + canvas_draw_icon(canvas, 66, 32, digits[model->digit[3]]); + canvas_draw_icon(canvas, 78, 32, digits[model->digit[4]]); + canvas_draw_icon(canvas, 90, 32, digits[model->digit[5]]); + + return; + } + const int cursorOffset = 30; + const int newCursorPos = (model->cursorpos * 12) + cursorOffset; + FuriHalRtcDateTime date_time; + furi_hal_rtc_get_datetime(&date_time); + uint32_t timestamp = furi_hal_rtc_datetime_to_timestamp(&date_time); + uint32_t time_elapsed = timestamp - model->timestamp_start; + model->time_spent = time_elapsed; + + char closeness_string[4]; + + parse_time_str(timer_string, time_elapsed); + snprintf(closeness_string, CLOSENESS_LENGTH, CLOSENESS_FORMAT, model->closeness); + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Time spent:"); + canvas_draw_str_aligned(canvas, 55, 0, AlignLeft, AlignTop, timer_string); // DRAW TIMER + canvas_draw_str_aligned(canvas, 0, 9, AlignLeft, AlignTop, "You are this close:"); + canvas_draw_str_aligned(canvas, 105, 9, AlignLeft, AlignTop, closeness_string); + + canvas_draw_icon(canvas, newCursorPos, 20, &I_ButtonUp_10x5); + canvas_draw_icon(canvas, newCursorPos, 43, &I_ButtonDown_10x5); + canvas_draw_icon(canvas, 18, 27, digits[16]); + canvas_draw_icon(canvas, 30, 27, digits[model->digit[0]]); + canvas_draw_icon(canvas, 42, 27, digits[model->digit[1]]); + canvas_draw_icon(canvas, 54, 27, digits[model->digit[2]]); + canvas_draw_icon(canvas, 66, 27, digits[model->digit[3]]); + canvas_draw_icon(canvas, 78, 27, digits[model->digit[4]]); + canvas_draw_icon(canvas, 90, 27, digits[model->digit[5]]); + elements_button_right(canvas, "Guess this color"); + drawDifficulty(canvas, model); +} + +static void color_guess_play_model_init(ColorGuessPlayModel* const model) { + model->cursorpos = 0; + for(int i = 0; i < 6; i++) { + model->digit[i] = 0; + } + model->closeness = 0; + model->difficulty = 1; +} + +bool color_guess_play_input(InputEvent* event, void* context) { + furi_assert(context); + ColorGuessPlay* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + ColorGuessPlayModel * model, + { + UNUSED(model); + instance->callback(ColorGuessCustomEventPlayBack, instance->context); + }, + true); + break; + case InputKeyLeft: + with_view_model( + instance->view, + ColorGuessPlayModel * model, + { + model->cursorpos--; + if(model->cursorpos < 0) { + model->cursorpos = 5; + } + }, + true); + break; + case InputKeyRight: + with_view_model( + instance->view, + ColorGuessPlayModel * model, + { + model->cursorpos++; + if(model->cursorpos > 5) { + model->cursorpos = 0; + } + }, + true); + break; + case InputKeyUp: + with_view_model( + instance->view, + ColorGuessPlayModel * model, + { + model->digit[model->cursorpos]++; + if(model->digit[model->cursorpos] > 15) { + model->digit[model->cursorpos] = 0; + } + color_guess_play_calculate_closeness(instance, model); + play_haptic(instance->context, model); + }, + true); + break; + case InputKeyDown: + with_view_model( + instance->view, + ColorGuessPlayModel * model, + { + model->digit[model->cursorpos]--; + if(model->digit[model->cursorpos] < 0) { + model->digit[model->cursorpos] = 15; + } + color_guess_play_calculate_closeness(instance, model); + play_haptic(instance->context, model); + }, + true); + break; + case InputKeyOk: + with_view_model( + instance->view, + ColorGuessPlayModel * model, + { + if(model->success == 1) { + model->success = 0; + } else { + model->difficulty++; + if(model->difficulty > 2) { + model->difficulty = 0; + } + } + color_guess_play_new_round(instance->context, model); + }, + true); + break; + case InputKeyMAX: + break; + } + } + return true; +} + +void color_guess_play_exit(void* context) { + furi_assert(context); +} + +void color_guess_play_enter(void* context) { + furi_assert(context); + ColorGuessPlay* instance = (ColorGuessPlay*)context; + dolphin_deed(DolphinDeedPluginGameStart); + with_view_model( + instance->view, + ColorGuessPlayModel * model, + { + color_guess_play_model_init(model); + color_guess_play_new_round(instance->context, model); + }, + true); +} + +ColorGuessPlay* color_guess_play_alloc() { + ColorGuessPlay* instance = malloc(sizeof(ColorGuessPlay)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(ColorGuessPlayModel)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)color_guess_play_draw); + view_set_input_callback(instance->view, color_guess_play_input); + view_set_enter_callback(instance->view, color_guess_play_enter); + //view_set_exit_callback(instance->view, color_guess_play_exit); + + with_view_model( + instance->view, ColorGuessPlayModel * model, { color_guess_play_model_init(model); }, true); + + return instance; +} + +void color_guess_play_free(ColorGuessPlay* instance) { + furi_assert(instance); + + with_view_model( + instance->view, ColorGuessPlayModel * model, { free(model->digit); }, true); + view_free(instance->view); + free(instance); +} + +View* color_guess_play_get_view(ColorGuessPlay* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/views/color_guess_play.h b/views/color_guess_play.h new file mode 100644 index 00000000000..f9f630c40bf --- /dev/null +++ b/views/color_guess_play.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "../helpers/color_guess_custom_event.h" + +extern const Icon* digits[17]; + +#define TIMER_FORMAT "%02ld:%02ld" //"%.2d:%.2d" +#define TIMER_LENGHT 13 +#define CLOSENESS_LENGTH 7 +#define CLOSENESS_FORMAT "%d%%" +#define ARR_SIZE(arr) (sizeof((arr)) / sizeof((arr[0]))) + +typedef struct ColorGuessPlay ColorGuessPlay; + +typedef void (*ColorGuessPlayCallback)(ColorGuessCustomEvent event, void* context); + +typedef enum { + ColorGuessPlayStatusStart, + ColorGuessPlayStatusIDLE, +} ColorGuessPlayStatus; + +void color_guess_play_set_callback( + ColorGuessPlay* color_guess_play, + ColorGuessPlayCallback callback, + void* context); + +View* color_guess_play_get_view(ColorGuessPlay* color_guess_static); + +ColorGuessPlay* color_guess_play_alloc(); + +void color_guess_play_free(ColorGuessPlay* color_guess_static); \ No newline at end of file diff --git a/views/color_guess_startscreen.c b/views/color_guess_startscreen.c new file mode 100644 index 00000000000..a837574807b --- /dev/null +++ b/views/color_guess_startscreen.c @@ -0,0 +1,127 @@ +#include "../color_guess.h" +#include +#include +#include +#include +#include +#include "color_guess_icons.h" + +struct ColorGuessStartscreen { + View* view; + ColorGuessStartscreenCallback callback; + void* context; +}; + +typedef struct { + int some_value; +} ColorGuessStartscreenModel; + +void color_guess_startscreen_set_callback( + ColorGuessStartscreen* instance, + ColorGuessStartscreenCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void color_guess_startscreen_draw(Canvas* canvas, ColorGuessStartscreenModel* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_icon(canvas, 0, 9, &I_start_dolph_49x55); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Color Guess"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 54, 22, AlignLeft, AlignTop, "Guess the color"); + canvas_draw_str_aligned(canvas, 54, 32, AlignLeft, AlignTop, "on Flipper's LED"); + elements_button_center(canvas, "Start"); +} + +static void color_guess_startscreen_model_init(ColorGuessStartscreenModel* const model) { + model->some_value = 1; +} + +bool color_guess_startscreen_input(InputEvent* event, void* context) { + furi_assert(context); + ColorGuessStartscreen* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + ColorGuessStartscreenModel * model, + { + UNUSED(model); + instance->callback(ColorGuessCustomEventStartscreenBack, instance->context); + }, + true); + break; + case InputKeyLeft: + case InputKeyRight: + case InputKeyUp: + case InputKeyDown: + case InputKeyOk: + with_view_model( + instance->view, + ColorGuessStartscreenModel * model, + { + UNUSED(model); + instance->callback(ColorGuessCustomEventStartscreenOk, instance->context); + }, + true); + break; + case InputKeyMAX: + break; + } + } + return true; +} + +void color_guess_startscreen_exit(void* context) { + furi_assert(context); +} + +void color_guess_startscreen_enter(void* context) { + furi_assert(context); + ColorGuessStartscreen* instance = (ColorGuessStartscreen*)context; + with_view_model( + instance->view, + ColorGuessStartscreenModel * model, + { color_guess_startscreen_model_init(model); }, + true); +} + +ColorGuessStartscreen* color_guess_startscreen_alloc() { + ColorGuessStartscreen* instance = malloc(sizeof(ColorGuessStartscreen)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(ColorGuessStartscreenModel)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)color_guess_startscreen_draw); + view_set_input_callback(instance->view, color_guess_startscreen_input); + //view_set_enter_callback(instance->view, color_guess_startscreen_enter); + //view_set_exit_callback(instance->view, color_guess_startscreen_exit); + + with_view_model( + instance->view, + ColorGuessStartscreenModel * model, + { color_guess_startscreen_model_init(model); }, + true); + + return instance; +} + +void color_guess_startscreen_free(ColorGuessStartscreen* instance) { + furi_assert(instance); + + with_view_model( + instance->view, ColorGuessStartscreenModel * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* color_guess_startscreen_get_view(ColorGuessStartscreen* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/views/color_guess_startscreen.h b/views/color_guess_startscreen.h new file mode 100644 index 00000000000..7474629b46c --- /dev/null +++ b/views/color_guess_startscreen.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/color_guess_custom_event.h" + +typedef struct ColorGuessStartscreen ColorGuessStartscreen; + +typedef void (*ColorGuessStartscreenCallback)(ColorGuessCustomEvent event, void* context); + +void color_guess_startscreen_set_callback( + ColorGuessStartscreen* color_guess_startscreen, + ColorGuessStartscreenCallback callback, + void* context); + +View* color_guess_startscreen_get_view(ColorGuessStartscreen* color_guess_static); + +ColorGuessStartscreen* color_guess_startscreen_alloc(); + +void color_guess_startscreen_free(ColorGuessStartscreen* color_guess_static); \ No newline at end of file