diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-04 14:04:39 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-04 14:04:39 +0100 |
| commit | 8cd07c04efafcd3e927a21152d10e9fafea54517 (patch) | |
| tree | c89fdf577c90e82080ff5c15982d016cc3f5f7fa | |
| parent | Add ESP, aim, view and movement batch (diff) | |
| download | hl2h-8cd07c04efafcd3e927a21152d10e9fafea54517.tar.gz hl2h-8cd07c04efafcd3e927a21152d10e9fafea54517.zip | |
Transitions: per-trigger targets and 3D wireframe; FOV range fixes
- Each changelevel trigger now shows its own target map. The trigger whose
target is the map we came from is marked "<- map"; the forward one is
"-> map". No more appending one global previous-map line to every box.
- Draw the volume as a projected 8-corner / 12-edge wireframe with the tag
above its highest visible corner instead of a screen-space rectangle.
- Camera FOV is 40..140 (stock 75) and viewmodel FOV 20..120 (stock 54); 0 is
no longer allowed and old configs using 0 migrate to the stock value.
- Log the mat_monitorgamma readback via ICvar so we can tell whether the
command is rejected or the OS/compositor ignores the gamma ramp.
| -rw-r--r-- | CONTEXT.md | 19 | ||||
| -rw-r--r-- | src/esp.cpp | 64 | ||||
| -rw-r--r-- | src/gui.cpp | 8 | ||||
| -rw-r--r-- | src/hl2h.cpp | 31 | ||||
| -rw-r--r-- | src/menu.cpp | 17 |
5 files changed, 82 insertions, 57 deletions
| @@ -234,11 +234,16 @@ vocabulary / decisions. | |||
| 234 | 234 | ||
| 235 | - The rendered camera FOV comes from `CViewRender::OnRenderStart` copying | 235 | - The rendered camera FOV comes from `CViewRender::OnRenderStart` copying |
| 236 | `C_BasePlayer::m_iDefaultFOV` into `default_fov`, so the cheat writes the | 236 | `C_BasePlayer::m_iDefaultFOV` into `default_fov`, so the cheat writes the |
| 237 | client netvar each tick when `Camera FOV > 0` (0 = stock 75). | 237 | client netvar every tick. The camera slider is 40..140 (stock 75) and the |
| 238 | viewmodel slider 20..120 (stock 54); 0 is not allowed (old configs with 0 | ||
| 239 | migrate to the stock value). | ||
| 238 | - `viewmodel_fov` and `mat_monitorgamma` are client cvars; applied through | 240 | - `viewmodel_fov` and `mat_monitorgamma` are client cvars; applied through |
| 239 | `ClientCmd` when their slider changes. Gamma is stored as x10 | 241 | `ClientCmd` when their slider changes. Gamma is stored as x10 |
| 240 | (`[view] gamma=22` = 2.2). Both live in the always-on | 242 | (`[view] gamma=22` = 2.2). The gamma value is read back through |
| 241 | `Camera / display` card (`[view] fov/vmfov/gamma`). | 243 | `ICvar::FindVar` and logged (`view: gamma request ..., cvar reports ...`) |
| 244 | to distinguish a rejected command from an OS gamma ramp the compositor | ||
| 245 | ignores. Both live in the always-on `Camera / display` card | ||
| 246 | (`[view] fov/vmfov/gamma`). | ||
| 242 | 247 | ||
| 243 | ## Map transitions | 248 | ## Map transitions |
| 244 | 249 | ||
| @@ -250,9 +255,11 @@ vocabulary / decisions. | |||
| 250 | interface comes from `IServerUnknown::GetCollideable`, slot 4). | 255 | interface comes from `IServerUnknown::GetCollideable`, slot 4). |
| 251 | - The current map name comes from the worldspawn (`entity 0`) model name | 256 | - The current map name comes from the worldspawn (`entity 0`) model name |
| 252 | (`maps/<name>.bsp`). The map before the last change is kept as | 257 | (`maps/<name>.bsp`). The map before the last change is kept as |
| 253 | `g_map_prev`, so the box label reads `-> next` and `prev <map>`. | 258 | `g_map_prev`, so each trigger's own target is drawn as `-> map`, and the |
| 254 | - Scanning runs at 4 Hz in the ESP draw; boxes are projected from their 8 | 259 | trigger whose target equals `g_map_prev` is marked `<- map` (the way back). |
| 255 | world corners and drawn as an orange outline with a 10 px label. | 260 | - Scanning runs at 4 Hz in the ESP draw; the 8 world corners are projected and |
| 261 | the 12 edges are drawn as a 3D wireframe, with a 10 px tag above the highest | ||
| 262 | visible corner. | ||
| 256 | 263 | ||
| 257 | ## Bones / ESP | 264 | ## Bones / ESP |
| 258 | 265 | ||
diff --git a/src/esp.cpp b/src/esp.cpp index 1e67a8a..7baf2c8 100644 --- a/src/esp.cpp +++ b/src/esp.cpp | |||
| @@ -1769,45 +1769,55 @@ static void transitions_scan() { | |||
| 1769 | static void draw_transitions() { | 1769 | static void draw_transitions() { |
| 1770 | if (!g_surface || !g_ntrans) | 1770 | if (!g_surface || !g_ntrans) |
| 1771 | return; | 1771 | return; |
| 1772 | // Corner bits: +x, +y, +z. Twelve edges connect corners differing by one | ||
| 1773 | // bit; projecting the corners and joining them gives a proper 3D wireframe | ||
| 1774 | // instead of a screen-space rectangle. | ||
| 1775 | static const int edges[12][2] = { | ||
| 1776 | {0, 1}, {2, 3}, {4, 5}, {6, 7}, {0, 2}, {1, 3}, | ||
| 1777 | {4, 6}, {5, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7}}; | ||
| 1772 | for (int i = 0; i < g_ntrans; i++) { | 1778 | for (int i = 0; i < g_ntrans; i++) { |
| 1773 | const TransitionBox &b = g_trans[i]; | 1779 | const TransitionBox &b = g_trans[i]; |
| 1774 | float minx = 1e9f, miny = 1e9f, maxx = -1e9f, maxy = -1e9f; | 1780 | Vector corners[8]; |
| 1781 | float xs[8], ys[8]; | ||
| 1782 | bool ok[8]; | ||
| 1775 | int seen = 0; | 1783 | int seen = 0; |
| 1776 | for (int c = 0; c < 8; c++) { | 1784 | for (int c = 0; c < 8; c++) { |
| 1777 | Vector p{(c & 1) ? b.maxs.x : b.mins.x, (c & 2) ? b.maxs.y : b.mins.y, | 1785 | corners[c] = Vector{(c & 1) ? b.maxs.x : b.mins.x, |
| 1778 | (c & 4) ? b.maxs.z : b.mins.z}; | 1786 | (c & 2) ? b.maxs.y : b.mins.y, |
| 1779 | float sx, sy; | 1787 | (c & 4) ? b.maxs.z : b.mins.z}; |
| 1780 | if (!world_to_screen(p, sx, sy) || !std::isfinite(sx) || | 1788 | ok[c] = world_to_screen(corners[c], xs[c], ys[c]) && |
| 1781 | !std::isfinite(sy)) | 1789 | std::isfinite(xs[c]) && std::isfinite(ys[c]); |
| 1782 | continue; | 1790 | if (ok[c]) |
| 1783 | if (sx < minx) minx = sx; | 1791 | seen++; |
| 1784 | if (sx > maxx) maxx = sx; | ||
| 1785 | if (sy < miny) miny = sy; | ||
| 1786 | if (sy > maxy) maxy = sy; | ||
| 1787 | seen++; | ||
| 1788 | } | 1792 | } |
| 1789 | if (!seen) | 1793 | if (!seen) |
| 1790 | continue; | 1794 | continue; |
| 1791 | int x0 = (int)minx, x1 = (int)maxx, y0 = (int)miny, y1 = (int)maxy; | 1795 | // The trigger whose target is the map we came from is the way back. |
| 1792 | if (x1 < -2000 || x0 > g_scr_w + 2000 || y1 < -2000 || | 1796 | bool back = g_map_prev[0] && strcmp(b.next, g_map_prev) == 0; |
| 1793 | y0 > g_scr_h + 2000) | 1797 | int r = back ? 200 : 255, g = back ? 200 : 170, bl = back ? 200 : 60; |
| 1794 | continue; | 1798 | surf_color(r, g, bl, 210); |
| 1795 | surf_color(255, 170, 60, 200); | 1799 | for (int e = 0; e < 12; e++) { |
| 1796 | surf_rect(0, x0, y0, x1, y1); | 1800 | int a = edges[e][0], c = edges[e][1]; |
| 1801 | if (ok[a] && ok[c]) | ||
| 1802 | surf_line((int)xs[a], (int)ys[a], (int)xs[c], (int)ys[c]); | ||
| 1803 | } | ||
| 1804 | // Tag above the highest projected corner. | ||
| 1805 | float topx = 0.0f, topy = 1e9f; | ||
| 1806 | for (int c = 0; c < 8; c++) { | ||
| 1807 | if (ok[c] && ys[c] < topy) { | ||
| 1808 | topy = ys[c]; | ||
| 1809 | topx = xs[c]; | ||
| 1810 | } | ||
| 1811 | } | ||
| 1797 | char line[128]; | 1812 | char line[128]; |
| 1798 | snprintf(line, sizeof(line), "-> %.100s", b.next); | 1813 | snprintf(line, sizeof(line), "%s %.100s", back ? "<-" : "->", b.next); |
| 1799 | int tx = x0; | 1814 | int tx = (int)topx; |
| 1800 | if (tx < 4) tx = 4; | 1815 | if (tx < 4) tx = 4; |
| 1801 | if (tx > g_scr_w - 120) tx = g_scr_w - 120; | 1816 | if (tx > g_scr_w - 120) tx = g_scr_w - 120; |
| 1802 | int ty = y0 - 16; | 1817 | int ty = (int)topy - 16; |
| 1803 | if (ty < 4) ty = 4; | 1818 | if (ty < 4) ty = 4; |
| 1804 | draw_text_small_col(tx + 1, ty + 1, line, 0, 0, 0); | 1819 | draw_text_small_col(tx + 1, ty + 1, line, 0, 0, 0); |
| 1805 | draw_text_small_col(tx, ty, line, 255, 180, 70); | 1820 | draw_text_small_col(tx, ty, line, r, g, bl); |
| 1806 | if (g_map_prev[0]) { | ||
| 1807 | snprintf(line, sizeof(line), "prev %.48s", g_map_prev); | ||
| 1808 | draw_text_small_col(tx + 1, ty + 13, line, 0, 0, 0); | ||
| 1809 | draw_text_small_col(tx, ty + 12, line, 200, 200, 200); | ||
| 1810 | } | ||
| 1811 | } | 1821 | } |
| 1812 | } | 1822 | } |
| 1813 | 1823 | ||
diff --git a/src/gui.cpp b/src/gui.cpp index 974347a..88e5521 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -413,11 +413,11 @@ static void build_cards() { | |||
| 413 | int view = add_card(2, nullptr, "Camera / display", | 413 | int view = add_card(2, nullptr, "Camera / display", |
| 414 | "Camera FOV, viewmodel FOV and monitor gamma.", true); | 414 | "Camera FOV, viewmodel FOV and monitor gamma.", true); |
| 415 | add_slider(view, "Camera FOV", | 415 | add_slider(view, "Camera FOV", |
| 416 | "World camera field of view; 0 keeps the game default (75).", 0, | 416 | "World camera field of view (40-140; stock 75).", 40, 140, |
| 417 | 140, &g_fov_camera); | 417 | &g_fov_camera); |
| 418 | add_slider(view, "Viewmodel FOV", | 418 | add_slider(view, "Viewmodel FOV", |
| 419 | "Weapon model field of view; 0 keeps the game default (54).", 0, | 419 | "Weapon model field of view (20-120; stock 54).", 20, 120, |
| 420 | 120, &g_fov_viewmodel); | 420 | &g_fov_viewmodel); |
| 421 | add_slider(view, "Gamma x10", | 421 | add_slider(view, "Gamma x10", |
| 422 | "mat_monitorgamma x10: 16 = 1.6, 30 = 3.0, stock is 22.", 16, 30, | 422 | "mat_monitorgamma x10: 16 = 1.6, 30 = 3.0, stock is 22.", 16, 30, |
| 423 | &g_gamma); | 423 | &g_gamma); |
diff --git a/src/hl2h.cpp b/src/hl2h.cpp index 74a8c6c..f7106cd 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp | |||
| @@ -664,25 +664,14 @@ static void install_uniform_random_hook() { | |||
| 664 | // and monitor gamma are plain client cvars. | 664 | // and monitor gamma are plain client cvars. |
| 665 | 665 | ||
| 666 | static void view_settings_apply(void *ent) { | 666 | static void view_settings_apply(void *ent) { |
| 667 | if (g_net.default_fov >= 0 && ent) { | 667 | if (g_net.default_fov >= 0 && ent) |
| 668 | static int last = -2; | 668 | *(int *)((uintptr_t)ent + g_net.default_fov) = g_fov_camera; |
| 669 | int want = g_fov_camera > 0 ? g_fov_camera : 0; | 669 | static int last_vm = 54; |
| 670 | if (want != last || g_fov_camera > 0) { | 670 | if (g_fov_viewmodel != last_vm) { |
| 671 | *(int *)((uintptr_t)ent + g_net.default_fov) = want; | ||
| 672 | last = want; | ||
| 673 | } | ||
| 674 | } | ||
| 675 | // 0 means "leave the game's value alone": only send a command once the | ||
| 676 | // slider is non-zero, then restore the stock value when it returns to 0. | ||
| 677 | static int last_vm = 0; | ||
| 678 | if (g_fov_viewmodel > 0 && g_fov_viewmodel != last_vm) { | ||
| 679 | char cmd[48]; | 671 | char cmd[48]; |
| 680 | snprintf(cmd, sizeof(cmd), "viewmodel_fov %d", g_fov_viewmodel); | 672 | snprintf(cmd, sizeof(cmd), "viewmodel_fov %d", g_fov_viewmodel); |
| 681 | console_cmd(cmd); | 673 | console_cmd(cmd); |
| 682 | last_vm = g_fov_viewmodel; | 674 | last_vm = g_fov_viewmodel; |
| 683 | } else if (g_fov_viewmodel == 0 && last_vm > 0) { | ||
| 684 | console_cmd("viewmodel_fov 54"); | ||
| 685 | last_vm = 0; | ||
| 686 | } | 675 | } |
| 687 | static int last_gamma = 22; | 676 | static int last_gamma = 22; |
| 688 | if (g_gamma != last_gamma) { | 677 | if (g_gamma != last_gamma) { |
| @@ -690,6 +679,18 @@ static void view_settings_apply(void *ent) { | |||
| 690 | snprintf(cmd, sizeof(cmd), "mat_monitorgamma %d.%d", g_gamma / 10, | 679 | snprintf(cmd, sizeof(cmd), "mat_monitorgamma %d.%d", g_gamma / 10, |
| 691 | g_gamma % 10); | 680 | g_gamma % 10); |
| 692 | console_cmd(cmd); | 681 | console_cmd(cmd); |
| 682 | // Read back through ICvar to tell an ignored command from an OS gamma | ||
| 683 | // ramp that the compositor discards. | ||
| 684 | float got = -1.0f; | ||
| 685 | if (g_iface.cvar) { | ||
| 686 | using FindFn = void *(*)(void *, const char *); | ||
| 687 | void *v = vfunc<FindFn>(g_iface.cvar, 12)(g_iface.cvar, | ||
| 688 | "mat_monitorgamma"); | ||
| 689 | if (v) | ||
| 690 | got = *(float *)((uintptr_t)v + 0x2c); // ConVar m_Value.f | ||
| 691 | } | ||
| 692 | logf("view: gamma request %d.%d, cvar reports %.2f", g_gamma / 10, | ||
| 693 | g_gamma % 10, got); | ||
| 693 | last_gamma = g_gamma; | 694 | last_gamma = g_gamma; |
| 694 | } | 695 | } |
| 695 | } | 696 | } |
diff --git a/src/menu.cpp b/src/menu.cpp index 4e521f7..ead5f2d 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -79,8 +79,8 @@ bool g_aim_punch = true; // compensate server aim punch | |||
| 79 | bool g_spread_engine = true; // zero the pellet random draws, not just the seed | 79 | bool g_spread_engine = true; // zero the pellet random draws, not just the seed |
| 80 | bool g_auto_crouchjump = false; | 80 | bool g_auto_crouchjump = false; |
| 81 | bool g_edge_jump = false; | 81 | bool g_edge_jump = false; |
| 82 | int g_fov_camera = 0; // 0 = game default | 82 | int g_fov_camera = 75; // HL2's stock camera FOV |
| 83 | int g_fov_viewmodel = 0; // 0 = game default (54) | 83 | int g_fov_viewmodel = 54; // HL2's stock viewmodel FOV |
| 84 | int g_gamma = 22; // mat_monitorgamma x10, stock 2.2 | 84 | int g_gamma = 22; // mat_monitorgamma x10, stock 2.2 |
| 85 | int g_esp_col_hostile[3] = {255, 60, 60}; | 85 | int g_esp_col_hostile[3] = {255, 60, 60}; |
| 86 | int g_esp_col_neutral[3] = {220, 220, 220}; | 86 | int g_esp_col_neutral[3] = {220, 220, 220}; |
| @@ -393,20 +393,27 @@ static bool config_load() { | |||
| 393 | } | 393 | } |
| 394 | if (in_view) { | 394 | if (in_view) { |
| 395 | int *dst = nullptr; | 395 | int *dst = nullptr; |
| 396 | int lo = 0, hi = 0; | 396 | int lo = 0, hi = 0, stock = 0; |
| 397 | if (strcmp(key, "fov") == 0) { | 397 | if (strcmp(key, "fov") == 0) { |
| 398 | dst = &g_fov_camera; | 398 | dst = &g_fov_camera; |
| 399 | lo = 40; | ||
| 399 | hi = 140; | 400 | hi = 140; |
| 401 | stock = 75; | ||
| 400 | } else if (strcmp(key, "vmfov") == 0) { | 402 | } else if (strcmp(key, "vmfov") == 0) { |
| 401 | dst = &g_fov_viewmodel; | 403 | dst = &g_fov_viewmodel; |
| 404 | lo = 20; | ||
| 402 | hi = 120; | 405 | hi = 120; |
| 406 | stock = 54; | ||
| 403 | } else if (strcmp(key, "gamma") == 0) { | 407 | } else if (strcmp(key, "gamma") == 0) { |
| 404 | dst = &g_gamma; | 408 | dst = &g_gamma; |
| 405 | lo = 16; | 409 | lo = 16; |
| 406 | hi = 30; | 410 | hi = 30; |
| 411 | stock = 22; | ||
| 407 | } | 412 | } |
| 408 | if (dst) { | 413 | if (dst) { |
| 409 | int v = atoi(val); | 414 | int v = atoi(val); |
| 415 | if (v <= 0) | ||
| 416 | v = stock; // old configs used 0 as "off" | ||
| 410 | if (v < lo) v = lo; | 417 | if (v < lo) v = lo; |
| 411 | if (v > hi) v = hi; | 418 | if (v > hi) v = hi; |
| 412 | *dst = v; | 419 | *dst = v; |
| @@ -595,8 +602,8 @@ void config_reset_defaults() { | |||
| 595 | g_spread_engine = true; | 602 | g_spread_engine = true; |
| 596 | g_auto_crouchjump = false; | 603 | g_auto_crouchjump = false; |
| 597 | g_edge_jump = false; | 604 | g_edge_jump = false; |
| 598 | g_fov_camera = 0; | 605 | g_fov_camera = 75; |
| 599 | g_fov_viewmodel = 0; | 606 | g_fov_viewmodel = 54; |
| 600 | g_gamma = 22; | 607 | g_gamma = 22; |
| 601 | g_esp_col_hostile[0] = 255; | 608 | g_esp_col_hostile[0] = 255; |
| 602 | g_esp_col_hostile[1] = 60; | 609 | g_esp_col_hostile[1] = 60; |
