From 8ba97262a0219d7defd928d7bfb5f4214f235fc8 Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 27 Oct 2026 18:36:31 +0100 Subject: GUI: fix footer clipping; remove the old text menu - Right-align the footer hint and size the config label from the remaining width, so "INS close" is no longer truncated. - Delete the text-tree menu now that the GUI has feature parity: remove the tree/input/render code, the F1 toggle and menu_force_close. - Keep only held-key polling for feature binds; poll it every frame from outer_draw. - Update CONTEXT/ADR: the GUI is the only menu. --- src/menu.cpp | 359 ++--------------------------------------------------------- 1 file changed, 12 insertions(+), 347 deletions(-) (limited to 'src/menu.cpp') diff --git a/src/menu.cpp b/src/menu.cpp index 2a96b02..5fc325e 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -1,4 +1,4 @@ -// menu.cpp — menu state, rendering, input, config, paint hook, X11 mouse +// menu.cpp — feature state, config, overlay draw + ISurface discovery #include "hl2h.h" #include #include @@ -29,28 +29,6 @@ void init_conmsg() { if (h) g_conmsg = (decltype(g_conmsg))dlsym(h, "_Z6ConMsgPKcz"); } -// --------------------------------------------------------------------------- -// menu tree structure -// --------------------------------------------------------------------------- - -enum NodeType { NODE_ROOT, NODE_FEATURE, NODE_TOGGLE, NODE_BIND, NODE_VALUE }; - -struct MenuNode { - NodeType type; - const char *label; - bool expanded; // for root/feature: whether children are visible - int child_count; - MenuNode *children[12]; // max children per node - - // leaf data - bool *active_ptr; // NODE_TOGGLE: pointer to the active flag - int *bind_ptr; // NODE_BIND: pointer to the key binding (SDL scancode) - const char *bind_name; // NODE_BIND: current bind display name - int *value_ptr = nullptr; // NODE_VALUE: pointer to the value - int value_min = 0; - int value_max = 0; - int value_step = 0; -}; // feature data (declared in hl2h.h; shared with the GUI) FeatureState g_bhop{true, 0}; @@ -63,7 +41,7 @@ FeatureState g_melee{true, 0}; FeatureState g_spread{true, 0}; bool g_spread_no_spread = true; FeatureState g_esp{true, 0}; -FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu +FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in the GUI FeatureState g_triggerbot{true, 603}; // default Mouse4 FeatureState g_silentaim{true, 602}; // default Mouse3 @@ -76,146 +54,9 @@ int g_aim_fov = 60; // angular distance from the crosshair bool g_aim_fov_circle = true; int g_silent_fov = 60; -// ESP sub-options (menu "ESP" children). Defaults match the pre-option -// behaviour: everything on. +// ESP sub-options. Defaults match the pre-option behaviour: everything on. bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true, true}; -// tree -static MenuNode g_root{NODE_ROOT, "HL2hake", false, 0, {}, nullptr, nullptr, nullptr}; -static MenuNode g_bhop_feat{NODE_FEATURE, "Bunnyhop", false, 1, {}, nullptr, nullptr, nullptr}; -static MenuNode g_bhop_active{NODE_TOGGLE, "Active", false, 0, {}, &g_bhop.active, nullptr, nullptr}; -static MenuNode g_bhop_autostrafe_node{NODE_TOGGLE, "Uni-directional auto-strafe", false, 0, {}, &g_bhop_autostrafe, nullptr, nullptr}; -static MenuNode g_abh_feat{NODE_FEATURE, "ABH", false, 1, {}, nullptr, nullptr, nullptr}; -static MenuNode g_abh_active{NODE_TOGGLE, "Active", false, 0, {}, &g_abh.active, nullptr, nullptr}; -static MenuNode g_jumpbug_feat{NODE_FEATURE, "Jumpbug", false, 1, {}, nullptr, nullptr, nullptr}; -static MenuNode g_jumpbug_active{NODE_TOGGLE, "Active", false, 0, {}, &g_jumpbug.active, nullptr, nullptr}; -static MenuNode g_jumpbug_assist_node{NODE_TOGGLE, "Assist timing", false, 0, {}, &g_jumpbug_assist, nullptr, nullptr}; -static MenuNode g_melee_feat{NODE_FEATURE, "Melee killbot", false, 1, {}, nullptr, nullptr, nullptr}; -static MenuNode g_melee_active{NODE_TOGGLE, "Active", false, 0, {}, &g_melee.active, nullptr, nullptr}; -static MenuNode g_spread_feat{NODE_FEATURE, "Spread control", false, 1, {}, nullptr, nullptr, nullptr}; -static MenuNode g_spread_active{NODE_TOGGLE, "Active", false, 0, {}, &g_spread.active, nullptr, nullptr}; -static MenuNode g_spread_no_spread_node{NODE_TOGGLE, "No spread", false, 0, {}, &g_spread_no_spread, nullptr, nullptr}; -static MenuNode g_esp_feat{NODE_FEATURE, "ESP", false, 1, {}, nullptr, nullptr, nullptr}; -static MenuNode g_esp_active{NODE_TOGGLE, "Active", false, 0, {}, &g_esp.active, nullptr, nullptr}; -static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = { - {NODE_TOGGLE, "Show neutral", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_NEUTRAL], nullptr, nullptr}, - {NODE_TOGGLE, "Show healthbar", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_HEALTHBAR], nullptr, nullptr}, - {NODE_TOGGLE, "Show healthnum", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], nullptr, nullptr}, - {NODE_TOGGLE, "Show name", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_NAME], nullptr, nullptr}, - {NODE_TOGGLE, "Show skeleton", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SKELETON], nullptr, nullptr}, - {NODE_TOGGLE, "Show box", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_BOX], nullptr, nullptr}, - {NODE_TOGGLE, "Show speed graph", false, 0, {}, &g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH], nullptr, nullptr}, -}; -static MenuNode g_aimbot_feat{NODE_FEATURE, "Aimbot", false, 1, {}, nullptr, nullptr, nullptr}; -static MenuNode g_aimbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_aimbot.active, nullptr, nullptr}; -static MenuNode g_aimbot_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_aimbot.bind, nullptr}; -static MenuNode g_aimbot_bones_feat{NODE_FEATURE, "Bones", false, 0, {}, nullptr, nullptr, nullptr}; -static MenuNode g_aimbot_vischeck_node{NODE_TOGGLE, "Vischeck", false, 0, {}, &g_aim_vischeck, nullptr, nullptr}; -static MenuNode g_aimbot_bone_nodes[AIM_BONE_COUNT] = { - {NODE_TOGGLE, "Head", false, 0, {}, &g_aim_bones[AIM_BONE_HEAD], nullptr, nullptr}, - {NODE_TOGGLE, "Torso", false, 0, {}, &g_aim_bones[AIM_BONE_TORSO], nullptr, nullptr}, - {NODE_TOGGLE, "Left arm", false, 0, {}, &g_aim_bones[AIM_BONE_LARM], nullptr, nullptr}, - {NODE_TOGGLE, "Right arm", false, 0, {}, &g_aim_bones[AIM_BONE_RARM], nullptr, nullptr}, - {NODE_TOGGLE, "Left leg", false, 0, {}, &g_aim_bones[AIM_BONE_LLEG], nullptr, nullptr}, - {NODE_TOGGLE, "Right leg", false, 0, {}, &g_aim_bones[AIM_BONE_RLEG], nullptr, nullptr}, -}; -static MenuNode g_aimbot_smooth_node{NODE_VALUE, "Smoothness", false, 0, {}, nullptr, - nullptr, nullptr, &g_aim_smooth, 1, 45, 1}; -static MenuNode g_aimbot_fov_node{NODE_VALUE, "FOV", false, 0, {}, nullptr, - nullptr, nullptr, &g_aim_fov, 1, 180, 1}; -static MenuNode g_aimbot_fov_circle_node{NODE_TOGGLE, "Show FOV circle", false, 0, {}, &g_aim_fov_circle, nullptr, nullptr}; -static MenuNode g_triggerbot_feat{NODE_FEATURE, "Triggerbot", false, 1, {}, nullptr, nullptr, nullptr}; -static MenuNode g_triggerbot_active{NODE_TOGGLE, "Active", false, 0, {}, &g_triggerbot.active, nullptr, nullptr}; -static MenuNode g_triggerbot_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_triggerbot.bind, nullptr}; -static MenuNode g_silentaim_feat{NODE_FEATURE, "Silent aim", false, 1, {}, nullptr, nullptr, nullptr}; -static MenuNode g_silentaim_active{NODE_TOGGLE, "Active", false, 0, {}, &g_silentaim.active, nullptr, nullptr}; -static MenuNode g_silentaim_bind{NODE_BIND, "Bind", false, 0, {}, nullptr, &g_silentaim.bind, nullptr}; -static MenuNode g_silentaim_fov_node{NODE_VALUE, "FOV", false, 0, {}, nullptr, - nullptr, nullptr, &g_silent_fov, 1, 180, 1}; - -static MenuNode *g_visible[48]; // flattened visible nodes for arrow navigation -static int g_visible_count = 0; -static int g_selected = 0; -bool g_menu_open = false; -static bool g_menu_draw_logged = false; - -static bool g_bind_mode = false; -static bool g_insert_latch = false; -static void build_tree() { - g_bhop_feat.children[0] = &g_bhop_active; - g_bhop_feat.children[1] = &g_bhop_autostrafe_node; - g_bhop_feat.child_count = 2; - g_abh_feat.children[0] = &g_abh_active; - g_abh_feat.child_count = 1; - g_jumpbug_feat.children[0] = &g_jumpbug_active; - g_jumpbug_feat.children[1] = &g_jumpbug_assist_node; - g_jumpbug_feat.child_count = 2; - g_melee_feat.children[0] = &g_melee_active; - g_melee_feat.child_count = 1; - g_spread_feat.children[0] = &g_spread_active; - g_spread_feat.children[1] = &g_spread_no_spread_node; - g_spread_feat.child_count = 2; - g_esp_feat.children[0] = &g_esp_active; - g_aimbot_feat.children[0] = &g_aimbot_active; - g_aimbot_feat.children[1] = &g_aimbot_bind; - g_aimbot_feat.children[2] = &g_aimbot_vischeck_node; - g_aimbot_feat.children[3] = &g_aimbot_bones_feat; - g_aimbot_feat.children[4] = &g_aimbot_smooth_node; - g_aimbot_feat.children[5] = &g_aimbot_fov_node; - g_aimbot_feat.children[6] = &g_aimbot_fov_circle_node; - g_aimbot_feat.child_count = 7; - for (int i = 0; i < AIM_BONE_COUNT; i++) - g_aimbot_bones_feat.children[i] = &g_aimbot_bone_nodes[i]; - g_aimbot_bones_feat.child_count = AIM_BONE_COUNT; - g_triggerbot_feat.children[0] = &g_triggerbot_active; - g_triggerbot_feat.children[1] = &g_triggerbot_bind; - g_triggerbot_feat.child_count = 2; - g_silentaim_feat.children[0] = &g_silentaim_active; - g_silentaim_feat.children[1] = &g_silentaim_bind; - g_silentaim_feat.children[2] = &g_silentaim_fov_node; - g_silentaim_feat.child_count = 3; - for (int i = 0; i < ESP_OPT_COUNT; i++) - g_esp_feat.children[i + 1] = &g_esp_opt_nodes[i]; - g_esp_feat.child_count = ESP_OPT_COUNT + 1; - g_root.children[0] = &g_bhop_feat; - g_root.children[1] = &g_abh_feat; - g_root.children[2] = &g_jumpbug_feat; - g_root.children[3] = &g_melee_feat; - g_root.children[4] = &g_spread_feat; - g_root.children[5] = &g_esp_feat; - g_root.children[6] = &g_aimbot_feat; - g_root.children[7] = &g_triggerbot_feat; - g_root.children[8] = &g_silentaim_feat; - g_root.child_count = 9; -} - -static int g_depth[48]; // depth of each visible row (for indentation) - -static void flatten(MenuNode *node, int depth) { - if (g_visible_count >= 48) - return; - node->type = (depth == 0) ? NODE_ROOT - : (node->child_count > 0) ? NODE_FEATURE - : (node->active_ptr) ? NODE_TOGGLE - : (node->value_ptr) ? NODE_VALUE - : NODE_BIND; - g_depth[g_visible_count] = depth; - g_visible[g_visible_count++] = node; - if (node->expanded) { - for (int i = 0; i < node->child_count; i++) - flatten(node->children[i], depth + 1); - } -} - -static void rebuild_visible() { - g_visible_count = 0; - flatten(&g_root, 0); - if (g_selected >= g_visible_count) - g_selected = g_visible_count - 1; - if (g_selected < 0) - g_selected = 0; -} - // --------------------------------------------------------------------------- // config persistence (INI format) // --------------------------------------------------------------------------- @@ -483,20 +324,6 @@ void config_reset_defaults() { config_save(); } -// --------------------------------------------------------------------------- -// menu input handling -// --------------------------------------------------------------------------- - -// SDL scancodes for navigation keys (matches the game's scancode set) -static constexpr int SCAN_INSERT = 73; // reserved for the GUI -static constexpr int SCAN_TEXT_MENU = 58; // F1 preview key for the old tree -static constexpr int SCAN_UP = 82; // these are SDL scancodes -static constexpr int SCAN_DOWN = 81; -static constexpr int SCAN_LEFT = 80; -static constexpr int SCAN_RIGHT = 79; -static constexpr int SCAN_ENTER = 40; -static constexpr int SCAN_ESCAPE = 41; - // simplified scancode-to-name for common keys const char *scancode_name(int sc) { switch (sc) { @@ -531,9 +358,8 @@ const char *scancode_name(int sc) { } } -// get a snapshot of key states: returns array of currently-held scancodes +// held state for feature binds static bool g_prev_key_state[608] = {}; -static bool g_key_down[608] = {}; // true for ONE frame on press // read SDL_GetKeyboardState — the game's own SDL is in-process using SDL_GetKeyboardState_fn = const uint8_t *(*)(int *); @@ -556,7 +382,6 @@ static void poll_mouse() { for (int b = 0; b < 5; b++) { bool down = (mask & (1u << b)) != 0; // SDL buttons 1..5 = bits 0..4 int sc = MOUSE_BASE + b; - g_key_down[sc] = down && !g_prev_key_state[sc]; g_prev_key_state[sc] = down; } } @@ -570,18 +395,12 @@ static void poll_keyboard() { int numkeys = 0; const uint8_t *state = SDL_GetKeyboardState_real(&numkeys); if (state) - for (int i = 0; i < numkeys && i < MOUSE_BASE; i++) { - g_key_down[i] = state[i] && !g_prev_key_state[i]; - g_prev_key_state[i] = state[i]; - } + for (int i = 0; i < numkeys && i < MOUSE_BASE; i++) + g_prev_key_state[i] = state[i] != 0; poll_mouse(); } -static bool key_pressed(int sc) { - return sc >= 0 && sc < MOUSE_BASE + 5 && g_key_down[sc]; -} - -[[maybe_unused]] static bool key_held(int sc) { +static bool key_held(int sc) { return sc >= 0 && sc < MOUSE_BASE + 5 && g_prev_key_state[sc]; } @@ -650,118 +469,8 @@ int silentaim_fov() { return g_silent_fov; } bool aimbot_fov_circle() { return g_aim_fov_circle; } -void menu_force_close() { - g_menu_open = false; - g_bind_mode = false; - g_insert_latch = false; - g_menu_draw_logged = false; -} - -static void menu_handle_input() { - poll_keyboard(); - // SDL state can be sampled several times per rendered frame (both ISurface - // hooks call outer_draw). Use a physical-key latch instead of the one-poll - // edge so one press cannot open and close the menu in the same frame. - bool insert_down = key_held(SCAN_TEXT_MENU); - bool insert_edge = insert_down && !g_insert_latch; - if (insert_down) - g_insert_latch = true; - else - g_insert_latch = false; - - if (!g_menu_open) { - if (insert_edge) { - g_menu_open = true; - g_bind_mode = false; - if (!g_root.expanded) - g_root.expanded = true; - rebuild_visible(); - logf("menu: open rows=%d", g_visible_count); - } - return; - } - - // menu is open - if (g_bind_mode) { - // wait for any key press (except Insert, Up, Down, Escape, Enter) - for (int i = 0; i < MOUSE_BASE + 5; i++) { - if (!g_key_down[i]) - continue; - if (i == SCAN_INSERT || i == SCAN_TEXT_MENU || i == SCAN_UP || - i == SCAN_DOWN || i == SCAN_LEFT || i == SCAN_RIGHT) { - g_bind_mode = false; - return; // cancel bind select - } - if (i == SCAN_ESCAPE) { - // set bind to None - MenuNode *n = g_visible[g_selected]; - if (n->bind_ptr) - *n->bind_ptr = 0; - g_bind_mode = false; - config_save(); - rebuild_visible(); - return; - } - if (i == SCAN_ENTER) { - g_bind_mode = false; - return; // cancel, keep old bind - } - // any other key = new bind (features may share the same bind) - MenuNode *n = g_visible[g_selected]; - if (n->bind_ptr) - *n->bind_ptr = i; - g_bind_mode = false; - config_save(); - rebuild_visible(); - return; - } - return; // no key pressed yet - } - - // normal navigation - if (insert_edge || key_pressed(SCAN_ESCAPE)) { - g_menu_open = false; - g_menu_draw_logged = false; - logf("menu: closed"); - return; - } - if (key_pressed(SCAN_UP) && g_selected > 0) { - g_selected--; - return; - } - if (key_pressed(SCAN_DOWN) && g_selected < g_visible_count - 1) { - g_selected++; - return; - } - if (key_pressed(SCAN_LEFT) || key_pressed(SCAN_RIGHT)) { - MenuNode *n = g_visible[g_selected]; - if (n->type == NODE_VALUE && n->value_ptr) { - int dir = key_pressed(SCAN_RIGHT) ? 1 : -1; - int step = n->value_step ? n->value_step : 1; - int v = *n->value_ptr + dir * step; - if (v < n->value_min) v = n->value_min; - if (v > n->value_max) v = n->value_max; - *n->value_ptr = v; - config_save(); - } - return; - } - if (key_pressed(SCAN_ENTER)) { - MenuNode *n = g_visible[g_selected]; - if (n->child_count > 0) { - n->expanded = !n->expanded; - rebuild_visible(); - } else if (n->type == NODE_TOGGLE && n->active_ptr) { - *n->active_ptr = !(*n->active_ptr); - config_save(); - } else if (n->type == NODE_BIND) { - g_bind_mode = true; - } - } -} - // --------------------------------------------------------------------------- -// menu rendering (VGUI ISurface) +// overlay rendering (VGUI ISurface) // --------------------------------------------------------------------------- // VGUI surface interface @@ -869,13 +578,12 @@ void draw_text_col(int x, int y, const char *text, int r, int g, int b) { // (see hkdDrawPrintText hook in hl2h.cpp): we wrap our draws in // PushMakeCurrent/PopMakeCurrent so they are submitted with a clean clip. void outer_draw() { + poll_keyboard(); gui_handle_input(); - if (!gui_is_open()) - menu_handle_input(); - // HL2H_DRAW_TEST=1 draws a probe rect without needing the menu open + // HL2H_DRAW_TEST=1 draws a probe rect without needing the GUI open static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; bool esp_on = feature_active(FEAT_ESP); - if (!g_surface || (!g_menu_open && !gui_is_open() && !draw_test && !esp_on)) + if (!g_surface || (!gui_is_open() && !draw_test && !esp_on)) return; // Level/menu -> game transitions can invalidate the VGUI font handle. @@ -929,50 +637,8 @@ void outer_draw() { surf_draw_set_color(255, 255, 255, 255); draw_text(28, 36, "HL2H DRAW TEST"); } - if (gui_is_open()) { + if (gui_is_open()) gui_draw(); - surf_pop_make_current(panel); - return; - } - if (!g_menu_open) { - surf_pop_make_current(panel); - return; - } - if (!g_menu_draw_logged) { - g_menu_draw_logged = true; - logf("menu: drawing rows=%d selected=%d", g_visible_count, g_selected); - } - for (int i = 0; i < g_visible_count; i++) { - MenuNode *n = g_visible[i]; - char line[96]; - int x = 12 + g_depth[i] * 12; - snprintf(line, sizeof(line), "%s %s", - (i == g_selected) ? ">" : (n->child_count ? "-" : " "), n->label); - draw_text(x, 10 + i * 20, line); - // right-aligned state - if (n->type == NODE_TOGGLE) { - draw_text(270, 10 + i * 20, *n->active_ptr ? "[on]" : "[off]"); - } else if (n->type == NODE_BIND) { - if (g_bind_mode && i == g_selected) - draw_text(270, 10 + i * 20, "[press key]"); - else { - /* stable name for current bind */ - static char bn[5][32]; - static FeatureState *feats[] = {&g_bhop, &g_abh, &g_aimbot, - &g_triggerbot, &g_silentaim}; - int fi = 0; - for (int k = 0; k < 5; k++) - if (n->bind_ptr == &feats[k]->bind) - fi = k; - snprintf(bn[fi], 32, "[%s]", scancode_name(*n->bind_ptr)); - draw_text(270, 10 + i * 20, bn[fi]); - } - } else if (n->type == NODE_VALUE) { - char vb[32]; - snprintf(vb, sizeof(vb), "%d deg/tick", *n->value_ptr); - draw_text(270, 10 + i * 20, vb); - } - } surf_pop_make_current(panel); } @@ -1029,7 +695,6 @@ static ISurface *find_surface_interface() { } void install_menu() { - build_tree(); register_bundled_font(); logf("config: %s", config_path().c_str()); config_load(); -- cgit v1.2.3