Center zombie counters and use ASCII HUD separators
This commit is contained in:
@@ -4,6 +4,60 @@ Changes are listed newest first, with an independent version for every individua
|
|||||||
T5/T6 script and each native plugin. Current versions are recorded in
|
T5/T6 script and each native plugin. Current versions are recorded in
|
||||||
[versions.json](versions.json); see the [version policy](README.md#versioning).
|
[versions.json](versions.json); see the [version policy](README.md#versioning).
|
||||||
|
|
||||||
|
## 2026-10-17
|
||||||
|
|
||||||
|
### Center the complete zombie count above the health bar
|
||||||
|
|
||||||
|
| Component | Version | Affected source |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `tws_t5_hud.gsc` | `1.4.2` | `t5/scripts/tws_t5_hud.gsc` |
|
||||||
|
| `tws_t6_hud.gsc` | `1.4.2` | `t6/scripts/tws_t6_hud.gsc` |
|
||||||
|
|
||||||
|
- Replace the separately positioned number and label with one center-aligned
|
||||||
|
`N ZOMBIES` readout at the health track's midpoint in both engines. Use a
|
||||||
|
uniform font scale of `1.25` and the existing count color; digit changes and
|
||||||
|
the initial `-- ZOMBIES` placeholder share the same center.
|
||||||
|
- Update text only when the count changes using `SetTextUnlimited`, avoiding
|
||||||
|
a new config string for each distinct count. Keep counting, `.hud` visibility,
|
||||||
|
game-over termination and disconnect cleanup behavior. Core HUD allocation
|
||||||
|
decreases from 11 to 10 archived elements; AFK still adds one.
|
||||||
|
- Compatibility: HUD `1.4.2` requires matching common `1.4.0+` and
|
||||||
|
[Plutonium r5334+](https://plutonium.pw/docs/changelog/#r5334), which provides
|
||||||
|
`SetTextUnlimited` for T5 and T6. No new plugin or backend requirement.
|
||||||
|
Common and AFK need no source change for this fix; both HUD scripts change.
|
||||||
|
- Validation: 73 HUD checks passed, including digit transitions, a non-default
|
||||||
|
track midpoint, unchanged-count polling, hidden updates and service cleanup.
|
||||||
|
Leaderboard (143), bank (42), join (17) and telemetry (23) portable checks
|
||||||
|
passed. These use engine shims; actual EC integration and live Plutonium
|
||||||
|
validation were not run. GSC compilation was unavailable because gsc-tool
|
||||||
|
and PowerShell are not installed. Verify rendering on Windows/Plutonium.
|
||||||
|
- Deployment: copy each updated HUD script and restart the map on r5334+.
|
||||||
|
Older installations must first update Plutonium on clients and servers and
|
||||||
|
fully restart those processes. No full server-process restart is required
|
||||||
|
for the script update alone. No deployment performed.
|
||||||
|
|
||||||
|
### Use ASCII separators in HUD branding and map records
|
||||||
|
|
||||||
|
| Component | Version | Affected source |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `tws_t5_common.gsc` | `1.4.1` | `t5/scripts/tws_t5_common.gsc` |
|
||||||
|
| `tws_t6_common.gsc` | `1.4.1` | `t6/scripts/tws_t6_common.gsc` |
|
||||||
|
| `tws_t5_hud.gsc` | `1.4.1` | `t5/scripts/tws_t5_hud.gsc` |
|
||||||
|
| `tws_t6_hud.gsc` | `1.4.1` | `t6/scripts/tws_t6_hud.gsc` |
|
||||||
|
|
||||||
|
- Replace the middle dot with an ASCII vertical bar in the top-center branding
|
||||||
|
and map-record heading after the separator displayed as two dots on T5.
|
||||||
|
Apply the same text change to T6 for a consistent appearance. Keep existing
|
||||||
|
spacing, positioning and HUD/service behavior.
|
||||||
|
- Compatibility: no new dependency requirement. No native-plugin, backend or
|
||||||
|
other script changes are required.
|
||||||
|
- Validation: 71 HUD, 143 leaderboard, 42 bank, 17 join and 23 telemetry portable
|
||||||
|
checks passed. These use engine shims; actual EC bank integration, GSC
|
||||||
|
compilation and live Windows/Plutonium validation were not performed.
|
||||||
|
- Deployment: copy the updated common and HUD scripts for each installed game,
|
||||||
|
then restart the map. No full server-process restart is required. No deployment
|
||||||
|
performed; live Windows/Plutonium rendering validation remains outstanding.
|
||||||
|
|
||||||
## 2026-10-15 — Player moderation runtime
|
## 2026-10-15 — Player moderation runtime
|
||||||
|
|
||||||
- **tws_t5_moderation.gsc 1.0.0 / tws_t6_moderation.gsc 1.0.0**: new optional
|
- **tws_t5_moderation.gsc 1.0.0 / tws_t6_moderation.gsc 1.0.0**: new optional
|
||||||
|
|||||||
@@ -362,11 +362,14 @@ settles legacy and public journals without duplicate ledger entries or lost poin
|
|||||||
## Core HUD and service window (T5 and T6)
|
## Core HUD and service window (T5 and T6)
|
||||||
|
|
||||||
The core uses a light, unboxed layout: match time above round time at the upper
|
The core uses a light, unboxed layout: match time above round time at the upper
|
||||||
left, `TWS ZOMBIES · discord.tws.lol` centered along the top, and the zombie
|
left, `TWS ZOMBIES | discord.tws.lol` centered along the top, and the zombie
|
||||||
count centered near the bottom above a slim health bar. Current/maximum HP
|
count centered near the bottom above a slim health bar. The complete
|
||||||
appears to the right of the bar only while injured, then disappears at full
|
`N ZOMBIES` text uses one centered element at the health track's midpoint,
|
||||||
health. The AFK indicator sits above the zombie count. Text uses an outline for
|
keeping the group centered as the count gains or loses digits. It uses a
|
||||||
contrast; there are no panel backgrounds or timer labels.
|
uniform font scale of `1.25` and shows `-- ZOMBIES` before data is available.
|
||||||
|
Current/maximum HP appears to the right of the bar only while injured, then
|
||||||
|
disappears at full health. The AFK indicator sits above the zombie count. Text
|
||||||
|
uses an outline for contrast; there are no panel backgrounds or timer labels.
|
||||||
|
|
||||||
Fullscreen X/Y anchors bypass safe-area insets. Overlay positions and shader
|
Fullscreen X/Y anchors bypass safe-area insets. Overlay positions and shader
|
||||||
widths use the normalized `640 × 480` viewport directly. Text uses a minimum
|
widths use the normalized `640 × 480` viewport directly. Text uses a minimum
|
||||||
@@ -401,7 +404,7 @@ count appears only when more than one page is needed. Each page lasts
|
|||||||
command restarts page one. Both engines position rows explicitly, avoiding
|
command restarts page one. Both engines position rows explicitly, avoiding
|
||||||
T5's newline-rendering problem.
|
T5's newline-rendering problem.
|
||||||
|
|
||||||
Records use the same service area, right aligned: `Map record · Round N`
|
Records use the same service area, right aligned: `Map record | Round N`
|
||||||
followed by up to three bounded holder lines. Single-page records have no
|
followed by up to three bounded holder lines. Single-page records have no
|
||||||
counter or footer; ties spanning multiple pages retain a page count and
|
counter or footer; ties spanning multiple pages retain a page count and
|
||||||
12 seconds per page. Help pauses a visible record and resumes its remaining
|
12 seconds per page. Help pauses a visible record and resumes its remaining
|
||||||
@@ -411,19 +414,24 @@ remain private and work when the HUD module is omitted.
|
|||||||
|
|
||||||
Both games allocate only the active service view and release it when idle or
|
Both games allocate only the active service view and release it when idle or
|
||||||
before switching between help and records. Pages reuse the active view's
|
before switching between help and records. Pages reuse the active view's
|
||||||
elements. The core uses 11 archived elements, plus one for AFK. The standard
|
elements. The core uses 10 archived elements, plus one for AFK. The standard
|
||||||
nine-command help view uses 19 nonarchived elements, or 20 with a multi-page
|
nine-command help view uses 19 nonarchived elements, or 20 with a multi-page
|
||||||
counter; records use four, or five with a multi-page counter. Hidden elements
|
counter; records use four, or five with a multi-page counter. Hidden elements
|
||||||
still occupy slots. The controller and pending requests are separate from
|
still occupy slots. The controller and pending requests are separate from
|
||||||
display lifetime, so an idle service holds no HUD elements.
|
display lifetime, so an idle service holds no HUD elements.
|
||||||
|
|
||||||
HUD `1.4.0` and AFK `1.2.0` require matching common `1.4.0+` on both games.
|
HUD `1.4.2` requires matching common `1.4.0+` and
|
||||||
Update common, HUD and AFK together where installed, then restart the map.
|
[Plutonium r5334+](https://plutonium.pw/docs/changelog/#r5334) on both games.
|
||||||
Leaderboard `1.1.0` remains compatible. There is no new Plutonium, native-plugin
|
The centered counter uses `SetTextUnlimited` only when the count changes,
|
||||||
or backend requirement, and this change does not require a full server-process
|
avoiding dynamic counter strings in the text config-string pool. Numeric HP
|
||||||
restart. Existing leaderboard plugin requirements still apply. Common retains
|
still uses `SetValue`. On r5334+, deploy the updated HUD script and restart
|
||||||
its older panel helpers for unchanged consumers; the new layout uses fullscreen
|
the map; no full server-process restart is required. On older Plutonium
|
||||||
overlay helpers with the same integer shader-width handling.
|
revisions, update clients and server and fully restart them before deploying.
|
||||||
|
AFK `1.2.0` and leaderboard `1.1.0` remain compatible; AFK still requires
|
||||||
|
common `1.4.0+`. There is no new native-plugin or backend requirement. Existing
|
||||||
|
leaderboard plugin requirements still apply. Common retains its older panel
|
||||||
|
helpers for unchanged consumers; the new layout uses fullscreen overlay helpers
|
||||||
|
with the same integer shader-width handling.
|
||||||
|
|
||||||
### HUD validation
|
### HUD validation
|
||||||
|
|
||||||
@@ -445,7 +453,9 @@ Before rollout, validate both engines in Plutonium:
|
|||||||
legible without panel backgrounds; services must clear the Plutonium tag.
|
legible without panel backgrounds; services must clear the Plutonium tag.
|
||||||
Verify the health-bar fill stays within its track and damage-only HP clears
|
Verify the health-bar fill stays within its track and damage-only HP clears
|
||||||
stock scores/ammo. Check text fit at other aspect ratios because horizontal
|
stock scores/ammo. Check text fit at other aspect ratios because horizontal
|
||||||
geometry scales with viewport width.
|
geometry scales with viewport width. Check `0`, `6`, `9`, `10`, `99` and
|
||||||
|
`100 ZOMBIES`: the entire text must stay centered over the health track,
|
||||||
|
including after `.hud` hides/shows it and after game over freezes the count.
|
||||||
2. Join before spawning, join mid-match, die/respawn and reconnect. Verify one
|
2. Join before spawning, join mid-match, die/respawn and reconnect. Verify one
|
||||||
core per player, spectator placeholders, no early match-clock start, correct
|
core per player, spectator placeholders, no early match-clock start, correct
|
||||||
health/Juggernog changes, and frozen timers at game over. HP numbers must
|
health/Juggernog changes, and frozen timers at game over. HP numbers must
|
||||||
@@ -509,7 +519,7 @@ when changing variants; restart the map after changing these settings.
|
|||||||
|
|
||||||
The announcement starts five seconds after player readiness and waits for the
|
The announcement starts five seconds after player readiness and waits for the
|
||||||
initial record fetch if necessary. It appears as right-aligned text in the
|
initial record fetch if necessary. It appears as right-aligned text in the
|
||||||
common-owned upper-right service area: `Map record · Round N`, followed by up
|
common-owned upper-right service area: `Map record | Round N`, followed by up
|
||||||
to three holder lines. There is no background or replay footer, and a page
|
to three holder lines. There is no background or replay footer, and a page
|
||||||
count appears only for multi-page records. Each page lasts 12 seconds. Opening `.help` pauses the record's remaining
|
count appears only for multi-page records. Each page lasts 12 seconds. Opening `.help` pauses the record's remaining
|
||||||
page time until help closes. A `.leaders` request during help replaces any
|
page time until help closes. A `.leaders` request during help replaces any
|
||||||
|
|||||||
+24
-13
@@ -32,12 +32,15 @@ before copying the renamed scripts.
|
|||||||
- **`SetTimerUp(N)` only ticks when `N == 0`** on T5 SP. Non-zero seeds
|
- **`SetTimerUp(N)` only ticks when `N == 0`** on T5 SP. Non-zero seeds
|
||||||
render "0:00" but don't advance. Late joiners see 0, not real elapsed;
|
render "0:00" but don't advance. Late joiners see 0, not real elapsed;
|
||||||
authoritative elapsed lives in `level.match_start_time`.
|
authoritative elapsed lives in `level.match_start_time`.
|
||||||
- **Dynamic numbers use `SetValue`.** Repeatedly generating unique strings
|
- **Dynamic counter text uses `SetTextUnlimited`; HP uses `SetValue`.**
|
||||||
risks exhausting the text config-string pool; identical strings can be reused
|
Repeatedly generating unique strings through `SetText` risks exhausting the
|
||||||
by the engine's [string lookup](https://github.com/MisaDev4/bo1-zombies-decompiled/blob/102f6992be332610aff587e681e3180b15f395bb/src/game_mp/g_utils_mp.cpp#L46).
|
text config-string pool; identical strings can be reused by the engine's
|
||||||
Composite HP uses numeric elements around a static slash and `HP` suffix;
|
[string lookup](https://github.com/MisaDev4/bo1-zombies-decompiled/blob/102f6992be332610aff587e681e3180b15f395bb/src/game_mp/g_utils_mp.cpp#L46).
|
||||||
the group appears only while injured. Help reuses up to nine explicitly
|
The centered `N ZOMBIES` element uses Plutonium r5334+'s `SetTextUnlimited`
|
||||||
positioned command/description pairs, and record rows skip unchanged text.
|
only when the count changes. Composite HP retains numeric elements around a
|
||||||
|
static slash and `HP` suffix; the group appears only while injured. Help
|
||||||
|
reuses up to nine explicitly positioned command/description pairs, and
|
||||||
|
record rows skip unchanged text.
|
||||||
- **`NewClientHudElem(player)`, not `NewHudElem()`.** `NewHudElem` is
|
- **`NewClientHudElem(player)`, not `NewHudElem()`.** `NewHudElem` is
|
||||||
globally owned; in co-op every player's HUD draws on top of every other
|
globally owned; in co-op every player's HUD draws on top of every other
|
||||||
player's. Fall back to `NewHudElem` only if elements don't render at all
|
player's. Fall back to `NewHudElem` only if elements don't render at all
|
||||||
@@ -77,7 +80,7 @@ before copying the renamed scripts.
|
|||||||
from page one. Keep both services independent of the HUD module. Services
|
from page one. Keep both services independent of the HUD module. Services
|
||||||
never reposition the core, and AFK remains centered above the zombie count.
|
never reposition the core, and AFK remains centered above the zombie count.
|
||||||
- **Bound HUD element counts, including hidden elements.** Core + AFK use
|
- **Bound HUD element counts, including hidden elements.** Core + AFK use
|
||||||
11 + 1 archived elements. Common allocates only the active service view:
|
10 + 1 archived elements. Common allocates only the active service view:
|
||||||
19 nonarchived elements for nine-command help (20 with a multi-page counter),
|
19 nonarchived elements for nine-command help (20 with a multi-page counter),
|
||||||
or four for records (five with a multi-page counter), and none while idle.
|
or four for records (five with a multi-page counter), and none while idle.
|
||||||
Switching help/record views destroys the previous view first; pages reuse
|
Switching help/record views destroys the previous view first; pages reuse
|
||||||
@@ -99,12 +102,16 @@ before copying the renamed scripts.
|
|||||||
polling `self.health` misses the zero-crossing (health is rewritten during
|
polling `self.health` misses the zero-crossing (health is rewritten during
|
||||||
the game-over sequence).
|
the game-over sequence).
|
||||||
|
|
||||||
HUD `1.4.0` and AFK `1.2.0` require matching common `1.4.0+`. Update all three
|
HUD `1.4.2` requires matching common `1.4.0+` and
|
||||||
where installed and restart the map. Leaderboard `1.1.0` remains compatible.
|
[Plutonium r5334+](https://plutonium.pw/docs/changelog/#r5334) for
|
||||||
|
`SetTextUnlimited`. On r5334+, update the HUD script and restart the map;
|
||||||
|
older Plutonium clients and servers need updates and full restarts first.
|
||||||
|
AFK `1.2.0` and leaderboard `1.1.0` remain compatible; AFK still requires
|
||||||
|
common `1.4.0+`.
|
||||||
The full help order is `.help`, `.hud`, `.afk`, `.back`, `.join`, `.leaders`,
|
The full help order is `.help`, `.hud`, `.afk`, `.back`, `.join`, `.leaders`,
|
||||||
`.b`, `.d`, `.w`: one page of nine commands, with full `<amount|all>` bank
|
`.b`, `.d`, `.w`: one page of nine commands, with full `<amount|all>` bank
|
||||||
syntax. `.hud` is absent when HUD is omitted. Records use right-aligned
|
syntax. `.hud` is absent when HUD is omitted. Records use right-aligned
|
||||||
`Map record · Round N` and up to three bounded holder lines, with no footer
|
`Map record | Round N` and up to three bounded holder lines, with no footer
|
||||||
and no single-page counter. Both views retain 12-second page timing.
|
and no single-page counter. Both views retain 12-second page timing.
|
||||||
Fullscreen X/Y overlay anchors use direct normalized `640 × 480` coordinates.
|
Fullscreen X/Y overlay anchors use direct normalized `640 × 480` coordinates.
|
||||||
Dynamic health fills use `make_overlay_width()` like their tracks. Apply
|
Dynamic health fills use `make_overlay_width()` like their tracks. Apply
|
||||||
@@ -112,10 +119,14 @@ Dynamic health fills use `make_overlay_width()` like their tracks. Apply
|
|||||||
`SetShader` requires an integer width. Legacy panel helpers and their `0.75`
|
`SetShader` requires an integer width. Legacy panel helpers and their `0.75`
|
||||||
horizontal conversion remain available for existing consumers. Font scale is
|
horizontal conversion remain available for existing consumers. Font scale is
|
||||||
at least `1.0`. Numeric HP appears only while injured; the core and services
|
at least `1.0`. Numeric HP appears only while injured; the core and services
|
||||||
have no panel backgrounds. There is no new Plutonium/native-plugin/backend
|
have no panel backgrounds. The counter is one `N ZOMBIES` element centered
|
||||||
dependency or full server-process restart requirement.
|
at the health track's midpoint with uniform scale `1.25`, starting as
|
||||||
|
`-- ZOMBIES`. There is no new native-plugin/backend dependency or full
|
||||||
|
server-process restart requirement when already using Plutonium r5334+.
|
||||||
Run the [shared HUD checks](../../README.md#hud-validation); portable fixtures
|
Run the [shared HUD checks](../../README.md#hud-validation); portable fixtures
|
||||||
do not replace T5 live loading, layout and lifecycle validation.
|
do not replace T5 live loading, layout and lifecycle validation. Check the
|
||||||
|
counter's centering at `0`, `6`, `9`, `10`, `99` and `100`, after `.hud`
|
||||||
|
hides/shows it, and after game over freezes the count.
|
||||||
|
|
||||||
## Module map
|
## Module map
|
||||||
|
|
||||||
|
|||||||
+22
-12
@@ -60,10 +60,13 @@ invalid identities fail privately without changing points. Follow the shared
|
|||||||
## Core HUD and service window
|
## Core HUD and service window
|
||||||
|
|
||||||
The unboxed core puts match time above round time at the upper left,
|
The unboxed core puts match time above round time at the upper left,
|
||||||
`TWS ZOMBIES · discord.tws.lol` at the top center, and zombies remaining above a
|
`TWS ZOMBIES | discord.tws.lol` at the top center, and zombies remaining above a
|
||||||
slim health bar at the bottom center. Current/maximum HP appears to the right
|
slim health bar at the bottom center. `N ZOMBIES` is one centered element at
|
||||||
of the bar only while injured; it disappears at full health. The AFK indicator
|
the health track's midpoint, using uniform font scale `1.25` so the whole group
|
||||||
sits above the zombie count. There are no panel backgrounds or timer labels.
|
stays centered as the digit count changes. Its initial placeholder is
|
||||||
|
`-- ZOMBIES`. Current/maximum HP appears to the right of the bar only while
|
||||||
|
injured; it disappears at full health. The AFK indicator sits above the zombie
|
||||||
|
count. There are no panel backgrounds or timer labels.
|
||||||
Fullscreen X/Y anchors use the normalized `640 × 480` viewport directly and
|
Fullscreen X/Y anchors use the normalized `640 × 480` viewport directly and
|
||||||
bypass safe-area insets. Font scale is at least `1.0`; verify text fit, stock
|
bypass safe-area insets. Font scale is at least `1.0`; verify text fit, stock
|
||||||
HUD overlap and Plutonium tag clearance at each aspect ratio in-game.
|
HUD overlap and Plutonium tag clearance at each aspect ratio in-game.
|
||||||
@@ -81,7 +84,7 @@ continue updating while hidden; game-over values remain frozen. Help, records
|
|||||||
and the stock HUD are unaffected. `.hud` requires the HUD module.
|
and the stock HUD are unaffected. `.hud` requires the HUD module.
|
||||||
|
|
||||||
Common provides one unboxed upper-right service area below the Plutonium tag.
|
Common provides one unboxed upper-right service area below the Plutonium tag.
|
||||||
Records are right aligned, with `Map record · Round N` followed by up to three
|
Records are right aligned, with `Map record | Round N` followed by up to three
|
||||||
holder lines; there is no replay footer or single-page counter. Help displays
|
holder lines; there is no replay footer or single-page counter. Help displays
|
||||||
the full nine-command list in aligned rows. Services remain private, leave the
|
the full nine-command list in aligned rows. Services remain private, leave the
|
||||||
core in place and work without the HUD module installed. See the
|
core in place and work without the HUD module installed. See the
|
||||||
@@ -90,12 +93,15 @@ Only the active service view holds HUD elements: 19 for nine-command help
|
|||||||
(20 with a multi-page counter), or four for records (five with a multi-page
|
(20 with a multi-page counter), or four for records (five with a multi-page
|
||||||
counter). Pages reuse elements; switching views or closing the service releases
|
counter). Pages reuse elements; switching views or closing the service releases
|
||||||
them. Idle services hold no HUD elements, while the controller retains pending
|
them. Idle services hold no HUD elements, while the controller retains pending
|
||||||
requests and interruption/resume behavior. Core + AFK use 11 + 1 elements.
|
requests and interruption/resume behavior. Core + AFK use 10 + 1 elements.
|
||||||
|
|
||||||
HUD `1.4.0` and AFK `1.2.0` require common `1.4.0+`. Update common, HUD and AFK
|
HUD `1.4.2` requires common `1.4.0+` and
|
||||||
together where installed, then restart the map. Leaderboard `1.1.0` remains
|
[Plutonium r5334+](https://plutonium.pw/docs/changelog/#r5334) for its
|
||||||
compatible. This change requires no new Plutonium/plugin/backend dependency or
|
`SetTextUnlimited` counter, updated only when the count changes. Deploy the
|
||||||
full server-process restart.
|
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. AFK `1.2.0` and leaderboard `1.1.0` remain compatible;
|
||||||
|
AFK still requires common `1.4.0+`. No new native-plugin or backend requirement.
|
||||||
|
|
||||||
## Map record announcements
|
## Map record announcements
|
||||||
|
|
||||||
@@ -263,6 +269,9 @@ connect listener anywhere else crashes the T5 SP handshake.
|
|||||||
Check that branding, the health bar, damage-only HP, zombie count and timers
|
Check that branding, the health bar, damage-only HP, zombie count and timers
|
||||||
remain correct during each view and after it closes.
|
remain correct during each view and after it closes.
|
||||||
Include other installed HUD mods; the live T5 SP element limit is unverified.
|
Include other installed HUD mods; the live T5 SP element limit is unverified.
|
||||||
|
6. Check `0`, `6`, `9`, `10`, `99` and `100 ZOMBIES` at different resolutions:
|
||||||
|
the complete text must stay centered above the health track. Repeat after
|
||||||
|
toggling `.hud` and after game over freezes the count.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -274,8 +283,8 @@ gameplay updates start through the existing spawn lifecycle.
|
|||||||
**Features**
|
**Features**
|
||||||
- Upper-left match timer above round timer (round resets each round, both freeze on game over)
|
- Upper-left match timer above round timer (round resets each round, both freeze on game over)
|
||||||
- `.hud` toggles this player's core and AFK indicator; data updates continue while hidden
|
- `.hud` toggles this player's core and AFK indicator; data updates continue while hidden
|
||||||
- Top-center `TWS ZOMBIES · discord.tws.lol` branding
|
- Top-center `TWS ZOMBIES | discord.tws.lol` branding
|
||||||
- Bottom-center zombies-remaining counter, with the AFK indicator above it
|
- One `N ZOMBIES` element centered above the health track, with the AFK indicator above it
|
||||||
- Slim health bar with green / amber / red ramp; current/maximum HP appears
|
- Slim health bar with green / amber / red ramp; current/maximum HP appears
|
||||||
beside it only while injured
|
beside it only while injured
|
||||||
- Spectator placeholders without starting the match clock on connection
|
- Spectator placeholders without starting the match clock on connection
|
||||||
@@ -301,6 +310,7 @@ without seeding clocks; `hud_start` calls it as a fallback if needed.
|
|||||||
`destroy_on_disconnect`, `format_mmss`, `register_help_command`,
|
`destroy_on_disconnect`, `format_mmss`, `register_help_command`,
|
||||||
`register_spawn_handler`, `register_connect_handler`
|
`register_spawn_handler`, `register_connect_handler`
|
||||||
- Every `level.hud_*` config value from `tws_t5_common`
|
- Every `level.hud_*` config value from `tws_t5_common`
|
||||||
|
- Plutonium r5334+ for the count's `SetTextUnlimited` updates
|
||||||
- Stock BO1 state: `level.round_number`, `level.zombie_total`,
|
- Stock BO1 state: `level.round_number`, `level.zombie_total`,
|
||||||
`level.players`, `self.health`, `self.maxhealth`, AI entities' `.animname`
|
`level.players`, `self.health`, `self.maxhealth`, AI entities' `.animname`
|
||||||
and `.ignore_enemy_count`
|
and `.ignore_enemy_count`
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ service_render(state)
|
|||||||
{
|
{
|
||||||
header = "No recorded high round yet";
|
header = "No recorded high round yet";
|
||||||
if (snapshot.round > 0)
|
if (snapshot.round > 0)
|
||||||
header = "Map record · Round " + snapshot.round;
|
header = "Map record | Round " + snapshot.round;
|
||||||
}
|
}
|
||||||
if (!isDefined(state.record_header_text) || state.record_header_text != header)
|
if (!isDefined(state.record_header_text) || state.record_header_text != header)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ hud_connect()
|
|||||||
self.tws_hud_round = self hud_text(level.hud_overlay_timer_x,
|
self.tws_hud_round = self hud_text(level.hud_overlay_timer_x,
|
||||||
level.hud_overlay_round_y, "left", 1.25, level.hud_c_dim, "--:--");
|
level.hud_overlay_round_y, "left", 1.25, level.hud_c_dim, "--:--");
|
||||||
self hud_text(level.hud_overlay_brand_x, level.hud_overlay_brand_y,
|
self hud_text(level.hud_overlay_brand_x, level.hud_overlay_brand_y,
|
||||||
"center", 1.1, level.hud_c_dim, "TWS ZOMBIES · discord.tws.lol");
|
"center", 1.1, level.hud_c_dim, "TWS ZOMBIES | discord.tws.lol");
|
||||||
|
|
||||||
self.tws_hud_count = self hud_text(level.hud_overlay_count_x,
|
// Center the entire readout on the track, regardless of the digit count.
|
||||||
level.hud_overlay_count_y, "right", 1.5, level.hud_c_count, "--");
|
count_x = level.hud_overlay_bar_x + level.hud_overlay_bar_width / 2.0;
|
||||||
self hud_text(level.hud_overlay_count_label_x, level.hud_overlay_count_label_y,
|
self.tws_hud_count = self hud_text(count_x,
|
||||||
"left", 1.05, level.hud_c_dim, "ZOMBIES");
|
level.hud_overlay_count_y, "center", 1.25, level.hud_c_count, "-- ZOMBIES");
|
||||||
|
|
||||||
// Independent numeric elements avoid a new config string on every tick.
|
// Independent numeric elements avoid a new config string on every tick.
|
||||||
// The readout appears only after a valid damaged-health sample.
|
// The readout appears only after a valid damaged-health sample.
|
||||||
@@ -233,7 +233,8 @@ hud_zombie_counter()
|
|||||||
|
|
||||||
if (remaining != last)
|
if (remaining != last)
|
||||||
{
|
{
|
||||||
count SetValue(remaining);
|
// Plutonium r5334+: reuse text storage as the count changes.
|
||||||
|
count SetTextUnlimited(int(remaining) + " ZOMBIES");
|
||||||
last = remaining;
|
last = remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-7
@@ -43,7 +43,7 @@ view: 19 nonarchived elements for nine-command help (20 with a multi-page
|
|||||||
counter), or four for records (five with a multi-page counter). Closing the
|
counter), or four for records (five with a multi-page counter). Closing the
|
||||||
service or switching kinds destroys the view; idle holds no elements. Pages
|
service or switching kinds destroys the view; idle holds no elements. Pages
|
||||||
reuse the active elements, separately from controller and pending-request state.
|
reuse the active elements, separately from controller and pending-request state.
|
||||||
Core + AFK use 11 + 1 archived elements. Hidden elements still occupy slots.
|
Core + AFK use 10 + 1 archived elements. Hidden elements still occupy slots.
|
||||||
The [T6 player HUD structure](https://github.com/skiff/t6-gsc-helper/blob/50ff6cb7397c9376894a195f48118d1743812d1d/src/game/structs.hpp#L184)
|
The [T6 player HUD structure](https://github.com/skiff/t6-gsc-helper/blob/50ff6cb7397c9376894a195f48118d1743812d1d/src/game/structs.hpp#L184)
|
||||||
has 31 slots in each bucket; it does not establish current Plutonium stock HUD
|
has 31 slots in each bucket; it does not establish current Plutonium stock HUD
|
||||||
usage. Keep the live overlap/element-budget checks alongside the portable checks.
|
usage. Keep the live overlap/element-budget checks alongside the portable checks.
|
||||||
@@ -54,18 +54,24 @@ creation must not seed the match clock. The unboxed layout has timers at the
|
|||||||
upper left, branding at top center and zombies/health at bottom center. HP
|
upper left, branding at top center and zombies/health at bottom center. HP
|
||||||
numbers appear to the right of the bar only while injured; AFK sits above the
|
numbers appear to the right of the bar only while injured; AFK sits above the
|
||||||
zombie count. Services never reposition the core. Records are right aligned,
|
zombie count. Services never reposition the core. Records are right aligned,
|
||||||
with `Map record · Round N` followed by up to three bounded holder lines, no
|
with `Map record | Round N` followed by up to three bounded holder lines, no
|
||||||
footer and no single-page counter. Their 12-second page timing is unchanged.
|
footer and no single-page counter. Their 12-second page timing is unchanged.
|
||||||
|
|
||||||
HUD `1.4.0` and AFK `1.2.0` require matching common `1.4.0+`; update all three
|
HUD `1.4.2` requires matching common `1.4.0+` and
|
||||||
where installed and restart the map. Leaderboard `1.1.0` remains compatible.
|
[Plutonium r5334+](https://plutonium.pw/docs/changelog/#r5334) for
|
||||||
|
`SetTextUnlimited`. The `N ZOMBIES` counter is one element centered at the
|
||||||
|
health track's midpoint with uniform scale `1.25`, starting as `-- ZOMBIES`.
|
||||||
|
Only changed counts update its text; numeric HP retains `SetValue`. On r5334+,
|
||||||
|
update the HUD script and restart the map. Older Plutonium clients and servers
|
||||||
|
need updates and full restarts first. AFK `1.2.0` and leaderboard `1.1.0` remain
|
||||||
|
compatible; AFK still requires common `1.4.0+`.
|
||||||
Overlay helpers use direct normalized `640 × 480` coordinates. Dynamic health
|
Overlay helpers use direct normalized `640 × 480` coordinates. Dynamic health
|
||||||
fills use `make_overlay_width()` like their tracks. Apply `int()` after `max()`
|
fills use `make_overlay_width()` like their tracks. Apply `int()` after `max()`
|
||||||
in that helper: native `max()` returns a float, but `SetShader` requires an
|
in that helper: native `max()` returns a float, but `SetShader` requires an
|
||||||
integer width. Legacy panel helpers retain their `0.75` horizontal conversion
|
integer width. Legacy panel helpers retain their `0.75` horizontal conversion
|
||||||
for existing consumers. Font scale is at least `1.0`. This introduces no
|
for existing consumers. Font scale is at least `1.0`. There is no new
|
||||||
Plutonium/native-plugin/backend dependency or full server-process restart
|
native-plugin/backend dependency or full server-process restart requirement
|
||||||
requirement.
|
when already using Plutonium r5334+.
|
||||||
|
|
||||||
HUD owns the exact, case-insensitive `.hud` chat command and registers it in
|
HUD owns the exact, case-insensitive `.hud` chat command and registers it in
|
||||||
help only while installed. Common's core wrappers preserve each element's
|
help only while installed. Common's core wrappers preserve each element's
|
||||||
@@ -195,6 +201,8 @@ Live checks after deploying:
|
|||||||
spectator placeholders without an early clock start. Check damage-only HP,
|
spectator placeholders without an early clock start. Check damage-only HP,
|
||||||
health/Juggernog changes, AFK placement and timers frozen at game over.
|
health/Juggernog changes, AFK placement and timers frozen at game over.
|
||||||
Test safe-area settings and different resolutions against stock HUD elements.
|
Test safe-area settings and different resolutions against stock HUD elements.
|
||||||
|
Check `0`, `6`, `9`, `10`, `99` and `100 ZOMBIES` remain centered above the
|
||||||
|
health track, including after `.hud` toggles and game over freezes the count.
|
||||||
2. `.afk` blocks movement, shooting and grenades; `.back` restores them. Try rapid
|
2. `.afk` blocks movement, shooting and grenades; `.back` restores them. Try rapid
|
||||||
toggles, a round transition, death/respawn and game over.
|
toggles, a round transition, death/respawn and game over.
|
||||||
3. Lock fires at the threshold and clears on game over/restart. HUD must be installed.
|
3. Lock fires at the threshold and clears on game over/restart. HUD must be installed.
|
||||||
|
|||||||
+17
-11
@@ -8,7 +8,7 @@ private server token.
|
|||||||
| File | Role | Dependencies |
|
| File | Role | Dependencies |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `tws_t6_common.gsc` | Shared HUD values, helpers, paginated `.help`, service window, connection and first-spawn dispatchers | Stock T6 |
|
| `tws_t6_common.gsc` | Shared HUD values, helpers, paginated `.help`, service window, connection and first-spawn dispatchers | Stock T6 |
|
||||||
| `tws_t6_hud.gsc` | Unboxed timers, branding, zombies/health and per-player `.hud` visibility toggle | Common 1.4.0+ |
|
| `tws_t6_hud.gsc` | Unboxed timers, branding, zombies/health and per-player `.hud` visibility toggle | Common 1.4.0+, Plutonium r5334+ |
|
||||||
| `tws_t6_afk.gsc` | `.afk` / `.back`, protection, movement and weapon blocking | Common 1.4.0+ |
|
| `tws_t6_afk.gsc` | `.afk` / `.back`, protection, movement and weapon blocking | Common 1.4.0+ |
|
||||||
| `tws_t6_lock.gsc` | Lock at round 20; clear password on game over | Common, HUD |
|
| `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_telemetry.gsc` | Async queued game reports (game_start/heartbeat/round_change/game_end) | tws-t6-gsc-utils |
|
||||||
@@ -73,10 +73,13 @@ populates that field before the `connected` notification. Follow the shared
|
|||||||
## Core HUD and service window
|
## Core HUD and service window
|
||||||
|
|
||||||
The unboxed core puts match time above round time at the upper left,
|
The unboxed core puts match time above round time at the upper left,
|
||||||
`TWS ZOMBIES · discord.tws.lol` at the top center, and zombies remaining above a
|
`TWS ZOMBIES | discord.tws.lol` at the top center, and zombies remaining above a
|
||||||
slim health bar at the bottom center. Current/maximum HP appears to the right
|
slim health bar at the bottom center. `N ZOMBIES` is one centered element at
|
||||||
of the bar only while injured; it disappears at full health. The AFK indicator
|
the health track's midpoint, using uniform font scale `1.25` so the whole group
|
||||||
sits above the zombie count. There are no panel backgrounds or timer labels.
|
stays centered as the digit count changes. Its initial placeholder is
|
||||||
|
`-- ZOMBIES`. Current/maximum HP appears to the right of the bar only while
|
||||||
|
injured; it disappears at full health. The AFK indicator sits above the zombie
|
||||||
|
count. There are no panel backgrounds or timer labels.
|
||||||
Fullscreen X/Y anchors use the normalized `640 × 480` viewport directly and
|
Fullscreen X/Y anchors use the normalized `640 × 480` viewport directly and
|
||||||
bypass safe-area insets. Font scale is at least `1.0`; verify text fit, stock
|
bypass safe-area insets. Font scale is at least `1.0`; verify text fit, stock
|
||||||
HUD overlap and Plutonium tag clearance at each aspect ratio in-game.
|
HUD overlap and Plutonium tag clearance at each aspect ratio in-game.
|
||||||
@@ -94,7 +97,7 @@ continue updating while hidden; game-over values remain frozen. Help, records
|
|||||||
and the stock HUD are unaffected. `.hud` requires the HUD module.
|
and the stock HUD are unaffected. `.hud` requires the HUD module.
|
||||||
|
|
||||||
Common provides one unboxed upper-right service area below the Plutonium tag.
|
Common provides one unboxed upper-right service area below the Plutonium tag.
|
||||||
Records are right aligned, with `Map record · Round N` followed by up to three
|
Records are right aligned, with `Map record | Round N` followed by up to three
|
||||||
holder lines; there is no replay footer or single-page counter. Help displays
|
holder lines; there is no replay footer or single-page counter. Help displays
|
||||||
the full nine-command list in aligned rows. Services remain private, leave the
|
the full nine-command list in aligned rows. Services remain private, leave the
|
||||||
core in place and work without the HUD module installed. See the
|
core in place and work without the HUD module installed. See the
|
||||||
@@ -103,12 +106,15 @@ Only the active service view holds HUD elements: 19 for nine-command help
|
|||||||
(20 with a multi-page counter), or four for records (five with a multi-page
|
(20 with a multi-page counter), or four for records (five with a multi-page
|
||||||
counter). Pages reuse elements; switching views or closing the service releases
|
counter). Pages reuse elements; switching views or closing the service releases
|
||||||
them. Idle services hold no HUD elements, while the controller retains pending
|
them. Idle services hold no HUD elements, while the controller retains pending
|
||||||
requests and interruption/resume behavior. Core + AFK use 11 + 1 elements.
|
requests and interruption/resume behavior. Core + AFK use 10 + 1 elements.
|
||||||
|
|
||||||
HUD `1.4.0` and AFK `1.2.0` require common `1.4.0+`. Update common, HUD and AFK
|
HUD `1.4.2` requires common `1.4.0+` and
|
||||||
together where installed, then restart the map. Leaderboard `1.1.0` remains
|
[Plutonium r5334+](https://plutonium.pw/docs/changelog/#r5334) for its
|
||||||
compatible. This change requires no new Plutonium/plugin/backend dependency or
|
`SetTextUnlimited` counter, updated only when the count changes. Deploy the
|
||||||
full server-process restart.
|
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. AFK `1.2.0` and leaderboard `1.1.0` remain compatible;
|
||||||
|
AFK still requires common `1.4.0+`. No new native-plugin or backend requirement.
|
||||||
|
|
||||||
## Map record announcements
|
## Map record announcements
|
||||||
|
|
||||||
|
|||||||
@@ -453,7 +453,7 @@ service_render(state)
|
|||||||
{
|
{
|
||||||
header = "No recorded high round yet";
|
header = "No recorded high round yet";
|
||||||
if (snapshot.round > 0)
|
if (snapshot.round > 0)
|
||||||
header = "Map record · Round " + snapshot.round;
|
header = "Map record | Round " + snapshot.round;
|
||||||
}
|
}
|
||||||
if (!isDefined(state.record_header_text) || state.record_header_text != header)
|
if (!isDefined(state.record_header_text) || state.record_header_text != header)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,12 +54,12 @@ hud_connect()
|
|||||||
self.tws_hud_round = self hud_text(level.hud_overlay_timer_x,
|
self.tws_hud_round = self hud_text(level.hud_overlay_timer_x,
|
||||||
level.hud_overlay_round_y, "left", 1.25, level.hud_c_dim, "--:--");
|
level.hud_overlay_round_y, "left", 1.25, level.hud_c_dim, "--:--");
|
||||||
self hud_text(level.hud_overlay_brand_x, level.hud_overlay_brand_y,
|
self hud_text(level.hud_overlay_brand_x, level.hud_overlay_brand_y,
|
||||||
"center", 1.1, level.hud_c_dim, "TWS ZOMBIES · discord.tws.lol");
|
"center", 1.1, level.hud_c_dim, "TWS ZOMBIES | discord.tws.lol");
|
||||||
|
|
||||||
self.tws_hud_count = self hud_text(level.hud_overlay_count_x,
|
// Center the entire readout on the track, regardless of the digit count.
|
||||||
level.hud_overlay_count_y, "right", 1.5, level.hud_c_count, "--");
|
count_x = level.hud_overlay_bar_x + level.hud_overlay_bar_width / 2.0;
|
||||||
self hud_text(level.hud_overlay_count_label_x, level.hud_overlay_count_label_y,
|
self.tws_hud_count = self hud_text(count_x,
|
||||||
"left", 1.05, level.hud_c_dim, "ZOMBIES");
|
level.hud_overlay_count_y, "center", 1.25, level.hud_c_count, "-- ZOMBIES");
|
||||||
|
|
||||||
// Independent numeric elements avoid a new config string on every tick.
|
// Independent numeric elements avoid a new config string on every tick.
|
||||||
// The readout appears only after a valid damaged-health sample.
|
// The readout appears only after a valid damaged-health sample.
|
||||||
@@ -239,7 +239,8 @@ hud_zombie_counter()
|
|||||||
|
|
||||||
if (remaining != last)
|
if (remaining != last)
|
||||||
{
|
{
|
||||||
count SetValue(remaining);
|
// Plutonium r5334+: reuse text storage as the count changes.
|
||||||
|
count SetTextUnlimited(int(remaining) + " ZOMBIES");
|
||||||
last = remaining;
|
last = remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+47
-15
@@ -26,11 +26,12 @@ function install(context, source, names) {
|
|||||||
const locals = [...new Set([...code.matchAll(/(?<![\w.])(\w+)\s*(?:=(?!=)|\+\+|--)/g)].map(match => match[1]))];
|
const locals = [...new Set([...code.matchAll(/(?<![\w.])(\w+)\s*(?:=(?!=)|\+\+|--)/g)].map(match => match[1]))];
|
||||||
code = code.replace(/(?:self|level) endon\([^;]+;/g, '')
|
code = code.replace(/(?:self|level) endon\([^;]+;/g, '')
|
||||||
.replace(/scripts\\(?:sp|zm)\\tws_t[56]_common::/g, 'common.')
|
.replace(/scripts\\(?:sp|zm)\\tws_t[56]_common::/g, 'common.')
|
||||||
|
.replace(/maps\\mp\\zombies\\_zm_utility::get_current_zombie_count/g, 'get_enemy_count')
|
||||||
.replace(/self thread ([\w.]+)\(([^;]*)\);/g, (_, fn, values) => `queued.push(['${fn}', ${values}]);`)
|
.replace(/self thread ([\w.]+)\(([^;]*)\);/g, (_, fn, values) => `queued.push(['${fn}', ${values}]);`)
|
||||||
.replace(/self common\.toggle_core_hud\(\)/g, 'common.toggle_core_hud()')
|
.replace(/self common\.toggle_core_hud\(\)/g, 'common.toggle_core_hud()')
|
||||||
.replace(/self ((?:hud_|afk_)\w+)\(/g, '$1(')
|
.replace(/self ((?:hud_|afk_)\w+)\(/g, '$1(')
|
||||||
.replace(/self (\w+)\(/g, 'self.$1(')
|
.replace(/self (\w+)\(/g, 'self.$1(')
|
||||||
.replace(/(\w+) (SetText|SetValue|SetTimerUp|SetShader)\(([^;]*)\);/g, '$1.$2($3);')
|
.replace(/(\w+) (SetText|SetTextUnlimited|SetValue|SetTimerUp|SetShader)\(([^;]*)\);/g, '$1.$2($3);')
|
||||||
.replace(/level notify\(([^;]*)\);/g, 'level.notify($1);')
|
.replace(/level notify\(([^;]*)\);/g, 'level.notify($1);')
|
||||||
.replace(/level waittill\(([^;]*)\);/g, 'yield { event: $1 };')
|
.replace(/level waittill\(([^;]*)\);/g, 'yield { event: $1 };')
|
||||||
.replace(/\.size\b/g, '.length')
|
.replace(/\.size\b/g, '.length')
|
||||||
@@ -51,6 +52,7 @@ function engine(game, source, afk, hidden = false) {
|
|||||||
function element(args, alpha = 1) {
|
function element(args, alpha = 1) {
|
||||||
const result = { args, alpha, writes: [],
|
const result = { args, alpha, writes: [],
|
||||||
SetText(value) { this.text = value; this.writes.push(['text', value]); },
|
SetText(value) { this.text = value; this.writes.push(['text', value]); },
|
||||||
|
SetTextUnlimited(value) { this.text = value; this.writes.push(['unlimitedText', value]); },
|
||||||
SetValue(value) { this.value = value; this.writes.push(['value', value]); },
|
SetValue(value) { this.value = value; this.writes.push(['value', value]); },
|
||||||
SetShader(...args) { this.shader = args; },
|
SetShader(...args) { this.shader = args; },
|
||||||
SetTimerUp(value) { this.timer = value; this.writes.push(['timer', value]); },
|
SetTimerUp(value) { this.timer = value; this.writes.push(['timer', value]); },
|
||||||
@@ -79,7 +81,7 @@ function engine(game, source, afk, hidden = false) {
|
|||||||
player_active: player => player.active,
|
player_active: player => player.active,
|
||||||
format_mmss: ms => `${Math.floor(ms / 60000)}:${String(Math.floor(ms / 1000) % 60).padStart(2, '0')}`,
|
format_mmss: ms => `${Math.floor(ms / 60000)}:${String(Math.floor(ms / 1000) % 60).padStart(2, '0')}`,
|
||||||
};
|
};
|
||||||
const level = { round_number: 1, hud_rate: .1, afk_grace_seconds: 2,
|
const level = { round_number: 1, hud_rate: .1, afk_grace_seconds: 2, zombie_team: 'axis',
|
||||||
hud_overlay_timer_x: 6, hud_overlay_match_y: 8, hud_overlay_round_y: 30,
|
hud_overlay_timer_x: 6, hud_overlay_match_y: 8, hud_overlay_round_y: 30,
|
||||||
hud_overlay_brand_x: 320, hud_overlay_brand_y: 8,
|
hud_overlay_brand_x: 320, hud_overlay_brand_y: 8,
|
||||||
hud_overlay_count_x: 300, hud_overlay_count_y: 440,
|
hud_overlay_count_x: 300, hud_overlay_count_y: 440,
|
||||||
@@ -92,10 +94,11 @@ function engine(game, source, afk, hidden = false) {
|
|||||||
hud_c_count: 'white', hud_c_dim: 'dim', hud_c_afk: 'yellow',
|
hud_c_count: 'white', hud_c_dim: 'dim', hud_c_afk: 'yellow',
|
||||||
hud_c_high: 'green', hud_c_mid: 'yellow', hud_c_low: 'red', notify() {} };
|
hud_c_high: 'green', hud_c_mid: 'yellow', hud_c_low: 'red', notify() {} };
|
||||||
const context = vm.createContext({ self, level, common, queued, getTime: () => now,
|
const context = vm.createContext({ self, level, common, queued, getTime: () => now,
|
||||||
|
get_enemy_count: () => level.fixture_enemies ?? 0,
|
||||||
isDefined: value => value !== undefined && value !== null, is_true: value => !!value,
|
isDefined: value => value !== undefined && value !== null, is_true: value => !!value,
|
||||||
int: Math.trunc, max: Math.max });
|
int: Math.trunc, max: Math.max });
|
||||||
install(context, source, ['hud_connect', 'hud_text', 'hud_start', 'hud_toggle', 'hud_timer_row',
|
install(context, source, ['hud_connect', 'hud_text', 'hud_start', 'hud_toggle', 'hud_timer_row',
|
||||||
'hud_health_display', 'freeze_timers_on_game_over']);
|
'hud_health_display', 'hud_zombie_counter', 'freeze_timers_on_game_over']);
|
||||||
install(context, afk, ['afk_spawn', 'afk_enter', 'afk_exit',
|
install(context, afk, ['afk_spawn', 'afk_enter', 'afk_exit',
|
||||||
...(game === 't6' ? ['afk_release', 'afk_lock_controls', 'afk_restore_controls'] : [])]);
|
...(game === 't6' ? ['afk_release', 'afk_lock_controls', 'afk_restore_controls'] : [])]);
|
||||||
return { context, self, level, queued, elems, actions, common,
|
return { context, self, level, queued, elems, actions, common,
|
||||||
@@ -118,7 +121,7 @@ for (const game of ['t5', 't6']) {
|
|||||||
assert(chat.includes('player thread hud_toggle()'), 'disconnect scope stays in the player worker');
|
assert(chat.includes('player thread hud_toggle()'), 'disconnect scope stays in the player worker');
|
||||||
assert(!body(source, 'hud_connect')[1].includes('getTime()'));
|
assert(!body(source, 'hud_connect')[1].includes('getTime()'));
|
||||||
assert(!source.includes('tws_hud_hidden ='), 'spawn/construction must not reset the preference');
|
assert(!source.includes('tws_hud_hidden ='), 'spawn/construction must not reset the preference');
|
||||||
for (const name of ['round_watcher', 'reset_timer_each_round', 'match_timer_bind'])
|
for (const name of ['round_watcher', 'reset_timer_each_round', 'match_timer_bind', 'hud_zombie_counter'])
|
||||||
assert(body(source, name)[1].includes('level endon("hud_game_over")'));
|
assert(body(source, name)[1].includes('level endon("hud_game_over")'));
|
||||||
const bind = body(source, 'match_timer_bind')[1];
|
const bind = body(source, 'match_timer_bind')[1];
|
||||||
if (game === 't5') assert(bind.includes('elem SetTimerUp(0)'));
|
if (game === 't5') assert(bind.includes('elem SetTimerUp(0)'));
|
||||||
@@ -127,13 +130,13 @@ for (const game of ['t5', 't6']) {
|
|||||||
assert(afk.includes('level.hud_overlay_afk_y'));
|
assert(afk.includes('level.hud_overlay_afk_y'));
|
||||||
assert(!/\.alpha\s*=/.test(afk), 'AFK visibility must use the common mask');
|
assert(!/\.alpha\s*=/.test(afk), 'AFK visibility must use the common mask');
|
||||||
});
|
});
|
||||||
check(`${game}: connection places 11 box-free core elements at the screen edges without seeding clocks`, () => {
|
check(`${game}: connection places 10 box-free core elements at the screen edges without seeding clocks`, () => {
|
||||||
const e = make(); e.context.hud_connect();
|
const e = make(); e.context.hud_connect();
|
||||||
assert.equal(e.level.match_start_time, undefined); assert.equal(e.level.round_start_time, undefined);
|
assert.equal(e.level.match_start_time, undefined); assert.equal(e.level.round_start_time, undefined);
|
||||||
assert.equal(e.self.tws_hud_match.text, '--:--'); assert.equal(e.self.tws_hud_health.text, '--');
|
assert.equal(e.self.tws_hud_match.text, '--:--'); assert.equal(e.self.tws_hud_health.text, '--');
|
||||||
assert.equal(e.elems.length, 11);
|
assert.equal(e.elems.length, 10);
|
||||||
assert.equal(e.self.tws_core_hud_elems.length, 11);
|
assert.equal(e.self.tws_core_hud_elems.length, 10);
|
||||||
e.context.hud_connect(); assert.equal(e.elems.length, 11);
|
e.context.hud_connect(); assert.equal(e.elems.length, 10);
|
||||||
assert.equal(e.queued.length, 1); assert.equal(e.queued[0][0], 'common.destroy_on_disconnect');
|
assert.equal(e.queued.length, 1); assert.equal(e.queued[0][0], 'common.destroy_on_disconnect');
|
||||||
assert.equal(e.queued[0][1].length, e.elems.length);
|
assert.equal(e.queued[0][1].length, e.elems.length);
|
||||||
const textAt = (text, x, y, align, scale, colour) => {
|
const textAt = (text, x, y, align, scale, colour) => {
|
||||||
@@ -142,9 +145,9 @@ for (const game of ['t5', 't6']) {
|
|||||||
};
|
};
|
||||||
assert.deepEqual(e.self.tws_hud_match.args.slice(1), [6, 8, 'left', 1.4, 'white']);
|
assert.deepEqual(e.self.tws_hud_match.args.slice(1), [6, 8, 'left', 1.4, 'white']);
|
||||||
assert.deepEqual(e.self.tws_hud_round.args.slice(1), [6, 30, 'left', 1.25, 'dim']);
|
assert.deepEqual(e.self.tws_hud_round.args.slice(1), [6, 30, 'left', 1.25, 'dim']);
|
||||||
textAt('TWS ZOMBIES · discord.tws.lol', 320, 8, 'center', 1.1, 'dim');
|
textAt('TWS ZOMBIES | discord.tws.lol', 320, 8, 'center', 1.1, 'dim');
|
||||||
assert.deepEqual(e.self.tws_hud_count.args.slice(1), [300, 440, 'right', 1.5, 'white']);
|
textAt('-- ZOMBIES', 320, 440, 'center', 1.25, 'white');
|
||||||
textAt('ZOMBIES', 306, 446, 'left', 1.05, 'dim');
|
assert(!e.elems.some(elem => elem.text === 'ZOMBIES'), 'count and label share one alignment');
|
||||||
textAt('HP', 449, 460, 'left', 1, 'white');
|
textAt('HP', 449, 460, 'left', 1, 'white');
|
||||||
for (const [key, x, align] of [['health', 414, 'right'], ['slash', 418, 'left'], ['maxhealth', 445, 'right']]) {
|
for (const [key, x, align] of [['health', 414, 'right'], ['slash', 418, 'left'], ['maxhealth', 445, 'right']]) {
|
||||||
const elem = e.self[`tws_hud_${key}`];
|
const elem = e.self[`tws_hud_${key}`];
|
||||||
@@ -165,13 +168,42 @@ for (const game of ['t5', 't6']) {
|
|||||||
assert.equal(e.level.match_start_time, undefined); assert.equal(e.level.round_start_time, undefined);
|
assert.equal(e.level.match_start_time, undefined); assert.equal(e.level.round_start_time, undefined);
|
||||||
assert.equal(e.queued.length, 1, 'toggle only queues disconnect cleanup');
|
assert.equal(e.queued.length, 1, 'toggle only queues disconnect cleanup');
|
||||||
e.context.hud_start(); e.context.hud_start();
|
e.context.hud_start(); e.context.hud_start();
|
||||||
assert.equal(e.elems.length, 11); assert.equal(e.self.tws_hud_hidden, true);
|
assert.equal(e.elems.length, 10); assert.equal(e.self.tws_hud_hidden, true);
|
||||||
assert.deepEqual(e.queued.slice(1).map(item => item[0]), ['hud_timer_row', 'hud_zombie_counter', 'hud_health_display']);
|
assert.deepEqual(e.queued.slice(1).map(item => item[0]), ['hud_timer_row', 'hud_zombie_counter', 'hud_health_display']);
|
||||||
e.context.hud_timer_row();
|
e.context.hud_timer_row();
|
||||||
assert.equal(e.level.match_start_time, 123456); assert.equal(e.level.round_start_time, 123456);
|
assert.equal(e.level.match_start_time, 123456); assert.equal(e.level.round_start_time, 123456);
|
||||||
const threads = e.queued.length;
|
const threads = e.queued.length;
|
||||||
e.context.hud_toggle(); e.context.hud_toggle(); e.context.hud_start();
|
e.context.hud_toggle(); e.context.hud_toggle(); e.context.hud_start();
|
||||||
assert.equal(e.queued.length, threads); assert.equal(e.elems.length, 11);
|
assert.equal(e.queued.length, threads); assert.equal(e.elems.length, 10);
|
||||||
|
});
|
||||||
|
check(`${game}: complete zombie text stays centered through digit changes and hidden updates without config-string churn`, () => {
|
||||||
|
const e = make();
|
||||||
|
// Center on the configured track, including non-default and fractional midpoints.
|
||||||
|
e.level.hud_overlay_bar_x = 210;
|
||||||
|
e.level.hud_overlay_bar_width = 155;
|
||||||
|
e.context.hud_connect();
|
||||||
|
const count = e.self.tws_hud_count;
|
||||||
|
assert.deepEqual(count.args.slice(1, 4), [287.5, 440, 'center']);
|
||||||
|
const run = e.context.hud_zombie_counter();
|
||||||
|
for (const value of [0, 6, 9, 10, 99, 100, 999, 1000, 6]) {
|
||||||
|
e.level.fixture_enemies = value;
|
||||||
|
run.next();
|
||||||
|
assert.equal(count.text, `${value} ZOMBIES`);
|
||||||
|
assert.deepEqual(count.args.slice(1, 4), [287.5, 440, 'center']);
|
||||||
|
const writes = count.writes.length;
|
||||||
|
run.next(); assert.equal(count.writes.length, writes, 'unchanged counts do not rewrite text');
|
||||||
|
}
|
||||||
|
e.level.zombie_total = 94; run.next();
|
||||||
|
assert.equal(count.text, '100 ZOMBIES', 'remaining spawn count remains included');
|
||||||
|
e.context.hud_toggle();
|
||||||
|
e.level.zombie_total = -4; run.next();
|
||||||
|
assert.equal(count.text, '6 ZOMBIES'); assert.equal(count.alpha, 0);
|
||||||
|
e.context.hud_toggle();
|
||||||
|
assert.equal(count.text, '6 ZOMBIES'); assert.equal(count.alpha, 1);
|
||||||
|
assert.equal(count.writes.filter(([kind]) => kind === 'text').length, 1,
|
||||||
|
'only the fixed placeholder uses a normal config string');
|
||||||
|
assert(count.writes.slice(1).every(([kind]) => kind === 'unlimitedText'));
|
||||||
|
assert.equal(e.elems.length, 10, 'updates and toggles never allocate replacement elements');
|
||||||
});
|
});
|
||||||
check(`${game}: health numbers appear only while damaged, follow the bar colour, and avoid config-string churn`, () => {
|
check(`${game}: health numbers appear only while damaged, follow the bar colour, and avoid config-string churn`, () => {
|
||||||
const e = make(); e.context.hud_connect(); const { self } = e;
|
const e = make(); e.context.hud_connect(); const { self } = e;
|
||||||
@@ -216,7 +248,7 @@ for (const game of ['t5', 't6']) {
|
|||||||
e.context.hud_toggle();
|
e.context.hud_toggle();
|
||||||
assert.equal(e.self.tws_hud_bar.alpha, 1); assert.equal(e.self.tws_hud_health.value, 50);
|
assert.equal(e.self.tws_hud_bar.alpha, 1); assert.equal(e.self.tws_hud_health.value, 50);
|
||||||
assert.equal(other.self.tws_hud_hidden, undefined); assert.equal(other.self.tws_hud_health.alpha, 0);
|
assert.equal(other.self.tws_hud_hidden, undefined); assert.equal(other.self.tws_hud_health.alpha, 0);
|
||||||
assert.equal(e.elems.length, 11); assert.equal(e.queued.length, 1);
|
assert.equal(e.elems.length, 10); assert.equal(e.queued.length, 1);
|
||||||
e.context.hud_toggle(); e.self.health = 100; run.next(); e.context.hud_toggle();
|
e.context.hud_toggle(); e.self.health = 100; run.next(); e.context.hud_toggle();
|
||||||
assert.equal(e.self.tws_hud_health.alpha, 0, 'healing while hidden conceals the readout when shown');
|
assert.equal(e.self.tws_hud_health.alpha, 0, 'healing while hidden conceals the readout when shown');
|
||||||
assert.equal(e.self.tws_hud_hp.alpha, 0); assert.equal(e.self.tws_hud_bar.alpha, 1);
|
assert.equal(e.self.tws_hud_hp.alpha, 0); assert.equal(e.self.tws_hud_bar.alpha, 1);
|
||||||
@@ -254,7 +286,7 @@ for (const game of ['t5', 't6']) {
|
|||||||
e.context.hud_toggle(); assert.equal(indicator.alpha, 0);
|
e.context.hud_toggle(); assert.equal(indicator.alpha, 0);
|
||||||
exit.next(); assert.equal(e.self.ignoreme, false);
|
exit.next(); assert.equal(e.self.ignoreme, false);
|
||||||
assert(e.actions.some(([name]) => name === 'disableInvulnerability'));
|
assert(e.actions.some(([name]) => name === 'disableInvulnerability'));
|
||||||
assert.equal(e.elems.length, 12);
|
assert.equal(e.elems.length, 11);
|
||||||
});
|
});
|
||||||
if (game === 't6') check('t6: AFK release while hidden respects stock protection ownership', () => {
|
if (game === 't6') check('t6: AFK release while hidden respects stock protection ownership', () => {
|
||||||
const e = make(); e.context.hud_connect(); const watcher = e.spawnAfk();
|
const e = make(); e.context.hud_connect(); const watcher = e.spawnAfk();
|
||||||
|
|||||||
+13
-12
@@ -45,7 +45,7 @@ function translate(source) {
|
|||||||
.replace(/(self|level) notify\(([^;]+)\);/g, 'emit($1, $2);')
|
.replace(/(self|level) notify\(([^;]+)\);/g, 'emit($1, $2);')
|
||||||
.replace(/self (\w+)\(/g, '$1(')
|
.replace(/self (\w+)\(/g, '$1(')
|
||||||
.replace(/\bwait ([\d.]+);/g, 'yield { milliseconds: $1 * 1000 };')
|
.replace(/\bwait ([\d.]+);/g, 'yield { milliseconds: $1 * 1000 };')
|
||||||
.replace(/([\w.]+(?:\[[^\]\n]+\])?)\s+(SetText|SetValue|SetShader|Destroy)\(/g, '$1.$2(')
|
.replace(/([\w.]+(?:\[[^\]\n]+\])?)\s+(SetText|SetTextUnlimited|SetValue|SetShader|Destroy)\(/g, '$1.$2(')
|
||||||
.replace(/__literal_(\d+)__/g, (_, i) => literals[Number(i)]);
|
.replace(/__literal_(\d+)__/g, (_, i) => literals[Number(i)]);
|
||||||
result.push(`function${coroutineNames.has(name) ? '*' : ''} ${name}(${match[2]}) { var ${[...locals].join(', ') || 'unused'}; ${body} }`);
|
result.push(`function${coroutineNames.has(name) ? '*' : ''} ${name}(${match[2]}) { var ${[...locals].join(', ') || 'unused'}; ${body} }`);
|
||||||
}
|
}
|
||||||
@@ -99,6 +99,7 @@ function engine(game, count = 8, withHud = false) {
|
|||||||
owner,
|
owner,
|
||||||
alpha: 1, textCalls: 0, valueCalls: 0, destroyCalls: 0,
|
alpha: 1, textCalls: 0, valueCalls: 0, destroyCalls: 0,
|
||||||
SetText(text) { assert(!this.destroyCalls); this.text = text; this.textCalls++; },
|
SetText(text) { assert(!this.destroyCalls); this.text = text; this.textCalls++; },
|
||||||
|
SetTextUnlimited(text) { assert(!this.destroyCalls); this.text = text; this.textCalls++; },
|
||||||
SetValue(value) { assert(!this.destroyCalls); this.value = value; this.valueCalls++; },
|
SetValue(value) { assert(!this.destroyCalls); this.value = value; this.valueCalls++; },
|
||||||
SetShader(shader, width, height) {
|
SetShader(shader, width, height) {
|
||||||
assert(!this.destroyCalls);
|
assert(!this.destroyCalls);
|
||||||
@@ -335,7 +336,7 @@ for (const game of ['t5', 't6']) {
|
|||||||
assertCounter(e, 1, 1);
|
assertCounter(e, 1, 1);
|
||||||
e.record(snapshot(['A', 'B', 'C', 'D']));
|
e.record(snapshot(['A', 'B', 'C', 'D']));
|
||||||
assert(first.every(elem => elem.destroyCalls === 1), 'pagedness changes release the old view first');
|
assert(first.every(elem => elem.destroyCalls === 1), 'pagedness changes release the old view first');
|
||||||
assert.equal(e.state.record_title.text, 'Map record · Round 82');
|
assert.equal(e.state.record_title.text, 'Map record | Round 82');
|
||||||
assert.equal(e.live.length, 5);
|
assert.equal(e.live.length, 5);
|
||||||
const paged = [...e.live];
|
const paged = [...e.live];
|
||||||
assertCounter(e, 1, 2);
|
assertCounter(e, 1, 2);
|
||||||
@@ -380,7 +381,7 @@ for (const game of ['t5', 't6']) {
|
|||||||
e.advance(11000);
|
e.advance(11000);
|
||||||
assert.equal(e.textCalls, calls);
|
assert.equal(e.textCalls, calls);
|
||||||
assert.equal(e.elements.length, count);
|
assert.equal(e.elements.length, count);
|
||||||
assert.equal(e.state.record_title.text, 'Map record · Round 82');
|
assert.equal(e.state.record_title.text, 'Map record | Round 82');
|
||||||
assert.equal(e.state.commands[3].command, '.join');
|
assert.equal(e.state.commands[3].command, '.join');
|
||||||
assert.match(e.c.help_description(e.state.commands[3]), /newcomer/);
|
assert.match(e.c.help_description(e.state.commands[3]), /newcomer/);
|
||||||
assert.equal(e.c.help_command_label(e.state.commands[6].command), '.d <amount|all>');
|
assert.equal(e.c.help_command_label(e.state.commands[6].command), '.d <amount|all>');
|
||||||
@@ -522,9 +523,9 @@ check('T5 service transitions preserve actual core element identities, styles, d
|
|||||||
const e = engine('t5', 8, true);
|
const e = engine('t5', 8, true);
|
||||||
e.createCore();
|
e.createCore();
|
||||||
const core = Array.from(e.self.tws_core_hud_elems);
|
const core = Array.from(e.self.tws_core_hud_elems);
|
||||||
assert.equal(core.length, 12, 'eleven core elements plus one optional AFK indicator');
|
assert.equal(core.length, 11, 'ten core elements plus one optional AFK indicator');
|
||||||
assert.equal(e.live.length, 12);
|
assert.equal(e.live.length, 11);
|
||||||
e.self.tws_hud_count.SetValue(6);
|
e.self.tws_hud_count.SetTextUnlimited('6 ZOMBIES');
|
||||||
e.self.tws_hud_health.SetValue(100);
|
e.self.tws_hud_health.SetValue(100);
|
||||||
e.self.tws_hud_maxhealth.SetValue(100);
|
e.self.tws_hud_maxhealth.SetValue(100);
|
||||||
e.c.set_core_alpha(e.self, e.self.tws_hud_bar, 1);
|
e.c.set_core_alpha(e.self, e.self.tws_hud_bar, 1);
|
||||||
@@ -536,17 +537,17 @@ check('T5 service transitions preserve actual core element identities, styles, d
|
|||||||
e.advance(3000);
|
e.advance(3000);
|
||||||
e.record(snapshot(['Jeff', 'Lokester', 'Moniialx']));
|
e.record(snapshot(['Jeff', 'Lokester', 'Moniialx']));
|
||||||
const controller = e.state, tasks = e.tasks.length;
|
const controller = e.state, tasks = e.tasks.length;
|
||||||
assert.equal(e.live.length, 16);
|
assert.equal(e.live.length, 15);
|
||||||
assert.deepEqual(state(), initial);
|
assert.deepEqual(state(), initial);
|
||||||
e.advance(12000);
|
e.advance(12000);
|
||||||
assert.equal(e.state, controller);
|
assert.equal(e.state, controller);
|
||||||
assert.equal(e.state.closed, false, 'idle releases the view, not its controller');
|
assert.equal(e.state.closed, false, 'idle releases the view, not its controller');
|
||||||
assert.equal(e.state.elems.length, 0);
|
assert.equal(e.state.elems.length, 0);
|
||||||
assert.equal(e.live.length, 12);
|
assert.equal(e.live.length, 11);
|
||||||
assert.deepEqual(state(), initial);
|
assert.deepEqual(state(), initial);
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
e.record(snapshot(['One', 'Two', 'Three', 'Four'])); e.advance(15000); e.help();
|
e.record(snapshot(['One', 'Two', 'Three', 'Four'])); e.advance(15000); e.help();
|
||||||
assert.equal(e.live.length, 31, '12 core/AFK plus 19 help elements; this is not an engine pool-limit assertion');
|
assert.equal(e.live.length, 30, '11 core/AFK plus 19 help elements; this is not an engine pool-limit assertion');
|
||||||
assert.equal(e.live.filter(elem => elem.archived === false).length, 19);
|
assert.equal(e.live.filter(elem => elem.archived === false).length, 19);
|
||||||
e.c.toggle_core_hud();
|
e.c.toggle_core_hud();
|
||||||
assert(core.every(elem => elem.alpha === 0));
|
assert(core.every(elem => elem.alpha === 0));
|
||||||
@@ -556,17 +557,17 @@ check('T5 service transitions preserve actual core element identities, styles, d
|
|||||||
e.advance(12000);
|
e.advance(12000);
|
||||||
assert.equal(e.state.rows[0].text, 'Latest');
|
assert.equal(e.state.rows[0].text, 'Latest');
|
||||||
assert.equal(e.state.record.remaining, 12000);
|
assert.equal(e.state.record.remaining, 12000);
|
||||||
assert.equal(e.live.length, 16);
|
assert.equal(e.live.length, 15);
|
||||||
assert(core.every(elem => elem.alpha === 0 && elem.destroyCalls === 0));
|
assert(core.every(elem => elem.alpha === 0 && elem.destroyCalls === 0));
|
||||||
e.c.toggle_core_hud();
|
e.c.toggle_core_hud();
|
||||||
assert.deepEqual(state(), initial);
|
assert.deepEqual(state(), initial);
|
||||||
e.advance(12000);
|
e.advance(12000);
|
||||||
assert.equal(e.live.length, 12);
|
assert.equal(e.live.length, 11);
|
||||||
assert.equal(e.state.elems.length, 0);
|
assert.equal(e.state.elems.length, 0);
|
||||||
assert.equal(e.tasks.length, tasks, 'reopening reuses the controller and cleanup watchers');
|
assert.equal(e.tasks.length, tasks, 'reopening reuses the controller and cleanup watchers');
|
||||||
assert.deepEqual(state(), initial);
|
assert.deepEqual(state(), initial);
|
||||||
}
|
}
|
||||||
assert.equal(e.peakLive, 31);
|
assert.equal(e.peakLive, 30);
|
||||||
assert.deepEqual(Array.from(e.self.tws_core_hud_elems), core);
|
assert.deepEqual(Array.from(e.self.tws_core_hud_elems), core);
|
||||||
e.event('disconnect');
|
e.event('disconnect');
|
||||||
assert.equal(e.live.length, 0);
|
assert.equal(e.live.length, 0);
|
||||||
|
|||||||
+4
-4
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"tws_t5_afk.gsc": "1.2.0",
|
"tws_t5_afk.gsc": "1.2.0",
|
||||||
"tws_t5_bank.gsc": "2.0.3",
|
"tws_t5_bank.gsc": "2.0.3",
|
||||||
"tws_t5_common.gsc": "1.4.0",
|
"tws_t5_common.gsc": "1.4.1",
|
||||||
"tws_t5_hud.gsc": "1.4.0",
|
"tws_t5_hud.gsc": "1.4.2",
|
||||||
"tws_t5_join.gsc": "1.0.0",
|
"tws_t5_join.gsc": "1.0.0",
|
||||||
"tws_t5_leaderboard.gsc": "1.1.0",
|
"tws_t5_leaderboard.gsc": "1.1.0",
|
||||||
"tws_t5_lock.gsc": "1.0.0",
|
"tws_t5_lock.gsc": "1.0.0",
|
||||||
"tws_t5_telemetry.gsc": "1.0.0",
|
"tws_t5_telemetry.gsc": "1.0.0",
|
||||||
"tws_t6_afk.gsc": "1.2.0",
|
"tws_t6_afk.gsc": "1.2.0",
|
||||||
"tws_t6_bank.gsc": "2.0.3",
|
"tws_t6_bank.gsc": "2.0.3",
|
||||||
"tws_t6_common.gsc": "1.4.0",
|
"tws_t6_common.gsc": "1.4.1",
|
||||||
"tws_t6_hud.gsc": "1.4.0",
|
"tws_t6_hud.gsc": "1.4.2",
|
||||||
"tws_t6_join.gsc": "1.0.1",
|
"tws_t6_join.gsc": "1.0.1",
|
||||||
"tws_t6_leaderboard.gsc": "1.1.0",
|
"tws_t6_leaderboard.gsc": "1.1.0",
|
||||||
"tws_t6_lock.gsc": "1.0.0",
|
"tws_t6_lock.gsc": "1.0.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user