Simplify T5 and T6 HUD overlays and service displays
This commit is contained in:
+49
-18
@@ -35,8 +35,9 @@ before copying the renamed scripts.
|
||||
- **Dynamic numbers use `SetValue`.** Repeatedly generating unique strings
|
||||
risks exhausting the text config-string pool; identical strings can be reused
|
||||
by the engine's [string lookup](https://github.com/MisaDev4/bo1-zombies-decompiled/blob/102f6992be332610aff587e681e3180b15f395bb/src/game_mp/g_utils_mp.cpp#L46).
|
||||
Composite HP uses numeric elements around a static slash. Help updates two
|
||||
multiline columns only on page changes, and record rows skip unchanged text.
|
||||
Composite HP uses 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
|
||||
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
|
||||
@@ -58,21 +59,39 @@ before copying the renamed scripts.
|
||||
Many BO1 maps pre-set `round_number = 1` at init, so the round watcher
|
||||
fires on transitions only and the timer row seeds on first spawn as a
|
||||
fallback.
|
||||
- **Connection-time HUD creation must not seed clocks.** The upper-right core
|
||||
is available to waiting spectators with placeholders. Timer updates retain
|
||||
- **Connection-time HUD creation must not seed clocks.** The unboxed core is
|
||||
available to waiting spectators with placeholders: timers upper left,
|
||||
branding top center and zombies/health bottom center. Timer updates retain
|
||||
their existing spawn/round lifecycle, independently of element creation.
|
||||
- **One service window per player.** Common owns fixed geometry, ordered
|
||||
four-command help pages, record display state and cleanup. Help pauses record
|
||||
page time; `.leaders` during help replaces the pending replay from page one.
|
||||
Keep both services independent of the HUD module. The core never moves or
|
||||
resizes when a service opens, and the AFK indicator uses its reserved footer.
|
||||
- **Visibility is separate from updates.** HUD owns the exact, case-insensitive
|
||||
`.hud` chat command and its help entry. Common's core wrappers register each
|
||||
element and preserve its intended alpha behind a per-player visibility mask;
|
||||
health and AFK changes use `set_core_alpha()`. Hidden data/timers keep updating
|
||||
until game over freezes them. Help, records and stock elements remain separate.
|
||||
Visibility defaults on, survives death/respawn during the connection/map, and
|
||||
resets on reconnect or map restart. AFK protection is unaffected.
|
||||
- **One service view per player.** Common owns the unboxed upper-right service
|
||||
area, ordered help rows, record display state and cleanup. Help shows all
|
||||
nine installed commands; extra registrations use nine rows per page. Help
|
||||
pauses record page time; `.leaders` during help replaces the pending replay
|
||||
from page one. Keep both services independent of the HUD module. Services
|
||||
never reposition the core, and AFK remains centered above the zombie count.
|
||||
- **Bound HUD element counts, including hidden elements.** Core + AFK use
|
||||
15 + 1 archived elements; the service uses 19 nonarchived elements regardless
|
||||
of command/page count. Help reuses two multiline columns and one muted column
|
||||
backdrop. Hiding an element does not free its slot. The
|
||||
11 + 1 archived elements. Common allocates only the active service view:
|
||||
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.
|
||||
Switching help/record views destroys the previous view first; pages reuse
|
||||
its elements. Keep the controller and pending requests separate from element
|
||||
lifetime so paused records resume normally. Both engines use explicit help
|
||||
rows; BO1 can concatenate newline-separated HUD text. Hiding an element does
|
||||
not free its slot. The previous 24-element persistent service allocation was
|
||||
consistent with the reported core corruption after records first appeared.
|
||||
The exact live T5 SP capacity remains unverified. The
|
||||
[BO1 HUD update](https://github.com/MisaDev4/bo1-zombies-decompiled/blob/102f6992be332610aff587e681e3180b15f395bb/src/game/g_hudelem.cpp#L2596)
|
||||
shows 31 slots per bucket; this is not proof of current Plutonium stock HUD
|
||||
usage, which still needs live validation.
|
||||
comes from an MP-based reconstruction and is not proof of T5 SP limits or
|
||||
current Plutonium stock usage. Validate automatic records, help, view switches
|
||||
and dismissal alongside stock notifications and other installed HUD mods;
|
||||
the core must remain intact throughout and after every service closes.
|
||||
- **Game-over trigger is level-scope, raced across three candidate
|
||||
notifies** (`end_game` / `game_ended` / `intermission`) since BO1 maps
|
||||
are inconsistent. Per-player triggers do not work — `self waittill("death")`
|
||||
@@ -80,9 +99,21 @@ before copying the renamed scripts.
|
||||
polling `self.health` misses the zero-crossing (health is rewritten during
|
||||
the game-over sequence).
|
||||
|
||||
HUD `1.2.0`, leaderboard `1.1.0` and AFK `1.0.1` require matching common `1.2.0+`.
|
||||
Install the changed scripts together and restart the map. There is no new
|
||||
native-plugin/backend dependency or full server-process restart requirement.
|
||||
HUD `1.4.0` and AFK `1.2.0` require matching common `1.4.0+`. Update all three
|
||||
where installed and restart the map. Leaderboard `1.1.0` remains compatible.
|
||||
The full help order is `.help`, `.hud`, `.afk`, `.back`, `.join`, `.leaders`,
|
||||
`.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
|
||||
`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.
|
||||
Fullscreen X/Y overlay anchors use direct normalized `640 × 480` coordinates.
|
||||
Dynamic health fills use `make_overlay_width()` like their tracks. Apply
|
||||
`int()` after `max()` in that helper: native `max()` returns a float, but
|
||||
`SetShader` requires an integer width. Legacy panel helpers and their `0.75`
|
||||
horizontal conversion remain available for existing consumers. Font scale is
|
||||
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
|
||||
dependency or full server-process restart requirement.
|
||||
Run the [shared HUD checks](../../README.md#hud-validation); portable fixtures
|
||||
do not replace T5 live loading, layout and lifecycle validation.
|
||||
|
||||
@@ -91,7 +122,7 @@ do not replace T5 live loading, layout and lifecycle validation.
|
||||
| File | Role | Fires notifies | Reads notifies |
|
||||
|---|---|---|---|
|
||||
| `tws_t5_common` | Shared config, HUD helpers, paginated `.help`, service window, spawn-handler registry, single connect listener + dispatcher | — | `say`, game-end notifications |
|
||||
| `tws_t5_hud` | Upper-right core: branding, timer row, zombie count, HP and health bar | `hud_round_changed`, `hud_match_started`, `hud_game_over` | (internal) |
|
||||
| `tws_t5_hud` | Unboxed core overlays and private `.hud` visibility toggle | `hud_round_changed`, `hud_match_started`, `hud_game_over` | `say`, internal notifications |
|
||||
| `tws_t5_afk` | `.afk` / `.back` chat toggle, owns its [AFK] indicator | — | — |
|
||||
| `tws_t5_join` | `.join` for spectators who have not played on this connection | — | `say`, `end_game`, `game_ended`, `intermission` |
|
||||
| `tws_t5_lock` | Lobby `g_password` lock at milestone round (`tws_lock_round`, default 20) | — | `hud_round_changed`, `hud_game_over` |
|
||||
|
||||
+90
-42
@@ -11,7 +11,7 @@ Platform quirks and deploy commands: [NOTES.md](NOTES.md).
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| [tws_t5_common.gsc](tws_t5_common.gsc) | Shared config, HUD helpers, paginated `.help`, service window, connect/spawn registries, single `connected` listener + dispatcher |
|
||||
| [tws_t5_hud.gsc](tws_t5_hud.gsc) | Fixed upper-right core: branding, timers, zombie count, current/maximum HP and health bar |
|
||||
| [tws_t5_hud.gsc](tws_t5_hud.gsc) | Unboxed timers, branding, zombies/health and per-player `.hud` visibility toggle |
|
||||
| [tws_t5_afk.gsc](tws_t5_afk.gsc) | `.afk` / `.back` chat toggle + `[AFK]` indicator |
|
||||
| [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 |
|
||||
@@ -56,22 +56,43 @@ invalid identities fail privately without changing points. Follow the shared
|
||||
|
||||
## Core HUD and service window
|
||||
|
||||
The fixed upper-right core shows `TWS ZOMBIES`, `discord.tws.lol`, side-by-side
|
||||
match/round timers, zombie count, current/maximum HP and a full-width health bar.
|
||||
HP stays visible at full health. A reserved footer holds the AFK indicator;
|
||||
there is no co-op name row. The core is created once per connection, including
|
||||
for spectator newcomers with placeholders until values are available. Creation
|
||||
does not seed the match clock. It persists across death and respawn.
|
||||
Clocks, health and zombie-count displays freeze at game over. Health fallbacks
|
||||
only affect the display and never modify gameplay max health.
|
||||
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
|
||||
slim health bar at the bottom center. 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
|
||||
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.
|
||||
|
||||
Common provides one fixed-height service window beneath the core for help or
|
||||
records. Services never move or resize the core. They remain private and work
|
||||
without the HUD module installed. See the [shared layout and validation guide](../../README.md#core-hud-and-service-window-t5-and-t6).
|
||||
The core is created once per connection, including for spectator newcomers
|
||||
with placeholders until values are available. Creation does not seed the match
|
||||
clock. It persists across death and respawn. Clocks, health and zombie-count
|
||||
displays freeze at game over. Health fallbacks only affect the display and
|
||||
never modify gameplay max health.
|
||||
|
||||
Install HUD `1.2.0`, leaderboard `1.1.0` and AFK `1.0.1` with common `1.2.0+`.
|
||||
Update the installed scripts together and restart the map; this change requires
|
||||
no new plugin/backend dependency or full server-process restart.
|
||||
The exact, case-insensitive `.hud` command toggles your core and AFK indicator.
|
||||
It defaults visible, keeps your choice across death/respawn on the current
|
||||
connection/map, and resets visible on reconnect or map restart. Timers and data
|
||||
continue updating while hidden; game-over values remain frozen. Help, records
|
||||
and the stock HUD are unaffected. `.hud` requires the HUD module.
|
||||
|
||||
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
|
||||
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
|
||||
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), or four for records (five with a multi-page
|
||||
counter). 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 11 + 1 elements.
|
||||
|
||||
HUD `1.4.0` and AFK `1.2.0` require common `1.4.0+`. Update common, HUD and AFK
|
||||
together where installed, then restart the map. Leaderboard `1.1.0` remains
|
||||
compatible. This change requires no new Plutonium/plugin/backend dependency or
|
||||
full server-process restart.
|
||||
|
||||
## Map record announcements
|
||||
|
||||
@@ -93,14 +114,17 @@ apply to both games.
|
||||
Type `.help` or `.commands` in chat to see available player commands and a short description.
|
||||
The response is visible only to the requesting player and works while spectating,
|
||||
before the first spawn. Commands are case-insensitive.
|
||||
Help uses the service window below the core. It shows four commands per page,
|
||||
advances automatically every 12 seconds and closes after the last page. The
|
||||
panel retains its height on short pages. Typing either command again returns to
|
||||
page one with a fresh timer.
|
||||
Help uses the unboxed upper-right service area. All nine installed commands
|
||||
fit in one list with command and description columns. Additional registered
|
||||
commands use pages of nine rows, with a counter only for multi-page lists.
|
||||
Pages advance every 12 seconds and close after the last page. Typing either
|
||||
command again returns to page one with a fresh timer. Both engines use
|
||||
explicit row positions so T5 does not concatenate newline-separated text.
|
||||
|
||||
| Command | Effect |
|
||||
|---|---|
|
||||
| `.help`, `.commands` | List available commands |
|
||||
| `.hud` | Hide or show your core HUD and AFK indicator (HUD module required) |
|
||||
| `.afk` | Pause with protection |
|
||||
| `.back` | Return from AFK |
|
||||
| `.join` | Join mid-round (newcomers only) |
|
||||
@@ -112,6 +136,9 @@ page one with a fresh timer.
|
||||
Each module registers its own help entries. Commands follow the order above,
|
||||
independent of initialization order; removing a module also removes its entries
|
||||
from `.help` after a map restart.
|
||||
With all command modules installed, help shows one page of nine commands,
|
||||
including the full `.d <amount|all>` / `.w <amount|all>` syntax. Removing HUD
|
||||
removes `.hud` from the list.
|
||||
The enabled leaderboard module registers `.leaders`, which replays the latest
|
||||
cached map record privately. Repeating it restarts the record from the first page;
|
||||
help pauses the record's remaining page time and resumes it after the final
|
||||
@@ -170,13 +197,15 @@ single `level waittill("connected")` in the fleet. Adding a second
|
||||
connect listener anywhere else crashes the T5 SP handshake.
|
||||
|
||||
**Features**
|
||||
- Shared HUD constants for panel geometry, rows/columns, fonts, health bar,
|
||||
- Shared HUD constants for overlay positions, rows/columns, fonts, health bar,
|
||||
colors and update rate
|
||||
- Spawn-handler registry
|
||||
- Connection-handler registry for optional modules that serve spectator newcomers
|
||||
- `.help` / `.commands` listener and ordered help registry populated by installed modules
|
||||
- One reusable service window per player, with help/record pagination,
|
||||
interruption/resume, request replacement and disconnect/game-end cleanup
|
||||
- One service controller per player, with help/record pagination,
|
||||
interruption/resume, request replacement and disconnect/game-end cleanup;
|
||||
display elements exist only for the active view and are released when idle
|
||||
- Core element registry with per-element alpha state and a per-player visibility mask
|
||||
- Connect listener + per-player dispatch: iterates
|
||||
`level.tws_spawn_handlers` after `spawned_player` and threads each on
|
||||
the player
|
||||
@@ -191,8 +220,18 @@ connect listener anywhere else crashes the T5 SP handshake.
|
||||
| `register_help_command(command, description)` | Adds a player command to `.help`; call from the owning module's `init()` |
|
||||
| `make_text_elem(p, x, y, ax, s, c)`| `NewClientHudElem` text element with black-glow outline |
|
||||
| `make_shader_elem(p, x, y)` | `NewClientHudElem` shader element |
|
||||
| `make_panel_text(p, x, y, ax, s, c)` | Text positioned relative to the fixed upper-right core origin |
|
||||
| `make_panel_shader(p, x, y, w, h, c, alpha, sort)` | Background/bar shader positioned relative to the core origin |
|
||||
| `make_overlay_text(p, x, y, ax, s, c)` | Text at absolute fullscreen coordinates in the normalized `640 × 480` frame |
|
||||
| `make_overlay_shader(p, x, y, w, h, c, alpha, sort)` | Shader at absolute fullscreen coordinates |
|
||||
| `make_overlay_width(width)` | Clamp a normalized width and return the integer required by `SetShader` |
|
||||
| `make_core_overlay_text(p, x, y, ax, s, c)` | Create overlay text registered for `.hud` visibility |
|
||||
| `make_core_overlay_shader(p, x, y, w, h, c, alpha, sort)` | Create an overlay shader registered for `.hud` visibility |
|
||||
| `make_panel_text(p, x, y, ax, s, c)` | Legacy text positioned relative to the former upper-right panel origin |
|
||||
| `make_panel_shader(p, x, y, w, h, c, alpha, sort)` | Legacy shader positioned relative to that panel origin |
|
||||
| `make_panel_width(width)` | Retained legacy design-width conversion for existing consumers |
|
||||
| `make_core_text(p, x, y, ax, s, c)` | Retained legacy panel text with `.hud` visibility |
|
||||
| `make_core_shader(p, x, y, w, h, c, alpha, sort)` | Retained legacy panel shader with `.hud` visibility |
|
||||
| `set_core_alpha(p, elem, alpha)` | Preserve the element's intended alpha while applying the player's visibility choice |
|
||||
| `toggle_core_hud()` | Toggle the player's registered core elements without stopping updates |
|
||||
| `show_record(snapshot)` | Replace the player's record service request; an undefined snapshot shows unavailable |
|
||||
| `destroy_on_disconnect(elems)` | Waits on `disconnect`, destroys the array |
|
||||
| `int_dvar_or(name, fallback)` | Parse an integer dvar with default |
|
||||
@@ -203,31 +242,39 @@ connect listener anywhere else crashes the T5 SP handshake.
|
||||
**Live validation (requires a T5 server)**
|
||||
|
||||
1. Type `.help`, `.HELP` and `.commands` while playing and spectating before the
|
||||
first spawn. Verify four commands per page in the service window, private to
|
||||
the requesting player. Check that full and damaged HP remain visible above it.
|
||||
first spawn. Verify all nine commands appear in one unboxed list, private to
|
||||
the requesting player. Check explicit rows, full bank syntax and the absence
|
||||
of a single-page counter. HP should appear beside the bottom health bar only
|
||||
while injured.
|
||||
2. Remove AFK or join, restart the map, and verify its entries disappear while
|
||||
`.help` and the remaining commands still work. Restore the scripts afterward.
|
||||
3. Verify pages advance every 12 seconds and close after the last page. Repeat
|
||||
`.help` on a later page and check that it restarts page one. Interrupt a record
|
||||
3. Verify help closes after 12 seconds. Add custom registered commands to test
|
||||
the nine-row page limit, counter and repeat-to-restart behavior. Interrupt a record
|
||||
with help and verify remaining page time resumes; repeat `.leaders` during
|
||||
help and verify only the latest replay starts from page one.
|
||||
4. Reopen after expiry, die/respawn and reconnect; verify no duplicate text or
|
||||
leftover elements. Repeat without the HUD module. Follow the complete
|
||||
[HUD validation guide](../../README.md#hud-validation) before rollout.
|
||||
5. Watch the first automatic record announcement and its dismissal. Repeat
|
||||
records, help and interrupted/resumed records alongside stock notifications.
|
||||
Check that branding, the health bar, damage-only HP, zombie count and timers
|
||||
remain correct during each view and after it closes.
|
||||
Include other installed HUD mods; the live T5 SP element limit is unverified.
|
||||
|
||||
---
|
||||
|
||||
## tws_t5_hud.gsc
|
||||
|
||||
Fixed upper-right core panel. Per-player elements are created on connection;
|
||||
Unboxed core overlays. Per-player elements are created on connection;
|
||||
gameplay updates start through the existing spawn lifecycle.
|
||||
|
||||
**Features**
|
||||
- Match timer + round timer (round resets each round, both freeze on game over)
|
||||
- `TWS ZOMBIES` / `discord.tws.lol` header and reserved AFK footer
|
||||
- Zombies-remaining counter
|
||||
- Full-width health bar with green / amber / red ramp; current/maximum HP
|
||||
remains visible at full health
|
||||
- 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
|
||||
- Top-center `TWS ZOMBIES · discord.tws.lol` branding
|
||||
- Bottom-center zombies-remaining counter, with the AFK indicator above it
|
||||
- Slim health bar with green / amber / red ramp; current/maximum HP appears
|
||||
beside it only while injured
|
||||
- Spectator placeholders without starting the match clock on connection
|
||||
- Round-transition and game-over notify machinery
|
||||
|
||||
@@ -246,8 +293,9 @@ Connection handler registered: `::hud_connect` — creates the core with placeho
|
||||
without seeding clocks; `hud_start` calls it as a fallback if needed.
|
||||
|
||||
**Depends on**
|
||||
- `scripts\sp\tws_t5_common::` — `ensure_config`, `make_panel_text`,
|
||||
`make_panel_shader`, `destroy_on_disconnect`, `format_mmss`,
|
||||
- `scripts\sp\tws_t5_common::` — `ensure_config`, `make_core_overlay_text`,
|
||||
`make_core_overlay_shader`, `make_overlay_width`, `set_core_alpha`, `toggle_core_hud`,
|
||||
`destroy_on_disconnect`, `format_mmss`, `register_help_command`,
|
||||
`register_spawn_handler`, `register_connect_handler`
|
||||
- Every `level.hud_*` config value from `tws_t5_common`
|
||||
- Stock BO1 state: `level.round_number`, `level.zombie_total`,
|
||||
@@ -258,8 +306,8 @@ without seeding clocks; `hud_start` calls it as a fallback if needed.
|
||||
|
||||
## tws_t5_afk.gsc
|
||||
|
||||
Self-invoked pause mode. Owns the `[AFK]` indicator element, positioned in the
|
||||
core's reserved footer through common layout values. It does not require HUD.
|
||||
Self-invoked pause mode. Owns the `[AFK]` indicator element, centered above the
|
||||
zombie count through common layout values. It does not require HUD.
|
||||
|
||||
**Features**
|
||||
- Chat commands `.afk` and `.back` (via `level waittill("say")` — not
|
||||
@@ -269,7 +317,7 @@ core's reserved footer through common layout values. It does not require HUD.
|
||||
- On exit: reverse, then a 2s grace tail keeps invuln + ignoreme through
|
||||
the target-reacquisition window
|
||||
- `[AFK]` HUD element created per-player via spawn handler; alpha-toggled
|
||||
by enter / exit
|
||||
by enter / exit through common's core visibility helpers, respecting `.hud`
|
||||
|
||||
**Provides**
|
||||
- Per-player: `self.hud_afk_on` (bool), `self.hud_afk_elem` (HUD element),
|
||||
@@ -277,9 +325,9 @@ core's reserved footer through common layout values. It does not require HUD.
|
||||
- Level var: `level.afk_grace_seconds` (default 2)
|
||||
|
||||
**Depends on**
|
||||
- `scripts\sp\tws_t5_common::` — `ensure_config`, `make_panel_text`,
|
||||
`destroy_on_disconnect`, `register_spawn_handler`, `register_help_command`
|
||||
- Shared config: core footer position, `level.hud_font_small`, `level.hud_c_afk`
|
||||
- `scripts\sp\tws_t5_common::` — `ensure_config`, `make_core_overlay_text`,
|
||||
`set_core_alpha`, `destroy_on_disconnect`, `register_spawn_handler`, `register_help_command`
|
||||
- Shared config: overlay AFK position, `level.hud_font_afk`, `level.hud_c_afk`
|
||||
- Stock BO1: `enableInvulnerability`, `disableInvulnerability`,
|
||||
`freezeControls`, `.ignoreme`, `level waittill("say", message, player)`
|
||||
|
||||
|
||||
@@ -50,11 +50,11 @@ afk_spawn()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
afk = scripts\sp\tws_t5_common::make_panel_text(
|
||||
self, level.hud_pad, level.hud_row_afk,
|
||||
"left", level.hud_font_afk, level.hud_c_afk);
|
||||
afk = scripts\sp\tws_t5_common::make_core_overlay_text(
|
||||
self, level.hud_overlay_afk_x, level.hud_overlay_afk_y,
|
||||
"center", 1.0, level.hud_c_afk);
|
||||
afk SetText("[AFK]");
|
||||
afk.alpha = 0;
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, afk, 0);
|
||||
self.hud_afk_elem = afk;
|
||||
|
||||
elems = [];
|
||||
@@ -75,7 +75,7 @@ afk_enter()
|
||||
self freezeControls(true);
|
||||
|
||||
if (isDefined(self.hud_afk_elem))
|
||||
self.hud_afk_elem.alpha = 1;
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, self.hud_afk_elem, 1);
|
||||
|
||||
self iPrintLnBold("AFK — type .back to return");
|
||||
}
|
||||
@@ -91,7 +91,7 @@ afk_exit()
|
||||
self freezeControls(false);
|
||||
|
||||
if (isDefined(self.hud_afk_elem))
|
||||
self.hud_afk_elem.alpha = 0;
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, self.hud_afk_elem, 0);
|
||||
|
||||
self iPrintLnBold("Back in the fight");
|
||||
|
||||
|
||||
+288
-123
@@ -28,8 +28,7 @@ common_config()
|
||||
if (isDefined(level.hud_x))
|
||||
return;
|
||||
|
||||
// Existing generic helpers retain their screen-anchor conventions. Panels
|
||||
// use their own right-edge origin and never push stock HUD areas around.
|
||||
// Retain legacy panel coordinates for existing helper consumers.
|
||||
level.hud_x = 10;
|
||||
level.hud_y = 30;
|
||||
// Retain the legacy fields for consumers of the original common config.
|
||||
@@ -43,7 +42,10 @@ common_config()
|
||||
level.hud_col_health = level.hud_x + 120 + 12;
|
||||
level.hud_health_threshold = 1.0;
|
||||
level.hud_panel_right = 10;
|
||||
level.hud_panel_top = 42;
|
||||
level.hud_panel_top = 20;
|
||||
// Fullscreen uses a 640x480 frame. Convert the panel's 480-height units
|
||||
// to that frame at the 16:9 reference aspect (640 / (480 * 16 / 9)).
|
||||
level.hud_panel_x_scale = 0.75;
|
||||
level.hud_panel_width = 220;
|
||||
level.hud_core_height = 100;
|
||||
level.hud_service_y = 108;
|
||||
@@ -73,6 +75,38 @@ common_config()
|
||||
level.hud_c_low = (1, 0.2, 0.2);
|
||||
level.hud_c_afk = (1, 0.8, 0.2);
|
||||
|
||||
// Minimal overlays use absolute coordinates in the fullscreen 640x480 frame.
|
||||
level.hud_overlay_timer_x = 6;
|
||||
level.hud_overlay_match_y = 8;
|
||||
level.hud_overlay_round_y = 30;
|
||||
level.hud_overlay_brand_x = 320;
|
||||
level.hud_overlay_brand_y = 8;
|
||||
level.hud_overlay_count_x = 300;
|
||||
level.hud_overlay_count_y = 440;
|
||||
level.hud_overlay_count_label_x = 306;
|
||||
level.hud_overlay_count_label_y = 446;
|
||||
level.hud_overlay_bar_x = 242;
|
||||
level.hud_overlay_bar_y = 466;
|
||||
level.hud_overlay_bar_width = 156;
|
||||
level.hud_overlay_bar_height = 4;
|
||||
level.hud_overlay_health_y = 460;
|
||||
level.hud_overlay_health_x = 414;
|
||||
level.hud_overlay_slash_x = 418;
|
||||
level.hud_overlay_maxhealth_x = 445;
|
||||
level.hud_overlay_hp_x = 449;
|
||||
level.hud_overlay_afk_x = 320;
|
||||
level.hud_overlay_afk_y = 421;
|
||||
level.hud_service_left = 432;
|
||||
level.hud_service_right = 633;
|
||||
level.hud_service_description_x = 538;
|
||||
level.hud_service_title_y = 54;
|
||||
level.hud_service_help_y = 80;
|
||||
level.hud_service_help_pitch = 24;
|
||||
level.hud_service_help_rows = 9;
|
||||
level.hud_service_record_y = 36;
|
||||
level.hud_service_holders_y = 60;
|
||||
level.hud_service_holders_pitch = 18;
|
||||
|
||||
if (!isDefined(level.tws_spawn_handlers))
|
||||
level.tws_spawn_handlers = [];
|
||||
}
|
||||
@@ -137,7 +171,7 @@ show_help()
|
||||
state = self.tws_service;
|
||||
service_advance(state, getTime());
|
||||
state.generation++;
|
||||
state.help = service_request("help", service_page_count(state.commands.size, 4), state.generation);
|
||||
state.help = service_request("help", service_page_count(state.commands.size, level.hud_service_help_rows), state.generation);
|
||||
service_render(state);
|
||||
}
|
||||
|
||||
@@ -209,14 +243,15 @@ service_advance(state, now)
|
||||
help_rank(command)
|
||||
{
|
||||
if (command == ".help") return 0;
|
||||
if (command == ".afk") return 1;
|
||||
if (command == ".back") return 2;
|
||||
if (command == ".join") return 3;
|
||||
if (command == ".leaders") return 4;
|
||||
if (command == ".b") return 5;
|
||||
if (command.size >= 3 && getSubStr(command, 0, 3) == ".d ") return 6;
|
||||
if (command.size >= 3 && getSubStr(command, 0, 3) == ".w ") return 7;
|
||||
return 8;
|
||||
if (command == ".hud") return 1;
|
||||
if (command == ".afk") return 2;
|
||||
if (command == ".back") return 3;
|
||||
if (command == ".join") return 4;
|
||||
if (command == ".leaders") return 5;
|
||||
if (command == ".b") return 6;
|
||||
if (command.size >= 3 && getSubStr(command, 0, 3) == ".d ") return 7;
|
||||
if (command.size >= 3 && getSubStr(command, 0, 3) == ".w ") return 8;
|
||||
return 9;
|
||||
}
|
||||
|
||||
help_entries()
|
||||
@@ -224,7 +259,7 @@ help_entries()
|
||||
entries = [];
|
||||
if (!isDefined(level.tws_help_commands))
|
||||
return entries;
|
||||
for (rank = 0; rank <= 8; rank++)
|
||||
for (rank = 0; rank <= 9; rank++)
|
||||
{
|
||||
for (i = 0; i < level.tws_help_commands.size; i++)
|
||||
{
|
||||
@@ -238,9 +273,6 @@ help_entries()
|
||||
|
||||
help_command_label(command)
|
||||
{
|
||||
rank = help_rank(command);
|
||||
if (rank == 6) return ".d <n|all>";
|
||||
if (rank == 7) return ".w <n|all>";
|
||||
return command;
|
||||
}
|
||||
|
||||
@@ -248,13 +280,14 @@ help_description(entry)
|
||||
{
|
||||
rank = help_rank(entry.command);
|
||||
if (rank == 0) return "Show commands";
|
||||
if (rank == 1) return "Enable protection";
|
||||
if (rank == 2) return "Return to play";
|
||||
if (rank == 3) return "Join as newcomer";
|
||||
if (rank == 4) return "View records";
|
||||
if (rank == 5) return "Check balance";
|
||||
if (rank == 6) return "Deposit points";
|
||||
if (rank == 7) return "Withdraw points";
|
||||
if (rank == 1) return "Toggle core HUD";
|
||||
if (rank == 2) return "Protect & freeze";
|
||||
if (rank == 3) return "Return to play";
|
||||
if (rank == 4) return "Join as newcomer";
|
||||
if (rank == 5) return "View records";
|
||||
if (rank == 6) return "Bank balance";
|
||||
if (rank == 7) return "Deposit";
|
||||
if (rank == 8) return "Withdraw";
|
||||
return entry.description;
|
||||
}
|
||||
|
||||
@@ -277,57 +310,82 @@ service_ensure()
|
||||
state.record_text = [];
|
||||
state.help_page = -1;
|
||||
state.rows = [];
|
||||
state.command_rows = [];
|
||||
state.description_rows = [];
|
||||
state.view_kind = undefined;
|
||||
self.tws_service = state;
|
||||
|
||||
y = level.hud_service_y;
|
||||
state.background = make_panel_shader(self, 0, y, level.hud_panel_width,
|
||||
level.hud_service_height, level.hud_c_panel, level.hud_panel_alpha, 0);
|
||||
state.elems[state.elems.size] = state.background;
|
||||
state.help_title = service_text(self, state, 10, 10, "left", 1.1, "COMMANDS", level.hud_c_label);
|
||||
state.record_title = service_text(self, state, 10, 10, "left", 1.1, "MAP RECORD", level.hud_c_label);
|
||||
state.help_footer = service_text(self, state, 10, 108, "left", 0.9,
|
||||
"Four commands per page", level.hud_c_dim);
|
||||
state.record_footer = service_text(self, state, 10, 108, "left", 0.9,
|
||||
"Briefly shown - .leaders to view again", level.hud_c_dim);
|
||||
state.page_number = service_text(self, state, 178, 10, "right", 1.1, "", level.hud_c_dim);
|
||||
state.page_slash = service_text(self, state, 184, 10, "center", 1.1, "/", level.hud_c_dim);
|
||||
state.page_total = service_text(self, state, 210, 10, "right", 1.1, "", level.hud_c_dim);
|
||||
// Two fixed text columns keep the HUD element budget independent of the
|
||||
// command count. Strings change only at page boundaries, never per tick.
|
||||
state.command_back = make_panel_shader(self, 10, y + 30, 65, 68,
|
||||
(0.16, 0.19, 0.17), 0.9, 5);
|
||||
state.elems[state.elems.size] = state.command_back;
|
||||
state.command_column = service_text(self, state, 14, 31,
|
||||
"left", 1.0, "", level.hud_c_name);
|
||||
state.description_column = service_text(self, state, 81, 31,
|
||||
"left", 1.0, "", level.hud_c_count);
|
||||
state.round_label = service_text(self, state, 10, 32, "left", 1.1, "ROUND", level.hud_c_label);
|
||||
state.round_number = service_text(self, state, 54, 27, "left", 1.9, "", level.hud_c_count);
|
||||
state.holders_label = service_text(self, state, 10, 52, "left", 1.0, "RECORD HOLDERS", level.hud_c_label);
|
||||
for (i = 0; i < 3; i++)
|
||||
state.rows[i] = service_text(self, state, 10, 65 + i * 13,
|
||||
"left", 1.1, "", level.hud_c_count);
|
||||
state.empty = service_text(self, state, 10, 40, "left", 1.1,
|
||||
"No recorded high round yet", level.hud_c_count);
|
||||
state.unavailable = service_text(self, state, 10, 40, "left", 1.1,
|
||||
"Map record unavailable", level.hud_c_count);
|
||||
for (i = 0; i < state.elems.size; i++)
|
||||
{
|
||||
state.elems[i].alpha = 0;
|
||||
// Keep temporary private services out of the archived HUD bucket.
|
||||
// Hidden elements still occupy slots; do not allocate one per page.
|
||||
state.elems[i].archived = false;
|
||||
}
|
||||
|
||||
self thread service_loop(state);
|
||||
self thread service_disconnect(state);
|
||||
self thread service_end_game(state);
|
||||
}
|
||||
|
||||
// Allocate only the active view. Alpha-zero elements still occupy HUD slots.
|
||||
service_view_create(state, request)
|
||||
{
|
||||
service_view_destroy(state);
|
||||
state.view_kind = request.kind;
|
||||
state.view_paged = request.pages > 1;
|
||||
if (state.view_paged)
|
||||
state.page_counter = service_text(self, state, level.hud_service_right,
|
||||
level.hud_service_title_y, "right", 1.0, "", level.hud_c_dim);
|
||||
|
||||
if (request.kind == "help")
|
||||
{
|
||||
state.help_title = service_text(self, state, level.hud_service_left,
|
||||
level.hud_service_title_y, "left", 1.1, "COMMANDS", level.hud_c_label);
|
||||
rows = state.commands.size;
|
||||
if (rows > level.hud_service_help_rows)
|
||||
rows = level.hud_service_help_rows;
|
||||
// Explicit lines also work on T5, which can collapse multiline strings.
|
||||
for (i = 0; i < rows; i++)
|
||||
{
|
||||
y = level.hud_service_help_y + i * level.hud_service_help_pitch;
|
||||
state.command_rows[i] = service_text(self, state, level.hud_service_left,
|
||||
y, "left", 1.0, "", level.hud_c_count);
|
||||
state.description_rows[i] = service_text(self, state, level.hud_service_description_x,
|
||||
y, "left", 1.0, "", level.hud_c_label);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
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++)
|
||||
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);
|
||||
if (isDefined(state.page_counter))
|
||||
state.page_counter.y = level.hud_service_holders_y + 3 * level.hud_service_holders_pitch;
|
||||
}
|
||||
|
||||
service_view_destroy(state)
|
||||
{
|
||||
for (i = 0; i < state.elems.size; i++)
|
||||
{
|
||||
if (isDefined(state.elems[i]))
|
||||
state.elems[i] Destroy();
|
||||
}
|
||||
state.elems = [];
|
||||
state.view_kind = undefined;
|
||||
state.view_paged = undefined;
|
||||
state.page_counter = undefined;
|
||||
state.help_title = undefined;
|
||||
state.command_rows = [];
|
||||
state.description_rows = [];
|
||||
state.record_title = undefined;
|
||||
state.rows = [];
|
||||
state.page_text = undefined;
|
||||
state.record_header_text = undefined;
|
||||
state.record_text = [];
|
||||
state.help_page = -1;
|
||||
}
|
||||
|
||||
service_text(player, state, x, y, align, scale, text, colour)
|
||||
{
|
||||
elem = make_panel_text(player, x, level.hud_service_y + y, align, scale, colour);
|
||||
elem = make_overlay_text(player, x, y, align, scale, colour);
|
||||
elem.alpha = 0;
|
||||
elem.archived = false;
|
||||
if (text != "")
|
||||
elem SetText(text);
|
||||
state.elems[state.elems.size] = elem;
|
||||
@@ -351,74 +409,70 @@ service_render(state)
|
||||
state.rendered = true;
|
||||
state.last_generation = generation;
|
||||
state.last_page = page;
|
||||
if (!isDefined(request))
|
||||
{
|
||||
service_view_destroy(state);
|
||||
return;
|
||||
}
|
||||
paged = request.pages > 1;
|
||||
if (!isDefined(state.view_kind) || state.view_kind != request.kind || state.view_paged != paged)
|
||||
service_view_create(state, request);
|
||||
for (i = 0; i < state.elems.size; i++)
|
||||
state.elems[i].alpha = 0;
|
||||
if (!isDefined(request))
|
||||
return;
|
||||
|
||||
state.background.alpha = level.hud_panel_alpha;
|
||||
state.page_number SetValue(page + 1);
|
||||
state.page_total SetValue(request.pages);
|
||||
state.page_number.alpha = 1;
|
||||
state.page_slash.alpha = 1;
|
||||
state.page_total.alpha = 1;
|
||||
if (paged)
|
||||
{
|
||||
page_text = "" + (page + 1) + " / " + request.pages;
|
||||
if (!isDefined(state.page_text) || state.page_text != page_text)
|
||||
{
|
||||
state.page_counter SetText(page_text);
|
||||
state.page_text = page_text;
|
||||
}
|
||||
state.page_counter.alpha = 1;
|
||||
}
|
||||
if (request.kind == "help")
|
||||
{
|
||||
state.help_title.alpha = 1;
|
||||
state.help_footer.alpha = 1;
|
||||
if (state.help_page != page)
|
||||
for (i = 0; i < state.command_rows.size; i++)
|
||||
{
|
||||
command_text = "";
|
||||
description_text = "";
|
||||
for (i = 0; i < 4; i++)
|
||||
index = page * level.hud_service_help_rows + i;
|
||||
if (index >= state.commands.size)
|
||||
continue;
|
||||
if (state.help_page != page)
|
||||
{
|
||||
index = page * 4 + i;
|
||||
if (index < state.commands.size)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
command_text += "\n";
|
||||
description_text += "\n";
|
||||
}
|
||||
entry = state.commands[index];
|
||||
command_text += help_command_label(entry.command);
|
||||
description_text += help_description(entry);
|
||||
}
|
||||
entry = state.commands[index];
|
||||
state.command_rows[i] SetText(help_command_label(entry.command));
|
||||
state.description_rows[i] SetText(help_description(entry));
|
||||
}
|
||||
state.command_column SetText(command_text);
|
||||
state.description_column SetText(description_text);
|
||||
state.help_page = page;
|
||||
state.command_rows[i].alpha = 1;
|
||||
state.description_rows[i].alpha = 1;
|
||||
}
|
||||
state.command_back.alpha = 0.9;
|
||||
state.command_column.alpha = 1;
|
||||
state.description_column.alpha = 1;
|
||||
state.help_page = page;
|
||||
return;
|
||||
}
|
||||
|
||||
state.record_title.alpha = 1;
|
||||
state.record_footer.alpha = 1;
|
||||
header = "Map record unavailable";
|
||||
snapshot = request.snapshot;
|
||||
if (!isDefined(snapshot))
|
||||
if (isDefined(snapshot))
|
||||
{
|
||||
state.unavailable.alpha = 1;
|
||||
return;
|
||||
header = "No recorded high round yet";
|
||||
if (snapshot.round > 0)
|
||||
header = "Map record · Round " + snapshot.round;
|
||||
}
|
||||
if (snapshot.round == 0)
|
||||
if (!isDefined(state.record_header_text) || state.record_header_text != header)
|
||||
{
|
||||
state.empty.alpha = 1;
|
||||
return;
|
||||
state.record_title SetText(header);
|
||||
state.record_header_text = header;
|
||||
}
|
||||
state.round_label.alpha = 1;
|
||||
state.round_number SetValue(snapshot.round);
|
||||
state.round_number.alpha = 1;
|
||||
state.holders_label.alpha = 1;
|
||||
state.record_title.alpha = 1;
|
||||
if (!isDefined(snapshot) || snapshot.round == 0)
|
||||
return;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
index = page * 3 + i;
|
||||
if (index < snapshot.lines.size)
|
||||
{
|
||||
text = snapshot.lines[index];
|
||||
// Skip identical text when replaying a page or resuming after help.
|
||||
if (!isDefined(state.record_text[i]) || state.record_text[i] != text)
|
||||
{
|
||||
state.rows[i] SetText(text);
|
||||
@@ -461,12 +515,7 @@ service_destroy(state)
|
||||
state.closed = true;
|
||||
state.help = undefined;
|
||||
state.record = undefined;
|
||||
for (i = 0; i < state.elems.size; i++)
|
||||
{
|
||||
if (isDefined(state.elems[i]))
|
||||
state.elems[i] Destroy();
|
||||
}
|
||||
state.elems = [];
|
||||
service_view_destroy(state);
|
||||
}
|
||||
|
||||
service_game_over_via(event)
|
||||
@@ -526,7 +575,8 @@ make_text_elem(player, x, y, align_x, scale, colour)
|
||||
elem.x = x;
|
||||
elem.y = y;
|
||||
elem.foreground = 1;
|
||||
elem.fontScale = scale;
|
||||
// Sub-1.0 text can render oversized on clients; keep a safe minimum.
|
||||
elem.fontScale = max(1.0, scale);
|
||||
elem.alpha = 1;
|
||||
elem.color = colour;
|
||||
|
||||
@@ -553,14 +603,59 @@ make_shader_elem(player, x, y)
|
||||
return elem;
|
||||
}
|
||||
|
||||
// Panel x/y are local to the fixed upper-right core origin. A right screen
|
||||
// anchor keeps the panel on-screen across aspect ratios; negative offsets grow
|
||||
// left from that edge. Keep each engine's existing vertical anchor convention.
|
||||
// Absolute fullscreen overlays leave stock HUD areas and the center clear.
|
||||
make_overlay_text(player, x, y, align_x, scale, colour)
|
||||
{
|
||||
elem = make_text_elem(player, x, y, align_x, scale, colour);
|
||||
elem.horzAlign = "fullscreen";
|
||||
elem.vertAlign = "fullscreen";
|
||||
elem.sort = 20;
|
||||
elem.archived = true;
|
||||
return elem;
|
||||
}
|
||||
|
||||
make_overlay_shader(player, x, y, width, height, colour, alpha, sort)
|
||||
{
|
||||
elem = make_shader_elem(player, x, y);
|
||||
elem.horzAlign = "fullscreen";
|
||||
elem.vertAlign = "fullscreen";
|
||||
elem.color = colour;
|
||||
elem.alpha = alpha;
|
||||
elem.sort = sort;
|
||||
elem.archived = true;
|
||||
elem SetShader("white", make_overlay_width(width), int(max(1, height)));
|
||||
return elem;
|
||||
}
|
||||
|
||||
make_overlay_width(width)
|
||||
{
|
||||
// Native max() returns a float; shader dimensions must be integers.
|
||||
return int(max(1, width));
|
||||
}
|
||||
|
||||
make_core_overlay_text(player, x, y, align_x, scale, colour)
|
||||
{
|
||||
elem = make_overlay_text(player, x, y, align_x, scale, colour);
|
||||
register_core_elem(player, elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
make_core_overlay_shader(player, x, y, width, height, colour, alpha, sort)
|
||||
{
|
||||
elem = make_overlay_shader(player, x, y, width, height, colour, alpha, sort);
|
||||
register_core_elem(player, elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
// Panel x/y are local to the upper-right core origin. Fullscreen bypasses the
|
||||
// engine safe area. Horizontal conversion preserves the original size at 16:9;
|
||||
// other aspects retain the right-edge anchor with proportional horizontal size.
|
||||
make_panel_text(player, x, y, align_x, scale, colour)
|
||||
{
|
||||
elem = make_text_elem(player, x - level.hud_panel_width - level.hud_panel_right,
|
||||
elem = make_text_elem(player, 640 + (x - level.hud_panel_width - level.hud_panel_right) * level.hud_panel_x_scale,
|
||||
level.hud_panel_top + y, align_x, scale, colour);
|
||||
elem.horzAlign = "right";
|
||||
elem.horzAlign = "fullscreen";
|
||||
elem.vertAlign = "fullscreen";
|
||||
elem.sort = 20;
|
||||
elem.archived = true;
|
||||
return elem;
|
||||
@@ -568,17 +663,87 @@ make_panel_text(player, x, y, align_x, scale, colour)
|
||||
|
||||
make_panel_shader(player, x, y, width, height, colour, alpha, sort)
|
||||
{
|
||||
elem = make_shader_elem(player, x - level.hud_panel_width - level.hud_panel_right,
|
||||
elem = make_shader_elem(player, 640 + (x - level.hud_panel_width - level.hud_panel_right) * level.hud_panel_x_scale,
|
||||
level.hud_panel_top + y);
|
||||
elem.horzAlign = "right";
|
||||
elem.horzAlign = "fullscreen";
|
||||
elem.vertAlign = "fullscreen";
|
||||
elem.color = colour;
|
||||
elem.alpha = alpha;
|
||||
elem.sort = sort;
|
||||
elem.archived = true;
|
||||
elem SetShader("white", width, height);
|
||||
elem SetShader("white", make_panel_width(width), height);
|
||||
return elem;
|
||||
}
|
||||
|
||||
// Native shader widths are integers; dynamic fills use the same conversion.
|
||||
make_panel_width(width)
|
||||
{
|
||||
// max() returns a float even for integer inputs; cast its final result.
|
||||
return int(max(1, width * level.hud_panel_x_scale));
|
||||
}
|
||||
|
||||
// Core elements share a visibility mask; services keep their independent alpha.
|
||||
make_core_text(player, x, y, align_x, scale, colour)
|
||||
{
|
||||
elem = make_panel_text(player, x, y, align_x, scale, colour);
|
||||
register_core_elem(player, elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
make_core_shader(player, x, y, width, height, colour, alpha, sort)
|
||||
{
|
||||
elem = make_panel_shader(player, x, y, width, height, colour, alpha, sort);
|
||||
register_core_elem(player, elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
register_core_elem(player, elem)
|
||||
{
|
||||
live = [];
|
||||
if (isDefined(player.tws_core_hud_elems))
|
||||
{
|
||||
for (i = 0; i < player.tws_core_hud_elems.size; i++)
|
||||
{
|
||||
old = player.tws_core_hud_elems[i];
|
||||
if (isDefined(old))
|
||||
live[live.size] = old;
|
||||
}
|
||||
}
|
||||
live[live.size] = elem;
|
||||
player.tws_core_hud_elems = live;
|
||||
set_core_alpha(player, elem, elem.alpha);
|
||||
}
|
||||
|
||||
set_core_alpha(player, elem, alpha)
|
||||
{
|
||||
if (!isDefined(elem))
|
||||
return;
|
||||
elem.tws_core_alpha = alpha;
|
||||
elem.alpha = alpha;
|
||||
if (isDefined(player.tws_hud_hidden) && player.tws_hud_hidden)
|
||||
elem.alpha = 0;
|
||||
}
|
||||
|
||||
toggle_core_hud()
|
||||
{
|
||||
hidden = false;
|
||||
if (isDefined(self.tws_hud_hidden))
|
||||
hidden = self.tws_hud_hidden;
|
||||
self.tws_hud_hidden = !hidden;
|
||||
if (!isDefined(self.tws_core_hud_elems))
|
||||
return;
|
||||
live = [];
|
||||
for (i = 0; i < self.tws_core_hud_elems.size; i++)
|
||||
{
|
||||
elem = self.tws_core_hud_elems[i];
|
||||
if (!isDefined(elem))
|
||||
continue;
|
||||
set_core_alpha(self, elem, elem.tws_core_alpha);
|
||||
live[live.size] = elem;
|
||||
}
|
||||
self.tws_core_hud_elems = live;
|
||||
}
|
||||
|
||||
destroy_on_disconnect(elems)
|
||||
{
|
||||
self waittill("disconnect");
|
||||
|
||||
+72
-47
@@ -1,4 +1,4 @@
|
||||
/* tws_t5_hud.gsc - fixed upper-right core HUD, visible from connection. */
|
||||
/* tws_t5_hud.gsc - minimal edge HUD, toggleable with .hud. */
|
||||
|
||||
#include maps\_utility;
|
||||
#include common_scripts\utility;
|
||||
@@ -9,14 +9,34 @@ init()
|
||||
return;
|
||||
|
||||
scripts\sp\tws_t5_common::ensure_config();
|
||||
scripts\sp\tws_t5_common::register_help_command(".hud", "Toggle core HUD");
|
||||
|
||||
level thread round_watcher();
|
||||
level thread game_over_watcher();
|
||||
level thread hud_chat_listener();
|
||||
|
||||
scripts\sp\tws_t5_common::register_connect_handler(::hud_connect);
|
||||
scripts\sp\tws_t5_common::register_spawn_handler(::hud_start);
|
||||
}
|
||||
|
||||
hud_chat_listener()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
level waittill("say", message, player);
|
||||
if (!isDefined(player) || !isDefined(message))
|
||||
continue;
|
||||
if (toLower(message) == ".hud")
|
||||
player thread hud_toggle();
|
||||
}
|
||||
}
|
||||
|
||||
hud_toggle()
|
||||
{
|
||||
self hud_connect();
|
||||
self scripts\sp\tws_t5_common::toggle_core_hud();
|
||||
}
|
||||
|
||||
// Connection construction never seeds clocks or reads player gameplay state.
|
||||
// The first-spawn fallback also handles a spawn before the connect callback.
|
||||
hud_connect()
|
||||
@@ -27,48 +47,42 @@ hud_connect()
|
||||
return;
|
||||
|
||||
self.tws_hud_elems = [];
|
||||
background = scripts\sp\tws_t5_common::make_panel_shader(
|
||||
self, 0, 0, level.hud_panel_width, level.hud_core_height,
|
||||
level.hud_c_panel, level.hud_panel_alpha, 0);
|
||||
self.tws_hud_elems[self.tws_hud_elems.size] = background;
|
||||
self.tws_hud_match = self hud_text(level.hud_overlay_timer_x,
|
||||
level.hud_overlay_match_y, "left", 1.4, level.hud_c_count, "--:--");
|
||||
self.tws_hud_round = self hud_text(level.hud_overlay_timer_x,
|
||||
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,
|
||||
"center", 1.1, level.hud_c_dim, "TWS ZOMBIES · discord.tws.lol");
|
||||
|
||||
self hud_text(level.hud_pad, level.hud_row_header, "left",
|
||||
level.hud_font_small, level.hud_c_name, "TWS ZOMBIES");
|
||||
self hud_text(level.hud_panel_width - level.hud_pad, level.hud_row_header,
|
||||
"right", level.hud_font_small, level.hud_c_dim, "discord.tws.lol");
|
||||
|
||||
self hud_text(level.hud_pad, level.hud_row_timers, "left",
|
||||
level.hud_font_small, level.hud_c_label, "MATCH");
|
||||
self.tws_hud_match = self hud_text(104, level.hud_row_timers - 2, "right",
|
||||
level.hud_font_mid, level.hud_c_count, "--:--");
|
||||
self hud_text(114, level.hud_row_timers, "left",
|
||||
level.hud_font_small, level.hud_c_label, "ROUND");
|
||||
self.tws_hud_round = self hud_text(level.hud_panel_width - level.hud_pad,
|
||||
level.hud_row_timers - 2, "right",
|
||||
level.hud_font_mid, level.hud_c_count, "--:--");
|
||||
|
||||
self hud_text(level.hud_pad, level.hud_row_stats, "left",
|
||||
level.hud_font_small, level.hud_c_label, "ZOMBIES");
|
||||
self.tws_hud_count = self hud_text(67, level.hud_row_stats, "left",
|
||||
level.hud_font_mid, level.hud_c_count, "--");
|
||||
self hud_text(132, level.hud_row_stats, "left",
|
||||
level.hud_font_small, level.hud_c_label, "HP");
|
||||
self.tws_hud_count = self hud_text(level.hud_overlay_count_x,
|
||||
level.hud_overlay_count_y, "right", 1.5, level.hud_c_count, "--");
|
||||
self hud_text(level.hud_overlay_count_label_x, level.hud_overlay_count_label_y,
|
||||
"left", 1.05, level.hud_c_dim, "ZOMBIES");
|
||||
|
||||
// Independent numeric elements avoid a new config string on every tick.
|
||||
self.tws_hud_health = self hud_text(174, level.hud_row_stats, "right",
|
||||
level.hud_font_mid, level.hud_c_count, "--");
|
||||
self.tws_hud_slash = self hud_text(181, level.hud_row_stats, "center",
|
||||
level.hud_font_mid, level.hud_c_count, "/");
|
||||
self.tws_hud_maxhealth = self hud_text(level.hud_panel_width - level.hud_pad,
|
||||
level.hud_row_stats, "right", level.hud_font_mid, level.hud_c_count, "--");
|
||||
// The readout appears only after a valid damaged-health sample.
|
||||
self.tws_hud_health = self hud_text(level.hud_overlay_health_x,
|
||||
level.hud_overlay_health_y, "right", 1.0, level.hud_c_count, "--");
|
||||
self.tws_hud_slash = self hud_text(level.hud_overlay_slash_x,
|
||||
level.hud_overlay_health_y, "left", 1.0, level.hud_c_count, "/");
|
||||
self.tws_hud_maxhealth = self hud_text(level.hud_overlay_maxhealth_x,
|
||||
level.hud_overlay_health_y, "right", 1.0, level.hud_c_count, "--");
|
||||
self.tws_hud_hp = self hud_text(level.hud_overlay_hp_x,
|
||||
level.hud_overlay_health_y, "left", 1.0, level.hud_c_count, "HP");
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, self.tws_hud_health, 0);
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, self.tws_hud_slash, 0);
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, self.tws_hud_maxhealth, 0);
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, self.tws_hud_hp, 0);
|
||||
|
||||
track = scripts\sp\tws_t5_common::make_panel_shader(
|
||||
self, level.hud_pad, level.hud_row_bar,
|
||||
level.hud_bar_width, level.hud_bar_height, (0.18, 0.22, 0.19), 0.8, 10);
|
||||
track = scripts\sp\tws_t5_common::make_core_overlay_shader(
|
||||
self, level.hud_overlay_bar_x, level.hud_overlay_bar_y,
|
||||
level.hud_overlay_bar_width, level.hud_overlay_bar_height,
|
||||
(0.18, 0.22, 0.19), 0.8, 10);
|
||||
self.tws_hud_elems[self.tws_hud_elems.size] = track;
|
||||
self.tws_hud_bar = scripts\sp\tws_t5_common::make_panel_shader(
|
||||
self, level.hud_pad, level.hud_row_bar,
|
||||
level.hud_bar_width, level.hud_bar_height, level.hud_c_high, 0, 11);
|
||||
self.tws_hud_bar = scripts\sp\tws_t5_common::make_core_overlay_shader(
|
||||
self, level.hud_overlay_bar_x, level.hud_overlay_bar_y,
|
||||
level.hud_overlay_bar_width, level.hud_overlay_bar_height,
|
||||
level.hud_c_high, 0, 11);
|
||||
self.tws_hud_elems[self.tws_hud_elems.size] = self.tws_hud_bar;
|
||||
|
||||
self thread scripts\sp\tws_t5_common::destroy_on_disconnect(self.tws_hud_elems);
|
||||
@@ -76,7 +90,7 @@ hud_connect()
|
||||
|
||||
hud_text(x, y, align, scale, colour, text)
|
||||
{
|
||||
elem = scripts\sp\tws_t5_common::make_panel_text(self, x, y, align, scale, colour);
|
||||
elem = scripts\sp\tws_t5_common::make_core_overlay_text(self, x, y, align, scale, colour);
|
||||
elem SetText(text);
|
||||
self.tws_hud_elems[self.tws_hud_elems.size] = elem;
|
||||
return elem;
|
||||
@@ -145,7 +159,7 @@ hud_timer_row()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
// A visible panel for a waiting newcomer must not start a pre-set round 1.
|
||||
// A visible HUD for a waiting newcomer must not start a pre-set round 1.
|
||||
if (!isDefined(level.match_start_time) &&
|
||||
isDefined(level.round_number) &&
|
||||
level.round_number >= 1)
|
||||
@@ -190,7 +204,7 @@ match_timer_bind(elem)
|
||||
elem SetTimerUp(0);
|
||||
}
|
||||
|
||||
// The panel remains visible at game over, with authoritative frozen clocks.
|
||||
// Preserve authoritative frozen clocks when the core is shown after game over.
|
||||
freeze_timers_on_game_over(match_elem, round_elem)
|
||||
{
|
||||
self endon("disconnect");
|
||||
@@ -249,11 +263,12 @@ hud_health_display()
|
||||
self endon("disconnect");
|
||||
level endon("hud_game_over");
|
||||
|
||||
bar_width = level.hud_bar_width;
|
||||
bar_width = level.hud_overlay_bar_width;
|
||||
bar = self.tws_hud_bar;
|
||||
cur = self.tws_hud_health;
|
||||
slash = self.tws_hud_slash;
|
||||
max_hp = self.tws_hud_maxhealth;
|
||||
hp = self.tws_hud_hp;
|
||||
last_width = -1;
|
||||
last_health = -1;
|
||||
last_max = -1;
|
||||
@@ -272,16 +287,17 @@ hud_health_display()
|
||||
if (frac > 1)
|
||||
frac = 1;
|
||||
|
||||
width = int(max(frac * bar_width, 1));
|
||||
width = scripts\sp\tws_t5_common::make_overlay_width(frac * bar_width);
|
||||
if (width != last_width)
|
||||
{
|
||||
bar SetShader("white", width, level.hud_bar_height);
|
||||
bar SetShader("white", width, level.hud_overlay_bar_height);
|
||||
last_width = width;
|
||||
}
|
||||
|
||||
bar.alpha = 1;
|
||||
bar_alpha = 1;
|
||||
if (frac == 0)
|
||||
bar.alpha = 0;
|
||||
bar_alpha = 0;
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, bar, bar_alpha);
|
||||
|
||||
colour = level.hud_c_high;
|
||||
if (frac <= 0.25)
|
||||
@@ -293,8 +309,17 @@ hud_health_display()
|
||||
cur.color = colour;
|
||||
slash.color = colour;
|
||||
max_hp.color = colour;
|
||||
hp.color = colour;
|
||||
|
||||
// Health numbers remain visible at full health.
|
||||
readout_alpha = 0;
|
||||
if (isDefined(self.health) && current < maximum)
|
||||
readout_alpha = 1;
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, cur, readout_alpha);
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, slash, readout_alpha);
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, max_hp, readout_alpha);
|
||||
scripts\sp\tws_t5_common::set_core_alpha(self, hp, readout_alpha);
|
||||
|
||||
// Keep health numbers current even at full health or while hidden.
|
||||
if (current != last_health)
|
||||
{
|
||||
cur SetValue(current);
|
||||
|
||||
Reference in New Issue
Block a user