Files
tws-zombies-scripts/t5/scripts/tws_t5_hud.gsc
T

313 lines
8.8 KiB
Plaintext

/* tws_t5_hud.gsc - fixed upper-right core HUD, visible from connection. */
#include maps\_utility;
#include common_scripts\utility;
init()
{
if (GetDvar(#"zombiemode") != "1")
return;
scripts\sp\tws_t5_common::ensure_config();
level thread round_watcher();
level thread game_over_watcher();
scripts\sp\tws_t5_common::register_connect_handler(::hud_connect);
scripts\sp\tws_t5_common::register_spawn_handler(::hud_start);
}
// Connection construction never seeds clocks or reads player gameplay state.
// The first-spawn fallback also handles a spawn before the connect callback.
hud_connect()
{
self endon("disconnect");
if (isDefined(self.tws_hud_elems))
return;
self.tws_hud_elems = [];
background = scripts\sp\tws_t5_common::make_panel_shader(
self, 0, 0, level.hud_panel_width, level.hud_core_height,
level.hud_c_panel, level.hud_panel_alpha, 0);
self.tws_hud_elems[self.tws_hud_elems.size] = background;
self hud_text(level.hud_pad, level.hud_row_header, "left",
level.hud_font_small, level.hud_c_name, "TWS ZOMBIES");
self hud_text(level.hud_panel_width - level.hud_pad, level.hud_row_header,
"right", level.hud_font_small, level.hud_c_dim, "discord.tws.lol");
self hud_text(level.hud_pad, level.hud_row_timers, "left",
level.hud_font_small, level.hud_c_label, "MATCH");
self.tws_hud_match = self hud_text(104, level.hud_row_timers - 2, "right",
level.hud_font_mid, level.hud_c_count, "--:--");
self hud_text(114, level.hud_row_timers, "left",
level.hud_font_small, level.hud_c_label, "ROUND");
self.tws_hud_round = self hud_text(level.hud_panel_width - level.hud_pad,
level.hud_row_timers - 2, "right",
level.hud_font_mid, level.hud_c_count, "--:--");
self hud_text(level.hud_pad, level.hud_row_stats, "left",
level.hud_font_small, level.hud_c_label, "ZOMBIES");
self.tws_hud_count = self hud_text(67, level.hud_row_stats, "left",
level.hud_font_mid, level.hud_c_count, "--");
self hud_text(132, level.hud_row_stats, "left",
level.hud_font_small, level.hud_c_label, "HP");
// Independent numeric elements avoid a new config string on every tick.
self.tws_hud_health = self hud_text(174, level.hud_row_stats, "right",
level.hud_font_mid, level.hud_c_count, "--");
self.tws_hud_slash = self hud_text(181, level.hud_row_stats, "center",
level.hud_font_mid, level.hud_c_count, "/");
self.tws_hud_maxhealth = self hud_text(level.hud_panel_width - level.hud_pad,
level.hud_row_stats, "right", level.hud_font_mid, level.hud_c_count, "--");
track = scripts\sp\tws_t5_common::make_panel_shader(
self, level.hud_pad, level.hud_row_bar,
level.hud_bar_width, level.hud_bar_height, (0.18, 0.22, 0.19), 0.8, 10);
self.tws_hud_elems[self.tws_hud_elems.size] = track;
self.tws_hud_bar = scripts\sp\tws_t5_common::make_panel_shader(
self, level.hud_pad, level.hud_row_bar,
level.hud_bar_width, level.hud_bar_height, level.hud_c_high, 0, 11);
self.tws_hud_elems[self.tws_hud_elems.size] = self.tws_hud_bar;
self thread scripts\sp\tws_t5_common::destroy_on_disconnect(self.tws_hud_elems);
}
hud_text(x, y, align, scale, colour, text)
{
elem = scripts\sp\tws_t5_common::make_panel_text(self, x, y, align, scale, colour);
elem SetText(text);
self.tws_hud_elems[self.tws_hud_elems.size] = elem;
return elem;
}
hud_start()
{
self endon("disconnect");
self hud_connect();
if (isDefined(self.tws_hud_bound) || is_true(level.tws_ended))
return;
self.tws_hud_bound = true;
self thread hud_timer_row();
self thread hud_zombie_counter();
self thread hud_health_display();
}
// Race the map-dependent game-over notifications once at level scope.
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");
}
// Pre-set round 1 is seeded on first spawn; only observed transitions seed here.
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");
// A visible panel for a waiting newcomer must not start a pre-set round 1.
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");
}
round = self.tws_hud_round;
round SetTimerUp(0);
self thread reset_timer_each_round(round);
self thread match_timer_bind(self.tws_hud_match);
self thread freeze_timers_on_game_over(self.tws_hud_match, round);
}
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);
}
}
// T5 SP only ticks with a zero seed; late joiners retain that engine limitation.
match_timer_bind(elem)
{
self endon("disconnect");
level endon("hud_game_over");
if (!isDefined(level.match_start_time))
level waittill("hud_match_started");
if (isDefined(elem))
elem SetTimerUp(0);
}
// The panel remains visible at game over, with authoritative frozen clocks.
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\sp\tws_t5_common::format_mmss(getTime() - level.match_start_time));
if (isDefined(round_elem) && isDefined(level.round_start_time))
round_elem SetText(scripts\sp\tws_t5_common::format_mmss(getTime() - level.round_start_time));
}
hud_zombie_counter()
{
self endon("disconnect");
level endon("hud_game_over");
count = self.tws_hud_count;
last = -1;
for (;;)
{
remaining = get_enemy_count();
if (isDefined(level.zombie_total))
remaining += max(0, level.zombie_total);
if (remaining != last)
{
count SetValue(remaining);
last = remaining;
}
wait (level.hud_rate);
}
}
get_enemy_count()
{
valid = [];
enemies = GetAiSpeciesArray("axis", "all");
for (i = 0; i < enemies.size; i++)
{
if (is_true(enemies[i].ignore_enemy_count))
continue;
if (isDefined(enemies[i].animname))
valid[valid.size] = enemies[i];
}
return valid.size;
}
hud_health_display()
{
self endon("disconnect");
level endon("hud_game_over");
bar_width = level.hud_bar_width;
bar = self.tws_hud_bar;
cur = self.tws_hud_health;
slash = self.tws_hud_slash;
max_hp = self.tws_hud_maxhealth;
last_width = -1;
last_health = -1;
last_max = -1;
for (;;)
{
// Display fallbacks must not alter gameplay health values.
maximum = 100;
if (isDefined(self.maxhealth) && self.maxhealth > 0)
maximum = self.maxhealth;
current = 0;
if (isDefined(self.health))
current = max(0, self.health);
frac = current / (maximum * 1.0);
if (frac > 1)
frac = 1;
width = int(max(frac * bar_width, 1));
if (width != last_width)
{
bar SetShader("white", width, level.hud_bar_height);
last_width = width;
}
bar.alpha = 1;
if (frac == 0)
bar.alpha = 0;
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;
// Health numbers remain visible at full health.
if (current != last_health)
{
cur SetValue(current);
last_health = current;
}
if (maximum != last_max)
{
max_hp SetValue(maximum);
last_max = maximum;
}
wait (level.hud_rate);
}
}