diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-03 23:32:16 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-03 23:32:16 +0100 |
| commit | edba65625dd5823705619a2e682a99d6ad91ae8b (patch) | |
| tree | cae03d46267469d1076937ad22c7b2aec63b068e /src | |
| parent | GUI: add inline colour picker control (diff) | |
| download | hl2h-edba65625dd5823705619a2e682a99d6ad91ae8b.tar.gz hl2h-edba65625dd5823705619a2e682a99d6ad91ae8b.zip | |
World: add prop tint and fog override
- World tint now also colours prop materials (models/props*) with their own
colour, covering static props and prop_physics while leaving NPC, player and
weapon materials untouched.
- New Fog card: fog_override colour/start/end via ClientCmd, optional skybox
fog, and a Remove fog mode. Restores the map fog (fog_override 0) when
disabled and re-applies after level changes.
- Config sections [worldtint] prop/colour keys and [fog].
Diffstat (limited to 'src')
| -rw-r--r-- | src/gui.cpp | 18 | ||||
| -rw-r--r-- | src/hl2h.cpp | 17 | ||||
| -rw-r--r-- | src/hl2h.h | 14 | ||||
| -rw-r--r-- | src/menu.cpp | 72 | ||||
| -rw-r--r-- | src/world.cpp | 120 |
5 files changed, 213 insertions, 28 deletions
diff --git a/src/gui.cpp b/src/gui.cpp index 9981f25..aa47868 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -401,6 +401,24 @@ static void build_cards() { | |||
| 401 | add_toggle(tint, "Skybox", "Tint the skybox textures.", &g_tint_sky_on); | 401 | add_toggle(tint, "Skybox", "Tint the skybox textures.", &g_tint_sky_on); |
| 402 | add_color(tint, "Sky colour", "Click the swatch to open the picker.", | 402 | add_color(tint, "Sky colour", "Click the swatch to open the picker.", |
| 403 | g_tint_sky_rgb); | 403 | g_tint_sky_rgb); |
| 404 | add_toggle(tint, "Props", | ||
| 405 | "Tint model materials under models/props (map props, static " | ||
| 406 | "and physics).", | ||
| 407 | &g_tint_prop_on); | ||
| 408 | add_color(tint, "Prop colour", "Click the swatch to open the picker.", | ||
| 409 | g_tint_prop_rgb); | ||
| 410 | |||
| 411 | int fog = add_card(2, &g_fog, "Fog", | ||
| 412 | "Override the map's fog colour and distances.", true); | ||
| 413 | add_color(fog, "Colour", "Click the swatch to open the picker.", g_fog_rgb); | ||
| 414 | add_slider(fog, "Start", "Distance in units where fog starts.", 0, 8000, | ||
| 415 | &g_fog_start); | ||
| 416 | add_slider(fog, "End", "Distance in units where fog is fully opaque.", 0, | ||
| 417 | 20000, &g_fog_end); | ||
| 418 | add_toggle(fog, "Skybox fog", "Apply the same fog to the 3D skybox.", | ||
| 419 | &g_fog_skybox); | ||
| 420 | add_toggle(fog, "Remove fog", | ||
| 421 | "Disable fog entirely instead of recolouring it.", &g_fog_disable); | ||
| 404 | 422 | ||
| 405 | int spread = add_card(3, &g_spread, "Spread control", | 423 | int spread = add_card(3, &g_spread, "Spread control", |
| 406 | "Choose a server shot seed that tightens the cone.", | 424 | "Choose a server shot seed that tightens the cone.", |
diff --git a/src/hl2h.cpp b/src/hl2h.cpp index dfe4e57..3f1acb4 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp | |||
| @@ -130,17 +130,23 @@ static int g_modified_logged = 0; | |||
| 130 | // ConCommandBase has two dtor slots, then IsCommand, IsFlagSet, AddFlags, | 130 | // ConCommandBase has two dtor slots, then IsCommand, IsFlagSet, AddFlags, |
| 131 | // GetName, GetHelpText, IsRegistered, GetDLLIdentifier; ConVar then adds | 131 | // GetName, GetHelpText, IsRegistered, GetDLLIdentifier; ConVar then adds |
| 132 | // SetValue(const char*), SetValue(float), SetValue(int) -> slot 11. | 132 | // SetValue(const char*), SetValue(float), SetValue(int) -> slot 11. |
| 133 | bool console_cmd(const char *cmd) { | ||
| 134 | if (!cmd || !*cmd || !g_iface.engine) | ||
| 135 | return false; | ||
| 136 | using CmdFn = void (*)(void *, const char *); | ||
| 137 | vfunc<CmdFn>(g_iface.engine, 7)(g_iface.engine, cmd); | ||
| 138 | logd("cmd: %s", cmd); | ||
| 139 | return true; | ||
| 140 | } | ||
| 141 | |||
| 133 | bool cvar_set_int(const char *name, int value) { | 142 | bool cvar_set_int(const char *name, int value) { |
| 134 | if (!name || !g_iface.engine) | 143 | if (!name) |
| 135 | return false; | 144 | return false; |
| 136 | // Use the stable IVEngineClient ClientCmd slot rather than poking ConVar's | 145 | // Use the stable IVEngineClient ClientCmd slot rather than poking ConVar's |
| 137 | // vtable; the installed VEngineCvar004 layout differs enough to crash. | 146 | // vtable; the installed VEngineCvar004 layout differs enough to crash. |
| 138 | char cmd[96]; | 147 | char cmd[96]; |
| 139 | snprintf(cmd, sizeof(cmd), "%s %d", name, value); | 148 | snprintf(cmd, sizeof(cmd), "%s %d", name, value); |
| 140 | using CmdFn = void (*)(void *, const char *); | 149 | return console_cmd(cmd); |
| 141 | vfunc<CmdFn>(g_iface.engine, 7)(g_iface.engine, cmd); | ||
| 142 | logd("cvar: command '%s'", cmd); | ||
| 143 | return true; | ||
| 144 | } | 150 | } |
| 145 | 151 | ||
| 146 | void note_modified(const CUserCmd *cmd, int flags, const float *vel) { | 152 | void note_modified(const CUserCmd *cmd, int flags, const float *vel) { |
| @@ -585,6 +591,7 @@ static void do_move(int seq, float frametime) { | |||
| 585 | silentaim_reset_lock(); | 591 | silentaim_reset_lock(); |
| 586 | fullbright_invalidate(); | 592 | fullbright_invalidate(); |
| 587 | world_tint_invalidate(); | 593 | world_tint_invalidate(); |
| 594 | fog_invalidate(); | ||
| 588 | } | 595 | } |
| 589 | 596 | ||
| 590 | if (feature_active(FEAT_SPREAD)) { | 597 | if (feature_active(FEAT_SPREAD)) { |
| @@ -179,7 +179,8 @@ enum FeatureId { | |||
| 179 | FEAT_ITEM_ESP = 9, | 179 | FEAT_ITEM_ESP = 9, |
| 180 | FEAT_FULLBRIGHT = 10, | 180 | FEAT_FULLBRIGHT = 10, |
| 181 | FEAT_WORLD_TINT = 11, | 181 | FEAT_WORLD_TINT = 11, |
| 182 | FEAT_COUNT = 12 | 182 | FEAT_FOG = 12, |
| 183 | FEAT_COUNT = 13 | ||
| 183 | }; | 184 | }; |
| 184 | enum EspOption { | 185 | enum EspOption { |
| 185 | ESP_OPT_SHOW_NEUTRAL = 0, | 186 | ESP_OPT_SHOW_NEUTRAL = 0, |
| @@ -212,9 +213,11 @@ struct FeatureState { | |||
| 212 | 213 | ||
| 213 | extern FeatureState g_bhop, g_abh, g_jumpbug, g_melee, g_spread, g_esp, | 214 | extern FeatureState g_bhop, g_abh, g_jumpbug, g_melee, g_spread, g_esp, |
| 214 | g_aimbot, g_triggerbot, g_silentaim, g_item_esp, g_fullbright, | 215 | g_aimbot, g_triggerbot, g_silentaim, g_item_esp, g_fullbright, |
| 215 | g_world_tint; | 216 | g_world_tint, g_fog; |
| 216 | extern bool g_tint_world_on, g_tint_sky_on; | 217 | extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on; |
| 217 | extern int g_tint_world_rgb[3], g_tint_sky_rgb[3]; | 218 | extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3]; |
| 219 | extern bool g_fog_skybox, g_fog_disable; | ||
| 220 | extern int g_fog_rgb[3], g_fog_start, g_fog_end; | ||
| 218 | extern bool g_bhop_autostrafe; | 221 | extern bool g_bhop_autostrafe; |
| 219 | extern bool g_jumpbug_assist; | 222 | extern bool g_jumpbug_assist; |
| 220 | extern bool g_spread_no_spread; | 223 | extern bool g_spread_no_spread; |
| @@ -249,11 +252,14 @@ void fix_verified_crc(int seq, uint32_t crc); | |||
| 249 | uint32_t usercmd_checksum(const CUserCmd *c); | 252 | uint32_t usercmd_checksum(const CUserCmd *c); |
| 250 | bool find_input_global(); | 253 | bool find_input_global(); |
| 251 | void resolve_netvars(); | 254 | void resolve_netvars(); |
| 255 | bool console_cmd(const char *cmd); | ||
| 252 | bool cvar_set_int(const char *name, int value); | 256 | bool cvar_set_int(const char *name, int value); |
| 253 | void fullbright_update(); | 257 | void fullbright_update(); |
| 254 | void fullbright_invalidate(); | 258 | void fullbright_invalidate(); |
| 255 | void world_tint_update(); | 259 | void world_tint_update(); |
| 256 | void world_tint_invalidate(); | 260 | void world_tint_invalidate(); |
| 261 | void fog_update(); | ||
| 262 | void fog_invalidate(); | ||
| 257 | bool resolve_server_netvars(); | 263 | bool resolve_server_netvars(); |
| 258 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); | 264 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); |
| 259 | bool server_set_velocity_z(int idx, float vz); | 265 | bool server_set_velocity_z(int idx, float vz); |
diff --git a/src/menu.cpp b/src/menu.cpp index 6a7ffe5..237502b 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -49,8 +49,16 @@ FeatureState g_fullbright{false, 0}; // mat_fullbright world lighting | |||
| 49 | FeatureState g_world_tint{false, 0}; // material colour modulation | 49 | FeatureState g_world_tint{false, 0}; // material colour modulation |
| 50 | bool g_tint_world_on = true; | 50 | bool g_tint_world_on = true; |
| 51 | bool g_tint_sky_on = true; | 51 | bool g_tint_sky_on = true; |
| 52 | bool g_tint_prop_on = true; | ||
| 52 | int g_tint_world_rgb[3] = {255, 255, 255}; | 53 | int g_tint_world_rgb[3] = {255, 255, 255}; |
| 53 | int g_tint_sky_rgb[3] = {255, 255, 255}; | 54 | int g_tint_sky_rgb[3] = {255, 255, 255}; |
| 55 | int g_tint_prop_rgb[3] = {255, 255, 255}; | ||
| 56 | FeatureState g_fog{false, 0}; // fog_override colour/distance | ||
| 57 | bool g_fog_skybox = true; | ||
| 58 | bool g_fog_disable = false; | ||
| 59 | int g_fog_rgb[3] = {160, 170, 190}; | ||
| 60 | int g_fog_start = 200; | ||
| 61 | int g_fog_end = 3000; | ||
| 54 | bool g_item_health = true; | 62 | bool g_item_health = true; |
| 55 | bool g_item_ammo = true; | 63 | bool g_item_ammo = true; |
| 56 | bool g_item_armor = true; | 64 | bool g_item_armor = true; |
| @@ -193,11 +201,17 @@ void config_save() { | |||
| 193 | fprintf(f, "[fullbright]\nactive=%d\nbind=%d\n", g_fullbright.active, | 201 | fprintf(f, "[fullbright]\nactive=%d\nbind=%d\n", g_fullbright.active, |
| 194 | g_fullbright.bind); | 202 | g_fullbright.bind); |
| 195 | fprintf(f, "[worldtint]\nactive=%d\nbind=%d\nworld=%d\nsky=%d\n" | 203 | fprintf(f, "[worldtint]\nactive=%d\nbind=%d\nworld=%d\nsky=%d\n" |
| 196 | "wr=%d\nwg=%d\nwb=%d\nsr=%d\nsg=%d\nsb=%d\n", | 204 | "prop=%d\nwr=%d\nwg=%d\nwb=%d\nsr=%d\nsg=%d\nsb=%d\n" |
| 205 | "pr=%d\npg=%d\npb=%d\n", | ||
| 197 | g_world_tint.active, g_world_tint.bind, (int)g_tint_world_on, | 206 | g_world_tint.active, g_world_tint.bind, (int)g_tint_world_on, |
| 198 | (int)g_tint_sky_on, g_tint_world_rgb[0], g_tint_world_rgb[1], | 207 | (int)g_tint_sky_on, (int)g_tint_prop_on, g_tint_world_rgb[0], |
| 199 | g_tint_world_rgb[2], g_tint_sky_rgb[0], g_tint_sky_rgb[1], | 208 | g_tint_world_rgb[1], g_tint_world_rgb[2], g_tint_sky_rgb[0], |
| 200 | g_tint_sky_rgb[2]); | 209 | g_tint_sky_rgb[1], g_tint_sky_rgb[2], g_tint_prop_rgb[0], |
| 210 | g_tint_prop_rgb[1], g_tint_prop_rgb[2]); | ||
| 211 | fprintf(f, "[fog]\nactive=%d\nbind=%d\nr=%d\ng=%d\nb=%d\n" | ||
| 212 | "start=%d\nend=%d\nskybox=%d\ndisable=%d\n", | ||
| 213 | g_fog.active, g_fog.bind, g_fog_rgb[0], g_fog_rgb[1], g_fog_rgb[2], | ||
| 214 | g_fog_start, g_fog_end, (int)g_fog_skybox, (int)g_fog_disable); | ||
| 201 | fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" | 215 | fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" |
| 202 | "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n", | 216 | "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n", |
| 203 | g_item_esp.active, g_item_esp.bind, (int)g_item_health, | 217 | g_item_esp.active, g_item_esp.bind, (int)g_item_health, |
| @@ -232,7 +246,7 @@ static bool config_load() { | |||
| 232 | FeatureState *cur = nullptr; | 246 | FeatureState *cur = nullptr; |
| 233 | bool in_esp = false, in_bhop = false, in_aim = false, in_silent = false, | 247 | bool in_esp = false, in_bhop = false, in_aim = false, in_silent = false, |
| 234 | in_jumpbug = false, in_spread = false, in_items = false, | 248 | in_jumpbug = false, in_spread = false, in_items = false, |
| 235 | in_tint = false; | 249 | in_tint = false, in_fog = false; |
| 236 | while (fgets(line, sizeof(line), f)) { | 250 | while (fgets(line, sizeof(line), f)) { |
| 237 | line[strcspn(line, "\r\n")] = 0; | 251 | line[strcspn(line, "\r\n")] = 0; |
| 238 | if (line[0] == '[') { | 252 | if (line[0] == '[') { |
| @@ -254,6 +268,8 @@ static bool config_load() { | |||
| 254 | cur = &g_fullbright; | 268 | cur = &g_fullbright; |
| 255 | else if (strstr(line, "[worldtint]")) | 269 | else if (strstr(line, "[worldtint]")) |
| 256 | cur = &g_world_tint; | 270 | cur = &g_world_tint; |
| 271 | else if (strstr(line, "[fog]")) | ||
| 272 | cur = &g_fog; | ||
| 257 | else if (strstr(line, "[aimbot]")) | 273 | else if (strstr(line, "[aimbot]")) |
| 258 | cur = &g_aimbot; | 274 | cur = &g_aimbot; |
| 259 | else if (strstr(line, "[triggerbot]")) | 275 | else if (strstr(line, "[triggerbot]")) |
| @@ -270,6 +286,7 @@ static bool config_load() { | |||
| 270 | in_spread = (cur == &g_spread); | 286 | in_spread = (cur == &g_spread); |
| 271 | in_items = (cur == &g_item_esp); | 287 | in_items = (cur == &g_item_esp); |
| 272 | in_tint = (cur == &g_world_tint); | 288 | in_tint = (cur == &g_world_tint); |
| 289 | in_fog = (cur == &g_fog); | ||
| 273 | } else if (cur) { | 290 | } else if (cur) { |
| 274 | char *eq = strchr(line, '='); | 291 | char *eq = strchr(line, '='); |
| 275 | if (!eq) | 292 | if (!eq) |
| @@ -345,6 +362,8 @@ static bool config_load() { | |||
| 345 | g_tint_world_on = atoi(val) != 0; | 362 | g_tint_world_on = atoi(val) != 0; |
| 346 | else if (strcmp(key, "sky") == 0) | 363 | else if (strcmp(key, "sky") == 0) |
| 347 | g_tint_sky_on = atoi(val) != 0; | 364 | g_tint_sky_on = atoi(val) != 0; |
| 365 | else if (strcmp(key, "prop") == 0) | ||
| 366 | g_tint_prop_on = atoi(val) != 0; | ||
| 348 | else if (strcmp(key, "wr") == 0 || strcmp(key, "wg") == 0 || | 367 | else if (strcmp(key, "wr") == 0 || strcmp(key, "wg") == 0 || |
| 349 | strcmp(key, "wb") == 0) { | 368 | strcmp(key, "wb") == 0) { |
| 350 | int v = atoi(val); | 369 | int v = atoi(val); |
| @@ -357,6 +376,35 @@ static bool config_load() { | |||
| 357 | if (v < 0) v = 0; | 376 | if (v < 0) v = 0; |
| 358 | if (v > 255) v = 255; | 377 | if (v > 255) v = 255; |
| 359 | g_tint_sky_rgb[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; | 378 | g_tint_sky_rgb[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; |
| 379 | } else if (strcmp(key, "pr") == 0 || strcmp(key, "pg") == 0 || | ||
| 380 | strcmp(key, "pb") == 0) { | ||
| 381 | int v = atoi(val); | ||
| 382 | if (v < 0) v = 0; | ||
| 383 | if (v > 255) v = 255; | ||
| 384 | g_tint_prop_rgb[key[1] == 'r' ? 0 : key[1] == 'g' ? 1 : 2] = v; | ||
| 385 | } | ||
| 386 | } | ||
| 387 | if (in_fog) { | ||
| 388 | if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || | ||
| 389 | strcmp(key, "b") == 0) { | ||
| 390 | int v = atoi(val); | ||
| 391 | if (v < 0) v = 0; | ||
| 392 | if (v > 255) v = 255; | ||
| 393 | g_fog_rgb[key[0] == 'r' ? 0 : key[0] == 'g' ? 1 : 2] = v; | ||
| 394 | } else if (strcmp(key, "start") == 0) { | ||
| 395 | int v = atoi(val); | ||
| 396 | if (v < 0) v = 0; | ||
| 397 | if (v > 8000) v = 8000; | ||
| 398 | g_fog_start = v; | ||
| 399 | } else if (strcmp(key, "end") == 0) { | ||
| 400 | int v = atoi(val); | ||
| 401 | if (v < 0) v = 0; | ||
| 402 | if (v > 20000) v = 20000; | ||
| 403 | g_fog_end = v; | ||
| 404 | } else if (strcmp(key, "skybox") == 0) { | ||
| 405 | g_fog_skybox = atoi(val) != 0; | ||
| 406 | } else if (strcmp(key, "disable") == 0) { | ||
| 407 | g_fog_disable = atoi(val) != 0; | ||
| 360 | } | 408 | } |
| 361 | } | 409 | } |
| 362 | if (in_esp) { | 410 | if (in_esp) { |
| @@ -415,8 +463,18 @@ void config_reset_defaults() { | |||
| 415 | g_world_tint = {false, 0}; | 463 | g_world_tint = {false, 0}; |
| 416 | g_tint_world_on = true; | 464 | g_tint_world_on = true; |
| 417 | g_tint_sky_on = true; | 465 | g_tint_sky_on = true; |
| 466 | g_tint_prop_on = true; | ||
| 418 | g_tint_world_rgb[0] = g_tint_world_rgb[1] = g_tint_world_rgb[2] = 255; | 467 | g_tint_world_rgb[0] = g_tint_world_rgb[1] = g_tint_world_rgb[2] = 255; |
| 419 | g_tint_sky_rgb[0] = g_tint_sky_rgb[1] = g_tint_sky_rgb[2] = 255; | 468 | g_tint_sky_rgb[0] = g_tint_sky_rgb[1] = g_tint_sky_rgb[2] = 255; |
| 469 | g_tint_prop_rgb[0] = g_tint_prop_rgb[1] = g_tint_prop_rgb[2] = 255; | ||
| 470 | g_fog = {false, 0}; | ||
| 471 | g_fog_skybox = true; | ||
| 472 | g_fog_disable = false; | ||
| 473 | g_fog_rgb[0] = 160; | ||
| 474 | g_fog_rgb[1] = 170; | ||
| 475 | g_fog_rgb[2] = 190; | ||
| 476 | g_fog_start = 200; | ||
| 477 | g_fog_end = 3000; | ||
| 420 | g_item_health = true; | 478 | g_item_health = true; |
| 421 | g_item_ammo = true; | 479 | g_item_ammo = true; |
| 422 | g_item_armor = true; | 480 | g_item_armor = true; |
| @@ -534,7 +592,7 @@ static FeatureState *feature_state(int feature) { | |||
| 534 | static FeatureState *feats[FEAT_COUNT] = { | 592 | static FeatureState *feats[FEAT_COUNT] = { |
| 535 | &g_bhop, &g_abh, &g_esp, &g_aimbot, &g_triggerbot, | 593 | &g_bhop, &g_abh, &g_esp, &g_aimbot, &g_triggerbot, |
| 536 | &g_silentaim, &g_jumpbug, &g_melee, &g_spread, &g_item_esp, | 594 | &g_silentaim, &g_jumpbug, &g_melee, &g_spread, &g_item_esp, |
| 537 | &g_fullbright, &g_world_tint}; | 595 | &g_fullbright, &g_world_tint, &g_fog}; |
| 538 | if (feature < 0 || feature >= FEAT_COUNT) | 596 | if (feature < 0 || feature >= FEAT_COUNT) |
| 539 | return nullptr; | 597 | return nullptr; |
| 540 | return feats[feature]; | 598 | return feats[feature]; |
| @@ -741,6 +799,7 @@ void outer_draw() { | |||
| 741 | poll_keyboard(); | 799 | poll_keyboard(); |
| 742 | fullbright_update(); | 800 | fullbright_update(); |
| 743 | world_tint_update(); | 801 | world_tint_update(); |
| 802 | fog_update(); | ||
| 744 | gui_handle_input(); | 803 | gui_handle_input(); |
| 745 | // HL2H_DRAW_TEST=1 draws a probe rect without needing the GUI open | 804 | // HL2H_DRAW_TEST=1 draws a probe rect without needing the GUI open |
| 746 | static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; | 805 | static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; |
| @@ -762,6 +821,7 @@ void outer_draw() { | |||
| 762 | last_local = le; | 821 | last_local = le; |
| 763 | font_reset = true; | 822 | font_reset = true; |
| 764 | world_tint_invalidate(); | 823 | world_tint_invalidate(); |
| 824 | fog_invalidate(); | ||
| 765 | } | 825 | } |
| 766 | } | 826 | } |
| 767 | if (font_reset && g_font) { | 827 | if (font_reset && g_font) { |
diff --git a/src/world.cpp b/src/world.cpp index eeffb99..2333829 100644 --- a/src/world.cpp +++ b/src/world.cpp | |||
| @@ -48,8 +48,16 @@ struct Tint { | |||
| 48 | bool valid; | 48 | bool valid; |
| 49 | int world_r, world_g, world_b; | 49 | int world_r, world_g, world_b; |
| 50 | int sky_r, sky_g, sky_b; | 50 | int sky_r, sky_g, sky_b; |
| 51 | int prop_r, prop_g, prop_b; | ||
| 51 | }; | 52 | }; |
| 52 | Tint g_applied{false, 255, 255, 255, 255, 255, 255}; | 53 | Tint g_applied{false, 255, 255, 255, 255, 255, 255, 255, 255, 255}; |
| 54 | |||
| 55 | // Static props and prop_physics share their model materials; every stock HL2 | ||
| 56 | // map prop lives under models/props*, which keeps NPC/player/weapon materials | ||
| 57 | // out of the prop pass. | ||
| 58 | bool is_prop_material(const char *name) { | ||
| 59 | return name && strncmp(name, "models/props", 12) == 0; | ||
| 60 | } | ||
| 53 | 61 | ||
| 54 | struct MatCalls { | 62 | struct MatCalls { |
| 55 | using FirstFn = unsigned short (*)(void *); | 63 | using FirstFn = unsigned short (*)(void *); |
| @@ -145,7 +153,7 @@ void *material_system() { | |||
| 145 | // Push one colour pair over all materials. Groups that are off get white so a | 153 | // Push one colour pair over all materials. Groups that are off get white so a |
| 146 | // previously tinted world is restored. | 154 | // previously tinted world is restored. |
| 147 | bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, | 155 | bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, |
| 148 | int sky_b) { | 156 | int sky_b, int prop_r, int prop_g, int prop_b) { |
| 149 | void *ms = material_system(); | 157 | void *ms = material_system(); |
| 150 | if (!ms) | 158 | if (!ms) |
| 151 | return false; | 159 | return false; |
| @@ -166,9 +174,10 @@ bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, | |||
| 166 | 174 | ||
| 167 | float wr = world_r / 255.0f, wg = world_g / 255.0f, wb = world_b / 255.0f; | 175 | float wr = world_r / 255.0f, wg = world_g / 255.0f, wb = world_b / 255.0f; |
| 168 | float sr = sky_r / 255.0f, sg = sky_g / 255.0f, sb = sky_b / 255.0f; | 176 | float sr = sky_r / 255.0f, sg = sky_g / 255.0f, sb = sky_b / 255.0f; |
| 177 | float pr = prop_r / 255.0f, pg = prop_g / 255.0f, pb = prop_b / 255.0f; | ||
| 169 | 178 | ||
| 170 | unsigned short inv = invalid(ms); | 179 | unsigned short inv = invalid(ms); |
| 171 | int seen = 0, world_hits = 0, sky_hits = 0; | 180 | int seen = 0, world_hits = 0, sky_hits = 0, prop_hits = 0; |
| 172 | for (unsigned short h = first(ms); h != inv; h = next(ms, h)) { | 181 | for (unsigned short h = first(ms); h != inv; h = next(ms, h)) { |
| 173 | if (++seen > total + 16) { | 182 | if (++seen > total + 16) { |
| 174 | logf("tint: material walk runaway (%d > %d); stopped", seen, total); | 183 | logf("tint: material walk runaway (%d > %d); stopped", seen, total); |
| @@ -199,11 +208,15 @@ bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, | |||
| 199 | } else if (is_sky) { | 208 | } else if (is_sky) { |
| 200 | mod_fn(mat, sr, sg, sb); | 209 | mod_fn(mat, sr, sg, sb); |
| 201 | sky_hits++; | 210 | sky_hits++; |
| 211 | } else if (is_prop_material(name)) { | ||
| 212 | mod_fn(mat, pr, pg, pb); | ||
| 213 | prop_hits++; | ||
| 202 | } | 214 | } |
| 203 | } | 215 | } |
| 204 | logf("tint: applied world=%d(%d,%d,%d) sky=%d(%d,%d,%d) of %d materials", | 216 | logf("tint: applied world=%d(%d,%d,%d) sky=%d(%d,%d,%d) props=%d(%d,%d,%d) " |
| 217 | "of %d materials", | ||
| 205 | world_hits, world_r, world_g, world_b, sky_hits, sky_r, sky_g, sky_b, | 218 | world_hits, world_r, world_g, world_b, sky_hits, sky_r, sky_g, sky_b, |
| 206 | total); | 219 | prop_hits, prop_r, prop_g, prop_b, total); |
| 207 | return true; | 220 | return true; |
| 208 | } | 221 | } |
| 209 | 222 | ||
| @@ -215,6 +228,7 @@ void world_tint_update() { | |||
| 215 | bool on = feature_active(FEAT_WORLD_TINT); | 228 | bool on = feature_active(FEAT_WORLD_TINT); |
| 216 | bool world_on = on && g_tint_world_on; | 229 | bool world_on = on && g_tint_world_on; |
| 217 | bool sky_on = on && g_tint_sky_on; | 230 | bool sky_on = on && g_tint_sky_on; |
| 231 | bool prop_on = on && g_tint_prop_on; | ||
| 218 | 232 | ||
| 219 | int wr = world_on ? g_tint_world_rgb[0] : 255; | 233 | int wr = world_on ? g_tint_world_rgb[0] : 255; |
| 220 | int wg = world_on ? g_tint_world_rgb[1] : 255; | 234 | int wg = world_on ? g_tint_world_rgb[1] : 255; |
| @@ -222,27 +236,107 @@ void world_tint_update() { | |||
| 222 | int sr = sky_on ? g_tint_sky_rgb[0] : 255; | 236 | int sr = sky_on ? g_tint_sky_rgb[0] : 255; |
| 223 | int sg = sky_on ? g_tint_sky_rgb[1] : 255; | 237 | int sg = sky_on ? g_tint_sky_rgb[1] : 255; |
| 224 | int sb = sky_on ? g_tint_sky_rgb[2] : 255; | 238 | int sb = sky_on ? g_tint_sky_rgb[2] : 255; |
| 239 | int pr = prop_on ? g_tint_prop_rgb[0] : 255; | ||
| 240 | int pg = prop_on ? g_tint_prop_rgb[1] : 255; | ||
| 241 | int pb = prop_on ? g_tint_prop_rgb[2] : 255; | ||
| 225 | 242 | ||
| 226 | bool white = wr == 255 && wg == 255 && wb == 255 && sr == 255 && | 243 | bool white = wr == 255 && wg == 255 && wb == 255 && sr == 255 && |
| 227 | sg == 255 && sb == 255; | 244 | sg == 255 && sb == 255 && pr == 255 && pg == 255 && pb == 255; |
| 228 | if (g_applied.valid && g_applied.world_r == wr && g_applied.world_g == wg && | 245 | if (g_applied.valid && g_applied.world_r == wr && g_applied.world_g == wg && |
| 229 | g_applied.world_b == wb && g_applied.sky_r == sr && | 246 | g_applied.world_b == wb && g_applied.sky_r == sr && |
| 230 | g_applied.sky_g == sg && g_applied.sky_b == sb) | 247 | g_applied.sky_g == sg && g_applied.sky_b == sb && |
| 248 | g_applied.prop_r == pr && g_applied.prop_g == pg && | ||
| 249 | g_applied.prop_b == pb) | ||
| 231 | return; | 250 | return; |
| 232 | // Nothing was ever tinted and nothing is requested: no need to walk the | 251 | // Nothing was ever tinted and nothing is requested: no need to walk the |
| 233 | // material list just to write white over white. | 252 | // material list just to write white over white. |
| 234 | if (!g_applied.valid && white) { | 253 | if (!g_applied.valid && white) { |
| 235 | g_applied = {true, wr, wg, wb, sr, sg, sb}; | 254 | g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; |
| 236 | return; | 255 | return; |
| 237 | } | 256 | } |
| 238 | if (g_matsys_failed && !world_on && !sky_on) { | 257 | if (g_matsys_failed && !world_on && !sky_on && !prop_on) { |
| 239 | g_applied = {true, wr, wg, wb, sr, sg, sb}; | 258 | g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; |
| 240 | return; | 259 | return; |
| 241 | } | 260 | } |
| 242 | if (!apply_tint(wr, wg, wb, sr, sg, sb)) { | 261 | if (!apply_tint(wr, wg, wb, sr, sg, sb, pr, pg, pb)) { |
| 243 | if (g_matsys_failed) | 262 | if (g_matsys_failed) |
| 244 | g_applied = {true, wr, wg, wb, sr, sg, sb}; | 263 | g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; |
| 245 | return; | 264 | return; |
| 246 | } | 265 | } |
| 247 | g_applied = {true, wr, wg, wb, sr, sg, sb}; | 266 | g_applied = {true, wr, wg, wb, sr, sg, sb, pr, pg, pb}; |
| 267 | } | ||
| 268 | |||
| 269 | // --------------------------------------------------------------------------- | ||
| 270 | // Fog override (engine console commands; no render hooks) | ||
| 271 | // --------------------------------------------------------------------------- | ||
| 272 | |||
| 273 | namespace { | ||
| 274 | |||
| 275 | struct FogState { | ||
| 276 | bool valid; | ||
| 277 | bool on; | ||
| 278 | bool disable; | ||
| 279 | int r, g, b; | ||
| 280 | int start, end; | ||
| 281 | bool skybox; | ||
| 282 | }; | ||
| 283 | FogState g_fog_applied{false, false, false, 0, 0, 0, 0, 0, false}; | ||
| 284 | |||
| 285 | } // namespace | ||
| 286 | |||
| 287 | void fog_invalidate() { g_fog_applied.valid = false; } | ||
| 288 | |||
| 289 | void fog_update() { | ||
| 290 | bool on = feature_active(FEAT_FOG); | ||
| 291 | bool disable = on && g_fog_disable; | ||
| 292 | int r = g_fog_rgb[0], g = g_fog_rgb[1], b = g_fog_rgb[2]; | ||
| 293 | int start = g_fog_start; | ||
| 294 | int end = g_fog_end; | ||
| 295 | if (end <= start) | ||
| 296 | end = start + 1; | ||
| 297 | bool skybox = on && g_fog_skybox; | ||
| 298 | |||
| 299 | if (g_fog_applied.valid && g_fog_applied.on == on && | ||
| 300 | g_fog_applied.disable == disable && g_fog_applied.r == r && | ||
| 301 | g_fog_applied.g == g && g_fog_applied.b == b && | ||
| 302 | g_fog_applied.start == start && g_fog_applied.end == end && | ||
| 303 | g_fog_applied.skybox == skybox) | ||
| 304 | return; | ||
| 305 | |||
| 306 | if (!on) { | ||
| 307 | if (g_fog_applied.valid && g_fog_applied.on) | ||
| 308 | console_cmd("fog_override 0"); | ||
| 309 | g_fog_applied = {true, false, false, 0, 0, 0, 0, 0, false}; | ||
| 310 | return; | ||
| 311 | } | ||
| 312 | |||
| 313 | console_cmd("fog_override 1"); | ||
| 314 | char cmd[128]; | ||
| 315 | if (disable) { | ||
| 316 | console_cmd("fog_enable 0"); | ||
| 317 | if (skybox) | ||
| 318 | console_cmd("fog_enableskybox 0"); | ||
| 319 | } else { | ||
| 320 | console_cmd("fog_enable 1"); | ||
| 321 | snprintf(cmd, sizeof(cmd), "fog_color %d %d %d", r, g, b); | ||
| 322 | console_cmd(cmd); | ||
| 323 | snprintf(cmd, sizeof(cmd), "fog_start %d", start); | ||
| 324 | console_cmd(cmd); | ||
| 325 | snprintf(cmd, sizeof(cmd), "fog_end %d", end); | ||
| 326 | console_cmd(cmd); | ||
| 327 | if (skybox) { | ||
| 328 | console_cmd("fog_enableskybox 1"); | ||
| 329 | snprintf(cmd, sizeof(cmd), "fog_colorskybox %d %d %d", r, g, b); | ||
| 330 | console_cmd(cmd); | ||
| 331 | snprintf(cmd, sizeof(cmd), "fog_startskybox %d", start); | ||
| 332 | console_cmd(cmd); | ||
| 333 | snprintf(cmd, sizeof(cmd), "fog_endskybox %d", end); | ||
| 334 | console_cmd(cmd); | ||
| 335 | } else { | ||
| 336 | console_cmd("fog_enableskybox 0"); | ||
| 337 | } | ||
| 338 | } | ||
| 339 | g_fog_applied = {true, on, disable, r, g, b, start, end, skybox}; | ||
| 340 | logf("fog: %s colour=(%d,%d,%d) start=%d end=%d skybox=%d", disable ? "off" | ||
| 341 | : "on", r, g, b, start, end, (int)skybox); | ||
| 248 | } | 342 | } |
