Add proximity-based map record reminders
This commit is contained in:
+18
-5
@@ -16,7 +16,7 @@ Platform quirks and deploy commands: [NOTES.md](NOTES.md).
|
||||
| [tws_t5_join.gsc](tws_t5_join.gsc) | `.join` lets newcomers spawn before the current round ends |
|
||||
| [tws_t5_lock.gsc](tws_t5_lock.gsc) | Lobby `g_password` lock at a milestone round |
|
||||
| [tws_t5_telemetry.gsc](tws_t5_telemetry.gsc) | Direct HTTP game start, round, heartbeat, and game end reports |
|
||||
| [tws_t5_leaderboard.gsc](tws_t5_leaderboard.gsc) | Private map high-round record and all tied holders on connection |
|
||||
| [tws_t5_leaderboard.gsc](tws_t5_leaderboard.gsc) | Private map record on connection, `.leaders` replay and adaptive round-start reminders |
|
||||
| [tws_t5_bank.gsc](tws_t5_bank.gsc) | Persistent shared GUID bank and private balance/deposit/withdraw commands |
|
||||
| [tws_t5_moderation.gsc](tws_t5_moderation.gsc) | Optional controller-managed bans, mutes and live moderation; requires common 1.2.0+ and native plugin 1.3.0+ |
|
||||
|
||||
@@ -89,8 +89,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.
|
||||
|
||||
@@ -99,13 +99,16 @@ 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
|
||||
|
||||
Copy `tws_t5_leaderboard.gsc` and the updated `tws_t5_common.gsc` to
|
||||
`storage/t5/scripts/sp/`, then restart the map. Leaderboard requires a
|
||||
`storage/t5/scripts/sp/`, then restart the map. Leaderboard `1.2.0` requires
|
||||
common `1.5.0+`; this script update adds no native-plugin or backend dependency
|
||||
and requires no full server-process restart. Leaderboard requires a
|
||||
[t5-gsc-utils](https://github.com/alicealys/t5-gsc-utils) build exposing
|
||||
`http::request`, `jsonParse`, `typeof` and `formatString`. A plugin upgrade
|
||||
requires a full server-process restart. To omit leaderboard, remove its script;
|
||||
@@ -117,6 +120,16 @@ the same private announcement as playable joins. T5 matches its engine map to
|
||||
the API's map list. [Shared configuration, caching and validation](../../README.md#map-record-announcements)
|
||||
apply to both games.
|
||||
|
||||
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.
|
||||
|
||||
## Player commands
|
||||
|
||||
Type `.help` or `.commands` in chat to see available player commands and a short description.
|
||||
|
||||
@@ -170,6 +170,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);
|
||||
@@ -192,6 +195,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);
|
||||
@@ -205,6 +231,8 @@ service_request(kind, pages, generation)
|
||||
request.page = 0;
|
||||
request.pages = pages;
|
||||
request.remaining = 12000;
|
||||
if (kind == "reminder")
|
||||
request.remaining = 6000;
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -351,7 +379,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);
|
||||
@@ -467,6 +498,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_t5_leaderboard.gsc - private map record announcements and .leaders replay. */
|
||||
/* tws_t5_leaderboard.gsc - private map records, round reminders and .leaders replay. */
|
||||
|
||||
#include maps\_utility;
|
||||
#include common_scripts\utility;
|
||||
@@ -24,10 +24,12 @@ init()
|
||||
level.tws_lb_pending = undefined;
|
||||
level.tws_lb_cache = undefined;
|
||||
level.tws_lb_attempt = 0;
|
||||
level.tws_lb_chase_finished = undefined;
|
||||
scripts\sp\tws_t5_common::register_help_command(".leaders", "Show the map record again");
|
||||
scripts\sp\tws_t5_common::register_connect_handler(::lb_welcome);
|
||||
level thread lb_chat_listener();
|
||||
level thread lb_worker();
|
||||
level thread lb_round_watcher();
|
||||
}
|
||||
|
||||
lb_worker()
|
||||
@@ -64,6 +66,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(player.hasSpawned) && !player.hasSpawned)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
lb_remind_player(snapshot, message)
|
||||
{
|
||||
if (!lb_player_ready(self))
|
||||
return;
|
||||
self scripts\sp\tws_t5_common::show_record_reminder(snapshot, message);
|
||||
}
|
||||
|
||||
lb_welcome()
|
||||
{
|
||||
self endon("disconnect");
|
||||
@@ -128,6 +212,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