# The Tactical FPS Cell

This is V4's gunfight. The Tactical FPS cell is the Rainbow-Six / Call-of-Duty
surface where the moment-to-moment loop is **aim, fire, take cover, lean,
breach, revive** — the genre that lives or dies on sub-frame numbers: how long
the gun takes to come up to the sight, whether a recoil pattern is memorizable
or a slot-machine spray, whether a peek clears the corner or exposes your head.
It is the fattest of V4's six genre modules and the one carrying the
server-authoritative competitive backbone, because everything from a 5v5 ranked
breach to a 100-player drop runs on the same aim, recoil, stance, and cover
math.

V4 markets this cell across five sub-rulesets — Raven-Shield planning, R6
Siege-era attack/defense, a CoD cinematic campaign, a 6v6 multiplayer suite, and
a 100-player battle royale — each shipped as its own hot-swappable
`GameFeatures` plugin on top of one shared C++ core. The promise is
**per-ruleset feel from one gunplay engine**: a deliberate Raven-Shield
room-clear and a frantic Warzone third-circle firefight read completely
differently to the player, yet both consume the same `UV4AimComponent`
interpolation and the same sampled recoil curve. The aim of this page is to
inventory what that core actually does, on the player's side of the screen, and
to point at the exact Unreal C++ that backs each feature. For the full mode
taxonomy, roster, and the scope this slots into, start at the hub:
[../V4_features.md](../V4_features.md).

## What ships, honestly

**The gunplay core is real, compiled, and tested.** The cell module
`V4/ue/Source/V4Tactical` carries 12 `.cpp` / 12 `.h` of domain logic — an
aim-state machine, a curve-sampling recoil asset, cover/lean/stance components,
weapon and attachment data layers, a ballistic grenade preview, and a
downed/revive state machine — wired into GAS through eight cell-gated abilities
in `V4TacticalAbilities`. The signature systems are sub-frame and
designer-editable, not stat-sheet placeholders: ADS timings of
`HipToADSSeconds = 0.18` / `ADSToHipSeconds = 0.12`, a `1×–8×` zoom band, a
recoil curve scaled by per-stance and movement multipliers. Most are pinned by
dedicated automation specs under `V4/ue/Source/V4Tests` — `RecoilSpec`,
`CoverSpec`, `WeaponLibrarySpec`, plus the per-plugin `RavenShieldSpec`,
`R6ModernSpec`, `CoDCampaignSpec`, `CoDMultiplayerSpec`, and `CoDWarzoneSpec`.

Three honest qualifications, in the spirit of the architecture companion.
**First**, "cell" here means the _mechanic module_ `V4Tactical`. The features
catalogue also files three stealth-leaning FPS sub-rulesets — Splinter Cell,
Spies-vs-Mercs, and Hitman — under the Tactical-FPS banner because they share
the first-person camera, but on disk those run on `V4Stealth`, not `V4Tactical`
(their plugins are even namespaced `V4Mode_Stealth_*`). They are covered on the
sibling page,
[./cell-stealth-and-real-time-tactics.md](./cell-stealth-and-real-time-tactics.md).
This page is the _loud_ tactical surface: gunfights, breaches, and destruction.
**Second**, the _logic_ is in-tree C++ but the _content_ is not — V4 ships JSON
sidecars, not cooked `.uasset` binaries, so a recoil-curve **sampler** is real
code while the specific recoil-curve **assets**, weapon meshes, and MetaHuman
principals are described, not baked. Weapon balance numbers live in soft-pathed
data, surfaced through `UV4WeaponLibraryCatalog`. **Third**, network authority
and lag compensation are not in this module: `V4Tactical.Build.cs` deliberately
does not depend on `V4Netcode`; hit-registration authority sits one layer down,
covered in [./shared-cross-cell-engine.md](./shared-cross-cell-engine.md). What
follows is the gunplay engine on its own terms.

## The tactical FPS experience — what a fight feels like

Strip the five sub-rulesets away and the shared verb set is small and tactile:
bring the gun up, control the climb, slice a corner from cover, manage a
30-round magazine, cook a grenade onto an arc, and drag a downed teammate back
into the fight. Each verb is a component on the operator pawn, and each carries
the specific tuning a tactical shooter argues about.

### Aim and the time-to-kill economy

`UV4AimComponent` (`V4/ue/Source/V4Tactical/Public/V4AimComponent.h`) is a
four-state machine — `Hipfire → EnteringADS → ADS → ExitingADS` — that drives an
`ADSAlpha` interpolation every tick. The two transition times are deliberately
**asymmetric**: raising the sight takes `HipToADSSeconds = 0.18` while dropping
back to the hip takes only `ADSToHipSeconds = 0.12`, so committing to a scope is
slower than bailing out of one — exactly the risk/reward a competitive shooter
tunes. `TickComponent` advances `ADSAlpha` by `DeltaTime / HipToADSSeconds` on
the way up and clamps at `1.0` before flipping to the steady `ADS` state, with a
zero-time fast path that snaps instantly. `BeginAimDownSights` clamps the
requested zoom into the `MinZoomLevel = 1.0` … `MaxZoomLevel = 8.0` band, so the
same component serves a red-dot and an 8× marksman optic. Per-weapon
`ADSSeconds` and per-attachment `ADSTimeModifierSeconds` (a heavy barrel slows
the raise; a lightweight stock speeds it) feed the same number, which is how an
attachment loadout changes the gun's _handling_ and not just its damage.

### Recoil you can learn

The property that separates a tactical shooter from a spray simulator is a
recoil pattern you can _memorize and counter_, and V4 implements it as sampled
interpolation rather than a random kick. `UV4WeaponRecoilCurveAsset`
(`V4/ue/Source/V4Tactical/Private/V4WeaponRecoilCurveAsset.cpp`) holds a
`TArray<FV4WeaponRecoilSample>` of `{TimeSeconds, Offset}` keys;
`SampleRecoil()` finds the bracketing pair for the current shot time and
`FMath::Lerp`s between them, clamping to the first sample before the curve
starts and the last sample after it ends. The interpolated offset is then scaled
by a **stance multiplier** (`Standing 1.0`, `Crouch 0.8`, `Prone 0.6`) and a
`MovingMultiplier = 1.3`, so going prone tightens your pattern by 40% while
moving blooms it by 30%. `RecoilSpec` pins the exact arithmetic: a mid-curve
standing shot interpolates to `(1.0, 2.0)`, a crouched-and-moving shot at the
second key resolves to a pitch of `4.16` (`4.0 × 0.8 × 1.3`), and a late prone
shot clamps to `4.8` (`8.0 × 0.6`) — a test that would fail instantly against a
`Math.random()` kick. Because the weapon library also carries a
`DeterministicRecoilSeed`, the pattern is reproducible shot-for-shot, which is
what makes recoil both learnable for players and auditable for anti-cheat.

```mermaid
flowchart LR
    Input["V4Input · Enhanced Input"] --> Abilities
    subgraph Abilities["V4TacticalAbilities · GAS (cell-gated)"]
        Fire[UGA_Fire]
        ADS[UGA_ADS]
        Lean[UGA_Lean]
        Nade[UGA_ThrowGrenade]
        Revive[UGA_Revive]
    end
    subgraph Tac["V4Tactical — gunplay components"]
        Aim["UV4AimComponent<br/><sub>ADS 0.18s / hip 0.12s · 1–8×</sub>"]
        Recoil["UV4WeaponRecoilCurveAsset<br/><sub>sampled curve × stance × move</sub>"]
        Cover["UV4CoverComponent<br/><sub>snap · lean · project</sub>"]
        Stance["UV4StanceComponent<br/><sub>speed × sound footprint</sub>"]
        Wpn["UV4WeaponBase / Library / Attachments"]
        Gren[UV4GrenadeComponent]
    end
    ADS --> Aim
    Fire --> Wpn
    Lean --> Cover
    Nade --> Gren
    Wpn --> Recoil
    Wpn --> Attr["UV4Attr_Tactical<br/><sub>Health · Armor · Ammo · Suppression</sub>"]
    Revive --> Attr
    Tac --> Net["V4Netcode<br/><sub>server-authoritative hit-reg (one layer down)</sub>"]
```

### Stance, cover, and lean — the defensive vocabulary

`UV4StanceComponent` ties posture to two outputs at once: a movement-speed
multiplier (`Crouch 0.7`, `Prone 0.45`) and a **sound footprint** radius scaled
off a `BaseFootstepRadius = 600` by stance (`Crouch 0.65`, `Prone 0.35`) and a
per-surface `TMap` multiplier, broadcasting an `OnSoundFootprintEmitted` event
that the shared perception model can hear. Crouching is therefore both slower
and quieter — the genre's core stealth/speed trade made into one number.

Cover is geometric, not a binary flag. `UV4CoverComponent`
(`V4/ue/Source/V4Tactical/Private/V4CoverComponent.cpp`) stores a list of
`FV4CoverAnchor`s (location, surface normal, width, and per-side peek
permissions); `FindBestCoverAnchor` does a squared-distance search inside
`SnapRadius`, `SetLeanSide` refuses a peek the anchor doesn't support,
`CalculatePeekLocation` offsets along the cover tangent with a 15-unit forward
clearance, and `ProjectMovementAlongCover` clamps lateral movement to `±Width/2`
so you slide along a wall instead of through it. `CoverSpec` verifies the
geometry to the millimetre: snapping near a 240-wide anchor and leaning right
yields a peek at `(115, 50, 0)`, and a 500-unit lateral push clamps to
`(0, 120, 0)`. That is real corner-slicing math, the spatial grammar a breach is
built on.

### Weapons, ammo, and the attachment economy

The weapon data layer is split cleanly. `FV4WeaponBase`
(`V4/ue/Source/V4Tactical/Private/V4WeaponBase.cpp`) is the runtime instance —
`MagazineSize = 30`, `MaxReserveAmmo = 90`, `Damage = 35`,
`FireRateRoundsPerMinute = 650` — with real ammo bookkeeping against the
operator attribute set: `ConsumeRound` decrements `AmmoCurrent` only when
`CanFire` passes, `ReloadFromReserve` computes `min(needed, reserve)` and moves
exactly that many rounds, and `CalculateDamageAfterArmor` resolves
`max(0, Damage − max(0, Armor − ArmorPiercing))` — a genuine armor-penetration
curve, not a flat subtract. Those numbers live on `UV4Attr_Tactical`
(`V4/ue/Source/V4Gameplay/Public/V4AttributeSets.h`), a replicated GAS attribute
set carrying `Health`, `Armor`, `Suppression`, `StaminaSprint`, `AmmoCurrent`,
and `AmmoReserve`. Above the instance, `UV4WeaponLibraryCatalog` is the
authoring layer: ten weapon categories (assault rifle through marksman rifle,
LMG, shotgun, pistol), a per-hit-zone `FV4WeaponDamageProfile` (`Head` / `Chest`
/ `Limb` / `ArmorPiercing`), soft-pathed recoil curves and foley, and a
`ValidateLaunchLibrary` linter. Attachments are a six-slot system (`Sight`,
`Barrel`, `Underbarrel`, `Magazine`, `Grip`, `Stock`):
`UV4WeaponAttachmentComponent::ApplyAttachments` folds each equipped
`FV4WeaponAttachmentSpec`'s recoil, ADS-time, mobility, and magazine modifiers
into the base weapon — the Create-a-Class tuning loop expressed as composable
data.

### Grenades and downed-teammate care

`UV4GrenadeComponent::BuildArcPreview` integrates real projectile motion —
`Start + Velocity·t + ½·g·t²` sampled over `PreviewSteps = 16` at 0.1 s — so the
throw arc the HUD draws is the path the grenade actually flies, and
`ThrowGrenade` decrements a real two-grenade count. Revive is a proper state
machine: `UV4ReviveComponent` runs `Alive → Downed → Reviving → Dead` with a
ticking bleed-out timer, a cancelable revive progress bar, and
`ApplyRevivedHealth` restoring exactly `RevivedHealth = 25` on completion — the
"knocked, not killed" loop that anchors squad play and the CoD campaign's
reviving AI teammates.

## The systems behind the sub-rulesets

Each ruleset is a `GameFeatures` plugin under `V4/ue/Plugins/` that composes the
gunplay core above with its own mode logic, content root, and test spec. The two
most mechanically distinctive are real and spec-covered today.

### Raven Shield — the planning phase and the breach

`V4Mode_Tactical_RavenShield` is the "you plan it, the squad executes it"
doctrine. `UV4RavenShieldPlanningComponent` builds a top-down tactical map,
accepts **two squads (Red, Gold) capped at three operators each**, and authors a
verb-typed waypoint list — `MoveTo`, `StackOnDoor`, `BreachFlash`, `WaitFor`
(with a seconds/sound/hostage condition), `MoveOnGo` — that `ValidatePlan` must
accept before the mission can start. `BuildRehearsalPreview` replays the plan at
a `4×` slowdown, `FindConflictHighlights` flags crossfire and friendly-fire
paths in red, and `PauseAndEnterLiveOverride` / `CommitLiveOverride` let the
player re-author waypoints mid-mission. Execution is
`UV4RavenShieldExecutionComponent`: doors with lock states, breach tools where a
`Kick` cannot defeat a `Locked` door but `Thermite` can (and emits a noise
event), a stack that caps at three with the point operator held first, a
four-color threat-ID system (`Red` hostile, `Blue` hostage), and a room-clear
state machine (`Breach → ThreatAssessment → SecureHostages → Cuffing → Cleared`)
with `Suppress`/`Cuff` verbs gated by threat color. `RavenShieldSpec` drives the
whole loop end-to-end across a validated 16-mission campaign, and registers a
feel-test gate asserting the planning-to-breach loop stays under 75 seconds.

```mermaid
stateDiagram-v2
    [*] --> Plan
    Plan --> Plan: AuthorWaypoint() · ValidatePlan()
    Plan --> Rehearse: BuildRehearsalPreview() (4× slow)
    Rehearse --> Execute: BeginExecution()
    Execute --> Override: PauseAndEnterLiveOverride()
    Override --> Execute: CommitLiveOverride()
    Execute --> RoomClear: stack · breach tool
    RoomClear --> RoomClear: Suppress / Cuff by threat color
    RoomClear --> Cleared
```

### R6 Modern — gadgets, destruction, and two-sided rounds

`V4Mode_Tactical_R6Modern` is the Siege-era asymmetric surface.
`UV4R6ModernMatchComponent` runs **5v5** (the roster rejects a sixth player and
duplicate ids), a `Setup → Action` phase clock that flips at 60 seconds, and
objective formats (`RankedBestOf9` to 5 round wins, `CasualBestOf7` to 4) over
`Bomb`, `Hostage`, and `SecureArea` win conditions with stored partial progress.
Its gadget catalogue is fully authored — **9 attacker gadgets** (hard breach,
soft breach, drone, EMP, smoke, flash, frag, claymore, heartbeat scanner), **7
defender gadgets** (reinforced wall through jammer), and **40 launch
operators**, 20 per side. The standout is `UV4R6ModernDestructionComponent`,
which is not a cosmetic: it classifies wall materials (drywall and plywood soft,
concrete and brick hard, plus floors and ceilings for vertical attack vectors),
gates each destruction tool correctly (frag and shotgun open soft walls but
bounce off concrete; thermite and hard-breach open hard walls), and restricts
reinforcement to the defender side during setup only. `R6ModernSpec` proves the
integration is real by binding an actual Chaos `UGeometryCollectionComponent` in
a live test world and asserting a hard breach drives a genuine fracture
(`bChaosFractureApplied`) whose replication debits a byte-budgeted band — real
physics, server-authoritative, measured rather than asserted.

### CoD — campaign, 6v6, and battle royale

The Call-of-Duty trio (`V4Mode_Tactical_CoDCampaign`,
`V4Mode_Tactical_CoDMultiplayer`, `V4Mode_Tactical_CoDWarzone`) leans hardest on
the shared gunplay core: a 12-mission cinematic campaign with reviving AI squad
teammates, a 6v6 suite (TDM, Domination, Hardpoint, Search-&-Destroy, Kill
Confirmed and more) with a Create-a-Class / Pick-10 loadout economy and
killstreaks, and a 100-player battle royale with the Gulag, buy stations, and
loadout drops. Each is its own plugin with its own `*Spec`, and each consumes
the same `UV4AimComponent`, recoil curve, and `UV4Attr_Tactical` ammo math
described above — which is precisely why a weapon tuned once behaves
consistently from the campaign to the third circle of Warzone. As with all V4
cells, the mode _logic_ is in-tree C++ and the maps, cinematics, and weapon art
are JSON-described content, not baked binaries.

## How gunplay ties to GAS and the shared engine

Every player action routes through the Gameplay Ability System, and V4's
abilities are **cell-gated** so a tactical ability cannot fire on a non-tactical
pawn. `UV4GameplayAbilityBase`
(`V4/ue/Source/V4Gameplay/Public/V4GameplayAbilities.h`) overrides
`CanActivateAbility` to check `IsAbilityCellCompatible`, then funnels real work
into a protected `ExecuteV4Ability` that records a `bLastActivationSuccessful`
flag and a `LastActivationReason` string. The eight tactical abilities in
`V4TacticalAbilities.cpp` each set `RequiredCell = EV4RulesetCell::Tactical` and
**delegate to the components** rather than reimplementing them: `UGA_Fire` calls
`Weapon.ConsumeRound` and fails loud with `"no-ammo"`; `UGA_Reload` returns
`"reload-empty"` when the reserve is dry; `UGA_ADS` drives the aim component;
`UGA_Lean` is refused with `"lean-blocked"` if you are not in cover;
`UGA_ThrowGrenade`, `UGA_BreachStack`, `UGA_Revive`, and `UGA_Heal` each thread
through their component and report a precise rejection tag on failure. This is
the fail-loud seam the project favors: the ability honestly reports what it
could not do instead of faking success.

Two shared `V4Core` libraries back the cell's content without belonging to it.
`UV4GadgetLibraryCatalog` (`V4/ue/Source/V4Core/Public/V4GadgetLibrary.h`)
defines a cross-cell gadget vocabulary with a balance profile per entry
(`Charges`, `CooldownSeconds`, `RadiusMeters`, `NoiseMeters`, `PickCost`, a
`CounterplayWindowSeconds`) and a published balance _ledger_ so nerfs and buffs
are auditable — the same `TacticalFPS` domain that the R6 gadget catalogue draws
from. `UV4TakedownLibraryCatalog` authors melee/takedown verbs (sneak choke,
garrote, and the rest) with motion-warped animation pairs and per-takedown sound
profiles, shared with the stealth cell. The throughline is the architecture's
unifying claim: six genres stay one game because they sit on the same GAS spine,
the same shared `V4Core` libraries, and the same mode machinery. For the
engine-side treatment — netcode authority, the GAS layout, and the cross-cell
seams named here — read the architecture companion,
[../architecture/per-cell-deep-dives.md](../architecture/per-cell-deep-dives.md).

## Related

- The feature hub: [../V4_features.md](../V4_features.md)
- [Stealth & Real-Time Tactics cell](./cell-stealth-and-real-time-tactics.md) —
  the quiet FPS surfaces (Splinter Cell, Spies-vs-Mercs, Hitman) the catalogue
  also files under Tactical FPS, plus the Commandos/Desperados squad genre
- [Shared cross-cell engine](./shared-cross-cell-engine.md) — the GAS spine,
  netcode authority, perception model, and mode machinery every cell sits on
- Architecture companion:
  [../architecture/per-cell-deep-dives.md](../architecture/per-cell-deep-dives.md)
  — the genre-by-genre engine view, including `V4Tactical` maturity
