Custom Callouts
You can add your own callouts in client/open/custom_callouts.lua. It runs on the client of the unit who accepted the callout.
Step 1: add it to the list
In shared/config_callouts.lua, add an entry to Config.Callouts.list:
{ id = 'loitering', service = 'police', label = 'Loitering', code = '10-37', priority = 3, weight = 5,
description = 'Group loitering outside a store.' },Step 2: register the scenario
In client/open/custom_callouts.lua, register a scenario with the same id:
RegisterCallout('loitering', {
tasks = {
{ id = 'talk', label = 'task_check_id' },
{ id = 'resolve', label = 'task_arrest_or_release', final = true },
},
setup = function(ctx)
local pos = FindPedSpot(ctx.coords, 15.0)
for i = 1, 2 do
local ped = ctx:SpawnPed(RandomFrom(Config.CalloutPeds.suspects), pos + vec3(i, 0.0, 0.0), 0.0)
TaskStartScenarioInPlace(ped, 'WORLD_HUMAN_SMOKING', 0, true)
ctx:AddSuspect(ped, { compliant = true })
end
end,
update = function(ctx)
ctx:Task('talk', ctx:AnySuspect(function(_, s) return s.idChecked end))
ctx:Task('resolve', ctx:SuspectsHandled())
end,
})Restart kartik-ers and the callout is offered like any other.
The scenario
| Field | When it runs |
|---|---|
tasks | The HUD checklist. { id, label, optional = true, final = true } |
pending(ctx) | Every 500 ms until the scene spawns (e.g. dispatch updates on the way) |
setup(ctx) | Once, when the unit gets close. Spawn everything here |
onArrive(ctx) | Once, when the unit is on scene |
update(ctx) | Every tick after the scene spawns. Return 'completed' or 'failed' to finish |
cleanup(ctx) | When the callout ends, however it ends |
assist | For joint callouts: tasks for assisting services (see below) |
The callout completes on its own once every required task is ticked. Task labels are locale keys: add your own to locales/en.json.
The ctx API
Location
| Method | Returns |
|---|---|
ctx.coords, ctx.heading | The road node the callout was placed on |
ctx:RoadSide(offset) | A point at the side of the road |
FindPedSpot(coords, radius) | A pavement spot for a ped |
ctx.state | A free table for your own data |
Spawning
| Method | |
|---|---|
ctx:SpawnPed(model, coords, heading) | A ped, cleaned up when the callout ends |
ctx:SpawnVehicle(model, coords, heading) | A vehicle |
ctx:SpawnPedInVehicle(vehicle, model, seat) | A ped in a seat |
ctx:SpawnObject(model, coords, heading) | A prop |
ctx:SpawnPatient(coords, model, conditionId) | A patient on the ground, for the medical system |
People and vehicles
| Method | |
|---|---|
ctx:AddSuspect(ped, overrides) | Registers a suspect |
ctx:AddVictim(ped, overrides) | Registers a victim (or a patient with condition) |
ctx:AddCivilian(ped, overrides) | Registers a witness or bystander |
ctx:FlagVehicle(vehicle, { stolen = true }) | Flags a vehicle on its record |
ctx:SetTarget(entity, label) | Routes the unit to a moving entity |
overrides shape the person's record and behaviour:
{
flags = { wanted = true, armed = true },
bac = 0.12, drugs = true,
weapon = true, weaponLabel = 'Knife',
items = { 'Stolen watch' },
flee = true, hostile = true, compliant = true,
condition = 'fracture', -- for patients (config_medical.lua)
description = 'a man in a red cap', -- answer to "Can you describe the suspect?"
}Tasks and checks
| Method | |
|---|---|
ctx:Task(id, condition) | Ticks a task when condition is true |
ctx:CompleteTask(id) | Ticks a task now |
ctx:IsTaskDone(id) / ctx:AllTasksDone() | |
ctx:SetTaskOptional(id, optional) | Makes a task optional or required |
ctx:SuspectsHandled() | Every suspect arrested, released or gone |
ctx:VictimsHandled() | Every victim dealt with |
ctx:VictimsCaredFor() | Every patient treated, transported or handed to EMS |
ctx:AnySuspect(fn) / ctx:AnyVictim(fn) / ctx:AllVictims(fn) | fn(ped, state) checks, e.g. state.idChecked, state.cuffed, state.treated |
ctx:SceneResolved() | Nothing left to do on scene |
ctx:Elapsed() | Seconds since the callout was accepted |
Scene actions
-- "press E here" with a progress bar
ctx:AddAction('valve', { at = coords, label = 'action_shut_gas', time = 6000, run = function(ctx) end })
ctx:IsActionDone('valve')
-- a choice menu instead of a progress bar
ctx:AddAction('agent', { at = coords, label = 'action_mitigate',
choices = function(ctx) return { ... } end,
choose = function(ctx, id) return 'done' end }) -- nil | 'done' | 'close'Investigations
| Method | |
|---|---|
ctx:AddLead(id, text) / ctx:HasLead(id) / ctx:LeadCount() | Leads listed on the HUD |
ctx:AddSearchArea(center, radius) | A yellow search circle (never centred exactly on the target) |
ctx:StartTracker(entity, { interval, min, max, label }) / ctx:StopTracker() | GPS or phone pings |
AddTalkDown(ctx, id, { at = ped, profile = Config.Negotiation.barricade, onSuccess = fn, onFail = fn }) | A negotiation with a trust meter |
Fires and patients
| Method | |
|---|---|
ctx:AddFire(coords, children) | A fire with spread points |
ctx:FiresOut(radius) | True when every fire in range is out |
ctx:SetPatientCondition(ped, conditionId) | The patient deteriorates (assisting EMS sees it too) |
HUD
| Method | |
|---|---|
ctx:SetLabel(text) | Renames the call, e.g. when it escalates |
ctx:SetObjective(text) | The objective line |
ctx:AddBlip(entity, sprite, colour, label, scale) | A blip, removed when the callout ends |
Fixed locations
By default a callout is placed on a road 350 to 1100 m from the unit. To use fixed spots instead, add locations to the list entry:
{ id = 'bank_alarm', service = 'police', ..., locations = { vec4(150.2, -1040.3, 29.4, 340.0), vec4(-1212.9, -330.6, 37.8, 25.0) } },Or name a Config table, like the gas station fire does: locations = 'FuelStations'.
Joint callouts
Add assist = { 'ambulance', 'tow' } to the list entry. Assisting units get the default tasks for their service. To give them your own:
RegisterCallout('my_scene', {
-- ...
assist = {
ambulance = {
tasks = { { id = 'treat', label = 'task_stabilise_all', final = true } },
update = function(ctx) ctx:Task('treat', ctx:VictimsCaredFor()) end,
},
police = 'cordon', -- police only close the road
},
})Everything you register with AddSuspect, AddVictim, AddCivilian and FlagVehicle is shared with assisting units, and the approached, idChecked, treated, arrested, released and delivered flags are synced.
The built-in scenarios in client/callouts/ are escrowed, but custom_callouts.lua has the full list of helpers in its header comment.