From f1794edcc60f6a27e622884c82397bb77e2aa280 Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 27 Oct 2026 18:31:27 +0100 Subject: GUI: wire real feature state and config Replace the dummy preview controls with the actual settings: - cards bind directly to the feature state and the existing option variables; Active/Bind now drive the same gates as the text menu; - every feature persists active+bind, including movement, melee and spread; - config path and build info are real; Reload config re-reads the INI; - Reset defaults is two-click, restores feature settings/binds and keeps the GUI layout; - window position, category and expanded cards persist under [gui] in the same config file; layout saves on drag/category/expand/close; - mouse bind capture handles all five buttons and consumes the click so it cannot re-arm immediately; - Active/Bind tooltips and full scancode names. The old text tree stays on F1 for one verification pass. --- src/gui.cpp | 448 ++++++++++++++++++++++++++++++++++++++++------------------- src/hl2h.h | 39 ++++++ src/menu.cpp | 117 ++++++++++------ 3 files changed, 417 insertions(+), 187 deletions(-) (limited to 'src') diff --git a/src/gui.cpp b/src/gui.cpp index eb9062e..7baf383 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -1,9 +1,8 @@ -// gui.cpp — custom immediate-mode GUI shell (temporary Insert preview) +// gui.cpp — custom immediate-mode GUI // -// This is the first vertical slice: the visual shell, categories, cards, -// dummy controls, cursor handling and input freeze. It does not read or write -// the real feature settings yet; the next pass replaces the dummy state and -// removes the text menu. +// Categories, feature cards and controls are wired to the real settings in +// menu.cpp. The old text tree is still reachable on F1 until the GUI has been +// verified as a full replacement. #include "hl2h.h" @@ -177,21 +176,24 @@ enum CtrlKind { CK_BUTTON }; +enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET }; + struct Ctrl { int kind; const char *label; const char *desc; - int *value; + bool *bval; // CK_TOGGLE + int *ival; // CK_SLIDER int min, max; + int action; // CK_BUTTON }; struct Card { int cat; + FeatureState *feat; // nullptr for non-feature cards (System) const char *title; const char *desc; bool expanded; - bool active; - int bind; int c0, n; }; @@ -199,123 +201,167 @@ static Ctrl g_ctrls[192]; static int g_nctrl = 0; static Card g_cards[40]; static int g_ncards = 0; -static int g_vals[192]; -static bool g_demo_built = false; +static bool g_cards_built = false; +static bool g_layout_loaded = false; +static bool g_reset_armed = false; +static double g_reset_armed_at = 0.0; +static char g_config_label[512]; +static char g_build_label[128]; static const char *g_cats[] = {"Movement", "Combat", "Visuals", "Weapons", "System"}; static constexpr int CAT_COUNT = 5; -static int add_card(int cat, const char *title, const char *desc, - bool expanded) { +static int add_card(int cat, FeatureState *feat, const char *title, + const char *desc, bool expanded) { Card &c = g_cards[g_ncards]; c.cat = cat; + c.feat = feat; c.title = title; c.desc = desc; c.expanded = expanded; - c.active = true; - c.bind = 0; c.c0 = g_nctrl; c.n = 0; return g_ncards++; } static void add_ctrl(int card, int kind, const char *label, const char *desc, - int min, int max, int init) { + int min, int max, bool *bval, int *ival, int action) { int i = g_nctrl++; - g_vals[i] = init; - g_ctrls[i].kind = kind; - g_ctrls[i].label = label; - g_ctrls[i].desc = desc; - g_ctrls[i].value = &g_vals[i]; - g_ctrls[i].min = min; - g_ctrls[i].max = max; + Ctrl &c = g_ctrls[i]; + c.kind = kind; + c.label = label; + c.desc = desc; + c.bval = bval; + c.ival = ival; + c.min = min; + c.max = max; + c.action = action; g_cards[card].n++; } static void add_toggle(int card, const char *label, const char *desc, - int init) { - add_ctrl(card, CK_TOGGLE, label, desc, 0, 1, init); + bool *value) { + add_ctrl(card, CK_TOGGLE, label, desc, 0, 1, value, nullptr, ACT_NONE); } static void add_slider(int card, const char *label, const char *desc, int min, - int max, int init) { - add_ctrl(card, CK_SLIDER, label, desc, min, max, init); + int max, int *value) { + add_ctrl(card, CK_SLIDER, label, desc, min, max, nullptr, value, ACT_NONE); } static void add_section(int card, const char *label) { - add_ctrl(card, CK_SECTION, label, nullptr, 0, 0, 0); + add_ctrl(card, CK_SECTION, label, nullptr, 0, 0, nullptr, nullptr, ACT_NONE); } static void add_info(int card, const char *label) { - add_ctrl(card, CK_INFO, label, nullptr, 0, 0, 0); + add_ctrl(card, CK_INFO, label, nullptr, 0, 0, nullptr, nullptr, ACT_NONE); } -static void add_button(int card, const char *label, const char *desc) { - add_ctrl(card, CK_BUTTON, label, desc, 0, 0, 0); +static void add_button(int card, const char *label, const char *desc, + int action) { + add_ctrl(card, CK_BUTTON, label, desc, 0, 0, nullptr, nullptr, action); } -static void build_demo() { - if (g_demo_built) +static int ctrl_val(const Ctrl &c) { + if (c.kind == CK_TOGGLE && c.bval) + return *c.bval ? 1 : 0; + if (c.kind == CK_SLIDER && c.ival) + return *c.ival; + return 0; +} +static void ctrl_set(Ctrl &c, int v) { + if (c.kind == CK_TOGGLE && c.bval) { + *c.bval = v != 0; + config_save(); + } else if (c.kind == CK_SLIDER && c.ival) { + if (v < c.min) v = c.min; + if (v > c.max) v = c.max; + if (*c.ival != v) { + *c.ival = v; + // Slider drags persist on mouse release; discrete changes persist now. + } + } +} + +static void build_cards() { + if (g_cards_built) return; - g_demo_built = true; + g_cards_built = true; - int bhop = add_card(0, "Bunnyhop", "Automatic jump on landing.", true); + int bhop = add_card(0, &g_bhop, "Bunnyhop", "Automatic jump on landing.", + true); add_toggle(bhop, "Uni-directional auto-strafe", - "Rotate airborne movement into the velocity.", 1); + "Rotate airborne movement into the velocity.", + &g_bhop_autostrafe); - int abh = add_card(0, "ABH", "Accelerate on the jump tick by facing away " - "from the velocity.", true); - (void)abh; + add_card(0, &g_abh, "ABH", "Accelerate on the jump tick by facing away " + "from the velocity.", true); - int jb = add_card(0, "Jumpbug", "Duck+jump when landing would hurt.", true); + int jb = add_card(0, &g_jumpbug, "Jumpbug", + "Duck+jump when landing would hurt.", true); add_toggle(jb, "Assist timing", - "Nudge the final command into the unduck window.", 0); + "Nudge the final command into the unduck window.", + &g_jumpbug_assist); - int aim = add_card(1, "Aimbot", "Smoothly snap to a hostile target.", true); - g_cards[aim].bind = 604; - add_toggle(aim, "Vischeck", "Require world line of sight.", 1); + int aim = add_card(1, &g_aimbot, "Aimbot", + "Smoothly snap to a hostile target.", true); + add_toggle(aim, "Vischeck", "Require world line of sight.", &g_aim_vischeck); add_slider(aim, "FOV", "Maximum angular distance from the crosshair.", 1, 180, - 60); - add_slider(aim, "Smoothness", "Maximum degrees per tick.", 1, 45, 5); - add_toggle(aim, "Show FOV circle", "Draw the acquisition cone on screen.", 1); + &g_aim_fov); + add_slider(aim, "Smoothness", "Maximum degrees per tick.", 1, 45, + &g_aim_smooth); + add_toggle(aim, "Show FOV circle", "Draw the acquisition cone on screen.", + &g_aim_fov_circle); add_section(aim, "Bones"); - add_toggle(aim, "Head", "Aim at the head hitbox.", 1); - add_toggle(aim, "Torso", "Aim at the chest/stomach.", 0); - add_toggle(aim, "Left arm", "Include the left arm.", 0); - add_toggle(aim, "Right arm", "Include the right arm.", 0); - add_toggle(aim, "Left leg", "Include the left leg.", 0); - add_toggle(aim, "Right leg", "Include the right leg.", 0); - - int sil = add_card(1, "Silent aim", "Fire at the target without turning " - "the rendered view.", true); - g_cards[sil].bind = 602; + static const char *bone_names[AIM_BONE_COUNT] = { + "Head", "Torso", "Left arm", "Right arm", "Left leg", "Right leg"}; + static const char *bone_descs[AIM_BONE_COUNT] = { + "Aim at the head hitbox.", "Aim at the chest/stomach.", + "Include the left arm.", "Include the right arm.", + "Include the left leg.", "Include the right leg."}; + for (int i = 0; i < AIM_BONE_COUNT; i++) + add_toggle(aim, bone_names[i], bone_descs[i], &g_aim_bones[i]); + + int sil = add_card(1, &g_silentaim, "Silent aim", + "Fire at the target without turning the rendered view.", + true); add_slider(sil, "FOV", "Maximum angular distance from the crosshair.", 1, 180, - 60); - - int tb = add_card(1, "Triggerbot", "Fire when the crosshair is on a hostile.", - true); - g_cards[tb].bind = 603; - - int melee = add_card(1, "Melee killbot", "360-degree crowbar silent aim.", - true); - - int esp = add_card(2, "ESP", "Draw hostile NPC information.", true); - add_toggle(esp, "Show neutral", "Include neutral wildlife.", 1); - add_toggle(esp, "Show healthbar", "Draw a health bar.", 1); - add_toggle(esp, "Show healthnum", "Draw the numeric health value.", 1); - add_toggle(esp, "Show name", "Draw the model name.", 1); - add_toggle(esp, "Show skeleton", "Draw the animated bone links.", 1); - add_toggle(esp, "Show box", "Draw a bounding box.", 1); - add_toggle(esp, "Show speed graph", "Draw the lower-center speed graph.", 1); - - int spread = add_card(3, "Spread control", + &g_silent_fov); + + add_card(1, &g_triggerbot, "Triggerbot", + "Fire when the crosshair is on a hostile.", true); + + add_card(1, &g_melee, "Melee killbot", "360-degree crowbar silent aim.", + true); + + int esp = add_card(2, &g_esp, "ESP", "Draw hostile NPC information.", true); + static const char *esp_names[ESP_OPT_COUNT] = { + "Show neutral", "Show healthbar", "Show healthnum", + "Show name", "Show skeleton", "Show box", + "Show speed graph"}; + static const char *esp_descs[ESP_OPT_COUNT] = { + "Include neutral wildlife.", "Draw a health bar.", + "Draw the numeric health value.", "Draw the model name.", + "Draw the animated bone links.", "Draw a bounding box.", + "Draw the lower-center speed graph."}; + for (int i = 0; i < ESP_OPT_COUNT; i++) + add_toggle(esp, esp_names[i], esp_descs[i], &g_esp_opt[i]); + + int spread = add_card(3, &g_spread, "Spread control", "Choose a server shot seed that tightens the cone.", true); add_toggle(spread, "No spread", - "Override the FireBullets seed with the best offset.", 1); - - int sys = add_card(4, "System", "Configuration and maintenance.", true); - add_info(sys, "Config: ~/.config/hl2hake/config"); - add_info(sys, "Build: GUI shell preview"); - add_button(sys, "Reload config", "Re-read the INI without restarting."); - add_button(sys, "Reset defaults", "Restore feature defaults; GUI layout kept."); - - (void)g_cards[melee]; + "Override the FireBullets seed with the best offset.", + &g_spread_no_spread); + + int sys = add_card(4, nullptr, "System", "Configuration and maintenance.", + true); + snprintf(g_config_label, sizeof(g_config_label), "Config: %s", + config_path_cstr()); + snprintf(g_build_label, sizeof(g_build_label), "Build: %s %s", __DATE__, + __TIME__); + add_info(sys, g_config_label); + add_info(sys, g_build_label); + add_button(sys, "Reload config", "Re-read the INI without restarting.", + ACT_RELOAD); + add_button(sys, "Reset defaults", + "Click twice to restore feature defaults; GUI layout kept.", + ACT_RESET); } // --------------------------------------------------------------------------- @@ -344,6 +390,7 @@ static bool g_mouse_click = false; static bool g_mouse_down = false; static bool g_mouse_rclick = false; static unsigned int g_prev_mask = 0; +static int g_mouse_press = 0; // 1..5 on the frame a button goes down static unsigned char g_prev_keys[512] = {}; static bool g_prev_cursor = true; static bool g_prev_relative = true; @@ -480,21 +527,6 @@ static void line(int x0, int y0, int x1, int y1, const Col &c) { s_line(x0, y0, x1, y1); } -static const char *bind_label(int b) { - switch (b) { - case 0: return "None"; - case 600: return "MouseL"; - case 601: return "MouseR"; - case 602: return "MouseM"; - case 603: return "Mouse4"; - case 604: return "Mouse5"; - case 41: return "Esc"; - case 73: return "Insert"; - case 59: return "F2"; - default: return "Key"; - } -} - static void layout() { int w = g_scr_w, h = g_scr_h; if (g_surface) { @@ -575,6 +607,7 @@ static void close_gui() { g_dragging = false; g_scroll_drag = false; g_slider_drag = -1; + g_reset_armed = false; input_clear_mouse_states(); if (g_sdl_ready) { if (sd_set_relative) @@ -582,6 +615,7 @@ static void close_gui() { if (sd_show_cursor) sd_show_cursor(g_prev_cursor ? 1 : 0); } + config_save(); logf("gui: closed"); } @@ -617,7 +651,8 @@ static void open_gui() { g_capture_card = -1; g_wheel.exchange(0); g_scroll = 0.0f; - g_cat = 0; + if (g_cat < 0 || g_cat >= CAT_COUNT) + g_cat = 0; menu_force_close(); logf("gui: opened"); } @@ -630,8 +665,16 @@ static void update_mouse() { g_mouse_x = mx; g_mouse_y = my; g_mouse_down = (mask & 1u) != 0; - g_mouse_click = g_mouse_down && !(g_prev_mask & 1u); - g_mouse_rclick = ((mask & 2u) != 0) && !(g_prev_mask & 2u); + g_mouse_press = 0; + unsigned int pressed = mask & ~g_prev_mask; + for (int b = 0; b < 5; b++) { + if (pressed & (1u << b)) { + g_mouse_press = b + 1; + break; + } + } + g_mouse_click = g_mouse_press == 1; + g_mouse_rclick = g_mouse_press == 2; if (g_mouse_x < 0) g_mouse_x = 0; if (g_mouse_y < 0) g_mouse_y = 0; g_prev_mask = mask; @@ -648,14 +691,15 @@ static void handle_capture(const unsigned char *keys) { g_capture_card = -1; return; } - if (g_mouse_click) { - g_cards[g_capture_card].bind = 600; - g_capture_card = -1; - return; - } - if (g_mouse_rclick) { - g_cards[g_capture_card].bind = 601; + Card &card = g_cards[g_capture_card]; + if (g_mouse_press >= 1 && g_mouse_press <= 5 && card.feat) { + card.feat->bind = 600 + (g_mouse_press - 1); + // Consume the click: it must not immediately re-arm capture on the same + // bind box. + g_mouse_click = false; + g_mouse_rclick = false; g_capture_card = -1; + config_save(); return; } for (int k = 0; k < 512; k++) { @@ -663,8 +707,12 @@ static void handle_capture(const unsigned char *keys) { continue; if (k == 73) // Insert is reserved for opening/closing the GUI continue; - g_cards[g_capture_card].bind = k; + if (k == 58) // F1 opens the old text tree during the verification pass + continue; + if (card.feat) + card.feat->bind = k; g_capture_card = -1; + config_save(); return; } } @@ -674,7 +722,7 @@ static void handle_mouse_click() { return; int mx = g_mouse_x, my = g_mouse_y; - // title bar: drag or close + // title bar: drag Rect title{g_win_x, g_win_y, g_win_w, S(38.0f)}; if (title.has(mx, my)) { g_dragging = true; @@ -693,6 +741,7 @@ static void handle_mouse_click() { if (r.has(mx, my)) { g_cat = i; g_scroll = 0.0f; + config_save(); return; } } @@ -736,18 +785,20 @@ static void handle_mouse_click() { if (my >= cr.y && my < cr.y + ch) { // header if (my < cr.y + S(36.0f)) { - if (c.cat != 4) { - Rect active{cr.x + cr.w - S(150.0f), cr.y + S(9.0f), S(18.0f), + if (c.feat) { + Rect active{cr.x + cr.w - S(170.0f), cr.y + S(9.0f), S(18.0f), S(18.0f)}; Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f), S(22.0f)}; if (active.has(mx, my)) { - c.active = !c.active; + c.feat->active = !c.feat->active; + config_save(); return; } if (bind.has(mx, my)) { if (mx >= bind.x + bind.w - S(18.0f)) { - c.bind = 0; + c.feat->bind = 0; + config_save(); return; } g_capture_card = i; @@ -755,6 +806,7 @@ static void handle_mouse_click() { } } c.expanded = !c.expanded; + config_save(); return; } // body @@ -767,7 +819,7 @@ static void handle_mouse_click() { continue; } if (ct.kind == CK_TOGGLE) { - *ct.value = !*ct.value; + ctrl_set(ct, !ctrl_val(ct)); return; } if (ct.kind == CK_SLIDER) { @@ -778,7 +830,7 @@ static void handle_mouse_click() { int v = ct.min + (int)(t * (ct.max - ct.min) + 0.5f); if (v < ct.min) v = ct.min; if (v > ct.max) v = ct.max; - *ct.value = v; + ctrl_set(ct, v); g_slider_drag = c.c0 + j; } return; @@ -787,7 +839,20 @@ static void handle_mouse_click() { Rect b{cr.x + cr.w - S(176.0f), by + S(3.0f), S(160.0f), S(26.0f)}; if (b.has(mx, my)) { - logf("gui: dummy action %s", ct.label ? ct.label : "?"); + if (ct.action == ACT_RELOAD) { + config_reload(); + logf("gui: config reloaded"); + } else if (ct.action == ACT_RESET) { + double now = now_sec(); + if (g_reset_armed && now - g_reset_armed_at < 3.0) { + config_reset_defaults(); + g_reset_armed = false; + logf("gui: defaults restored"); + } else { + g_reset_armed = true; + g_reset_armed_at = now; + } + } return; } } @@ -815,7 +880,25 @@ static void handle_hover() { int ch = card_h(c); if (my >= cr.y && my < cr.y + ch) { if (my < cr.y + S(36.0f)) { - g_hover_desc = c.desc; + if (c.feat) { + Rect active{cr.x + cr.w - S(170.0f), cr.y + S(9.0f), S(18.0f), + S(18.0f)}; + Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f), + S(22.0f)}; + if (active.has(mx, my)) + g_hover_desc = "Enable this feature. A bind, when set, must be " + "held for it to run."; + else if (bind.has(mx, my)) + g_hover_desc = (mx >= bind.x + bind.w - S(18.0f)) + ? "Clear the bind. An unbound feature is " + "always-on." + : "Click, then press a key or mouse button. " + "Esc cancels."; + else + g_hover_desc = c.desc; + } else { + g_hover_desc = c.desc; + } return; } int by = cr.y + S(36.0f) + S(6.0f); @@ -840,7 +923,7 @@ void gui_handle_input() { gui_init_sdl(); if (!g_surface) return; - build_demo(); + build_cards(); layout(); if (g_scr_w <= 0 || g_scr_h <= 0) return; @@ -895,6 +978,7 @@ void gui_handle_input() { if (g_dragging) { if (!g_mouse_down) { g_dragging = false; + config_save(); } else { g_win_x = g_mouse_x - g_drag_dx; g_win_y = g_mouse_y - g_drag_dy; @@ -910,6 +994,7 @@ void gui_handle_input() { if (g_slider_drag >= 0) { if (!g_mouse_down) { g_slider_drag = -1; + config_save(); } else { Ctrl &ct = g_ctrls[g_slider_drag]; Rect content{g_win_x + S(190.0f), g_win_y + S(38.0f), @@ -919,9 +1004,7 @@ void gui_handle_input() { (void)content; float t = (float)(g_mouse_x - x0) / (float)(x1 - x0); int v = ct.min + (int)(t * (ct.max - ct.min) + 0.5f); - if (v < ct.min) v = ct.min; - if (v > ct.max) v = ct.max; - *ct.value = v; + ctrl_set(ct, v); } } @@ -950,9 +1033,8 @@ void gui_handle_input() { if (left_edge || right_edge) { if (g_hover_slider >= 0) { Ctrl &ct = g_ctrls[g_hover_slider]; - *ct.value += right_edge ? 1 : -1; - if (*ct.value < ct.min) *ct.value = ct.min; - if (*ct.value > ct.max) *ct.value = ct.max; + ctrl_set(ct, ctrl_val(ct) + (right_edge ? 1 : -1)); + config_save(); } } @@ -1003,15 +1085,16 @@ static void draw_slider(const Rect &clip, const Rect &cr, int y, const Ctrl &ct) if (track_w < S(30.0f)) track_w = S(30.0f); Rect track{track_x, y + S(9.0f), track_w, S(8.0f)}; fill(track, Col{38, 42, 50, 255}, &clip); + int value = ctrl_val(ct); float t = (ct.max > ct.min) - ? (float)(*ct.value - ct.min) / (float)(ct.max - ct.min) + ? (float)(value - ct.min) / (float)(ct.max - ct.min) : 0.0f; int fw = (int)(track_w * t + 0.5f); fill(Rect{track.x, track.y, fw, track.h}, C_ACCENT_DIM, &clip); int kx = track.x + fw - S(4.0f); fill(Rect{kx, track.y - S(4.0f), S(8.0f), S(16.0f)}, C_ACCENT, &clip); char val[32]; - snprintf(val, sizeof(val), "%d", *ct.value); + snprintf(val, sizeof(val), "%d", value); text_in(clip, cr.x + cr.w - S(52.0f), y + S(4.0f), val, g_font_body, g_body_h, S(44.0f), C_TEXT); } @@ -1037,15 +1120,18 @@ static void draw_card(const Rect &clip, int index, int y) { } text_in(clip, cr.x + S(24.0f), cr.y + S(8.0f), c.title, g_font_title, g_title_h, cr.w - S(180.0f), C_TEXT); - if (c.cat != 4) { - draw_checkbox(cr.x + cr.w - S(150.0f), cr.y + S(10.0f), c.active); - text_in(clip, cr.x + cr.w - S(128.0f), cr.y + S(9.0f), - c.active ? "On" : "Off", g_font_body, g_body_h, S(30.0f), - c.active ? C_GOOD : C_DIM); + if (c.feat) { + bool active = c.feat->active; + draw_checkbox(cr.x + cr.w - S(170.0f), cr.y + S(10.0f), active); + text_in(clip, cr.x + cr.w - S(148.0f), cr.y + S(9.0f), + active ? "On" : "Off", g_font_body, g_body_h, S(32.0f), + active ? C_GOOD : C_DIM); Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f), S(22.0f)}; fill(bind, Col{45, 50, 60, 255}, &clip); outline(bind, C_BORDER, &clip); - const char *bl = (g_capture_card == index) ? "Press..." : bind_label(c.bind); + const char *bl = (g_capture_card == index) + ? "Press..." + : scancode_name(c.feat->bind); text_in(clip, bind.x + S(7.0f), bind.y + S(3.0f), bl, g_font_body, g_body_h, bind.w - S(22.0f), C_TEXT); text_in(clip, bind.x + bind.w - S(16.0f), bind.y + S(3.0f), "x", @@ -1068,16 +1154,24 @@ static void draw_card(const Rect &clip, int index, int y) { Rect b{cr.x + cr.w - S(176.0f), by + S(3.0f), S(160.0f), S(26.0f)}; fill(b, Col{45, 50, 60, 255}, &clip); outline(b, C_BORDER, &clip); - text_in(clip, b.x + S(10.0f), b.y + S(4.0f), ct.label, g_font_body, + const char *label = ct.label; + if (ct.action == ACT_RESET && g_reset_armed) { + if (now_sec() - g_reset_armed_at >= 3.0) + g_reset_armed = false; + else + label = "Confirm reset"; + } + text_in(clip, b.x + S(10.0f), b.y + S(4.0f), label, g_font_body, g_body_h, b.w - S(16.0f), C_TEXT); } else { text_in(clip, cr.x + S(16.0f), by + S(4.0f), ct.label, g_font_body, g_body_h, S(220.0f), C_TEXT); if (ct.kind == CK_TOGGLE) { - draw_checkbox(cr.x + cr.w - S(31.0f), by + S(5.0f), *ct.value != 0); + bool on = ctrl_val(ct) != 0; + draw_checkbox(cr.x + cr.w - S(31.0f), by + S(5.0f), on); text_in(clip, cr.x + cr.w - S(58.0f), by + S(3.0f), - *ct.value ? "On" : "Off", g_font_body, g_body_h, S(26.0f), - *ct.value ? C_GOOD : C_DIM); + on ? "On" : "Off", g_font_body, g_body_h, S(26.0f), + on ? C_GOOD : C_DIM); } else if (ct.kind == CK_SLIDER) { draw_slider(clip, cr, by, ct); } @@ -1126,8 +1220,8 @@ void gui_draw() { fill(Rect{win.x, win.y, S(4.0f), win.h}, C_ACCENT); text_in(win, win.x + S(16.0f), win.y + S(7.0f), "HL2hake", g_font_title, g_title_h, win.w - S(80.0f), C_TEXT); - text_in(win, win.x + S(110.0f), win.y + S(10.0f), "GUI shell preview", - g_font_body, g_body_h, win.w - S(180.0f), C_DIM); + text_in(win, win.x + S(110.0f), win.y + S(10.0f), "Settings", g_font_body, + g_body_h, win.w - S(180.0f), C_DIM); Rect side{win.x, win.y + S(38.0f), S(190.0f), win.h - S(38.0f) - S(30.0f)}; @@ -1181,7 +1275,7 @@ void gui_draw() { "config: ~/.config/hl2hake/config", g_font_body, g_body_h, foot.w - S(220.0f), C_DIM); text_in(foot, foot.x + foot.w - S(200.0f), foot.y + S(6.0f), - g_capture_card >= 0 ? "press a key / Esc" : "INS close | F1 text menu", + g_capture_card >= 0 ? "press a key / Esc" : "INS close | F1 old menu", g_font_body, g_body_h, S(190.0f), C_DIM); draw_tooltip(); @@ -1191,6 +1285,78 @@ bool gui_is_open() { return g_open; } bool gui_bind_capture_active() { return g_open && g_capture_card >= 0; } QAngle gui_frozen_view() { return g_frozen; } +// --------------------------------------------------------------------------- +// [gui] layout persistence +// --------------------------------------------------------------------------- + +static void gui_load_layout() { + FILE *f = fopen(config_path_cstr(), "r"); + if (!f) + return; + bool in_gui = false, have_x = false, have_y = false; + int x = 0, y = 0, cat = 0; + unsigned expanded = 0; + bool have_expanded = false; + char line[256]; + while (fgets(line, sizeof(line), f)) { + line[strcspn(line, "\r\n")] = 0; + if (line[0] == '[') { + in_gui = strstr(line, "[gui]") != nullptr; + continue; + } + if (!in_gui) + continue; + char *eq = strchr(line, '='); + if (!eq) + continue; + *eq = 0; + const char *key = line, *val = eq + 1; + if (strcmp(key, "x") == 0) { + x = atoi(val); + have_x = true; + } else if (strcmp(key, "y") == 0) { + y = atoi(val); + have_y = true; + } else if (strcmp(key, "category") == 0) { + cat = atoi(val); + } else if (strcmp(key, "expanded") == 0) { + expanded = (unsigned)strtoul(val, nullptr, 10); + have_expanded = true; + } + } + fclose(f); + if (have_x && have_y) { + g_win_x = x; + g_win_y = y; + g_positioned = true; + } + if (cat >= 0 && cat < CAT_COUNT) + g_cat = cat; + if (have_expanded) { + for (int i = 0; i < g_ncards && i < 32; i++) + g_cards[i].expanded = (expanded & (1u << i)) != 0; + } +} + +void gui_config_init() { + build_cards(); + if (g_layout_loaded) + return; + g_layout_loaded = true; + gui_load_layout(); +} + +void gui_config_write(FILE *f) { + if (!f || !g_cards_built || !g_positioned) + return; + unsigned expanded = 0; + for (int i = 0; i < g_ncards && i < 32; i++) + if (g_cards[i].expanded) + expanded |= 1u << i; + fprintf(f, "[gui]\nx=%d\ny=%d\ncategory=%d\nexpanded=%u\n", g_win_x, + g_win_y, g_cat, expanded); +} + // --------------------------------------------------------------------------- // SDL cursor interposition // --------------------------------------------------------------------------- diff --git a/src/hl2h.h b/src/hl2h.h index d5a9dc3..678c5af 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -174,6 +174,43 @@ enum EspOption { ESP_OPT_SHOW_SPEEDGRAPH = 6, ESP_OPT_COUNT = 7 }; +enum AimBone { + AIM_BONE_HEAD = 0, + AIM_BONE_TORSO, + AIM_BONE_LARM, + AIM_BONE_RARM, + AIM_BONE_LLEG, + AIM_BONE_RLEG, + AIM_BONE_COUNT +}; + +// --------------------------------------------------------------------------- +// feature state / settings (owned by menu.cpp until the text menu is removed) +// --------------------------------------------------------------------------- + +struct FeatureState { + bool active; + int bind; // SDL scancode, 0 = none (always-on) +}; + +extern FeatureState g_bhop, g_abh, g_jumpbug, g_melee, g_spread, g_esp, + g_aimbot, g_triggerbot, g_silentaim; +extern bool g_bhop_autostrafe; +extern bool g_jumpbug_assist; +extern bool g_spread_no_spread; +extern bool g_aim_bones[AIM_BONE_COUNT]; +extern int g_aim_smooth; +extern bool g_aim_vischeck; +extern int g_aim_fov; +extern bool g_aim_fov_circle; +extern int g_silent_fov; +extern bool g_esp_opt[ESP_OPT_COUNT]; + +const char *scancode_name(int sc); +void config_save(); +void config_reload(); +void config_reset_defaults(); +const char *config_path_cstr(); // --------------------------------------------------------------------------- // cross-file declarations @@ -237,6 +274,8 @@ void gui_draw(); bool gui_is_open(); bool gui_bind_capture_active(); QAngle gui_frozen_view(); +void gui_config_init(); +void gui_config_write(FILE *f); // ISurface pointer (defined in menu.cpp, used for drawing tests) struct ISurface; extern ISurface *g_surface; diff --git a/src/menu.cpp b/src/menu.cpp index a0cf323..2a96b02 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -52,47 +52,33 @@ struct MenuNode { int value_step = 0; }; -// feature data -struct FeatureState { - bool active; - int bind; // SDL scancode, 0 = none -}; - -static FeatureState g_bhop{true, 0}; -static bool g_bhop_autostrafe = true; - -static FeatureState g_abh{true, 0}; -static FeatureState g_jumpbug{true, 0}; -static bool g_jumpbug_assist = false; -static FeatureState g_melee{true, 0}; -static FeatureState g_spread{true, 0}; -static bool g_spread_no_spread = true; -static FeatureState g_esp{true, 0}; -static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu -static FeatureState g_triggerbot{true, 603}; // default Mouse4 -static FeatureState g_silentaim{true, 602}; // default Mouse3 +// feature data (declared in hl2h.h; shared with the GUI) +FeatureState g_bhop{true, 0}; +bool g_bhop_autostrafe = true; + +FeatureState g_abh{true, 0}; +FeatureState g_jumpbug{true, 0}; +bool g_jumpbug_assist = false; +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_triggerbot{true, 603}; // default Mouse4 +FeatureState g_silentaim{true, 602}; // default Mouse3 // Aimbot bone checklist. Head defaults on (v1 behaviour). -enum { - AIM_BONE_HEAD = 0, - AIM_BONE_TORSO, - AIM_BONE_LARM, - AIM_BONE_RARM, - AIM_BONE_LLEG, - AIM_BONE_RLEG, - AIM_BONE_COUNT -}; -static bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false, - false, false, false}; -static int g_aim_smooth = 5; // degrees per tick -static bool g_aim_vischeck = true; -static int g_aim_fov = 60; // angular distance from the crosshair -static bool g_aim_fov_circle = true; -static int g_silent_fov = 60; +bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false, + false, false, false}; +int g_aim_smooth = 5; // degrees per tick +bool g_aim_vischeck = true; +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. -static bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true, true}; +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}; @@ -315,18 +301,18 @@ void register_bundled_font() { logf("font: failed to register %s", path.c_str()); } -static void config_save() { +void config_save() { FILE *f = fopen(config_path().c_str(), "w"); if (!f) return; fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, g_bhop.bind, g_bhop_autostrafe); fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); - fprintf(f, "[jumpbug]\nactive=%d\nassist=%d\n", g_jumpbug.active, - (int)g_jumpbug_assist); - fprintf(f, "[melee]\nactive=%d\n", g_melee.active); - fprintf(f, "[spread]\nactive=%d\nno_spread=%d\n", g_spread.active, - (int)g_spread_no_spread); + fprintf(f, "[jumpbug]\nactive=%d\nbind=%d\nassist=%d\n", + g_jumpbug.active, g_jumpbug.bind, (int)g_jumpbug_assist); + fprintf(f, "[melee]\nactive=%d\nbind=%d\n", g_melee.active, g_melee.bind); + fprintf(f, "[spread]\nactive=%d\nbind=%d\nno_spread=%d\n", + g_spread.active, g_spread.bind, (int)g_spread_no_spread); fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" "fov=%d\nfov_circle=%d\n" "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" @@ -347,6 +333,7 @@ static void config_save() { g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], g_esp_opt[ESP_OPT_SHOW_NAME], g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX], g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH]); + gui_config_write(f); fclose(f); } @@ -462,6 +449,40 @@ static void config_load() { fclose(f); } +void config_reload() { config_load(); } + +const char *config_path_cstr() { + static std::string path; + path = config_path(); + return path.c_str(); +} + +void config_reset_defaults() { + g_bhop = {true, 0}; + g_bhop_autostrafe = true; + g_abh = {true, 0}; + g_jumpbug = {true, 0}; + g_jumpbug_assist = false; + g_melee = {true, 0}; + g_spread = {true, 0}; + g_spread_no_spread = true; + g_esp = {true, 0}; + for (int i = 0; i < ESP_OPT_COUNT; i++) + g_esp_opt[i] = true; + g_aimbot = {true, 604}; + g_triggerbot = {true, 603}; + g_silentaim = {true, 602}; + for (int i = 0; i < AIM_BONE_COUNT; i++) + g_aim_bones[i] = false; + g_aim_bones[AIM_BONE_HEAD] = true; + g_aim_smooth = 5; + g_aim_vischeck = true; + g_aim_fov = 60; + g_aim_fov_circle = true; + g_silent_fov = 60; + config_save(); +} + // --------------------------------------------------------------------------- // menu input handling // --------------------------------------------------------------------------- @@ -477,7 +498,7 @@ static constexpr int SCAN_ENTER = 40; static constexpr int SCAN_ESCAPE = 41; // simplified scancode-to-name for common keys -static const char *scancode_name(int sc) { +const char *scancode_name(int sc) { switch (sc) { case 0: return "None"; case 4: return "A"; case 5: return "B"; case 6: return "C"; @@ -497,9 +518,12 @@ static const char *scancode_name(int sc) { case 43: return "Tab"; case 44: return "Space"; case 79: return "Right"; case 80: return "Left"; case 81: return "Down"; case 82: return "Up"; - case 87: return "F1"; case 88: return "F2"; case 89: return "F3"; - case 90: return "F4"; case 91: return "F5"; case 92: return "F6"; - case 93: return "F7"; case 94: return "F8"; + case 58: return "F1"; case 59: return "F2"; case 60: return "F3"; + case 61: return "F4"; case 62: return "F5"; case 63: return "F6"; + case 64: return "F7"; case 65: return "F8"; case 66: return "F9"; + case 67: return "F10"; case 68: return "F11"; case 69: return "F12"; + case 73: return "Insert"; case 74: return "Home"; case 75: return "PgUp"; + case 76: return "Delete"; case 77: return "End"; case 78: return "PgDn"; case 600: return "MouseL"; case 601: return "MouseR"; case 602: return "MouseM"; case 603: return "Mouse4"; case 604: return "Mouse5"; @@ -1009,6 +1033,7 @@ void install_menu() { register_bundled_font(); logf("config: %s", config_path().c_str()); config_load(); + gui_config_init(); // resolve ISurface once (used by the DrawPrintText hook in hl2h.cpp and // by outer_draw). DrawPrintText hook is installed from hkdCreateMove. g_surface = find_surface_interface(); -- cgit v1.2.3