diff --git a/controller/hid-test.py b/controller/hid-test.py index 7a8c338..0bac34b 100755 --- a/controller/hid-test.py +++ b/controller/hid-test.py @@ -23,6 +23,9 @@ def main(): h.match_found(RiotPlayerInfo.dummy(valorant=ValorantPlayerInfo())) h.pregame(RiotPlayerInfo.dummy(valorant=ValorantPlayerInfo())) h.game_generic(RiotPlayerInfo.dummy(valorant=ValorantPlayerInfo(queue_type='ggteam'))) + h.game_start(None) + h.game_over(None, False) + h.game_over(None, True) h.service() finally: diff --git a/controller/valconomy.py b/controller/valconomy.py index 6ff46e7..412166a 100644 --- a/controller/valconomy.py +++ b/controller/valconomy.py @@ -337,6 +337,12 @@ class HIDValconomyHandler(ValconomyHandler): self._enq(6, gm, fmt='B') + def game_start(self, info: RiotPlayerInfo): + self._enq(7) + + def game_over(self, info: RiotPlayerInfo, won: bool): + self._enq(8, 1 if won else 0, fmt='B') + class GameState(Enum): NONE = 0 MENU = 1 diff --git a/firmware/main/ui.c b/firmware/main/ui.c index 31f89e9..6021f2b 100644 --- a/firmware/main/ui.c +++ b/firmware/main/ui.c @@ -15,7 +15,7 @@ const char *val_ext_gamemodes[] = { static const lv_font_t *font_normal = &lv_font_montserrat_24; static lv_color_t color_primary, color_secondary; -static lv_color_t color_text_hero, color_text_subtitle; +static lv_color_t color_text_hero, color_text_subtitle, color_text_victory; static lv_style_t s_hero, s_subtitle; @@ -74,6 +74,7 @@ static void setup_next_state() { lv_obj_set_style_bg_opa(o_active, LV_OPA_0, 0); lv_obj_set_style_border_width(o_active, 0, 0); lv_obj_set_size(o_active, lv_pct(100), lv_pct(100)); + lv_obj_set_scrollbar_mode(o_active, LV_SCROLLBAR_MODE_OFF); at_active = lv_anim_timeline_create(); at_active_rep = lv_anim_timeline_create(); @@ -201,7 +202,7 @@ void val_ui_menu(bool was_idle) { lv_anim_set_var(&a_sub, l_subtitle); lv_anim_set_values( &a_sub, - (lv_obj_get_height(o_container) + lv_obj_get_height(l_main)) / 2, + (lv_obj_get_height(o_container) + lv_obj_get_height(l_subtitle)) / 2, (lv_obj_get_height(l_main) + lv_obj_get_height(l_subtitle)) / 2 + 5); lv_anim_set_exec_cb(&a_sub, anim_y_cb); lv_anim_set_path_cb(&a_sub, lv_anim_path_ease_in); @@ -496,12 +497,105 @@ void val_ui_game_generic(const char *gamemode) { lv_anim_timeline_start(at_active); } +void val_ui_game_start() { + setup_next_state(); + + // Widgets + lv_obj_t *l_main = lv_label_create(o_active); + lv_obj_add_style(l_main, &s_hero, 0); + lv_obj_center(l_main); + lv_label_set_text_static(l_main, "GOOD LUCK!"); + + state_ready = true; +} + +void val_ui_game_over(bool won) { + setup_next_state(); + + // Widgets + lv_obj_t *l_main = lv_label_create(o_active); + lv_obj_add_style(l_main, &s_hero, 0); + lv_obj_center(l_main); + lv_label_set_text_static(l_main, won ? "VICTORY" : "DEFEAT"); + if (won) lv_obj_set_style_text_color(l_main, color_text_victory, 0); + + lv_obj_t *l_subtitle = lv_label_create(o_active); + lv_obj_add_style(l_subtitle, &s_subtitle, 0); + lv_obj_center(l_subtitle); + lv_label_set_text_static(l_subtitle, won ? "WELL PLAYED!" : "HARD LUCK..."); + + lv_obj_update_layout(o_active); + lv_obj_set_y( + l_subtitle, + (lv_obj_get_height(l_main) + lv_obj_get_height(l_subtitle)) / 2 + 5); + + // Animations + if (won) { + lv_anim_t a_fly; + lv_anim_init(&a_fly); + lv_anim_set_path_cb(&a_fly, lv_anim_path_ease_out); + lv_anim_set_duration(&a_fly, 750); + + // Main text from top-left + // Y + lv_anim_set_var(&a_fly, l_main); + lv_anim_set_exec_cb(&a_fly, anim_y_cb); + lv_anim_set_values( + &a_fly, + -(lv_obj_get_height(o_container) + lv_obj_get_height(l_main)) / 2, 0); + lv_anim_timeline_add(at_active, 0, &a_fly); + + // X + lv_anim_set_exec_cb(&a_fly, anim_x_cb); + lv_anim_set_values( + &a_fly, + -(lv_obj_get_width(o_container) + lv_obj_get_width(l_main)) / 2, 0); + lv_anim_timeline_add(at_active, 0, &a_fly); + + + // Subtitle from bottom right + // Y + lv_anim_set_var(&a_fly, l_subtitle); + lv_anim_set_exec_cb(&a_fly, anim_y_cb); + lv_anim_set_values( + &a_fly, + (lv_obj_get_height(o_container) + lv_obj_get_height(l_subtitle)) / 2, + (lv_obj_get_height(l_main) + lv_obj_get_height(l_subtitle)) / 2 + 5); + lv_anim_timeline_add(at_active, 0, &a_fly); + + // X + lv_anim_set_exec_cb(&a_fly, anim_x_cb); + lv_anim_set_values( + &a_fly, + (lv_obj_get_width(o_container) + lv_obj_get_width(l_subtitle)) / 2, 0); + lv_anim_set_completed_cb(&a_fly, anim_state_ready_cb); + lv_anim_timeline_add(at_active, 0, &a_fly); + } else { + lv_anim_t a_fade_in; + lv_anim_init(&a_fade_in); + lv_anim_set_values(&a_fade_in, 0, 255); + lv_anim_set_exec_cb(&a_fade_in, anim_opa_cb); + lv_anim_set_path_cb(&a_fade_in, lv_anim_path_ease_in); + lv_anim_set_duration(&a_fade_in, 1000); + + lv_anim_set_var(&a_fade_in, l_main); + lv_anim_timeline_add(at_active, 0, &a_fade_in); + + lv_anim_set_var(&a_fade_in, l_subtitle); + lv_anim_set_completed_cb(&a_fade_in, anim_state_ready_cb); + lv_anim_timeline_add(at_active, 0, &a_fade_in); + } + + lv_anim_timeline_start(at_active); +} + void val_lvgl_ui(lv_display_t *disp) { color_primary = lv_color_hex(0xff4655); color_secondary = lv_color_hex(0xf7518f); color_text_hero = lv_palette_lighten(LV_PALETTE_GREY, 2); color_text_subtitle = lv_palette_darken(LV_PALETTE_GREY, 1); + color_text_victory = lv_palette_lighten(LV_PALETTE_GREEN, 1); // init default theme lv_theme_default_init( @@ -532,6 +626,7 @@ void val_lvgl_ui(lv_display_t *disp) { lv_obj_set_style_border_width(o_container, 0, 0); lv_obj_set_style_pad_all(o_container, 0, 0); lv_obj_set_size(o_container, lv_pct(100), lv_pct(100)); + lv_obj_set_scrollbar_mode(o_container, LV_SCROLLBAR_MODE_OFF); // Settings button lv_obj_t *b_cfg = lv_button_create(lv_screen_active()); diff --git a/firmware/main/ui.h b/firmware/main/ui.h index 5293328..6a5fa11 100644 --- a/firmware/main/ui.h +++ b/firmware/main/ui.h @@ -22,5 +22,7 @@ void val_ui_queue_start(bool ms_not_comp); void val_ui_match_found(bool is_premier); void val_ui_pregame(bool is_split); void val_ui_game_generic(const char *gamemode); +void val_ui_game_start(); +void val_ui_game_over(bool won); void val_lvgl_ui(lv_display_t *disp); diff --git a/firmware/main/usb.c b/firmware/main/usb.c index 10a1b99..876073d 100644 --- a/firmware/main/usb.c +++ b/firmware/main/usb.c @@ -101,7 +101,7 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ ESP_LOGE(TAG, "Failed to grab LVGL lock"); return; } - if (buf[0] > ST_GAME_GENERIC) { + if (buf[0] > ST_GAME_OVER) { ESP_LOGW(TAG, "Unknown state %hhu", buf[0]); goto ret; } @@ -151,6 +151,16 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_ } val_ui_game_generic(val_ext_gamemodes[buf[1]]); break; + case ST_GAME_START: + val_ui_game_start(); + break; + case ST_GAME_OVER: + if (bufsize < 2) { + ESP_LOGE(TAG, "Invalid ST_GAME_OVER command"); + goto ret; + } + val_ui_game_over((bool)buf[1]); + break; } ret: diff --git a/firmware/main/usb.h b/firmware/main/usb.h index 26334a2..8f4ae25 100644 --- a/firmware/main/usb.h +++ b/firmware/main/usb.h @@ -13,6 +13,8 @@ typedef enum val_state { ST_MATCH_FOUND, ST_PREGAME, ST_GAME_GENERIC, + ST_GAME_START, + ST_GAME_OVER, } val_state_t; void val_usb_init(void);