Warcraft III Score Screen
This file describes the score foundation, controller, campaign-quit follow-on, retail-presentation Patch 1, and dynamic-row Patch 2. The installed Warcraft assets are required for visual presentation; the project never bundles Blizzard FDF or score artwork.
For the repeatable retail workflow used to capture score traces, edit disposable map copies, repack them safely, and compare UnitBalance variants, see Retail Score Experiments.
Source and confidence
The raw score counters are simulation-authored as described in the preceding
foundation patch. Four unverified guesses now return values through
GetPlayerScore and are also shown on the result screen. They are deliberately
separated from the accounting hooks in g_score.c and tagged GUESS:
| Player score | Temporary algorithm (NOT confirmed retail) |
|---|---|
| Tech percent | 100 × researched technology IDs / observed technology IDs, or 0 with none |
| Units total | 10× trained + 10× enemy units killed + 20× structures built + 20× structures razed |
| Heroes total | 100× hero kills + 10× items gained + 10× experience gained |
| Resources total | Retail-verified (TFT 1.29.2): Gold Mined Total + Lumber Harvested Total, one point per gathered resource |
| Overall total | Confirmed: Units + Heroes + Resources (no Tech bonus) |
All totals saturate at INT32_MAX. These weights and denominators are guesses,
not reverse-engineered Warcraft calculations. In particular, the technology
denominator lacks unresearched-but-available tech; hero XP is now tracked but
its 10× score weight is inferred from one retail case; and resources do not
have authoritative transfer/tax accounting. Compare
against original retail scores with controlled games before claiming parity.
Snapshot and screen lifecycle
The first EndGame(true), ChangeLevel(map, true) or RestartGame(true)
queues a transient deferred destination and captures a complete immutable player
score snapshot. The first request wins; duplicate exits cannot overwrite it.
A pending SaveGame/ChangeLevel pair commits the zone save first and
queues the outgoing score screen only if that save succeeded. Failed saves do
not load or show the score modal. Values and names survive unloading because
the frozen snapshot is not tied to live edicts; the snapshot and selected tabs
are NOT written to zone saves.
At the end of the frame, the Warcraft game module loads the retail
UI\FrameDef\Glue\ScoreScreen.fdf and binds its ScoreScreenFrame root.
It authors a svc_window with a unique score ID, modal input and the standard
client-owned pause semantics. The same window also carries generic
UI_WINDOW_PRESENTATION, which makes the client draw only the presentation
window over a cleared screen instead of the 3-D world, HUD or other windows.
This is an opt-in generic client capability, not a WC3-specific client screen.
It remains active across tab refreshes and ends when the destination transition
cleans up the old map. Four page buttons switch between Overview, Units,
Heroes and Resources, re-emitting the SAME unique window; Continue uses
blackout_window_command scorescreen_continue. The generic client immediately
blocks further local interaction and fades a black overlay over the score
window for 400 ms of client time. It forwards the command only after reaching
full black, while retaining its modal UI_WINDOW_PRESENTATION surface so the
outgoing world stays hidden. The command is accepted only from an authorized
score recipient. Continue consumes the destination once; normal loading/menu
cleanup subsequently removes the blacked-out window. Late tab refreshes
preserve the fade and cannot restore artwork after it completes.
Only included score-screen players appear. Each explicit SetPlayerOnScoreScreen
override is honoured independently; non-overridden players use map-controller
eligibility instead of including every used/connected slot.
Removed/eliminated players remain visible when eligible.
If the FDF root is absent, the UI cannot be sent, or there are no connected recipients, the engine immediately follows the previously resolved destination. A broken/absent MPQ must not strand the outgoing session.
Retail FDF and artwork: presentation Patch 1
The RoC and TFT UI\FrameDef\Glue\ScoreScreen.fdf files are reportedly
byte-identical (based on the game-data research report of 2026-10-13). The
repo continues to use mounted retail assets, and does not bundle Blizzard
FDF, model or image files. The manually generated-style binding contract in
generated/score_screen.h covers the FDF root/main panel, background, page
tabs, top bar, race artwork, four- and five-column header banks, tab controls,
selected-tab text and OK button. The exact FDF sources were not uploaded;
regenerate/check this binding with fdfbindgen using the extracted FDF when it
becomes available.
The four authored MODEL layers are presented through the generic 2-D
FT_SPRITE path rather than a portrait camera:
| FDF frame | Retail asset and selection |
|---|---|
ScoreScreenBackground |
Authored ScoreScreen-Background MDX; Scorescreen-4Button or Scorescreen-5Button by page |
ScoreScreenPageTab |
Authored ScoreScreen-Tabs MDX; sequence Tab1...Tab4 |
ScoreScreenBarTop |
Authored ScoreScreen-TopBar MDX; TeamColorNN Stand |
ScoreScreenRaceArt |
Skin-resolved race/edition victory model or shared ScoreScreen-Defeat MDX |
The real FDF defines tab/header/button positions and fonts. The score screen
retains its FDF SetAllPoints root instead of being re-centered as a gameplay
window. The original tab controls dispatch the existing scorescreen_tab
command and the original OK control dispatches scorescreen_continue. The
header text uses GlobalStrings.fdf keys rather than hard-coded headings.
Score tables: dynamic-row Patch 2
The retail FDF creates the background, header banks, tabs and scrollbar but not
player score rows. Patch 2 constructs a bounded viewport of ten runtime rows.
Every row owns a stock team-colour bar model (TopBar, CenterBar or BottomBar
according to its position), silver banner art,
original scorescreen-player-{race}.blp icon where supported, name, and up to
five separately positioned numeric cells. Numeric-cell centers reuse the
exact FDF header offsets for 4-/5-column pages, while row start,
height/spacing, text size, tint and icon placement remain PRESENTATION
GUESSES pending retail runtime verification. The bar models select one of
their twelve stock colour sequences by sequence index, because animation names
do not follow the player-colour order. The original FDF's separate ScoreScreenBarTop
is hidden because it was painting over the column headings. The player ordering is the
frozen score snapshot order; retail's exact order/grouping rules are unknown.
The initial Patch 2 pages followed the keys from GlobalStrings.fdf; see the corrective section below for expanded columns:
| Page | Displayed columns |
|---|---|
| Overview | Unit Score, Heroes Score, Resource Score, Total Score |
| Units | Units Produced, Units Killed, Buildings Produced, Buildings Razed, Largest Army |
| Heroes | Heroes Used, Heroes Killed, Items Obtained, Mercenaries Hired, Experience Gained |
| Resources | Gold Mined, Lumber Harvested, Resources Traded, Tech Percentage, Gold Lost to Upkeep |
Raw counters from the frozen snapshot populate known columns. The initial
patch showed - for untracked Heroes Used, Experience Gained and Resources
Traded; these now have tracking/provisional projections as described below.
Technology percentage and component aggregate weights retain explicit GUESS
comments in g_score.c; the overall sum is supported by the retail reference. Highlighting the
highest positive score per column is an additional presentation guess
inferred from the screenshot, not verified retail sorting logic.
Retail's Heroes Used cell is a list of hero portraits with each hero's current level, not an aggregate count. Controlled retail maps show that it follows current ownership, displays duplicate instances separately, retains dead or hidden heroes, and omits removed heroes. See Heroes Used presentation experiments. OpenRealm's scalar hero-entry count is diagnostic data and must not be presented as the retail cell; implementation parity requires snapshotting current player-owned hero instances and rendering their portraits and levels. Static preplaced heroes and campaign-cache restoration seeded by prior mission progression remain unverified.
The snapshot now captures player colour and gameplay elapsed time using
level.framenum * FRAMETIME, capped at 32-bit milliseconds. This reflects
simulation time, not pause time spent reading scores. The existing elapsed
label is FDF-authored; the controller binds its value if available, otherwise
positions a runtime text value near the authored label. This fallback needs
confirmation against the extracted FDF source.
The FDF scrollbar is reused for multi-player overflow. The generic window client forwards an opt-in standalone scrollbar's normalized user scroll position through its authored command, and the game validates the requesting player and frozen result lifecycle before changing which rows are presented. No out-of-range player row is serialized. A short table hides the scrollbar. Scroll position is per recipient and is retained across score tab changes. The scrollbar accepts direct wheel/thumb input; exact drag/track behaviour remains subject to local visual/manual verification.
Missing mandatory FDF frames prevent score-window presentation so deferred navigation can proceed; optional cosmetic/model data is not replaced with OpenRealm art. No local compilation or runtime validation was performed.
Score interactions: Patch 3
The original four- and five-column FDF checkbox buttons now dispatch
scorescreen_sort <column> intents. Sorting is per recipient over a temporary
index array into the immutable match snapshot; it never mutates source scores.
Clicking a column first sorts descending, clicking it again ascending. Missing
statistics always follow real scores and ties retain prior snapshot order.
Changing tabs returns to the snapshot order (sort selection clears) but retains
the viewer's scroll offset. Header checked states display the active column;
blank Units headers are hidden rather than clickable.
The retail ScoreScreenTeamsButton checkbox groups players by numeric team ID
when enabled. GUESS: sorting within each team uses the selected column,
while the initial order uses the outgoing snapshot. This is provisional: exact
retail grouping, team totals and tie rules require retail runtime evidence.
The optional ally-colour FDF control uses the candidate name
ScoreScreenTeamColorButton; the agent's report did not give its exact name,
so if absent no replacement visual control is invented. When present it toggles
the player-strip colour to the lowest-numbered teammate's player colour.
GUESS: this is only a substitute for retail's local ally-colour rules, not
an assertion of parity. Race icons remain the player's actual race.
The generic FDF serialization bridge now preserves ControlShortcutKey as a
window button hotkey. The retail score-screen OK button has shortcut O,
which follows the same recipient-validated scorescreen_continue transition
as mouse click. Escape remains blocked; no second transition is sent when a
stale viewer command arrives after completing the score screen. The optional
Save Replay / Replay Saved controls are hidden until the project has a real
replay-save backend, avoiding inert or misleading UI controls. All new user
commands validate the waiting score-viewer slot and selected page before
altering the view.
All rules above that are explicitly marked GUESS remain open for controlled retail screenshot/run comparison. The original FDF source and full asset set were not provided with the research report, so optional control identifiers still need confirmation against the extraction.
Custom map score-screen artwork overrides
The score screen uses the stock ScoreScreen.fdf model path, not a W3I
background field. The renderer resolves a map-scoped MDX first and falls back
to the installed game model when no imported replacement exists. The MDX's
file-backed texture references use the same map-first lookup; common parchment
texture overrides include scorescreen-background-L1.blp, -L2.blp, -R1.blp,
-R2.blp, and -sort4.blp under
UI\Glues\ScoreScreen\ScoreScreen-Background\.
The renderer's candidate-order regression covers these five paths, alternative
map scopes, stock fallback, and the corresponding background MDX lookup.
It does not substitute for running a real custom .w3x with imported BLPs
and checking the GL output: the test fixture does not ship that proprietary
artwork. The manual comparison should end a match, inspect both four- and
five-column pages, leave to a different map without imports, and confirm the
original textures and model return. Keep the outgoing map scope active until
Continue; do not unmount/reset it merely because the world is not rendered.
Manual validation needed
- Launch a finished single-player mission with
EndGame(true)and inspect model selection, real FDF tabs/headers, font sizes and OK routing. - Compare a defeat screenshot at 4:3 against the provided retail screenshot: check parchment and defeat artwork, absent world/console, column bank, tab animation, OK button, black sidebars on widescreen, row icon/tint/geometry, aligned scores, elapsed time and horizontal/vertical clipping.
- Verify no renderer or presentation flicker when switching tabs, closing the
exclusive score window, or opening with
r_hud 0. - SaveAndChangeLevelBJ: confirm that the script save commits before the score modal opens and Continue loads the intended next map once.
ChangeLevel(..., false)still moves directly, never opening the modal.- Multiplayer: every connected participant sees frozen columns; a Continue from any displayed recipient releases the shared server destination.
- Compare guessed aggregate values against retail under controlled matches.
- With more than ten eligible players, wheel and drag the scrollbar; verify the server displays precisely the expected page without stale hidden rows.
- Click each page's populated column headers twice and verify descending then ascending score order, stable ties, missing values last, and per-player views.
- Toggle Teams and the ally-colour control (if the FDF binds it), and compare the provisional grouping/palette against controlled retail teams.
- Press O on the score screen; it should trigger the same OK action once.
- Compare each Heroes Used portrait/level pair with retail after transfers, hiding, death, revival and removal. Verify the frozen snapshot survives tab changes and that resources follow retail headings rather than guessed totals.
- Ensure a missing
ScoreScreen.fdfproceeds to the destination.
Compilation, gameplay, rendering and the above observations remain for the
local build/test run. AGENTS.md, CONTRIBUTING.md and docs/ui-authoring.md
remain authoritative.
Retail score-row bar models (14 October 2026)
Runtime player rows now use the stock ScoreScreen-TopBar,
ScoreScreen-CenterBar and ScoreScreen-BottomBar MDX models. The first
visible row uses TopBar, the last uses BottomBar when there is more than one
row, and rows between them use CenterBar. The selected animation is addressed
by sequence ordinal (#N@0) so it follows the player palette despite the
nonsequential TeamColorNN Stand sequence names. The FDF's separate
ScoreScreenBarTop model is hidden, removing its previous overlap with the
column headings. Exact runtime row placement, border overlap and appearance
still need visual comparison with retail; no build or runtime verification
was performed for this change.
Retail FDF parser and campaign Quit Mission fix (2026-10-13)
The stock ScoreScreen.fdf uses LayerStyle "NOSHADING" and related layer
styles. The shared FDF parser now consumes and retains NOSHADING,
IGNORETRACKEVENTS and SETSVIEWPORT flags; ignored tracking is propagated
to frame hit-testing. Unlit rendering and viewport scissor enforcement remain
separate renderer concerns (the style bits are retained, not silently dropped).
Malformed FDFs no longer enter the successfully-loaded FDF cache.
The campaign Quit Mission command now queues the same frozen score snapshot
and score-screen transition as EndGame(true), whereas Exit Program and
non-campaign quit retain their existing routes. If the retail score FDF cannot
be parsed or the player cannot be shown the modal, the existing fail-open
transition returns to campaign selection instead of stranding the session.
Validation: exercise both the scripted Defeat -> Quit Campaign dialog and
ESC -> End Game -> Quit Mission on retail MPQ data. The LayerStyle parsing
regression is supplied but local compilation/runtime testing was not performed.
Retail score-data corrections (13 October 2026)
The UndeadX07c reference captures at ~9 seconds show two participants
(Naphos and Faceless Ones), 7 and 50 Units Produced, one level-3 Arthas
portrait in Heroes Used, and 500 Experience Gained for Naphos. The 3 in that
portrait is a hero level, not a count. Total Score = Unit + Hero + Resource.
The reference is evidence for this map, not a proof of general retail weights.
The prior fallback enumerated script-only map factions and returned 550/250
aggregate points from an unrelated Tech Percentage bonus.
The score snapshot now honors each explicit SetPlayerOnScoreScreen(false)
independently; when unset, only W3I human/computer participants appear by
default (plus connected synthetic players without W3I data). It keeps defeated
participants, and uses the local name profile cvar for single-player humans
unless subsequent JASS SetPlayerName overrides it. Nonstandard scripted
controller changes and retail eligibility tie-breaking remain to be verified.
Units Produced now includes preplaced W3I units, successful immediate
CreateUnit units, and completed training; it deliberately does not count
queued trainees at reservation, or transformations as new units. The preplaced
and runtime paths do not double-count script-restored map units because the
existing unit_createorfind resolution reuses entities. Buildings Produced
still measures finished construction only. Largest Army provisionally uses
peak food used, already tracked by the authoritative food subsystem. All
W3I participants now get an initial preplaced-food pass, not only clients that
connect, so AI armies contribute to their own largest-army peaks.
The current Heroes Used presentation snapshots current player-owned hero
instances and displays their portraits with level labels. Experience Gained is
the sum of positive hero XP changes through the common
G_HeroSetXP path, including JASS grants and combat XP. The Heroes page renders
the hero roster and XP value; the Heroes Score formula now includes GUESS
10× XP after the single retail sample (500 XP => 5000 Hero Score). The
pre-existing kill and item weights are GUESS too. The temporary Unit Score
is still weighted by raw event counts rather than Warcraft unit-cost values;
do not expect it to match the retail 12465/2325 yet.
The Units page now exposes all five retail fields: produced, killed, buildings
produced, buildings razed and largest army. Resources Traded shows the retail
gold/lumber presentation, currently using the authored give counters as a
GUESS because bidirectional trade semantics have not been recovered. The
Tech Percentage denominator is still unverified; the UI displays its value
without %, matching the sample reference's typography. Victory/defeat art
selection and the 3-D MDX background are intentionally left to the separate
presentation work.
Save version 91 captures the new hero totals and explicit inclusion marker, rejecting prior save versions according to the project's save policy. This patch does not change the 25 JASS PLAYER_SCORE_* numeric identifiers.
Scripted hero level score separation (2026-10-13 follow-on)
SetHeroLevel and SetHeroLevelBJ use the common hero XP/level transition
without crediting the XP implicitly required by an authored level increase.
Direct SetHeroXP, AddHeroXP, and combat grants still credit positive XP.
This is a deliberate interpretation of Experience Gained as awarded XP
rather than a hero's cumulative XP threshold; verify against retail maps.
At that stage, Unit Score, Tech Percentage, Largest Army, and Heroes Used
still required retail comparison; see the later corrections below.
Cost-weighted scoring and diagnostics (follow-on)
Produced/killed units and built/razed structures now accumulate their authored
UnitBalance gold-plus-lumber costs independently of the raw counts. This is
provisional for retail: exact contribution rules for heroes, kills and
zero-cost/custom campaign units require controlled comparison. The new persisted
value totals require save version 92. Earlier instrumentation traced every
unit and food change; that investigation-only output has since been removed.
The opt-in diagnostic build retains bounded score/result summaries instead.
No mission-specific correction is applied to Largest Army, Heroes Used, XP
or Tech Percentage without verified causality.
Score startup and scripted hero XP attribution (follow-on)
An earlier implementation reset the map-setup food peak after the first
playable frame. Retail comparison proved that reset incorrect; it and the
survivor audit have since been removed. Subsequent food peaks still accumulate
normally.
Scripted SetHeroLevel XP is retained on the hero entity (including across
ownership transfers) and is added to the outgoing score for its current
owner. Ordinary earned XP remains event-attributed. This approximates the
UndeadX07c retail 500 versus the temporary neutral hero's 4700 without map
name/hero ID special cases; other campaign hero restoration rules need checks.
The new entity field requires save version 93. Unit cost values and technology
percentage remain provisional pending further retail evidence.
Retail testing of HumanX03 exposed a stock XP-threshold bug in the formula
extension: OpenRealm used 1000 XP for the scripted level-4 Kael, plus 500 for
level-3 Vashj, producing 15000 Heroes Score. Retail reports 900 + 500 XP and
14000 Heroes Score. NeedHeroXP=200 with the stock A/B/C formula was being
extended using the absolute table index, creating a 500-XP third requirement
instead of 400. The stock progression now uses the measured 200,300,400,...
sequence. Regression coverage is in wc3_combat.stock_misc_hero_xp_formula_matches_retail_thresholds
and wc3_score.stock_level_four_and_three_scripted_xp_total_retail_value.
Largest Army startup, hero and technology investigations
The first-playable-frame survivor enumeration was temporary instrumentation and has been removed. Largest Army retains the peak food usage observed during map initialization even when JASS subsequently removes units; there is no post-main reset or diagnostic full-world scan in normal gameplay.
Hero creation, ownership changes, game-cache restoration and ShowUnit visibility were previously traced during the Heroes Used investigation. Those verbose per-unit traces have been removed; ownership and score calculation remain independent of the diagnostic switch. Retail evidence establishes current-owner portrait presentation; prior-mission cache restoration and static preplaced-hero behavior remain open.
The technology-percentage candidate counts researched levels against
race-matching authored UpgradeData.slk maximum levels. The obsolete
observed-entry calculation and per-upgrade logs have been removed. The
candidate rule remains unverified beyond the current retail comparisons.
Retail Largest Army startup peak correction (2026-10-13)
Retail TFT 1.29.2 Normal-difficulty UndeadX07c tracing reports a peak
food usage of 171 before scripted unit removals. Current food then falls
to 127, but Largest Army remains 171. Keeping the removed units yields
171 current and peak food without changing the 50 Units Produced or 12,465
Unit Score. Consequently, the first-playable-frame survivor audit must not
reset WC3_PLAYER_SCORE_FOOD_MAXUSED to current food. The audit remains
read-only; the food subsystem continues accumulating new peaks.
Retail Hard screenshot reporting 71 has not been reproduced by this Normal
experiment. The unit-cost discrepancy was subsequently resolved by selecting
the correct Custom_V1 game-data set for the map; the Heroes Used difference
was a comparison error because retail's 3 is Arthas's level label.
Score diagnostic cleanup (2026-10-14)
Removed investigation-only unit lifecycle tracing and first-frame survivor
audits. Largest Army remains the highest observed food usage, including
scripted setup peaks, without a post-startup reset or scan. Optional bounded
score summaries are compiled via WC3_DEBUG_SCORE=1 (internal
BZ_WC3_DEBUG_SCORE) and gated by wc3_game_result_debug 1; gameplay and score
accounting remain independent.
Retail Heroes Used portraits and current ownership (14 October 2026)
Controlled TFT 1.29.2 experiments show that the Heroes Used column presents
individual hero portraits with their levels, not a historical numeric
creation count. In a fresh UndeadX07c run, the 3 belongs to level-3 Arthas;
Anubarak, transferred to Neutral Passive, is not listed under Player 0.
The outgoing score snapshot now scans currently owned hero edicts. Each distinct
instance is represented separately, including duplicate rawcodes. Hidden, dead
and revived heroes remain included; finished RemoveUnit and pending deferred
removals are excluded. The snapshot copies entity identity, level and authored
portrait texture before map unload. A unit's owner at snapshot time determines
which player's Heroes page lists it. Hero Score, gained XP and JASS score IDs
are unaffected. The previous persisted heroes_used creation counter has been
removed, requiring save version 95.
The generated score row uses the authored unit command-button art, with a
small level label. Presentation approximation: native score-row placement
and level badge styling are not available in the supplied FDF. Four portraits
fit in the existing five-column row; larger hero rosters show a +N overflow
indicator while retaining the full count for sorting. This is a bounded UI
policy, not a recovered retail overflow rule. Retest the portrait sizes at
4:3 and wide resolutions. Preplaced/cross-mission cached hero edge cases
are still pending separate retail verification.
Hero level and tab paint order (14 October 2026)
The Heroes Used portrait levels are placed immediately to the right of their portraits instead of over the bottom-right corner. The FDF tab strip's stock MDX is a single composite sprite; it is drawn in the generic late-sprite pass, after content. Its tab caption text is drawn in a generic late-text pass with dedicated flag ranks 3 to 0 (Resources, Heroes, Units, Overview), so captions remain on top of the model. This changes caption order, not the internal mesh/material order of the stock composite MDX. Verify parchment overlap visually against retail before treating that part as fixed.
Score tab caption and composite-geoset correction (14 October 2026)
A screenshot after the previous presentation patch showed 0 instead of the
Units/Heroes/Resources labels: the old late-caption code assigned 1/2/3 to the
Stat binding, which the generic client resolves as player-stat text. The
caption paint rank is now transported in distinct high bits of ui_flags,
leaving Stat and the actual localized text untouched. The existing hero
portrait/level side-by-side layout stays unchanged.
The late text flags retain the four tab captions in Resources/Heroes/Units/
Overview draw order without changing Stat bindings. See the endpoint fix
below for the actual cause of the overlapping tab artwork.
Score tab one-shot animation endpoint (14 October 2026)
Inspection of the retail ScoreScreen-Tabs.mdx and its scorescreen-tabs.blp
found five overlapping alpha-key geosets, one for each Tab1–Tab5
sequence. Each GEOA track has static alpha 1 and keys with alpha 0 at the start
of every other sequence. Thus only the selected tab is meant to be visible;
these are not independent tabs that should be sorted by horizontal position.
The UI sprite path previously clamped a non-looping sequence to its authored
interval[1] (for example, 333 for Tab1), while
R_FindSequenceAtTime() uses an exclusive end. After that hold elapsed, the
frame was outside every sequence: GEOA lookup returned without applying the
inactive tabs' zero-alpha keys, so all five static-alpha-1 geosets were drawn.
Their overlapping quads produce the hard vertical seams seen in screenshots.
One-shot UI sprite sampling now freezes at interval[1] - 1, just like the
already end-exclusive @ratio path, preserving the current sequence for
geoset and material animation evaluation. The unused opt-in right-to-left
blended-geoset sorting feature was removed. This is a generic sprite animation
fix rather than a score-screen-specific override. Confirm Tab1–Tab4 after
at least 1 second of display and while switching tabs; no build or runtime
verification was performed while preparing the patch.
Post-game lifecycle Patch 1 — finalization boundary (14 October 2026)
G_QueueScoreTransition now records only the first destination. The immutable
result snapshot is captured once in G_FlushScoreTransition, after the frame
has drained deferred removals and completed pending scripted saves. In
particular, late-frame unit removal and score changes are not hidden by an
early EndGame(true) or ChangeLevel(..., true) call. No next-map loading is
submitted before Continue (or the existing missing-UI fallback).
For result-dialog commands processed while script/modal pause is active,
G_ClientCommand calls the game-owned paused finalization helper after its
synchronous handler has returned. That helper drains terminal result events,
deferred unit/item removals and dependent scripted saves before capturing and
presenting results. Unpaused commands still use the normal G_RunFrame tail.
This patch does not stop the simulation permanently; match-complete pause
ownership and gameplay command rejection belong to Patch 2. The outgoing
map/game module and its score artwork remain loaded until the existing
Continue transition.
Regression coverage should exercise a score change after queueing and before finalization, deferred hero removal, a paused result command with no subsequent server simulation frame, and unchanged unpaused menu deferral.
Post-game lifecycle Patch 2 — terminal single-player pause (14 October 2026)
After G_FlushScoreTransition successfully writes a score window to exactly
one connected viewer in a single-human map, the WC3 module marks the outgoing
match terminal and recomputes the existing authoritative server pause. This
is a distinct, irreversible pause reason until map load/shutdown, so a late
PauseGame(false) or client modal close cannot restart gameplay. The server
continues processing client network messages while paused; the WC3 command
entry point only accepts score-screen Continue, tab, sort, scroll, teams and
ally-colour intents, while ClientInput ignores gameplay camera input.
G_RunFrame also refuses to advance a terminal match if another pause owner
unexpectedly releases the server scheduler. The terminal guard survives the
one-shot Continue action until the destination starts loading.
Only a successfully presented single-viewer score screen enters this state.
Missing FDF/no-recipient fallback keeps its existing immediate destination,
and matches with multiple connected clients retain their prior behavior.
This patch does not change score construction, the network wire format, save
format, or engine rendering. Explicit external engine-level SV_SetPaused(false)
can still advance the server clock by at most a scheduling tick before the
WC3 frame guard takes effect; preventing that would require a separate generic
pause-ownership contract and is not implemented here.
Headless regression coverage verifies pause ownership after script/modal release, unchanged gameplay frame/time, blocked client input and gameplay commands, continued score interaction, multi-client compatibility, and the Continue-to-new-map guard lifetime. Local compile/runtime verification remains outstanding.
Post-game lifecycle Patch 3 — exit and disconnect safety (14 October 2026)
The first accepted score-screen exit owns the destination and, for a level
transition, its resolved map and current/default difficulty pair. A subsequent
EndGame, Quit Mission, RestartGame, ChangeLevel, scripted save/load, LoadGame,
or campaign-select intent cannot overwrite that ticket, reset its staged
campaign difficulty, or initiate another loading/menu action while it is
pending. Once the terminal single-player score screen has consumed Continue,
the same guard remains in effect until map replacement clears the transient
state. A separate transient destination_submitted latch also protects the
missing-FDF/no-recipient fallback: the first action stays one-shot without
claiming a terminal pause when no score window was shown. No save-format or
network message changes are introduced.
The score window remains server-authored and continues using its existing
recipient checks. While the simulation is paused, G_ClientDisconnect
checks the presented score transition immediately after releasing the client
modal. The first disconnect in a multi-viewer score leaves the destination
pending; when the last authorized viewer leaves, the stored destination is
consumed once without waiting for another G_RunFrame. A paused disconnect
before score presentation also uses the existing paused finalization path,
including deferred removals and script-save ordering. With no recipients or
no valid FDF, the existing fail-open path executes the destination without
claiming terminal pause ownership.
Regression tests cover the final viewer leaving during terminal presentation, multiple score viewers disconnecting in turn, missing recipients, repeated Continue, and competing exit intents attempting to replace the first staged campaign difficulty. Runtime artwork/FDF failures still require a separate retail-archive validation run. Patch 4 owns integration/visual verification.
Score-row banner and ally-colour control (14 October 2026)
The silver player-name bevel now extends from 0.153 to 0.180 FDF units. The Heroes Used portrait and level text move down 0.004 units to align the badge baseline. These are presentation adjustments and still need screenshot comparison.
The ally-colour button now references the stock
scorescreen-button.blp and scorescreen-button-down.blp state images through
hidden texture child frames; the button selectors contain frame names rather
than image paths. Its server-owned scorescreen_allycolors command is
unchanged. With ally colours enabled, rows are coloured relative to the viewer:
blue for self, teal for same-team allies, and red for other teams. Disabled
mode preserves each player's original colour. This replaces the previous
lowest-numbered-teammate mapping, which did not distinguish solo teams.
No local build or runtime tests were performed. Verify the stock button states visually, then toggle the control in a game containing allied and enemy rows.
Score-row silver bevel and ally-colour icon follow-up (14 October 2026)
The silver team-name ribbon is stock ScoreScreenBannerLoss artwork and its
BLP includes transparent margins. The row frame is expanded from 0.180 x
0.031 to 0.205 x 0.039 FDF units, centred vertically across the score-bar
height. These sizes are screenshot-derived and need a retail visual check; the
source image, text and bar animations are unchanged.
The original score button textures are border/chrome, not the handshake
artwork. Use skin-defined MiniMapAllyButtonInactiveEnabled/Pushed and
MiniMapAllyButtonActiveEnabled/Pushed, already present in stock
War3Skins.txt, for the two toggle states. The existing FDF button remains
clickable and preserves its server-owned scorescreen_allycolors command.
Remove its decorative parent BACKDROP's gold edge and background (without
hiding the interactive button), avoiding double borders. Since the control
is a GLUEBUTTON rather than a checkbox, the selected art is explicitly
updated in ScoreScreenSetPage from the viewer's ally-colour state.
No compile/game run has been done. Verify skin artwork availability for each race, the on/off icon appearance, button click target, and ribbon coverage of the Heroes Used column in an in-game screenshot.
Score-row ribbon height and ally-button border (14 October 2026)
Retail comparison shows that the silver team-name ribbon nearly fills its
coloured score row. The stock scorescreen-silverbannerborder.blp contains
transparent vertical padding, so its UI texture frame is expanded from
0.205 x 0.039 to 0.205 x 0.051 FDF units, adjusting the top anchor by
0.006 to keep it centred. This is a screenshot-derived visual adjustment.
The ally-colour button uses the skin-provided handshake images rather than the generic score-button chrome. Preserve the stock parent BACKDROP's gold border/background: it frames the icon once, without the prior duplicate chrome. Button state and server-owned ally-colour switching are unchanged. No compilation or in-game testing was performed; compare the resulting bevel coverage and button frame with the retail screenshot.
Score ribbon/button sizing refinement (14 October 2026)
Comparing retail and OpenRealm screenshots at their respective UI scales, the preceding 0.205 x 0.051 ribbon texture frame covered too much of the coloured score row. Use 0.198 x 0.047 with the same stock silver BLP; this retains the ribbon bevel and avoids changing the team icon or score cells.
The ally-colour button already has the correct skin-dependent handshake icon and server-backed click action. Resize both its existing FDF gold-border BACKDROP and the button hit frame to 0.0205 x 0.0185, avoiding a second frame texture or a mismatched hit area. These are screenshot-derived dimensions; visual parity should be rechecked at the same canvas/aspect policy. No compile or runtime validation was performed.
Post-game lifecycle Patch 4 — integration checks and acceptance (14 October 2026)
The post-game sequence is finish JASS/frame work → finalize the frozen
score snapshot → enter terminal single-player pause when the score window is
presentable → accept score-only commands → consume Continue once → begin the
existing menu/map destination. The outgoing game module, archive scope and
renderer registrations remain resident until Continue (or no-viewer/failure
fallback); a complete SV_Shutdown before scores is not implemented.
No client-specific Warcraft routing, new svc_* protocol, or save version is
introduced by this lifecycle.
The headless wc3_score_postgame tests now include a genuine
globals.RunFrame() transition with a queued deferred hero removal, exercising
frame-end ordering before the one-shot no-viewer menu action. A second
integration case uses actual G_ClientCommand entry points to switch tabs,
sort and scroll while terminal, reject unapproved commands, verify that
globals.RunFrame() remains inert, and consume Continue only once. The
ChangeLevel(..., false) inverse verifies immediate navigation without a
terminal pause. Earlier tests cover paused result commands, scripted saves,
disconnections, the first-exit/difficulty contract, and multiplayer
non-interference. Renderer renderer_model.score_screen_background_* tests
cover map-first MDX and all five BLP candidate paths, stock fallback, and
scope replacement; they test lookup, not real GPU artwork.
Local build/test checklist (not executed while authoring):
- Run the Warcraft III headless engine target (
make test-wc3-engine) and full project tests (make test). Run the post-gamewc3_score_postgame.*selection with the existing+testmechanism if isolating a failure. - Verify
EndGame(true), campaign Quit Mission, andSaveAndChangeLevelBJat 4:3 using an installed retail asset set; wait on the score screen while observing that units, AI, timers, script work and gameplay time do not advance. Confirm tabs, sort, scroll, team options and the O Continue shortcut still respond during terminal pause. - Confirm no loading plaque or campaign selector before Continue.
Observe the correct campaign difficulty and destination after Continue.
On OK (or its O shortcut), verify that the score artwork fades to black over
400 ms without exposing an old gameplay frame, and that the black interval
ends when the next loading screen or campaign selector is ready. Double
click/press O to verify the pending transition is not submitted again.
Check
ChangeLevel(..., false)andEndGame(false)bypass scores. - Repeat from a paused victory/defeat choice and with a queued cinematic; ensure the result is not stranded after the script returns. Cancel or disconnect the final viewer; ensure exactly one deferred destination.
- For custom artwork, import the five stock-path parchment BLPs and,
separately, the stock-path background MDX into a disposable
.w3x. Inspect all four score tabs after several seconds; then Continue to an unmodified map and confirm that its stock score artwork returns. Inspect both model and texture resolution and watch for stale or missing textures after map cleanup. - Confirm multiplayer behavior is unchanged: no terminal single-player pause is introduced for multiple connected viewers, and disconnect/OK still uses the existing shared-transition policy.
Outstanding evidence: The headless tests do not exercise the full
SV_Frame network scheduler or the GL sprite path, and there is no bundled
retail ScoreScreen.fdf or proprietary score artwork in the test assets.
Therefore pixel parity, imported-image rendering, cinematic interposition,
actual map-loading-screen timing and direct engine-level pause releases still
require the targeted local integration run. Avoid calling these verified
until that run succeeds.
Buildings Produced from preplaced and scripted units (14 October 2026)
Retail Prologue02 reports 7 Buildings Produced for both Alliance Forces
and Player. OpenRealm previously credited only completed worker construction.
The ordinary enter-play score path now credits an already-finished structure
as Buildings Produced (and its authored cost as structure value), rather than
ignoring it or classifying it as a trained unit. Map preplacements call this
path after spawning; JASS CreateUnit already calls it. Worker-constructed
buildings continue to earn exactly one building credit on construction finish,
not at the beginning of construction. This corrects the missing accounting
path without map-specific score constants. The retail 7/7 result remains a
runtime acceptance check, not a value hard-coded into the implementation.
Resource Score retail parity (14 October 2026)
Controlled retail TFT 1.29.2 experiments verified Resource Score = cumulative
Gold Mined Total + cumulative Lumber Harvested Total with no divisors or
rounding: gold-only 0–1,000, lumber-only 1–200, and mixed (1,1), (5,7),
(37,63) all matched. W3I starting balances did not count, whereas a positive
SetPlayerStateBJ resource grant updated the gathered counters through
AdjustPlayerStateBJ and therefore scored. Raw writes to gathered counters
changed Resource Score; raw current-balance writes and subsequent spending did
not. Physical harvesting of 80 gold or 30 lumber also scored one-to-one.
The Prologue02 script grants 90 lumber through SetPlayerStateBJ, explaining
its retail 90 Resource Score without actual harvesting. The implementation
uses a 64-bit intermediate sum and clamps to INT32_MAX at the public score
boundary. Multiplayer trading, independent upkeep effects, and some custom
resource-grant paths remain unverified and are not included speculatively.