From 00456f82244a1d532f80253d5431b37f15b1b081 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Wed, 11 Nov 2026 00:10:28 -0500 Subject: [PATCH] Initial triangle swap animations (+demote on incorrect answers) --- package.json | 7 --- resources/x.png | Bin 5091 -> 0 bytes src/c/main.c | 122 ++++++++++++++++++++++++++++++------------------ 3 files changed, 76 insertions(+), 53 deletions(-) delete mode 100644 resources/x.png diff --git a/package.json b/package.json index 67e24aa..996a5be 100644 --- a/package.json +++ b/package.json @@ -58,13 +58,6 @@ "spaceOptimization": "storage", "type": "bitmap" }, - { - "file": "x.png", - "memoryFormat": "Smallest", - "name": "X", - "spaceOptimization": "storage", - "type": "bitmap" - }, { "file": "check.png", "memoryFormat": "Smallest", diff --git a/resources/x.png b/resources/x.png deleted file mode 100644 index b5bdd33633029ad6f8db09d06a54f6ae3dade701..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5091 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 O4xpg|Q!Z6B3;I7HI6vtC diff --git a/src/c/main.c b/src/c/main.c index 3cf46a3..6d0a1f5 100644 --- a/src/c/main.c +++ b/src/c/main.c @@ -23,8 +23,6 @@ static BitmapLayer *s_check_bmp_layer; static TextLayer *s_setter_txt_layer; static Layer *s_setter_highlight_layer; //// alarm -static BitmapLayer *s_x_bmp_layer; -static Animation *s_x_anim_master; static Layer *s_graph_layer; static TextLayer *s_theta_txt_layer; static TextLayer *s_parenthesis_txt_layer; @@ -44,6 +42,8 @@ static TextLayer *s_progress_txt_layer; static TextLayer *s_side_a_txt_layer; static TextLayer *s_side_b_txt_layer; static TextLayer *s_side_c_txt_layer; +static Layer *s_left_door_layer; +static Layer *s_right_door_layer; // fonts GFont s_font_lecoish_small; @@ -57,8 +57,6 @@ GFont s_font_mansalva_small; //// setter static GBitmap *s_clock_bmp; static GBitmap *s_check_bmp; -//// alarm -static GBitmap *s_x_bmp; // global vars static WakeupReason s_alarm_reason; @@ -91,6 +89,7 @@ static const GColor s_primary_color = GColorJaegerGreen; #define MAX_SECS_WITHOUT_INPUT 10 #define SHAKE_COOLDOWN_SECS 2 #define ZAP_COOLDOWN_SECS 4 +static const uint8_t s_small_top_bar_height = s_top_bar_grect.size.h / 2; static const uint8_t s_drop_height = 22; static const GRect s_drop_function_grect = GRect(PBL_IF_ROUND_ELSE(55, 45), s_bottom_bar_grect.origin.y + ((s_bottom_bar_grect.size.h / 2) - PBL_IF_ROUND_ELSE(s_drop_height - 1, s_drop_height / 2)), 50, s_drop_height); static const GRect s_drop_over_grect = GRect(s_drop_function_grect.origin.x + 78, s_drop_function_grect.origin.y - 13, s_drop_function_grect.size.w + 1, s_drop_function_grect.size.h); @@ -107,6 +106,10 @@ static const VibePattern s_first_triangle_vibe_pattern = { .num_segments = ARRAY_LENGTH(s_first_triangle_vibe_segments), }; +// animation stuff +static GRect s_left_door_starting_grect = GRect(-(PBL_DISPLAY_WIDTH / 2), s_small_top_bar_height, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_HEIGHT - s_small_top_bar_height - s_bottom_bar_grect.size.h); +static GRect s_right_door_starting_grect = GRect(PBL_DISPLAY_WIDTH, s_small_top_bar_height, PBL_DISPLAY_WIDTH / 2, PBL_DISPLAY_HEIGHT - s_small_top_bar_height - s_bottom_bar_grect.size.h); + // alarm drop menu options static const char *s_options_function[3]; static char *s_options_over[2]; @@ -371,7 +374,8 @@ static void set_alarm_highlighter_frame() { } } -static void clear_selections() { +static void update_for_new_triangle() { + // clear selections for (int i = 0; i < 3; ++i) { s_selected_indices[i] = 9; } @@ -380,10 +384,6 @@ static void clear_selections() { text_layer_set_text(s_drop_txt_function_layer, ""); text_layer_set_text(s_drop_txt_over_layer, ""); text_layer_set_text(s_drop_txt_under_layer, ""); -} - -static void update_for_new_triangle() { - clear_selections(); // pick & display 2 side lengths to the user static char side_a_buffer[8], side_b_buffer[8], side_c_buffer[8]; @@ -505,6 +505,65 @@ static void update_for_new_triangle() { text_layer_set_text(s_progress_txt_layer, s_progress_buf); } +static void door_layer_red_update_proc(Layer *layer, GContext *ctx) { + graphics_context_set_fill_color(ctx, GColorMelon); + graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone); +} + +static void door_layer_green_update_proc(Layer *layer, GContext *ctx) { + graphics_context_set_fill_color(ctx, GColorMediumAquamarine); + graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone); +} + +static void door_closed_fail_animation_handler(Animation *animation, bool finished, void *context) { + if (s_current_triangle_num > 1) { + s_current_triangle_num--; + } + right_triangle_layer_regen(s_triangle_layer); + update_for_new_triangle(); +} + +static void door_closed_success_animation_handler(Animation *animation, bool finished, void *context) { + s_current_triangle_num++; + right_triangle_layer_regen(s_triangle_layer); + update_for_new_triangle(); +} + +static void destroy_animation_handler(Animation *animation, bool finished, void *context) { + animation_destroy(animation); +} + +// TODO: +// reuse some of these animations, if possible +// add iconography to door halves +static void play_door_animation(bool correct) { + PropertyAnimation *left_door_prop_anim = property_animation_create_layer_frame(s_left_door_layer, &s_left_door_starting_grect, &GRect(0, s_left_door_starting_grect.origin.y, s_left_door_starting_grect.size.w, s_left_door_starting_grect.size.h)); + PropertyAnimation *right_door_prop_anim = property_animation_create_layer_frame(s_right_door_layer, &s_right_door_starting_grect, &GRect(PBL_DISPLAY_WIDTH / 2, s_left_door_starting_grect.origin.y, s_left_door_starting_grect.size.w, s_left_door_starting_grect.size.h)); + Animation *left_door_close_anim = property_animation_get_animation(left_door_prop_anim); + Animation *right_door_close_anim = property_animation_get_animation(right_door_prop_anim); + animation_set_duration(left_door_close_anim, 500); + animation_set_duration(right_door_close_anim, 500); + Animation *left_door_open_anim = animation_clone(left_door_close_anim); + Animation *right_door_open_anim = animation_clone(right_door_close_anim); + animation_set_reverse(left_door_open_anim, true); + animation_set_reverse(right_door_open_anim, true); + Animation *door_close_spawn_anim = animation_spawn_create(left_door_close_anim, right_door_close_anim, NULL); + Animation *door_open_spawn_anim = animation_spawn_create(left_door_open_anim, right_door_open_anim, NULL); + animation_set_delay(door_open_spawn_anim, 100); + if (correct) { + layer_set_update_proc(s_left_door_layer, door_layer_green_update_proc); + layer_set_update_proc(s_right_door_layer, door_layer_green_update_proc); + animation_set_handlers(door_close_spawn_anim, (AnimationHandlers){.stopped = door_closed_success_animation_handler}, NULL); + } else { + layer_set_update_proc(s_left_door_layer, door_layer_red_update_proc); + layer_set_update_proc(s_right_door_layer, door_layer_red_update_proc); + animation_set_handlers(door_close_spawn_anim, (AnimationHandlers){.stopped = door_closed_fail_animation_handler}, NULL); + } + Animation *seq = animation_sequence_create(door_close_spawn_anim, door_open_spawn_anim, NULL); + animation_set_handlers(seq, (AnimationHandlers){.stopped = destroy_animation_handler}, NULL); + animation_schedule(seq); +}; + static void increment_secs_since_last_input() { if (s_secs_since_last_input < MAX_SECS_WITHOUT_INPUT) { s_secs_since_last_input++; @@ -536,12 +595,8 @@ static void alarm_tick_handler(struct tm *tick_time, TimeUnits units_changed) { if (s_countdown_int == 0) { s_continuous_zap_on_first_triangle = true; // begin zapping once the first countdown expires send_zap(); - if (s_current_triangle_num > 1) { - s_current_triangle_num--; - } s_secs_since_last_input = 10; - right_triangle_layer_regen(s_triangle_layer); - update_for_new_triangle(); + play_door_animation(false); } // minutes @@ -612,8 +667,7 @@ static void accel_tap_handler(AccelAxisType axis, int32_t direction) { // check solution for (int i = 0; i < 3; ++i) { if (s_selected_indices[i] != s_solution_indices[i]) { - animation_schedule(animation_clone(s_x_anim_master)); - clear_selections(); + play_door_animation(false); return; } } @@ -626,9 +680,7 @@ static void accel_tap_handler(AccelAxisType axis, int32_t direction) { push_status_window(STATUS_CONFIRMATION); } } else { - s_current_triangle_num++; - right_triangle_layer_regen(s_triangle_layer); - update_for_new_triangle(); + play_door_animation(true); } } @@ -705,16 +757,6 @@ static void live_wakeup_handler_for_alarm(WakeupId id, int32_t wakeup_reason) { } } -static void anim_handler_flash_x_start(Animation *animation, void *context) { - layer_set_hidden(bitmap_layer_get_layer(s_x_bmp_layer), false); -} -static void anim_handler_flash_x_end(Animation *animation, bool finished, void *context) { - layer_set_hidden(bitmap_layer_get_layer(s_x_bmp_layer), true); - animation_destroy(animation); -} -static void anim_update_flash_x(Animation *animation, const AnimationProgress progress) {} // dummy -static const AnimationImplementation s_x_anim_impl = {.update = anim_update_flash_x}; - static void alarm_window_load() { wakeup_service_subscribe(live_wakeup_handler_for_alarm); @@ -795,7 +837,6 @@ static void alarm_window_load() { layer_add_child(window_layer, s_alarm_highlight_layer); // triangle - static const uint8_t s_small_top_bar_height = s_top_bar_grect.size.h / 2; s_triangle_layer = right_triangle_layer_create(GRect(0, s_small_top_bar_height, PBL_DISPLAY_WIDTH, PBL_DISPLAY_HEIGHT - s_small_top_bar_height - s_bottom_bar_grect.size.h), GColorBlack); layer_add_child(window_layer, s_triangle_layer->layer); @@ -850,19 +891,11 @@ static void alarm_window_load() { text_layer_set_font(s_progress_txt_layer, fonts_get_system_font(FONT_KEY_LECO_20_BOLD_NUMBERS)); layer_add_child(window_layer, text_layer_get_layer(s_progress_txt_layer)); - // x (incorrect equation icon) - s_x_bmp = gbitmap_create_with_resource(RESOURCE_ID_X); - s_x_bmp_layer = bitmap_layer_create(GRect(20, 24, 160, 179)); - bitmap_layer_set_bitmap(s_x_bmp_layer, s_x_bmp); - bitmap_layer_set_compositing_mode(s_x_bmp_layer, GCompOpSet); - layer_set_hidden(bitmap_layer_get_layer(s_x_bmp_layer), true); // hide until needed - layer_add_child(window_layer, bitmap_layer_get_layer(s_x_bmp_layer)); - - // x flash animation init - s_x_anim_master = animation_create(); - animation_set_implementation(s_x_anim_master, &s_x_anim_impl); - animation_set_duration(s_x_anim_master, 1500); - animation_set_handlers(s_x_anim_master, (AnimationHandlers){.started = anim_handler_flash_x_start, .stopped = anim_handler_flash_x_end}, NULL); + // door layers + s_left_door_layer = layer_create(s_left_door_starting_grect); + s_right_door_layer = layer_create(s_right_door_starting_grect); + layer_add_child(window_layer, s_left_door_layer); + layer_add_child(window_layer, s_right_door_layer); // configure UI around current triangle in s_triangle_layer s_current_triangle_num = 1; @@ -880,9 +913,6 @@ static void alarm_window_load() { static void alarm_window_unload() { tick_timer_service_unsubscribe(); accel_tap_service_unsubscribe(); - animation_destroy(s_x_anim_master); - gbitmap_destroy(s_x_bmp); - bitmap_layer_destroy(s_x_bmp_layer); text_layer_destroy(s_progress_txt_layer); text_layer_destroy(s_countdown_txt_layer); window_destroy(s_drop_window);