Add fixed core HUD and shared service window for T5 and T6
This commit is contained in:
+409
-68
@@ -3,6 +3,11 @@
|
||||
#include maps\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
main()
|
||||
{
|
||||
precacheShader("white");
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
if (GetDvar(#"zombiemode") != "1")
|
||||
@@ -11,6 +16,9 @@ init()
|
||||
common_config();
|
||||
|
||||
register_help_command(".help", "List available commands");
|
||||
level thread service_game_over_via("end_game");
|
||||
level thread service_game_over_via("game_ended");
|
||||
level thread service_game_over_via("intermission");
|
||||
level thread help_chat_listener();
|
||||
level thread on_player_connect_dispatch();
|
||||
}
|
||||
@@ -20,41 +28,50 @@ common_config()
|
||||
if (isDefined(level.hud_x))
|
||||
return;
|
||||
|
||||
// Anchor the gameplay stack below the persistent Discord line.
|
||||
// Existing generic helpers retain their screen-anchor conventions. Panels
|
||||
// use their own right-edge origin and never push stock HUD areas around.
|
||||
level.hud_x = 10;
|
||||
level.hud_y = 30;
|
||||
|
||||
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
|
||||
// Retain the legacy fields for consumers of the original common config.
|
||||
// The panel renderers below use dedicated row/column offsets instead.
|
||||
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_col_count = level.hud_x + 90;
|
||||
level.hud_col_name = level.hud_x + 60;
|
||||
level.hud_col_health = level.hud_x + 120 + 12;
|
||||
level.hud_health_threshold = 1.0;
|
||||
level.hud_panel_right = 10;
|
||||
level.hud_panel_top = 42;
|
||||
level.hud_panel_width = 220;
|
||||
level.hud_core_height = 100;
|
||||
level.hud_service_y = 108;
|
||||
level.hud_service_height = 124;
|
||||
level.hud_pad = 10;
|
||||
level.hud_row_header = 10;
|
||||
level.hud_row_timers = 34;
|
||||
level.hud_row_stats = 59;
|
||||
level.hud_row_bar = 80;
|
||||
level.hud_row_afk = 86;
|
||||
|
||||
level.hud_font_small = 1.1;
|
||||
level.hud_font_large = 1.6;
|
||||
level.hud_font_mid = 1.25;
|
||||
level.hud_font_afk = 1.0;
|
||||
level.hud_bar_width = 200;
|
||||
level.hud_bar_height = 4;
|
||||
level.hud_rate = 0.1;
|
||||
level.hud_c_panel = (0.07, 0.10, 0.08);
|
||||
level.hud_panel_alpha = 0.72;
|
||||
level.hud_c_label = (0.72, 0.77, 0.74);
|
||||
level.hud_c_dim = (0.65, 0.70, 0.67);
|
||||
level.hud_c_count = (0.93, 0.96, 0.94);
|
||||
level.hud_c_name = (0.65, 0.85, 0.70);
|
||||
level.hud_c_high = (0.65, 0.85, 0.70);
|
||||
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);
|
||||
|
||||
if (!isDefined(level.tws_spawn_handlers))
|
||||
level.tws_spawn_handlers = [];
|
||||
@@ -111,58 +128,356 @@ help_chat_listener()
|
||||
}
|
||||
}
|
||||
|
||||
// A single per-player controller owns service priority, pages and expiry.
|
||||
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;
|
||||
if (isDefined(level.tws_service_ended))
|
||||
return;
|
||||
self service_ensure();
|
||||
state = self.tws_service;
|
||||
service_advance(state, getTime());
|
||||
state.generation++;
|
||||
state.help = service_request("help", service_page_count(state.commands.size, 4), state.generation);
|
||||
service_render(state);
|
||||
}
|
||||
|
||||
// An undefined snapshot is the explicit unavailable view for manual requests.
|
||||
show_record(snapshot)
|
||||
{
|
||||
if (isDefined(level.tws_service_ended))
|
||||
return;
|
||||
self service_ensure();
|
||||
state = self.tws_service;
|
||||
service_advance(state, getTime());
|
||||
count = 0;
|
||||
if (isDefined(snapshot))
|
||||
count = snapshot.lines.size;
|
||||
state.generation++;
|
||||
state.record = service_request("record", service_page_count(count, 3), state.generation);
|
||||
state.record.snapshot = snapshot;
|
||||
service_render(state);
|
||||
}
|
||||
|
||||
service_page_count(count, rows)
|
||||
{
|
||||
return int((max(count, 1) + rows - 1) / rows);
|
||||
}
|
||||
|
||||
service_request(kind, pages, generation)
|
||||
{
|
||||
request = spawnStruct();
|
||||
request.kind = kind;
|
||||
request.generation = generation;
|
||||
request.page = 0;
|
||||
request.pages = pages;
|
||||
request.remaining = 12000;
|
||||
return request;
|
||||
}
|
||||
|
||||
service_current(state)
|
||||
{
|
||||
if (isDefined(state.help))
|
||||
return state.help;
|
||||
return state.record;
|
||||
}
|
||||
|
||||
service_advance(state, now)
|
||||
{
|
||||
elapsed = max(0, now - state.updated_at);
|
||||
state.updated_at = now;
|
||||
request = service_current(state);
|
||||
if (!isDefined(request) || state.closed)
|
||||
return;
|
||||
|
||||
// Only the visible request consumes time. Help leaves the record untouched.
|
||||
request.remaining -= elapsed;
|
||||
if (request.remaining > 0)
|
||||
return;
|
||||
request.page++;
|
||||
request.remaining = 12000;
|
||||
if (request.page >= request.pages)
|
||||
{
|
||||
if (request.kind == "help")
|
||||
state.help = undefined;
|
||||
else
|
||||
state.record = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Stable order is independent of module initialization order; absent commands
|
||||
// stay absent. Unknown future commands follow the built-in ones in registry order.
|
||||
help_rank(command)
|
||||
{
|
||||
if (command == ".help") return 0;
|
||||
if (command == ".afk") return 1;
|
||||
if (command == ".back") return 2;
|
||||
if (command == ".join") return 3;
|
||||
if (command == ".leaders") return 4;
|
||||
if (command == ".b") return 5;
|
||||
if (command.size >= 3 && getSubStr(command, 0, 3) == ".d ") return 6;
|
||||
if (command.size >= 3 && getSubStr(command, 0, 3) == ".w ") return 7;
|
||||
return 8;
|
||||
}
|
||||
|
||||
help_entries()
|
||||
{
|
||||
entries = [];
|
||||
if (!isDefined(level.tws_help_commands))
|
||||
return entries;
|
||||
for (rank = 0; rank <= 8; rank++)
|
||||
{
|
||||
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;
|
||||
if (help_rank(entry.command) == rank)
|
||||
entries[entries.size] = entry;
|
||||
}
|
||||
|
||||
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();
|
||||
return entries;
|
||||
}
|
||||
|
||||
hide_help_after_delay()
|
||||
help_command_label(command)
|
||||
{
|
||||
rank = help_rank(command);
|
||||
if (rank == 6) return ".d <n|all>";
|
||||
if (rank == 7) return ".w <n|all>";
|
||||
return command;
|
||||
}
|
||||
|
||||
help_description(entry)
|
||||
{
|
||||
rank = help_rank(entry.command);
|
||||
if (rank == 0) return "Show commands";
|
||||
if (rank == 1) return "Enable protection";
|
||||
if (rank == 2) return "Return to play";
|
||||
if (rank == 3) return "Join as newcomer";
|
||||
if (rank == 4) return "View records";
|
||||
if (rank == 5) return "Check balance";
|
||||
if (rank == 6) return "Deposit points";
|
||||
if (rank == 7) return "Withdraw points";
|
||||
return entry.description;
|
||||
}
|
||||
|
||||
service_ensure()
|
||||
{
|
||||
if (isDefined(self.tws_service))
|
||||
return;
|
||||
ensure_config();
|
||||
state = spawnStruct();
|
||||
state.elems = [];
|
||||
state.commands = help_entries();
|
||||
state.help = undefined;
|
||||
state.record = undefined;
|
||||
state.closed = false;
|
||||
state.updated_at = getTime();
|
||||
state.rendered = false;
|
||||
state.generation = 0;
|
||||
state.last_generation = 0;
|
||||
state.last_page = -1;
|
||||
state.record_text = [];
|
||||
state.help_page = -1;
|
||||
state.rows = [];
|
||||
self.tws_service = state;
|
||||
|
||||
y = level.hud_service_y;
|
||||
state.background = make_panel_shader(self, 0, y, level.hud_panel_width,
|
||||
level.hud_service_height, level.hud_c_panel, level.hud_panel_alpha, 0);
|
||||
state.elems[state.elems.size] = state.background;
|
||||
state.help_title = service_text(self, state, 10, 10, "left", 1.1, "COMMANDS", level.hud_c_label);
|
||||
state.record_title = service_text(self, state, 10, 10, "left", 1.1, "MAP RECORD", level.hud_c_label);
|
||||
state.help_footer = service_text(self, state, 10, 108, "left", 0.9,
|
||||
"Four commands per page", level.hud_c_dim);
|
||||
state.record_footer = service_text(self, state, 10, 108, "left", 0.9,
|
||||
"Briefly shown - .leaders to view again", level.hud_c_dim);
|
||||
state.page_number = service_text(self, state, 178, 10, "right", 1.1, "", level.hud_c_dim);
|
||||
state.page_slash = service_text(self, state, 184, 10, "center", 1.1, "/", level.hud_c_dim);
|
||||
state.page_total = service_text(self, state, 210, 10, "right", 1.1, "", level.hud_c_dim);
|
||||
// Two fixed text columns keep the HUD element budget independent of the
|
||||
// command count. Strings change only at page boundaries, never per tick.
|
||||
state.command_back = make_panel_shader(self, 10, y + 30, 65, 68,
|
||||
(0.16, 0.19, 0.17), 0.9, 5);
|
||||
state.elems[state.elems.size] = state.command_back;
|
||||
state.command_column = service_text(self, state, 14, 31,
|
||||
"left", 1.0, "", level.hud_c_name);
|
||||
state.description_column = service_text(self, state, 81, 31,
|
||||
"left", 1.0, "", level.hud_c_count);
|
||||
state.round_label = service_text(self, state, 10, 32, "left", 1.1, "ROUND", level.hud_c_label);
|
||||
state.round_number = service_text(self, state, 54, 27, "left", 1.9, "", level.hud_c_count);
|
||||
state.holders_label = service_text(self, state, 10, 52, "left", 1.0, "RECORD HOLDERS", level.hud_c_label);
|
||||
for (i = 0; i < 3; i++)
|
||||
state.rows[i] = service_text(self, state, 10, 65 + i * 13,
|
||||
"left", 1.1, "", level.hud_c_count);
|
||||
state.empty = service_text(self, state, 10, 40, "left", 1.1,
|
||||
"No recorded high round yet", level.hud_c_count);
|
||||
state.unavailable = service_text(self, state, 10, 40, "left", 1.1,
|
||||
"Map record unavailable", level.hud_c_count);
|
||||
for (i = 0; i < state.elems.size; i++)
|
||||
{
|
||||
state.elems[i].alpha = 0;
|
||||
// Keep temporary private services out of the archived HUD bucket.
|
||||
// Hidden elements still occupy slots; do not allocate one per page.
|
||||
state.elems[i].archived = false;
|
||||
}
|
||||
|
||||
self thread service_loop(state);
|
||||
self thread service_disconnect(state);
|
||||
self thread service_end_game(state);
|
||||
}
|
||||
|
||||
service_text(player, state, x, y, align, scale, text, colour)
|
||||
{
|
||||
elem = make_panel_text(player, x, level.hud_service_y + y, align, scale, colour);
|
||||
elem.alpha = 0;
|
||||
if (text != "")
|
||||
elem SetText(text);
|
||||
state.elems[state.elems.size] = elem;
|
||||
return elem;
|
||||
}
|
||||
|
||||
service_render(state)
|
||||
{
|
||||
if (state.closed)
|
||||
return;
|
||||
request = service_current(state);
|
||||
page = -1;
|
||||
generation = 0;
|
||||
if (isDefined(request))
|
||||
{
|
||||
page = request.page;
|
||||
generation = request.generation;
|
||||
}
|
||||
if (state.rendered && state.last_generation == generation && state.last_page == page)
|
||||
return;
|
||||
state.rendered = true;
|
||||
state.last_generation = generation;
|
||||
state.last_page = page;
|
||||
for (i = 0; i < state.elems.size; i++)
|
||||
state.elems[i].alpha = 0;
|
||||
if (!isDefined(request))
|
||||
return;
|
||||
|
||||
state.background.alpha = level.hud_panel_alpha;
|
||||
state.page_number SetValue(page + 1);
|
||||
state.page_total SetValue(request.pages);
|
||||
state.page_number.alpha = 1;
|
||||
state.page_slash.alpha = 1;
|
||||
state.page_total.alpha = 1;
|
||||
if (request.kind == "help")
|
||||
{
|
||||
state.help_title.alpha = 1;
|
||||
state.help_footer.alpha = 1;
|
||||
if (state.help_page != page)
|
||||
{
|
||||
command_text = "";
|
||||
description_text = "";
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
index = page * 4 + i;
|
||||
if (index < state.commands.size)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
command_text += "\n";
|
||||
description_text += "\n";
|
||||
}
|
||||
entry = state.commands[index];
|
||||
command_text += help_command_label(entry.command);
|
||||
description_text += help_description(entry);
|
||||
}
|
||||
}
|
||||
state.command_column SetText(command_text);
|
||||
state.description_column SetText(description_text);
|
||||
state.help_page = page;
|
||||
}
|
||||
state.command_back.alpha = 0.9;
|
||||
state.command_column.alpha = 1;
|
||||
state.description_column.alpha = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
state.record_title.alpha = 1;
|
||||
state.record_footer.alpha = 1;
|
||||
snapshot = request.snapshot;
|
||||
if (!isDefined(snapshot))
|
||||
{
|
||||
state.unavailable.alpha = 1;
|
||||
return;
|
||||
}
|
||||
if (snapshot.round == 0)
|
||||
{
|
||||
state.empty.alpha = 1;
|
||||
return;
|
||||
}
|
||||
state.round_label.alpha = 1;
|
||||
state.round_number SetValue(snapshot.round);
|
||||
state.round_number.alpha = 1;
|
||||
state.holders_label.alpha = 1;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
index = page * 3 + i;
|
||||
if (index < snapshot.lines.size)
|
||||
{
|
||||
text = snapshot.lines[index];
|
||||
// Skip identical text when replaying a page or resuming after help.
|
||||
if (!isDefined(state.record_text[i]) || state.record_text[i] != text)
|
||||
{
|
||||
state.rows[i] SetText(text);
|
||||
state.record_text[i] = text;
|
||||
}
|
||||
state.rows[i].alpha = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
service_loop(state)
|
||||
{
|
||||
self endon("disconnect");
|
||||
self endon("tws_help_refresh");
|
||||
|
||||
wait 12;
|
||||
|
||||
for (i = 0; i < self.tws_help_elems.size; i++)
|
||||
level endon("tws_service_game_over");
|
||||
for (;;)
|
||||
{
|
||||
elem = self.tws_help_elems[i];
|
||||
elem.alpha = 0;
|
||||
service_advance(state, getTime());
|
||||
service_render(state);
|
||||
wait 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
// only connect listener in the fleet; a second one crashes the T5 SP handshake
|
||||
service_disconnect(state)
|
||||
{
|
||||
self waittill("disconnect");
|
||||
service_destroy(state);
|
||||
}
|
||||
|
||||
service_end_game(state)
|
||||
{
|
||||
self endon("disconnect");
|
||||
level waittill("tws_service_game_over");
|
||||
service_destroy(state);
|
||||
}
|
||||
|
||||
service_destroy(state)
|
||||
{
|
||||
if (state.closed)
|
||||
return;
|
||||
state.closed = true;
|
||||
state.help = undefined;
|
||||
state.record = undefined;
|
||||
for (i = 0; i < state.elems.size; i++)
|
||||
{
|
||||
if (isDefined(state.elems[i]))
|
||||
state.elems[i] Destroy();
|
||||
}
|
||||
state.elems = [];
|
||||
}
|
||||
|
||||
service_game_over_via(event)
|
||||
{
|
||||
level endon("tws_service_game_over");
|
||||
level waittill(event);
|
||||
level.tws_service_ended = true;
|
||||
level notify("tws_service_game_over");
|
||||
}
|
||||
|
||||
// The only connected listener; modules use the shared registries.
|
||||
on_player_connect_dispatch()
|
||||
{
|
||||
for (;;)
|
||||
@@ -238,6 +553,32 @@ make_shader_elem(player, x, y)
|
||||
return elem;
|
||||
}
|
||||
|
||||
// Panel x/y are local to the fixed upper-right core origin. A right screen
|
||||
// anchor keeps the panel on-screen across aspect ratios; negative offsets grow
|
||||
// left from that edge. Keep each engine's existing vertical anchor convention.
|
||||
make_panel_text(player, x, y, align_x, scale, colour)
|
||||
{
|
||||
elem = make_text_elem(player, x - level.hud_panel_width - level.hud_panel_right,
|
||||
level.hud_panel_top + y, align_x, scale, colour);
|
||||
elem.horzAlign = "right";
|
||||
elem.sort = 20;
|
||||
elem.archived = true;
|
||||
return elem;
|
||||
}
|
||||
|
||||
make_panel_shader(player, x, y, width, height, colour, alpha, sort)
|
||||
{
|
||||
elem = make_shader_elem(player, x - level.hud_panel_width - level.hud_panel_right,
|
||||
level.hud_panel_top + y);
|
||||
elem.horzAlign = "right";
|
||||
elem.color = colour;
|
||||
elem.alpha = alpha;
|
||||
elem.sort = sort;
|
||||
elem.archived = true;
|
||||
elem SetShader("white", width, height);
|
||||
return elem;
|
||||
}
|
||||
|
||||
destroy_on_disconnect(elems)
|
||||
{
|
||||
self waittill("disconnect");
|
||||
|
||||
Reference in New Issue
Block a user