Quests, story and scenes
The campaign state store, the quest table and the quest object's state machine, how story characters are built, betrayals and region ownership, army transforms, and the presentation system that plays scripted scenes: relays, events, sequences, actors, cameras, the scene clock and slow motion.
Campaign state
Campaign progress is data, held in two tables owned by the GameManager (GM = [base+0x26FFAA8]).
Game variables. GameData/Variables holds 573 records (VariableType 1 int, 2 bool, 0 float:
45 / 514 / 14, each with a default and a designer's description). The store is [GM+0x6C58]:
+0x00 entries (stride 0x18), +0x08 count, +0x10 the category; an entry is +0x00 the record,
+0x08 the value. Entries are in record-index order. Scripts use GameData.<Variable>:GetValue() /
:SetValue(v); the method tables are registered by 0x1B46E44, 0x1B46EF4 and 0x1B46FA4 (Set,
SetValueByRecord, Get, GetSerializeId, TestPredicate). The getter core is 0x1E154C(store, record) and
the bool setter 0x59A1D8(store, record, value).
The variable StoryState carries the campaign stage (its stages, from its own description): 5
intro, 100 Act 1, 105 after the intro of the Shadow of the Past, 110 after Gondor 1, 198 / 199 Blade
1, 200 Act 2, 205 / 210 after the Ratbag and Carnan steps, 230 after Carnan 3, 290 during the finale,
300 after the finale.
Global-variable predicates (Quest/GlobalVarPredicates, 290 records) gate quests and scenes.
The evaluator 0x1E1118: with AndTest (the default) every test must pass, otherwise any one; a
bool test (0x1DFF70) passes when the value equals RequiredValue; an int test (0x1E1470) when
Min <= value <= Max inclusive; a test without a variable fails; with no tests an AND passes and an
OR fails. 330 of the 573 variables are read by no GameDB record (scripts may still read them).
Quests. The quest system is [GM+0x6C60]. Its table at +0x18: entries of 0x40 bytes,
count at +0x08, category at +0x10; an entry is +0x00 the Quest/Quests record, +0x28 the
placed quest object (null when its region is not loaded) and the byte +0x34 the completion counter
(GetIsComplete 0x19F4064 -> 0x1B55008: counter >= NumCompletesForPreReq, which is 1 for every
quest). 2,256 quests exist. GetIsActive 0x171A20 tests the object's state for 17. The running
quests are listed at +0x3D8 of the quest system.
Quest availability (0x1E00C4) rejects at the first failing gate, in this order: completed /
incomplete quest lists (Quest/QuestLists with RequireAllMatch), failed last attempt, active /
inactive quests, any quest active, triggerable quests, preset member unlocked, day only, the
global-variable predicate, and the rest. The rejection messages exist in the executable, but the
logging function they are passed to (0x19C83AC) is an empty ret in the shipped build.
The quest object
A mission marker is a placed quest object (vt_0x1F9DFE0): +0x40 name, +0x58 context, +0x120
its state machine (vt_0x1F9DFB8), +0x180 the trigger and +0x188 the Quest/Quests record; the
trigger's +0x198 is its navigation marker.
The state is the pending state (+0x12C) unless that is -1, otherwise the current state
(+0x128); SetState 0x159DE4 parks the current state at -1 during a transition. The dispatcher
0x1162D8 sends five events: 0 CanEnter, 1 CanExit, 2 Enter, 3 Update, 4 Exit.
| State | Meaning |
|---|---|
| 0 | Inactive |
| 1 | SimEventEval |
| 2 | PreActivate |
| 3 | DirectActivate |
| 4 | FadeOut |
| 10 | TimeOfDay |
| 11-15 | Load* |
| 16 | InstantiateBundles |
| 17 | Active (CanEnter = its bundles loaded; Update = the fail-distance check) |
| 19 | PostActive |
| 20 / 21 / 22 | Completed / Failed / Cancelled |
| 25 | Cleanup |
ActivateBypassTrigger (body 0x19F060C) moves a quest in state 2 or lower to state 3; its handlers
run synchronously, so it belongs on the main thread. Tribe missions fail beyond 120 m of their relay
(the prefab script sets QR:SetFailObject(QR, 15000) and RequestDistanceToObjectEvents(QR, 12000)).
Teleports are queued. CCharacter.TeleportToObject for the player (0x658C30) pushes 0x659884
onto the queue at [base+0x2A86880] and sets the character's byte +0x2C21 until the engine
performs it; the AI version is 0x658B5C. The character vtable's slot 109 (+0x368) is
TeleportToObject and slot 110 (+0x370) TeleportToPoint (coordinates in xmm1-xmm3).
CCharacter.TeleportToLastKnowNavMeshPos 0x19B7AA0 is the engine's own "unstuck", also queued.
Story characters
A story character is a Faction/PresetMembers record (203 exist); a built orc carries it at
FM+0x190 (FactionMember; see The Nemesis system). Two paths build them: army initialisation
(Faction/Definition.PresetMembers, 0xFDAC54) and quests, whose PrefetchPresetFactionMembers are
built fresh at the quest's LoadFactionBundles state (0x625D98 -> 0x62662C -> the army of the
current region -> 0x62666C). The build sequence:
ctx = 0x18DE04(FS+0x98, army)(asserts on a record that is not an army: an army context is a 0x290-byte entry whose+0x18matches the army category);fm = 0x1C1D378(ctx, preset);- if
0x1C1C638([ctx+0xD8], preset)(the preset belongs to that army),0x1BD2300([ctx+0x20], fm).
A character created without a row starts at level NoRowXPLevel - 1. The build copies
GlobalVarToSetOnDeath to FM+0x398; SetDead 0x626E38 sets bit 2 of FM+0x508 and sets that
campaign variable (for example the Bruz brothers' death variables, which gate the next brother's
ambush). After his story ambush the character becomes a captain of the current region (row, tribe,
role, rarity) and FM+0x190 is cleared. A built copy is saved.
A queued ambush fires only in its own region: the request's +0x18 must equal the current
region's faction [FS+0x1B8] (0x6DD92C, "The faction has changed"); otherwise it is retried.
Betrayal and region ownership
The shipped data never lets the player's Overlord betray: all 18 betrayal ambush definitions list
Overlord in ExcludedRowRanks (91 of 100 ambush definitions do), the betrayal job
General_Betrayal picks only captain and warchief rows, and the hit-your-follower betrayal
(ImmediateBetrayal_Predicate: 3 hits in 3 s, then 5 in 6 s) requires IsOverlord_Not. No
Overlord-specific betrayal voice exists; a forced betrayal plays the Overlord's own personality line.
AI_BecomeHighestLevelFollower_Betrayal takes a provided ambusher (NeverPickAmbusherWithJob),
and TriggerAmbush is 0x6DDCB8.
Region ownership is the Overlord's follower bit. Faction:GetFactionPlayerOwned (stub 0x7A2800)
calls 0x1E484C(FS, faction), which finds the region's Overlord and returns bit 27 of his FM+0x508,
nothing else. An Overlord who betrays therefore takes the fortress with him. The fort-defence script
(OwnerNamedOnSpawn) converts any named defender, the Overlord included, to the attackers on a
FA_FollowerBetraysPlayer event.
Army transforms
Faction/Transforms holds four records (AddOlogs, AddBerserkers, AddDefenders, AddSpearmen). The
game applies them once, when the Shelob mission in Cirith Ungol completes (mtnpass/shelob1
QuestComplete), to the Minas Ithil and Cirith Ungol factions. A transform's ClassInjections name a
_StructuresFactionTransformClass record (OldFactionMemberJob
Quest/Jobs/Definition::PlayerKiller_Duel_Other, a ChanceArray [0,1,2,4,1], and
PossibleSimEvents Transform_IntoCaptain_<class>); AddOlogs runs the Berserker event, because no
Olog event ships. The binding 0x1B755EC leads to the injection 0x1C261A4 -> 0x1BD2A54 -> the job
filler 0x1BD40C4, which builds a new orc only when the job names a FactionMemberBuilder
(0x1BD42F1). This job names none, so it picks existing members (0xF9A320, matcher 0xF9BB20: alive,
captain rank, no species, faction or identity filter) and seats them.
Return packages. The 23 WillResurrect_<death> events are the package applied to a captain who
came back from that kind of death: LevelsToAdd 1, 15 actions, KeepPriority 30, reward lists
MaybeNewRole_CheatDeath_<death>, CheatDeath_Ambush and Betrayal_CheatDeath_Ambush, the
ambusher flag and the resurrected gear tag. The 31 Contract_* training orders mostly add a level
and the trait, gang or mount, plus Upgrade_CommonOrcToEpic and Contract_RemoveAnEpicTrait; the
element orders add only the element.
Presentations: scripted scenes
A scripted scene is a presentation. The scripts queue a relay on the player:
System:GetPlayer():QueuePresentation(DBPresentationRelay.FortAssault_Intro, overlord). The native
call is CPlayerObj::QueuePresentation 0x734C30(player, relay, target) -> 0x734C7C(the player's
scene component [player+0x2FC0], relay, target), which builds a message holding the relay's
Event, the target handle (0x18D888) and a camera type (message +0x78).
The chain is relay -> event -> sequence pool -> the first sequence whose predicate passes ->
elements. Each element sets a camera, a duration or an advance hook, script keys, voice and actions;
an action is {source actor, target actor, AnimType or AudioKey}. A sequence plays only if every
predicate passes; WarchiefIntro_EVT, for example, tries the mounted betrayal, the betrayal, the
failed spy chief and finally the default.
Who plays which actor
- The target actor (
Target_ACT,Overlord_ACT) is the relay's target, tested by theAssumedTargetPredicate. - Extra actors (a failed spy chief, a betrayer, a beast) come from
ExtraTargetPredicates. The binder 0x4303E4 takes the first character in a candidate list that passes its object predicate and is not already bound. 0x6FD134 picks the list: relay messages (types 14 / 15) use[base+0x27265B0], every spawned character (it keeps corpses too: about 60 of 120 entries in a battle); other messages use[base+0x2728EE8], a subset of about 12. - Object predicates bind actors mostly by inventory items, which the scene scripts hand out
(
AddInventoryItem).
The sequence evaluator 0x3650B4 (called per pool entry by the start routine 0x43E6B8; the
queued message is its context: subject at ctx+0x88, source at ctx+0x80) tests, in order: the
player predicate; the source predicate (a source, when present, must pass the object predicate and
is bound to its actor by 0x43EE28; a failure fails the branch); the assumed-target predicate (the
subject; none fails the branch); the player's-target predicate (0x182B5F0 -> 0x523B0C([player+0x2FA0])
= the player's own combat object's current target; null while idle, which fails the branch); and
the AI-spawn predicate last, so a spawn anchored on the target sees a source-bound victim. Field
reads go through a per-category slot table ([base+0x2645720] -> static slots at 0x2892A70, 0x48
bytes each). The evaluator does not check that the target is still alive: the target test reaches
the held-item test 0x24DF7C([char+0x2348], item) through 0x365882 -> 0x2F1768 -> 0x275BE0, so a
character being removed (inventory component already cleared) faults there.
Cameras
The camera filter 0x4328DC compares each pool entry's CameraType and CameraVariantRequireAny
with the active type [ctx+0x38] and variant [ctx+0x40]. The type is decided when the relay is
queued (0x734E1C): the relay's own CameraType (at +0x20 in every relay, a link to
Presentation/Predicates/Types/Camera), otherwise, when QuestCameraType is set, the
PresentationCameraType of the first running quest. Siege quests carry <Region>_FortAssault_CT,
so the fixed fortress shots exist only inside a siege. The variant (A or B, the two sides an assault
can start from) comes from the sequence's CameraVariance picker. Fixed frames are absolute world
positions in cm (Presentation/WorldFrame, 694 records with CameraPos and LookAtPos); their
prefixes name the regions: DS Lithlad, IS Nurnen, MP Cirith Ungol, SM Seregost, VC Gorgoroth.
The scene clock and slow motion
Every element duration is a timer at +0x288 of the running presentation, on the clock
[base+0x2702578] (0x37B784 -> 0x37BCE4; expiry is bit 1 of pres+0x6C). The engine keeps five
scaled clocks, which slow motion drives as channels (0x5CF888): 0 the world [base+0x2702598], 1
the player [base+0x2702548], 2 the combat companion [base+0x2702570], 3 the world UI
[base+0x2702558] and 4 presentations [base+0x2702578]. 0x5CF834 writes a clock's numerator,
denominator and scale at [clock+8]+0x28 / +0x2C / +0x30; 0x5CEBB4 resets it to 1/1. A slow-motion
instance stores channel c as {+0x44 enabled, +0x48 scale} + c * 0xC. Period and ramp-up timers run
on [base+0x2702550] (UI: [base+0x2702588]), which slow motion never scales.
The slow-motion manager is MainScene+0x7A8 (ticked by the scene tick): requested instances at
+0x00 / +0x08, the running one at +0x38 (the update 0x5CF55C picks the lowest priority at
instance+0x80; requests go through 0x5D08B8). An instance copies its SlowMo record's scales at
Start (0x5CEC10), and the manager re-runs Start whenever its current instance changes. A SlowMo
record's enum pair (1, N) means N times slower (332 records; KillCam_13x_SM = (1, 13)).
Advance hooks (Presentation/AdvanceHook, 122 records) are {Actor, Hook} with hook 0 FacialEnd,
1 DialogEnd, 2 AttackEnd and 5 AttackStart. They are raised by 0x42DA2C (via 0x42DA18 and the
attacker's presentation link [obj+0x100], from the attack path 0x2E0520) and matched by
0x42FFEC(pres, type, actor) against the element's copied hook (pres+0x1C0). AdvanceOnTargetDeath
(0x37B6C4) advances when the target actor's binding returns null; a victim's dead flag and binding
drop as the execution starts. Element camera definitions, durations and slow motion are copied when
the presentation starts (0x5D0AB4).
A presentation can take the camera and hold the player (CommandSet PresentationDisablePlayer);
0x17A13AC stops the player's scene component.
Execution variants
Which animation a sync kill plays is a variant table: AnimationData.AnimOverridePicker /
AnimOverrides -> Combat/AttackAnimOverride -> an entry chosen by the attacker's weapon class
(0x2D5430, which checks the source's character type [combat+0x60] and weapon type per entry) ->
_StructuresCombatAttackAnimOverride variants (Weight, source and target animations, the source's
weapon animation, PresentationCameraType, SeverActionTarget and Pred_* gates). The chooser 0x2D5828
keeps variants with weight > 0 that pass every gate, then picks at random by weight (0x1B3B90) among
those not played within RecentAttackWindow; when all are recent it plays the oldest, so one passing
variant always plays. Olog entries are selected only by weapon type (SourceRequiredWeaponType =
WeaponTypeList/Olog_All), so an olog holding an orc-typed weapon item draws an orc animation.
Combat/Sync sets both characters' scale (0x11498C8) to SourceModelScaleOverride /
ModelScaleOverride; with UseMinimalScaling it keeps only their difference, split half each
(0x202E8F8).
Fortress scenes
A siege is staged by the fortress layers' scripts with presentations and animate tasks.
| Scene | Relay | Cameras |
|---|---|---|
Warchief walk-out intro (WarchiefIntro_EVT) |
WarchiefIntro_RL |
follow the actor |
Fort defence: the Overlord arrives (FortDefense_OverlordArrival_EVT) |
FortDefense_Overlord |
follow the actor; the voice line rides the presentation, so any orc delivers it |
Talion's victory walk-out (FortOutro_TalionBalcony_EVT) |
FortAssault_Outro (_Conquest without the speech) |
a rig on Talion's clip |
Keep throne showdown (Overlord_Showdown_EVT) |
FortAssault_OverlordKeep |
a fixed throne view per fortress, then follow |
Keep-top intro: the Overlord executes the failed spy chief (FortAssault_Intro_Overlord_EVT) |
none (only the NextEvent of the walk-out) |
fixed fortress frames |
The Witch-king makes an Overlord kill himself (She3_OverlordSuicide_EVT) |
its relay has no event | follow the actor |
Spy-chief reveal explosion (SpychiefExplosion_0x_EVT) |
FA_RevealSpy_0x |
look at the target |
E3 demo fortress scenes (DEMO_*) |
FAD_* |
E3 rigs (cut content) |
85 of the 860 Presentation/Events belong to fortress assaults and defences, with 103 relays.
Script keys (FA_Intro_UnhideOverlord_SK, FD_OV_Arrive_DoorOpen_SK ...) do not stop a scene;
they are signals the siege layer's scripts listen to.
The keep-top execution (FortAssault_Intro_Overlord_EVT, sequence "Spychief Failed"): a fort
shot and a keep shot; scale set on both actors; the attack FA_OV_Spychief_Walkout_AN (clip
OrcOV_Orc_Sync_Spyfail_A, sync Sync_FA_Intro_OV_Spychief_Fail: the victim is aligned to the
Overlord, off-navmesh movement allowed, DmgForm_All_Instakill); the voice
PR_FA_Intro_Ov1_SpyFound_Bundled, a kill camera, the crowd speech; then NextEvent to the
followers' scene. The siege script dismounts and hides the Overlord, teleports him and the spy chief
to the balcony markers, gives the spy chief ScriptItem_DoNotExplodeOnDeath and
SetNeverSimulate(true), and queues the relay when the player arrives.
Actor items and their save flags:
| Item | Saved | Binds |
|---|---|---|
FortIntro_Overlord |
no | the Overlord (intro, keep showdown) |
FA_SpyChiefFail_IT |
yes | the failed spy chief |
Warchief_Betrayed_IT |
yes | the betrayed warchief |
Warchief_SpychiefBetrayer_IT |
yes | the betrayer |
ScriptItem_DoNotExplodeOnDeath |
yes | (spy-chief safety) |
Markers (cm, from the fortress layers):
| Fortress | Balcony pBalcony.tpOverWithSC |
Throne RZKeepLayout.tpOverKeep |
|---|---|---|
| Gorgoroth | 851.0, 6636.5, 7065.0 | 849, 2395, -798.5 |
| Nurnen | -28927.0, 5028.5, 64966.0 | -28938, 798, 72818 |
| Cirith Ungol | -1786.0, 44887.5, 37240.0 | -1808, 40665, 45081 |
| Seregost | -1795.0, 6178.5, 13446.0 | -1789, 1952, 21298 |
| Lithlad | -784.0, 10614.5, 14543.0 | -799, 6345, 22398 |
The throne is an AI node in the fortress layer (RZKeepLayout.nKeep, smart object
SO_OvrlrdSitting, tag FA_Overlord); the Overlord's spawn job walks to it and sits. Each throne
lies 47-57 m down the hall from its fortress's throne-view camera, on its axis.
Animate tasks
The scripts animate any NPC through smart objects, without a presentation or a camera:
| Binding | Native | Arguments |
|---|---|---|
AnimateSmartObject |
0x1A8AC48 | (npc, AI/SmartObjects record) |
AnimateSmartObjectWithTarget |
0x1A8AE68 | (npc, smart object, target) |
AnimateSmartObjectPickerWithTarget |
0x1A8ACE4 | (npc, AI/SmartObjectPicker, target) |
AnimateSmartObjectPickerLooping |
0x1A8AC8C | |
AnimateClear |
0x757F50 | (npc, check dead) |
Ambient* variants |
0x1A8AA20 - 0x1A8ABCC | interruptible versions |
Each refuses a dead NPC and creates an AI animate task. Useful smart objects:
Bip_Prisoner_Hostage_Kneeling_Looping (kneel as a hostage); Bip_KillHostage_Single (attack type
KillHostage_Single: head chop, head rip, neck break, gut stab or a shot, chosen by weapon; each a
sync where the executioner walks to the victim, victim filter P0070_BipedsExceptSlaves; a Nazgul
variant exists); Picker_Bip_HostageExecute_(Un)Sheathed (executions by species and weapon);
SO_OvrlrdSitting (the throne), SO_ChantLoop (the siege chant), Bip_Cheer_Looping,
TrlCave_Sitting_Looping and DEMO_Overlord_SitChair. Both characters of an execution must share a
walkable surface.
CCharacter::AddInventoryItem 0x1E6B88(char, item) gives an item (0x1C01E0([char+0x2348], item, 1)
plus a refresh 0x114E0C8); RemoveInventoryItem is 0x4F12B4. FM+0x48 is a FactionMember's spawned
character.
Open questions
- What the bytes at
+0x30..+0x3Fof a quest-table entry mean. The table lookup returns the entry's value part at+0x08, andGetIsCompletereads the completion count as the byte at value+0x2C(entry+0x34). In one late campaign (2,256 entries): the word at+0x30held 1 to 6 (3 on 1,953 entries, 1 on 255, 4 on 31, 6 on 6, 5 on 5, 2 on 4);+0x31 .. +0x33and+0x35were 0;+0x34was 1 on 459;+0x36 .. +0x3Eare 0 / 1 flags set on 80 to 1,975 entries each;+0x3Fheld many different small values. - Whether the warchief intro's lines are personality dialogue (they did not play on a plain grunt).
- What the 56 placeholder movies of the third story DLC stand in for at run time (see Audio and video).
Shadow of War (Steam), game version 1.21. An unofficial fan research project, not affiliated with or endorsed by Warner Bros. Games, Monolith Productions or Middle-earth Enterprises. Middle-earth: Shadow of War belongs to its owners.