Add fixed core HUD and shared service window for T5 and T6

This commit is contained in:
2026-10-14 15:55:22 -04:00
parent d15bc50b7d
commit 929bf0354b
18 changed files with 1973 additions and 804 deletions
+72 -112
View File
@@ -127,120 +127,17 @@ lb_request(manual)
level endon("game_ended");
level endon("intermission");
snapshot = lb_cached_record();
if (!isDefined(snapshot))
{
if (manual)
self iPrintLnBold("^3Map record unavailable; try .leaders again shortly");
return;
}
// A manual replay replaces any welcome announcement still waiting to run.
// This also applies when the cache is unavailable.
if (manual)
self notify("tws_lb_requested");
self lb_show_hud(snapshot);
}
snapshot = lb_cached_record();
if (!isDefined(snapshot) && !manual)
return;
lb_show_hud(snapshot)
{
// Stop the previous display and its cleanup threads before replacing it.
self notify("tws_lb_hud_done");
if (isDefined(self.tws_lb_elems))
lb_hud_destroy(self.tws_lb_elems);
self endon("disconnect");
self endon("tws_lb_hud_done");
scripts\zm\tws_t6_common::ensure_config();
// Match .help's position, typography and row spacing below the health bar.
y = level.hud_row_bar + level.hud_bar_height + 20;
elems = [];
title = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_x, y, "left", level.hud_font_mid, level.hud_c_afk);
title.alpha = 0;
if (snapshot.round == 0)
title SetText("No recorded high round yet");
else
title SetText("Record: Round " + snapshot.round);
elems[elems.size] = title;
for (i = 0; i < 3; i++)
{
row = scripts\zm\tws_t6_common::make_text_elem(
self, level.hud_x, y + 22 + i * 18,
"left", level.hud_font_small, level.hud_c_count);
row.alpha = 0;
elems[elems.size] = row;
}
// Cleanup runs independently of the display and ends when it is replaced.
self.tws_lb_elems = elems;
self thread lb_hud_disconnect(elems);
self thread lb_hud_end_game(elems, "end_game");
self thread lb_hud_end_game(elems, "game_ended");
self thread lb_hud_end_game(elems, "intermission");
offset = 0;
for (;;)
{
// Text changes only once per page, never during the visibility loop.
for (i = 0; i < 3; i++)
{
row = elems[i + 1];
if (offset + i < snapshot.lines.size)
row SetText("^3Held by: ^7" + snapshot.lines[offset + i]);
else
row SetText("");
}
remaining = 12;
while (remaining > 0)
{
visible = !self lb_hud_help_visible();
for (i = 0; i < elems.size; i++)
elems[i].alpha = visible;
wait 0.1;
// Pause the page while .help occupies the same area.
if (visible)
remaining -= 0.1;
}
offset += 3;
if (offset >= snapshot.lines.size)
break;
}
lb_hud_destroy(elems);
self.tws_lb_elems = undefined;
self notify("tws_lb_hud_done");
}
lb_hud_help_visible()
{
return isDefined(self.tws_help_elems) && self.tws_help_elems.size > 0 &&
isDefined(self.tws_help_elems[0]) && self.tws_help_elems[0].alpha > 0;
}
lb_hud_disconnect(elems)
{
self endon("tws_lb_hud_done");
self waittill("disconnect");
lb_hud_destroy(elems);
}
lb_hud_end_game(elems, event)
{
self endon("disconnect");
self endon("tws_lb_hud_done");
level waittill(event);
lb_hud_destroy(elems);
self.tws_lb_elems = undefined;
self notify("tws_lb_hud_done");
}
lb_hud_destroy(elems)
{
for (i = 0; i < elems.size; i++)
{
if (isDefined(elems[i]))
elems[i] Destroy();
}
// Common owns pagination, help priority and the single service window.
// Undefined snapshots produce a private unavailable page for manual replay.
self scripts\zm\tws_t6_common::show_record(snapshot);
}
lb_refresh()
@@ -529,22 +426,85 @@ lb_holder_lines(names)
{
lines = [];
line = "";
columns = 0;
for (i = 0; i < names.size; i++)
{
if (line != "" && line.size + names[i].size + 2 > 100)
name = lb_holder_name(names[i]);
width = lb_holder_width(name);
if (line != "" && columns + width + 2 > 16)
{
lines[lines.size] = line;
line = "";
columns = 0;
}
if (line != "")
{
line += ", ";
line += names[i];
columns += 2;
}
line += name;
columns += width;
}
if (line != "")
lines[lines.size] = line;
return lines;
}
lb_holder_name(value)
{
// The service has 200 virtual units at font scale 1.1. Use a conservative
// 16-column bound, counting non-ASCII glyphs twice, pending in-game metrics.
// A long name gets one bounded entry; no tied holder is dropped.
value = lb_display_text(value, 64);
if (value == "")
value = "Unknown player";
if (lb_holder_width(value) <= 16)
return value;
output = "";
columns = 0;
for (i = 0; i < value.size; i++)
{
code = level.tws_lb_bytes[getSubStr(value, i, i + 1)];
width = 1;
if (code >= 128)
width = 2;
if (columns + width > 13)
break;
count = lb_glyph_bytes(code);
output += getSubStr(value, i, i + count);
columns += width;
i += count - 1;
}
return output + "...";
}
lb_holder_width(value)
{
columns = 0;
for (i = 0; i < value.size; i++)
{
code = level.tws_lb_bytes[getSubStr(value, i, i + 1)];
if (code >= 128)
columns += 2;
else
columns++;
i += lb_glyph_bytes(code) - 1;
}
return columns;
}
lb_glyph_bytes(code)
{
if (code >= 240)
return 4;
if (code >= 224)
return 3;
if (code >= 192)
return 2;
return 1;
}
lb_display_text(value, limit)
{
output = "";