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.
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.
V4Stealthis eight.cpp/ eight.hof 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.Samplingwalks a lit light through a light-out into darkness and checks the 150 cm close-detect boundary;V4.Stealth.Disguise.Enforcerproves an enforcer detection time of exactly4.0 × 0.5 = 2.0 sand that a sub-two-second disguise swap is refused.V4Tacticsis eight.cpp/ eight.hof real squad-tactics logic. The command queue, party roster, vision cone, body-disposal, and the Showdown subsystem are all in-tree;V4.Tactics.Showdown.DependencyResolverproves the topological order resolver and its cycle rejection, andV4.Mode.RTST.Desperadosdrives 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
V4StealthwithV4TacticalandV4Perception, and the Commandos plugin pullsV4Tactical+V4Stealth+V4Tacticstogether. Second, the logic is code but the art is JSON — meshes, montages, and levels are*.v4asset.jsonsidecars andDT_*data tables (e.g. the Hitman world underV4/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.
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 returns 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.
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.
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.
Related#
- The Tactical-FPS cell — 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 — the other top-down cell, where determinism is deterministic lockstep rather than RTST's server-authoritative real-time-with-pause.
- AI, Perception & Stealth — the shared suspicion gauge, sense library, and crowd model both cells read.
- Per-Cell Architecture Deep-Dives — the module-by-module engine view of all six cells.
- The catalogue hub: ../V4_features.md.