#include "menu_cmdr.h" #include "menus.h" #include "config.h" #include "state.h" #include "draw.h" #include "game.h" #include "ble.h" #include "events.h" #include // ── Offline vtable ──────────────────────────────────────────────────────────── static const uint8_t s_icon_shield[2][ICON_W] = { // Shield (commander damage) {0x00,0xFC,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFC,0x00,0x00}, {0x00,0x07,0x0F,0x1F,0x3F,0x3F,0x3F,0x7F,0x7F,0x3F,0x3F,0x1F,0x0F,0x07,0x00,0x00}, }; static const uint8_t s_icon_net[2][ICON_W] = { // Signal bars (BLE commander) {0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0xF8,0xF8,0x00,0xFE,0xFE,0x00,0x00}, {0x00,0x00,0x00,0x38,0x38,0x00,0x3F,0x3F,0x00,0x3F,0x3F,0x00,0x3F,0x3F,0x00,0x00}, }; static void cmdr_offline_nav_short(int d) { g_active_opponent = (g_active_opponent + d + g_num_opponents) % g_num_opponents; } static void cmdr_offline_lr_delta(int d) { g_cmdr_damage[g_active_opponent] += d; if (g_cmdr_damage[g_active_opponent] < 0) g_cmdr_damage[g_active_opponent] = 0; check_elimination(); mark_dirty(); } static void cmdr_offline_draw(void) { char opponent_labels[MAX_OPPONENTS][PLAYER_NAME_LEN + 1]; const char *opponent_label_ptrs[MAX_OPPONENTS]; for (int i = 0; i < g_num_opponents; i++) { snprintf(opponent_labels[i], sizeof(opponent_labels[i]), "CMD %d", i + 1); opponent_label_ptrs[i] = opponent_labels[i]; } oled_draw_list(opponent_label_ptrs, g_cmdr_damage, g_num_opponents, g_active_opponent); } const menu_vtable_t menu_cmdr_offline_vtable = { .nav_short = cmdr_offline_nav_short, .lr_delta = cmdr_offline_lr_delta, .draw = cmdr_offline_draw, .on_tick = NULL, .icon = s_icon_shield, }; // ── BLE vtable ──────────────────────────────────────────────────────────────── static void cmdr_ble_nav_short(int d) { int total = MAX_BLE_PEERS + 1; int next = (g_active_player + d + total) % total; while (next != g_active_player && next != 0 && !g_peers[next - 1].active) next = (next + d + total) % total; g_active_player = next; } static void cmdr_ble_lr_delta(int d) { if (g_active_player == 0) { g_life += d; g_life_delta += d; restart_life_delta_timer(); check_elimination(); } else { int slot = g_active_player - 1; g_cmdr_damage[slot] += d; if (g_cmdr_damage[slot] < 0) g_cmdr_damage[slot] = 0; check_elimination(); } mark_dirty(); } static void cmdr_ble_draw(void) { oled_draw_players(); } const menu_vtable_t menu_cmdr_ble_vtable = { .nav_short = cmdr_ble_nav_short, .lr_delta = cmdr_ble_lr_delta, .draw = cmdr_ble_draw, .on_tick = NULL, .icon = s_icon_net, };