Open-World Narrative · Features

The Mind Palace

A focused page within the Open-World Narrative Features documentation. The full map and every sibling page live in the Features hub.

9sections13 minread1diagram

On this page

V5 is one open world worn five ways — Urban Crime, Period Drama, Frontier, Monster Hunter, Sci-Fi — and the Mind Palace is the single screen that refuses to let those five cells drift into five disconnected games. It is the detective deduction layer the design calls V5's signature: a graph of evidence orbs collected anywhere in the world, a circular "memory room" the player rotates to manipulate them, deduction threads drawn between clues that genuinely connect, and an accusation system that resolves a case into a branching outcome. Pick up a brass-key clue in a 1947 noir case, examine a faction badge in the Sci-Fi cell, and — if you have earned the Bureau XP to unlock cross-era reasoning — drag the two orbs together and watch a gold thread snap across two hundred years. That cross-cell "aha" is the whole pitch, and it is the one mechanic that makes the anthology feel like a universe instead of a menu.

This is a cross-cell system, not a sixth cell. Every cell's investigation gameplay pushes its clues into the same graph through one interface; the Mind Palace owns the reasoning and the verdict. Crucially, it allows wrong deductions and wrong accusations — there is no game-over for a bad hypothesis — and it proves the right one with a real proof-of-work gate rather than a coin flip. This page is the player-facing tour of how that works, grounded in the committed Unreal C++ behind each verb; the deep data-contract companion (and the in-editor Case Author that authors the cases the Mind Palace solves) is ../architecture/creator-suite-and-mind-palace.md. For the full mode taxonomy and the scope this slots into, start at the hub: ../V5_features.md.

What ships, honestly#

The logic is real, compiled, and tested. The Mind Palace is the V5MindPalace Unreal module — 36 source files under V5/ue/Source/V5MindPalace/ with a Public//Private/ split, a .Build.cs, and a Private/Tests/ automation spec — and the linker has been through it on this box: libUnrealEditor-V5MindPalace.so sits in V5/ue/Binaries/Linux/, built by the ueagent user. The architecture is a core state-and-algorithm object, UV5_MindPalace_Graph (Public/V5MindPalaceGraph.h), wrapped by a suite of thin UBlueprintFunctionLibrary classes that expose each verb statically to Blueprint (UV5_MindPalace_AttemptDeduction, …_BuildHypothesisChain, …_MakeAccusation, …_AddEvidenceNode, …_CrossEraGate, …_FilterRail, …_View_Volumetric, …_CrossEraCinematic, …_CloudSync, …_CompanionAppRead, …_Catalog). Four IMPLEMENT_SIMPLE_AUTOMATION_TEST suites (Private/Tests/V5MindPalaceTests.cpp) assert specific authored counts and computed verdicts — a Tier-1 ledger is blocked, a Tier-5 ledger unlocks, an incomplete chain collapses to the wrong branch — not truthiness.

Authored content is JSON, validated on load. The 110 deduction pairs and 15 accusation outcomes are committed data (V5/ue/Content/V5MindPalace/Data/deduction_pairs.json, accusation_outcomes.json), parsed and schema-checked at load behind a schemaVersion gate, not hard-coded.

Honest fail-loud seams. Mind-Palace cloud-sync builds a real FHttpModule POST, but its backend is a seam: an unhealthy service yields a 503, a missing JWT short-circuits to 401 rather than POSTing an unauthenticated diff, and the live round-trip test is gated on V5_ONLINE_LIVE=1. These refuse to fabricate a success they did not get.

Art is referenced, not committed. The memory-orb meshes, the rendered threads and cinematic, and the examinable 3-D evidence the Detective mode counts are asset paths (FSoftObjectPath) named by the logic here — the UE-art surface V5 keeps out of git. V5MindPalace computes the room (orb positions, scales, colors, thread intensities, camera keyframes); the UMG/Niagara rendering of it is the UI layer.

Evidence enters the room — one push interface, five cells#

Conceptually the Mind Palace is a side-car to all five cells: each cell's exploration mode supports "examine" on a pick-up clue, and examining pushes a notebook entry into the shared graph. That push is a single interface. IV5_Investigation_MindPalacePush declares one method, EmitEvidenceNodeEvent(FV5InvestigationEvidenceNodeEvent), and the graph implements it (V5MindPalaceGraph.cpp:21): it translates an in-cell evidence event — ClueId, SceneId, Tags, EvidenceTitle, EvidenceBody, bFalseClue — into an FV5MindPalaceEvidenceNode tagged to the MindPalace cell, then upserts it. That is how a clue examined in any cell becomes an orb in the room.

The node type is the design's V5_Evidence_Node (Public/V5MindPalaceTypes.h): a NodeId, CaseId, source id, source Cell, LocationId, a TimestampInFiction string, a Tags array, a Description, a soft Image path, the ExaminedDetails the player has uncovered, and a bFalseLead flag for diegetic red herrings. AddEvidenceNode (V5MindPalaceGraph.cpp:38) is the gate into the graph and it is not a blind append: it rejects a node with an empty NodeId, CaseId, or Description, and when a node id already exists it upserts — refreshing the metadata and merging new tags and examined details onto the existing orb rather than duplicating it. So re-examining a clue deepens it; it never clones it.

The deduction mechanic — authored-first, weak fallback, cross-era gate#

AttemptDeduction(NodeA, NodeB, BureauLedger) (V5MindPalaceGraph.cpp:78) is the "drag two orbs onto the pedestal" action, and its logic is specific rather than generic. After rejecting a same-node, none, or non-existent pair, it looks up an authored edge for the unordered pair via FindAuthoredEdge (matching from→to or to→from, :247). If an authored insight exists, that is the candidate. If not, it falls back to BuildWeakEdge (:271), which only succeeds when the two nodes share at least one tag: it computes the tag intersection, and if that intersection is empty it returns nothing and the attempt reports "No meaningful connection found." (:91). The system refuses to invent a deduction the data does not support.

When the shared tag exists, the weak edge is honestly weak: id weak.<a>.<b>, Confidence 0.35, Kind = Weak, with DeductionText reading "A weak thematic connection is visible, but it needs stronger corroboration." The cross-era nuance is encoded right here: the weak edge is marked bCrossEra when the two nodes come from different cells, and in that case it is stamped RequiredBureauTier 5 and UnlockFlag MindPalace.CrossEraPairs (:303-305). A same-cell weak link is tier 1 and always passes; a cross-cell weak link is gated exactly like an authored cross-era thread.

Every candidate — authored or weak — then passes the cross-era gate before it is committed. UV5_MindPalace_CrossEraGate::CanUnlockDeduction (V5MindPalaceCrossEraGate.cpp:3) is the spine connecting the Mind Palace to progression: a non-cross-era edge is always allowed; a cross-era edge is allowed only when BureauLedger.CurrentTier >= max(1, RequiredBureauTier) or the ledger's Unlocks already contains the edge's UnlockFlag. If the gate blocks, AttemptDeduction returns bGateBlocked = true with the candidate edge attached for the UI to tease — but it is not added to State.Edges. The graph stays unchanged until the player earns the unlock, and the cross-cell epiphanies are paid for in Bureau XP, the same ledger (FV5BureauXPLedger, V5/ue/Source/V5Core/Public/V5Types.h) whose Tier-5 milestone grants MindPalace.CrossEraPairs. Only a cleared candidate is appended to state (deduped by edge id), and the result reports bSuccess with the resolved Edge.

From a thread to an accusation — chains, the gate, and outcomes#

A single thread is rarely the case. BuildHypothesisChain(start, maxDepth) (V5MindPalaceGraph.cpp:112) is a real breadth-first traversal over the committed edges: a frontier expands outward (depth clamped to 1–12), collecting every reachable node and edge id and accumulating an AverageConfidence across the threads it walks. It is the UI helper for visualizing a line of reasoning, and the test pins that an authored chain carries real strength — AverageConfidence >= 0.80 (V5MindPalaceTests.cpp:99) — because the authored cross-era threads ship at confidence 0.86 and per-cell threads at 0.74, while weak guesses drag the average down toward 0.35.

MakeAccusation(case, accused, chain) (:165) is the proof-of-work gate that closes a case, and it defaults to the worst outcome. It seeds the result as Wrong / Unproven with the summary "filed without a complete supporting chain," then searches the authored catalog for an outcome matching the (case, accused) pair. A match only "sticks" with its authored Rating (Brilliant/Good/Doubtful/Wrong) and OutcomeBranch (TruthRevealed/PlausibleConviction/PoliticalCoverup/Unproven) if the player's EvidenceChain contains every RequiredEdgeId the outcome demands. A chain missing even one required link collapses straight back to Wrong / Unproven with "lacked the required evidence chain" (:196). The outcome is always backed by the deductions the player actually assembled — you cannot brute-force a conviction by naming the right culprit without the reasoning that indicts them. Every resolved accusation, right or wrong, is appended to State.Accusations and logged by UV5_MindPalace_OutcomesArchive::LogOutcome into the reviewable Outcomes Archive.

How a player actually solves a case#

Stitch the verbs together and the loop is exactly the Sherlock-style fantasy the design promises — examine, theorize, chain, accuse — with the cross-era gate as the long-game unlock.

flowchart TD EX["Examine a clue in ANY cell<br/>(Urban / Period / Frontier / Hunter / Sci-Fi)"] EX -->|"EmitEvidenceNodeEvent → AddEvidenceNode"| ORB["Orb appears in the memory room<br/>tagged cell · case · in-fiction date · key terms"] CAT[("deduction_pairs.json<br/>110 authored pairs")] -->|ApplyAuthoredDeductionCatalog| ROOM ORB --> ROOM{{"Volumetric room + FilterRail<br/>rotate · filter by cell / case / tag / date"}} ROOM -->|"drag two orbs → AttemptDeduction"| Q{authored edge<br/>for the pair?} Q -- yes --> GATE{cross-era gate<br/>CanUnlockDeduction} Q -- "no, shared tag" --> WEAK["weak thread<br/>Confidence 0.35"] Q -- "no, no shared tag" --> NONE["No meaningful<br/>connection found"] WEAK --> GATE GATE -- "Tier &lt; 5 (cross-era)" --> BLOCK["bGateBlocked — earn Bureau XP<br/>graph unchanged"] GATE -- allowed --> THREAD["thread committed to State.Edges<br/>cross-era → 4s gold-thread cinematic"] THREAD --> CHAIN["BuildHypothesisChain<br/>BFS · AverageConfidence"] CHAIN --> ACC{"MakeAccusation<br/>chain ⊇ RequiredEdgeIds?"} ACC -- yes --> WIN["authored Rating + branch<br/>Truth / Plausible / Coverup"] ACC -- no --> LOSE["Wrong / Unproven<br/>(the wrong answer still ships)"] WIN --> ARCH[("Outcomes Archive<br/>+ cloud-sync :4220")] LOSE --> ARCH

The player examines clues across cells; each becomes an orb. In the room they filter the clutter, pick two orbs, and attempt a deduction. Most early pairings resolve inside a single cell; the cross-era pairings stay gated until Bureau Tier 5, at which point dragging a 1947 orb against a 2078 orb fires a four-second "thread connects" cinematic. They chain threads into a hypothesis, read its confidence, and at case-close accuse one suspect — but the accusation only lands its authored branch if the required threads are in the chain. Get it wrong and the case still resolves, to the wrong branch, logged to the Archive for a New Game+ replay. No penalty, no soft-lock, just a worse outcome you can see you earned.

The volumetric memory room and the filter rail#

UV5_MindPalace_View_Volumetric::BuildViewState (V5MindPalaceViewVolumetric.cpp:33) renders the design's "circular memory room with evidence orbs in concentric rings by cell of origin." It first filters the node set through the rail, then places each surviving node on a ring whose radius is a function of its cell — RingForCell = 220 + cellIndex·70 cm, so Urban orbs sit on the inner 220 cm ring and Sci-Fi orbs on a 500 cm ring — at an angle evenly distributed around the circle (2π·i/N), lifted in z by index. The selected orb scales up to 1.35 and glows at full intensity while the rest sit at 0.45; each orb takes a per-cell accent color (ColorForCell — Urban cyan, Period amber, Hunter rust, Sci-Fi indigo). Deduction threads are drawn only between two visible orbs; an authored thread's intensity tracks its confidence (clamped to 0.25–1.0) while a weak thread reads flat at 0.25, and a cross-era thread is gold where an in-cell thread is blue-grey. The whole layout is deterministic geometry the UI consumes; nothing about it is random.

UV5_MindPalace_FilterRail::FilterNodes (V5MindPalaceFilterRail.cpp:19) is the left rail: filter by cell, by case, by an in-fiction timestamp range, and by required tags (a node must carry all of them). The timestamp range is a string comparison that works precisely because the in-fiction timestamps are written ISO-ordered ("1947-06-14T21:00:00" sorts before "2078-…"), so a date window is a lexical bound. The automation test drives this end-to-end: filter to a single node by cell+tag, then build a view that resolves to exactly two orbs and one thread (V5MindPalaceTests.cpp:106-110). The cross-era reveal is itself code: UV5_MindPalace_CrossEraCinematic::BuildThreadConnectCinematic (V5MindPalaceCrossEraCinematic.cpp:19) returns a four-second camera move whose ThreadAlpha eases in over the duration (FMath::InterpEaseInOut) and whose camera lerps to the midpoint between the two orbs — the test asserts the duration is 4.0 s and the alpha advances strictly between 0 and 1.

Authored content is data, validated on load#

The 110 deduction pairs and 15 outcomes are not hard-coded. UV5_MindPalace_Catalog::LoadDeductionPairs (V5MindPalaceCatalog.cpp:121) reads deduction_pairs.json and validates it hard: schemaVersion must be 1, the file is a compact generator spec of pairGroups, and the generated edges are de-duplicated by id, rejected on self-loops or non-positive confidence (AddDeductionIfValid, :103). The spec is one crossEra group of count: 30 spanning all five cells at requiredBureauTier 5, plus five perCell groups of count: 16 (Urban, Period, Frontier, Hunter, Sci-Fi) = 80, and the validator counts: it errors unless cross-era pairs total exactly 30 and per-cell pairs total exactly 80 (:269-276), matching the test's Deductions.Num() == 110, CrossEraCount == 30, PerCellCount == 80. ValidateAccusationCatalogJson (:280) requires at least three outcomes, each with a non-empty requiredEdgeIds and a unique id; accusation_outcomes.json ships 15 — five anchor cases × three authored branches, an even Truth-Revealed / Plausible-Conviction / Political-Coverup split, each branch demanding its own pair of cross-era edges. The accusation test completes each outcome's required deductions and asserts the verdict resolves the expected branch, covering three distinct branches across the arc (V5MindPalaceTests.cpp:179-202).

Cloud sync, the companion app, and the live seam#

Mind-Palace state follows the account across devices, and that path is a real request with an honest backend seam. UV5_MindPalace_CloudSync::BuildGraphDiffPayload (V5MindPalaceCloudSync.cpp:16) serializes a graph diff — nodes (id/case/cell) and edges (id/from/to/cross-era) plus counts — targeting @v5/service-mindpalace-cloud-sync at /v5/mindpalace-cloud-sync/graph-diff. FV5MindPalaceCloudSyncHttp::BuildHttpRequest (V5MindPalaceCloudSyncHttp.cpp:35) turns that into a genuine FHttpModule POST on port :4220 with a Bearer JWT and application/json, and the latent UV5_MindPalace_CloudSyncRequest::Activate (:75) fails loud — short-circuiting to 401 auth.jwt.required when no token is present rather than transmitting an unauthenticated diff, and reporting service.unreachable on a connection failure. ParseResult maps 2xx→success and 503→failure; the request-construction test pins the verb, URL, port, headers, and body, and a second test does a real round-trip behind V5_ONLINE_LIVE=1, asserting the response acknowledges the merged diff (V5MindPalaceTests.cpp:246-318).

The companion-app path (V5MindPalaceCompanionAppRead.cpp) is the Year-1 Companion-App Mind Palace Editor: BuildReadOnlySnapshot exposes the graph for an AR/touch overlay, and BuildEditableSnapshot flips on the editor with two operations — add-clue and annotate-clue. ApplyCompanionEdit (:72) validates each draft (it requires a graph, a clue id, a case id, and an annotation body), creates or annotates the targeted node — an annotation must target an existing clue — and queues an append-only mindpalace-edit upload for cloud-sync. The companion can add to your case file from your phone, offline, and it merges back through the same seam.

Edge cases & connections#

  • A pairing with no authored edge and no shared tag yields "No meaningful connection found" — never a fabricated certainty. Shared-tag pairings produce only a 0.35-confidence weak thread, and a cross-cell weak thread is gated exactly like an authored cross-era one.
  • A cross-era deduction below Bureau Tier 5 returns bGateBlocked with the candidate attached but not committed to state — the gate fails loud and leaves the graph untouched until the XP is earned.
  • An accusation with an incomplete chain can never reach an authored conviction branch; the missing-link path is the explicit Wrong/Unproven collapse, so wrong answers ship (and are archived) without penalty — there is no deduction-graph game-over.
  • Offline / unhealthy backends surface as 503 or, with no JWT, 401; the live cloud-sync round-trip is opt-in behind V5_ONLINE_LIVE, so the default test run never depends on a service being up.
  • The Detective Mind Palace mode — the ten-hour main arc, the deterministic daily-puzzle rotation, the leaderboard scoring, and the count-validated 100 examinable 3-D evidence assets — is a separate runtime module (V5DetectiveMindPalace, also compiled on this box) built on top of this graph; its dailies-and-scoring internals are documented in the architecture companion rather than re-cited here.
  • Where cases are authored and where the spine lives. The in-editor Case Author that validates a case under the runtime's own rules is the matched pair to this system — ../architecture/creator-suite-and-mind-palace.md. The Bureau-XP ledger this gates against, the online backbone the cloud-sync targets, and the roster every cell shares are inventoried in ./shared-cross-cell-systems.md. One of the five cells feeding evidence into the room is detailed in ./urban-crime-cell.md.
  • The feature hub: ../V5_features.md.