summaryrefslogtreecommitdiffstats
path: root/src/world.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/world.cpp')
-rw-r--r--src/world.cpp152
1 files changed, 152 insertions, 0 deletions
diff --git a/src/world.cpp b/src/world.cpp
index 2333829..73738db 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -340,3 +340,155 @@ void fog_update() {
340 logf("fog: %s colour=(%d,%d,%d) start=%d end=%d skybox=%d", disable ? "off" 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); 341 : "on", r, g, b, start, end, (int)skybox);
342} 342}
343
344// ---------------------------------------------------------------------------
345// Skybox changer (sv_skyname; same mechanism as the skybox_swapper entity)
346// ---------------------------------------------------------------------------
347
348namespace {
349
350// HL2's skybox set. Index 0 means "do not force a sky"; the rest are the
351// base names the engine expands to skybox/<name>{rt,bk,lf,ft,up,dn}.
352const char *const kSkyboxLabels[] = {
353 "Map default", "sky_day01_01", "sky_day01_04", "sky_day01_05",
354 "sky_day01_06", "sky_day01_07", "sky_day01_08", "sky_day01_09",
355 "sky_day02_01", "sky_day02_02", "sky_day02_03", "sky_day02_04",
356 "sky_day02_05", "sky_day02_06", "sky_day02_07", "sky_day02_09",
357 "sky_day02_10", "sky_day03_01", "sky_day03_02", "sky_day03_03",
358 "sky_day03_04", "sky_day03_05", "sky_day03_06", "sky_day03_06b",
359 "sky_day03_06c", "sky_wasteland02", "sky_borealis01", "sky_fog_002",
360 "sky_fake_white"};
361
362std::string g_sky_original; // map's own sky, captured before overriding
363bool g_sky_original_valid = false;
364std::string g_sky_active; // value we forced, empty when not forcing
365
366// "skybox/sky_day01_01_hdrrt" -> "sky_day01_01"
367bool parse_sky_material(const char *mat, char *out, size_t outn) {
368 if (!mat)
369 return false;
370 const char *n = mat;
371 if (strncmp(n, "skybox/", 7) == 0)
372 n += 7;
373 size_t len = strlen(n);
374 if (len < 4)
375 return false;
376 static const char *faces[] = {"rt", "bk", "lf", "ft", "up", "dn", nullptr};
377 const char *suf = n + len - 2;
378 bool face = false;
379 for (int i = 0; faces[i]; i++) {
380 if (strcmp(suf, faces[i]) == 0) {
381 face = true;
382 break;
383 }
384 }
385 if (!face)
386 return false;
387 size_t blen = len - 2;
388 if (blen > 4 && (strncmp(n + blen - 4, "_hdr", 4) == 0 ||
389 strncmp(n + blen - 4, "_ldr", 4) == 0))
390 blen -= 4;
391 if (blen == 0 || blen >= outn)
392 return false;
393 memcpy(out, n, blen);
394 out[blen] = 0;
395 return true;
396}
397
398void capture_map_sky() {
399 if (g_sky_original_valid)
400 return;
401 g_sky_original_valid = true; // one attempt per map, even on failure
402 void *ms = material_system();
403 if (!ms)
404 return;
405 auto invalid = vfunc<MatCalls::InvalidFn>(ms, MATSYS_InvalidMaterial);
406 auto first = vfunc<MatCalls::FirstFn>(ms, MATSYS_FirstMaterial);
407 auto next = vfunc<MatCalls::NextFn>(ms, MATSYS_NextMaterial);
408 auto get = vfunc<MatCalls::GetFn>(ms, MATSYS_GetMaterial);
409 auto count = vfunc<MatCalls::CountFn>(ms, MATSYS_GetNumMaterials);
410 int total = count(ms);
411 if (total <= 0 || total > 200000)
412 return;
413 unsigned short inv = invalid(ms);
414 int seen = 0;
415 std::string last;
416 for (unsigned short h = first(ms); h != inv; h = next(ms, h)) {
417 if (++seen > total + 16)
418 break;
419 void *mat = get(ms, h);
420 if (!mat)
421 continue;
422 const char *name = vfunc<MatCalls::NameFn>(mat, MAT_GetName)(mat);
423 const char *group =
424 vfunc<MatCalls::GroupFn>(mat, MAT_GetTextureGroupName)(mat);
425 if (!plausible_name(name) || !plausible_name(group))
426 continue;
427 if (strncmp(group, GROUP_SKYBOX, strlen(GROUP_SKYBOX)) != 0)
428 continue;
429 char base[128];
430 if (parse_sky_material(name, base, sizeof(base)))
431 last = base; // newest loaded sky materials win after a level change
432 }
433 if (last.empty()) {
434 logd("skybox: could not capture the map sky");
435 return;
436 }
437 g_sky_original = last;
438 logf("skybox: map sky is %s", g_sky_original.c_str());
439}
440
441} // namespace
442
443int skybox_option_count() {
444 return (int)(sizeof(kSkyboxLabels) / sizeof(kSkyboxLabels[0]));
445}
446
447const char *skybox_option_name(int i) {
448 int n = skybox_option_count();
449 return (i >= 0 && i < n) ? kSkyboxLabels[i] : kSkyboxLabels[0];
450}
451
452const char *const *skybox_options() { return kSkyboxLabels; }
453
454void skybox_invalidate() {
455 // Level change: the engine sets sv_skyname from worldspawn again, so there is
456 // nothing to restore. Forget the captured base and let it recapture.
457 g_sky_original.clear();
458 g_sky_original_valid = false;
459 g_sky_active.clear();
460}
461
462void skybox_update() {
463 bool on = feature_active(FEAT_SKYBOX);
464 int choice = g_skybox_choice;
465 int n = skybox_option_count();
466 if (choice < 0 || choice >= n)
467 choice = 0;
468
469 if (!on || choice == 0) {
470 if (g_sky_active.empty())
471 return;
472 if (g_sky_original_valid && !g_sky_original.empty() &&
473 g_sky_original != g_sky_active) {
474 char cmd[160];
475 snprintf(cmd, sizeof(cmd), "sv_skyname %s", g_sky_original.c_str());
476 console_cmd(cmd);
477 logf("skybox: restored %s", g_sky_original.c_str());
478 world_tint_invalidate(); // the new sky materials need the sky tint
479 }
480 g_sky_active.clear();
481 return;
482 }
483
484 capture_map_sky(); // before the first override of this map
485 const char *want = skybox_option_name(choice);
486 if (g_sky_active == want)
487 return;
488 char cmd[160];
489 snprintf(cmd, sizeof(cmd), "sv_skyname %s", want);
490 console_cmd(cmd);
491 logf("skybox: %s", want);
492 g_sky_active = want;
493 world_tint_invalidate(); // tint materials for the freshly loaded sky
494}