summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 01:13:09 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 01:13:09 +0100
commitb9d5965c0366fd7777c3627b9fbf89c8526ac0ec (patch)
tree65a1213b902f2a3352ecc19dc76be396e80f63f3 /src
parentSkybox image: swap red/blue in the uploaded texture (diff)
downloadhl2h-b9d5965c0366fd7777c3627b9fbf89c8526ac0ec.tar.gz
hl2h-b9d5965c0366fd7777c3627b9fbf89c8526ac0ec.zip
World tint: colour dynamic props through m_clrRender
Prop model materials (VertexLitGeneric) ignore IMaterial::ColorModulate: the engine feeds StudioRender the per-entity render colour (m_clrRender) on every draw, so the material-side tint never showed on crates and other props. Resolve m_clrRender from DT_BaseEntity and, every 10 ms while World tint and Props are enabled, write the prop colour into every entity whose model is under models/props. Restore white once when disabled.
Diffstat (limited to 'src')
-rw-r--r--src/esp.cpp49
-rw-r--r--src/hl2h.h2
-rw-r--r--src/menu.cpp1
-rw-r--r--src/netvars.cpp6
4 files changed, 56 insertions, 2 deletions
diff --git a/src/esp.cpp b/src/esp.cpp
index 50d7b5d..9d041b0 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -1405,6 +1405,55 @@ bool triggerbot_apply(CUserCmd *cmd) {
1405 return false; 1405 return false;
1406} 1406}
1407 1407
1408// Tints dynamic props by writing their client render colour. StudioRender
1409// uses m_clrRender as the per-model colour modulation, which is why changing
1410// the prop MATERIAL colour had no effect.
1411void prop_tint_update() {
1412 static bool applied = false;
1413 static bool logged_missing = false;
1414 static bool logged_touched = false;
1415 bool on = feature_active(FEAT_WORLD_TINT) && g_tint_prop_on;
1416 if (!on && !applied)
1417 return;
1418 if (!g_iface.entitylist || g_net.clr_render < 0) {
1419 if (on && !logged_missing) {
1420 logged_missing = true;
1421 logf("prop tint: m_clrRender unavailable");
1422 }
1423 return;
1424 }
1425 static double last = 0.0;
1426 double now = esp_now();
1427 if (on && now - last < 0.01)
1428 return;
1429 last = now;
1430
1431 using Fn = int (*)(void *);
1432 int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist);
1433 if (hi < 0 || hi > 4096)
1434 hi = 4096;
1435 uint32_t col =
1436 on ? ((uint32_t)g_tint_prop_rgb[0] | ((uint32_t)g_tint_prop_rgb[1] << 8) |
1437 ((uint32_t)g_tint_prop_rgb[2] << 16) | (255u << 24))
1438 : 0xFFFFFFFFu;
1439 int touched = 0;
1440 for (int idx = 1; idx <= hi; idx++) {
1441 void *ent = entitylist_get(idx);
1442 if (!ent)
1443 continue;
1444 const char *model = entity_model_name(ent);
1445 if (!model || strncmp(model, "models/props", 12) != 0)
1446 continue;
1447 *(uint32_t *)((uintptr_t)ent + (uintptr_t)g_net.clr_render) = col;
1448 touched++;
1449 }
1450 applied = on;
1451 if (on && touched > 0 && !logged_touched) {
1452 logged_touched = true;
1453 logf("prop tint: %d prop entities", touched);
1454 }
1455}
1456
1408void esp_draw() { 1457void esp_draw() {
1409 if (!g_surface || !g_iface.entitylist || !g_net.esp_ok()) return; 1458 if (!g_surface || !g_iface.entitylist || !g_net.esp_ok()) return;
1410 if (!g_scr_w) { 1459 if (!g_scr_w) {
diff --git a/src/hl2h.h b/src/hl2h.h
index fd7a863..779a241 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -96,6 +96,7 @@ struct NetVars {
96 int activeweapon = -1; // client m_hActiveWeapon handle 96 int activeweapon = -1; // client m_hActiveWeapon handle
97 int owner = -1; // client m_hOwnerEntity handle 97 int owner = -1; // client m_hOwnerEntity handle
98 int weapon_owner = -1; // client m_hOwner handle (C_BaseCombatWeapon) 98 int weapon_owner = -1; // client m_hOwner handle (C_BaseCombatWeapon)
99 int clr_render = -1; // client m_clrRender (entity render colour)
99 // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72 100 // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72
100 bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; } 101 bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; }
101 // ponytail: m_iMaxHealth isn't a netvar in HL2 SP; esp.cpp falls back to 100. 102 // ponytail: m_iMaxHealth isn't a netvar in HL2 SP; esp.cpp falls back to 100.
@@ -296,6 +297,7 @@ int engine_get_local_player();
296void draw_text_col(int x, int y, const char *s, int r, int g, int b); 297void draw_text_col(int x, int y, const char *s, int r, int g, int b);
297void esp_draw(); 298void esp_draw();
298void esp_draw_items(); 299void esp_draw_items();
300void prop_tint_update();
299bool world_to_screen(const Vector &w, float &sx, float &sy); 301bool world_to_screen(const Vector &w, float &sx, float &sy);
300void set_silent_restore(const QAngle &angles); 302void set_silent_restore(const QAngle &angles);
301bool is_visible(const Vector &eye, const Vector &point, 303bool is_visible(const Vector &eye, const Vector &point,
diff --git a/src/menu.cpp b/src/menu.cpp
index 7ffc84a..0ce6c05 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -815,6 +815,7 @@ void outer_draw() {
815 poll_keyboard(); 815 poll_keyboard();
816 fullbright_update(); 816 fullbright_update();
817 world_tint_update(); 817 world_tint_update();
818 prop_tint_update();
818 fog_update(); 819 fog_update();
819 gui_handle_input(); 820 gui_handle_input();
820 // HL2H_DRAW_TEST=1 draws a probe rect without needing the GUI open 821 // HL2H_DRAW_TEST=1 draws a probe rect without needing the GUI open
diff --git a/src/netvars.cpp b/src/netvars.cpp
index 238af70..4578e52 100644
--- a/src/netvars.cpp
+++ b/src/netvars.cpp
@@ -92,6 +92,8 @@ static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth) {
92 } else if (strcmp(pname2, "m_hActiveWeapon") == 0 && 92 } else if (strcmp(pname2, "m_hActiveWeapon") == 0 &&
93 g_net.activeweapon < 0) { 93 g_net.activeweapon < 0) {
94 g_net.activeweapon = (int)(base_off + (uint32_t)poff); 94 g_net.activeweapon = (int)(base_off + (uint32_t)poff);
95 } else if (strcmp(pname2, "m_clrRender") == 0 && g_net.clr_render < 0) {
96 g_net.clr_render = (int)(base_off + (uint32_t)poff);
95 } else if (strcmp(pname2, "m_iTeamNum") == 0 && g_net.teamnum < 0) { 97 } else if (strcmp(pname2, "m_iTeamNum") == 0 && g_net.teamnum < 0) {
96 g_net.teamnum = (int)(base_off + (uint32_t)poff); 98 g_net.teamnum = (int)(base_off + (uint32_t)poff);
97 } else if (strcmp(pname2, "m_vecOrigin") == 0 && g_net.origin < 0) { 99 } else if (strcmp(pname2, "m_vecOrigin") == 0 && g_net.origin < 0) {
@@ -273,9 +275,9 @@ void resolve_netvars() {
273 logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d " 275 logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d "
274 "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d lifeste=%d " 276 "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d lifeste=%d "
275 "modelindex=%d absvelocity=%d activeweapon=%d owner=%d " 277 "modelindex=%d absvelocity=%d activeweapon=%d owner=%d "
276 "weapon_owner=%d", 278 "weapon_owner=%d clr_render=%d",
277 g_net.flags, g_net.velocity, g_net.movetype, g_net.health, 279 g_net.flags, g_net.velocity, g_net.movetype, g_net.health,
278 g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset, 280 g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset,
279 g_net.mins, g_net.maxs, g_net.lifestate, g_net.modelindex, 281 g_net.mins, g_net.maxs, g_net.lifestate, g_net.modelindex,
280 g_net.absvelocity, g_net.activeweapon); 282 g_net.absvelocity, g_net.activeweapon, g_net.clr_render);
281} 283}