Allow solo and last-active-player AFK

This commit is contained in:
2026-10-17 22:19:04 -04:00
parent c19bef6dc6
commit caaaa102a4
9 changed files with 115 additions and 142 deletions
+5 -1
View File
@@ -340,7 +340,11 @@ Chat uses `level waittill("say")`; the independent central countdown stays visib
with `.hud` hidden. Activation rejects laststand before changing state. A stock
damage callback owns only AFK protection; expiration immediately relinquishes it
and AFK-owned controls/targeting, while manual `.back` keeps its separate two-second
grace. Spawn handler `::afk_spawn` binds contribution and lifecycle watchers.
grace. AFK `2.1.0` permits solo and last-active-player entry, including all players
being AFK together; teammate loss does not force a match end. Upgrade from `2.0.0`
by copying the script and restarting the map; no new dependencies or full
server-process restart are required. Spawn handler `::afk_spawn` binds contribution
and lifecycle watchers.
---
+1 -60
View File
@@ -45,7 +45,6 @@ init()
level thread afk_startup();
level thread afk_end_watcher();
level thread afk_round_watcher();
level thread afk_team_watcher();
}
afk_startup()
@@ -408,11 +407,6 @@ afk_activate(player, state)
afk_say(state, "Wait briefly after returning before entering AFK again.");
return;
}
if (afk_other_active(player) == 0)
{
afk_say(state, "The last active player cannot enter AFK. Another player must remain in the fight.");
return;
}
data = afk_base(state, "reserve");
data["session_id"] = telemetryNewMatch();
data["seconds"] = 604800;
@@ -459,7 +453,7 @@ afk_activate(player, state)
reply = afk_http("start", start_body, start["request_id"]);
if (!isDefined(reply) || reply["state"] != "started" || reply["session_id"] != session ||
reply["authorized_seconds"] != authorized || !state.connected ||
state.cancelled || !afk_can_enter(player) || afk_other_active(player) == 0)
state.cancelled || !afk_can_enter(player))
{
afk_stop_state(state, "never_started");
afk_say(state, "AFK entry cancelled. No protected time used.");
@@ -710,59 +704,6 @@ afk_end_watcher()
level.afk_ended = true;
}
afk_other_active(excluded)
{
count = 0;
players = getPlayers();
for (i = 0; i < players.size; i++)
{
player = players[i];
if (isDefined(excluded) && player == excluded)
continue;
if (!afk_playing(player) || is_true(player.hud_afk_on))
continue;
if (isDefined(player.tws_afk_state) && player.tws_afk_state.pending)
continue;
count++;
}
return count;
}
afk_team_watcher()
{
level endon("end_game");
level endon("game_ended");
level endon("intermission");
for (;;)
{
players = getPlayers();
protected = 0;
survivors = 0;
stock_recovery = is_true(level.no_end_game_check) || is_true(level.isresetting_grief);
for (i = 0; i < players.size; i++)
{
player = players[i];
if (is_true(player.afterlife))
stock_recovery = true;
if (is_true(player.hud_afk_on))
protected++;
else if (afk_playing(player))
survivors++;
}
if (protected > 0 && survivors == 0 && !stock_recovery)
{
for (i = 0; i < players.size; i++)
if (is_true(players[i].hud_afk_on))
players[i] afk_finish("match_end", false);
// AFK does not change sessionstate/player count or zombie scaling.
// The verified stock end signal prevents AFK bodies prolonging play.
level notify("end_game");
return;
}
wait 0.05;
}
}
// Both stock engines notify the credited attacker, with the actual dead zombie.
// Never infer contributions from spendable score, score_total, or bank actions.
afk_kill_watcher()