summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTEXT.md26
-rw-r--r--src/gui.cpp18
-rw-r--r--src/hl2h.cpp17
-rw-r--r--src/hl2h.h14
-rw-r--r--src/menu.cpp72
-rw-r--r--src/world.cpp120
6 files changed, 236 insertions, 31 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index b48100b..1d365e8 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -316,8 +316,11 @@ vocabulary / decisions.
316 already-loaded `materialsystem.so` and walk every material with 316 already-loaded `materialsystem.so` and walk every material with
317 `FirstMaterial` / `NextMaterial` / `GetMaterial`. 317 `FirstMaterial` / `NextMaterial` / `GetMaterial`.
318- `IMaterial::ColorModulate(r,g,b)` is applied to materials whose 318- `IMaterial::ColorModulate(r,g,b)` is applied to materials whose
319 `GetTextureGroupName()` is `World textures` (sky names excluded) and to 319 `GetTextureGroupName()` is `World textures` (sky names excluded), to
320 `SkyBox textures` (or names containing `sky`). Lightmaps are not touched. 320 `SkyBox textures` (or names containing `sky`), and to prop materials whose
321 name starts with `models/props` (static props and prop_physics share those
322 model materials; NPC/player/weapon materials are excluded). Lightmaps are
323 not touched.
321- Slot map (2013 SDK layout, registered under `VMaterialSystem080`): 324- Slot map (2013 SDK layout, registered under `VMaterialSystem080`):
322 `IMaterialSystem` 73/74/75/76/77 = First/Next/Invalid/Get/GetNumMaterials; 325 `IMaterialSystem` 73/74/75/76/77 = First/Next/Invalid/Get/GetNumMaterials;
323 `IMaterial` 0/1/28 = GetName/GetTextureGroupName/ColorModulate. 326 `IMaterial` 0/1/28 = GetName/GetTextureGroupName/ColorModulate.
@@ -327,7 +330,24 @@ vocabulary / decisions.
327- Validation before any walk: `GetNumMaterials()` must return 1..200000; the 330- Validation before any walk: `GetNumMaterials()` must return 1..200000; the
328 first material's name/group must be printable strings. A mismatch logs and 331 first material's name/group must be printable strings. A mismatch logs and
329 toasts instead of poking garbage. 332 toasts instead of poking garbage.
330- Saved as `[worldtint] active/bind/world/sky/wr..wb/sr..sb`. 333- Saved as `[worldtint] active/bind/world/sky/prop/wr..pb`.
334- Particle tint is deliberately not implemented: HL2's old particle manager
335 draws per-vertex coloured IMesh quads, and SpriteCard/TF2-style particles
336 take colour from per-particle vertex data, so `IMaterial::ColorModulate`
337 does nothing for them. It needs a hook in the client particle render path.
338
339### Fog
340
341- `src/world.cpp` `fog_update()`, driven by the `fog_override` engine command
342 family through `IVEngineClient::ClientCmd` (same safe path as fullbright).
343 No render hooks.
344- Enabled: `fog_override 1`, `fog_enable 1`, `fog_color`, `fog_start`,
345 `fog_end`, plus `fog_enableskybox` / `fog_colorskybox` /
346 `fog_startskybox` / `fog_endskybox` when skybox fog is on.
347 "Remove fog" issues `fog_enable 0` instead.
348- Disabled: `fog_override 0` restores the map's own fog controller.
349- End is clamped above Start. Re-applied on level change via
350 `fog_invalidate()`. Saved as `[fog] active/bind/r/g/b/start/end/skybox/disable`.
331 351
332## Logging 352## Logging
333 353
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.
133bool 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
133bool cvar_set_int(const char *name, int value) { 142bool 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
146void note_modified(const CUserCmd *cmd, int flags, const float *vel) { 152void 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)) {
diff --git a/src/hl2h.h b/src/hl2h.h
index e90ba91..6f23221 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -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};
184enum EspOption { 185enum EspOption {
185 ESP_OPT_SHOW_NEUTRAL = 0, 186 ESP_OPT_SHOW_NEUTRAL = 0,
@@ -212,9 +213,11 @@ struct FeatureState {
212 213
213extern FeatureState g_bhop, g_abh, g_jumpbug, g_melee, g_spread, g_esp, 214extern 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;
216extern bool g_tint_world_on, g_tint_sky_on; 217extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on;
217extern int g_tint_world_rgb[3], g_tint_sky_rgb[3]; 218extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3];
219extern bool g_fog_skybox, g_fog_disable;
220extern int g_fog_rgb[3], g_fog_start, g_fog_end;
218extern bool g_bhop_autostrafe; 221extern bool g_bhop_autostrafe;
219extern bool g_jumpbug_assist; 222extern bool g_jumpbug_assist;
220extern bool g_spread_no_spread; 223extern bool g_spread_no_spread;
@@ -249,11 +252,14 @@ void fix_verified_crc(int seq, uint32_t crc);
249uint32_t usercmd_checksum(const CUserCmd *c); 252uint32_t usercmd_checksum(const CUserCmd *c);
250bool find_input_global(); 253bool find_input_global();
251void resolve_netvars(); 254void resolve_netvars();
255bool console_cmd(const char *cmd);
252bool cvar_set_int(const char *name, int value); 256bool cvar_set_int(const char *name, int value);
253void fullbright_update(); 257void fullbright_update();
254void fullbright_invalidate(); 258void fullbright_invalidate();
255void world_tint_update(); 259void world_tint_update();
256void world_tint_invalidate(); 260void world_tint_invalidate();
261void fog_update();
262void fog_invalidate();
257bool resolve_server_netvars(); 263bool resolve_server_netvars();
258int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); 264int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out);
259bool server_set_velocity_z(int idx, float vz); 265bool 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
49FeatureState g_world_tint{false, 0}; // material colour modulation 49FeatureState g_world_tint{false, 0}; // material colour modulation
50bool g_tint_world_on = true; 50bool g_tint_world_on = true;
51bool g_tint_sky_on = true; 51bool g_tint_sky_on = true;
52bool g_tint_prop_on = true;
52int g_tint_world_rgb[3] = {255, 255, 255}; 53int g_tint_world_rgb[3] = {255, 255, 255};
53int g_tint_sky_rgb[3] = {255, 255, 255}; 54int g_tint_sky_rgb[3] = {255, 255, 255};
55int g_tint_prop_rgb[3] = {255, 255, 255};
56FeatureState g_fog{false, 0}; // fog_override colour/distance
57bool g_fog_skybox = true;
58bool g_fog_disable = false;
59int g_fog_rgb[3] = {160, 170, 190};
60int g_fog_start = 200;
61int g_fog_end = 3000;
54bool g_item_health = true; 62bool g_item_health = true;
55bool g_item_ammo = true; 63bool g_item_ammo = true;
56bool g_item_armor = true; 64bool 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};
52Tint g_applied{false, 255, 255, 255, 255, 255, 255}; 53Tint 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.
58bool is_prop_material(const char *name) {
59 return name && strncmp(name, "models/props", 12) == 0;
60}
53 61
54struct MatCalls { 62struct 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.
147bool apply_tint(int world_r, int world_g, int world_b, int sky_r, int sky_g, 155bool 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
273namespace {
274
275struct FogState {
276 bool valid;
277 bool on;
278 bool disable;
279 int r, g, b;
280 int start, end;
281 bool skybox;
282};
283FogState g_fog_applied{false, false, false, 0, 0, 0, 0, 0, false};
284
285} // namespace
286
287void fog_invalidate() { g_fog_applied.valid = false; }
288
289void 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}