# The Stealth & Real-Time-Tactics Cells

Two of V4's cells are built on the same insight: the most dangerous thing in the
room is not a guard's gun, it is a guard's _attention_. The **Stealth cell**
(`V4Stealth`) is the Splinter Cell / Hitman surface — one operator, first- or
third-person, who survives by managing light, sound, and a stolen uniform. The
**Real-Time-Tactics cell** (`V4Tactics`, which V4 markets as _Real-Time Stealth
Tactics_) is the Commandos / Desperados surface — a top-down squad of six
specialists who pause time, queue precise orders, and trigger them in unison so
that six kills land before a single patrol can shout. They look nothing alike on
screen, but they read the world through one perception model, hide bodies with
sibling components, and weaponise the same vision cones. This page covers what
each cell plays like and the signature systems that give it its feel, grounded
in the in-tree Unreal C++ that actually ships. It is part of **The Cells**
group; the catalogue hub is [../V4_features.md](../V4_features.md).

## What ships, honestly

Both cell modules are **real, compiled, and value-tested** — and both lean on
referenced art rather than baked binaries. The line between the two is drawn
explicitly below so the rest of the page reads at face value.

- **`V4Stealth` is eight `.cpp` / eight `.h` of real stealth logic.** The light
  gauge, sound footprint, disguise economy, body-drag, gadget, and
  mark-and-execute components all carry domain formulas, not CRUD. Two
  automation specs assert _computed_ values: `V4.Stealth.LightGauge.Sampling`
  walks a lit light through a light-out into darkness and checks the 150 cm
  close-detect boundary; `V4.Stealth.Disguise.Enforcer` proves an enforcer
  detection time of exactly `4.0 × 0.5 = 2.0 s` and that a sub-two-second
  disguise swap is refused.
- **`V4Tactics` is eight `.cpp` / eight `.h` of real squad-tactics logic.** The
  command queue, party roster, vision cone, body-disposal, and the Showdown
  subsystem are all in-tree; `V4.Tactics.Showdown.DependencyResolver` proves the
  topological order resolver and its cycle rejection, and
  `V4.Mode.RTST.Desperados` drives a full six-specialist mission set through it.
- **Three honest caveats.** _First_, "cell" means the **engine module**, not a
  one-to-one game: the Splinter Cell and Hitman plugins compose `V4Stealth` with
  `V4Tactical` and `V4Perception`, and the Commandos plugin pulls `V4Tactical` +
  `V4Stealth` + `V4Tactics` together. _Second_, the **logic is code but the art
  is JSON** — meshes, montages, and levels are `*.v4asset.json` sidecars and
  `DT_*` data tables (e.g. the Hitman world under `V4/stealth/hitman/`), not
  cooked `.uasset`. _Third_, two seams are narrower than they look and are
  flagged in place: `CanSplinterCellSightDetect()` currently **delegates** to
  the generic detect path, and the RTT vision cone's low-light term is a flat
  designer scalar, not a live lux probe. The architecture-side treatment of the
  shared perception model is
  [../architecture/ai-perception-stealth.md](../architecture/ai-perception-stealth.md).

## The Stealth cell — `V4Stealth`

One operator against a populated building. You are not stronger than the guards;
you are _quieter_, and the cell turns quietness into mechanics you can read on
the HUD. `V4Stealth.Build.cs` depends on `V4Perception`, `V4Gameplay`, and
`GameplayAbilities`, so every exposure the player generates feeds the _shared_
suspicion gauge that the NPCs read — the player's side of the equation lives
here, the guard's side lives in `V4Perception`.

### Reading the dark — the light/shadow gauge

The Splinter Cell fantasy is "the shadows are a tool," and
`UV4LightGaugeComponent` (`V4Stealth/Public/V4LightGaugeComponent.h`) makes the
shadows a real number. `CalculateLuxAtCapsule()` sums every enabled
`FV4StealthLuxSample` against the player capsule with a _squared_ distance
falloff, measured to the capsule's vertical **axis** rather than a single point
— so a crouched silhouette under a ceiling lamp reads as lit, not as a dot that
the lamp happens to miss. The lux total classifies through
`DarkLuxThreshold = 0.3` and `LitLuxThreshold = 0.65` over an
`AmbientLux = 0.02` floor into `EV4LightExposureState { Dark, Dim, Lit }`.

Detection is then gated by that state. `CanBeSightDetected(distance, hasLOS)`
returns false with no line of sight, true whenever you are not in `Dark`, and in
true darkness only when the observer closes inside
`DarkCloseDetectionDistanceCentimeters = 150` cm. Darkness hides you at range
and fails you at arm's length — the rule the genre lives on. Shooting a bulb is
a real state change, not a cosmetic: `ApplyLightOut(LightId)` flips that
sample's `bEnabled` off (reversible via `RestoreLight`), dropping the next
capsule sample below the dark threshold. `LightGaugeSpec.cpp` walks the whole
loop — lit overhead light, light-out, dark, then the 150 cm gate — and the
**honest seam** shows up right there: `CanSplinterCellSightDetect()` is its own
named entry point, but the body simply `return`s `CanBeSightDetected(...)`. It
is the hook where a stricter Splinter-Cell-only rule would land; today it shares
the generic threshold, and the spec tests it at the 150/151 cm boundary as
exactly that.

### Sound, surfaces, and bodies

Light is only half of exposure; the other half is the noise you make.
`UV4SoundFootprintComponent` owns the _emission_ side (distinct from the guard's
_hearing_). Base radii fall off with stance — `Sprint 1200`, `Run 900`,
`Walk 500`, `Crouch 250`, `Crawl 120` cm — and scale by a surface multiplier
resolved from the physical material: `Concrete 1.0`, `Wood 1.15`, `Metal 1.35`,
`Gravel 1.45`, `Water 1.6`, `Carpet 0.55` (registered as defaults in
`RegisterDefaultSurfaceProfiles`). `EvaluateHearingRange` answers whether a
listener is inside that radius and with what falloff strength; the spec asserts
the _ordering_ a player must internalise — a metal walkway is louder than
concrete, carpet is quieter — rather than just "a number came back."

When a guard does go down, `UV4BodyDragComponent` is the carry verb:
`AttachBody`/`DropBody` at a `DragMovementSpeedMultiplier = 0.5` (half speed),
the tax that makes hiding a corpse a real decision under a patrol clock. Drag it
out of sight, or the discovery raises the alarm for the whole sector.

### The disguise economy — the Hitman face

The same module wears the Hitman mask. `UV4DisguiseComponent` is a genuine
disguise _economy_, not a costume slot. `BeginDisguisePickupSwap()` **fails
loud**: it returns false unless the target was knocked out **and** dragged
**and** hidden in a container **and** the swap takes `≥ 2 s` — only then is the
uniform worn. Once you are wearing it,
`ComputeSuspicionMultiplier(zone, suspiciousAction, obviousWeapon)` is the live
cost knob: an allowed zone scales incoming suspicion by
`0.3 × lerp(1.0, 0.5, Stealthiness)`, a restricted zone by `1.75×`, a suspicious
action by an extra `2×`, an obvious weapon by `1.5×`. The enforcer — the NPC who
wears your stolen uniform and sees through it — is modelled as _detection time_,
not as a suspicion bump: `ComputeEnforcerDetectionTimeSeconds` shortens the base
detect time by a clamped scale, so the disguise that fools the crowd is your
fastest tell near the people whose job it imitates. (An
`EnforcerSuspicionMultiplier = 2.0` field exists but is **not** read by the
multiplier path — the wired enforcer mechanic is the detection-time one, which
`DisguiseSpec.cpp` pins at `4.0 × 0.5 = 2.0 s`.)

That sandbox has data behind it. `V4/stealth/hitman/persistent-world.json`
defines a named-NPC roster (`TargetA`, `TargetB`, `SecurityChief`, `Chef`,
`Doctor`, `VIPGuest`, `Mechanic`) keyed `{MapId}.{NpcSuffix}`, with
story-persistent target deaths, a codex that unlocks each NPC's schedule once
_observed in mission_, and a `/crime-rate` telemetry dashboard aggregating
community kills by season, map, room, and method. The seasonal companion rotates
those routines quarterly (`schedule-rotation-patch-notes.md`) — the same roster,
re-timed each season — and the NPC daily schedules themselves are authored
`FV4ScheduleNode` graphs in `V4Schedules`, which `V4Tactics` also depends on.

### Gadgets and mark-and-execute

The loadout layer rounds out the kit. `UV4StealthGadgetComponent` runs a
charge-limited loadout over
`EV4StealthGadgetType { StickyCam, EMP, OpticCable, SleepingGas, StickyShocker }`,
decrementing charges on activation and tracking active effects.
`UV4MarkAndExecuteComponent` is the Conviction-style tag-then-fire chain as a
small honest slot economy: `AwardMarkSlot` earns capacity (capped at
`MaxMarkSlots = 4`), `TryMarkTarget` consumes a slot, `ExecuteMarkedTargets`
fires the queue and clears it. All of these verbs surface as real Gameplay
Abilities — `UGA_BodyDrag`, `UGA_LightKill` (Damage 100), `UGA_StealthTakedown`
(`TakedownId = "SneakChoke"`), `UGA_StickyCam`, `UGA_MarkAndExecute`,
`UGA_Disguise` (default `Stealthiness 0.85`) — mounted on the shared
`UV4GameplayAbilityBase`, so a stealth operator carries the same attribute spine
as every other cell.

```mermaid
flowchart TB
    Lights["world lights"] --> Lux["UV4LightGaugeComponent<br/><sub>lux → Dark 0.3 / Lit 0.65 · 150cm close-gate</sub>"]
    Move["stance × surface"] --> Foot["UV4SoundFootprintComponent<br/><sub>120–1200cm × 0.55–1.6</sub>"]
    Body["downed NPC"] --> Drag["UV4BodyDragComponent<br/><sub>0.5× drag speed</sub>"]
    Disg["UV4DisguiseComponent<br/><sub>zone 0.3/1.75× · enforcer detect-time</sub>"]
    Lux --> Susp["V4Perception<br/><sub>shared suspicion gauge</sub>"]
    Foot --> Susp
    Disg --> Susp
    Drag --> Disg
```

## The Real-Time-Tactics cell — `V4Tactics`

Now pull the camera up to a top-down isometric, swap the lone operator for a
squad of six, and add the verb the genre is named for: **pause**. The RTST cell
is real-time-with-pause, no respawns — lose a specialist and the mission fails.
You watch enemy vision cones sweep the ground, queue a precise
move-attack-distract plan, and release it. `V4Tactics.Build.cs` depends on
`V4Perception` and `V4Schedules` (notably _not_ `V4Stealth` — that fusion
happens one layer up, in the Commandos plugin).

### Pause, queue, trigger — the command model

Two components carry the moment-to-moment loop. `UV4PartyComponent` holds the
roster — `MaxPartyMembers = 6`, with a focused-specialist selection and
add/remove that broadcasts focus changes. `UV4CommandQueueComponent` is the
per-character order buffer, capped at `MaxCommandsPerSpecialist = 2` and drained
FIFO; `CommandSpec.cpp` proves the cap (a third order for one specialist is
rejected) and the first-in-first-out pop order. Each `FV4TacticsCommand` is a
typed unit of intent — `Move / Attack / ItemUse / Distraction / Takedown`, a
target, a `DependencyIds` list, a `ResolveSeconds`, and a `bPreFireValid` flag —
which is exactly the shape the Showdown planner consumes.

### Showdown Mode — the simultaneous-turn planner

`UV4ShowdownModeSubsystem` is the Desperados signature, and the code is emphatic
that the world **slows, it does not stop**: `ShowdownWorldTimeDilation = 0.125f`
is one-eighth speed, chosen so the scene stays readable while the AI cannot
react in time. You queue up to `MaxShowdownCommands = 4` cross-specialist
actions, then `TriggerShowdown()` fires them. The heart of it is
`ResolveCommandOrder()`, a real **topological sort**: it repeatedly emits any
command whose dependencies are already resolved, routes invalid or
missing-dependency commands into a `OutBlockedCommandIds` list, and — crucially
— returns **false** when a full pass makes no progress, which is precisely a
dependency cycle. `ShowdownSpec.cpp` queues a shot that depends on a coin throw
_out of order_ and asserts the coin resolves first and the shot second; then it
makes the two depend on each other and asserts both are blocked. This is a
genuine simultaneous-turn solver, not a slow-motion toggle.

The full mode test goes further. `V4.Mode.RTST.Desperados` builds the
six-specialist roster (Gunslinger, Sharpshooter/Medic, Brawler, Saloon
Infiltrator, Mystic, Pack-Animal), authors seven distractions and fourteen
missions (ten main + four side ops), drives the Showdown planner through a
four-action plan that resolves dependencies and executes in **under one second
of real time**, and confirms a _failure preview_ catches a queued action whose
dependency is missing **before** the trigger — the UI tell the genre needs. A
registered feel-test case asserts that sub-one-second budget and passes.

```mermaid
stateDiagram-v2
    [*] --> RealTime
    RealTime --> Showdown : SetShowdownActive(true)<br/>world × 0.125
    Showdown --> Showdown : QueueShowdownCommand()<br/>(≤ 4, bPreFireValid)
    Showdown --> Resolve : TriggerShowdown()
    Resolve --> RealTime : ResolveCommandOrder()<br/>topo-sort · fire ordered · drop blocked GUIDs
    note right of Resolve
        cycle or missing dep → false,
        offending GUIDs reported.
    end note
```

### Vision cones — the genre's iconic primitive

`UV4VisionConeComponent` is both UI and logic. It is a 2D cone — `Forward`,
`HalfAngleDegrees = 60` (a 120° field), `RangeCentimeters = 1200` (12 m) — with
green/yellow/red Calm/Alert/Hostile colours. `ContainsPoint()` is just
`GetDetectionWeight() > 0`; `GetDetectionWeight()` blends an angle term and a
distance term (`angleAlpha × 0.65 + distAlpha × 0.35`) so dead-centre, close
targets weigh most. `BuildGroundConeVertices()` fans the cone into a triangle
strip for the floor decal you see swept across the ground. The **honest seam**:
that weight is then multiplied by `(1 - LowLightFalloff)`, and `LowLightFalloff`
is a flat per-cone designer scalar (default `0.5`), _not_ a live lux query — the
lux-driven version of sight lives in the shared `UV4PerceptionSenseLibrary`. So
this component is the RTT-facing cone primitive with a static darkness knob; the
real light coupling is a cross-system concern documented in the architecture
companion.

### Specialists, distractions, body disposal

`UV4SpecialistAbilitiesComponent` grants and activates the per-character verbs
in
`EV4SpecialistVerb { Lasso, Coin, Perfume, MindControl, Whistle, StoneThrow, DecoyBoot, BodyDecoy }`
— the distraction toolkit that lures patrols off their routes. And the cell has
its _own_ body-disposal path: `UV4BodyDisposalComponent` runs
`BeginDragBody → HideDraggedBodyInContainer`, broadcasting `OnBodyHidden` so a
found body can raise the squad's alarm. It is the sibling of `V4Stealth`'s
`UV4BodyDragComponent` — two cells, the same "where do I put this corpse"
problem, solved at each cell's altitude.

## Where the two cells meet

Neither cell owns the rule that decides whether you were _seen_ — that lives in
`V4Perception` and `V4Crowd`, which both cells consume. A guard's suspicion runs
through `UV4SuspicionStateMachine` (Calm → Curious → Alert → Hostile with a
hysteresis margin so it can't strobe); a Hitman crowd panics through
`UV4CrowdSubsystem`, which tiers up to 400 agents into Background / Medium /
Hero, routes panicked civilians to the nearest exfil node with capacity, and
demotes tiers until the estimated tick fits `MassTickBudgetMilliseconds = 8`
(the perf spec proves 400 agents stay under budget). That crowd layer is
**Mass-adjacent**: each agent carries a real `FMassEntityHandle` and
`GetMassIntegrationStatus()` honestly reports which Mass modules are loaded, but
the panic/flee/tier work runs on a plain `TArray` that mirrors Mass concepts
rather than a live processor graph. The Desperados mode test confirms the wiring
from the other direction — it asserts the cell _reuses_ `V4Tactics.VisionCone`,
`V4Tactics.ShowdownMode`, `V4Perception.LineOfSight`, and
`V4Perception.Suspicion`. The full perception and crowd treatment is its own
page:
[../architecture/ai-perception-stealth.md](../architecture/ai-perception-stealth.md).

```mermaid
flowchart LR
    Stealth["V4Stealth<br/><sub>light · sound · disguise</sub>"] --> Perc["V4Perception<br/><sub>suspicion state machine</sub>"]
    Tactics["V4Tactics<br/><sub>cones · Showdown · squad</sub>"] --> Perc
    Perc --> Crowd["V4Crowd<br/><sub>panic · tiers · exfil (≤ 8ms)</sub>"]
    Sched["V4Schedules"] --> Tactics
    Sched --> Stealth
```

## Related

- [The Tactical-FPS cell](./cell-tactical-fps.md) — the FPS surface that _also_
  composes `V4Stealth` (Splinter Cell linear ops, Spies vs Mercs, the R6 stealth
  gadgets) at the plugin layer.
- [The RTS cell — StarCraft & Age of Empires](./cell-rts-starcraft-and-aoe.md) —
  the other top-down cell, where determinism is deterministic _lockstep_ rather
  than RTST's server-authoritative real-time-with-pause.
- [AI, Perception & Stealth](../architecture/ai-perception-stealth.md) — the
  shared suspicion gauge, sense library, and crowd model both cells read.
- [Per-Cell Architecture Deep-Dives](../architecture/per-cell-deep-dives.md) —
  the module-by-module engine view of all six cells.
- The catalogue hub: [../V4_features.md](../V4_features.md).
