Roster lore, authored fighter versions, account/fighter mastery, match-derived progression, and codex unlocks are related but independently governed. Creator customization cannot rewrite canonical fighter or competitive data.
This is the product-side view of everything that wraps around the fight: who these 60 fighters are to each other, what a player earns by playing them, and the authoring surface that lets a player build their own. A fighting game lives or dies on its moment-to-moment combat — but it retains on the connective tissue: the rival you keep meeting on the Arcade ladder, the mastery bar that ticks toward a signature finisher, the wrestler you sculpted slider-by-slider and gave your own theme. This page inventories three systems as features — the per-fighter rivals / relationship matrix / cross-character timeline (the story layer), fighter and account progression with the codex and encyclopedia, and the Creator Suite plus the personalization layer of profile cards, championships, and match history — and points at the exact data, C++, contracts, and CI gates that back each one.
The throughline here is V2's quieter discipline: narrative, progression, and
authoring are data, validated by real gates, not prose in a design doc. The
relationships between fighters are a typed CSV the build can prove is
consistent; mastery curves are a spreadsheet a designer edits; the Creator Suite
is a family of UBlueprintFunctionLibrary validators with fail-loud rejection
tags, the same pattern the combat resolver uses. For the full mode taxonomy,
roster, and the rest of the scope this slots into, start at the feature hub:
../V2_features.md.
What ships, honestly#
Three honest layers, labeled. First, the story/relationship layer is data plus
gates. The rivalries, the relationship matrix, the cross-character timeline,
the faction map, and the convergent endings are authored as CSV and Markdown
under V2/balance/lore/, described by a JSON contract at
V2/ue/Content/V2/Lore/RelationshipTimeline_V2_Contract.json, and held
consistent by a 422-line Python CI checker
(check-v2-relationship-timeline.py) that fails the build on any inconsistency.
The narrative itself — the voice-over, cinematics, comic art — is not in the
repo; per the contract's sourceBoundary, the authoring source lives in a
sister monorepo (apps/hathor/studio-web/, via @hathor/lore-compiler) and V2
owns the launch-roster ids, the compiled artifact ids, the profile-timeline
presentation, and the V2Editor tooling.
Second, progression is a real spreadsheet-of-truth plus real save logic. The
mastery curves, account-level rewards, prestige tiers, codex entries, quest
catalog, and reward-inbox rules live under V2/balance/progression/ and
V2/balance/codex/, gated by a 412-line checker
(check-v2-player-progression-codex-quest-inbox.py); the runtime side is real
C++ — FV2ProgressionTrack::AddXP is a genuine level-up loop, and the
FV2MatchHistoryArchive actually dedups, sorts, and caps history.
Third, the Creator Suite is substantive, tested C++. CAW/Create-a-Soul, the
Move Editor, the variation system, profile cards, and save integrity are real
USTRUCT/UBlueprintFunctionLibrary code in V2Gameplay, V2Combat, and
V2Persistence, exercised by automation specs under V2/ue/Source/V2Tests
(Gameplay.Module, Persistence.Module, MorphTargetRig, MovesetDataAsset,
ProfileSave, …) and gated by a fleet of check-v2-create-*.py scripts. The
honest qualification: there are no .uasset binaries in the repo — this is
a logic-and-data skeleton, the editor authoring UI is modeled as descriptor
types rather than shipped Slate panels, and face-from-photo via MetaHuman is
license-gated (manual sculpt always works). What exists is real; what's
absent (art, audio, the interactive editor chrome) is absent honestly.
Rivals, the relationship matrix & the cross-character timeline#
V2 models its roster as a graph, not a cast list. Three datasets describe who fighters are to each other, and a fourth ties their history into a single canon.
Rivals — reciprocal primaries, 2–3 secondaries#
V2/balance/lore/rivals.csv gives all 60 launch fighters a primary_rival
and a pipe-delimited secondary_rivals list. The primary rivalry is the spine
of the Arcade mid-ladder rival fight, the Story banter, and the lore-page
cross-references; secondaries seed Side Stories, Konquest, and per-matchup
banter. The checker enforces the rules that make a rivalry mean something:
Fighter.Asha's primary is Fighter.Mara, and Mara's primary must be Asha
(reciprocity), no fighter may rival itself, every named rival must be a real
roster id, and each fighter carries exactly 2–3 secondaries that don't
duplicate self or primary. A one-directional or dangling rivalry fails CI.
The relationship matrix — typed, drive-tagged, asymmetric#
relationship-matrix.csv is the richer structure: 78 directed edges, each
typed from the canonical set the contract pins — Ally, Friend, Rival,
Enemy, Mentor, Student, Mentor-Betrayed, Family, Romantic,
Cordial, Indifferent (default Indifferent) — and each carrying a drives
list naming what it feeds (arcade-rival, story-banter, mygm-feud,
side-story, profile-timeline, …). Because the edges are directed, the
matrix supports asymmetric relationships, and the data uses them
deliberately: Fighter.Cassia → Fighter.ChunLi is a Friend, while
Fighter.ChunLi → Fighter.Cassia is a Rival — one fighter's mentor is the
other's betrayer (Fighter.Orin/Fighter.Thorne as Mentor-Betrayed/Enemy;
Fighter.Ryu/Fighter.Nova as Mentor/Student). The checker is adversarial
about this: it requires every one of the 11 types to appear, every launch
fighter to participate in at least one edge, every primary rivalry to have a
matching Rival edge, and — pointedly — that at least one asymmetric
reciprocal pair exists with genuinely different views in each direction, so
"asymmetric relationships supported" is a proven property, not a claim. The
contract names the runtime target as a DataTable, URV2_RelationshipMatrix,
indexed by fighter pair, driving per-matchup banter, Side-Story eligibility,
Konquest faction-master assignment, crew-formation suggestions, and MyGM AI feud
generation.
The master cross-character timeline & convergent endings#
timeline.csv (with a companion timeline.md) is the canon spine: 14
events spanning the contract's six eventTypes — pre-story, story-major,
side-story, arcade-ending, post-credits, dlc-future — from
TL.PreStormArchive (Ancient era, Raiden/Liu Kang) through
TL.OpeningTournament (Story Ch01) to branch-canon side stories and what-if
arcade endings like TL.BloodMoonEnding. Every event carries its participants
(roster-checked), its linked_lore_pages, side_story_refs,
audio_diary_refs, and codec_refs — and the checker requires all four
reference fields to be non-empty on every row, plus the presence of
arcade-branch and DLC hooks, so the timeline can never ship an event with no
lore behind it. This is the data the per-fighter profile timeline view
reads.
The timeline resolves into the payoff system: convergent-endings.md defines
4 convergent endings (Convergence.Oathbreaker, NeonDynasty,
AncestorRift, FutureEcho), each a post-credits cinematic that unlocks only
when a combination of inputs is satisfied on one profile — specific Arcade
endings plus Story branch states plus a Side-Story completion set.
Oathbreaker, for instance, needs Arcade.Ending.DragonCrown, the
StormCupMercy + BlackIceCompactPreserved branches, and the Asha.Rival /
SubZero.Compact side stories — narrative convergence expressed as a boolean
over the player's resolved branch history.
Factions and the Hathor boundary#
faction-relationships.csv lifts the same idea to organizations: 16 directed
edges (8 reciprocal pairs) typed from Allied / Neutral / Rival / Enemy
/ At-War (Lin Kuei At-War with Black Dragon, Soul Temple Allied with
Shirai Ryu), each scoped to the modes it drives (Universe, MyGM, Konquest,
FactionQuests). The honest seam worth naming: the contract's sourceBoundary
makes explicit that this data is a compiled artifact — the narrative is
authored upstream in Hathor's studio-web and compiled by
@hathor/lore-compiler; V2 owns the launch ids, the presentation, and the
editor tooling, modeled in the real V2Editor module (the
EV2EditorToolSurface enum carries RelationshipMatrixEditor and
TimelineVisualizer surfaces). The interactive matrix/timeline editors are
descriptor-and-validation types today, not shipped Slate UI — but the data they
describe is already proven consistent by the CI gate.
Fighter & account progression, codex, encyclopedia#
Progression is two interlocking loops — per-fighter mastery and per-account level — both authored as CSV and both backed by real save-side C++.
Per-fighter mastery and the progression track#
fighter-mastery-tracks.csv gives all 60 fighters a mastery track (max
level 30, a shared Curve.Mastery.Standard, a per-fighter seasonal boost
theme), with the unlock ladder in mastery-level-rewards.csv. Def Jam's
per-style mastery is its own dataset (style-mastery-tracks.csv: independent
30-level tracks per equipped style). The runtime is the real thing:
FV2ProgressionTrack (V2Persistence/Public/V2PersistenceTypes.h) carries a
Domain (GlobalPlayerLevel / FighterMastery / StyleMastery / ModeRank),
a Level, an int64 XP, and a Prestige, and
AddXP(DeltaXP, XPPerLevel, MaxLevel) is a genuine level-up loop — it
accumulates XP, advances the level while there's enough to spend and the cap
isn't hit, and clamps overflow at the cap (`XP = XPPerLevel
- 1
) so a maxed track can't bank phantom levels. The transparent XP economy is data too (xp-gain-rates.csv`: Win 450, Loss 150, Perfect-round 125, each with a "shown on results screen" transparency label the checker requires).
Account level, prestige, and long-form goals#
The account loop runs 1–100 with rewards every 5 levels
(account-level-rewards.csv, 20 reward rows) and a P1–P10 prestige loop
(prestige-tiers.csv, 10 tiers from Copper to a top flair, each
keeps_all_unlocks = true). The progression contract is gated hard: the checker
fails unless account level is exactly 1–100, rewards land every 5, and the
prestige loop has exactly 10 entries. Above both loops sit the marquee
long-form-goals.csv chases — Win 1000 across all rulesets, Master rank in 3
rulesets, all 60 Arcade endings, all 60 Side Stories — each granting a unique
title and animated badge.
Codex / Encyclopedia, quest log, reward inbox#
The Codex is a browsable encyclopedia spanning 8 categories
(codex-categories.csv: fighter, stage, faction, weapon, special-move, style,
ruleset, historical-event), each pointing at its content source — the
historical-event category, for example, reads timeline.csv, so the
encyclopedia and the canon share one dataset. The fighter category is fully
authored: codex-fighter-entries.csv carries 60 entries, and the checker
enforces a real content budget per entry — word_count between 200 and 500,
a present art ref, audio diary, diary entry, an unlock condition that actually
contains "Complete", and a replay_or_cinematic_link that resolves to a
Replay./Cinematic. id plus cross-links. The Unified Quest Log
(quest-log-catalog.csv) and Reward Inbox (reward-inbox-rules.csv, with
claim-all / per-item / auto-claim and high_value manual-claim flags for season
and charity rewards) round out the account surface, both type-checked against
the contract's required sets.
The Creator Suite & personalization#
V2's authoring surface is a first-class feature, and unlike the narrative layer
it is mostly runtime C++ with validation logic, gated by a dozen
check-v2-create-* scripts. The pattern mirrors combat: a
UBlueprintFunctionLibrary builds a default catalog, a HasRequired… validator
proves it's complete, and resolution functions return fail-loud reason tags.
Create-a-Fighter / Wrestler / Soul#
UV2FighterCustomizationLibrary (V2Gameplay, 742-line header / 1,345-line
impl) is the CAW/CAF/CAS core. The contract
(CreateFighterCAW_V2_Contract.json) pins the depth the catalog must hit and
the checker enforces it: ≥ 50 body-morph sliders wired to skeleton vertex
deltas, ≥ 100 FACS face-sculpt targets, ≥ 8 heritage presets, gender
expression independent of body archetype. Sliders are real bindings —
FV2MorphTargetBinding carries min/max/default and a ClampSliderValue that
clamps to range and falls back to default on non-finite input. The extended
sculpt adds an 11-region bone-scale rig (Head…Hand), 8 muscle-definition
regions, a layered tattoo painter with blend modes and symmetric mirroring, and
groom hair with per-strand color. The crucial honest seam: face-from-photo via
the MetaHuman pipeline is present but regionally license-gated
(faceFromPhotoRequiresRegionalLicenseGate), and manualSculptAlwaysAvailable
guarantees the creator never depends on a gated path — a fail-loud capability
boundary, not a fake feature.
Create-a-Soul style resolution — the DJ diminishing-returns rule#
ResolveCreateASoulStyleSelection is the showcase of real domain logic. It
takes a weapon-style catalog, a selection, and rules, and returns an
FV2CreateASoulStyleResolution — or one of a family of precise rejection tags
(SC.CreateASoul.Rejected.WeaponStyleDisciplineMix,
…CustomStyleTooManyStyles, …UnknownWeaponStyle). When a player mixes
disciplines in Custom Style, the resolver computes
ExtraDisciplinePenaltyStacks = max(0, distinctDisciplines − 1) and applies a
damage penalty scalar per extra discipline, floored at a minimum stat scalar
— the "diminishing returns past two styles" (Def Jam logic) expressed as an
actual formula, with the style-assignment config defaulting
DiminishingReturnScalar = 0.82 over a StylePointBudget = 100.
Color edit, decals, voice#
FV2CreateFighterColorEditConfig is the Tekken/SC-style palette system: 16
addressable color slots per item, an HSV picker, a saved-palette gallery,
shareable scheme codes, and per-slot material scalar channels the contract
requires by name (Metallic, Roughness, DirtLevel, WearLevel,
EmissiveIntensity). The decal editor declares a 32-layer pixel/vector surface
with Normal/Multiply/Overlay/Mask blend modes and cylindrical-wrap
projection; the logo painter exposes vector primitives plus a curve tool and a
moderated image upload. CAW also carries a voice pitch slider (±12
semitones) and per-locale announcer/commentary pack selection.
Create-a-Moveset & the Variation System#
UV2MoveEditorLibrary (V2Combat/V2MovesetData.h) is Create-a-Moveset: a
curated catalog keyed to command slots, each move carrying a CostWeight,
governed by a TotalCostBudget (default 100). CalculateMoveSelectionCost
sums the weights and IsMoveSelectionWithinBudget enforces the cap —
balance-by-budget, not by honor system. On top sits the Variation System
(MKX/MK11 Kustom Variations): FV2VariationCardDefinition cards
(Ability/Passive) with a CostWeight and a MutuallyExclusiveGroupId, a
SlotBudget of 5, and ValidateVariationCardSelection, which rejects an
over-budget pick, a duplicate, an unknown card, or two cards from the same
mutually-exclusive group — collecting all errors into an out-list rather
than bailing on the first. Tournament-Edition presets and shareable codes
complete the kit, and the config even names the frame-data export root
(V2/balance/exports/<branch>/variations/) so custom variations flow through
the same balance pipeline as the base roster (see the architecture companion,
../architecture/build-cook-assets-data-and-production.md).
Arena, Entrance, Championship, and the personalization layer#
The authoring surface extends past the fighter: real contracts and checkers
exist for Create-an-Arena (CreateArenaStageEditor_V2_Contract.json),
Create-an-Entrance (check-v2-create-entrance-theme-authoring.py), and the
WWE-2K-style Create-a-Championship custom belt
(check-v2-create-championship-custom-belt.py). On the persistence side,
V2PersistenceTypes backs the personalization layer: FV2MatchHistoryArchive
is genuine bookkeeping — RecordMatch validates and dedups by MatchId,
sorts newest-first, and GetRecentMatches caps the display list at a default of
200, with FV2MatchHistoryFilter::Matches filtering by
mode/ruleset/fighter/opponent/outcome. Save integrity is real policy too:
FV2SaveIntegrityPolicy keeps MaxAutomaticBackups = 3
(bAutoBackupLastThreeSaves), prompts on corruption detection, and carries a
non-destructive FV2SaveMigrationPlan that takes a backup before migrating —
"auto-backup last 3 saves" is a typed, tested guarantee.
Community gallery & moderation#
Every upload path above — decals, arenas, custom variations, championship logos
— is flagged moderated in its contract, and the customization-library lifetime
checker (check-v2-customization-library-lifetime.py, 340 lines) gates the
end-to-end discipline. The moderation pipeline itself (ML pre-filter,
human-review SLA, per-region content rules, performer protection) is
service-side and is covered in
./community-store-support-and-ai-services.md.
How it connects#
These three systems are the glue between the fight and the player's long-term relationship with the game. The rivalries and relationship matrix feed the Arcade ladder, Story banter, and the MyGM/Konquest feuds; the codex and timeline share their dataset with the in-game encyclopedia; mastery and account level reward the combat covered in ./combat-systems-defense-and-game-feel.md; and the Creator Suite produces the fighters, movesets, arenas, and cosmetics that the roster and presentation layer renders and the community, store & support layer moderates and sells. The data discipline underneath all of it — the spreadsheet-of-truth, the CSV↔DataTable round-trip, and the CI gates that fail a build on drift — is the engine-side subject of the architecture companion, ../architecture/build-cook-assets-data-and-production.md.
Related#
- The feature hub: ../V2_features.md
- Roster, Presentation & Stages — the fighters, intros, signature libraries, and stages this content describes
- Community, Store, Support & AI Services — gallery moderation, storefront, and the live-service surfaces
- Combat Systems, Defensive Options & Game Feel — the fight that progression rewards
- Architecture companion: ../architecture/build-cook-assets-data-and-production.md — the data pipeline, validators, and gates that hold all of this consistent