Fighting Game · Architecture

Open-World, Co-op, Mini-Games & Persistent World

A focused page within the Fighting Game Architecture documentation. The full map and every sibling page live in the Architecture hub.

9sections16 minread2diagrams

On this page

This page covers everything V2 puts around the fight: the persistent open-world economy with its NPC schedules and crime gauges, the cooperative Heist campaign, the large-format Battle Royale / Roguelike / Specialty-Combat modes, the arcade mini-game cabinets, and the community-scale surfaces — world boss raids, the per-account Vault, and estate inheritance. V2 is one game — a fighting game built on Unreal Engine 5.5, not a platform of loosely-bound modes (v2-product-promise.md) — and these surfaces are how that one game grows a living city, a co-op heist crew, a 100-player ring, and a hub full of playable cabinets without ever endangering the deterministic combat core they orbit. Every one of them is built to the same target-artifact discipline the rest of V2 uses: a named subsystem is a real C++ catalog with an IsValid… validator, a deterministic runtime resolver, an automation spec that asserts specific computed values, a cooked balance JSON under V2/balance/, and — where a backend is involved — a TypeScript bridge that composes the sister monorepos and refuses to touch the rollback loop.

The reason these systems are pulled out of combat and given their own modules is the same reason the netcode page exists: V2's competitive integrity rests on frame-deterministic rollback (combat-system-gas-frame-data-and-determinism.md, rollback-netcode-and-tag-team.md), and nothing in this page is allowed inside that envelope. The persistent economy ticks on a server, the world boss's shared HP is gameplay-inert during a match, Battle Royale is server-authoritative rather than deterministic, and every TS bridge here literally rejects calledFromLiveRollbackFrame / calledFromLiveMiniGameFrame. This is the section hub's "open world & special modes" family; the architecture catalogue is ../V2_ARCHITECTURE.md.

What ships, honestly#

Real and tested in compiled C++. The persistent world economy (§130) ships as the V2World module with deterministic price/NPC/crime resolvers and an automation spec that asserts exact credit values, gauge values, heat tiers, and cop-density counts (V2/ue/Source/V2Tests/Automation/World.PersistentEconomy.spec.cpp). The co-op Heist (§122), Battle Royale (§127), Roguelike Adventure (§126), Specialty Combat, the Arcade Mini-Game Suite (§133) and the Bonus Modes (§69) all ship as catalog structs in the V2Modes module with IsValidCatalog validators, deterministic resolvers (ResolveHeistRun, ResolveSessionResult), and dedicated specs under V2/ue/Source/V2Tests/Private/Modes/. The V2Modes registry registers 52 mode definitions at startup (Modes.Module.spec.cpp asserts Modes.Num() == 52). The world-boss, Maya-mini-game, Hestia-cooking, and Vault-estate TypeScript bridges are real compositions of @themis, @maat, @hathor, @kuanyin, @maya, @hestia, @lakshmi, and @oshun/identity packages, each with a .spec.ts sibling.

Spec-only / named target artifacts. The monolith describes per-mode gRPC service contracts at libs/proto/v2/heist/heist.proto, libs/proto/v2/world-boss/world-boss.proto, and libs/proto/v2/ai-commentary/commentary.proto. None of those files exist. The only V2 proto present in the tree is libs/proto/src/oshun/v2/persistent_economy/economy.proto — a real proto3 contract for the economy/NPC/crime services. The world-boss bridge even declares its gRPC path as a string constant (V2_WORLD_BOSS_COMMUNITY_RAID_GRPC_SERVICE = 'libs/proto/v2/world-boss/world-boss.proto') that no file backs. Likewise, the monolith's Heist "dedicated-server hosted gRPC service" with MatchmakeCrew/StartHeist RPCs is aspirational: what ships is the on-device FV2HeistModeCatalog and its deterministic ResolveHeistRun. The V2/docs/cost/*.md ceilings cited throughout (heist.md, world-boss.md, persistent-economy.md, ai-commentary.md) do not exist — there is no V2/docs/cost/ directory. Finally, V2 ships no binary maps or art for these modes: FV2ModeDefinition.PrimaryMapPath, GameFeaturePluginURL, and FV2BonusModeSpec.PrimaryWorldPath are target-artifact references, not shipped .umap/.uasset files, and there is no V2Mode_Heist GameFeaturePlugin in the tree.

Provider-gated. AI Commentary is real as a built UE plugin (V2/ue/Plugins/V2AICommentary/, with UHT-generated intermediates) plus the iris-commentary-orchestration and calliope-commentator-personas services, but its play-by-play text and TTS audio are gated on external models (Claude, ElevenLabs) per V2_DEPENDENCIES.md; it is covered on esports-companion-and-ai-services.md. The world boss's contribution classifier (@nous/safety) is likewise an external dependency.

The shape every surface shares#

Across this family there is one repeated structure, and recognizing it is the fastest way to read any single mode. The authoritative data is a C++ USTRUCT catalog (e.g. FV2PersistentWorldEconomyCatalog, FV2HeistModeCatalog, FV2BattleRoyale100PlayerCatalog) pinned to its design section by a SectionId FName. The catalog validates itself through an IsValidCatalog(FString* OutFailureReason) method that returns false with a human reason rather than silently accepting a malformed catalog. Where a mode has live behaviour, the catalog carries a deterministic resolver — a pure function with no RNG that the automation spec can assert against known values. The cooked balance JSON under V2/balance/<area>/ mirrors the catalog and names its sourceContract, so the Python docs checker can detect drift between prose, contract, and shipped data. And when a mode needs cloud behaviour, a TypeScript bridge under apps/v2/ composes the sister-monorepo packages and emits a cook/transfer manifest, always off-rollback.

Modes become playable through UV2ModeRegistrySubsystem (V2/ue/Source/V2Modes/Public/V2ModeRegistrySubsystem.h), a UGameInstanceSubsystem that RegisterDefaultModes(), exposes RegisterMode/ActivateMode/BuildModeLoadPlan, broadcasts an OnModeLifecycleChanged delegate, and holds the registered tag-team and arcade-suite catalogs as transient state. The registry is also the seam where Hathor world-state publications enter gameplay (SubscribeToHathorWorldPublishedEvents, HandleHathorWorldPublishedEvent).

Persistent world economy, NPC schedules & crime (§130)#

The open city's "living" feel is three deterministic resolvers on the V2World module (V2/ue/Source/V2World/Public/V2WorldTypes.h, V2WorldCatalog.h, V2WorldBlueprintLibrary.h; implementations in the sibling Private/ directory). The default catalog (FV2WorldCatalog::BuildDefaultPersistentWorldEconomyCatalog) is pinned to CatalogId "PersistentWorldEconomyNPCSchedulesCrime.V2", SectionId "130", and requires at least three shops, schedules, density profiles, and crime profiles covering the three canonical districts District.Downtown, District.Harbor, and District.ClubRow (HasRequiredDistricts).

Supply-and-demand pricing. FV2WorldShopEconomySpec::ResolveItemPrice is a pure function over a FV2WorldEconomyPriceRequest (BasePriceCredits, DemandPercent, StockPercent, bRareItem). Demand pricing only engages for a rare item in a shop that enables it; the demand multiplier is clamp(1 + (demand01 − 0.5) × 1.2, 0.70, 1.60) — neutral at 50 % demand, floored at 0.70× and capped at 1.60×. Scarcity applies to every rotation item: clamp(1 + (1 − stock01) × 0.5, 1.0, 1.5). Worked from the spec: a 1000-credit rare item at 100 % demand and full stock resolves to 1600 credits and migrates to a premium shop (demand ≥ 80 %); at 0 % demand it floors to 700; at 100 % demand and empty stock the two multipliers stack to 2400; a non-rare item ignores demand entirely and stays at 1000 with reason tag Economy.Price.SupplyOnly. Those four numbers are exactly the values asserted in World.PersistentEconomy.spec.cpp.

NPC daily routines. FV2NPCDailyScheduleSpec::ResolveRoutineAtMinute normalizes any minute-of-day onto a 24-hour clock and maps it across eight authored windows (Sleep → Commute → Work → Lunch → Work → Commute → Leisure → Sleep). An awake NPC that reacts to presence pauses its routine for a nearby player; vendors and mini-quests are reachable during downtime (lunch/leisure) but during work or commute only for a noticed player, a recognized vehicle, or a faction ally. The spec checks that the NPC is asleep and non-interactable at 02:00, interactable at lunch (12:30), busy and ignoring an absent player at 10:00, and pauses to engage a present player at 10:00.

Crime gauge → heat → lockdown. FV2DistrictCrimeRateSpec::AdvanceCrimeRate advances a 0–100 gauge one tick. Each takedown adds 8 gauge points, each chase 6, each property-damage event 4; the gauge passively decays 0.5 points per second. The resulting rate maps onto the §103.2 heat bands (≥ threshold → 6, ≥ 70 → 5, ≥ 50 → 4, ≥ 30 → 3, ≥ 15 → 2, else 1), scales cop spawn density linearly to a ceiling of 12 (round(rate / 100 × 12)), and triggers a district lockdown at the threshold (default 90 %). The spec pins the arithmetic: one chase from zero, with one second of decay, lands at 5.5 (heat 1, cop density 1); eight takedowns reach 64 (heat 4, cop density 8); a gauge already at 88 plus one chase crosses to 94, pins heat 6, tags Heat.6 for the lockdown cinematic, and saturates cop density to 11.

flowchart LR Events["Chase ×6 · Damage ×4 · Takedown ×8"] --> Gauge["Crime Gauge 0..100<br/>(− 0.5/s decay)"] Gauge --> Heat["Heat Tier 1..6<br/>(§103.2 bands)"] Heat --> Cops["Cop Spawn Density<br/>round(rate/100 × 12)"] Heat -->|"rate ≥ threshold (90)"| Lockdown["District Lockdown<br/>cinematic + siren"]

Around those resolvers the catalog also carries market-participation anti-bot rules — wanted-list demand correlation, ops-signed community-tier rewards, and per-account + per-IP purchase rate caps cross-referenced to the §49 anti-cheat backbone (AntiCheatCrossRef == "49") — plus per-account world-state persistence (territory, crime, NPC relationships, shop inventory, cross-platform sync) and an economy-ops dashboard surface (inflation/deflation/turnover/crime trend). The server side of this is the one shipped V2 proto: libs/proto/src/oshun/v2/persistent_economy/economy.proto defines the Economy, NPCSchedule, and CrimeRate services with GetShopInventory/GetPriceCurve/RecordTransaction/GetActiveSchedule/GetDistrictRate RPCs, daily/weekly fairness seeds, demand-priced and premium-migration flags, an ip_rate_bucket for per-IP caps, and a lockdown_active field on the crime response. The cooked mirror is V2/balance/world/persistent-world-economy.json.

Heist co-op (§122)#

The Heist is V2's narrative co-op campaign, and it is implemented entirely on-device as FV2HeistModeCatalogBuildDefaultHeistModeCatalog, validated against SectionId "122", with the deterministic resolver ResolveHeistRun(const TArray<bool>& StageOutcomes) at V2/ue/Source/V2Modes/Public/V2ModeTypes.h:10142 and the spec at V2/ue/Source/V2Tests/Private/Modes/HeistMode.spec.cpp. A heist is a 1–4 player mission that combines stealth, driving, and fighting across five authored stages, each a FV2HeistStageSpec with stage-specific required flags the validator enforces:

  • Recon — stealth observation, photograph targets, plant equipment, identify guard patterns.
  • Approach — drive to the objective and evade detection or roadblocks.
  • Infiltration — stealth-or-breach, fight through guards under a per-encounter fighting ruleset (the same GAS combat as the main game).
  • Extraction — drive away under police pursuit, with crew defending and Power-Play opportunities.
  • Resolution — a cinematic outcome based on the run's choices.

Four crew roles (FV2HeistRoleSpec: Hacker, Wheelman, Muscle, Strategist) each own distinct verbs — the Hacker's hacking puzzle (cross-ref 117.1), the Wheelman's driving challenge, the Muscle's fights, the Strategist's planning, target-tagging, and Power-Play triggers. The catalog also validates three branch decisions (rejecting any count other than three), four-player online co-op (rejecting fewer than four), the signature-event themes (Casino, Bank Vault, Yacht, Train Robbery, Stadium Concert), and a solo mode that fills empty roles with an AI crew at exactly 75 % rewards (rejecting any other percentage).

The deterministic core is ResolveHeistRun: pass a per-stage outcome array and it returns a FV2HeistRunResult. Clearing every stage succeeds with AlarmLevel == 0 and StagesCompleted == StageCount; tripping stage index 2 fails the run, stops clean-run depth at StagesCompleted == 2, raises AlarmLevel == 1, and reports FailedAtStageIndex == 2. That on-device resolver — not a dedicated-server gRPC service — is what ships; the monolith's heist.proto/MatchmakeCrew backend is a named target artifact only.

Battle Royale, Roguelike & Specialty Combat (§127 / §126 / specialty)#

These three large-format modes each ship as a self-validating catalog in V2Modes and a cooked balance JSON, and each makes a deliberate netcode choice distinct from the rollback core.

Battle Royale (§127) is FV2BattleRoyale100PlayerCatalog (V2ModeTypes.h:8997), covering three 100-player kinds — Mode.BattleRoyale.Fighter100, Vehicle100, and Hybrid100 (50 fighters + 50 vehicles). Its FV2ModeDefinition sets NetworkModel == ClientServerOpenWorld: Battle Royale sits outside the rollback envelope and is server-authoritative — clients see the same ring state because the server is the source of truth, never because they resimulate, so "deterministic" never applies and cheat detection runs server-side. The ring system validates to 4–6 contraction phases totalling 18–25 minutes, with per-phase contraction speed, ring damage, atmospheric shifts, and hazard activations; loot validates across Common/Rare/Epic/Legendary tiers with AI-mini-boss-guarded crates; squad modes cover Solo/Duo/Squad with revive, voice, and ping. The Modes.Module.spec.cpp block (lines ~1657–1720) asserts the 100-player counts, the phase/minute bounds, the tier and rank coverage, and a deterministic catalog signature (build it twice, the signature matches). Cooked data: V2/balance/battle-royale/battle-royale-100-player.json, down to per-phase contraction speeds and per-zone loot densities.

Roguelike Adventure (§126) is FV2RoguelikeAdventureModeCatalog (V2ModeTypes.h:9771): a Hades-inspired, run-based, permadeath mode of 30–60 minute runs over a directed-acyclic room graph (combat / boon-shrine / shop / boss-gate), drawing enemies from the V2 launch fighter pool with procedurally tuned AI difficulty and authored hazard modifiers. Its boon library validates 5–8 boons per run with rarities up to Legendary and named synergy bonuses (Boon.Synergy.FreezeStun.Frostbite and friends), a persistent hub with meta-currency upgrades, five cinematic bosses, a per-fighter narrative arc, local

  • online co-op, and a shared daily seed with a top-100 leaderboard. The Modes.Module.spec.cpp block (lines ~2168–2219) checks every sub-validator plus the deterministic signature; cooked data lives in V2/balance/roguelike/roguelike-adventure-mode.json with per-fighter boons.csv round-tripping to a data table.

Specialty Combat is FV2SpecialtyCombatModesCatalog (V2ModeTypes.h:10442): a pure Boxing Sim (realistic stamina, opponent pattern recognition with signature tells and counter windows, 180-second rounds, per-round scorecards, regional rulesets WBA/WBC/IBF/WBO/Olympic/bare-knuckle), a Bushido Blade-style weapon mode (no HP bar, clean-hit lethality, stance RPS, per-weapon reach/speed/lethal-zone tuning, stage physics affecting strikes), and an MK one-hit tournament. Cooked data: V2/balance/specialty-combat/specialty-combat-modes.json.

Arcade mini-games & the Maya / Hestia bridges (§133)#

The Battle Hub's playable cabinets are the §133 FV2ArcadeMiniGameSuiteCatalog (V2ModeTypes.h:4844) — eight entries (pinball, air hockey, mini-golf, darts, pool, cooking, photo tournament, Tekken Bowl) that the validator confirms via HasRequiredMiniGameKinds, HasBattleHubCabinetCoverage, HasCookingEconomyIntegration, HasPhotoTournamentRotation, and HasBowlingExtension. The deterministic ResolveSessionResult(MiniGameId, RawScore, ExistingScores) places a finished session against the cabinet's leaderboard and awards high-score/cosmetic, each play costing CoinInputCostFighterCoins == 1 (one Battle Hub Fighter Coin, with ambassador / pro-player free-play). Cooked data: V2/balance/modes/arcade-mini-game-suite.json; registration runs through UV2ModeRegistrySubsystem::RegisterArcadeMiniGameSuiteCatalog.

Two TypeScript bridges feed authored content into those cabinets at cook time, both off-rollback and both rejecting live mini-game frame RPCs:

  • @v2/maya-minigame-suite-bridge (apps/v2/maya-minigame-suite-bridge/src/maya-minigame-suite-bridge.ts) reuses the Maya monorepo's ProjectilePhysicsSystem (@maya/games), scene and physics readiness evaluators (@maya/scene, @maya/physics), and input mapping (@maya/client) for pinball/air-hockey/mini-golf/darts/pool, while V2 keeps per-game scoring, ruleset tuning, cabinet art, quest hooks, and leaderboard schema. It explicitly excludes Maya combat/abilities/items/ inventory, runs a real projectile probe per game, and emits a cook manifest for the V2Mode_* targets with a ready/needs-attention/blocked status driven by readiness and input-binding completeness.
  • @v2/hestia-cooking-minigame-bridge (apps/v2/hestia-cooking-minigame-bridge/src/hestia-cooking-minigame-bridge.ts) composes @hestia/ai-ml, @hestia/cooking, @hestia/education, @hestia/ingredients, @hestia/pantry, and @hestia/core to drive V2Mode_Cooking, the Battle Hub cabinet, and the quest log — the data side of the arcade catalog's HasCookingEconomyIntegration, which wires cooking ingredients back into the §130 world economy.

Community-scale: world boss, Vault & estate#

World boss community raid. @v2/world-boss-community-raid-bridge (apps/v2/world-boss-community-raid-bridge/src/world-boss-community-raid-bridge.ts) is the off-rollback backend that lets a whole community chip a shared HP pool to zero without ever touching a live match. Its rollback policy is literally server-authoritative-post-match-only-no-live-rollback-frame-rpc, the shared HP is hud-display-only-gameplay-inert, and the bridge throws if invoked from a live rollback frame. It composes four sister monorepos: @themis/community's SportsClubGovernanceManager runs a real rule-proposal vote that gates publication; @maat/intelligence's AnomalyDetectionEngine and TrendDetector build a per-contribution aggregate dashboard (region aggregates, a contributor leaderboard, anomaly run); @hathor/simulation's PoliticsManager spawns boss and defender factions and runs a simulation tick to pick the spawn region; and @kuanyin/community-harmony scores grief-raid signals (traffic spikes, new-account floods, coordinated messaging) and activates a graduated defense mode. Each contribution is validated server-side — known member account, region match, positive damage under a per-match cap, a sha256:-prefixed golden-replay hash, anti-cheat acceptance, and submission inside the event window — exactly the "damage computed client-side, submitted after the match, validated server-side" boundary the architecture demands.

flowchart TB Match["Local match (frame-deterministic combat)"] -->|"per-contribution damage<br/>computed client-side"| Submit["Post-match server submission"] Submit --> Validate["Server validation<br/>(member · region · cap · sha256 hash · anti-cheat · window)"] Validate --> Maat["@maat aggregate dashboard<br/>(leaderboard · anomalies)"] Validate --> SharedHP["Community shared HP"] SharedHP -.->|"HUD display only — gameplay-inert,<br/>never read into rollback"| Match

The honest caveat: the bridge names its gRPC service as the string constant 'libs/proto/v2/world-boss/world-boss.proto', but that proto file is not in the tree — the TypeScript composition is real, the wire contract is a named target artifact. The same is true of commentary.proto for AI Commentary.

Per-account Vault & estate. The Vault (§137, V2/balance/vault/per-account-vault-archive.json) is the cloud-side, unlimited-cosmetic store plus lifetime replay/ghost/stats archives, daily immutable cold-storage snapshots, cosmetic-only friend visiting, and cross-platform merge with dedup. Its most unusual feature — estate-planning inheritance — is backed by @v2/per-account-vault-estate-bridge (apps/v2/per-account-vault-estate-bridge/src/per-account-vault-estate-bridge.ts), which composes @lakshmi/estate (beneficiary tracking, executor toolkit, emergency access), @oshun/identity (verified-account and delegation gates), and @themis/transparency's ImmutableGovernanceAuditTrail for tamper-evident transfer records. Like every bridge here it is off-rollback and rejects live gameplay frame RPCs; it ties account inheritance to a documented-passing review queue and a §77.5 likeness opt-out. This is the clearest example of V2 reusing the sister monorepos rather than rebuilding governance, identity, and audit from scratch — see security-compliance-and-sister-monorepo-integration.md.

How it connects#

The Convoy & Free-Roam social lobby the monolith files under this section is actually racing-domain data — V2/balance/racing/convoy-free-roam-lobby.json backed by the V2RaceModes module (an 8–32 player social lobby with 2–8 player convoy formation, hot-spots, and cruise photo, no race and no rank) — and is covered on racing-and-vehicle-architecture.md. The combat that the Heist's Infiltration stage and the Specialty modes run on is the GAS frame-data core in combat-system-gas-frame-data-and-determinism.md; the rollback envelope every surface here stays outside of is described in rollback-netcode-and-tag-team.md and online-backbone-and-competitive-integrity.md; the registry, training/replay, and bonus-mode neighbours live in game-modes-training-and-replay.md; the HUD-injection seam the registry exposes is in ui-hud-vr-ar-and-accessibility.md; and AI commentary, the Vault store economy, and the live-service calendar are on esports-companion-and-ai-services.md and live-ops-store-progression-and-community.md.