Files
tws-zombies-scripts/t6/scripts/tws_t6_common.gsc
T
josh 1121b44857 Organize scripts and add TWS plugins with HTTP timeouts
Vendor pinned T5 and T6 plugin sources under the new plugins layout. Add native HTTP deadlines, status and timing diagnostics, telemetry integration, and build/test tooling.
2026-10-09 11:02:20 -04:00

294 lines
7.2 KiB
Plaintext

/* tws_t6_common.gsc — shared config, HUD helpers, connect/spawn/help registries. */
#include maps\mp\_utility;
#include common_scripts\utility;
main()
{
precacheShader("white");
}
init()
{
common_config();
register_help_command(".help", "List available commands");
level thread help_chat_listener();
level thread on_player_connect_dispatch();
}
common_config()
{
if (isDefined(level.hud_x))
return;
// Small inset from the physical screen corner (fullscreen HUD coordinates).
level.hud_x = 10;
level.hud_y = 10;
level.hud_row_match = level.hud_y;
level.hud_row_round = level.hud_y + 15;
level.hud_row_name = level.hud_y + 32;
level.hud_row_count = level.hud_y + 54;
level.hud_row_bar = level.hud_y + 84;
level.hud_font_small = 1.4;
level.hud_font_large = 2.0;
level.hud_font_mid = 1.6;
level.hud_bar_width = 120;
level.hud_bar_height = 8;
level.hud_col_count = level.hud_x + 90;
level.hud_col_name = level.hud_x + 60;
level.hud_col_health = level.hud_x + level.hud_bar_width + 12;
level.hud_rate = 0.1;
// green/amber/red ramp is health-only
level.hud_c_label = (0.85, 0.85, 0.85);
level.hud_c_dim = (0.55, 0.55, 0.55);
level.hud_c_count = (1, 1, 1);
level.hud_c_name = (0.4, 0.9, 0.5);
level.hud_c_high = (0.2, 1, 0.4);
level.hud_c_mid = (1, 0.8, 0);
level.hud_c_low = (1, 0.2, 0.2);
level.hud_c_afk = (1, 0.8, 0.2);
// numbers fade in below this fraction of max hp
level.hud_health_threshold = 1.0;
if (!isDefined(level.tws_spawn_handlers))
level.tws_spawn_handlers = [];
}
ensure_config()
{
if (!isDefined(level.hud_x))
common_config();
}
register_spawn_handler(fn)
{
if (!isDefined(level.tws_spawn_handlers))
level.tws_spawn_handlers = [];
level.tws_spawn_handlers[level.tws_spawn_handlers.size] = fn;
}
// Connection handlers also serve newcomers still waiting in spectator mode.
register_connect_handler(fn)
{
if (!isDefined(level.tws_connect_handlers))
level.tws_connect_handlers = [];
level.tws_connect_handlers[level.tws_connect_handlers.size] = fn;
}
// Modules register their own commands, regardless of init order.
register_help_command(command, description)
{
if (!isDefined(level.tws_help_commands))
level.tws_help_commands = [];
entry = spawnStruct();
entry.command = command;
entry.description = description;
level.tws_help_commands[level.tws_help_commands.size] = entry;
}
help_chat_listener()
{
for (;;)
{
level waittill("say", message, player);
if (!isDefined(player) || !isDefined(message))
continue;
command = toLower(message);
if (command == ".help" || command == ".commands")
player show_help();
}
}
show_help()
{
// Cache the text per connection; repeated SetText calls consume config strings.
if (!isDefined(self.tws_help_elems))
{
elems = [];
// Use the same anchors as the HUD, with room below the health numbers.
y = level.hud_row_bar + level.hud_bar_height + 20;
title = make_text_elem(self, level.hud_x, y,
"left", level.hud_font_mid, level.hud_c_afk);
title SetText("Commands");
elems[elems.size] = title;
for (i = 0; i < level.tws_help_commands.size; i++)
{
entry = level.tws_help_commands[i];
elem = make_text_elem(self, level.hud_x, y + 22 + i * 18,
"left", level.hud_font_small, level.hud_c_count);
elem SetText("^3" + entry.command + "^7 - " + entry.description);
elems[elems.size] = elem;
}
self.tws_help_elems = elems;
self thread destroy_on_disconnect(elems);
}
for (i = 0; i < self.tws_help_elems.size; i++)
{
elem = self.tws_help_elems[i];
elem.alpha = 1;
}
// Only the expiry timer is replaced; the say listener and cleanup stay alive.
self notify("tws_help_refresh");
self thread hide_help_after_delay();
}
hide_help_after_delay()
{
self endon("disconnect");
self endon("tws_help_refresh");
wait 12;
for (i = 0; i < self.tws_help_elems.size; i++)
{
elem = self.tws_help_elems[i];
elem.alpha = 0;
}
}
// Shared dispatcher: one HUD instance and account load per connection.
on_player_connect_dispatch()
{
for (;;)
{
level waittill("connected", player);
player thread on_player_spawned_dispatch();
player thread on_player_connected_dispatch();
}
}
on_player_connected_dispatch()
{
self endon("disconnect");
// Leave the connection handshake before invoking optional modules.
wait 0.05;
if (!isDefined(level.tws_connect_handlers))
return;
for (i = 0; i < level.tws_connect_handlers.size; i++)
self thread [[ level.tws_connect_handlers[i] ]]();
}
// handlers own their own self endon("disconnect")
on_player_spawned_dispatch()
{
self endon("disconnect");
self waittill("spawned_player");
if (!isDefined(level.tws_spawn_handlers))
return;
for (i = 0; i < level.tws_spawn_handlers.size; i++)
self thread [[ level.tws_spawn_handlers[i] ]]();
}
make_text_elem(player, x, y, align_x, scale, colour)
{
elem = NewClientHudElem(player);
// Fullscreen bypasses the safe-area inset of left/top screen anchors.
elem.horzAlign = "fullscreen";
elem.vertAlign = "fullscreen";
elem.alignX = align_x;
elem.alignY = "top";
elem.x = x;
elem.y = y;
elem.foreground = 1;
elem.fontScale = scale;
elem.alpha = 1;
elem.color = colour;
// black glow reads as an outline against bright geometry
elem.glowColor = (0, 0, 0);
elem.glowAlpha = 0.6;
return elem;
}
make_shader_elem(player, x, y)
{
elem = NewClientHudElem(player);
elem.horzAlign = "fullscreen";
elem.vertAlign = "fullscreen";
elem.alignX = "left";
elem.alignY = "top";
elem.x = x;
elem.y = y;
elem.foreground = 1;
elem.alpha = 1;
return elem;
}
destroy_on_disconnect(elems)
{
self waittill("disconnect");
for (i = 0; i < elems.size; i++)
{
if (isDefined(elems[i]))
elems[i] Destroy();
}
}
int_dvar_or(name, fallback)
{
raw = GetDvar(name);
if (raw == "")
return fallback;
return int(raw);
}
format_mmss(elapsed_ms)
{
total_secs = int(elapsed_ms / 1000);
if (total_secs < 0)
total_secs = 0;
mins = int(total_secs / 60);
secs = total_secs - (mins * 60);
sec_str = "" + secs;
if (secs < 10)
sec_str = "0" + secs;
return "" + mins + ":" + sec_str;
}
// T6 state guard used by the AFK control fix.
player_active(player)
{
if (is_true(level.intermission) || is_true(level.gameended))
return false;
if (!isDefined(player) || !isAlive(player))
return false;
if (!isDefined(player.sessionstate) || player.sessionstate != "playing")
return false;
if (player maps\mp\zombies\_zm_laststand::player_is_in_laststand())
return false;
if (isDefined(player.is_zombie) && player.is_zombie)
return false;
return true;
}