World Systems¶
Summary: Project Eternal's world systems include the generation-native automap (
UAutomapStateComponent— a server-built replicated projection of the dungeon layout, revealed by a containment scan and drawn as a Slate-painted parchment floor plan), procedural level generation via the topology-based Dungeon System (UEternalDungeonGenerator, see Dungeon System), and global event tracking inUQuestManagerComponent. Replicated state usesFFastArraySerializerfor efficient networking. Cosmetic layers — passive world-space tutorial hints (AWorldHintActor) and preset-driven ground fog mood (AGroundFogVolume) — are client-only/non-replicated and layer over GAS, the quest system, and the lighting subsystem.
Table of Contents¶
- Why This Architecture
- System Architecture
- Automap (Generation-Native v2)
- Procedural Floor Generation
- Global Event System
- World Hint System
- Local Ground Fog Volume System
- Source References
- Related Systems
- Recent Changes
Why This Architecture¶
Design Goals¶
The world systems are built around four principles:
- Zone Independence - Each zone maintains separate exploration data
- Efficient Replication - Delta-compressed arrays minimize network traffic
- Server Authority - All state changes originate on server
- Data-Driven - Procedural generation uses seed-based determinism
Component Ownership¶
| Component | Owner | Why |
|---|---|---|
UAutomapStateComponent |
GameState | Party-shared map projection + reveal (decision: exploration is run-scoped and shared) |
UQuestManagerComponent |
GameState | Global event tracking |
System Architecture¶
+------------------------+
| AEternalGameState | (Server Authority)
+------------------------+
|
+-- UQuestManagerComponent
| |
| +-- Quest Registry
| +-- Global Events (FGameplayTagContainer)
|
+-- UAutomapStateComponent
|
+-- FAutomapLayoutHeader (epoch + transform, ReplicatedUsing)
+-- FAutomapNodeArray (FastArray: footprints, reveal, state)
+-- TArray<FAutomapDoorEdge>
+-- FAutomapMarkerArray (FastArray: actor markers)
Replication Strategy¶
| System | Replication Type | Why |
|---|---|---|
| Automap projection + reveal | Shared FastArrays, all COND_None | Party-shared exploration; GameState has no owning connection, so COND_OwnerOnly here replicates to NOBODY on a dedicated server |
| Global Events | FGameplayTagContainer | Lightweight tag replication |
| Floor Data | Not replicated | Deterministic seed recreation |
Automap (Generation-Native v2)¶
The old scene-capture/fog automap (UMapExplorationComponent, UWorldPOIManagerComponent,
AAutomapCameraActor, render targets, radial fog masks, zone-name strings) was clean-replaced in
2026-08. Design source of truth: ImplementationDocs/AutomapRedesign.plan.md (13 ratified product
decisions, REQ-1..23 — the plan is binding for any change here). Governing principle (decision 12):
the map is a memory, not an oracle — it records only what the player has witnessed in-world. No
room-kind tints, no unrevealed intel; a future map feature must pass "would the player know this from
standing there?".
Data Plane — UAutomapStateComponent (GameState)¶
The server flattens the two layout datasets (semantic topology in UDungeonSubsystem ⊕ the
ProceduralDungeon plugin's spatial graph) into one compact, immutable, replicated projection, built
once per dungeon from the post-generation callback (never from observed room counts — generator
retries alias):
| Piece | Shape | Notes |
|---|---|---|
FAutomapLayoutHeader |
ReplicatedUsing struct |
Epoch LayoutId (new FGuid per dungeon), expected counts (completeness gate), lattice transform + RoomUnit, floor origin, domain tag |
FAutomapNodeArray |
FastArray | Per node: dense NodeId, XY footprint (max-exclusive), Z span, kind (Content/Environment/RemnantRealm), flags (reveal bit + witnessed room state), structural MarkerTag |
DoorEdges |
plain replicated array | Node pairs + door cell/direction; edges on the boss node render as the boss threshold |
FAutomapMarkerArray |
FastArray | Actor markers, epoch-stamped (stale node ids resolve to real-but-wrong rooms otherwise), optional world-XY hub path + quest gates |
Key invariants (each traces to a REQ; tests in Private/Tests/Automap/):
- Everything replicates COND_None — GameState has no owning connection.
- Completeness gate: clients never build a map from partially arrived arrays; HasMap() also
requires ExpectedNodeCount > 0 so a reset layout is never trivially "complete".
- Listen-server funnel: every mutation notifies through shared Handle*Changed methods called
from both the authority mutator and the replication callbacks (OnReps don't fire for the host).
- Reveal is a ~4 Hz server containment scan (pawn world position → lattice cell → node), NOT
door events — door-based room tracking is single-player-global and freezes on locked-room
rejections. Spectators/dead players don't reveal; the scan holds during transitions.
- Initial burst is budgeted (~5 KB/client/dungeon); BuildProjection logs the estimate and a spec
asserts a worst-case dungeon stays inside it.
Markers¶
- Structural (entrance/exit/boss/landmark/Remnant Realm portal) are resolved server-side into
node
MarkerTags at build time — zero registration, the "POI didn't register" failure class no longer exists. - Actor markers:
UAutomapMarkerComponenton the actor (NPCs, portals, objective devices). Dual-path server registration — the component push-registers on BeginPlay (with retry) and the state component pull-scans levels as they add to the world; the MarkerId derives from the owner's pathname so both paths are one idempotent registration. Static-position by contract. - Quest pins (decision 9): the replicated entry carries only world facts
(
VisibleWithQuestTag/HiddenByQuestTag); each client derives pin visibility from its OWN quest state at plan-build time, fail-closed.
Rendering + UI¶
Client-side: UAutomapController binds to the GameState component on a retry ticker (the GameState
changes on every travel) and rebuilds FAutomapPlan (presentation struct) on change; SAutomapCanvas
paints it — parchment ground, ghost plates for adjacent unentered rooms, a merged uniform wash for
revealed space, an ink boundary hull with gaps at connection spans (decision 13: passage = gap in
the ink, no door glyphs), witnessed-state tints, marker sigils with screen-space declutter (merge
radius + count badge), edge chevrons for off-view markers, party arrows, player arrow. Two surfaces:
- UAutomapMinimapWidget — always-on HUD corner minimap, player-centred, camera-aligned (the view
rotates by the gameplay camera's world yaw so screen-up on the map = screen-up in the world).
- UAutomapOverlayWidget — the N-key overlay (Tab is target cycling) with drag/wheel and gamepad
pan/zoom; fits to revealed bounds only so the dungeon's size never leaks. Its input actions live
in IMC_Game_Default — the Gameplay IMC is removed while a panel is open.
- Hubs (no layout): same canvas renders parchment + arrow + world-positioned markers; no floor plan.
Camera alignment is paint-time only — both surfaces rotate the view to the gameplay camera's yaw
and foreshorten its depth axis to the camera pitch in SAutomapCanvas::OnPaint (pan input inverts
both). The angles are PINNED as style values (ViewYawDeg = 50, ViewDepthScale = sin 30° = 0.5)
rather than read from the live camera, so cinematic/debug cameras never warp the map — retune them
with the rig in AEternalPlayerCharacter. Map SPACE stays north-locked.
Coordinate derivation (half-cell conventions, north lock) is documented in
Public/UI/ViewModels/AutomapPlan.h and locked by golden specs. Debug: Cog window +
Automap.RevealAll / Automap.RevealRoom / Automap.ClearReveal.
Procedural Floor Generation¶
Superseded. The seed-based
UFloorUtilities/FSaveFloorData/FSaveRoomDatafloor generator described here no longer exists. Procedural level generation is now handled by the topology-based Dungeon System (UEternalDungeonGeneratorproducingFDungeonInstancefromUDomainDungeonConfig, room content inUEternalRoomData). See Dungeon System for the current architecture, room configuration, and boss/landmark placement.
Global Event System¶
Event Architecture¶
UQuestManagerComponent
|
+-- ActiveGlobalEvents (FGameplayTagContainer)
| |
| +-- Replicated with OnRep
|
+-- QuestRegistry (TMap<Tag, DataAsset>)
|
+-- Delegates
+-- OnGlobalEventStarted
+-- OnGlobalEventEnded
Event Operations¶
| Operation | Authority | Effect |
|---|---|---|
StartGlobalEvent(Tag) |
Server only | Add tag, broadcast start |
EndGlobalEvent(Tag) |
Server only | Remove tag, broadcast end |
IsGlobalEventActive(Tag) |
Any | Query current state |
Event Flow¶
Server: StartGlobalEvent(Event.Boss.Phase2)
|
v
ActiveGlobalEvents.AddTag()
|
v
OnGlobalEventStarted.Broadcast()
|
v
Replication to clients
|
v
Client: OnRep_ActiveGlobalEvents()
|
v
Client systems react to new event
Common Event Tags¶
| Tag | Purpose |
|---|---|
Event.Boss.Phase1 |
Boss phase tracking |
Event.World.Invasion |
World event active |
Event.Merchant.Special |
Special vendor available |
Event.Portal.Open |
Portal activated |
World Hint System¶
Why This Exists¶
The world hint system delivers passive, diegetic tutorialization: rather than a modal popup, a mechanic is taught in-place by a level-authored cue that appears when the player walks near it and disappears once they actually perform the taught action. A hint is never re-shown after it has been learned, so it stays out of the way for experienced players and returning sessions.
Authority Model — Client-Only¶
Hints are a client-only / local concern. Display, overlap detection, and completion listening all run on the owning client; nothing about the prompt itself replicates. The single piece that must reach the server is persistence — the learned state is recorded as a quest tag, and the component routes that write through a server RPC so authority owns the player's quest state.
AWorldHintActor (level-placed)
|
+-- UWorldHintComponent (all logic)
|
+-- USphereComponent (DetectionRadius overlap)
+-- UWidgetComponent (world-space prompt)
|
+-- Completion source --> writes CompletionTag
|
v
UPlayerQuestComponent::ServerAddQuestTag() (authority)
Lifecycle / Data Flow¶
Player enters DetectionRadius
|
v
IsCompletedForPlayer()? --YES--> stay silent (already learned)
| NO
v
Show prompt widget (OnShowPrompt)
BindCompletionListeners(Player)
|
v
Player performs the taught action
|
v
CompleteHint(Player)
|
+-- Write CompletionTag via PlayerQuestComponent (server RPC)
+-- Hide widget (OnHidePrompt)
+-- Broadcast OnHintCompleted
+-- UnbindCompletionListeners
Player leaves radius (uncompleted) --> hide widget + unbind, no persistence
Completion Sources¶
Completion is triggered one of two ways. Both converge on CompleteHint().
| Source | How configured | Use case |
|---|---|---|
| GAS gameplay tag (default) | Set TriggerGameplayTag; the component listens for that tag on the player's ASC |
Mechanic exposed as a gameplay tag (most abilities/actions) |
| Blueprint override | Subclass actor/component, override BindCompletionListeners/UnbindCompletionListeners, call CompleteHint() |
Non-GAS triggers (e.g. ALS locomotion-action events, inventory delegates) |
CompletionTag controls persistence: if set, the learned state is recorded as a quest tag and the hint never shows
again; if empty, the hint is non-persistent and re-shows on each overlap. AEternalCharacter carries hooks supporting
hint interaction.
Local Ground Fog Volume System¶
Why This Exists¶
AGroundFogVolume is a cosmetic, non-replicated actor that wraps the engine's ULocalFogVolumeComponent with a
designer-friendly preset authoring layer. Instead of hand-tuning raw fog-component fields per placement, designers
pick a named mood preset and optionally tweak the resolved values. It is intended to live in lighting sublevels so
each dungeon's fog mood swaps alongside the rest of its lighting, coordinated by the environment subsystem.
Data Flow — Preset → Struct → Component¶
EGroundFogPreset (designer picks mood)
|
v
FGroundFogPresetData::GetDefaults(Preset) <-- hand-tuned defaults per preset
|
v
FGroundFogPresetData FogData <-- designer may override any field per-instance
|
v (ApplyToComponent / OnConstruction)
ULocalFogVolumeComponent <-- DensityScalability multiplier applied for quality scaling
Authoring Contract¶
- Preset fills the struct, not the component. Selecting a preset (or
ResetToPresetDefaults) overwritesFogDatawith that preset's defaults; the struct is then the editable source of truth andApplyToComponentpushes resolved values down to the engine component. Custompreset leaves the struct as-authored (no overwrite), for fully bespoke fog.DensityScalabilityis a per-instance multiplier on top of the resolved density, used to scale fog cheaply on Med/Low quality settings without re-authoring.- Cosmetic & non-replicated — never gameplay-affecting; place in lighting sublevels (see
UEnvironmentSubsystem).
Presets¶
| Preset | Intent |
|---|---|
ThinGroundMist |
Light low-lying mist |
HeavyPool |
Dense pooled fog |
WarmSmokey |
Warm smoky haze |
MoonlitChill |
Cool moonlit atmosphere |
EerieGreen |
Cursed/ominous green |
Custom |
No preset; struct used as-is |
(The procedural wisp fog-card material is an art detail outside this concept doc.)
Source References¶
Automap¶
UAutomapStateComponent- Source/ProjectEternal/Public/Automap/Components/AutomapStateComponent.hUAutomapMarkerComponent- Source/ProjectEternal/Public/Automap/Components/AutomapMarkerComponent.h- Replicated types (header/nodes/edges/markers) - Source/ProjectEternal/Public/Automap/AutomapTypes.h
FAutomapPlanBuilder+ coordinate derivation - Source/ProjectEternal/Public/UI/ViewModels/AutomapPlan.hUAutomapController/UAutomapViewModel- Source/ProjectEternal/Public/UI/Controllers|ViewModels/- Canvas / minimap / overlay widgets - Source/ProjectEternal/Public/UI/Widgets/Automap/
- Style keys - Source/ProjectEternal/Public/Automap/DataAssets/AutomapStyleDataAsset.h (asset:
DA_AutomapStyle) - Specs - Source/ProjectEternal/Private/Tests/Automap/
- Design contract - ImplementationDocs/AutomapRedesign.plan.md
Floor Generation (moved to Dungeon System)¶
UEternalDungeonGenerator- Source/ProjectEternal/Public/Dungeon/EternalDungeonGenerator.hUEternalRoomData- Source/ProjectEternal/Public/Dungeon/DataAssets/EternalRoomData.hUDomainDungeonConfig- Source/ProjectEternal/Public/Dungeon/DataAssets/DomainDungeonConfig.h- See Dungeon System for the full source reference list.
Global Events¶
UQuestManagerComponentclass - Source/ProjectEternal/Public/Quest/QuestManagerComponent.h:28- Event operations - Source/ProjectEternal/Private/Quest/QuestManagerComponent.cpp:56
World Hints¶
AWorldHintActor-Source/ProjectEternal/Public/WorldHint/WorldHintActor.hUWorldHintComponent-Source/ProjectEternal/Public/WorldHint/WorldHintComponent.h- Quest-tag persistence -
Source/ProjectEternal/Public/Quest/PlayerQuestComponent.h->ServerAddQuestTag() - Character hooks -
Source/ProjectEternal/Public/Character/EternalCharacter.h
Ground Fog¶
AGroundFogVolume-Source/ProjectEternal/Public/Environment/Fog/GroundFogVolume.hEGroundFogPreset/FGroundFogPresetData-Source/ProjectEternal/Public/Environment/Fog/GroundFogTypes.h- Preset defaults -
Source/ProjectEternal/Private/Environment/Fog/GroundFogTypes.cpp->GetDefaults()
Game State¶
AEternalGameState- Source/ProjectEternal/Public/GameMode/EternalGameState.h:18UEternalGameInstance- Source/ProjectEternal/Public/GameMode/EternalGameInstance.h:12
Related Systems¶
- Replication Overview - FastArray patterns
- Enemy AI - AI patrol routes use world systems
- Spawning System - Item spawning in world
- Quest System - Quest integration
Recent Changes¶
| Date | Change | Impact |
|---|---|---|
| - | Initial documentation | Document world systems architecture |
| 2026-03-16 | Added World Hint system (AWorldHintActor/UWorldHintComponent) |
Client-only passive tutorial prompts; overlap-triggered, auto-complete via GAS tag or BP hook, persist via quest tag (server RPC) |
| 2026-05-05 | Added Local Ground Fog Volume system (AGroundFogVolume) |
Cosmetic, non-replicated preset authoring layer over ULocalFogVolumeComponent for per-dungeon fog mood in lighting sublevels |
| 2026-08-02 | Automap v2 clean-replace (UAutomapStateComponent + Slate parchment renderer) |
Old capture/fog/POI machinery deleted; generation-native replicated projection, containment-scan reveal, dual-path marker registry, N-key overlay |
Future Considerations¶
| Enhancement | Benefit | Complexity |
|---|---|---|
| Level Streaming | Dynamic sublevel loading | High |
| Automap colorblind palette + settings toggle | Accessibility (plumbing exists in UAutomapStyleDataAsset) |
Low |
Automap silhouette plates (repurpose UEternalRoomData::PreviewImage) |
Per-room inked footprints | Medium |
| Automap two-layer canvas + dead-band anchoring | Cut minimap repaint cost | Medium |