Files
tws-zombies-scripts/t6/scripts/tws_t6_hud.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

346 lines
8.9 KiB
Plaintext

/* tws_t6_hud.gsc - configurable HUD stack: timers, zombie count, health bar. */
#include maps\mp\_utility;
#include common_scripts\utility;
// Plutonium probes both entry points; runtime setup belongs in init.
main()
{
}
init()
{
scripts\zm\tws_t6_common::ensure_config();
level thread round_watcher();
level thread game_over_watcher();
scripts\zm\tws_t6_common::register_spawn_handler(::hud_start);
}
hud_start()
{
self endon("disconnect");
if (isDefined(level.tws_ended) && level.tws_ended)
return;
self thread hud_timer_row();
self thread hud_zombie_counter();
self thread hud_health_display();
}
// Same game-over notification fan-out as T5.
game_over_watcher()
{
level thread game_over_via("end_game");
level thread game_over_via("game_ended");
level thread game_over_via("intermission");
}
game_over_via(notify_name)
{
level endon("hud_game_over");
level waittill(notify_name);
level.tws_ended = true;
level notify("hud_game_over");
}
// Poll stock round state; the first player spawn seeds pre-set round 1.
round_watcher()
{
level endon("hud_game_over");
last = 0;
if (isDefined(level.round_number))
last = level.round_number;
for (;;)
{
if (isDefined(level.round_number) && level.round_number != last)
{
last = level.round_number;
level.round_start_time = getTime();
if (!isDefined(level.match_start_time) && level.round_number >= 1)
{
level.match_start_time = getTime();
level notify("hud_match_started");
}
level notify("hud_round_changed");
}
wait (0.5);
}
}
hud_timer_row()
{
self endon("disconnect");
// fallback seed for the round-1-already-active case (round_watcher misses it)
if (!isDefined(level.match_start_time) &&
isDefined(level.round_number) &&
level.round_number >= 1)
{
level.match_start_time = getTime();
level.round_start_time = getTime();
level notify("hud_match_started");
}
match = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_x, level.hud_row_match,
"left", level.hud_font_small, level.hud_c_dim);
match.alpha = 0; // revealed by match_timer_bind when round 1 opens
round = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_x, level.hud_row_round,
"left", level.hud_font_small, level.hud_c_dim);
round SetTimerUp(0);
if (isDefined(level.round_start_time))
round SetTimerUp(max(0, (getTime() - level.round_start_time) / 1000.0));
// name only shows in co-op — your own name is noise in solo
name = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_x, level.hud_row_name,
"left", level.hud_font_small, level.hud_c_name);
if (isDefined(self.name))
name SetText(self.name);
else
name SetText(self.playername);
name.alpha = 0;
elems = [];
elems[elems.size] = match;
elems[elems.size] = round;
elems[elems.size] = name;
self thread scripts\zm\tws_t6_common::destroy_on_disconnect(elems);
self thread reset_timer_each_round(round);
self thread match_timer_bind(match);
self thread freeze_timers_on_game_over(match, round);
coop = -1;
for (;;)
{
is_coop = 0;
players = getPlayers();
if (players.size > 1)
is_coop = 1;
if (is_coop != coop)
{
name.alpha = is_coop;
coop = is_coop;
}
wait (1);
}
}
reset_timer_each_round(timer)
{
level endon("hud_game_over");
self endon("disconnect");
for (;;)
{
level waittill("hud_round_changed");
if (isDefined(timer))
timer SetTimerUp(0);
}
}
// T6 timer seed includes elapsed time for late joiners.
match_timer_bind(elem)
{
self endon("disconnect");
if (!isDefined(level.match_start_time))
level waittill("hud_match_started");
if (isDefined(elem))
{
elem SetTimerUp(max(0, (getTime() - level.match_start_time) / 1000.0));
elem.alpha = 1;
}
}
/*
Level-scope trigger only — per-player death/health polling is intercepted
by the zombiemode scripts. Freeze value math relies on the timers being
anchored at first spawn / first round transition, not level init.
*/
freeze_timers_on_game_over(match_elem, round_elem)
{
self endon("disconnect");
level waittill("hud_game_over");
if (isDefined(match_elem) && isDefined(level.match_start_time))
match_elem SetText(scripts\zm\tws_t6_common::format_mmss(getTime() - level.match_start_time));
if (isDefined(round_elem) && isDefined(level.round_start_time))
round_elem SetText(scripts\zm\tws_t6_common::format_mmss(getTime() - level.round_start_time));
}
hud_zombie_counter()
{
self endon("disconnect");
label = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_x, level.hud_row_count,
"left", level.hud_font_large, level.hud_c_label);
label SetText("Zombies");
count = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_col_count, level.hud_row_count,
"left", level.hud_font_large, level.hud_c_count);
count SetValue(0);
elems = [];
elems[elems.size] = label;
elems[elems.size] = count;
self thread scripts\zm\tws_t6_common::destroy_on_disconnect(elems);
last = -1;
for (;;)
{
remaining = 0;
if (isDefined(level.zombie_team))
remaining = maps\mp\zombies\_zm_utility::get_current_zombie_count();
if (isDefined(level.zombie_total))
remaining += max(0, level.zombie_total);
if (remaining != last)
{
count SetValue(remaining);
last = remaining;
}
wait (level.hud_rate);
}
}
hud_health_display()
{
self endon("disconnect");
bar_width = level.hud_bar_width;
// -2 on both axes so the back frames the bar with 2px padding
back = scripts\zm\tws_t6_common::make_shader_elem(self, level.hud_x - 2, level.hud_row_bar - 2);
back.alpha = 0.5;
back.color = (0, 0, 0);
back SetShader("white", bar_width + 4, level.hud_bar_height + 4);
bar = scripts\zm\tws_t6_common::make_shader_elem(self, level.hud_x, level.hud_row_bar);
bar.color = level.hud_c_high;
bar SetShader("white", bar_width, level.hud_bar_height);
/*
"87/160" is three elements, not a rebuilt string. SetText allocates
a config string per call; per-tick rebuilds crash the server.
cur is right-aligned so digits grow leftward and the slash + max
never shift.
*/
cur = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_col_health + 26, level.hud_row_bar + 3,
"right", level.hud_font_mid, level.hud_c_high);
cur SetValue(self.health);
slash = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_col_health + 30, level.hud_row_bar + 3,
"center", level.hud_font_mid, level.hud_c_high);
slash SetText("/");
max_hp = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_col_health + 34, level.hud_row_bar + 3,
"left", level.hud_font_mid, level.hud_c_high);
max_hp SetValue(100);
cur.alpha = 0;
slash.alpha = 0;
max_hp.alpha = 0;
elems = [];
elems[elems.size] = back;
elems[elems.size] = bar;
elems[elems.size] = cur;
elems[elems.size] = slash;
elems[elems.size] = max_hp;
self thread scripts\zm\tws_t6_common::destroy_on_disconnect(elems);
last_width = bar_width;
last_health = -1;
last_max = -1;
last_shown = -1;
for (;;)
{
maximum = 100;
if (isDefined(self.maxhealth) && self.maxhealth > 0)
maximum = self.maxhealth;
frac = self.health / (maximum * 1.0);
if (frac > 1)
frac = 1;
if (frac < 0)
frac = 0;
width = int(max(frac * bar_width, 1));
if (width != last_width)
{
bar SetShader("white", width, level.hud_bar_height);
last_width = width;
}
colour = level.hud_c_high;
if (frac <= 0.25)
colour = level.hud_c_low;
else if (frac <= 0.5)
colour = level.hud_c_mid;
bar.color = colour;
cur.color = colour;
slash.color = colour;
max_hp.color = colour;
// numbers only show once you're hurt
show = 0;
if (frac < level.hud_health_threshold)
show = 1;
if (show != last_shown)
{
cur.alpha = show;
slash.alpha = show;
max_hp.alpha = show;
last_shown = show;
}
if (self.health != last_health)
{
cur SetValue(self.health);
last_health = self.health;
}
if (maximum != last_max)
{
max_hp SetValue(maximum);
last_max = maximum;
}
wait (level.hud_rate);
}
}