diff --git a/src/c/main.c b/src/c/main.c index 9039d6f..7d19a24 100644 --- a/src/c/main.c +++ b/src/c/main.c @@ -72,7 +72,7 @@ static uint8_t s_selected_indices[3] = {9, 9, 9}; static uint8_t s_secs_since_last_input; static uint8_t s_shake_cooldown; static uint8_t s_zap_cooldown; -static uint8_t s_countdown_int = 0; +static uint8_t s_countdown_int; static bool s_continuous_zap_on_first_triangle; static char s_countdown_buf[4]; static char s_progress_buf[8]; @@ -107,8 +107,11 @@ static const VibePattern s_first_triangle_vibe_pattern = { }; // animation stuff +#define ANIMATION_DURATION 350 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); +static bool s_door_correct; +static Animation *s_door_anim; // alarm drop menu options static const char *s_options_function[3]; @@ -515,54 +518,69 @@ static void door_layer_green_update_proc(Layer *layer, GContext *ctx) { 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) { +static void door_closed_animation_stopped_handler(Animation *a, bool finished, void *ctx) { + if (!finished) + return; + if (s_door_correct) + s_current_triangle_num++; + else 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 door_animation_stopped_handler(Animation *a, bool finished, void *ctx) { + animation_destroy(a); + s_door_anim = NULL; } -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); + // prevent from playing if an instance is already running and not yet finished; + // prevents instances where the user is punished if the timer expires AFTER the submission + if (s_door_anim) { + return; } - 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); -}; + + // reset shake cooldown here so it is guaranteed + // to be in effect while an animation is playing + s_shake_cooldown = SHAKE_COOLDOWN_SECS; + + // ensure door layers are correct color + s_door_correct = correct; + layer_set_update_proc(s_left_door_layer, correct ? door_layer_green_update_proc : door_layer_red_update_proc); + layer_set_update_proc(s_right_door_layer, correct ? door_layer_green_update_proc : door_layer_red_update_proc); + + // create base close animations + PropertyAnimation *l = 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 *r = 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 *lc = property_animation_get_animation(l), *rc = property_animation_get_animation(r); + animation_set_duration(lc, ANIMATION_DURATION); + animation_set_duration(rc, ANIMATION_DURATION); + + // duplicate and reverse for open animations + Animation *lo = animation_clone(lc), *ro = animation_clone(rc); + animation_set_reverse(lo, true); + animation_set_reverse(ro, true); + + // create spawn animations + Animation *close = animation_spawn_create(lc, rc, NULL); + Animation *open = animation_spawn_create(lo, ro, NULL); + animation_set_delay(open, 100); + + // set door close handler + animation_set_handlers(close, (AnimationHandlers){.stopped = door_closed_animation_stopped_handler}, NULL); + + // create sequence + s_door_anim = animation_sequence_create(close, open, NULL); + + // set stopped handler; + // even though the SDK auto-destroys after finished, we destroy manually + // since we already need a stopped handler to NULL it for the check in alarm_window_unload() + animation_set_handlers(s_door_anim, (AnimationHandlers){.stopped = door_animation_stopped_handler}, NULL); + + // play animation sequence; + animation_schedule(s_door_anim); +} static void increment_secs_since_last_input() { if (s_secs_since_last_input < MAX_SECS_WITHOUT_INPUT) { @@ -674,7 +692,8 @@ static void accel_tap_handler(AccelAxisType axis, int32_t direction) { if (s_countdown_int > 0) { if (s_current_triangle_num == settings.TriangleCount) { - tick_timer_service_unsubscribe(); // alarm is fully disarmed - prevent stimulus in confirmation window + tick_timer_service_unsubscribe(); // prevent stimulus in confirmation window + accel_tap_service_unsubscribe(); // don't listen for shakes in confirmation window wakeup_service_subscribe(NULL); // ignore wakeup events to avoid overwriting new alarm if (schedule_alarm(WAKE_ALARM)) { push_status_window(STATUS_CONFIRMATION); @@ -683,8 +702,6 @@ static void accel_tap_handler(AccelAxisType axis, int32_t direction) { play_door_animation(true); } } - - s_shake_cooldown = SHAKE_COOLDOWN_SECS; } static void alarm_click_config_provider(void *ctx) { @@ -913,6 +930,12 @@ static void alarm_window_load() { static void alarm_window_unload() { tick_timer_service_unsubscribe(); accel_tap_service_unsubscribe(); + if (s_door_anim) { + // unschedule and destroy if unload occurs while an animation is running; + // e.g. if the alarm reset button is used from the phone-side + animation_destroy(s_door_anim); + s_door_anim = NULL; + } layer_destroy(s_right_door_layer); layer_destroy(s_left_door_layer); text_layer_destroy(s_progress_txt_layer);