diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-04 18:08:07 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-04 18:08:07 +0100 |
| commit | b3003583a1a66f5ae2eae63763f285635b43d58f (patch) | |
| tree | d4d2e778a2265652befcc86fbaa8bf1f4fcb30c3 | |
| parent | Chams: quiet material lookup, accept any fallback by name (diff) | |
| download | hl2h-b3003583a1a66f5ae2eae63763f285635b43d58f.tar.gz hl2h-b3003583a1a66f5ae2eae63763f285635b43d58f.zip | |
Chams: material styles and viewmodel support
- Material selector 0..4: debugwhite, debug wireframe (ignorez), vgui
white_additive, debugtranslucentsinglecolor, vgui/white; cached per index
with a debugwhite fallback.
- Add "Viewmodel (hands/weapon)" toggle; viewmodel entities (class contains
viewmodel or model path /v_...) get a normal-depth chams pass.
- [chams] gains viewmodel= and material=.
| -rw-r--r-- | CONTEXT.md | 17 | ||||
| -rw-r--r-- | src/esp.cpp | 14 | ||||
| -rw-r--r-- | src/gui.cpp | 6 | ||||
| -rw-r--r-- | src/hl2h.h | 3 | ||||
| -rw-r--r-- | src/menu.cpp | 21 | ||||
| -rw-r--r-- | src/studio.cpp | 58 |
6 files changed, 89 insertions, 30 deletions
| @@ -293,12 +293,17 @@ vocabulary / decisions. | |||
| 293 | materials ignore ColorModulate. The shared material's flag and colour are | 293 | materials ignore ColorModulate. The shared material's flag and colour are |
| 294 | restored to (false, white) after the draw. | 294 | restored to (false, white) after the draw. |
| 295 | - Material lookup walks the `IMaterialSystem` list (`world_find_material`), with | 295 | - Material lookup walks the `IMaterialSystem` list (`world_find_material`), with |
| 296 | `FindMaterial` (slot 71) as the fallback. Candidates, in order: | 296 | `FindMaterial` (slot 71) as the fallback. The style index selects: |
| 297 | `models/debug/debugwhite{2,3}`, `vgui/white`, | 297 | 0 `models/debug/debugwhite`, 1 `debug/debugwireframevertexcolorignorez`, |
| 298 | `debug/debugtranslucentsinglecolor`, `debug/debugwireframevertexcolorignorez`. | 298 | 2 `vgui/white_additive`, 3 `debug/debugtranslucentsinglecolor`, |
| 299 | The chosen material name is logged. | 299 | 4 `vgui/white`; if the requested style is unavailable it falls back to the |
| 300 | - Feature is off by default; `[chams] active/bind/walls/r/g/b`, with a | 300 | first available debugwhite candidate. The chosen material name is logged. |
| 301 | "Through walls" toggle and colour picker in the Visuals card. | 301 | - `Viewmodel (hands/weapon)` also applies the override to entities whose class |
| 302 | contains `viewmodel` or whose model path is `.../v_...`. The viewmodel gets | ||
| 303 | only the normal-depth pass (no ignorez). | ||
| 304 | - Feature is off by default; `[chams] active/bind/walls/viewmodel/material/r/g/b`, | ||
| 305 | with Through walls, Viewmodel, Material (0..4) and colour in the Visuals | ||
| 306 | card. | ||
| 302 | - This is a flat material override, not stencil outlines; depth handling is | 307 | - This is a flat material override, not stencil outlines; depth handling is |
| 303 | the simple ignorez/normal two-pass. | 308 | the simple ignorez/normal two-pass. |
| 304 | 309 | ||
diff --git a/src/esp.cpp b/src/esp.cpp index b50dd87..1631df1 100644 --- a/src/esp.cpp +++ b/src/esp.cpp | |||
| @@ -173,6 +173,20 @@ bool esp_is_npc_index(int idx) { | |||
| 173 | return is_npc_class(entity_class_name(net, nullptr)); | 173 | return is_npc_class(entity_class_name(net, nullptr)); |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | bool esp_is_viewmodel_index(int idx) { | ||
| 177 | if (!g_iface.entitylist || idx <= 0 || idx > 8192) | ||
| 178 | return false; | ||
| 179 | void *ent = entitylist_get(idx); | ||
| 180 | if (!ent) | ||
| 181 | return false; | ||
| 182 | void *net = entity_networkable(idx); | ||
| 183 | const char *cls = net ? entity_class_name(net, nullptr) : nullptr; | ||
| 184 | if (cls && strcasestr(cls, "viewmodel")) | ||
| 185 | return true; | ||
| 186 | const char *model = entity_model_name(ent); | ||
| 187 | return model && (strstr(model, "/v_") || strstr(model, "\\v_")); | ||
| 188 | } | ||
| 189 | |||
| 176 | static bool model_is_friendly(const char *m) { | 190 | static bool model_is_friendly(const char *m) { |
| 177 | static const char *patterns[] = { | 191 | static const char *patterns[] = { |
| 178 | "citizen", "humans/", "male_", "female_", "barney", "alyx", | 192 | "citizen", "humans/", "male_", "female_", "barney", "alyx", |
diff --git a/src/gui.cpp b/src/gui.cpp index a38f537..4a949f0 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -428,6 +428,12 @@ static void build_cards() { | |||
| 428 | add_toggle(chams, "Through walls", | 428 | add_toggle(chams, "Through walls", |
| 429 | "Draw a second pass with the depth test disabled.", | 429 | "Draw a second pass with the depth test disabled.", |
| 430 | &g_chams_walls); | 430 | &g_chams_walls); |
| 431 | add_toggle(chams, "Viewmodel (hands/weapon)", | ||
| 432 | "Apply chams to the first-person hands and weapon too.", | ||
| 433 | &g_chams_viewmodel); | ||
| 434 | add_slider(chams, "Material", | ||
| 435 | "0 Flat, 1 Wireframe, 2 Additive glow, 3 Solid, 4 White.", 0, 4, | ||
| 436 | &g_chams_mat_index); | ||
| 431 | add_color(chams, "Colour", "Chams colour.", g_chams_rgb); | 437 | add_color(chams, "Colour", "Chams colour.", g_chams_rgb); |
| 432 | 438 | ||
| 433 | int view = add_card(2, nullptr, "Camera / display", | 439 | int view = add_card(2, nullptr, "Camera / display", |
| @@ -231,6 +231,8 @@ extern FeatureState g_bhop, g_abh, g_jumpbug, g_melee, g_spread, g_esp, | |||
| 231 | g_world_tint, g_fog, g_skybox, g_projectiles, g_chams; | 231 | g_world_tint, g_fog, g_skybox, g_projectiles, g_chams; |
| 232 | extern bool g_proj_fuse, g_proj_radius; | 232 | extern bool g_proj_fuse, g_proj_radius; |
| 233 | extern bool g_chams_walls; | 233 | extern bool g_chams_walls; |
| 234 | extern bool g_chams_viewmodel; | ||
| 235 | extern int g_chams_mat_index; | ||
| 234 | extern int g_chams_rgb[3]; | 236 | extern int g_chams_rgb[3]; |
| 235 | extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on; | 237 | extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on; |
| 236 | extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3]; | 238 | extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3]; |
| @@ -334,6 +336,7 @@ void aimbot_reset_lock(); | |||
| 334 | void silentaim_reset_lock(); | 336 | void silentaim_reset_lock(); |
| 335 | void *entitylist_get(int idx); | 337 | void *entitylist_get(int idx); |
| 336 | bool esp_is_npc_index(int idx); | 338 | bool esp_is_npc_index(int idx); |
| 339 | bool esp_is_viewmodel_index(int idx); | ||
| 337 | void *world_find_material(const char *name); | 340 | void *world_find_material(const char *name); |
| 338 | void chams_init(); | 341 | void chams_init(); |
| 339 | int engine_get_local_player(); | 342 | int engine_get_local_player(); |
diff --git a/src/menu.cpp b/src/menu.cpp index 77eb35f..4f22d39 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -65,6 +65,8 @@ bool g_proj_fuse = true; | |||
| 65 | bool g_proj_radius = true; | 65 | bool g_proj_radius = true; |
| 66 | FeatureState g_chams{false, 0}; // flat model material, optional through walls | 66 | FeatureState g_chams{false, 0}; // flat model material, optional through walls |
| 67 | bool g_chams_walls = true; | 67 | bool g_chams_walls = true; |
| 68 | bool g_chams_viewmodel = false; | ||
| 69 | int g_chams_mat_index = 0; | ||
| 68 | int g_chams_rgb[3] = {255, 60, 60}; | 70 | int g_chams_rgb[3] = {255, 60, 60}; |
| 69 | bool g_item_health = true; | 71 | bool g_item_health = true; |
| 70 | bool g_item_ammo = true; | 72 | bool g_item_ammo = true; |
| @@ -237,8 +239,10 @@ void config_save() { | |||
| 237 | fprintf(f, "[projectiles]\nactive=%d\nbind=%d\nfuse=%d\nradius=%d\n", | 239 | fprintf(f, "[projectiles]\nactive=%d\nbind=%d\nfuse=%d\nradius=%d\n", |
| 238 | g_projectiles.active, g_projectiles.bind, (int)g_proj_fuse, | 240 | g_projectiles.active, g_projectiles.bind, (int)g_proj_fuse, |
| 239 | (int)g_proj_radius); | 241 | (int)g_proj_radius); |
| 240 | fprintf(f, "[chams]\nactive=%d\nbind=%d\nwalls=%d\nr=%d\ng=%d\nb=%d\n", | 242 | fprintf(f, "[chams]\nactive=%d\nbind=%d\nwalls=%d\nviewmodel=%d\n" |
| 241 | g_chams.active, g_chams.bind, (int)g_chams_walls, g_chams_rgb[0], | 243 | "material=%d\nr=%d\ng=%d\nb=%d\n", |
| 244 | g_chams.active, g_chams.bind, (int)g_chams_walls, | ||
| 245 | (int)g_chams_viewmodel, g_chams_mat_index, g_chams_rgb[0], | ||
| 242 | g_chams_rgb[1], g_chams_rgb[2]); | 246 | g_chams_rgb[1], g_chams_rgb[2]); |
| 243 | fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" | 247 | fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" |
| 244 | "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n" | 248 | "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n" |
| @@ -393,8 +397,15 @@ static bool config_load() { | |||
| 393 | if (in_chams) { | 397 | if (in_chams) { |
| 394 | if (strcmp(key, "walls") == 0) | 398 | if (strcmp(key, "walls") == 0) |
| 395 | g_chams_walls = atoi(val) != 0; | 399 | g_chams_walls = atoi(val) != 0; |
| 396 | else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || | 400 | else if (strcmp(key, "viewmodel") == 0) |
| 397 | strcmp(key, "b") == 0) { | 401 | g_chams_viewmodel = atoi(val) != 0; |
| 402 | else if (strcmp(key, "material") == 0) { | ||
| 403 | int v = atoi(val); | ||
| 404 | if (v < 0) v = 0; | ||
| 405 | if (v > 4) v = 4; | ||
| 406 | g_chams_mat_index = v; | ||
| 407 | } else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || | ||
| 408 | strcmp(key, "b") == 0) { | ||
| 398 | int v = atoi(val); | 409 | int v = atoi(val); |
| 399 | if (v < 0) v = 0; | 410 | if (v < 0) v = 0; |
| 400 | if (v > 255) v = 255; | 411 | if (v > 255) v = 255; |
| @@ -627,6 +638,8 @@ void config_reset_defaults() { | |||
| 627 | g_proj_radius = true; | 638 | g_proj_radius = true; |
| 628 | g_chams = {false, 0}; | 639 | g_chams = {false, 0}; |
| 629 | g_chams_walls = true; | 640 | g_chams_walls = true; |
| 641 | g_chams_viewmodel = false; | ||
| 642 | g_chams_mat_index = 0; | ||
| 630 | g_chams_rgb[0] = 255; | 643 | g_chams_rgb[0] = 255; |
| 631 | g_chams_rgb[1] = 60; | 644 | g_chams_rgb[1] = 60; |
| 632 | g_chams_rgb[2] = 60; | 645 | g_chams_rgb[2] = 60; |
diff --git a/src/studio.cpp b/src/studio.cpp index a1aae12..e667c4b 100644 --- a/src/studio.cpp +++ b/src/studio.cpp | |||
| @@ -197,15 +197,29 @@ void *g_modelrender = nullptr; | |||
| 197 | ModelExecFn g_orig_model_exec = nullptr; | 197 | ModelExecFn g_orig_model_exec = nullptr; |
| 198 | ForceOverrideFn g_force_override = nullptr; | 198 | ForceOverrideFn g_force_override = nullptr; |
| 199 | void *g_chams_mat = nullptr; | 199 | void *g_chams_mat = nullptr; |
| 200 | bool g_chams_mat_tried = false; | 200 | int g_chams_mat_idx = -1; |
| 201 | |||
| 202 | const char *chams_material_name(int idx) { | ||
| 203 | switch (idx) { | ||
| 204 | case 1: return "debug/debugwireframevertexcolorignorez"; | ||
| 205 | case 2: return "vgui/white_additive"; | ||
| 206 | case 3: return "debug/debugtranslucentsinglecolor"; | ||
| 207 | case 4: return "vgui/white"; | ||
| 208 | default: return "models/debug/debugwhite"; | ||
| 209 | } | ||
| 210 | } | ||
| 201 | 211 | ||
| 202 | void *chams_material() { | 212 | void *chams_material() { |
| 203 | if (g_chams_mat_tried) | 213 | int idx = g_chams_mat_index; |
| 214 | if (idx < 0) idx = 0; | ||
| 215 | if (idx > 4) idx = 4; | ||
| 216 | if (g_chams_mat && g_chams_mat_idx == idx) | ||
| 204 | return g_chams_mat; | 217 | return g_chams_mat; |
| 205 | g_chams_mat_tried = true; | 218 | g_chams_mat = world_find_material(chams_material_name(idx)); |
| 206 | g_chams_mat = world_find_material("models/debug/debugwhite"); | ||
| 207 | if (!g_chams_mat) | 219 | if (!g_chams_mat) |
| 208 | logf("chams: no usable material"); | 220 | g_chams_mat = world_find_material("models/debug/debugwhite"); |
| 221 | g_chams_mat_idx = idx; | ||
| 222 | logf("chams: style %d -> %p", idx, g_chams_mat); | ||
| 209 | return g_chams_mat; | 223 | return g_chams_mat; |
| 210 | } | 224 | } |
| 211 | 225 | ||
| @@ -239,23 +253,27 @@ void hkdModelExecute(void *self, const void *state, const void *info, | |||
| 239 | if (feature_active(FEAT_CHAMS) && info && g_force_override) { | 253 | if (feature_active(FEAT_CHAMS) && info && g_force_override) { |
| 240 | // ModelRenderInfo_t::entity_index is at 0x30 on 32-bit. | 254 | // ModelRenderInfo_t::entity_index is at 0x30 on 32-bit. |
| 241 | int idx = *(const int *)((const char *)info + 0x30); | 255 | int idx = *(const int *)((const char *)info + 0x30); |
| 242 | if (idx > 0 && idx <= 8192 && esp_is_npc_index(idx)) { | 256 | if (idx > 0 && idx <= 8192) { |
| 243 | void *mat = chams_material(); | 257 | bool is_npc = esp_is_npc_index(idx); |
| 244 | if (mat) { | 258 | bool is_vm = g_chams_viewmodel && esp_is_viewmodel_index(idx); |
| 245 | if (g_chams_walls) { | 259 | if (is_npc || is_vm) { |
| 246 | chams_apply(mat, true); | 260 | void *mat = chams_material(); |
| 247 | g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); | 261 | if (mat) { |
| 262 | if (g_chams_walls && !is_vm) { | ||
| 263 | chams_apply(mat, true); | ||
| 264 | g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); | ||
| 265 | g_orig_model_exec(self, state, info, bone); | ||
| 266 | } | ||
| 267 | chams_apply(mat, false); | ||
| 268 | g_force_override(self, mat, 0); | ||
| 248 | g_orig_model_exec(self, state, info, bone); | 269 | g_orig_model_exec(self, state, info, bone); |
| 270 | g_force_override(self, nullptr, 0); | ||
| 271 | // debugwhite is shared: restore colour and depth behaviour. | ||
| 272 | using FlagFn = void (*)(void *, int, bool); | ||
| 273 | vfunc<FlagFn>(mat, 29)(mat, 1 << 15, false); | ||
| 274 | chams_color(mat, 1.0f, 1.0f, 1.0f); | ||
| 275 | return; | ||
| 249 | } | 276 | } |
| 250 | chams_apply(mat, false); | ||
| 251 | g_force_override(self, mat, 0); | ||
| 252 | g_orig_model_exec(self, state, info, bone); | ||
| 253 | g_force_override(self, nullptr, 0); | ||
| 254 | // debugwhite is shared: restore colour and depth behaviour. | ||
| 255 | using FlagFn = void (*)(void *, int, bool); | ||
| 256 | vfunc<FlagFn>(mat, 29)(mat, 1 << 15, false); | ||
| 257 | chams_color(mat, 1.0f, 1.0f, 1.0f); | ||
| 258 | return; | ||
| 259 | } | 277 | } |
| 260 | } | 278 | } |
| 261 | } | 279 | } |
