diff --git a/applications/applications.c b/applications/applications.c index 3804a623..eada2daf 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -33,6 +33,8 @@ int32_t sd_filesystem(void* p); int32_t subghz_app(void* p); int32_t gui_test(void* p); int32_t keypad_test(void* p); +int32_t dolphin_scene(void* p); +int32_t passport(void* p); const FlipperApplication FLIPPER_SERVICES[] = { #ifdef APP_CLI @@ -146,6 +148,11 @@ const FlipperApplication FLIPPER_SERVICES[] = { #ifdef APP_KEYPAD_TEST {.app = keypad_test, .name = "keypad_test", .icon = A_Plugins_14}, #endif + +#ifdef APP_DOLPHIN_SCENE + {.app = dolphin_scene, .name = "Dolphin [beta]", .stack_size = 1024, .icon = A_Games_14}, +#endif + }; const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication); @@ -223,3 +230,17 @@ const FlipperApplication FLIPPER_PLUGINS[] = { }; const size_t FLIPPER_PLUGINS_COUNT = sizeof(FLIPPER_PLUGINS) / sizeof(FlipperApplication); + +#ifdef BUILD_DOLPHIN_SCENE +const FlipperApplication FLIPPER_SCENES = + {.app = dolphin_scene, .name = "Dolphin [beta]", .stack_size = 1024, .icon = A_Games_14}; + +const FlipperApplication FLIPPER_SCENE_APPS[] = { + {.app = passport, .name = "Passport", .stack_size = 1024, .icon = A_Games_14}, + {.app = music_player, .name = "Music player", .stack_size = 1024, .icon = A_Plugins_14}, + {.app = floopper_bloopper, .name = "Floopper Bloopper", .stack_size = 1024, .icon = A_Games_14}, +}; + +const size_t FLIPPER_SCENE_APPS_COUNT = sizeof(FLIPPER_SCENE_APPS) / sizeof(FlipperApplication); + +#endif diff --git a/applications/applications.h b/applications/applications.h index e941a8c3..830dbcc9 100644 --- a/applications/applications.h +++ b/applications/applications.h @@ -27,3 +27,10 @@ extern const size_t FLIPPER_APPS_COUNT; */ extern const FlipperApplication FLIPPER_PLUGINS[]; extern const size_t FLIPPER_PLUGINS_COUNT; + +/* Seperate scene app holder + * Spawned by app-loader + */ +extern const FlipperApplication FLIPPER_SCENES; +extern const FlipperApplication FLIPPER_SCENE_APPS[]; +extern const size_t FLIPPER_SCENE_APPS_COUNT; diff --git a/applications/applications.mk b/applications/applications.mk index 9d0ef667..9cce8147 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -16,6 +16,7 @@ APP_BT = 1 APP_CLI = 1 APP_SD_FILESYSTEM = 1 BUILD_IRDA = 1 +BUILD_DOLPHIN_SCENE = 1 APP_DOLPHIN = 1 BUILD_EXAMPLE_BLINK = 1 BUILD_EXAMPLE_UART_WRITE = 1 @@ -227,7 +228,6 @@ C_SOURCES += $(APP_DIR)/examples/keypad_test.c BUILD_KEYPAD_TEST = 1 endif - APP_GPIO_DEMO ?= 0 ifeq ($(APP_GPIO_DEMO), 1) CFLAGS += -DAPP_GPIO_DEMO @@ -261,6 +261,18 @@ CFLAGS += -DBUILD_FLOOPPER_BLOOPPER C_SOURCES += $(wildcard $(APP_DIR)/floopper-bloopper/*.c) endif +APP_DOLPHIN_SCENE ?= 0 +ifeq ($(APP_DOLPHIN_SCENE), 1) +CFLAGS += -DAPP_DOLPHIN_SCENE +BUILD_DOLPHIN_SCENE = 1 +endif +BUILD_DOLPHIN_SCENE ?= 0 +ifeq ($(BUILD_DOLPHIN_SCENE), 1) +CFLAGS += -DBUILD_DOLPHIN_SCENE +C_SOURCES += $(wildcard $(APP_DIR)/dolphin_scene/*.c) +C_SOURCES += $(wildcard $(APP_DIR)/passport/*.c) +endif + APP_IBUTTON ?= 0 ifeq ($(APP_IBUTTON), 1) CFLAGS += -DAPP_IBUTTON diff --git a/applications/dolphin/dolphin.c b/applications/dolphin/dolphin.c index 2eb9baf0..ad3f70ab 100644 --- a/applications/dolphin/dolphin.c +++ b/applications/dolphin/dolphin.c @@ -1,5 +1,19 @@ #include "dolphin_i.h" #include +#include "applications.h" + +static void +dolphin_switch_to_interactive_scene(Dolphin* dolphin, const FlipperApplication* flipper_app) { + furi_assert(dolphin); + furi_assert(flipper_app); + furi_assert(flipper_app->app); + furi_assert(flipper_app->name); + + furi_thread_set_name(dolphin->scene_thread, flipper_app->name); + furi_thread_set_stack_size(dolphin->scene_thread, flipper_app->stack_size); + furi_thread_set_callback(dolphin->scene_thread, flipper_app->app); + furi_thread_start(dolphin->scene_thread); +} // temporary main screen animation managment void dolphin_scene_handler_set_scene(Dolphin* dolphin, IconName icon) { @@ -66,11 +80,10 @@ bool dolphin_view_idle_main_input(InputEvent* event, void* context) { } else if(event->key == InputKeyLeft) { view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleUp); } else if(event->key == InputKeyRight) { - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMeta); + dolphin_switch_to_interactive_scene(dolphin, &FLIPPER_SCENES); } else if(event->key == InputKeyDown) { view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleDown); } - if(event->key == InputKeyBack) { view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); } @@ -121,19 +134,6 @@ static void lock_menu_callback(void* context, uint8_t index) { view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); view_port_enabled_set(dolphin->lock_viewport, true); break; - - default: - break; - } -} - -static void meta_menu_callback(void* context, uint8_t index) { - Dolphin* dolphin = context; - switch(index) { - case 0: - view_port_enabled_set(dolphin->passport, true); - break; - default: break; } @@ -145,59 +145,6 @@ static void lock_icon_callback(Canvas* canvas, void* context) { canvas_draw_icon(canvas, 0, 0, dolphin->lock_icon); } -static void draw_passport_callback(Canvas* canvas, void* context) { - furi_assert(context); - Dolphin* dolphin = context; - - char level[20]; - uint32_t current_level = dolphin_state_get_level(dolphin->state); - uint32_t prev_cap = dolphin_state_xp_to_levelup(dolphin->state, current_level - 1, false); - uint32_t exp = (dolphin_state_xp_to_levelup(dolphin->state, current_level, true) * 63) / - (dolphin_state_xp_to_levelup(dolphin->state, current_level, false) - prev_cap); - - canvas_clear(canvas); - - // multipass - canvas_draw_icon_name(canvas, 0, 0, I_PassportLeft_6x47); - canvas_draw_icon_name(canvas, 0, 47, I_PassportBottom_128x17); - canvas_draw_line(canvas, 6, 0, 125, 0); - canvas_draw_line(canvas, 127, 2, 127, 47); - canvas_draw_dot(canvas, 126, 1); - - //portrait frame - canvas_draw_line(canvas, 9, 6, 9, 53); - canvas_draw_line(canvas, 10, 5, 52, 5); - canvas_draw_line(canvas, 55, 8, 55, 53); - canvas_draw_line(canvas, 10, 54, 54, 54); - canvas_draw_line(canvas, 53, 5, 55, 7); - - // portrait - canvas_draw_icon_name(canvas, 14, 11, I_DolphinOkay_41x43); - canvas_draw_line(canvas, 59, 18, 124, 18); - canvas_draw_line(canvas, 59, 31, 124, 31); - canvas_draw_line(canvas, 59, 44, 124, 44); - - canvas_draw_str(canvas, 59, 15, api_hal_version_get_name_ptr()); - canvas_draw_str(canvas, 59, 28, "Mood: OK"); - - snprintf(level, 20, "Level: %ld", current_level); - - canvas_draw_str(canvas, 59, 41, level); - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 123 - exp, 48, exp + 1, 6); - canvas_set_color(canvas, ColorBlack); - canvas_draw_line(canvas, 123 - exp, 48, 123 - exp, 54); -} - -static void passport_input_callback(InputEvent* event, void* context) { - Dolphin* dolphin = context; - if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - view_port_enabled_set(dolphin->passport, false); - } - } -} - bool dolphin_view_lockmenu_input(InputEvent* event, void* context) { furi_assert(event); furi_assert(context); @@ -244,51 +191,6 @@ bool dolphin_view_lockmenu_input(InputEvent* event, void* context) { return true; } -bool dolphin_view_idle_meta_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - Dolphin* dolphin = context; - - if(event->type != InputTypeShort) return false; - - if(event->key == InputKeyLeft) { - with_view_model( - dolphin->idle_view_meta, (DolphinViewMenuModel * model) { - if(model->idx <= 0) - model->idx = 0; - else - --model->idx; - return true; - }); - } else if(event->key == InputKeyRight) { - with_view_model( - dolphin->idle_view_meta, (DolphinViewMenuModel * model) { - if(model->idx >= 2) - model->idx = 2; - else - ++model->idx; - return true; - }); - } else if(event->key == InputKeyOk) { - with_view_model( - dolphin->idle_view_meta, (DolphinViewMenuModel * model) { - meta_menu_callback(context, model->idx); - return true; - }); - } else if(event->key == InputKeyBack) { - with_view_model( - dolphin->idle_view_meta, (DolphinViewMenuModel * model) { - model->idx = 0; - return true; - }); - view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); - if(random() % 100 > 50) - dolphin_scene_handler_set_scene(dolphin, idle_scenes[random() % sizeof(idle_scenes)]); - } - - return true; -} - Dolphin* dolphin_alloc() { Dolphin* dolphin = furi_alloc(sizeof(Dolphin)); // Message queue @@ -298,8 +200,15 @@ Dolphin* dolphin_alloc() { dolphin->state = dolphin_state_alloc(); // Menu dolphin->menu_vm = furi_record_open("menu"); + // Scene thread + dolphin->scene_thread = furi_thread_alloc(); + // GUI + dolphin->gui = furi_record_open("gui"); + + // Dispatcher dolphin->idle_view_dispatcher = view_dispatcher_alloc(); + // First start View dolphin->idle_view_first_start = view_alloc(); view_allocate_model( @@ -309,6 +218,7 @@ Dolphin* dolphin_alloc() { view_set_input_callback(dolphin->idle_view_first_start, dolphin_view_first_start_input); view_dispatcher_add_view( dolphin->idle_view_dispatcher, DolphinViewFirstStart, dolphin->idle_view_first_start); + // Main Idle View dolphin->idle_view_main = view_alloc(); view_set_context(dolphin->idle_view_main, dolphin); @@ -319,6 +229,7 @@ Dolphin* dolphin_alloc() { view_set_input_callback(dolphin->idle_view_main, dolphin_view_idle_main_input); view_dispatcher_add_view( dolphin->idle_view_dispatcher, DolphinViewIdleMain, dolphin->idle_view_main); + // Stats Idle View dolphin->idle_view_up = view_alloc(); view_set_context(dolphin->idle_view_up, dolphin); @@ -330,6 +241,7 @@ Dolphin* dolphin_alloc() { view_set_previous_callback(dolphin->idle_view_up, dolphin_view_idle_back); view_dispatcher_add_view( dolphin->idle_view_dispatcher, DolphinViewIdleUp, dolphin->idle_view_up); + // Lock Menu View dolphin->view_lockmenu = view_alloc(); view_set_context(dolphin->view_lockmenu, dolphin); @@ -340,22 +252,14 @@ Dolphin* dolphin_alloc() { view_set_previous_callback(dolphin->view_lockmenu, dolphin_view_idle_back); view_dispatcher_add_view( dolphin->idle_view_dispatcher, DolphinViewLockMenu, dolphin->view_lockmenu); - // Meta View - dolphin->idle_view_meta = view_alloc(); - view_set_context(dolphin->idle_view_meta, dolphin); - view_allocate_model( - dolphin->idle_view_meta, ViewModelTypeLockFree, sizeof(DolphinViewMenuModel)); - view_set_draw_callback(dolphin->idle_view_meta, dolphin_view_idle_meta_draw); - view_set_input_callback(dolphin->idle_view_meta, dolphin_view_idle_meta_input); - view_set_previous_callback(dolphin->idle_view_meta, dolphin_view_idle_back); - view_dispatcher_add_view( - dolphin->idle_view_dispatcher, DolphinViewIdleMeta, dolphin->idle_view_meta); + // Down Idle View dolphin->idle_view_down = view_alloc(); view_set_draw_callback(dolphin->idle_view_down, dolphin_view_idle_down_draw); view_set_previous_callback(dolphin->idle_view_down, dolphin_view_idle_back); view_dispatcher_add_view( dolphin->idle_view_dispatcher, DolphinViewIdleDown, dolphin->idle_view_down); + // HW Mismatch dolphin->view_hw_mismatch = view_alloc(); view_set_draw_callback(dolphin->view_hw_mismatch, dolphin_view_hw_mismatch_draw); @@ -370,18 +274,40 @@ Dolphin* dolphin_alloc() { view_port_draw_callback_set(dolphin->lock_viewport, lock_icon_callback, dolphin); view_port_enabled_set(dolphin->lock_viewport, false); - // Passport - dolphin->passport = view_port_alloc(); - view_port_draw_callback_set(dolphin->passport, draw_passport_callback, dolphin); - view_port_input_callback_set(dolphin->passport, passport_input_callback, dolphin); - view_port_enabled_set(dolphin->passport, false); - // Main screen animation dolphin_scene_handler_set_scene(dolphin, idle_scenes[random() % sizeof(idle_scenes)]); + view_dispatcher_attach_to_gui( + dolphin->idle_view_dispatcher, dolphin->gui, ViewDispatcherTypeWindow); + gui_add_view_port(dolphin->gui, dolphin->lock_viewport, GuiLayerStatusBarLeft); + return dolphin; } +void dolphin_free(Dolphin* dolphin) { + furi_assert(dolphin); + + gui_remove_view_port(dolphin->gui, dolphin->lock_viewport); + view_port_free(dolphin->lock_viewport); + icon_free(dolphin->lock_icon); + + view_dispatcher_free(dolphin->idle_view_dispatcher); + + furi_record_close("gui"); + dolphin->gui = NULL; + + furi_thread_free(dolphin->scene_thread); + + furi_record_close("menu"); + dolphin->menu_vm = NULL; + + dolphin_state_free(dolphin->state); + + osMessageQueueDelete(dolphin->event_queue); + + free(dolphin); +} + void dolphin_save(Dolphin* dolphin) { furi_assert(dolphin); DolphinEvent event; @@ -400,13 +326,6 @@ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { int32_t dolphin_task() { Dolphin* dolphin = dolphin_alloc(); - Gui* gui = furi_record_open("gui"); - - view_dispatcher_attach_to_gui(dolphin->idle_view_dispatcher, gui, ViewDispatcherTypeWindow); - gui_add_view_port(gui, dolphin->lock_viewport, GuiLayerStatusBarLeft); - - gui_add_view_port(gui, dolphin->passport, GuiLayerFullscreen); - if(dolphin_state_load(dolphin->state)) { view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleMain); } else { @@ -439,5 +358,6 @@ int32_t dolphin_task() { dolphin_state_save(dolphin->state); } } + dolphin_free(dolphin); return 0; } diff --git a/applications/dolphin/dolphin_i.h b/applications/dolphin/dolphin_i.h index ff257921..6fe17c23 100644 --- a/applications/dolphin/dolphin_i.h +++ b/applications/dolphin/dolphin_i.h @@ -33,7 +33,10 @@ struct Dolphin { DolphinState* state; // Menu ValueMutex* menu_vm; + // Scene + FuriThread* scene_thread; // GUI + Gui* gui; ViewDispatcher* idle_view_dispatcher; View* idle_view_first_start; View* idle_view_main; @@ -42,7 +45,6 @@ struct Dolphin { View* idle_view_meta; View* view_hw_mismatch; View* view_lockmenu; - ViewPort* passport; ViewPort* lock_viewport; Icon* lock_icon; @@ -55,6 +57,8 @@ const IconName idle_scenes[] = {A_Wink_128x64, A_WatchingTV_128x64}; Dolphin* dolphin_alloc(); +void dolphin_free(Dolphin* dolphin); + /* Save Dolphin state (write to permanent memory) * Thread safe */ diff --git a/applications/dolphin/dolphin_state.c b/applications/dolphin/dolphin_state.c index cbe5e117..4c3c0574 100644 --- a/applications/dolphin/dolphin_state.c +++ b/applications/dolphin/dolphin_state.c @@ -40,7 +40,7 @@ DolphinState* dolphin_state_alloc() { return dolphin_state; } -void dolphin_state_release(DolphinState* dolphin_state) { +void dolphin_state_free(DolphinState* dolphin_state) { free(dolphin_state); } diff --git a/applications/dolphin/dolphin_state.h b/applications/dolphin/dolphin_state.h index 6e5a94e0..cbd9611a 100644 --- a/applications/dolphin/dolphin_state.h +++ b/applications/dolphin/dolphin_state.h @@ -8,7 +8,7 @@ typedef struct DolphinState DolphinState; DolphinState* dolphin_state_alloc(); -void dolphin_state_release(DolphinState* dolphin_state); +void dolphin_state_free(DolphinState* dolphin_state); bool dolphin_state_save(DolphinState* dolphin_state); diff --git a/applications/dolphin/dolphin_views.c b/applications/dolphin/dolphin_views.c index 0c0b569b..345e664b 100644 --- a/applications/dolphin/dolphin_views.c +++ b/applications/dolphin/dolphin_views.c @@ -5,7 +5,6 @@ #include static char* Lockmenu_Items[3] = {"Lock", "Set PIN", "DUMB mode"}; -static char* Meta_Items[3] = {"Passport", "Games", "???"}; void dolphin_view_first_start_draw(Canvas* canvas, void* model) { DolphinViewFirstStartModel* m = model; @@ -58,7 +57,7 @@ void dolphin_view_first_start_draw(Canvas* canvas, void* model) { void dolphin_view_idle_main_draw(Canvas* canvas, void* model) { canvas_clear(canvas); DolphinViewMainModel* m = model; - if(m->animation) canvas_draw_icon(canvas, 0, 0, m->animation); + if(m->animation) canvas_draw_icon(canvas, 0, -3, m->animation); } void dolphin_view_idle_up_draw(Canvas* canvas, void* model) { @@ -92,29 +91,6 @@ void dolphin_view_lockmenu_draw(Canvas* canvas, void* model) { } } -void dolphin_view_idle_meta_draw(Canvas* canvas, void* model) { - DolphinViewMenuModel* m = model; - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - - canvas_draw_icon_name(canvas, 20, 23, I_BigProfile_24x24); - canvas_draw_icon_name(canvas, 55, 23, I_BigGames_24x24); - canvas_draw_icon_name(canvas, 90, 23, I_BigBurger_24x24); - - canvas_draw_str_aligned(canvas, 66, 12, AlignCenter, AlignCenter, Meta_Items[m->idx]); - - canvas_draw_frame(canvas, 17 + (35 * m->idx), 20, 30, 30); - canvas_set_color(canvas, ColorWhite); - - canvas_draw_dot(canvas, 17 + (35 * m->idx), 20); - canvas_draw_dot(canvas, 17 + (35 * m->idx), 49); - canvas_draw_dot(canvas, 46 + (35 * m->idx), 20); - canvas_draw_dot(canvas, 46 + (35 * m->idx), 49); - - canvas_set_color(canvas, ColorBlack); -} - void dolphin_view_idle_down_draw(Canvas* canvas, void* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); diff --git a/applications/dolphin/dolphin_views.h b/applications/dolphin/dolphin_views.h index 2be3b14f..9d53626f 100644 --- a/applications/dolphin/dolphin_views.h +++ b/applications/dolphin/dolphin_views.h @@ -8,13 +8,12 @@ // Idle scree typedef enum { - DolphinViewFirstStart, DolphinViewIdleMain, + DolphinViewFirstStart, DolphinViewIdleUp, DolphinViewIdleDown, DolphinViewHwMismatch, DolphinViewLockMenu, - DolphinViewIdleMeta, } DolphinViewIdle; typedef struct { @@ -48,8 +47,6 @@ void dolphin_view_lockmenu_draw(Canvas* canvas, void* model); void dolphin_view_idle_down_draw(Canvas* canvas, void* model); -void dolphin_view_idle_meta_draw(Canvas* canvas, void* model); - void dolphin_view_hw_mismatch_draw(Canvas* canvas, void* model); uint32_t dolphin_view_idle_back(void* context); diff --git a/applications/dolphin_scene/dolphin_emotes.h b/applications/dolphin_scene/dolphin_emotes.h new file mode 100644 index 00000000..8f2f5611 --- /dev/null +++ b/applications/dolphin_scene/dolphin_emotes.h @@ -0,0 +1,15 @@ +#pragma once + +static const char* emotes_list[] = { + "(O_o)", "(!_?)", "(^_^)", "(*__*)", "(@_@)", "(X_x)", "(>_<)", "(^ ^)", "(^_^)", + "(-_-)", "(~_~)", "(#^.^#)", "(^ ^)", "(^.^)", "(-.-)", "zZzZ", "(^_-)", "(^_-)", + "(+_+)", "(+o+)", "(' ')", "('-')", "('.')", "('_')", "(* > *)", "(o o)", "(^_^)", + "(^O^)", "(^o^)", "(^o^)", "(._.)", "(_^_)", "('_')", "('_;)", "(T_T)", "(;_;)", + "(ー_ー)", "(-.-)", "(^o^)", "(-_-)", "(=_=)", "(=^ ^=)", "(. .)", "(._.)", "( ^m^)", + "(?_?)", "(*^_^*)", "(^<^)", "(^.^)", "(^·^)", "(^.^)", "(^_^.)", "(^_^)", "(^^)", + "(^J^)", "(*^.^*)", "(#^.^#)", "(~o~)", "(^o^)", "(-o-)", "(^. ^)", "(^o^)", "(*^0^*)", + "(*_*)", "(~ o ~)", "(~_~)", "(p_-)", "d[-_-]b", "(^0_0^)", "- ^ -"}; + +static const char* dialogues_list[] = { + "Let's hack!\n\nbla bla bla\nbla bla..", +}; diff --git a/applications/dolphin_scene/dolphin_scene.c b/applications/dolphin_scene/dolphin_scene.c new file mode 100644 index 00000000..97e94dc1 --- /dev/null +++ b/applications/dolphin_scene/dolphin_scene.c @@ -0,0 +1,31 @@ +#include +#include "dolphin_scene/dolphin_scene.h" + +void dolphin_scene_redraw(Canvas* canvas, void* ctx) { + furi_assert(canvas); + furi_assert(ctx); + + SceneState* state = (SceneState*)acquire_mutex((ValueMutex*)ctx, 25); + if(state == NULL) return; // redraw fail + uint32_t t = xTaskGetTickCount(); + + canvas_clear(canvas); + + dolphin_scene_render(state, canvas, t); + + dolphin_scene_render_dolphin_state(state, canvas); + + release_mutex((ValueMutex*)ctx, state); +} + +void dolphin_scene_handle_input(SceneState* state, InputEvent* input) { + // printf("[kb] event: %02x %s\n", input->key, input->state ? "pressed" : "released"); + dolphin_scene_handle_user_input(state, input); +} + +void dolphin_scene_tick_handler(SceneState* state, uint32_t t, uint32_t dt) { + // printf("t: %d, dt: %d\n", t, dt); + + dolphin_scene_coordinates(state, dt); + dolphin_scene_update_dolphin_state(state, t, dt); +} diff --git a/applications/dolphin_scene/dolphin_scene.h b/applications/dolphin_scene/dolphin_scene.h new file mode 100644 index 00000000..ccfc5d06 --- /dev/null +++ b/applications/dolphin_scene/dolphin_scene.h @@ -0,0 +1,141 @@ +#pragma once + +#include +#include +#include + +#ifndef ARRSIZE +#define ARRSIZE(arr) (sizeof(arr) / sizeof(arr[0])) +#endif + +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#ifndef CLAMP +#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) +#endif + +// global +#define SCALE 32 +// screen +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define BONDARIES_X_LEFT 40 +#define BONDARIES_X_RIGHT 88 + +// player +#define DOLPHIN_WIDTH 32 +#define DOLPHIN_HEIGHT 32 +#define DOLPHIN_CENTER (SCREEN_WIDTH / 2 - DOLPHIN_WIDTH / 2) +#define SPEED_X 2 +#define ACTIONS_NUM 5 +#define DOLPHIN_DEFAULT_Y 20 +// world +#define WORLD_WIDTH 2048 +#define WORLD_HEIGHT 64 + +#define LAYERS 8 +#define SCENE_ZOOM 9 +#define DOLPHIN_LAYER 6 +#define PARALLAX_MOD 7 +#define PARALLAX(layer) layer / PARALLAX_MOD - layer +#define ITEMS_NUM 4 + +#define DIALOG_PROGRESS 250 + +enum Actions { SLEEP = 0, IDLE, WALK, EMOTE, INTERACT, MINDCONTROL }; + +static const uint16_t default_timeout[] = + {[SLEEP] = 300, [IDLE] = 100, [WALK] = 100, [EMOTE] = 50, [INTERACT] = 10, [MINDCONTROL] = 50}; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + union { + InputEvent input; + } value; + EventType type; +} AppEvent; + +typedef struct { + int32_t x; + int32_t y; +} Vec2; + +typedef struct { + Gui* gui; + ViewPort* view_port; + ValueMutex* vm; + osTimerId_t* timer; + osMessageQueueId_t mqueue; + FuriThread* scene_app_thread; + +} SceneAppGui; + +typedef struct { + uint8_t layer; + uint16_t timeout; + int32_t x; + int32_t y; + IconName icon; + char action_name[16]; + void (*draw)(Canvas* canvas, void* model); + void (*callback)(Canvas* canvas, void* model); +} Item; + +typedef struct { + SceneAppGui ui; + /// + Vec2 player; + Vec2 player_global; + Vec2 player_v; + Vec2 screen; + + IconName dolphin_gfx; + IconName dolphin_gfx_b; // temp + + bool player_flipped; + bool use_pending; + // dolphin_scene_debug + bool debug; + + uint8_t player_anim; + uint8_t scene_id; + + uint8_t emote_id; + uint8_t previous_emote; + + uint8_t dialogue_id; + uint8_t previous_dialogue; + + uint32_t action_timeout; + uint8_t poi; + + uint8_t action; + uint8_t next_action; + uint8_t prev_action; + + int8_t zoom_v; + uint8_t scene_zoom; + uint8_t dialog_progress; +} SceneState; + +void dolphin_scene_render(SceneState* state, Canvas* canvas, uint32_t t); +void dolphin_scene_render_dolphin(SceneState* state, Canvas* canvas); +void dolphin_scene_handle_user_input(SceneState* state, InputEvent* input); +void dolphin_scene_coordinates(SceneState* state, uint32_t dt); + +void dolphin_scene_render_dolphin_state(SceneState* state, Canvas* canvas); +void dolphin_scene_update_dolphin_state(SceneState* state, uint32_t t, uint32_t dt); + +void dolphin_scene_redraw(Canvas* canvas, void* ctx); +void dolphin_scene_tick_handler(SceneState* state, uint32_t t, uint32_t dt); +void dolphin_scene_handle_input(SceneState* state, InputEvent* input); diff --git a/applications/dolphin_scene/engine.c b/applications/dolphin_scene/engine.c new file mode 100644 index 00000000..5d0b6127 --- /dev/null +++ b/applications/dolphin_scene/engine.c @@ -0,0 +1,115 @@ +#include +#include "dolphin_scene/dolphin_scene.h" + +void dolphin_engine_tick_cb(void* p) { + osMessageQueueId_t event_queue = p; + AppEvent tick_event; + tick_event.type = EventTypeTick; + osMessageQueuePut(event_queue, (void*)&tick_event, 0, 0); +} + +static void dolphin_engine_event_cb(InputEvent* input_event, void* ctx) { + osMessageQueueId_t event_queue = ctx; + + AppEvent event; + event.type = EventTypeKey; + event.value.input = *input_event; + osMessageQueuePut(event_queue, (void*)&event, 0, osWaitForever); +} + +ValueMutex* scene_init() { + SceneState* scene_state = furi_alloc(sizeof(SceneState)); + scene_state->ui.mqueue = osMessageQueueNew(2, sizeof(AppEvent), NULL); + + scene_state->player.y = DOLPHIN_DEFAULT_Y; + scene_state->player.x = DOLPHIN_CENTER; + + //randomize position + scene_state->player_global.x = random() % WORLD_WIDTH / 4; + + scene_state->screen.x = scene_state->player.x; + scene_state->screen.y = scene_state->player.y; + + ValueMutex* scene_state_mutex = furi_alloc(sizeof(ValueMutex)); + if(scene_state_mutex == NULL || + !init_mutex(scene_state_mutex, scene_state, sizeof(SceneState))) { + printf("[menu_task] cannot create menu mutex\r\n"); + furi_check(0); + } + + // Open GUI and register view_port + scene_state->ui.gui = furi_record_open("gui"); + + // Allocate and configure view_port + scene_state->ui.view_port = view_port_alloc(); + + // Open GUI and register fullscreen view_port + gui_add_view_port(scene_state->ui.gui, scene_state->ui.view_port, GuiLayerMain); + view_port_draw_callback_set( + scene_state->ui.view_port, dolphin_scene_redraw, scene_state_mutex); + view_port_input_callback_set( + scene_state->ui.view_port, dolphin_engine_event_cb, scene_state->ui.mqueue); + view_port_enabled_set(scene_state->ui.view_port, true); + + scene_state->ui.timer = + osTimerNew(dolphin_engine_tick_cb, osTimerPeriodic, scene_state->ui.mqueue, NULL); + + return scene_state_mutex; +} + +void scene_free(ValueMutex* scene_state_mutex) { + furi_assert(scene_state_mutex); + + SceneState* scene_state = (SceneState*)acquire_mutex_block(scene_state_mutex); + + osTimerDelete(scene_state->ui.timer); + gui_remove_view_port(scene_state->ui.gui, scene_state->ui.view_port); + view_port_free(scene_state->ui.view_port); + furi_record_close("gui"); + osMessageQueueDelete(scene_state->ui.mqueue); + free(scene_state); + + release_mutex(scene_state_mutex, scene_state); + delete_mutex(scene_state_mutex); + free(scene_state_mutex); +} + +int32_t dolphin_scene(void* p) { + ValueMutex* scene_state_mutex = scene_init(); + + furi_record_create("scene", scene_state_mutex); + + SceneState* _state = (SceneState*)acquire_mutex_block(scene_state_mutex); + osTimerStart(_state->ui.timer, 40); + uint32_t t = xTaskGetTickCount(); + uint32_t prev_t = 0; + osMessageQueueId_t q = _state->ui.mqueue; + release_mutex(scene_state_mutex, _state); + + while(1) { + AppEvent event; + if(osMessageQueueGet(q, &event, 0, osWaitForever) == osOK) { + SceneState* _state = (SceneState*)acquire_mutex_block(scene_state_mutex); + if(event.type == EventTypeTick) { + t = xTaskGetTickCount(); + dolphin_scene_tick_handler(_state, t, (t - prev_t) % 1024); + prev_t = t; + } else if(event.type == EventTypeKey) { + if(event.value.input.key == InputKeyBack && + event.value.input.type == InputTypeShort) { + release_mutex(scene_state_mutex, _state); + break; + + } else { + dolphin_scene_handle_input(_state, &event.value.input); + } + } + release_mutex(scene_state_mutex, _state); + view_port_update(_state->ui.view_port); + } + } + + scene_free(scene_state_mutex); + + return 0; +} diff --git a/applications/dolphin_scene/items.c b/applications/dolphin_scene/items.c new file mode 100644 index 00000000..1b1ae9fb --- /dev/null +++ b/applications/dolphin_scene/items.c @@ -0,0 +1,131 @@ +#include "dolphin_scene/items_i.h" +#include +#include "applications.h" + +const Item TV = { + .layer = 7, + .timeout = 10, + .x = 160, + .y = 34, + .icon = I_TV_20x24, + .action_name = "Use", + .draw = draw_tv, + .callback = smash_tv}; + +const Item Painting = { + .layer = 3, + .timeout = 20, + .x = 160, + .y = 10, + .icon = I_Home_painting_17x20, + .action_name = "Inspect", + .draw = NULL, + .callback = inspect_painting}; + +const Item Sofa = { + .layer = 4, + .timeout = 100, + .x = 250, + .y = 34, + .icon = I_Sofa_40x13, + .action_name = "Sit", + .draw = NULL, + .callback = sofa_sit}; + +const Item PC = { + .layer = 4, + .timeout = 100, + .x = 400, + .y = 10, + .icon = I_PC_22x29, + .action_name = "Use", + .draw = NULL, + .callback = pc_callback}; + +const Item* Home[ITEMS_NUM] = {&TV, &Sofa, &Painting, &PC}; +const Item** Scenes[1] = {*&Home}; + +const Item** get_scene(SceneState* state) { + return Scenes[state->scene_id]; +} + +static void dolphin_scene_start_app(SceneState* state, const FlipperApplication* flipper_app) { + furi_assert(state); + furi_assert(flipper_app); + + state->ui.scene_app_thread = furi_thread_alloc(); + + furi_assert(flipper_app->app); + furi_assert(flipper_app->name); + + furi_thread_set_name(state->ui.scene_app_thread, flipper_app->name); + furi_thread_set_stack_size(state->ui.scene_app_thread, flipper_app->stack_size); + furi_thread_set_callback(state->ui.scene_app_thread, flipper_app->app); + furi_thread_start(state->ui.scene_app_thread); +} + +const Item* is_nearby(SceneState* state) { + furi_assert(state); + uint8_t item = 0; + bool found = false; + const Item** current = get_scene(state); + while(item < ITEMS_NUM) { + int32_t rel = + (DOLPHIN_CENTER + DOLPHIN_WIDTH / 2 - + (current[item]->x - state->player_global.x) * PARALLAX(current[item]->layer)); + if(abs(rel) <= DOLPHIN_WIDTH / 2) { + found = !found; + break; + } + ++item; + } + return found ? current[item] : NULL; +} + +void draw_tv(Canvas* canvas, void* state) { + furi_assert(state); + SceneState* s = state; + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, (TV.x + 3 - s->player_global.x) * PARALLAX(TV.layer), TV.y + 4, 16, 20); + canvas_set_color(canvas, ColorBlack); + canvas_set_bitmap_mode(canvas, true); +} + +void smash_tv(Canvas* canvas, void* state) { + furi_assert(state); + SceneState* s = state; + s->player_flipped = true; + canvas_set_bitmap_mode(canvas, true); + canvas_draw_icon_name( + canvas, ((TV.x - 5) - s->player_global.x) * PARALLAX(TV.layer), TV.y - 2, I_FX_Bang_32x6); + canvas_set_bitmap_mode(canvas, false); + if(s->action_timeout < TV.timeout - 2) { + elements_multiline_text_framed(canvas, 80, 24, "Bang!"); + } +} + +void sofa_sit(Canvas* canvas, void* state) { + furi_assert(state); + SceneState* s = state; + // temp fix pos + s->player_global.x = 154; + s->dolphin_gfx = A_FX_Sitting_40x27; + s->dolphin_gfx_b = I_FX_SittingB_40x27; +} + +void inspect_painting(Canvas* canvas, void* state) { + furi_assert(state); + SceneState* s = state; + if(s->use_pending) { + dolphin_scene_start_app(s, &FLIPPER_SCENE_APPS[0]); + } +} + +void pc_callback(Canvas* canvas, void* state) { + furi_assert(state); + SceneState* s = state; + if(s->use_pending) { + dolphin_scene_start_app(s, &FLIPPER_SCENE_APPS[1]); + } +} diff --git a/applications/dolphin_scene/items.h b/applications/dolphin_scene/items.h new file mode 100644 index 00000000..08f91317 --- /dev/null +++ b/applications/dolphin_scene/items.h @@ -0,0 +1,5 @@ +#pragma once +#include "dolphin_scene/dolphin_scene.h" + +const Item* is_nearby(SceneState* state); +const Item** get_scene(SceneState* state); \ No newline at end of file diff --git a/applications/dolphin_scene/items_i.h b/applications/dolphin_scene/items_i.h new file mode 100644 index 00000000..ac1850f8 --- /dev/null +++ b/applications/dolphin_scene/items_i.h @@ -0,0 +1,8 @@ +#pragma once +#include "dolphin_scene/items.h" + +void smash_tv(Canvas* canvas, void* state); +void draw_tv(Canvas* canvas, void* state); +void sofa_sit(Canvas* canvas, void* state); +void inspect_painting(Canvas* canvas, void* state); +void pc_callback(Canvas* canvas, void* state); diff --git a/applications/dolphin_scene/scene.c b/applications/dolphin_scene/scene.c new file mode 100644 index 00000000..cbe36f0c --- /dev/null +++ b/applications/dolphin_scene/scene.c @@ -0,0 +1,172 @@ +#include +#include "dolphin_scene/dolphin_scene.h" +#include "dolphin_scene/dolphin_emotes.h" +#include "dolphin_scene/items.h" +#include + +const char* action_str[] = {"Sleep", "Idle", "Walk", "Emote", "Use", "MC"}; + +static bool item_screen_bounds(int32_t pos) { + return pos > -SCREEN_WIDTH && pos < (SCREEN_WIDTH * 2); +} + +static void draw_hint(SceneState* state, Canvas* canvas, bool glitching) { + furi_assert(state); + furi_assert(canvas); + char buf[32]; + + const Item* near = is_nearby(state); + if(near) { + int32_t hint_pos_x = (near->x - state->player_global.x) * PARALLAX(near->layer) + 25; + int8_t hint_pos_y = near->y < 15 ? near->y + 4 : near->y - 16; + + strcpy(buf, near->action_name); + if(glitching) { + for(size_t g = 0; g != state->action_timeout; g++) { + buf[(g * 23) % strlen(buf)] = ' ' + (random() % g * 17) % ('z' - ' '); + } + } + + canvas_draw_str(canvas, hint_pos_x, hint_pos_y, buf); + } +} + +static void draw_current_emote(SceneState* state, Canvas* canvas) { + furi_assert(state); + furi_assert(canvas); + elements_multiline_text_framed(canvas, 80, 20, (char*)emotes_list[state->emote_id]); +} + +static void draw_sleep_emote(SceneState* state, Canvas* canvas) { + furi_assert(state); + furi_assert(canvas); + + char dialog_str[] = "zZzZ..."; + char buf[64]; + + if(state->player_global.x == 154 && state->action_timeout % 100 < 30) { + if(state->dialog_progress < strlen(dialog_str)) { + if(state->action_timeout % 5 == 0) state->dialog_progress++; + dialog_str[state->dialog_progress] = '\0'; + snprintf(buf, state->dialog_progress, dialog_str); + // bubble vs just text? + //elements_multiline_text_framed(canvas, 80, 20, buf); + canvas_draw_str(canvas, 80, 20, buf); + } + + } else { + state->dialog_progress = 0; + } +} + +static void draw_dialog(SceneState* state, Canvas* canvas) { + furi_assert(state); + furi_assert(canvas); + + char dialog_str[64]; + char buf[64]; + + strcpy(dialog_str, (char*)dialogues_list[state->dialogue_id]); + + if(state->dialog_progress <= strlen(dialog_str)) { + if(state->action_timeout % 2 == 0) state->dialog_progress++; + dialog_str[state->dialog_progress] = '\0'; + snprintf(buf, state->dialog_progress, dialog_str); + } else { + snprintf(buf, 64, dialog_str); + } + + elements_multiline_text_framed(canvas, 68, 16, buf); +} + +/* +static void draw_idle_emote(SceneState* state, Canvas* canvas){ + if(state->action_timeout % 50 < 40 && state->prev_action == MINDCONTROL){ + elements_multiline_text_framed(canvas, 68, 16, "WUT?!"); + } +} +*/ + +static void activate_item_callback(SceneState* state, Canvas* canvas) { + furi_assert(state); + furi_assert(canvas); + + const Item* near = is_nearby(state); + if(near && state->use_pending == true) { + state->action_timeout = near->timeout; + near->callback(canvas, state); + state->use_pending = false; + } else if(near) { + near->callback(canvas, state); + } +} + +void dolphin_scene_render(SceneState* state, Canvas* canvas, uint32_t t) { + furi_assert(state); + furi_assert(canvas); + + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorBlack); + const Item** current_scene = get_scene(state); + + for(uint8_t l = 0; l < LAYERS; l++) { + if(state->scene_zoom < SCENE_ZOOM) { + for(uint8_t i = 0; i < ITEMS_NUM; i++) { + int32_t item_pos = (current_scene[i]->x - state->player_global.x); + if(item_screen_bounds(item_pos)) { + if(current_scene[i]->draw) current_scene[i]->draw(canvas, state); + + if(l == current_scene[i]->layer) { + canvas_draw_icon_name( + canvas, + item_pos * PARALLAX(l), + current_scene[i]->y, + current_scene[i]->icon); + canvas_set_bitmap_mode(canvas, false); + } + } + } + + if(l == 0) canvas_draw_line(canvas, 0, 42, 128, 42); + } + + if(l == DOLPHIN_LAYER) dolphin_scene_render_dolphin(state, canvas); + } +} + +void dolphin_scene_render_dolphin_state(SceneState* state, Canvas* canvas) { + furi_assert(state); + furi_assert(canvas); + + char buf[64]; + + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorBlack); + + // dolphin_scene_debug + if(state->debug) { + sprintf( + buf, + "x:%ld>%d %ld %s", + state->player_global.x, + state->poi, + state->action_timeout, + action_str[state->action]); + canvas_draw_str(canvas, 0, 13, buf); + } + + if(state->scene_zoom == SCENE_ZOOM) + draw_dialog(state, canvas); + else if(state->action == EMOTE) + draw_current_emote(state, canvas); + else if(state->action == MINDCONTROL) + draw_hint(state, canvas, state->action_timeout > 45); + else if(state->action == INTERACT) + activate_item_callback(state, canvas); + else if(state->action == SLEEP) + draw_sleep_emote(state, canvas); + /* + else if(state->action == IDLE) + draw_idle_emote(state, canvas); + */ +} \ No newline at end of file diff --git a/applications/dolphin_scene/state.c b/applications/dolphin_scene/state.c new file mode 100644 index 00000000..0e0680be --- /dev/null +++ b/applications/dolphin_scene/state.c @@ -0,0 +1,101 @@ +#include +#include "dolphin_scene/dolphin_scene.h" +#include "dolphin_scene/dolphin_emotes.h" + +static uint16_t roll_new(uint16_t prev, uint16_t max) { + uint16_t val = 999; + while(val != prev) { + val = random() % max; + break; + } + return val; +} + +static void dolphin_actions_proceed(SceneState* state) { + furi_assert(state); + + state->prev_action = state->action; + state->action = (state->prev_action != state->next_action) ? + state->next_action : + roll_new(state->next_action, ACTIONS_NUM); + state->action_timeout = default_timeout[state->action]; +} + +static void dolphin_go_to_poi(SceneState* state) { + furi_assert(state); + if(state->player_global.x < state->poi) { + state->player_flipped = false; + state->player_v.x = SPEED_X / 2; + } else if(state->player_global.x > state->poi) { + state->player_flipped = true; + state->player_v.x = -SPEED_X / 2; + } +} + +static void action_handler(SceneState* state) { + furi_assert(state); + if(state->action == MINDCONTROL && state->player_v.x != 0) { + state->action_timeout = default_timeout[state->action]; + } + + if(state->action_timeout > 0) { + state->action_timeout--; + } else { + if(random() % 1000 > 500) { + state->next_action = roll_new(state->prev_action, ACTIONS_NUM); + state->poi = roll_new(state->player_global.x, WORLD_WIDTH / 4); + } + } +} + +void dolphin_scene_update_dolphin_state(SceneState* state, uint32_t t, uint32_t dt) { + furi_assert(state); + action_handler(state); + + switch(state->action) { + case WALK: + if(state->player_global.x == state->poi) { + state->player_v.x = 0; + dolphin_actions_proceed(state); + } else { + dolphin_go_to_poi(state); + } + break; + case EMOTE: + state->player_flipped = false; + if(state->action_timeout == 0) { + dolphin_actions_proceed(state); + state->emote_id = roll_new(state->previous_emote, ARRSIZE(emotes_list)); + break; + } + case INTERACT: + if(state->action_timeout == 0) { + if(state->prev_action == MINDCONTROL) { + state->action = MINDCONTROL; + } else { + dolphin_actions_proceed(state); + } + } + break; + case SLEEP: + if(state->poi != 154) { // temp + state->poi = 154; + } else if(state->player_global.x != state->poi) { + dolphin_go_to_poi(state); + } else { + state->player_v.x = 0; + if(state->action_timeout == 0) { + state->poi = roll_new(state->player_global.x, WORLD_WIDTH / 4); + dolphin_actions_proceed(state); + } + break; + } + default: + if(state->action_timeout == 0) { + dolphin_actions_proceed(state); + } + break; + } + + UNUSED(dialogues_list); +} diff --git a/applications/dolphin_scene/user.c b/applications/dolphin_scene/user.c new file mode 100644 index 00000000..d54e7469 --- /dev/null +++ b/applications/dolphin_scene/user.c @@ -0,0 +1,111 @@ +#include +#include +#include "dolphin_scene/dolphin_scene.h" + +void dolphin_scene_render_dolphin(SceneState* state, Canvas* canvas) { + furi_assert(state); + furi_assert(canvas); + + if(state->scene_zoom == SCENE_ZOOM) { + state->dolphin_gfx = I_DolphinExcited_64x63; + } else if(state->action == SLEEP && state->player_global.x == 154) { + state->dolphin_gfx = A_FX_Sitting_40x27; + state->dolphin_gfx_b = I_FX_SittingB_40x27; + } else if(state->action != INTERACT) { + if(state->player_v.x < 0 || state->player_flipped) { + if(state->player_anim == 0) { + state->dolphin_gfx = I_WalkL1_32x32; + state->dolphin_gfx_b = I_WalkLB1_32x32; + + } else { + state->dolphin_gfx = I_WalkL2_32x32; + state->dolphin_gfx_b = I_WalkLB2_32x32; + } + } else if(state->player_v.x > 0 || !state->player_flipped) { + if(state->player_anim == 0) { + state->dolphin_gfx = I_WalkR1_32x32; + state->dolphin_gfx_b = I_WalkRB1_32x32; + + } else { + state->dolphin_gfx = I_WalkR2_32x32; + state->dolphin_gfx_b = I_WalkRB2_32x32; + } + } + } + + // zoom handlers + canvas_set_bitmap_mode(canvas, true); + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon_name(canvas, state->player.x, state->player.y, state->dolphin_gfx_b); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon_name(canvas, state->player.x, state->player.y, state->dolphin_gfx); + canvas_set_bitmap_mode(canvas, false); +} + +void dolphin_scene_handle_user_input(SceneState* state, InputEvent* input) { + furi_assert(state); + furi_assert(input); + + // dolphin_scene_debug + if(input->type == InputTypeShort) { + if(input->key == InputKeyUp) { + state->debug = !state->debug; + } + } + // toggle mind control on any user interaction + if(input->type == InputTypePress) { + if(input->key == InputKeyLeft || input->key == InputKeyRight || input->key == InputKeyOk) { + state->action = MINDCONTROL; + } + } + // zoom poc for tests + if(input->type == InputTypePress) { + if(input->key == InputKeyDown) { + state->zoom_v = SPEED_X; + } + } else if(input->type == InputTypeRelease) { + if(input->key == InputKeyDown) { + state->zoom_v = -SPEED_X * 2; + state->dialog_progress = 0; + } + } + // mind control + if(state->action == MINDCONTROL) { + if(input->type == InputTypePress) { + if(input->key == InputKeyRight) { + state->player_flipped = false; + state->player_v.x = SPEED_X; + } else if(input->key == InputKeyLeft) { + state->player_flipped = true; + state->player_v.x = -SPEED_X; + } + } else if(input->type == InputTypeRelease) { + if(input->key == InputKeyRight || input->key == InputKeyLeft) { + state->player_v.x = 0; + } + } else if(input->type == InputTypeShort) { + if(input->key == InputKeyOk) { + state->prev_action = MINDCONTROL; + state->action = INTERACT; + state->use_pending = true; + state->action_timeout = 0; + } + } + } +} + +void dolphin_scene_coordinates(SceneState* state, uint32_t dt) { + furi_assert(state); + + // global pos + state->player_global.x = CLAMP(state->player_global.x + state->player_v.x, WORLD_WIDTH, 0); + + // zoom handlers + state->scene_zoom = CLAMP(state->scene_zoom + state->zoom_v, SCENE_ZOOM, 0); + state->player.x = CLAMP(state->player.x - (state->zoom_v * (SPEED_X * 2)), DOLPHIN_CENTER, 0); + state->player.y = CLAMP(state->player.y - (state->zoom_v * SPEED_X / 2), DOLPHIN_DEFAULT_Y, 3); + + //center screen + state->screen.x = state->player_global.x - state->player.x; + state->player_anim = (state->player_global.x / 10) % 2; +} \ No newline at end of file diff --git a/applications/gui/canvas.c b/applications/gui/canvas.c index 0bb61b19..843f64be 100644 --- a/applications/gui/canvas.c +++ b/applications/gui/canvas.c @@ -250,3 +250,7 @@ void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) { y += canvas->offset_y; u8g2_DrawGlyph(&canvas->fb, x, y, ch); } + +void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) { + u8g2_SetBitmapMode(&canvas->fb, alpha ? 1 : 0); +} diff --git a/applications/gui/canvas.h b/applications/gui/canvas.h index c3a04ed5..0ed6be9f 100644 --- a/applications/gui/canvas.h +++ b/applications/gui/canvas.h @@ -147,6 +147,11 @@ void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t r); */ void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch); +/* + * Set transparency mode + */ +void canvas_set_bitmap_mode(Canvas* canvas, bool alpha); + #ifdef __cplusplus } #endif diff --git a/applications/gui/elements.c b/applications/gui/elements.c index 8881ff06..981bde2a 100644 --- a/applications/gui/elements.c +++ b/applications/gui/elements.c @@ -5,6 +5,7 @@ #include #include "canvas_i.h" #include +#include void elements_scrollbar(Canvas* canvas, uint8_t pos, uint8_t total) { furi_assert(canvas); diff --git a/applications/gui/elements.h b/applications/gui/elements.h index 421e7a50..4f765604 100644 --- a/applications/gui/elements.h +++ b/applications/gui/elements.h @@ -68,6 +68,14 @@ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* t */ void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text); +/* + * Draw framed multiline text + * @param x, y - top left corner coordinates + * @param text - string (possible multiline) + */ + +void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text); + /* * Draw slightly rounded frame * @param x, y - top left corner coordinates diff --git a/applications/gui/icon.c b/applications/gui/icon.c index a0cc2505..b3e58b39 100644 --- a/applications/gui/icon.c +++ b/applications/gui/icon.c @@ -48,6 +48,11 @@ bool icon_is_animated(Icon* icon) { return icon->data->frame_count > 1; } +bool icon_is_animating(Icon* icon) { + furi_assert(icon); + return icon->tick > 0; +} + void icon_start_animation(Icon* icon) { furi_assert(icon); icon->tick = osKernelGetTickCount(); diff --git a/applications/gui/icon.h b/applications/gui/icon.h index d9d14c94..73465023 100644 --- a/applications/gui/icon.h +++ b/applications/gui/icon.h @@ -36,6 +36,11 @@ uint8_t icon_get_height(Icon* icon); */ bool icon_is_animated(Icon* icon); +/* + * Check if icon animation is active + */ +bool icon_is_animating(Icon* icon); + /* * Start icon animation */ diff --git a/applications/passport/passport.c b/applications/passport/passport.c new file mode 100644 index 00000000..8973183f --- /dev/null +++ b/applications/passport/passport.c @@ -0,0 +1,122 @@ +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + union { + InputEvent input; + } value; + EventType type; +} AppEvent; + +typedef struct { +} State; + +static void input_callback(InputEvent* input_event, void* ctx) { + osMessageQueueId_t event_queue = ctx; + AppEvent event; + event.type = EventTypeKey; + event.value.input = *input_event; + osMessageQueuePut(event_queue, &event, 0, osWaitForever); +} + +static void render_callback(Canvas* canvas, void* ctx) { + State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25); + + /* + + char level[20]; + uint32_t current_level = dolphin_state_get_level(dolphin->state); + uint32_t prev_cap = dolphin_state_xp_to_levelup(dolphin->state, current_level - 1, false); + uint32_t exp = (dolphin_state_xp_to_levelup(dolphin->state, current_level, true) * 63) / + (dolphin_state_xp_to_levelup(dolphin->state, current_level, false) - prev_cap); + */ + canvas_clear(canvas); + + // multipass + canvas_draw_icon_name(canvas, 0, 0, I_PassportLeft_6x47); + canvas_draw_icon_name(canvas, 0, 47, I_PassportBottom_128x17); + canvas_draw_line(canvas, 6, 0, 125, 0); + canvas_draw_line(canvas, 127, 2, 127, 47); + canvas_draw_dot(canvas, 126, 1); + + //portrait frame + canvas_draw_line(canvas, 9, 6, 9, 53); + canvas_draw_line(canvas, 10, 5, 52, 5); + canvas_draw_line(canvas, 55, 8, 55, 53); + canvas_draw_line(canvas, 10, 54, 54, 54); + canvas_draw_line(canvas, 53, 5, 55, 7); + + // portrait + canvas_draw_icon_name(canvas, 14, 11, I_DolphinOkay_41x43); + canvas_draw_line(canvas, 59, 18, 124, 18); + canvas_draw_line(canvas, 59, 31, 124, 31); + canvas_draw_line(canvas, 59, 44, 124, 44); + + canvas_draw_str(canvas, 59, 15, api_hal_version_get_name_ptr()); + canvas_draw_str(canvas, 59, 28, "Mood: OK"); + /* + snprintf(level, 20, "Level: %ld", current_level); + + canvas_draw_str(canvas, 59, 41, level); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 123 - exp, 48, exp + 1, 6); + canvas_set_color(canvas, ColorBlack); + canvas_draw_line(canvas, 123 - exp, 48, 123 - exp, 54); + */ + + release_mutex((ValueMutex*)ctx, state); +} + +int32_t passport(void* p) { + State _state; + ValueMutex state_mutex; + + osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(AppEvent), NULL); + furi_check(event_queue); + + if(!init_mutex(&state_mutex, &_state, sizeof(State))) { + printf("cannot create mutex\r\n"); + return 0; + } + + ViewPort* view_port = view_port_alloc(); + + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + AppEvent event; + while(1) { + State* state = (State*)acquire_mutex_block(&state_mutex); + osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 25); + if(event_status == osOK) { + if(event.type == EventTypeKey && event.value.input.type == InputTypeShort) { + release_mutex(&state_mutex, state); + break; + } + } + + view_port_update(view_port); + release_mutex(&state_mutex, state); + } + + gui_remove_view_port(gui, view_port); + + view_port_free(view_port); + + furi_record_close("gui"); + + delete_mutex(&state_mutex); + + osMessageQueueDelete(event_queue); + + return 0; +} diff --git a/assets/icons/Animations/FX_Sitting_40x27/frame_0.png b/assets/icons/Animations/FX_Sitting_40x27/frame_0.png new file mode 100644 index 00000000..22e3d38d Binary files /dev/null and b/assets/icons/Animations/FX_Sitting_40x27/frame_0.png differ diff --git a/assets/icons/Animations/FX_Sitting_40x27/frame_1.png b/assets/icons/Animations/FX_Sitting_40x27/frame_1.png new file mode 100644 index 00000000..2438be9e Binary files /dev/null and b/assets/icons/Animations/FX_Sitting_40x27/frame_1.png differ diff --git a/assets/icons/Animations/FX_Sitting_40x27/frame_rate b/assets/icons/Animations/FX_Sitting_40x27/frame_rate new file mode 100644 index 00000000..9a037142 --- /dev/null +++ b/assets/icons/Animations/FX_Sitting_40x27/frame_rate @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/assets/icons/Animations/MDIB_32x32/frame_0.png b/assets/icons/Animations/MDIB_32x32/frame_0.png new file mode 100644 index 00000000..c8297922 Binary files /dev/null and b/assets/icons/Animations/MDIB_32x32/frame_0.png differ diff --git a/assets/icons/Animations/MDIB_32x32/frame_1.png b/assets/icons/Animations/MDIB_32x32/frame_1.png new file mode 100644 index 00000000..5ce5a67d Binary files /dev/null and b/assets/icons/Animations/MDIB_32x32/frame_1.png differ diff --git a/assets/icons/Animations/MDIB_32x32/frame_2.png b/assets/icons/Animations/MDIB_32x32/frame_2.png new file mode 100644 index 00000000..5ce5a67d Binary files /dev/null and b/assets/icons/Animations/MDIB_32x32/frame_2.png differ diff --git a/assets/icons/Animations/MDIB_32x32/frame_3.png b/assets/icons/Animations/MDIB_32x32/frame_3.png new file mode 100644 index 00000000..b5def708 Binary files /dev/null and b/assets/icons/Animations/MDIB_32x32/frame_3.png differ diff --git a/assets/icons/Animations/MDIB_32x32/frame_rate b/assets/icons/Animations/MDIB_32x32/frame_rate new file mode 100644 index 00000000..9a037142 --- /dev/null +++ b/assets/icons/Animations/MDIB_32x32/frame_rate @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/assets/icons/Animations/MDI_32x32/frame_0.png b/assets/icons/Animations/MDI_32x32/frame_0.png new file mode 100644 index 00000000..b71622f6 Binary files /dev/null and b/assets/icons/Animations/MDI_32x32/frame_0.png differ diff --git a/assets/icons/Animations/MDI_32x32/frame_1.png b/assets/icons/Animations/MDI_32x32/frame_1.png new file mode 100644 index 00000000..f58938a9 Binary files /dev/null and b/assets/icons/Animations/MDI_32x32/frame_1.png differ diff --git a/assets/icons/Animations/MDI_32x32/frame_2.png b/assets/icons/Animations/MDI_32x32/frame_2.png new file mode 100644 index 00000000..f58938a9 Binary files /dev/null and b/assets/icons/Animations/MDI_32x32/frame_2.png differ diff --git a/assets/icons/Animations/MDI_32x32/frame_3.png b/assets/icons/Animations/MDI_32x32/frame_3.png new file mode 100644 index 00000000..24fbf777 Binary files /dev/null and b/assets/icons/Animations/MDI_32x32/frame_3.png differ diff --git a/assets/icons/Animations/MDI_32x32/frame_rate b/assets/icons/Animations/MDI_32x32/frame_rate new file mode 100644 index 00000000..9a037142 --- /dev/null +++ b/assets/icons/Animations/MDI_32x32/frame_rate @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/assets/icons/Animations/MDWLB_32x32/frame_1.png b/assets/icons/Animations/MDWLB_32x32/frame_1.png new file mode 100644 index 00000000..e26b3a53 Binary files /dev/null and b/assets/icons/Animations/MDWLB_32x32/frame_1.png differ diff --git a/assets/icons/Animations/MDWLB_32x32/frame_2.png b/assets/icons/Animations/MDWLB_32x32/frame_2.png new file mode 100644 index 00000000..fa427cae Binary files /dev/null and b/assets/icons/Animations/MDWLB_32x32/frame_2.png differ diff --git a/assets/icons/Animations/MDWLB_32x32/frame_3.png b/assets/icons/Animations/MDWLB_32x32/frame_3.png new file mode 100644 index 00000000..ad269276 Binary files /dev/null and b/assets/icons/Animations/MDWLB_32x32/frame_3.png differ diff --git a/assets/icons/Animations/MDWLB_32x32/frame_rate b/assets/icons/Animations/MDWLB_32x32/frame_rate new file mode 100644 index 00000000..9a037142 --- /dev/null +++ b/assets/icons/Animations/MDWLB_32x32/frame_rate @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/assets/icons/Animations/MDWL_32x32/frame_1.png b/assets/icons/Animations/MDWL_32x32/frame_1.png new file mode 100644 index 00000000..4b0bd151 Binary files /dev/null and b/assets/icons/Animations/MDWL_32x32/frame_1.png differ diff --git a/assets/icons/Animations/MDWL_32x32/frame_2.png b/assets/icons/Animations/MDWL_32x32/frame_2.png new file mode 100644 index 00000000..fe8e32c3 Binary files /dev/null and b/assets/icons/Animations/MDWL_32x32/frame_2.png differ diff --git a/assets/icons/Animations/MDWL_32x32/frame_3.png b/assets/icons/Animations/MDWL_32x32/frame_3.png new file mode 100644 index 00000000..c63a48fe Binary files /dev/null and b/assets/icons/Animations/MDWL_32x32/frame_3.png differ diff --git a/assets/icons/Animations/MDWL_32x32/frame_rate b/assets/icons/Animations/MDWL_32x32/frame_rate new file mode 100644 index 00000000..9a037142 --- /dev/null +++ b/assets/icons/Animations/MDWL_32x32/frame_rate @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/assets/icons/Animations/MDWRB_32x32/frame_1.png b/assets/icons/Animations/MDWRB_32x32/frame_1.png new file mode 100644 index 00000000..be224443 Binary files /dev/null and b/assets/icons/Animations/MDWRB_32x32/frame_1.png differ diff --git a/assets/icons/Animations/MDWRB_32x32/frame_2.png b/assets/icons/Animations/MDWRB_32x32/frame_2.png new file mode 100644 index 00000000..3b70ca89 Binary files /dev/null and b/assets/icons/Animations/MDWRB_32x32/frame_2.png differ diff --git a/assets/icons/Animations/MDWRB_32x32/frame_3.png b/assets/icons/Animations/MDWRB_32x32/frame_3.png new file mode 100644 index 00000000..444dceae Binary files /dev/null and b/assets/icons/Animations/MDWRB_32x32/frame_3.png differ diff --git a/assets/icons/Animations/MDWRB_32x32/frame_rate b/assets/icons/Animations/MDWRB_32x32/frame_rate new file mode 100644 index 00000000..9a037142 --- /dev/null +++ b/assets/icons/Animations/MDWRB_32x32/frame_rate @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/assets/icons/Animations/MDWR_32x32/frame_1.png b/assets/icons/Animations/MDWR_32x32/frame_1.png new file mode 100644 index 00000000..d2e355a1 Binary files /dev/null and b/assets/icons/Animations/MDWR_32x32/frame_1.png differ diff --git a/assets/icons/Animations/MDWR_32x32/frame_2.png b/assets/icons/Animations/MDWR_32x32/frame_2.png new file mode 100644 index 00000000..522f1217 Binary files /dev/null and b/assets/icons/Animations/MDWR_32x32/frame_2.png differ diff --git a/assets/icons/Animations/MDWR_32x32/frame_3.png b/assets/icons/Animations/MDWR_32x32/frame_3.png new file mode 100644 index 00000000..b01e463e Binary files /dev/null and b/assets/icons/Animations/MDWR_32x32/frame_3.png differ diff --git a/assets/icons/Animations/MDWR_32x32/frame_rate b/assets/icons/Animations/MDWR_32x32/frame_rate new file mode 100644 index 00000000..9a037142 --- /dev/null +++ b/assets/icons/Animations/MDWR_32x32/frame_rate @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/assets/icons/Dolphin/FX_Bang_32x6.png b/assets/icons/Dolphin/FX_Bang_32x6.png new file mode 100644 index 00000000..d842e48f Binary files /dev/null and b/assets/icons/Dolphin/FX_Bang_32x6.png differ diff --git a/assets/icons/Dolphin/FX_SittingB_40x27.png b/assets/icons/Dolphin/FX_SittingB_40x27.png new file mode 100644 index 00000000..e9565111 Binary files /dev/null and b/assets/icons/Dolphin/FX_SittingB_40x27.png differ diff --git a/assets/icons/Scenes/Home_painting_17x20.png b/assets/icons/Scenes/Home_painting_17x20.png new file mode 100644 index 00000000..67322436 Binary files /dev/null and b/assets/icons/Scenes/Home_painting_17x20.png differ diff --git a/assets/icons/Scenes/PC_22x29.png b/assets/icons/Scenes/PC_22x29.png new file mode 100644 index 00000000..644b5148 Binary files /dev/null and b/assets/icons/Scenes/PC_22x29.png differ diff --git a/assets/icons/Scenes/Sofa_40x13.png b/assets/icons/Scenes/Sofa_40x13.png new file mode 100644 index 00000000..60695520 Binary files /dev/null and b/assets/icons/Scenes/Sofa_40x13.png differ diff --git a/assets/icons/Scenes/TV_20x20.png b/assets/icons/Scenes/TV_20x20.png new file mode 100644 index 00000000..9af8d1b6 Binary files /dev/null and b/assets/icons/Scenes/TV_20x20.png differ diff --git a/assets/icons/Scenes/TV_20x24.png b/assets/icons/Scenes/TV_20x24.png new file mode 100644 index 00000000..924e26fa Binary files /dev/null and b/assets/icons/Scenes/TV_20x24.png differ diff --git a/assets/icons/Scenes/WalkL1_32x32.png b/assets/icons/Scenes/WalkL1_32x32.png new file mode 100644 index 00000000..fe8e32c3 Binary files /dev/null and b/assets/icons/Scenes/WalkL1_32x32.png differ diff --git a/assets/icons/Scenes/WalkL2_32x32.png b/assets/icons/Scenes/WalkL2_32x32.png new file mode 100644 index 00000000..c63a48fe Binary files /dev/null and b/assets/icons/Scenes/WalkL2_32x32.png differ diff --git a/assets/icons/Scenes/WalkLB1_32x32.png b/assets/icons/Scenes/WalkLB1_32x32.png new file mode 100644 index 00000000..fa427cae Binary files /dev/null and b/assets/icons/Scenes/WalkLB1_32x32.png differ diff --git a/assets/icons/Scenes/WalkLB2_32x32.png b/assets/icons/Scenes/WalkLB2_32x32.png new file mode 100644 index 00000000..ad269276 Binary files /dev/null and b/assets/icons/Scenes/WalkLB2_32x32.png differ diff --git a/assets/icons/Scenes/WalkR1_32x32.png b/assets/icons/Scenes/WalkR1_32x32.png new file mode 100644 index 00000000..522f1217 Binary files /dev/null and b/assets/icons/Scenes/WalkR1_32x32.png differ diff --git a/assets/icons/Scenes/WalkR2_32x32.png b/assets/icons/Scenes/WalkR2_32x32.png new file mode 100644 index 00000000..b01e463e Binary files /dev/null and b/assets/icons/Scenes/WalkR2_32x32.png differ diff --git a/assets/icons/Scenes/WalkRB1_32x32.png b/assets/icons/Scenes/WalkRB1_32x32.png new file mode 100644 index 00000000..3b70ca89 Binary files /dev/null and b/assets/icons/Scenes/WalkRB1_32x32.png differ diff --git a/assets/icons/Scenes/WalkRB2_32x32.png b/assets/icons/Scenes/WalkRB2_32x32.png new file mode 100644 index 00000000..444dceae Binary files /dev/null and b/assets/icons/Scenes/WalkRB2_32x32.png differ