Event orchestration
Real GTA V missions do not just spawn enemies who immediately shoot. Think of the best moments in the story: two dealers argue in an alleyway, a third character steps out of the shadows, a phone rings, and only then does the deal go sideways.
Orchestrating multiple actors requires coordinating timelines: when actor A reaches a door, actor B must react, and actor C must flee.
The problem with hard-coded timers
Amateur scripters often try to coordinate scenes using raw delays:
// Fragile: what if actor A gets blocked by a car?
actorA.Task.GoTo(meetingPoint);
// 5 seconds later, assume actor A is there...
actorB.Task.PlayAnimation(...);
If actor A gets stuck behind a parked van or trips over a curb, actor B starts talking to empty air five seconds later. The scene falls apart.
Cue-based event choreography
Professional modding scenes are cue-based rather than time-based. Step N only begins when Step N-1 signals that its physical conditions have been met:
[ Step 1: Approach ]
Both actors walk to the meet point.
Wait until: Distance between actors < 2.0m.
| (signal: InPosition)
v
[ Step 2: Conversation ]
Actors play talking animations and subtitles show.
Wait until: Player draws weapon OR enters trigger zone.
| (signal: Provoked)
v
[ Step 3: Escalation ]
Buyer flees on foot. Dealer draws pistol and opens fire.
Implementing a scene coordinator in C#
You can coordinate this cleanly using a step counter or an explicit scene state:
public sealed class DealSceneCoordinator
{
private enum SceneStep { WalkingToMeet, Conversing, FleeingAndShooting, Finished }
private SceneStep _step = SceneStep.WalkingToMeet;
public void Update(Ped dealer, Ped buyer, Ped player)
{
switch (_step)
{
case SceneStep.WalkingToMeet:
if (dealer.Position.DistanceTo(buyer.Position) < 2.5f)
{
// Both arrived: trigger conversation
dealer.Task.ChatTo(buyer);
buyer.Task.ChatTo(dealer);
_step = SceneStep.Conversing;
}
break;
case SceneStep.Conversing:
// Interrupt if player intervenes
if (player.Position.DistanceTo(dealer.Position) < 6f || player.IsArmed(WeaponCheckFlags.All))
{
TriggerEscalation(dealer, buyer, player);
_step = SceneStep.FleeingAndShooting;
}
break;
case SceneStep.FleeingAndShooting:
// Check if scene has resolved
if (dealer.IsDead && buyer.Position.DistanceTo(player.Position) > 50f)
{
_step = SceneStep.Finished;
}
break;
}
}
private void TriggerEscalation(Ped dealer, Ped buyer, Ped player)
{
dealer.Task.ClearAll();
buyer.Task.ClearAll();
// One fights, the other flees
dealer.Weapons.Give(WeaponHash.MicroSMG, 100, true, true);
dealer.Task.Combat(player, TaskCombatFlags.None, TaskThreatResponseFlags.None);
buyer.Task.FleeFrom(player);
GTA.UI.Notification.PostTicker("The deal went sour!", false);
}
}
By decoupling the scene into observable conditions, the choreography adapts naturally whether the player watches silently from a rooftop with a sniper rifle or charges in with a shotgun.
In Project 06, we build this exact dynamic deal event.