Warcraft III minimap markers
OpenRealm keeps four minimap concepts separate: the authored gameplay minimap,
automatic live entity contacts, transient pings/alerts, and preview-only
war3map.mmp icons. This document covers the automatic live contact path.
See also alerts and minimap pings, team colors, fog and cinematics, minimap input routing, order queues, loading and assets, and the generic server-selected presentation contract.
Ownership and engine boundary
WC3 gameplay owns contact classification and recipient visibility. Shared engine code only transports opaque presentation values:
entityState_t.effect_flagsbits 13-15 are the genericEFX_GAME_VARIANT_*payload. WC3 interprets its0..7values aswc3MinimapContact_tingames/warcraft-3/common/minimap.h.playerState_t.stats[UI_PLAYERSTAT_GAME_VARIANT]is the generic local game-presentation value. WC3 uses it for the ally-colour filter and exposes the semantic aliasWC3_PLAYERSTAT_MINIMAP_ALLY_COLORonly undergames/warcraft-3/.renderEntity_t.owner,renderEntity_t.team,renderEntity_t.effect_flags,viewDef.player, andviewDef.game_variantare generic renderer inputs.client/cl_view.ccopies them without knowing WC3 marker classes or ally colour meanings.
This preserves the engine/game boundary in ARCHITECTURE.md: no WC3 marker
enum, skin key, or ally-filter meaning belongs in common/, client/,
renderer/, or server/.
The wire structs are not widened. The change assigns semantics to three
previously unused effect_flags bits and one existing stats[] slot; the
entity delta mask and entityState_t/playerState_t sizes are unchanged.
tests/test_net.c round-trips all eight values of the generic three-bit entity
variant.
G_CustomizeEntity() chooses exactly one automatic WC3 contact per recipient.
game_export.IsSnapshotPriorityEntity lets the game identify visible contacts
that should survive ordinary snapshot saturation; the server keeps those ahead
of non-contact entities while retaining the existing per-frame packet limit.
If prioritized contacts themselves exceed MAX_PACKET_ENTITIES, the nearest
prioritized contacts still win.
The contact values are:
WC3_MINIMAP_CONTACT_NONEWC3_MINIMAP_CONTACT_UNITWC3_MINIMAP_CONTACT_BUILDINGWC3_MINIMAP_CONTACT_HEROWC3_MINIMAP_CONTACT_GOLD_MINEWC3_MINIMAP_CONTACT_GOLD_ENTANGLEDWC3_MINIMAP_CONTACT_GOLD_HAUNTEDWC3_MINIMAP_CONTACT_NEUTRAL_BUILDING
The marker is presentation metadata, not a second simulation entity. Existing
snapshot/FOW/invisibility filtering remains authoritative. Dead units and units
that remain RF_HIDDEN after recipient-specific visibility handling publish no
automatic contact. The WC3 renderer only considers top-level render entities with
a non-zero snapshot entity number, so client-created secondary model2 draw
entities cannot create duplicate contacts and generic client code need not mutate
the opaque variant.
Unit-data classification
WC3 object data is evaluated on the game side before the renderer sees a contact:
hideOnMinimap(uhom) controls the ordinary unit/building representation.hideHeroMinimap(uhhm) independently controls the dedicated Hero marker. With only the Hero marker disabled, the entity falls through to its ordinary representation; when the generic minimap display is also hidden, no automatic marker is published. This matches the World Editor distinction: the Hero field hides the special Hero symbol, while the generic field controls the ordinary unit/building/Hero minimap display. Historical editor guidance likewise notes that both switches are required to remove a Hero from the minimap completely.neutralBuildingMinimapIcon(unbm/nbmmIcon) selects the special neutral building contact.
Gold-mine classification is data/ability driven. Natural resource mines use the
Gold Mine contact. Overlay mines carrying Aegm or Abgm select Entangled or
Haunted contacts respectively; a known overlay mine without either racial
ability uses the normal Gold Mine contact.
Compatibility evidence
The two minimap-hide fields are independently documented World Editor additions,
not names invented by OpenRealm: Warcraft III patch 1.13 added Stats - Hero -
Hide Hero Minimap Display, while patch 1.15 later added the generic Stats -
Hide Minimap Display field. Preserved patch notes are available at
https://warcraft.wiki.gg/wiki/Warcraft_III/Patch_1.13 and
https://liquipedia.net/warcraft/Patch_1.15. World Editor field documentation
describes the Hero field as hiding the special Hero symbol and the generic field
as controlling minimap visibility; historical editor guidance reports that both
are needed to completely hide a Hero. The resulting Hero-to-ordinary fallback is
covered by OpenRealm's contact-classification tests.
For colour presentation, Blizzard's classic recon documentation describes the
allied-colour minimap toggle making enemies red
(https://classic.battle.net/war3/basics/recon.shtml). Contemporary Frozen
Throne beta notes preserved at
https://forum.dvdtalk.com/video-game-talk/271375-warcraft-iii-expansion-beta-test-printthread.html
record the minimap relationship palette as self white, allies teal, enemies red,
and creeps black. The screenshot comparison that found
OpenRealm's own contacts should be white is captured as an automated policy test
rather than relying on future manual recollection.
Marker artwork and asset scope
Live special marker artwork is not hard-coded to file paths. WC3's renderer
loads the Game Interface values after R_SetMapAssetScope() has activated the
current map, resolving war3mapSkin.txt [CustomSkin] before stock
UI\\war3skins.txt [Default]:
| Contact | Game Interface key |
|---|---|
| Gold Mine | MinimapResourceTexture |
| Entangled Gold Mine | MinimapEntangledResourceTexture |
| Haunted Gold Mine | MinimapHauntedResourceTexture |
| Neutral building | MinimapNeutralTexture |
| Hero | MinimapHeroTexture |
The renderer reads these text files through its import filesystem, so map
archive precedence remains active. A malformed optional war3mapSkin.txt is
diagnosed once; fields that cannot be read then use valid stock [Default]
values. A missing/empty field or a resolved path that fails to load is diagnosed
and uses the renderer's visible placeholder texture.
There is deliberately no demotion from a missing special icon to an
ordinary unit/building marker: that would hide an asset/data error and violate
the repository's no-silent-fallback rule.
Stock skin textures use the normal pinned texture lifetime. Map-skin overrides
use the renderer's streaming texture cache and are reclaimed on the next map
registration if no persistent renderer consumer has pinned them. INI cache
parses own their table/text allocations; Stb_IniCacheFree() releases them.
The fixture MPQ includes all five stock keys and a map-level Hero override.
Loading/game-setup previews remain a different path. Existing
war3map.mmp types still use their preview artwork and do not create live
contacts.
Rendering and colours
R_DrawMinimap() draws live contacts after the fog texture and before the
camera outline/border. The existing world-to-minimap transform is reused, so
contacts share rectangular-map letterboxing with pings, camera projection, and
minimap input.
For ordinary unit/building contacts:
- the local player's contacts are white in every ally-filter state;
- state
0leaves non-local contacts in their already-resolved presentation team colour (renderEntity_t.team); - states
1and2use relationship colours for non-local contacts: ally teal, hostile red, and neutral player slots/creeps black.
G_SelectionRelation() and EF_NEUTRAL retain their selection/hover meaning:
an allied player without shared control can still be selection-neutral. The
minimap policy does not treat that flag as a neutral creep. It recognizes
neutral player slots as black, recipient-relative EF_HOSTILE as red, and
other non-local owners as teal. EF_NEUTRAL is published only for hoverable
entities where its existing world/hover semantics require it. The WC3 renderer
does not reconstruct alliance policy from team numbers.
Hero artwork receives relationship tint when the minimap ally-colour filter is
active. Mine and special neutral-building artwork retains its authored icon
colour.
SetAllyColorFilterState is local presentation state stored in
WC3_PLAYERSTAT_MINIMAP_ALLY_COLOR (the WC3 meaning of the generic
UI_PLAYERSTAT_GAME_VARIANT). currentplayer calls affect only that player's
value; calls outside a local-player context update all clients. Values are
clamped to the three WC3 states 0..2. State 2 currently has the same minimap
result as state 1; retail's separate world-model recolouring for state 2 is
not implemented by this marker work.
Get/SetCreepCampFilterState remains the pre-existing placeholder state. It is
not moved into the minimap presentation stat because creep-camp aggregate
markers are not implemented yet.
Marker size calibration
OpenRealm's WC3 UI uses FDF canvas units, not raw pixels. A prior patch passed
Warsmash-reference pixel values directly to R_DrawImage(), which made markers
several UI canvases wide. A later comparison used the inner minimap rectangle in
a retail screenshot and an OpenRealm screenshot to normalize away the different
screenshot resolutions; that comparison showed the corrected first pass still
about twice retail size.
The current screenshot-calibrated FDF sizes are therefore:
| Marker | FDF size |
|---|---|
| ordinary unit | 0.002 x 0.002 |
| ordinary building | 0.005 x 0.005 |
| Gold/Entangled/Haunted/neutral special | 0.0105 x 0.0105 |
| Hero | 0.014 x 0.014 |
wc3_minimap_marker_size() and wc3_minimap_marker_rect() own the size and
centered-rectangle path used by the renderer. wc3_minimap.marker_sizes_match_retail_capture_calibration
locks the values and rectangle geometry into an automated test. These values are capture-derived OpenRealm
calibration, not independently recovered retail engine constants; like-for-like
retail captures may refine them later without changing the snapshot contract.
wc3_minimap.ordinary_color_policy_keeps_self_white likewise records the second
visual regression found during comparison: self contacts must stay white instead
of using the local player's team colour.
Layering with other minimap systems
The gameplay minimap order is:
- authored
war3mapMapimage; - live fog overlay;
- automatic entity contacts;
- camera outline and minimap border;
- client transient pings/alerts (
CL_DrawMinimapPings).
Pings are one-shot attention events and do not use the automatic-contact
variant. Scripted minimapicon handles will need their own texture/tint,
attachment, lifetime, and fog-state contract rather than reusing these entity
bits.
Known gaps
The automatic-contact patch intentionally leaves unrelated or insufficiently verified systems separate:
- ally-colour state
2does not yet recolour world models; - Hero pulse timing/alpha is not retail-calibrated;
- the server still has a hard snapshot capacity; if visible minimap contacts alone exceed it, the nearest prioritized set is retained;
- creep-camp strategic markers and filter buttons are not rendered;
CreateMinimapIcon*,DestroyMinimapIcon, visibility/orphan lifetime,SetAltMinimapIcon, andUnitSetUsesAltIconare not implemented as live minimap objects;- minimap waypoints are not implemented;
- exact per-marker dimensions and overlap precedence can still be refined by like-for-like retail captures;
- retail persistence of the user's minimap filter preference across map/session transitions is not implemented by this server-authored presentation state.
Verification
Automated coverage is intentionally split by ownership:
tests/test_net.cverifies the genericEFX_GAME_VARIANT_*wire packing, without WC3 semantic names.games/warcraft-3/game/tests/t_api.cverifies WC3 contact classification, Hero/generic hide behavior, mine variants, hidden/dead suppression, recipient relationship publication, ally-filter clamping, andcurrentplayerlocality.games/warcraft-3/game/tests/t_minimap.cverifies the screenshot-derived marker-size and rectangle path, passive-ally customization through the minimap colour policy, fixture-MPQ stock/map-skin lookup, pinned versus map-scoped loader selection, placeholder selection, and stock-default resolution after an invalid optional map skin. The fixture test exercises the production asset resolver and loader selector without creating a GL context or calling the renderer's fullR_RegisterMap()entry point.tests/test_renderer_model.ccalls the production WC3R_RegisterMap()againsttests.mpqand its nestedMapOverlay.w3xfixture. It verifies that the imported Hero texture is cached as streamed, then reclaimed when a second map has no override, while stock skin textures stay pinned. The renderer texture cache and map-registration function are production code; filesystem reads and GL texture loads are test imports, so this checks archive lookup and cache lifetime without a GL context or pixel upload.games/warcraft-3/tests/test_server_net.cverifies that a game-prioritized minimap contact survives ordinary entity snapshot saturation.
The focused WC3 checks can be run with:
make test-wc3-engine WC3_PATTERN='wc3_minimap.*'
make test-wc3-engine WC3_PATTERN='wc3_api.customize_entity_*minimap*'
make test-wc3-engine WC3_PATTERN='wc3_api.ally_color_filter_*'
make test-server-net
The generic wire round trips remain part of make test (net.game_presentation_variant_stat_roundtrips
and net.entity_delta_preserves_game_presentation_variant_bits).
Visual verification is still required for framebuffer-only properties such as final texture appearance, overlap, Hero animation/pulse, and future fine-scale calibration. The automated tests are intended to prevent the two already-found size and self-colour regressions from returning.