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:

  1. ctx = 0x18DE04(FS+0x98, army) (asserts on a record that is not an army: an army context is a 0x290-byte entry whose +0x18 matches the army category);
  2. fm = 0x1C1D378(ctx, preset);
  3. 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 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

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.