Los Santos AliveDeveloper API LosSantosAlive.API 1.0 lossantosalive.com

Los Santos Alive

Developer API

Give your RAGE Plugin Hook plugin a voice in Los Santos Alive. Create people who live in the world and on the phone, teach them actions, tell them what they know, make them speak and act, and hear what was said.

LSA turns every pedestrian into someone the player can talk to by voice. The API lets callouts and other plugins take part in those conversations. It's one small assembly with one class, LsaApi, and no setup: the first call connects to LSA, and your plugin disconnects cleanly when it unloads.

Using an AI assistant? The whole API is in one file written for it: llms.txt. Copy it into Claude, ChatGPT or Copilot and ask for the plugin you want.

Getting started

Installation

  1. Install LSA. The installer puts LosSantosAlive.API.dll in the GTA V folder, next to GTA5.exe.
  2. In your plugin project (.NET Framework 4.8, x64), reference RagePluginHook.dll and LosSantosAlive.API.dll. Set the LSA reference to Copy Local: false.
  3. Add using LosSantosAlive.API;. That's the only namespace you need. Every member has tooltips in Visual Studio.
Don't ship the DLL

Every LSA install already has LosSantosAlive.API.dll. Don't include it with your plugin, and never reference LosSantosAlive.dll itself.

Optional dependency

Players without LSA won't have the API DLL. If LSA is optional for your plugin, keep every API call in its own class and only use it after checking that the file exists, so your plugin still loads without it.

Getting started

Quick start

A robbery callout: the suspect knows what they did and can hand over the cash, and a witness calls the player later with what she remembers.

using LosSantosAlive.API;
using Rage;

public static class RobberyCallout
{
    public static void Start(Ped suspect)
    {
        // What the suspect knows, in every conversation from now on.
        LsaApi.SetBackground(suspect, "crime",
            "You just robbed the 24/7 on Innocence Blvd. The cash is in your jacket. You don't want to be searched.");

        // Something only this suspect can do.
        LsaApi.DefineAction("HandOverCash", "Hand the stolen cash to the officer if you decide to come clean.",
            ctx => { Game.DisplayNotification("The suspect handed over the cash."); return true; },
            audience: LsaAudience.ChosenPeds);
        LsaApi.GiveAction(suspect, "HandOverCash");

        // Have them open the conversation.
        LsaApi.Say(suspect, "A police officer just walked up to you outside the store.");
    }

    public static void WitnessSeen(Ped bystander)
    {
        // A person who outlives this ped: same voice and memory on the phone later.
        LsaPerson maria = LsaApi.Person("witness.maria");
        maria.Name = "Maria";
        maria.Ped = bystander;
        maria.HasNumber = true;
        LsaApi.SetBackground(maria, "saw", "You saw the robbery from your balcony.");
    }

    public static void WitnessRemembers()
    {
        LsaPerson maria = LsaApi.Person("witness.maria");
        LsaApi.Tell(maria, "You just remembered the getaway car's plate: 46EEK572.");
        LsaApi.Call(maria, "Tell the officer the plate you remembered.");
    }
}

That's the whole integration. There's nothing to initialize or shut down.

People

People

An LsaPerson is someone in Los Santos. They can have a body (a ped), a phone number, or both, and they're the same person everywhere: the same voice, what they know, what they've said and the actions you gave them. Meet Maria in the street, and when she calls later she remembers the conversation and sounds the same.

Every ped is already a person. Anything in the API that takes an LsaPerson also takes a Ped, so LsaApi.Say(ped, …) just works. You only create a person by id when they should outlive their ped or live on the phone.

LsaApi.Person method#

LsaPerson Person(string id)   // your person, created the first time
LsaPerson Person(Ped ped)     // the person a ped is
void Forget(LsaPerson person)
LsaPersonDescription
IdYour id for them, such as "witness.maria".
NameThey know it, and it's what the phone shows.
PedTheir body right now, or null. Set it to put them in the world or give them a new ped; they keep their voice, memory and actions.
HasNumberIn the player's phonebook, so the player can call them. The person answers as themselves.
GenderHow they sound if they've never had a ped.
IsPlayerTrue for the player.
LsaPerson tipster = LsaApi.Person("callout.tipster");   // phone only, no ped
tipster.Name = "Dex";
tipster.Gender = LsaGender.Male;
tipster.HasNumber = true;
LsaApi.SetBackground(tipster, "job", "You sell information to the police. You know who runs the chop shop on Strawberry Ave.");

Forget removes a person, their phone contact and everything you set for them. Your people all go when your plugin unloads.

Calls and texts method#

LsaRequest Call(LsaPerson person, string reason)
bool Text(LsaPerson person, string message)

Call rings the player. Describe why they're calling; they say it in their own words. The request finishes when the call ends: Succeeded if the player answered, otherwise FailureReason is "declined", "missed", "busy" or "phone_unavailable". If the player doesn't pick up, the person decides what to do, such as leaving a voicemail or calling back.

LsaApi.Call(maria, "You remembered the getaway car's plate.")
      .OnDone(call =>
      {
          if (!call.Succeeded)
              LsaApi.Text(maria, "call me back, I remembered something");
      });
The player calls them too

With HasNumber set, the player can dial your person any time and they answer as themselves, with everything they know. What's said reaches LineSpoken like any other conversation.

NPC actions

Defining actions

An action is something an NPC can decide to do while talking. You describe when it fits; LSA offers it to the NPC, and when the NPC chooses it, LSA calls your code.

DefineAction method#

bool DefineAction(string name, string description, Func<LsaActionContext, bool> run,
                  LsaActionTarget target = LsaActionTarget.None,
                  LsaAudience audience = LsaAudience.Everyone)
ParameterDescription
nameOne word, as NPCs will write it: "HandOverKeys".
descriptionWhen an NPC should do it, in plain words.
runDoes the action, on a GameFiber. Return false if it couldn't.
targetWhat the NPC names with it: nothing, a Person nearby, or a free-text Detail.
audienceWhich NPCs can choose it. See Audience.
LsaApi.DefineAction("Frisk", "Pat down someone you suspect is carrying a weapon.",
    ctx => MyPlugin.Frisk(ctx.Person.Ped, ctx.Target.Ped),
    target: LsaActionTarget.Person,
    audience: LsaAudience.Police);
Writing the description

Say when the action fits, not just what it does. "Hand over your car keys when an officer asks for them" works better than "Gives keys".

Defining an action with the same name again replaces it. RemoveAction(name) takes it away. By default an NPC in a vehicle gets out before your action starts; set CanStartInVehicle on an LsaAction to skip that.

Audience enum#

LsaAudienceWho can choose the action
EveryoneEvery NPC. The default.
PolicePolice officers.
CiviliansEveryone except police officers.
ChosenPedsOnly peds you give it to.
bool GiveAction(LsaPerson person, string name)
bool RevokeAction(LsaPerson person, string name)

GiveAction lets one person choose an action, on top of its audience. It's how you give a single suspect, hostage taker or witness something nobody else can do. It belongs to the person, so it follows them to any ped you give them later.

Lasting actions LsaAction#

For something that goes on (dancing, guarding a door), define an LsaAction with Start, Update and Stop instead of Run. Update runs every tick until the NPC chooses something else, LSA interrupts it, or you call StopAction. ctx.StopReason says why it ended.

LsaApi.DefineAction(new LsaAction
{
    Name = "GuardDoor",
    Description = "Stand at the club entrance and keep people out until told otherwise.",
    Audience = LsaAudience.ChosenPeds,
    Start = ctx => { ctx.Person.Ped.Tasks.StandStill(-1); return true; },
    Update = ctx => { /* face anyone who comes close */ },
    Stop = ctx => ctx.Person.Ped.Tasks.Clear()
});

LsaApi.GiveAction(bouncer, "GuardDoor");
// ...
LsaApi.StopAction(bouncer, "GuardDoor");

LsaActionContext class#

What your code gets when an NPC chooses your action.

PropertyDescription
PersonWho's doing it. Their body is Person.Ped.
TargetThe person they named, for Person actions.
DetailThe text it wrote, for Detail actions.
VehicleThe vehicle involved, if any.
ActionNameYour action's name.
StopReasonIn Stop: why a lasting action ended.

Directing NPCs

DoAction

LsaRequest DoAction(LsaPerson person, string action, LsaPerson target = null, Vehicle vehicle = null, string detail = "")

Makes a person (in the world) do one of LSA's own actions, the same ones NPCs choose in conversation. LSA handles the details: walking over, facing, getting out of a car first. Use the names on LsaActions, listed under Built-in actions.

LsaApi.DoAction(witness, LsaActions.Follow, target: Game.LocalPlayer.Character);
LsaApi.DoAction(suspect, LsaActions.WalkTo, detail: "the bus stop");
LsaApi.DoAction(driver, LsaActions.EnterBackSeat, vehicle: patrolCar);
Held peds

LSA won't move someone you hold for movement. Release them first if you want LSA to move them.

Directing NPCs

Say

LsaRequest Say(LsaPerson speaker, string situation, LsaPerson to = null, LsaSayOptions options = null)

Makes someone in the world speak without the player talking first (for the phone, use Call). Describe the moment; the NPC decides what to say, in its own voice. It speaks to the player unless you pass to. If it's already talking, it finishes its line first.

// To the player.
LsaApi.Say(suspect, "The officer just found the cash in your jacket.");

// To another NPC, who answers once.
LsaApi.Say(partner, "Ask the driver where they were headed.", to: driver,
    options: new LsaSayOptions { MaxReplies = 1 });
LsaSayOptionsDescription
InterruptCut off what the speaker is saying now instead of waiting.
MaxRepliesSpeaking to an NPC: how many times they may answer. 0 = none; the default lets LSA decide.
DelayMsWait this long before speaking.

Directing NPCs

LsaRequest

DoAction, Say and Call return an LsaRequest. Use it in whichever way suits your code:

// 1. Fire and forget.
LsaApi.Say(suspect, "You hear sirens getting closer.");

// 2. Wait for it on your GameFiber.
if (LsaApi.DoAction(suspect, LsaActions.PutHandsUp).Wait())
    Game.DisplayNotification("Suspect complied.");

// 3. Be called back.
LsaApi.Say(suspect, "The officer asks for your ID.")
      .OnDone(r => Game.LogTrivial("Said it: " + r.Succeeded));
MemberDescription
Wait(timeoutMs = 30000)Waits on the current GameFiber, then returns Succeeded.
OnDone(callback)Calls back once done, on a GameFiber.
IsDone, SucceededWhere it stands.
FailureReasonWhy not, when it failed: "no_ped", "declined", "not_connected"…
TurnIdFor Say: matches LsaLine.TurnId of what was said.

Directing NPCs

What NPCs know

LSA already tells each NPC about the world around it. Add what only your plugin knows: a suspect's crime, a witness's memory, a victim's injuries. Write it to the NPC, in plain words.

SetBackground method#

bool SetBackground(LsaPerson person, string key, string text)
bool ClearBackground(LsaPerson person)

Something the person knows from now on, in every conversation, face to face and on the phone. Setting the same key again replaces it, and an empty text removes it. It reaches a conversation that's already under way at the NPC's next line.

LsaApi.SetBackground(witness, "saw", "You saw a red sedan speed off toward the docks a minute before the shots.");

Tell method#

bool Tell(LsaPerson person, string text)

Something that just happened, which the person takes into account once, from their next line or call.

LsaApi.Tell(suspect, "Your phone just buzzed with a text from your boss: 'Say nothing.'");
Describe, don't script

Give the NPC facts and let it decide how to act. "You're carrying a stolen pistol" makes for a better conversation than "Lie about the pistol".

Directing NPCs

Hold

bool Hold(LsaPerson person, LsaHold hold = LsaHold.All)
bool Release(LsaPerson person)

LSA moves the NPCs it talks to: they stop and face the player, follow, flee, react to gunfire. If your plugin is running a scripted scene with someone, hold them, and LSA leaves them alone until you release them. The hold follows the person to any ped you give them.

LsaHoldWhat LSA stops doing
MovementMoving, tasking or animating the ped, or changing its persistence. Other NPCs can't seat, search or arrest it either.
ConversationLetting anyone start a conversation with it. The talk key picks someone else, and other NPCs can't address it. Your own Say still works.
AllBoth. The default.
LsaApi.Hold(gunman);                     // the standoff: hands off completely
LsaApi.Hold(gunman, LsaHold.Movement);   // negotiation: the player can talk to him
LsaApi.Release(gunman);                  // scene over
What stays in place

Holding a ped stops what LSA was doing with it but doesn't clear its GTA tasks, so a scene you already set up keeps running. Holds end when your plugin unloads.

Listening

Dialogue events

Find out what was said and what NPCs decided to do, so your plugin can react to a confession, a description of the getaway car, or a refusal to step out. Both events are raised on a GameFiber, so you can call game functions from the handler.

LineSpoken event#

event Action<LsaLine> LineSpoken

Every line said aloud: the player's words, NPC replies, NPC-to-NPC talk, your own Say and phone calls. An NPC's line arrives once it has played. Speakers are people, so one handler covers face to face and the phone.

LsaLineDescription
TextThe words.
SpeakerWho said it, as an LsaPerson: the player (Speaker.IsPlayer), an NPC, or someone on the phone.
AddresseeWho it was said to, if anyone.
IsPhoneCallTrue when either side is on the phone.
TurnIdThe player's line and the reply to it share one.

NpcActionChosen event#

event Action<LsaChosenAction> NpcActionChosen

An action an NPC chose in its reply, as soon as LSA gets it. It's raised even if LSA can't carry the action out, so treat it as the NPC's intent.

LsaChosenActionDescription
PersonWho chose it.
ActionNameThe action, such as FleeFrom or one of yours.
Target, DetailThe person it's aimed at, and its free-text part.
TurnIdThe reply it came from.
LsaApi.NpcActionChosen += chosen =>
{
    if (chosen.Person == suspect && chosen.ActionName.StartsWith("Flee"))
        MyCallout.StartPursuit(suspect);
};

IsSpeaking method#

bool IsSpeaking(LsaPerson person)

True while the person's voice is playing in the world. Handy for waiting until a line is over before a cutscene.

Listening

Answering for LSA

If your plugin keeps its own records, such as an MDT, a warrant database or a callout's suspects, LSA can ask you before it needs them. You answer, and the NPC and Dispatch say what your records say, so the world stays consistent with your plugin.

PersonNoticed event#

event Action<LsaNoticedPerson> PersonNoticed
bool SupportsPersonNoticed

LSA is about to need someone's context: the player approaches, aims at or talks to them, or they were just spawned for a callout. Answer with SetBackground and it reaches their first line. It's raised at most every 30 seconds per person.

LsaNoticedPersonDescription
PersonWho was noticed, with a ped.
Reason"nearby", "conversation", "talking" or "callout".
WantedReasonWhat an LSA callout suspect is wanted for, or empty. Records you show should agree with it.
LsaApi.PersonNoticed += noticed =>
{
    MdtRecord record = Mdt.Lookup(noticed.Person.Ped);
    if (record != null && record.HasWarrant)
        LsaApi.SetBackground(noticed.Person, "record", "You have an active warrant for " + record.WarrantFor + ".");
};
Older LSA installs

SupportsPersonNoticed is false when the player's LSA doesn't raise it. Set background another way then, such as when your plugin first deals with the person.

DispatchCheck event#

event Action<LsaDispatchCheck> DispatchCheck
bool SupportsDispatchChecks

Dispatch ran a person or plate check, for the player or an AI officer. Call check.Answer(readout) with what your records say and Dispatch reads that back instead of its own readout, or check.Decline() to leave it. LSA waits about 1.5 seconds for an answer, then uses its own.

LsaDispatchCheckDescription
Kind"person" or "vehicle".
PersonFor person checks: who was run, with a ped.
VehicleFor vehicle checks: the vehicle.
SubjectThe name or plate that was run.
WantedReasonAn LSA callout suspect's, or empty. A warrant you report should agree with it.
ReadoutWhat Dispatch reads back if nobody answers.
Answer(readout)Dispatch reads your readout instead.
Decline()Dispatch reads its own readout without waiting any longer.
LsaApi.DispatchCheck += check =>
{
    if (check.Kind != "person") { check.Decline(); return; }
    MdtRecord record = Mdt.Lookup(check.Person.Ped);
    if (record == null) { check.Decline(); return; }
    check.Answer(record.HasWarrant
        ? record.Name + ", active warrant for " + record.WarrantFor + "."
        : record.Name + ", valid license, no wants or warrants.");
};
Writing the readout

Write plain sentences a dispatcher reads aloud. Say "active warrant" for a person with one, and Dispatch asks the unit if they're clear to copy.

Older LSA installs

SupportsDispatchChecks is false when the player's LSA doesn't raise it. Dispatch reads its own readout.

Reference

Connection

The API connects to LSA in the background and reconnects on its own if LSA reloads. Your people, actions, background and holds are kept and sent again every time it connects, so set them up whenever suits you. One-off calls (DoAction, Say, Call) need LSA to be running; check IsAvailable when it matters.

Older LSA installs

If a player's LSA is older than the API your plugin was built with, IsAvailable is false, requests fail with "lsa_too_old", and the player gets one notice to update LSA. You don't need to handle it yourself.

MemberDescription
IsAvailableTrue while connected to a running LSA that supports this API.
Initialize(pluginName)Optional. The name LSA files your plugin under; defaults to your plugin's name. Call it first if you use it.
Shutdown()Optional. Disconnects now; LSA removes your actions, background and holds. Happens on its own when your plugin unloads.

Reference

Built-in actions

Names for DoAction, as constants on LsaActions.

Movement and people LsaActions

FollowApproachWalkAwayFromStopAndFaceFleeFromWalkBackwardsToTurnAwayFromWaitHereWalkToStopFollowingResumeActivityPerformActivityPerformActivityWithItem

Weapons and posture LsaActions

AttackFireOnceAtAimAtEquipWeaponUnequipWeaponTakeWeaponFromGiveWeaponToPutHandsUpLowerHandsKneelStandUpSitOnGround

Items and documents LsaActions

GiveItemToTakeItemFromUseHeldItemDiscardHeldItemGiveIdToGiveWeaponPermitToGiveFishingPermitToGiveHuntingPermitToGiveVehicleDocumentsToGiveAllDocumentsTo

Vehicles LsaActions

DriveToDriveEvasivelyStopDrivingEvasivelyBeginDrivingExitVehicleEnterPassengerSeatEnterBackSeatEnterDriverSeatFollowVehicleOpenVehicleCargoLeanAgainstVehicleTurnOffEngine

Police interactions LsaActions

SubmitToSearchSubmitToBreathalyzerSubmitToDrugSwabPerformHorizontalGazeTestPerformWalkAndTurnTestPerformOneLegStandTestSubmitToArrestSubmitToTintTestAcceptCitationPlaceTrafficConesRequestBackupRequestPersonCheckRequestVehicleCheckPullOverApproachStoppedDriverArrestSeatSearchSearchVehicleTestEvidenceInitiateFelonyStop
Policing Redefined

The police interaction actions need Policing Redefined installed.

Reference

Everything at a glance

The whole API. Everything is on LsaApi, and every LsaPerson parameter also takes a Ped.

// People
LsaPerson Person(string id)          // .Name .Ped .HasNumber .Gender .IsPlayer
LsaPerson Person(Ped ped)
void Forget(LsaPerson person)
LsaRequest Call(LsaPerson person, string reason)
bool Text(LsaPerson person, string message)

// NPC actions
bool DefineAction(string name, string description, Func<LsaActionContext, bool> run,
                  LsaActionTarget target = None, LsaAudience audience = Everyone)
bool DefineAction(LsaAction action)
bool RemoveAction(string name)
bool GiveAction(LsaPerson person, string name)
bool RevokeAction(LsaPerson person, string name)
bool StopAction(LsaPerson person, string name)

// Directing people
LsaRequest DoAction(LsaPerson person, string action, LsaPerson target = null, Vehicle vehicle = null, string detail = "")
LsaRequest Say(LsaPerson speaker, string situation, LsaPerson to = null, LsaSayOptions options = null)
bool SetBackground(LsaPerson person, string key, string text)
bool ClearBackground(LsaPerson person)
bool Tell(LsaPerson person, string text)
bool Hold(LsaPerson person, LsaHold hold = All)
bool Release(LsaPerson person)

// Listening
event Action<LsaLine> LineSpoken
event Action<LsaChosenAction> NpcActionChosen
bool IsSpeaking(LsaPerson person)

// Answering for LSA
event Action<LsaNoticedPerson> PersonNoticed
event Action<LsaDispatchCheck> DispatchCheck   // check.Answer(readout) / check.Decline()
bool SupportsPersonNoticed
bool SupportsDispatchChecks

// Connection
bool IsAvailable
void Initialize(string pluginName)   // optional
void Shutdown()                      // optional