V5 is one open-world narrative game that has to play like seven. A Hong Kong
triad brawler diving into sticky cover, a 1947 detective thumbing a period
sidearm, a 1899 outlaw fanning a revolver, a Witcher coating a silver sword in
specter oil, a sci-fi marine snapping to a zero-G firing stance, a steampunk
gadgeteer, and a cross-cell Mind Palace deducer all run on the same shared
gameplay foundation — the same attribute machinery, the same animation
catalog, the same input-context model — and differ only in which cell they
are mounted as. That partitioning is the job of the four modules this page
covers: V5Gameplay (the Gameplay Ability System layout), V5Combat (the
third-person gunplay verbs that ride the GAS attributes), V5Animation (the
motion-matching and AnimBP pipeline), and V5Input (the Enhanced-Input context
model). The join key that stitches them together is one enum, EV5Cell
(V5Core/Public/V5Types.h:7):
Urban, Period, Frontier, Hunter, SciFi, Steampunk, MindPalace — seven values,
not five, because the DLC Steampunk cell and the Mind Palace meta-layer carry
tags and data even though only five cells ship combat at launch.
Unlike V5's per-cell ruleset plugins, this layer is cross-cell shared by
construction: the modules forbid per-cell dependencies and key everything on
EV5Cell so a new cell is a new enum arm plus data, not a fork. This page is
part of the Shared Gameplay Foundations group; the section hub is
../V5_ARCHITECTURE.md.
What ships, honestly#
These four modules are real, building Unreal C++ with domain-specific math and genuine automation tests — not skeletons. But they sit at different distances from the engine's runtime, and the honest distance matters. Five qualifications, so the rest of the page reads at face value:
-
GAS is the most engine-integrated of the four.
V5Gameplaylinks the realGameplayAbilitiesandGameplayTagsmodules (V5Gameplay.Build.cs), ships four concreteUAttributeSetsubclasses and twenty concreteUGameplayEffectsubclasses with modifiers configured in C++, and theV5.Gameplay.GE.*specs spawn the engine's realAAbilitySystemTestPawninto aUWorld, apply effects, and assert computed attribute values (V5GameplayEffectTests.cpp:27-97). These would fail against placeholders. -
There is no central GAS pawn or ASC subclass in this layer. V4 had an
AV4Pawn_Operatorthat owned aUV4AbilitySystemComponent, mounted one attribute set per cell, and switched cells by overwriting that pointer. V5 ships no equivalent —V5Gameplayprovides the attribute sets, the gated ability base, and the effects as reusable building blocks, and the only place an ASC is wired to a set is the test (engineAAbilitySystemTestPawn+AddSpawnedAttribute,V5GameplayEffectTests.cpp:29-32). The pawn that mounts a set, grants abilities, and flips cells at runtime is an integration site this module does not contain. -
V5Gameplayships zero concrete abilities. The architecture lists ability families (V5_Ability_Sign_*,V5_Ability_DeadEye,V5_Ability_Biotic_*). In the shared module the only ability class is the abstractUV5_GameplayAbility_Base(V5GameplayAbilityBase.h:9); the cell verbs live in per-cell modules, not here. Treat the families as the contract the base enforces, not as classes in this module. -
V5AnimationandV5Inputlink no engine animation/input plugins. BothV5Animation.Build.csandV5Input.Build.csdepend on onlyCore/CoreUObject/Engine/Json/JsonUtilities/V5Core. They do not linkPoseSearch,MotionWarping,Chooser, orEnhancedInput. So their IK solvers, motion-match scoring, chooser selection, and IMC-stack swaps are honest standalone C++ computations and content-catalog validators — not calls into UE's Motion Matching / IKRig / Enhanced Input runtimes. TheSimulatedLatencyMsfield on the IMC swap result (V5InputTypes.h:138) is the honest tell: it is an arithmetic estimate, not a measured frame cost. -
There are zero binary
.uassetfiles inV5/ue/Content. What ships instead are large, real JSON catalogs the loaders parse and validate — the animation catalog alone isContent/V5Animation/Data/animation_catalog.jsonat ~390k lines (30,000+ clips). No compiled AnimBlueprint, pose-search database, IKRig, montage, or GameplayEffect.uassetexists for the soft names to resolve to.
Everything below is real where it cites a function and a line; where it names an authored asset or an engine plugin runtime, that is the specified-but-pending part.
GAS layout — attribute sets, a gated base, real effects#
Four attribute sets, 44 replicated fields#
Every stat the game tracks lives in one of four UAttributeSet subclasses, each
declared with the V5_ATTRIBUTE_ACCESSORS macro (V5AttributeSets.h:8) that
synthesizes the GAS getter/setter/initter quartet. Unlike V4, these derive
directly from UAttributeSet and each carries its own clamp logic — there
is no shared abstract base:
| Set | Header | Fields | Notable defaults |
|---|---|---|---|
UV5_AttributeSet_Combat |
:15 |
14 | Health/Max 100, MovementSpeed 600, CritChance 0.05, CritMultiplier 1.5, Mana 100 (.cpp:39) |
UV5_AttributeSet_Honor |
:112 |
9 | WitcherHumanity 50, all reps 0 (.cpp:121) |
UV5_AttributeSet_Ship |
:179 |
11 | Hull 1000, Torpedoes 12, Railgun 40, PDC 12000 (.cpp:189) |
UV5_AttributeSet_Vehicle |
:258 |
10 | Fuel 100, four tire conditions + engine at 100 (.cpp:252) |
That is 44 replicated FGameplayAttributeData fields, each using
ReplicatedUsing = OnRep_* with REPNOTIFY_Always (V5_REPLICATE_ATTRIBUTE,
V5AttributeSets.cpp:12). The clamps are domain-specific, not boilerplate.
Combat PreAttributeChange (V5AttributeSets.cpp:76) floors
Health/Stamina/Toxicity/Mana at zero, holds CritChance to [0,1], and keeps
CritMultiplier/AttackSpeed at a 0.1 minimum so a debuff can never zero out a
multiplier; PostGameplayEffectExecute (:94) re-clamps current-vs-max pairs
after an instant effect. Honor uses three different clamp bands
(V5AttributeSets.cpp:148): Bounty ≥ 0, the Paragon/Renegade/WitcherHumanity
meters to [0,100], and the cop/triad/family reputations to a signed
[-100,100]. Ship deliberately exempts ThrustForward/ThrustLateral from the
positive clamp (:223) because thrust is a signed vector component. The
V5.Gameplay.GE.ReplicationMetadata spec reflection-counts these and pins
14 / 9 / 11 / 10 (V5GameplayEffectTests.cpp:128-131) — a test that fails the
moment an attribute loses its RepNotify.
The cell-gated ability base#
UV5_GameplayAbility_Base (V5GameplayAbilityBase.h:9) is
Abstract, Blueprintable and adds exactly what a multi-cell roster needs. Its
constructor sets InstancedPerActor instancing and LocalPredicted net
execution (V5GameplayAbilityBase.cpp:43-45), so every cell inherits
client-predicted, replicated activation. It carries a CellContext (:17) and
a resolved CellTag; SetCellContext (.cpp:48) maps the enum to a native tag
— EV5Cell::Urban → V5.Cell.Urban, MindPalace → V5.Layer.MindPalace
(ResolveV5CellTag, .cpp:7) — and adds it to AbilityTags. The gate lives in
CanActivateAbility (.cpp:58): after the Super check it returns true only
when the cell tag is invalid, the source tags are empty, or
SourceTags->HasTag(CellTag) (.cpp:70). This is a source-tag gate, subtly
different from V4's ASC-active-cell check: a frontier verb activates only when
the activation context is tagged frontier, which keeps a stray tag from firing a
foreign cell's verb.
Twenty effects with real GAS configuration#
This is where V5 departs most from V4. Where V4 used one enum, a resolve table,
and SetByCaller scaling, V5 ships twenty concrete UGameplayEffect
subclasses (V5GameplayEffects.h:7-165) whose constructors configure real GAS
modifiers via three helpers (V5GameplayEffects.cpp:7-40):
- Instant damage.
Damage_Physicalis anInstantadditive-20to Health (.cpp:43);Damage_Energyis-15(.cpp:49). - Periodic bleed.
Damage_Bleed(.cpp:55) is aHasDuration6 s effect withPeriod.Value = 1.0,bExecutePeriodicEffectOnApplication = true, and a-3health tick — a genuine periodic GAS effect, not a flat hit. - Stacking buffs.
ConfigureStackingBuff(.cpp:29) builds a 30 s effect withAggregateByTarget,StackLimitCount = 3,RefreshOnSuccessfulApplication,ClearEntireStackexpiry, andbDenyOverflowApplication = true(.cpp:21-25). Stamina regen adds+10per stack (.cpp:65); twelve buffs share this shape. - Oil coatings.
ConfigureOilEffect(.cpp:35) is a 300 s, single-stack effect adding a crit-chance and an attack-speed modifier; the specter oil grant is(+0.12 crit, +0.10 attack)(.cpp:124), tuned per monster class. - Persistent honor.
Persistent_HonorMod(.cpp:144) is anInfiniteeffect adding+5HonorScore and+2ParagonScore at once.
The V5.Gameplay.GE.Stacking spec applies the stamina buff four times and
asserts the active count caps at 3 and stamina lands at 80 — 50 + 3×10,
the overflow denied (V5GameplayEffectTests.cpp:91-97). The
V5.Gameplay.GE.Cancellation spec applies once (stamina 50 → 60), removes the
handle, and asserts the restore to 50 (:110-117). These pin specific
computed values that a hardcoded return could not satisfy.
A companion piece, UV5_StateMachine (V5StateMachine.h:32), is the narrative
flag store, not a combat machine: SetFlag/GetFlag/ClearFlag over
cell-namespaced FV5NarrativeFlagState rows carrying a revision and a Unix
timestamp (:8), plus ExportSnapshot/ImportSnapshot for save round-trips,
verified cross-cell at V5GameplayEffectTests.cpp:143-154.
The tag side backs all of this. V5/ue/Config/DefaultGameplayTags.ini declares
22 native tags under FastReplication=True — seven cell roots, the
honor/bounty/paragon/renegade axis roots, and input/save roots. The
UV5_GameplayTagRegistry game-instance subsystem (V5GameplayTagRegistry.h:10)
requests 17 required tags on Initialize, and fails loud: missing tags land
in MissingRequiredTags, bReady goes false, and each absence is logged as an
error (V5GameplayTagRegistry.cpp:99-106) rather than silently tolerated.
The combat verb layer (V5Combat)#
V5Combat is the third-person gunplay that rides those GAS attributes, and it
is real pure-function C++, mostly UBlueprintFunctionLibrary resolvers over
the data types in V5CombatTypes.h (14 weapon classes, 7 cover states, 12
hit-reaction classes, 5 friendly-fire policies). The load-bearing math:
- Bullet penetration.
ResolvePenetration(V5CombatRules.cpp:3) computesEffectiveResistance = max(Resistance·(Thickness/10) − ArmorPen, 0), blocks hard cover above0.85, scales damage byclamp(1 − resistance), and counts a penetration only when the through-damage clears a 12% floor. The mechanics spec drives 40 cm concrete (bIsHardCover) and asserts the shot is stopped (V5CombatTests.cpp:191). - Hit reactions.
ResolveHitReaction(.cpp:17) is a tiered selector: headshot ≥ 75 →KO; armor-break →WallSlam; Hunter cell ≥ 55 →KOBleed(a cell-specific arm); ≥ 90 →SpinFall; ≥ 65 →Knockdown; ≥ 35 →HeavyStagger. The spec pins the90 + headshot → KOoutcome (V5CombatTests.cpp:193). - Friendly fire.
GetPolicyForCell(.cpp:46) gives each cell its own doctrine — PeriodBlocked, HunterAllowed, Sci-FiSquadOnly, Urban/FrontierHonorPenalty— andGetReputationPenaltycharges 25 for a civilian, 10 for an ally, 35 for a squadmate (.cpp:79).
The feel systems are equally concrete. AdvanceADS
(V5CombatAimDownSight.cpp:27) blends FOV toward the weapon's zoom with
alpha = clamp(dt·12), decays recoil by 1 − dt·7, and recomputes an accuracy
scalar from movement and residual recoil. TryAttachToCover
(V5CombatCover.cpp:8) only sticks when the approach faces the cover —
dot(approach, −normal) ≥ 0.2 and height ≥ 60 cm — and flags vaultability below
145 cm. UV5_Combat_StanceSubsystem is a real UWorldSubsystem
(V5CombatStanceSubsystem.h:10) that gates ZeroG to the Sci-Fi cell and
Horseback to Frontier (.cpp:5-18) and returns per-stance accuracy/recoil/
profile/movement scalars (prone drops profile to 0.32, .cpp:48).
FireAtRange (V5CombatWeaponBase.cpp:11) ties it together: damage-at-range ×2
for headshots, through the penetration resolver, into the hit-reaction selector,
with a recoil key pulled from the pattern catalog. That catalog is a real
197-weapon roster expanded from weapon_roster.json and validated per cell —
Urban 65, Period 40, Frontier 22, Hunter 32, Sci-Fi 38 — with a tuning assertion
that the 1899 revolver kicks more than twice as hard as the sci-fi rifle
(V5CombatTests.cpp:79-97). The seam this layer does not cross is applying
its computed damage back through a GAS Damage effect onto
UV5_AttributeSet_Combat::Health; the resolvers return values, and the
integration pawn (qualification 2) would land them.
Animation pipeline (V5Animation)#
V5Animation is a library of standalone algorithms plus a catalog validator.
The motion-matching contract from the architecture — 0.5 s past trajectory, 1.0
s future intent — is enforced as data: ValidateAnimationCatalogJson
(V5AnimationSystems.cpp:207) parses the 390k-line catalog and requires schema
version 1, 30,000+ clips across five cells and three traversal modes, and
15 pose databases of ≥ 2000 clips each, rejecting any clip below the quality
bars (poseCoverage ≥ 0.86, qualityScore ≥ 0.82, ≥ 9 trajectory samples;
ParseClip, :157). EvaluateTraversalMatch (:334) scores a request as
alignment·0.5 + speed·0.3 + window·0.2, accepts above 0.68, and returns a
blend time lerped between 0.18 and 0.06 s; SpeedScoreForMode (:169)
tunes the ideal speed per mode (run 4.8, walk 1.5, crouch 0.9 m/s). The
pose-database quality spec averages every database and asserts mean quality ≥
0.86 and coverage ≥ 0.9 (V5AnimationTests.cpp:110-133).
The IK and retarget side is real geometry. Seven solvers
(V5AnimationSystems.cpp:373-455) each return an FV5AnimIKResult with a
validity flag, effector transform, blend alpha, and chain tag: foot-to-ground,
hand-to-ladder, hand-to-cover (target offset 0.08 m along the normal, valid
within 0.45 m), hand-weapon-grip, head-look-at (yaw/pitch clamped to limits),
and full-body mount keyed by Cockpit/Airlock/Horse. Three retargeters build
MetaHuman → quadruped (horse, scale clamped 0.5–2.0), → wildlife, and → alien
(Sci-Fi-only, :479). ResolveRootMotion (:490) accepts a delta only when
the montage is a server-authoritative cinematic finisher and flags a client
correction past 2 cm — the "root motion is server-authoritative" rule made
testable (V5AnimationTests.cpp:80-82). SelectHitReactVariant (:500) is the
Chooser stand-in: it derives a direction from the impact angle (back > 110°,
left < −35°, right > 35°, else front) and a tier from damage (finisher ≥ 90,
knockdown ≥ 60, heavy ≥ 30), composing a per-cell variant id like
hitreact.frontier.finisher.back. This is the genuine selection logic an
AnimBlueprint would call; today the call site is the test, and the engine
Motion-Matching/IKRig/Chooser nodes that would consume these results are
unlinked (qualification 4).
Input pipeline (V5Input)#
V5Input models Enhanced Input as a catalog and a planner rather than a live
subsystem binding. ValidateInputContextsJson (V5InputSystems.cpp:179) loads
input_context_catalog.json (417 lines) and requires schema version 1, 25+
mapping-context specs spanning all seven cells, all remappable; the spec
confirms the count and that, e.g., IMC_SciFi_Pilot advertises HOTAS,
IMC_Urban_Combat advertises Trackball, and IMC_Steampunk_GadgetWheel
targets the Steampunk cell (V5InputTests.cpp:35-47). The stack planner is the
core: BuildStackForCellAndMode (:315) selects the shared Menu/Remapping
contexts always, the Dialogue context when the mode is Dialogue, and the
cell-and-mode match otherwise, then sorts by priority then id (SortStack,
:155). SwapIMCStack (:336) diffs the current and target stacks into
AddedContexts/RemovedContexts and estimates
SimulatedLatencyMs = 0.25·(added+removed) + 0.08·stackSize (:363), flagging
whether it stays inside the request's budget. The latency spec swaps Urban-Walk
→ Sci-Fi-Pilot, asserts the swap stays under 8 ms, adds IMC_SciFi_Pilot, and
keeps IMC_Shared_Menu (V5InputTests.cpp:117-131). That SimulatedLatencyMs
is an estimate, not a measured cost, and no UEnhancedInputLocalPlayerSubsystem
is touched.
The rest of the module is honest device support: a dialogue-wheel cursor that
resolves a stick vector to a spoke via atan2 and floor(angle/360·spokes)
(EvaluateCursor, :368; the spec maps straight-up to spoke 2 of 8), plus
builders for adaptive-controller, HOTAS cockpit (5 axes / 4 buttons),
racing-wheel (valid only for Urban/Frontier, :421), trackball, arcade-panel
(6–12 buttons), iPad touch (Mind-Palace-primary drops the virtual stick), and
per-platform VR profiles.
How cells share and specialize#
The four modules repeat one pattern — one shared mechanism, seven narrow
specializations, all keyed on EV5Cell:
- Shared: the four attribute sets and their clamps, the cell-gated ability base and its predicted/replicated defaults, the twenty effects, the combat resolvers and stance subsystem, the motion-match scorer and IK/retarget solvers, the input catalog and stack planner, and the native tag table.
- Specialized per cell: which oil/sign/biotic effect a cell grants, which friendly-fire doctrine and stance gates apply (ZeroG ⇒ Sci-Fi, Horseback ⇒ Frontier), which motion-match database and per-cell hit-react table load, and which IMC stack a cell-and-mode switch assembles. Adding the Steampunk cell is a new enum arm plus catalog rows — no change to the shared spine.
How it connects#
These modules are the seam where everything else attaches. Where V5Gameplay,
V5Combat, V5Animation, and V5Input sit in the full module graph, and the
one-way cross-cell dependency rule that keeps them shareable, is laid out in
./topology-layout-module-split.md. The
honor and reputation attributes (UV5_AttributeSet_Honor) and the friendly-fire
penalties this layer computes feed the morality compass and NPC perception
described in
./perception-crowd-and-morality.md. The
reload montage paths, hit-react variant ids, dialogue-wheel cursor, and
cinematic-finisher root motion this layer selects are realized by the
presentation systems in
./audio-vfx-cinematics-ui.md. For these sections
in prose and the full subsystem glossary, return to the hub:
../V5_ARCHITECTURE.md.
Related#
- Topology, Layout & Module Split — where these modules live and the cross-cell dependency rule
- Perception, Crowd & Morality — the honor attributes and friendly-fire penalties this layer produces
- Audio, VFX, Cinematics & UI — the montages, hit-react variants, and finisher root motion this layer selects
- The section hub: ../V5_ARCHITECTURE.md