Add proximity-based map record reminders
This commit is contained in:
+20
-4
@@ -13,7 +13,7 @@ private server token.
|
||||
| `tws_t6_lock.gsc` | Lock at round 20; clear password on game over | Common, HUD |
|
||||
| `tws_t6_telemetry.gsc` | Async queued game reports (game_start/heartbeat/round_change/game_end) | tws-t6-gsc-utils |
|
||||
| `tws_t6_join.gsc` | `.join` chat command lets a spectator newcomer enter mid-match once | Common |
|
||||
| `tws_t6_leaderboard.gsc` | Private map high-round record and every tied holder on connection | Common 1.2.0+, tws-t6-gsc-utils |
|
||||
| `tws_t6_leaderboard.gsc` | Private map record on connection, `.leaders` replay and adaptive round-start reminders | Common 1.5.0+, tws-t6-gsc-utils |
|
||||
| `tws_t6_bank.gsc` | Persistent shared GUID bank | Common, telemetry, TWS plugin 1.2.0 |
|
||||
| `tws_t6_moderation.gsc` | Optional controller-managed bans, mutes and live moderation | Common 1.2.0+, TWS plugin 1.3.0+ |
|
||||
|
||||
@@ -102,8 +102,8 @@ the installed command list in pages of nine rows in aligned rows. Services remai
|
||||
core in place and work without the HUD module installed. See the
|
||||
[shared layout and validation guide](../../README.md#core-hud-and-service-window-t5-and-t6).
|
||||
Only the active service view holds HUD elements: 19 for nine-command help
|
||||
(20 with a multi-page counter; the standard AFK command set uses multiple pages), or four for records (five with a multi-page
|
||||
counter). Pages reuse elements; switching views or closing the service releases
|
||||
(20 with a multi-page counter; the standard AFK command set uses multiple pages), four for records (five with a multi-page
|
||||
counter), or two for compact round-start reminders. Pages reuse elements; switching views or closing the service releases
|
||||
them. Idle services hold no HUD elements, while the controller retains pending
|
||||
requests and interruption/resume behavior. Core + AFK use 10 + 2 elements.
|
||||
|
||||
@@ -112,7 +112,8 @@ HUD `1.4.2` requires common `1.4.0+` and
|
||||
`SetTextUnlimited` counter, updated only when the count changes. Deploy the
|
||||
updated HUD script and restart the map on r5334+; no full server-process
|
||||
restart is required. Older Plutonium revisions need client/server updates and
|
||||
full restarts first. Leaderboard `1.1.0` remains compatible. AFK `2.0.0` requires the matching
|
||||
full restarts first. Leaderboard `1.2.0` requires common `1.5.0+` for its
|
||||
round-start reminders. AFK `2.0.0` requires the matching
|
||||
plugin `1.4.0+` and EC reserve service; see the [AFK guide](../../docs/afk.md).
|
||||
|
||||
## Map record announcements
|
||||
@@ -121,6 +122,21 @@ plugin `1.4.0+` and EC reserve service; see the [AFK guide](../../docs/afk.md).
|
||||
It works independently of telemetry and HUD, and privately announces records
|
||||
once per connection, including newcomers waiting in spectator mode.
|
||||
|
||||
Leaderboard `1.2.0` requires common `1.5.0+`. Copy both scripts to
|
||||
`Plutonium/storage/t6/scripts/zm/` and restart the map. This script update adds
|
||||
no native-plugin or backend dependency and requires no full server-process
|
||||
restart when the existing plugin is installed.
|
||||
|
||||
Round-start reminders use the latest usable cache at gaps of 20, 15, 10, 8 and
|
||||
6 rounds, then every round for the final five, followed by matching/overtake
|
||||
milestones. After the first overtake, reminders stop for that match; `.leaders`
|
||||
still works. A reminder shows the record and one short status line for six
|
||||
seconds. Join and manual announcements keep all holders and 12-second pages.
|
||||
Reminders skip busy help/record views, unready players, recent welcome/manual
|
||||
requests and empty/unavailable/expired records. Help discards a reminder instead
|
||||
of pausing it. No extra HTTP requests are made. Follow the shared live checks
|
||||
for cadence, interruption, cleanup and operation without the optional HUD.
|
||||
|
||||
Map selection uses stock `level.scr_zm_ui_gametype` and
|
||||
`level.scr_zm_map_start_location`, falling back to the corresponding
|
||||
`ui_gametype` / `ui_zm_mapstartlocation` dvars when absent. For example,
|
||||
|
||||
@@ -166,6 +166,9 @@ show_help()
|
||||
self service_ensure();
|
||||
state = self.tws_service;
|
||||
service_advance(state, getTime());
|
||||
// Round-start notices are timely hints, not requests to resume after help.
|
||||
if (isDefined(state.record) && state.record.kind == "reminder")
|
||||
state.record = undefined;
|
||||
state.generation++;
|
||||
state.help = service_request("help", service_page_count(state.commands.size, level.hud_service_help_rows), state.generation);
|
||||
service_render(state);
|
||||
@@ -188,6 +191,29 @@ show_record(snapshot)
|
||||
service_render(state);
|
||||
}
|
||||
|
||||
// Automatic round-start hints yield to any existing service and never queue.
|
||||
show_record_reminder(snapshot, message)
|
||||
{
|
||||
if (isDefined(level.tws_service_ended) || !isDefined(snapshot) ||
|
||||
!isDefined(snapshot.round) || snapshot.round <= 0 ||
|
||||
!isDefined(message) || message == "")
|
||||
return false;
|
||||
self service_ensure();
|
||||
state = self.tws_service;
|
||||
if (state.closed)
|
||||
return false;
|
||||
service_advance(state, getTime());
|
||||
if (isDefined(state.help) || isDefined(state.record))
|
||||
return false;
|
||||
|
||||
state.generation++;
|
||||
state.record = service_request("reminder", 1, state.generation);
|
||||
state.record.snapshot = snapshot;
|
||||
state.record.message = message;
|
||||
service_render(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
service_page_count(count, rows)
|
||||
{
|
||||
return int((max(count, 1) + rows - 1) / rows);
|
||||
@@ -201,6 +227,8 @@ service_request(kind, pages, generation)
|
||||
request.page = 0;
|
||||
request.pages = pages;
|
||||
request.remaining = 12000;
|
||||
if (kind == "reminder")
|
||||
request.remaining = 6000;
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -347,7 +375,10 @@ service_view_create(state, request)
|
||||
|
||||
state.record_title = service_text(self, state, level.hud_service_right,
|
||||
level.hud_service_record_y, "right", 1.1, "", level.hud_c_count);
|
||||
for (i = 0; i < 3; i++)
|
||||
rows = 3;
|
||||
if (request.kind == "reminder")
|
||||
rows = 1;
|
||||
for (i = 0; i < rows; i++)
|
||||
state.rows[i] = service_text(self, state, level.hud_service_right,
|
||||
level.hud_service_holders_y + i * level.hud_service_holders_pitch,
|
||||
"right", 1.05, "", level.hud_c_label);
|
||||
@@ -463,6 +494,12 @@ service_render(state)
|
||||
state.record_title.alpha = 1;
|
||||
if (!isDefined(snapshot) || snapshot.round == 0)
|
||||
return;
|
||||
if (request.kind == "reminder")
|
||||
{
|
||||
state.rows[0] SetText(request.message);
|
||||
state.rows[0].alpha = 1;
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
index = page * 3 + i;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* tws_t6_leaderboard.gsc - private map record announcements and .leaders replay. */
|
||||
/* tws_t6_leaderboard.gsc - private map records, round reminders and .leaders replay. */
|
||||
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
@@ -26,10 +26,12 @@ init()
|
||||
level.tws_lb_pending = undefined;
|
||||
level.tws_lb_cache = undefined;
|
||||
level.tws_lb_attempt = 0;
|
||||
level.tws_lb_chase_finished = undefined;
|
||||
scripts\zm\tws_t6_common::register_help_command(".leaders", "Show the map record again");
|
||||
scripts\zm\tws_t6_common::register_connect_handler(::lb_welcome);
|
||||
level thread lb_chat_listener();
|
||||
level thread lb_worker();
|
||||
level thread lb_round_watcher();
|
||||
}
|
||||
|
||||
lb_worker()
|
||||
@@ -66,6 +68,88 @@ lb_worker()
|
||||
}
|
||||
}
|
||||
|
||||
lb_round_watcher()
|
||||
{
|
||||
level endon("end_game");
|
||||
level endon("game_ended");
|
||||
level endon("intermission");
|
||||
|
||||
last_round = 0;
|
||||
for (;;)
|
||||
{
|
||||
// T6 advances its counter before the between-round pause is over.
|
||||
// Both engines notify the actual start; never synthesize a late start.
|
||||
level waittill("start_of_round");
|
||||
if (!isDefined(level.round_number) || level.round_number <= last_round)
|
||||
continue;
|
||||
last_round = level.round_number;
|
||||
lb_round_start(last_round);
|
||||
}
|
||||
}
|
||||
|
||||
lb_round_start(round)
|
||||
{
|
||||
if (isDefined(level.tws_lb_chase_finished))
|
||||
return;
|
||||
snapshot = lb_cached_record();
|
||||
if (!isDefined(snapshot) || snapshot.round < 2)
|
||||
return;
|
||||
message = lb_round_message(round, snapshot.round);
|
||||
if (message == "")
|
||||
return;
|
||||
|
||||
// Celebrate at most once, even if a later refresh raises the fleet record.
|
||||
// Busy clients skip the milestone too; no delayed mid-round announcements.
|
||||
if (round > snapshot.round)
|
||||
level.tws_lb_chase_finished = true;
|
||||
players = getPlayers();
|
||||
for (i = 0; i < players.size; i++)
|
||||
players[i] lb_remind_player(snapshot, message);
|
||||
}
|
||||
|
||||
lb_round_message(round, record)
|
||||
{
|
||||
if (round < 1 || record < 2)
|
||||
return "";
|
||||
gap = record - round;
|
||||
if (gap < 0)
|
||||
return "Recorded high round passed!";
|
||||
if (gap == 0)
|
||||
return "Record round reached!";
|
||||
if (gap > 20)
|
||||
return "";
|
||||
// Anchor the cadence to the target, not to round numbers divisible by 5/2.
|
||||
if (gap > 10 && gap % 5 != 0)
|
||||
return "";
|
||||
if (gap > 5 && gap <= 10 && gap % 2 != 0)
|
||||
return "";
|
||||
if (gap == 1)
|
||||
return "1 round away from matching it";
|
||||
return gap + " rounds away from matching it";
|
||||
}
|
||||
|
||||
lb_player_ready(player)
|
||||
{
|
||||
// A welcome attempt (even with no record) or manual request releases this
|
||||
// gate. Give that full announcement at least 30 seconds before a reminder.
|
||||
if (!isDefined(player.tws_lb_last_request) ||
|
||||
getTime() - player.tws_lb_last_request < 30000)
|
||||
return false;
|
||||
if (!isDefined(player.sessionstate) ||
|
||||
(player.sessionstate != "playing" && player.sessionstate != "spectator"))
|
||||
return false;
|
||||
if (isDefined(level.passed_introscreen) && !level.passed_introscreen)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
lb_remind_player(snapshot, message)
|
||||
{
|
||||
if (!lb_player_ready(self))
|
||||
return;
|
||||
self scripts\zm\tws_t6_common::show_record_reminder(snapshot, message);
|
||||
}
|
||||
|
||||
lb_welcome()
|
||||
{
|
||||
self endon("disconnect");
|
||||
@@ -131,6 +215,7 @@ lb_request(manual)
|
||||
// This also applies when the cache is unavailable.
|
||||
if (manual)
|
||||
self notify("tws_lb_requested");
|
||||
self.tws_lb_last_request = getTime();
|
||||
snapshot = lb_cached_record();
|
||||
if (!isDefined(snapshot) && !manual)
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user