Skip to content

CODE3RESPONSE

Callouts SDK

A callout is one Lua file in c3r/callouts/. It says what the caller reported, where it can happen, and what really happened: a few hidden stories, one picked at random each time. Copy the example, change it, restart c3r, and it's in rotation.

How a callout works

  • The report is what dispatch reads out. Every story shares the same reports, so the report never gives the story away.
  • Locations are where it can happen: a street name, a scene point, and the exact spot for each person, car and prop.
  • Stories are what really happened. Each story's setup puts people, cars and evidence on scene, writes the objectives, and says what happens when officers do things.
  • People are real. Everyone you add gets a hidden truth like any driver: name checks find them, searches find their items, and they answer the usual questions plus their own.
  • Several units at once. Objectives are split between the units on scene, so a team works it faster.

Your first callout

1. Copy the example

c3r/callouts/_example.lua is a complete, fully commented callout (a suspicious person by parked cars, with two stories). Copy it to a new file, e.g. callouts/loitering.lua, and change its id and name.

2. Write the reports

Callout({
    id = 'loitering',
    name = 'Suspicious person',
    priority = 3,          -- 1 most urgent
    enabled = true,
    reports = {
        'Caller reports someone hanging around the parked cars, looking in the windows.',
        'Suspicious male by the parked cars. Caller is watching from across the road.',
    },

3. Name the points

points lists every spot a location needs, in the order /c3rplace walks you through them. A third value of 'car' means "sit in a car parked where it should be".

    points = {
        { 'caller', 'The person who rang it in, waiting nearby on their phone.' },
        { 'subject', 'The person they called about.' },
        { 'parked_car', 'The car he was at. Sit in a car parked where it should be.', 'car' },
    },
    locations = {},

4. Place it in game

Stand at the scene and run /c3rplace loitering. It walks you through each point: go to the spot, face the way they should face, press E. Backspace goes back a point, X cancels. When you're done, the location is written into your file's locations and used straight away.

Run it again for more locations. /c3rplace loitering 2 replaces location 2. You need the command.c3rconfig ace.

To place points by hand instead, /c3rpos caller copies caller = vector4(x, y, z, heading), to your clipboard.

5. Write the stories

    stories = {
        thief = {
            weight = 2, -- twice as likely as a weight-1 story
            setup = function(c)
                c:person('caller', { at = 'caller', label = 'Caller', scene = 'WORLD_HUMAN_STAND_MOBILE',
                    questions = { { id = 'c_what', label = 'What did you see?' } },
                    lines = { c_what = { { 'He went car to car, pulling the handles.', set = { c_saw = 'doors' } } } } })
                c:person('subject', { at = 'subject', label = 'Person', secret = 'warrant', demeanor = 'nervous',
                    items = { 'Slim jim' } })

                c:objective('caller', 'Talk to the caller', { blip = 'caller' })
                c:objective('find', 'Find the person they called about', { blip = 'subject' })
                c:objective('deal', 'Deal with them', { after = { 'find' }, who = 'all' })

                c:on('act', function(who, action, arg)
                    if who == 'caller' and action == 'ask' and arg == 'c_what' then c:done('caller') end
                end)
                c:on('contact', function(who) if who == 'subject' then c:done('find') end end)
                c:on('custody', function() c:finish('Suspect arrested for vehicle tampering.') end)
            end,
        },
        lockout = { weight = 1, setup = function(c) --[[ he locked himself out ]] end },
    },
})

6. Test it

Restart c3r, go on duty, and run /requestcall loitering. It puts your call out near you now.

Rules that keep a callout good

  • Same reports, same starting objectives, for every story. Story-specific objectives start hidden and are revealed by something found. Otherwise players learn to read the card instead of doing police work.
  • Start with objectives that can be done at once, so a second unit has real work.
  • Every objective needs something in c:on(...) that marks it done.
  • An error in your file cancels that one call and prints why in the server console. Nothing else breaks.
  • Check scenario, animation and prop names against the game files. A wrong one just does nothing.

Reference

Callout fields

FieldTypeWhat it is
idstringUnique id
namestringShown on the offer card
prioritynumber1 is the most urgent
enabledbooleanfalse keeps it as a template only
reportsstring[]What the caller said; one is picked
points{ name, description, 'car'? }[]The spots each location needs
locations{ street, at = vector3, points = { [name] = vector4 } }[]Where it can happen
stories{ [key] = { weight, setup = function(c) } }What really happened

People, cars and evidence

c:person(key, opts) Someone in the story, made when the first unit gets close.

OptionTypeWhat it does
atstring | vector3 | vector4A point name, or a position
labelstringWhat the officer sees before they know a name
model, malestring, booleanPed model, or just the sex
secretstringTheir hidden truth: 'clean', 'nervous', 'drunk', 'drugs', 'warrant'...
demeanorstring'polite', 'nervous', 'rude' or 'chatty'
flags, warrantstring[], stringWhat their record shows
itemsstring[]What a search finds that matters (shown as evidence)
pocketsstring[]Everyday things a search finds that aren't evidence
age{ min, max }How old their ID says they are
hometrue | stringThey live at the call's address, or on the street of a point
injuredbooleanOn the ground hurt: E is first aid, paramedics take them
scenestring | { dict, name, flag }What they're doing: a scenario or an animation
questions{ id, label, needs?, hide?, press?, stress? }[]Their own questions under The call. Ids start c_
lines{ [question id] = lines }Their answers. A line can give a fact away: { 'He went north.', set = { c_dir = 'north' } }

lines can also replace their answer to any normal question (e.g. lines.where), and a line can be a function that returns the list, decided when it's asked. needs = { c_fact = value } shows a question only once they've said it; press = true gives a fresh answer each time it's asked; stress = true lets nerves show.

c:vehicle(key, opts) A parked car, on file.

OptionTypeWhat it does
atstring | vector4A point name, or a position
modelstringSpawn name, e.g. 'asea'
make, name, colorstringWhat the plate check says: 'Declasse', 'Asea', 'Silver'
paintnumberGTA colour id
ownerstringA person key it's registered to (else someone else)
stolenbooleanComes back stolen
lockedbooleanDefaults to true
damage'front'The front end smashed in

c:clue(key, opts) Evidence. Up close, "E Inspect: label" shows text and puts it on every unit's MDT.

OptionTypeWhat it does
at / onstringA point, or a vehicle key to put it on that car
label, textstringWhat it's called, and what you find
propstringAn object laid on the ground there; it glows until looked at
hiddenbooleanNot there until c:reveal(key)

Objectives

c:objective(id, text, opts) A line on the objective card.

OptionWhat it does
after = { ids }Must be done first
who = 'one' | 'all'One unit takes it (default), or it's on everyone's card
hintSmaller text under it
hiddenOff the card until c:reveal(id)
blipA person, vehicle or clue key (or a list) to mark on the map of whoever has it

c:done(id) marks it done, c:reveal(id) shows a hidden one (and a clue with the same key), c:hide(id) takes it off again, c:is_done(id) checks.

Events

c:on(event, fn)

EventArgumentsWhen
arrivesrcA unit got there
contactperson, srcSomeone started talking to a person
actperson, action, arg, srcAnything done with them: 'ask' (arg = question id), 'licence', 'frisk', 'cuff', 'release', 'warn', 'cite', 'escort', 'seat'...
factperson, fact, value, srcSomething they gave away (a line's set)
contact_endedperson, outcome, src'released', 'arrested', 'cited', 'warning', 'cancelled'
inspectclue, srcEvidence looked at, the first time
ticksecondsOnce a second
gonepersonTaken away by a unit, or deleted
custodyperson, how, srcA prisoner taken in: 'booked' or 'transport'
aidperson, what, srcFirst aid ('vitals', 'pressure', 'cpr') or paramedics ('hospital', 'treated')

Helpers

CallDoes
c:custody(person)They're under arrest: "Take them in" goes on the card until they're booked or transported
c:talking()Whether an officer is talking to anyone on the call right now
c:questions(person)After giving them a fact from elsewhere: refreshes what the officer talking to them sees
c:who(person)Their record on the call: .facts, .cuffed, .identified...
c:name(person), c:plate(vehicle), c:street()Real name, plate, the call's street
c:owner(vehicle)Who the car's registered to: { first, last, address }
c:apart(a, b)Out of each other's earshot (10 m, or either cuffed or in a car)
c:task(person, task, point?)'go' (walk to a point), 'flee', 'surrender', 'wander', 'scene'
c:say(person, text)A line heard on scene
c:dispatch(text)A radio update to the units on it
c:note(text)A line on their MDT incident
c:set(k, v), c:get(k)The call's own memory
c:finish(summary)It's dealt with: completed, the summary on the MDT

Sharing

Made a good one? Share it on Discord. Callouts are yours to share; remember the licence: free to share, never to sell.