summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTEXT.md26
-rw-r--r--docs/adr/0001-custom-isurface-gui.md8
-rw-r--r--src/gui.cpp448
-rw-r--r--src/hl2h.h39
-rw-r--r--src/menu.cpp117
5 files changed, 438 insertions, 200 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index 6f6e59e..0cb05c5 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -100,8 +100,9 @@ Bind rows enter bind-select mode: press a key/button to assign, `Escape` sets
100None (always-on), `Enter` cancels. 100None (always-on), `Enter` cancels.
101 101
102> The text menu below is being replaced by the GUI described in the next 102> The text menu below is being replaced by the GUI described in the next
103> section. The old tree is kept only until the GUI reaches parity. During the 103> section. The GUI now drives the same feature state/config. The old tree is
104> preview, **Insert** toggles the GUI and the old text tree is on **F1**. 104> kept only for a verification pass; **Insert** toggles the GUI and the old
105> text tree is on **F1**.
105 106
106``` 107```
107HL2hake 108HL2hake
@@ -156,17 +157,24 @@ Binds use SDL scancodes; mouse buttons are pseudo-scancodes 600..604.
156 and ESP/FOV circle/speed graph keep drawing. No invulnerability. 157 and ESP/FOV circle/speed graph keep drawing. No invulnerability.
157- **Native cursor**: on open, leave SDL relative-mouse mode and show the OS 158- **Native cursor**: on open, leave SDL relative-mouse mode and show the OS
158 cursor; restore on close. Absolute mouse coordinates are used. 159 cursor; restore on close. Absolute mouse coordinates are used.
159- **Bind capture**: click Bind, press a key/button; Esc cancels capture but 160- **Bind capture**: click Bind, press a key or any mouse button; Esc cancels
160 does not close the GUI; clicking elsewhere cancels; Clear sets None; Insert 161 capture but does not close the GUI; the small x clears the bind (None =
161 is reserved for the GUI. 162 always-on). Insert is reserved for the GUI.
162- **UI scale**: `clamp(min(screen_w/1920, screen_h/1080), 0.75, 1.5)`; base 163- **UI scale**: `clamp(min(screen_w/1920, screen_h/1080), 0.75, 1.5)`; base
163 window 960x620, body/title fonts 15/20 px at scale 1. 164 window 960x620, body/title fonts 15/20 px at scale 1.
164- Every feature gets Active + Bind; bind None means always-on. 165- Every feature gets Active + Bind; bind None means always-on.
165- System page: config path/build info, Reload config, and a two-click Reset 166- System page: real config path/build info, Reload config (re-reads the INI),
166 that restores feature settings/binds but keeps GUI layout. 167 and a two-click Reset that restores feature settings/binds but keeps GUI
168 layout.
167- Window: fixed-size, draggable by title bar, clamped to screen, position, 169- Window: fixed-size, draggable by title bar, clamped to screen, position,
168 category and expanded cards persisted under `[gui]`. Esc does not close the 170 category and expanded cards persisted under `[gui]` in the same INI. Esc
169 GUI; the toggle key (Insert) is the only close path. There is no title-bar X. 171 does not close the GUI; the toggle key (Insert) is the only close path.
172 There is no title-bar X.
173- GUI controls write the same state the text tree used; sliders persist when
174 the drag ends, toggles/binds/layout persist immediately. `[gui]` stores
175 `x`, `y`, `category` and an `expanded` bitmask (card index order). Every
176 feature now persists an `active` and `bind` pair, including movement, melee
177 and spread.
170 178
171 179
172## Bones / ESP 180## Bones / ESP
diff --git a/docs/adr/0001-custom-isurface-gui.md b/docs/adr/0001-custom-isurface-gui.md
index 5525844..21fc7f6 100644
--- a/docs/adr/0001-custom-isurface-gui.md
+++ b/docs/adr/0001-custom-isurface-gui.md
@@ -53,7 +53,7 @@ path.
53- No new runtime dependency or GL context management. 53- No new runtime dependency or GL context management.
54- The GUI remains inside the engine's VGUI paint path, so it inherits the 54- The GUI remains inside the engine's VGUI paint path, so it inherits the
55 existing font registration and in-game overlay behavior. 55 existing font registration and in-game overlay behavior.
56- The old text menu is removed once the GUI reaches feature parity. During 56- The GUI drives the real feature state/config. The old text menu is removed
57 development the GUI is built alongside the text menu; Insert toggles the 57 after a verification pass; Insert toggles the GUI and F1 keeps the old tree
58 GUI and F1 keeps the old tree accessible. The replacement is complete only 58 accessible until then. The replacement is complete when the old tree is
59 when the old tree is deleted. 59 deleted.
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 @@
1// gui.cpp — custom immediate-mode GUI shell (temporary Insert preview) 1// gui.cpp — custom immediate-mode GUI
2// 2//
3// This is the first vertical slice: the visual shell, categories, cards, 3// Categories, feature cards and controls are wired to the real settings in
4// dummy controls, cursor handling and input freeze. It does not read or write 4// menu.cpp. The old text tree is still reachable on F1 until the GUI has been
5// the real feature settings yet; the next pass replaces the dummy state and 5// verified as a full replacement.
6// removes the text menu.
7 6
8#include "hl2h.h" 7#include "hl2h.h"
9 8
@@ -177,21 +176,24 @@ enum CtrlKind {
177 CK_BUTTON 176 CK_BUTTON
178}; 177};
179 178
179enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET };
180
180struct Ctrl { 181struct Ctrl {
181 int kind; 182 int kind;
182 const char *label; 183 const char *label;
183 const char *desc; 184 const char *desc;
184 int *value; 185 bool *bval; // CK_TOGGLE
186 int *ival; // CK_SLIDER
185 int min, max; 187 int min, max;
188 int action; // CK_BUTTON
186}; 189};
187 190
188struct Card { 191struct Card {
189 int cat; 192 int cat;
193 FeatureState *feat; // nullptr for non-feature cards (System)
190 const char *title; 194 const char *title;
191 const char *desc; 195 const char *desc;
192 bool expanded; 196 bool expanded;
193 bool active;
194 int bind;
195 int c0, n; 197 int c0, n;
196}; 198};
197 199
@@ -199,123 +201,167 @@ static Ctrl g_ctrls[192];
199static int g_nctrl = 0; 201static int g_nctrl = 0;
200static Card g_cards[40]; 202static Card g_cards[40];
201static int g_ncards = 0; 203static int g_ncards = 0;
202static int g_vals[192]; 204static bool g_cards_built = false;
203static bool g_demo_built = false; 205static bool g_layout_loaded = false;
206static bool g_reset_armed = false;
207static double g_reset_armed_at = 0.0;
208static char g_config_label[512];
209static char g_build_label[128];
204 210
205static const char *g_cats[] = {"Movement", "Combat", "Visuals", "Weapons", 211static const char *g_cats[] = {"Movement", "Combat", "Visuals", "Weapons",
206 "System"}; 212 "System"};
207static constexpr int CAT_COUNT = 5; 213static constexpr int CAT_COUNT = 5;
208 214
209static int add_card(int cat, const char *title, const char *desc, 215static int add_card(int cat, FeatureState *feat, const char *title,
210 bool expanded) { 216 const char *desc, bool expanded) {
211 Card &c = g_cards[g_ncards]; 217 Card &c = g_cards[g_ncards];
212 c.cat = cat; 218 c.cat = cat;
219 c.feat = feat;
213 c.title = title; 220 c.title = title;
214 c.desc = desc; 221 c.desc = desc;
215 c.expanded = expanded; 222 c.expanded = expanded;
216 c.active = true;
217 c.bind = 0;
218 c.c0 = g_nctrl; 223 c.c0 = g_nctrl;
219 c.n = 0; 224 c.n = 0;
220 return g_ncards++; 225 return g_ncards++;
221} 226}
222static void add_ctrl(int card, int kind, const char *label, const char *desc, 227static void add_ctrl(int card, int kind, const char *label, const char *desc,
223 int min, int max, int init) { 228 int min, int max, bool *bval, int *ival, int action) {
224 int i = g_nctrl++; 229 int i = g_nctrl++;
225 g_vals[i] = init; 230 Ctrl &c = g_ctrls[i];
226 g_ctrls[i].kind = kind; 231 c.kind = kind;
227 g_ctrls[i].label = label; 232 c.label = label;
228 g_ctrls[i].desc = desc; 233 c.desc = desc;
229 g_ctrls[i].value = &g_vals[i]; 234 c.bval = bval;
230 g_ctrls[i].min = min; 235 c.ival = ival;
231 g_ctrls[i].max = max; 236 c.min = min;
237 c.max = max;
238 c.action = action;
232 g_cards[card].n++; 239 g_cards[card].n++;
233} 240}
234static void add_toggle(int card, const char *label, const char *desc, 241static void add_toggle(int card, const char *label, const char *desc,
235 int init) { 242 bool *value) {
236 add_ctrl(card, CK_TOGGLE, label, desc, 0, 1, init); 243 add_ctrl(card, CK_TOGGLE, label, desc, 0, 1, value, nullptr, ACT_NONE);
237} 244}
238static void add_slider(int card, const char *label, const char *desc, int min, 245static void add_slider(int card, const char *label, const char *desc, int min,
239 int max, int init) { 246 int max, int *value) {
240 add_ctrl(card, CK_SLIDER, label, desc, min, max, init); 247 add_ctrl(card, CK_SLIDER, label, desc, min, max, nullptr, value, ACT_NONE);
241} 248}
242static void add_section(int card, const char *label) { 249static void add_section(int card, const char *label) {
243 add_ctrl(card, CK_SECTION, label, nullptr, 0, 0, 0); 250 add_ctrl(card, CK_SECTION, label, nullptr, 0, 0, nullptr, nullptr, ACT_NONE);
244} 251}
245static void add_info(int card, const char *label) { 252static void add_info(int card, const char *label) {
246 add_ctrl(card, CK_INFO, label, nullptr, 0, 0, 0); 253 add_ctrl(card, CK_INFO, label, nullptr, 0, 0, nullptr, nullptr, ACT_NONE);
247} 254}
248static void add_button(int card, const char *label, const char *desc) { 255static void add_button(int card, const char *label, const char *desc,
249 add_ctrl(card, CK_BUTTON, label, desc, 0, 0, 0); 256 int action) {
257 add_ctrl(card, CK_BUTTON, label, desc, 0, 0, nullptr, nullptr, action);
250} 258}
251 259
252static void build_demo() { 260static int ctrl_val(const Ctrl &c) {
253 if (g_demo_built) 261 if (c.kind == CK_TOGGLE && c.bval)
262 return *c.bval ? 1 : 0;
263 if (c.kind == CK_SLIDER && c.ival)
264 return *c.ival;
265 return 0;
266}
267static void ctrl_set(Ctrl &c, int v) {
268 if (c.kind == CK_TOGGLE && c.bval) {
269 *c.bval = v != 0;
270 config_save();
271 } else if (c.kind == CK_SLIDER && c.ival) {
272 if (v < c.min) v = c.min;
273 if (v > c.max) v = c.max;
274 if (*c.ival != v) {
275 *c.ival = v;
276 // Slider drags persist on mouse release; discrete changes persist now.
277 }
278 }
279}
280
281static void build_cards() {
282 if (g_cards_built)
254 return; 283 return;
255 g_demo_built = true; 284 g_cards_built = true;
256 285
257 int bhop = add_card(0, "Bunnyhop", "Automatic jump on landing.", true); 286 int bhop = add_card(0, &g_bhop, "Bunnyhop", "Automatic jump on landing.",
287 true);
258 add_toggle(bhop, "Uni-directional auto-strafe", 288 add_toggle(bhop, "Uni-directional auto-strafe",
259 "Rotate airborne movement into the velocity.", 1); 289 "Rotate airborne movement into the velocity.",
290 &g_bhop_autostrafe);
260 291
261 int abh = add_card(0, "ABH", "Accelerate on the jump tick by facing away " 292 add_card(0, &g_abh, "ABH", "Accelerate on the jump tick by facing away "
262 "from the velocity.", true); 293 "from the velocity.", true);
263 (void)abh;
264 294
265 int jb = add_card(0, "Jumpbug", "Duck+jump when landing would hurt.", true); 295 int jb = add_card(0, &g_jumpbug, "Jumpbug",
296 "Duck+jump when landing would hurt.", true);
266 add_toggle(jb, "Assist timing", 297 add_toggle(jb, "Assist timing",
267 "Nudge the final command into the unduck window.", 0); 298 "Nudge the final command into the unduck window.",
299 &g_jumpbug_assist);
268 300
269 int aim = add_card(1, "Aimbot", "Smoothly snap to a hostile target.", true); 301 int aim = add_card(1, &g_aimbot, "Aimbot",
270 g_cards[aim].bind = 604; 302 "Smoothly snap to a hostile target.", true);
271 add_toggle(aim, "Vischeck", "Require world line of sight.", 1); 303 add_toggle(aim, "Vischeck", "Require world line of sight.", &g_aim_vischeck);
272 add_slider(aim, "FOV", "Maximum angular distance from the crosshair.", 1, 180, 304 add_slider(aim, "FOV", "Maximum angular distance from the crosshair.", 1, 180,
273 60); 305 &g_aim_fov);
274 add_slider(aim, "Smoothness", "Maximum degrees per tick.", 1, 45, 5); 306 add_slider(aim, "Smoothness", "Maximum degrees per tick.", 1, 45,
275 add_toggle(aim, "Show FOV circle", "Draw the acquisition cone on screen.", 1); 307 &g_aim_smooth);
308 add_toggle(aim, "Show FOV circle", "Draw the acquisition cone on screen.",
309 &g_aim_fov_circle);
276 add_section(aim, "Bones"); 310 add_section(aim, "Bones");
277 add_toggle(aim, "Head", "Aim at the head hitbox.", 1); 311 static const char *bone_names[AIM_BONE_COUNT] = {
278 add_toggle(aim, "Torso", "Aim at the chest/stomach.", 0); 312 "Head", "Torso", "Left arm", "Right arm", "Left leg", "Right leg"};
279 add_toggle(aim, "Left arm", "Include the left arm.", 0); 313 static const char *bone_descs[AIM_BONE_COUNT] = {
280 add_toggle(aim, "Right arm", "Include the right arm.", 0); 314 "Aim at the head hitbox.", "Aim at the chest/stomach.",
281 add_toggle(aim, "Left leg", "Include the left leg.", 0); 315 "Include the left arm.", "Include the right arm.",
282 add_toggle(aim, "Right leg", "Include the right leg.", 0); 316 "Include the left leg.", "Include the right leg."};
283 317 for (int i = 0; i < AIM_BONE_COUNT; i++)
284 int sil = add_card(1, "Silent aim", "Fire at the target without turning " 318 add_toggle(aim, bone_names[i], bone_descs[i], &g_aim_bones[i]);
285 "the rendered view.", true); 319
286 g_cards[sil].bind = 602; 320 int sil = add_card(1, &g_silentaim, "Silent aim",
321 "Fire at the target without turning the rendered view.",
322 true);
287 add_slider(sil, "FOV", "Maximum angular distance from the crosshair.", 1, 180, 323 add_slider(sil, "FOV", "Maximum angular distance from the crosshair.", 1, 180,
288 60); 324 &g_silent_fov);
289 325
290 int tb = add_card(1, "Triggerbot", "Fire when the crosshair is on a hostile.", 326 add_card(1, &g_triggerbot, "Triggerbot",
291 true); 327 "Fire when the crosshair is on a hostile.", true);
292 g_cards[tb].bind = 603; 328
293 329 add_card(1, &g_melee, "Melee killbot", "360-degree crowbar silent aim.",
294 int melee = add_card(1, "Melee killbot", "360-degree crowbar silent aim.", 330 true);
295 true); 331
296 332 int esp = add_card(2, &g_esp, "ESP", "Draw hostile NPC information.", true);
297 int esp = add_card(2, "ESP", "Draw hostile NPC information.", true); 333 static const char *esp_names[ESP_OPT_COUNT] = {
298 add_toggle(esp, "Show neutral", "Include neutral wildlife.", 1); 334 "Show neutral", "Show healthbar", "Show healthnum",
299 add_toggle(esp, "Show healthbar", "Draw a health bar.", 1); 335 "Show name", "Show skeleton", "Show box",
300 add_toggle(esp, "Show healthnum", "Draw the numeric health value.", 1); 336 "Show speed graph"};
301 add_toggle(esp, "Show name", "Draw the model name.", 1); 337 static const char *esp_descs[ESP_OPT_COUNT] = {
302 add_toggle(esp, "Show skeleton", "Draw the animated bone links.", 1); 338 "Include neutral wildlife.", "Draw a health bar.",
303 add_toggle(esp, "Show box", "Draw a bounding box.", 1); 339 "Draw the numeric health value.", "Draw the model name.",
304 add_toggle(esp, "Show speed graph", "Draw the lower-center speed graph.", 1); 340 "Draw the animated bone links.", "Draw a bounding box.",
305 341 "Draw the lower-center speed graph."};
306 int spread = add_card(3, "Spread control", 342 for (int i = 0; i < ESP_OPT_COUNT; i++)
343 add_toggle(esp, esp_names[i], esp_descs[i], &g_esp_opt[i]);
344
345 int spread = add_card(3, &g_spread, "Spread control",
307 "Choose a server shot seed that tightens the cone.", 346 "Choose a server shot seed that tightens the cone.",
308 true); 347 true);
309 add_toggle(spread, "No spread", 348 add_toggle(spread, "No spread",
310 "Override the FireBullets seed with the best offset.", 1); 349 "Override the FireBullets seed with the best offset.",
311 350 &g_spread_no_spread);
312 int sys = add_card(4, "System", "Configuration and maintenance.", true); 351
313 add_info(sys, "Config: ~/.config/hl2hake/config"); 352 int sys = add_card(4, nullptr, "System", "Configuration and maintenance.",
314 add_info(sys, "Build: GUI shell preview"); 353 true);
315 add_button(sys, "Reload config", "Re-read the INI without restarting."); 354 snprintf(g_config_label, sizeof(g_config_label), "Config: %s",
316 add_button(sys, "Reset defaults", "Restore feature defaults; GUI layout kept."); 355 config_path_cstr());
317 356 snprintf(g_build_label, sizeof(g_build_label), "Build: %s %s", __DATE__,
318 (void)g_cards[melee]; 357 __TIME__);
358 add_info(sys, g_config_label);
359 add_info(sys, g_build_label);
360 add_button(sys, "Reload config", "Re-read the INI without restarting.",
361 ACT_RELOAD);
362 add_button(sys, "Reset defaults",
363 "Click twice to restore feature defaults; GUI layout kept.",
364 ACT_RESET);
319} 365}
320 366
321// --------------------------------------------------------------------------- 367// ---------------------------------------------------------------------------
@@ -344,6 +390,7 @@ static bool g_mouse_click = false;
344static bool g_mouse_down = false; 390static bool g_mouse_down = false;
345static bool g_mouse_rclick = false; 391static bool g_mouse_rclick = false;
346static unsigned int g_prev_mask = 0; 392static unsigned int g_prev_mask = 0;
393static int g_mouse_press = 0; // 1..5 on the frame a button goes down
347static unsigned char g_prev_keys[512] = {}; 394static unsigned char g_prev_keys[512] = {};
348static bool g_prev_cursor = true; 395static bool g_prev_cursor = true;
349static bool g_prev_relative = true; 396static bool g_prev_relative = true;
@@ -480,21 +527,6 @@ static void line(int x0, int y0, int x1, int y1, const Col &c) {
480 s_line(x0, y0, x1, y1); 527 s_line(x0, y0, x1, y1);
481} 528}
482 529
483static const char *bind_label(int b) {
484 switch (b) {
485 case 0: return "None";
486 case 600: return "MouseL";
487 case 601: return "MouseR";
488 case 602: return "MouseM";
489 case 603: return "Mouse4";
490 case 604: return "Mouse5";
491 case 41: return "Esc";
492 case 73: return "Insert";
493 case 59: return "F2";
494 default: return "Key";
495 }
496}
497
498static void layout() { 530static void layout() {
499 int w = g_scr_w, h = g_scr_h; 531 int w = g_scr_w, h = g_scr_h;
500 if (g_surface) { 532 if (g_surface) {
@@ -575,6 +607,7 @@ static void close_gui() {
575 g_dragging = false; 607 g_dragging = false;
576 g_scroll_drag = false; 608 g_scroll_drag = false;
577 g_slider_drag = -1; 609 g_slider_drag = -1;
610 g_reset_armed = false;
578 input_clear_mouse_states(); 611 input_clear_mouse_states();
579 if (g_sdl_ready) { 612 if (g_sdl_ready) {
580 if (sd_set_relative) 613 if (sd_set_relative)
@@ -582,6 +615,7 @@ static void close_gui() {
582 if (sd_show_cursor) 615 if (sd_show_cursor)
583 sd_show_cursor(g_prev_cursor ? 1 : 0); 616 sd_show_cursor(g_prev_cursor ? 1 : 0);
584 } 617 }
618 config_save();
585 logf("gui: closed"); 619 logf("gui: closed");
586} 620}
587 621
@@ -617,7 +651,8 @@ static void open_gui() {
617 g_capture_card = -1; 651 g_capture_card = -1;
618 g_wheel.exchange(0); 652 g_wheel.exchange(0);
619 g_scroll = 0.0f; 653 g_scroll = 0.0f;
620 g_cat = 0; 654 if (g_cat < 0 || g_cat >= CAT_COUNT)
655 g_cat = 0;
621 menu_force_close(); 656 menu_force_close();
622 logf("gui: opened"); 657 logf("gui: opened");
623} 658}
@@ -630,8 +665,16 @@ static void update_mouse() {
630 g_mouse_x = mx; 665 g_mouse_x = mx;
631 g_mouse_y = my; 666 g_mouse_y = my;
632 g_mouse_down = (mask & 1u) != 0; 667 g_mouse_down = (mask & 1u) != 0;
633 g_mouse_click = g_mouse_down && !(g_prev_mask & 1u); 668 g_mouse_press = 0;
634 g_mouse_rclick = ((mask & 2u) != 0) && !(g_prev_mask & 2u); 669 unsigned int pressed = mask & ~g_prev_mask;
670 for (int b = 0; b < 5; b++) {
671 if (pressed & (1u << b)) {
672 g_mouse_press = b + 1;
673 break;
674 }
675 }
676 g_mouse_click = g_mouse_press == 1;
677 g_mouse_rclick = g_mouse_press == 2;
635 if (g_mouse_x < 0) g_mouse_x = 0; 678 if (g_mouse_x < 0) g_mouse_x = 0;
636 if (g_mouse_y < 0) g_mouse_y = 0; 679 if (g_mouse_y < 0) g_mouse_y = 0;
637 g_prev_mask = mask; 680 g_prev_mask = mask;
@@ -648,14 +691,15 @@ static void handle_capture(const unsigned char *keys) {
648 g_capture_card = -1; 691 g_capture_card = -1;
649 return; 692 return;
650 } 693 }
651 if (g_mouse_click) { 694 Card &card = g_cards[g_capture_card];
652 g_cards[g_capture_card].bind = 600; 695 if (g_mouse_press >= 1 && g_mouse_press <= 5 && card.feat) {
653 g_capture_card = -1; 696 card.feat->bind = 600 + (g_mouse_press - 1);
654 return; 697 // Consume the click: it must not immediately re-arm capture on the same
655 } 698 // bind box.
656 if (g_mouse_rclick) { 699 g_mouse_click = false;
657 g_cards[g_capture_card].bind = 601; 700 g_mouse_rclick = false;
658 g_capture_card = -1; 701 g_capture_card = -1;
702 config_save();
659 return; 703 return;
660 } 704 }
661 for (int k = 0; k < 512; k++) { 705 for (int k = 0; k < 512; k++) {
@@ -663,8 +707,12 @@ static void handle_capture(const unsigned char *keys) {
663 continue; 707 continue;
664 if (k == 73) // Insert is reserved for opening/closing the GUI 708 if (k == 73) // Insert is reserved for opening/closing the GUI
665 continue; 709 continue;
666 g_cards[g_capture_card].bind = k; 710 if (k == 58) // F1 opens the old text tree during the verification pass
711 continue;
712 if (card.feat)
713 card.feat->bind = k;
667 g_capture_card = -1; 714 g_capture_card = -1;
715 config_save();
668 return; 716 return;
669 } 717 }
670} 718}
@@ -674,7 +722,7 @@ static void handle_mouse_click() {
674 return; 722 return;
675 int mx = g_mouse_x, my = g_mouse_y; 723 int mx = g_mouse_x, my = g_mouse_y;
676 724
677 // title bar: drag or close 725 // title bar: drag
678 Rect title{g_win_x, g_win_y, g_win_w, S(38.0f)}; 726 Rect title{g_win_x, g_win_y, g_win_w, S(38.0f)};
679 if (title.has(mx, my)) { 727 if (title.has(mx, my)) {
680 g_dragging = true; 728 g_dragging = true;
@@ -693,6 +741,7 @@ static void handle_mouse_click() {
693 if (r.has(mx, my)) { 741 if (r.has(mx, my)) {
694 g_cat = i; 742 g_cat = i;
695 g_scroll = 0.0f; 743 g_scroll = 0.0f;
744 config_save();
696 return; 745 return;
697 } 746 }
698 } 747 }
@@ -736,18 +785,20 @@ static void handle_mouse_click() {
736 if (my >= cr.y && my < cr.y + ch) { 785 if (my >= cr.y && my < cr.y + ch) {
737 // header 786 // header
738 if (my < cr.y + S(36.0f)) { 787 if (my < cr.y + S(36.0f)) {
739 if (c.cat != 4) { 788 if (c.feat) {
740 Rect active{cr.x + cr.w - S(150.0f), cr.y + S(9.0f), S(18.0f), 789 Rect active{cr.x + cr.w - S(170.0f), cr.y + S(9.0f), S(18.0f),
741 S(18.0f)}; 790 S(18.0f)};
742 Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f), 791 Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f),
743 S(22.0f)}; 792 S(22.0f)};
744 if (active.has(mx, my)) { 793 if (active.has(mx, my)) {
745 c.active = !c.active; 794 c.feat->active = !c.feat->active;
795 config_save();
746 return; 796 return;
747 } 797 }
748 if (bind.has(mx, my)) { 798 if (bind.has(mx, my)) {
749 if (mx >= bind.x + bind.w - S(18.0f)) { 799 if (mx >= bind.x + bind.w - S(18.0f)) {
750 c.bind = 0; 800 c.feat->bind = 0;
801 config_save();
751 return; 802 return;
752 } 803 }
753 g_capture_card = i; 804 g_capture_card = i;
@@ -755,6 +806,7 @@ static void handle_mouse_click() {
755 } 806 }
756 } 807 }
757 c.expanded = !c.expanded; 808 c.expanded = !c.expanded;
809 config_save();
758 return; 810 return;
759 } 811 }
760 // body 812 // body
@@ -767,7 +819,7 @@ static void handle_mouse_click() {
767 continue; 819 continue;
768 } 820 }
769 if (ct.kind == CK_TOGGLE) { 821 if (ct.kind == CK_TOGGLE) {
770 *ct.value = !*ct.value; 822 ctrl_set(ct, !ctrl_val(ct));
771 return; 823 return;
772 } 824 }
773 if (ct.kind == CK_SLIDER) { 825 if (ct.kind == CK_SLIDER) {
@@ -778,7 +830,7 @@ static void handle_mouse_click() {
778 int v = ct.min + (int)(t * (ct.max - ct.min) + 0.5f); 830 int v = ct.min + (int)(t * (ct.max - ct.min) + 0.5f);
779 if (v < ct.min) v = ct.min; 831 if (v < ct.min) v = ct.min;
780 if (v > ct.max) v = ct.max; 832 if (v > ct.max) v = ct.max;
781 *ct.value = v; 833 ctrl_set(ct, v);
782 g_slider_drag = c.c0 + j; 834 g_slider_drag = c.c0 + j;
783 } 835 }
784 return; 836 return;
@@ -787,7 +839,20 @@ static void handle_mouse_click() {
787 Rect b{cr.x + cr.w - S(176.0f), by + S(3.0f), S(160.0f), 839 Rect b{cr.x + cr.w - S(176.0f), by + S(3.0f), S(160.0f),
788 S(26.0f)}; 840 S(26.0f)};
789 if (b.has(mx, my)) { 841 if (b.has(mx, my)) {
790 logf("gui: dummy action %s", ct.label ? ct.label : "?"); 842 if (ct.action == ACT_RELOAD) {
843 config_reload();
844 logf("gui: config reloaded");
845 } else if (ct.action == ACT_RESET) {
846 double now = now_sec();
847 if (g_reset_armed && now - g_reset_armed_at < 3.0) {
848 config_reset_defaults();
849 g_reset_armed = false;
850 logf("gui: defaults restored");
851 } else {
852 g_reset_armed = true;
853 g_reset_armed_at = now;
854 }
855 }
791 return; 856 return;
792 } 857 }
793 } 858 }
@@ -815,7 +880,25 @@ static void handle_hover() {
815 int ch = card_h(c); 880 int ch = card_h(c);
816 if (my >= cr.y && my < cr.y + ch) { 881 if (my >= cr.y && my < cr.y + ch) {
817 if (my < cr.y + S(36.0f)) { 882 if (my < cr.y + S(36.0f)) {
818 g_hover_desc = c.desc; 883 if (c.feat) {
884 Rect active{cr.x + cr.w - S(170.0f), cr.y + S(9.0f), S(18.0f),
885 S(18.0f)};
886 Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f),
887 S(22.0f)};
888 if (active.has(mx, my))
889 g_hover_desc = "Enable this feature. A bind, when set, must be "
890 "held for it to run.";
891 else if (bind.has(mx, my))
892 g_hover_desc = (mx >= bind.x + bind.w - S(18.0f))
893 ? "Clear the bind. An unbound feature is "
894 "always-on."
895 : "Click, then press a key or mouse button. "
896 "Esc cancels.";
897 else
898 g_hover_desc = c.desc;
899 } else {
900 g_hover_desc = c.desc;
901 }
819 return; 902 return;
820 } 903 }
821 int by = cr.y + S(36.0f) + S(6.0f); 904 int by = cr.y + S(36.0f) + S(6.0f);
@@ -840,7 +923,7 @@ void gui_handle_input() {
840 gui_init_sdl(); 923 gui_init_sdl();
841 if (!g_surface) 924 if (!g_surface)
842 return; 925 return;
843 build_demo(); 926 build_cards();
844 layout(); 927 layout();
845 if (g_scr_w <= 0 || g_scr_h <= 0) 928 if (g_scr_w <= 0 || g_scr_h <= 0)
846 return; 929 return;
@@ -895,6 +978,7 @@ void gui_handle_input() {
895 if (g_dragging) { 978 if (g_dragging) {
896 if (!g_mouse_down) { 979 if (!g_mouse_down) {
897 g_dragging = false; 980 g_dragging = false;
981 config_save();
898 } else { 982 } else {
899 g_win_x = g_mouse_x - g_drag_dx; 983 g_win_x = g_mouse_x - g_drag_dx;
900 g_win_y = g_mouse_y - g_drag_dy; 984 g_win_y = g_mouse_y - g_drag_dy;
@@ -910,6 +994,7 @@ void gui_handle_input() {
910 if (g_slider_drag >= 0) { 994 if (g_slider_drag >= 0) {
911 if (!g_mouse_down) { 995 if (!g_mouse_down) {
912 g_slider_drag = -1; 996 g_slider_drag = -1;
997 config_save();
913 } else { 998 } else {
914 Ctrl &ct = g_ctrls[g_slider_drag]; 999 Ctrl &ct = g_ctrls[g_slider_drag];
915 Rect content{g_win_x + S(190.0f), g_win_y + S(38.0f), 1000 Rect content{g_win_x + S(190.0f), g_win_y + S(38.0f),
@@ -919,9 +1004,7 @@ void gui_handle_input() {
919 (void)content; 1004 (void)content;
920 float t = (float)(g_mouse_x - x0) / (float)(x1 - x0); 1005 float t = (float)(g_mouse_x - x0) / (float)(x1 - x0);
921 int v = ct.min + (int)(t * (ct.max - ct.min) + 0.5f); 1006 int v = ct.min + (int)(t * (ct.max - ct.min) + 0.5f);
922 if (v < ct.min) v = ct.min; 1007 ctrl_set(ct, v);
923 if (v > ct.max) v = ct.max;
924 *ct.value = v;
925 } 1008 }
926 } 1009 }
927 1010
@@ -950,9 +1033,8 @@ void gui_handle_input() {
950 if (left_edge || right_edge) { 1033 if (left_edge || right_edge) {
951 if (g_hover_slider >= 0) { 1034 if (g_hover_slider >= 0) {
952 Ctrl &ct = g_ctrls[g_hover_slider]; 1035 Ctrl &ct = g_ctrls[g_hover_slider];
953 *ct.value += right_edge ? 1 : -1; 1036 ctrl_set(ct, ctrl_val(ct) + (right_edge ? 1 : -1));
954 if (*ct.value < ct.min) *ct.value = ct.min; 1037 config_save();
955 if (*ct.value > ct.max) *ct.value = ct.max;
956 } 1038 }
957 } 1039 }
958 1040
@@ -1003,15 +1085,16 @@ static void draw_slider(const Rect &clip, const Rect &cr, int y, const Ctrl &ct)
1003 if (track_w < S(30.0f)) track_w = S(30.0f); 1085 if (track_w < S(30.0f)) track_w = S(30.0f);
1004 Rect track{track_x, y + S(9.0f), track_w, S(8.0f)}; 1086 Rect track{track_x, y + S(9.0f), track_w, S(8.0f)};
1005 fill(track, Col{38, 42, 50, 255}, &clip); 1087 fill(track, Col{38, 42, 50, 255}, &clip);
1088 int value = ctrl_val(ct);
1006 float t = (ct.max > ct.min) 1089 float t = (ct.max > ct.min)
1007 ? (float)(*ct.value - ct.min) / (float)(ct.max - ct.min) 1090 ? (float)(value - ct.min) / (float)(ct.max - ct.min)
1008 : 0.0f; 1091 : 0.0f;
1009 int fw = (int)(track_w * t + 0.5f); 1092 int fw = (int)(track_w * t + 0.5f);
1010 fill(Rect{track.x, track.y, fw, track.h}, C_ACCENT_DIM, &clip); 1093 fill(Rect{track.x, track.y, fw, track.h}, C_ACCENT_DIM, &clip);
1011 int kx = track.x + fw - S(4.0f); 1094 int kx = track.x + fw - S(4.0f);
1012 fill(Rect{kx, track.y - S(4.0f), S(8.0f), S(16.0f)}, C_ACCENT, &clip); 1095 fill(Rect{kx, track.y - S(4.0f), S(8.0f), S(16.0f)}, C_ACCENT, &clip);
1013 char val[32]; 1096 char val[32];
1014 snprintf(val, sizeof(val), "%d", *ct.value); 1097 snprintf(val, sizeof(val), "%d", value);
1015 text_in(clip, cr.x + cr.w - S(52.0f), y + S(4.0f), val, g_font_body, g_body_h, 1098 text_in(clip, cr.x + cr.w - S(52.0f), y + S(4.0f), val, g_font_body, g_body_h,
1016 S(44.0f), C_TEXT); 1099 S(44.0f), C_TEXT);
1017} 1100}
@@ -1037,15 +1120,18 @@ static void draw_card(const Rect &clip, int index, int y) {
1037 } 1120 }
1038 text_in(clip, cr.x + S(24.0f), cr.y + S(8.0f), c.title, g_font_title, 1121 text_in(clip, cr.x + S(24.0f), cr.y + S(8.0f), c.title, g_font_title,
1039 g_title_h, cr.w - S(180.0f), C_TEXT); 1122 g_title_h, cr.w - S(180.0f), C_TEXT);
1040 if (c.cat != 4) { 1123 if (c.feat) {
1041 draw_checkbox(cr.x + cr.w - S(150.0f), cr.y + S(10.0f), c.active); 1124 bool active = c.feat->active;
1042 text_in(clip, cr.x + cr.w - S(128.0f), cr.y + S(9.0f), 1125 draw_checkbox(cr.x + cr.w - S(170.0f), cr.y + S(10.0f), active);
1043 c.active ? "On" : "Off", g_font_body, g_body_h, S(30.0f), 1126 text_in(clip, cr.x + cr.w - S(148.0f), cr.y + S(9.0f),
1044 c.active ? C_GOOD : C_DIM); 1127 active ? "On" : "Off", g_font_body, g_body_h, S(32.0f),
1128 active ? C_GOOD : C_DIM);
1045 Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f), S(22.0f)}; 1129 Rect bind{cr.x + cr.w - S(112.0f), cr.y + S(7.0f), S(100.0f), S(22.0f)};
1046 fill(bind, Col{45, 50, 60, 255}, &clip); 1130 fill(bind, Col{45, 50, 60, 255}, &clip);
1047 outline(bind, C_BORDER, &clip); 1131 outline(bind, C_BORDER, &clip);
1048 const char *bl = (g_capture_card == index) ? "Press..." : bind_label(c.bind); 1132 const char *bl = (g_capture_card == index)
1133 ? "Press..."
1134 : scancode_name(c.feat->bind);
1049 text_in(clip, bind.x + S(7.0f), bind.y + S(3.0f), bl, g_font_body, g_body_h, 1135 text_in(clip, bind.x + S(7.0f), bind.y + S(3.0f), bl, g_font_body, g_body_h,
1050 bind.w - S(22.0f), C_TEXT); 1136 bind.w - S(22.0f), C_TEXT);
1051 text_in(clip, bind.x + bind.w - S(16.0f), bind.y + S(3.0f), "x", 1137 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) {
1068 Rect b{cr.x + cr.w - S(176.0f), by + S(3.0f), S(160.0f), S(26.0f)}; 1154 Rect b{cr.x + cr.w - S(176.0f), by + S(3.0f), S(160.0f), S(26.0f)};
1069 fill(b, Col{45, 50, 60, 255}, &clip); 1155 fill(b, Col{45, 50, 60, 255}, &clip);
1070 outline(b, C_BORDER, &clip); 1156 outline(b, C_BORDER, &clip);
1071 text_in(clip, b.x + S(10.0f), b.y + S(4.0f), ct.label, g_font_body, 1157 const char *label = ct.label;
1158 if (ct.action == ACT_RESET && g_reset_armed) {
1159 if (now_sec() - g_reset_armed_at >= 3.0)
1160 g_reset_armed = false;
1161 else
1162 label = "Confirm reset";
1163 }
1164 text_in(clip, b.x + S(10.0f), b.y + S(4.0f), label, g_font_body,
1072 g_body_h, b.w - S(16.0f), C_TEXT); 1165 g_body_h, b.w - S(16.0f), C_TEXT);
1073 } else { 1166 } else {
1074 text_in(clip, cr.x + S(16.0f), by + S(4.0f), ct.label, g_font_body, 1167 text_in(clip, cr.x + S(16.0f), by + S(4.0f), ct.label, g_font_body,
1075 g_body_h, S(220.0f), C_TEXT); 1168 g_body_h, S(220.0f), C_TEXT);
1076 if (ct.kind == CK_TOGGLE) { 1169 if (ct.kind == CK_TOGGLE) {
1077 draw_checkbox(cr.x + cr.w - S(31.0f), by + S(5.0f), *ct.value != 0); 1170 bool on = ctrl_val(ct) != 0;
1171 draw_checkbox(cr.x + cr.w - S(31.0f), by + S(5.0f), on);
1078 text_in(clip, cr.x + cr.w - S(58.0f), by + S(3.0f), 1172 text_in(clip, cr.x + cr.w - S(58.0f), by + S(3.0f),
1079 *ct.value ? "On" : "Off", g_font_body, g_body_h, S(26.0f), 1173 on ? "On" : "Off", g_font_body, g_body_h, S(26.0f),
1080 *ct.value ? C_GOOD : C_DIM); 1174 on ? C_GOOD : C_DIM);
1081 } else if (ct.kind == CK_SLIDER) { 1175 } else if (ct.kind == CK_SLIDER) {
1082 draw_slider(clip, cr, by, ct); 1176 draw_slider(clip, cr, by, ct);
1083 } 1177 }
@@ -1126,8 +1220,8 @@ void gui_draw() {
1126 fill(Rect{win.x, win.y, S(4.0f), win.h}, C_ACCENT); 1220 fill(Rect{win.x, win.y, S(4.0f), win.h}, C_ACCENT);
1127 text_in(win, win.x + S(16.0f), win.y + S(7.0f), "HL2hake", g_font_title, 1221 text_in(win, win.x + S(16.0f), win.y + S(7.0f), "HL2hake", g_font_title,
1128 g_title_h, win.w - S(80.0f), C_TEXT); 1222 g_title_h, win.w - S(80.0f), C_TEXT);
1129 text_in(win, win.x + S(110.0f), win.y + S(10.0f), "GUI shell preview", 1223 text_in(win, win.x + S(110.0f), win.y + S(10.0f), "Settings", g_font_body,
1130 g_font_body, g_body_h, win.w - S(180.0f), C_DIM); 1224 g_body_h, win.w - S(180.0f), C_DIM);
1131 1225
1132 Rect side{win.x, win.y + S(38.0f), S(190.0f), 1226 Rect side{win.x, win.y + S(38.0f), S(190.0f),
1133 win.h - S(38.0f) - S(30.0f)}; 1227 win.h - S(38.0f) - S(30.0f)};
@@ -1181,7 +1275,7 @@ void gui_draw() {
1181 "config: ~/.config/hl2hake/config", g_font_body, g_body_h, 1275 "config: ~/.config/hl2hake/config", g_font_body, g_body_h,
1182 foot.w - S(220.0f), C_DIM); 1276 foot.w - S(220.0f), C_DIM);
1183 text_in(foot, foot.x + foot.w - S(200.0f), foot.y + S(6.0f), 1277 text_in(foot, foot.x + foot.w - S(200.0f), foot.y + S(6.0f),
1184 g_capture_card >= 0 ? "press a key / Esc" : "INS close | F1 text menu", 1278 g_capture_card >= 0 ? "press a key / Esc" : "INS close | F1 old menu",
1185 g_font_body, g_body_h, S(190.0f), C_DIM); 1279 g_font_body, g_body_h, S(190.0f), C_DIM);
1186 1280
1187 draw_tooltip(); 1281 draw_tooltip();
@@ -1192,6 +1286,78 @@ bool gui_bind_capture_active() { return g_open && g_capture_card >= 0; }
1192QAngle gui_frozen_view() { return g_frozen; } 1286QAngle gui_frozen_view() { return g_frozen; }
1193 1287
1194// --------------------------------------------------------------------------- 1288// ---------------------------------------------------------------------------
1289// [gui] layout persistence
1290// ---------------------------------------------------------------------------
1291
1292static void gui_load_layout() {
1293 FILE *f = fopen(config_path_cstr(), "r");
1294 if (!f)
1295 return;
1296 bool in_gui = false, have_x = false, have_y = false;
1297 int x = 0, y = 0, cat = 0;
1298 unsigned expanded = 0;
1299 bool have_expanded = false;
1300 char line[256];
1301 while (fgets(line, sizeof(line), f)) {
1302 line[strcspn(line, "\r\n")] = 0;
1303 if (line[0] == '[') {
1304 in_gui = strstr(line, "[gui]") != nullptr;
1305 continue;
1306 }
1307 if (!in_gui)
1308 continue;
1309 char *eq = strchr(line, '=');
1310 if (!eq)
1311 continue;
1312 *eq = 0;
1313 const char *key = line, *val = eq + 1;
1314 if (strcmp(key, "x") == 0) {
1315 x = atoi(val);
1316 have_x = true;
1317 } else if (strcmp(key, "y") == 0) {
1318 y = atoi(val);
1319 have_y = true;
1320 } else if (strcmp(key, "category") == 0) {
1321 cat = atoi(val);
1322 } else if (strcmp(key, "expanded") == 0) {
1323 expanded = (unsigned)strtoul(val, nullptr, 10);
1324 have_expanded = true;
1325 }
1326 }
1327 fclose(f);
1328 if (have_x && have_y) {
1329 g_win_x = x;
1330 g_win_y = y;
1331 g_positioned = true;
1332 }
1333 if (cat >= 0 && cat < CAT_COUNT)
1334 g_cat = cat;
1335 if (have_expanded) {
1336 for (int i = 0; i < g_ncards && i < 32; i++)
1337 g_cards[i].expanded = (expanded & (1u << i)) != 0;
1338 }
1339}
1340
1341void gui_config_init() {
1342 build_cards();
1343 if (g_layout_loaded)
1344 return;
1345 g_layout_loaded = true;
1346 gui_load_layout();
1347}
1348
1349void gui_config_write(FILE *f) {
1350 if (!f || !g_cards_built || !g_positioned)
1351 return;
1352 unsigned expanded = 0;
1353 for (int i = 0; i < g_ncards && i < 32; i++)
1354 if (g_cards[i].expanded)
1355 expanded |= 1u << i;
1356 fprintf(f, "[gui]\nx=%d\ny=%d\ncategory=%d\nexpanded=%u\n", g_win_x,
1357 g_win_y, g_cat, expanded);
1358}
1359
1360// ---------------------------------------------------------------------------
1195// SDL cursor interposition 1361// SDL cursor interposition
1196// --------------------------------------------------------------------------- 1362// ---------------------------------------------------------------------------
1197// The game re-asserts relative mouse mode / hides the cursor every frame. While 1363// The game re-asserts relative mouse mode / hides the cursor every frame. While
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 {
174 ESP_OPT_SHOW_SPEEDGRAPH = 6, 174 ESP_OPT_SHOW_SPEEDGRAPH = 6,
175 ESP_OPT_COUNT = 7 175 ESP_OPT_COUNT = 7
176}; 176};
177enum AimBone {
178 AIM_BONE_HEAD = 0,
179 AIM_BONE_TORSO,
180 AIM_BONE_LARM,
181 AIM_BONE_RARM,
182 AIM_BONE_LLEG,
183 AIM_BONE_RLEG,
184 AIM_BONE_COUNT
185};
186
187// ---------------------------------------------------------------------------
188// feature state / settings (owned by menu.cpp until the text menu is removed)
189// ---------------------------------------------------------------------------
190
191struct FeatureState {
192 bool active;
193 int bind; // SDL scancode, 0 = none (always-on)
194};
195
196extern FeatureState g_bhop, g_abh, g_jumpbug, g_melee, g_spread, g_esp,
197 g_aimbot, g_triggerbot, g_silentaim;
198extern bool g_bhop_autostrafe;
199extern bool g_jumpbug_assist;
200extern bool g_spread_no_spread;
201extern bool g_aim_bones[AIM_BONE_COUNT];
202extern int g_aim_smooth;
203extern bool g_aim_vischeck;
204extern int g_aim_fov;
205extern bool g_aim_fov_circle;
206extern int g_silent_fov;
207extern bool g_esp_opt[ESP_OPT_COUNT];
208
209const char *scancode_name(int sc);
210void config_save();
211void config_reload();
212void config_reset_defaults();
213const char *config_path_cstr();
177 214
178// --------------------------------------------------------------------------- 215// ---------------------------------------------------------------------------
179// cross-file declarations 216// cross-file declarations
@@ -237,6 +274,8 @@ void gui_draw();
237bool gui_is_open(); 274bool gui_is_open();
238bool gui_bind_capture_active(); 275bool gui_bind_capture_active();
239QAngle gui_frozen_view(); 276QAngle gui_frozen_view();
277void gui_config_init();
278void gui_config_write(FILE *f);
240// ISurface pointer (defined in menu.cpp, used for drawing tests) 279// ISurface pointer (defined in menu.cpp, used for drawing tests)
241struct ISurface; 280struct ISurface;
242extern ISurface *g_surface; 281extern 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 {
52 int value_step = 0; 52 int value_step = 0;
53}; 53};
54 54
55// feature data 55// feature data (declared in hl2h.h; shared with the GUI)
56struct FeatureState { 56FeatureState g_bhop{true, 0};
57 bool active; 57bool g_bhop_autostrafe = true;
58 int bind; // SDL scancode, 0 = none 58
59}; 59FeatureState g_abh{true, 0};
60 60FeatureState g_jumpbug{true, 0};
61static FeatureState g_bhop{true, 0}; 61bool g_jumpbug_assist = false;
62static bool g_bhop_autostrafe = true; 62FeatureState g_melee{true, 0};
63 63FeatureState g_spread{true, 0};
64static FeatureState g_abh{true, 0}; 64bool g_spread_no_spread = true;
65static FeatureState g_jumpbug{true, 0}; 65FeatureState g_esp{true, 0};
66static bool g_jumpbug_assist = false; 66FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu
67static FeatureState g_melee{true, 0}; 67FeatureState g_triggerbot{true, 603}; // default Mouse4
68static FeatureState g_spread{true, 0}; 68FeatureState g_silentaim{true, 602}; // default Mouse3
69static bool g_spread_no_spread = true;
70static FeatureState g_esp{true, 0};
71static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu
72static FeatureState g_triggerbot{true, 603}; // default Mouse4
73static FeatureState g_silentaim{true, 602}; // default Mouse3
74 69
75// Aimbot bone checklist. Head defaults on (v1 behaviour). 70// Aimbot bone checklist. Head defaults on (v1 behaviour).
76enum { 71bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false,
77 AIM_BONE_HEAD = 0, 72 false, false, false};
78 AIM_BONE_TORSO, 73int g_aim_smooth = 5; // degrees per tick
79 AIM_BONE_LARM, 74bool g_aim_vischeck = true;
80 AIM_BONE_RARM, 75int g_aim_fov = 60; // angular distance from the crosshair
81 AIM_BONE_LLEG, 76bool g_aim_fov_circle = true;
82 AIM_BONE_RLEG, 77int g_silent_fov = 60;
83 AIM_BONE_COUNT
84};
85static bool g_aim_bones[AIM_BONE_COUNT] = {true, false, false,
86 false, false, false};
87static int g_aim_smooth = 5; // degrees per tick
88static bool g_aim_vischeck = true;
89static int g_aim_fov = 60; // angular distance from the crosshair
90static bool g_aim_fov_circle = true;
91static int g_silent_fov = 60;
92 78
93// ESP sub-options (menu "ESP" children). Defaults match the pre-option 79// ESP sub-options (menu "ESP" children). Defaults match the pre-option
94// behaviour: everything on. 80// behaviour: everything on.
95static bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true, true}; 81bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true, true};
96 82
97// tree 83// tree
98static MenuNode g_root{NODE_ROOT, "HL2hake", false, 0, {}, nullptr, nullptr, nullptr}; 84static MenuNode g_root{NODE_ROOT, "HL2hake", false, 0, {}, nullptr, nullptr, nullptr};
@@ -315,18 +301,18 @@ void register_bundled_font() {
315 logf("font: failed to register %s", path.c_str()); 301 logf("font: failed to register %s", path.c_str());
316} 302}
317 303
318static void config_save() { 304void config_save() {
319 FILE *f = fopen(config_path().c_str(), "w"); 305 FILE *f = fopen(config_path().c_str(), "w");
320 if (!f) 306 if (!f)
321 return; 307 return;
322 fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, 308 fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active,
323 g_bhop.bind, g_bhop_autostrafe); 309 g_bhop.bind, g_bhop_autostrafe);
324 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); 310 fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind);
325 fprintf(f, "[jumpbug]\nactive=%d\nassist=%d\n", g_jumpbug.active, 311 fprintf(f, "[jumpbug]\nactive=%d\nbind=%d\nassist=%d\n",
326 (int)g_jumpbug_assist); 312 g_jumpbug.active, g_jumpbug.bind, (int)g_jumpbug_assist);
327 fprintf(f, "[melee]\nactive=%d\n", g_melee.active); 313 fprintf(f, "[melee]\nactive=%d\nbind=%d\n", g_melee.active, g_melee.bind);
328 fprintf(f, "[spread]\nactive=%d\nno_spread=%d\n", g_spread.active, 314 fprintf(f, "[spread]\nactive=%d\nbind=%d\nno_spread=%d\n",
329 (int)g_spread_no_spread); 315 g_spread.active, g_spread.bind, (int)g_spread_no_spread);
330 fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" 316 fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n"
331 "fov=%d\nfov_circle=%d\n" 317 "fov=%d\nfov_circle=%d\n"
332 "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" 318 "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n"
@@ -347,6 +333,7 @@ static void config_save() {
347 g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], g_esp_opt[ESP_OPT_SHOW_NAME], 333 g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], g_esp_opt[ESP_OPT_SHOW_NAME],
348 g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX], 334 g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX],
349 g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH]); 335 g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH]);
336 gui_config_write(f);
350 fclose(f); 337 fclose(f);
351} 338}
352 339
@@ -462,6 +449,40 @@ static void config_load() {
462 fclose(f); 449 fclose(f);
463} 450}
464 451
452void config_reload() { config_load(); }
453
454const char *config_path_cstr() {
455 static std::string path;
456 path = config_path();
457 return path.c_str();
458}
459
460void config_reset_defaults() {
461 g_bhop = {true, 0};
462 g_bhop_autostrafe = true;
463 g_abh = {true, 0};
464 g_jumpbug = {true, 0};
465 g_jumpbug_assist = false;
466 g_melee = {true, 0};
467 g_spread = {true, 0};
468 g_spread_no_spread = true;
469 g_esp = {true, 0};
470 for (int i = 0; i < ESP_OPT_COUNT; i++)
471 g_esp_opt[i] = true;
472 g_aimbot = {true, 604};
473 g_triggerbot = {true, 603};
474 g_silentaim = {true, 602};
475 for (int i = 0; i < AIM_BONE_COUNT; i++)
476 g_aim_bones[i] = false;
477 g_aim_bones[AIM_BONE_HEAD] = true;
478 g_aim_smooth = 5;
479 g_aim_vischeck = true;
480 g_aim_fov = 60;
481 g_aim_fov_circle = true;
482 g_silent_fov = 60;
483 config_save();
484}
485
465// --------------------------------------------------------------------------- 486// ---------------------------------------------------------------------------
466// menu input handling 487// menu input handling
467// --------------------------------------------------------------------------- 488// ---------------------------------------------------------------------------
@@ -477,7 +498,7 @@ static constexpr int SCAN_ENTER = 40;
477static constexpr int SCAN_ESCAPE = 41; 498static constexpr int SCAN_ESCAPE = 41;
478 499
479// simplified scancode-to-name for common keys 500// simplified scancode-to-name for common keys
480static const char *scancode_name(int sc) { 501const char *scancode_name(int sc) {
481 switch (sc) { 502 switch (sc) {
482 case 0: return "None"; 503 case 0: return "None";
483 case 4: return "A"; case 5: return "B"; case 6: return "C"; 504 case 4: return "A"; case 5: return "B"; case 6: return "C";
@@ -497,9 +518,12 @@ static const char *scancode_name(int sc) {
497 case 43: return "Tab"; case 44: return "Space"; 518 case 43: return "Tab"; case 44: return "Space";
498 case 79: return "Right"; case 80: return "Left"; 519 case 79: return "Right"; case 80: return "Left";
499 case 81: return "Down"; case 82: return "Up"; 520 case 81: return "Down"; case 82: return "Up";
500 case 87: return "F1"; case 88: return "F2"; case 89: return "F3"; 521 case 58: return "F1"; case 59: return "F2"; case 60: return "F3";
501 case 90: return "F4"; case 91: return "F5"; case 92: return "F6"; 522 case 61: return "F4"; case 62: return "F5"; case 63: return "F6";
502 case 93: return "F7"; case 94: return "F8"; 523 case 64: return "F7"; case 65: return "F8"; case 66: return "F9";
524 case 67: return "F10"; case 68: return "F11"; case 69: return "F12";
525 case 73: return "Insert"; case 74: return "Home"; case 75: return "PgUp";
526 case 76: return "Delete"; case 77: return "End"; case 78: return "PgDn";
503 case 600: return "MouseL"; case 601: return "MouseR"; 527 case 600: return "MouseL"; case 601: return "MouseR";
504 case 602: return "MouseM"; case 603: return "Mouse4"; 528 case 602: return "MouseM"; case 603: return "Mouse4";
505 case 604: return "Mouse5"; 529 case 604: return "Mouse5";
@@ -1009,6 +1033,7 @@ void install_menu() {
1009 register_bundled_font(); 1033 register_bundled_font();
1010 logf("config: %s", config_path().c_str()); 1034 logf("config: %s", config_path().c_str());
1011 config_load(); 1035 config_load();
1036 gui_config_init();
1012 // resolve ISurface once (used by the DrawPrintText hook in hl2h.cpp and 1037 // resolve ISurface once (used by the DrawPrintText hook in hl2h.cpp and
1013 // by outer_draw). DrawPrintText hook is installed from hkdCreateMove. 1038 // by outer_draw). DrawPrintText hook is installed from hkdCreateMove.
1014 g_surface = find_surface_interface(); 1039 g_surface = find_surface_interface();