Skip to content
View in the app

A better way to browse. Learn more.

LCPDFR.com

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Guide to Creating a Callouts Plugin [LSPDFR+RPH]

Featured Replies

  • Popular Post

Hey there! Looking to create a callouts plugin of your own? Well, I'm here to help you.

So, let's get started. The requirements are as follows:

1) Have some general knowledge of C#, to learn the basics of C# I recommend reading Sections 1-17 here: http://rbwhitaker.wikidot.com/c-sharp-tutorials

2) Have Visual Studio Community Free, or any other version other than a trial, found here: https://www.visualstudio.com/en-us/visual-studio-homepage-vs.aspx

3) Have the files "RagePluginHookSDK.dll" and "LSPD First Response.dll", found in the downloads of RPH and LSPDFR.

 

Alright, once you have met those requirements, we can move onto the steps of starting your first callouts plugin!

1) Create a folder called "References" anywhere easily accessible to you.

     - In this folder, place your files "RagePluginHookSDK.dll" and "LSPD First Response.dll".

20e768855147e7fe0e49f2ec16ce282b.png

2) Open Visual Studio.

     - Once VS has fully loaded, at the top right click "File", "New", "Project".

     - At the left click the arrows next to "Installed", "Templates", and click on "Visual C#".

     - Then, in the middle box click "Class Library".

     - Under "Name" at the bottom, in the box type whatever you want your project/callouts plugin to be called.

     - Under "Location" choose where you want to save the project, this is recommended to be an easily accessible area.

     - Click "OK".

afb00499de7aa1ea6cd1c5af3c7503a9.png

3) When all loads in, you should get a screen of code like this:

Spoiler

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExamplePlugin
{
    public class Class1
    {
    }
}

 

 

4) First, click "Project" at the top of the screen, then "Add Reference..".

     - A window will pop with a bunch of gibber jabber on it. Click the arrow next to "Browse".

     - At the bottom left, click "Browse.."

     - Once in Windows Explorer, navigate to your folder "References".

    - Hold the left control key and select both the files in the folder References (Rage SDK and LSPD DLL).

    - Make sure both boxes for the two files are ticked. Then, click "OK".

d1738ff53387671ef319c4f2a7b809e3.png

5) At the top of your code where everything says "using blahblah;". Replace all of those with the following lines below:

Spoiler

using LSPD_First_Response;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rage;
using Rage.Native;
using LSPD_First_Response.Mod.API;
using LSPD_First_Response.Mod.Callouts;
using LSPD_First_Response.Engine.Scripting.Entities;
using ExamplePlugin;

 

These basically state what references you're using, simple as that.

6) Over to the right, rename Class1.cs to Main.cs.

7) Then, you need to replace the code below the using statements with the following (explanations in the code, after "//"):

Spoiler

public class Main : Plugin
{
    //Initialization of the plugin.
    public override void Initialize()
    {
        //This is saying that when our OnDuty status is changed, it calls for the code to call private static void OnOnDutyStateChangedHandler near the bottom of this page.
        Functions.OnOnDutyStateChanged += OnOnDutyStateChangedHandler;

        //Game.LogTrivial's are used to help you identify problems when debugging a crash with your plugin, so you know exactly what's going on when.

        //This will show in the RagePluginHook.log as "Example Plugin 1.0.0.0 has been initialised." 
        Game.LogTrivial("Example Plugin " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + " has been initialised.");

        //This one will show in RagePluginHook.log as "Go on duty to fully load Example Plugin."
        Game.LogTrivial("Go on duty to fully load Example Plugin.");
    }
    //This is a simple message saying that Example Plugin has been cleanup.
    public override void Finally()
    {
        Game.LogTrivial("Example Plugin has been cleaned up.");
    }
    //This is called by Functions.OnOnDutyStateChanged as stated above, but only when bool OnDuty is true.
    private static void OnOnDutyStateChangedHandler(bool OnDuty)
    {
        //We have to make sure they are actually on duty so the code can do its work, so we use an "if" statement.
        if (OnDuty)
        {
            //This calls for the method private static void RegisterCallouts() down below.
            RegisterCallouts();

            //This shows a notification at the bottom left, above the minimap, of your screen when you go on duty, stating exactly what's in the quotation marks.
            Game.DisplayNotification("Example Plugin v1.0 has loaded successfully, thank you for downloading!");
        }
    }
    //This is the method that we called earlier in private static void OnOnDutyStateChangedHandler. This registers the callouts we have it setup to register, we'll come back to this after we make our callout.
    private static void RegisterCallouts()
    {

    }
}

 

So this basically concludes our Main.cs file, all we have to do later on, as stated near the end of Main.cs above our method RegisterCallouts(), is register the callouts that we make, which we are about to do.

8) Next, we need to double click on "Properties" to the right. Under the dropdown box "Target framework:" select .NET Framework 4.6.

     - Then click Control+S and exit the Properties tab.

c6a7c66a3d4ae94e65c440bbaa66bc3a.png

9) Over to the right, right click on ExamplePlugin and click "Add" then "New Folder", naming it "Callouts".

     - Then, right click on the "Callouts" folder, click "Add" then "Class..".

     - Name it whatever your callout name is, for this example we're going to be creating a simple "Pursuit in Progress" callout, so name it "PursuitinProgress.cs".

1d0e7e5fdebe406765f8f7c216f6ad97.png

10) In it, use this code below:

Spoiler

using System;
using Rage;
using Rage.Native;
using LSPD_First_Response.Mod.Callouts;
using LSPD_First_Response.Mod.API;
using LSPD_First_Response.Engine.Scripting.Entities;

//Identify where your Callouts folder is.
namespace ExamplePlugin.Callouts
{
    //Name the callout, and set the probability.
    [CalloutInfo("PursuitinProgress", CalloutProbability.Low)]

    //Let PursuitinProgress inherit the Callout class.
    public class PursuitinProgress : Callout
    {
        public LHandle pursuit;
        public Vector3 SpawnPoint;
        public Blip myBlip;
        public Ped mySuspect;
        public Vehicle myVehicle;

        public override bool OnBeforeCalloutDisplayed()
        {
            //Get a valid spawnpoint for the callout.
            SpawnPoint = World.GetNextPositionOnStreet(Game.LocalPlayer.Character.Position.Around(300f));

            //Create a list of VehicleModels to get them randomly generated.
            Model[] VehicleModels = new Model[]
            {
                "NINFEF2", "BUS", "COACH", "AIRBUS", "AMBULANCE", "BARRACKS", "BARRACKS2", "BALLER", "BALLER2", "BANSHEE", "BJXL", "BENSON", "BOBCATXL", "BUCCANEER", "BUFFALO", "BUFFALO2", "BULLDOZER", "BULLET", "BURRITO", "BURRITO2", "BURRITO3", "BURRITO4", "BURRITO5", "CAVALCADE", "CAVALCADE2", "POLICET", "GBURRITO", "CAMPER", "CARBONIZZARE", "CHEETAH", "COMET2", "COGCABRIO", "COQUETTE", "GRESLEY", "DUNE2", "HOTKNIFE", "DUBSTA", "DUBSTA2", "DUMP", "DOMINATOR", "EMPEROR", "EMPEROR2", "EMPEROR3", "ENTITYXF", "EXEMPLAR", "ELEGY2", "F620", "FBI", "FBI2", "FELON", "FELON2", "FELTZER2", "FIRETRUK", "FQ2", "FUGITIVE", "FUTO", "GRANGER", "GAUNTLET", "HABANERO", "INFERNUS", "INTRUDER", "JACKAL", "JOURNEY", "JB700", "KHAMELION", "LANDSTALKER", "MESA", "MESA2", "MESA3", "MIXER", "MINIVAN", "MIXER2", "MULE", "MULE2", "ORACLE", "ORACLE2", "MONROE", "PATRIOT", "PBUS", "PACKER", "PENUMBRA", "PEYOTE", "POLICE", "POLICE2", "POLICE3", "POLICE4", "PHANTOM", "PHOENIX", "PICADOR", "POUNDER", "PRANGER", "PRIMO", "RANCHERXL", "RANCHERXL2", "RAPIDGT", "RAPIDGT2", "RENTALBUS", "RUINER", "RIOT", "RIPLEY", "SABREGT", "SADLER", "SADLER2", "SANDKING", "SANDKING2", "SHERIFF", "SHERIFF2", "SPEEDO", "SPEEDO2", "STINGER", "STOCKADE", "STINGERGT", "SUPERD", "STRATUM", "SULTAN", "AKUMA", "PCJ", "FAGGIO2", "DAEMON", "BATI2"
            };

            //Choose a random vehicle model for myVehicle by using the models we listed above, and spawn it at SpawnPoint.
            myVehicle = new Vehicle(VehicleModels[new Random().Next(VehicleModels.Length)], SpawnPoint);

            //Set myVehicle as persistent, so it doesn't randomly disappear.
            myVehicle.IsPersistent = true;

            //Spawn mySuspect at SpawnPoint.
            mySuspect = new Ped(SpawnPoint);

            //Warp mySuspect into myVehicle, -1 represents the drivers seat.
            mySuspect.WarpIntoVehicle(myVehicle, -1);

            //Set mySuspect as persistent, so it doesn't randomly disappear.
            mySuspect.IsPersistent = true;

            //Block permanent events from mySuspect so they don't react weirdly to different things from GTA V.
            mySuspect.BlockPermanentEvents = true;

            //If for some reason, the spawning of mySuspect failed, don't display the callout.
            if (!mySuspect.Exists()) return false;

            //If the peds are valid, display the area that the callout is in.
            this.ShowCalloutAreaBlipBeforeAccepting(SpawnPoint, 15f);
            this.AddMinimumDistanceCheck(5f, SpawnPoint);

            //Set the callout message(displayed in the notification), and the position(also shown in the notification)
            this.CalloutMessage = "Pursuit in Progress";
            this.CalloutPosition = SpawnPoint;

            //Play the scanner audio using SpawnPoint to identify "POSITION" stated in "IN_OR_ON_POSITION". These audio files can be found in GTA V > LSPDFR > Police Scanner.
            Functions.PlayScannerAudioUsingPosition("OFFICERS_REPORT CRIME_RESIST_ARREST IN_OR_ON_POSITION", this.SpawnPoint);

            return base.OnBeforeCalloutDisplayed();
        }

        public override bool OnCalloutAccepted()
        {
            //Attach myBlip to mySuspect to show where they are.
            myBlip = mySuspect.AttachBlip();

            //Display a message to let the user know what to do.
            Game.DisplaySubtitle("Pursue the ~r~suspect~w~.", 7500);

            return base.OnCalloutAccepted();
        }

        public override void OnCalloutNotAccepted()
        {
            base.OnCalloutNotAccepted();

            //Clean up what we spawned earlier, since the player didn't accept the callout.

            //This states that if mySuspect exists, then we need to delete it.
            if (mySuspect.Exists()) mySuspect.Delete();

            //This states that if myBlip exists, then we need to delete it.
            if (myBlip.Exists()) myBlip.Delete();
        }

        public override void Process()
        {
            base.Process();
            {
                //This states that if the player is less than or equal to 100 meters away from SpawnPoint, then it will do whatever is in the brackets.
                if (Game.LocalPlayer.Character.Position.DistanceTo(SpawnPoint) <= 100f)
                {
                    //Create the pursuit.
                    this.pursuit = Functions.CreatePursuit();

                    //Add mySuspect to the pursuit.
                    Functions.AddPedToPursuit(this.pursuit, mySuspect);

                    //Request Backup for an air unit and one local unit to join into the pursuit.
                    Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.AirUnit);
                    Functions.RequestBackup(Game.LocalPlayer.Character.Position, LSPD_First_Response.EBackupResponseType.Pursuit, LSPD_First_Response.EBackupUnitType.LocalUnit);
                }
            }
        }

        public override void End()
        {
            //Dismiss mySuspect, so it can be deleted by the game once the player leaves the scene.
            //Delete myBlip, so it doesn't stick around on the minimap annoying the player.

            //This states that if mySuspect exists, then we need to dismiss it.
            if (mySuspect.Exists()) mySuspect.Dismiss();

            //Delete the blip attached to mySuspect.
            if (myBlip.Exists()) myBlip.Delete();

            base.End();
        }
    }
}

 

Everything is explained in the code to help you out. I recommend you experiment with it to change things to your liking.

11) Now, go to Main.cs and under the RegisterCallouts() method you need to add the following:

Spoiler

Functions.RegisterCallout(typeof(PursuitinProgress));

 

So, it would end up  being:


//This is the method that we called earlier in private static void OnOnDutyStateChangedHandler. This registers the callouts we have it setup to register, we'll come back to this after we make our callout.
    private static void RegisterCallouts()
    {
        Functions.RegisterCallout(typeof(PursuitinProgress));
    }

 

12) Lastly, double click "Properties" to the right. Then, click "Build" to the left. Under "Platform Target:" select x64.

     - Thank you @Stealth22 for informing me of this!

bfa5751dfb86b02056a96c554de6389a.png

Save and you're good to go! You got your first callout. You can ultimately expand upon this information I have provided with you to make your own unique callouts plugin!

     - To actually use the plugin, you have to click "Build" at the top of VS, click "Build Solution", make sure there are no errors, then go to wherever your project "ExamplePlugin" is located, go into bin > Debug. There you will find your references that were used and your "ExamplePlugin.dll". Drag and drop this file into GTA V > Plugins > LSPDFR and boot up GTA V! Also, if you want to quickly debug and make solving crashes easier, install "ExamplePlugin.pdb" to the same spot you installed ExamplePlugin.dll.

3b1ec651475b1dada33bf7e4e4ebd4c2.png

 

If you have any questions, please ask me!

If I forgot something or messed up something (as expected), please let me know.

Edited by ToastinYou

  • Replies 36
  • Views 46.9k
  • Created
  • Last Reply

Top Posters In This Topic

Most Popular Posts

  • Stealth22
    Stealth22

    For the MSIL warning, set your project's processor architecture from Any CPU to x64. 

  • There are no 32-bit players. GTA V needs 64-bit.

  • You can use a relative path so wherever GTA V is installed it'll be the same. You got to know that despite of the fact plugins are located in \Plugins they all work from the level of GTA root folder (

Posted Images

For the MSIL warning, set your project's processor architecture from Any CPU to x64. 

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

12 minutes ago, ToastinYou said:

I'll change that in a minute, thank you guys!

Did that get rid of the warning? Your platform still says any CPU. 

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

There should be an option to open Configuration Manager or whatever it's called. There you can add x64 as a platform. But if it fixed it, whatever. 

Edited by Stealth22

Stealth22
LSPDFR Tester | Plugin Developer
My Plugins: Code 3 Callouts | Traffic Control | Keep Calm | ALPR+

Please do not PM me for any kind of technical support.
I unfortunately do not have enough free time to answer every PM that I get. For issues with my plugins, please post in the comments section of the file, or it's forum thread. You'll get a much quicker response from me there than if you send me a PM; I do my best to respond to every question in the comments sections. For API/programming questions, please post them in the API Development forum, so all developers can benefit from the answer as well. Thanks!

4 hours ago, ToastinYou said:

Can't change Platform, no other options under it. So I changed Platform target to x64. That removed the warning.

x64.png

Might be worth adding a mention of the PDB file, which makes debugging when things go wrong a whole lot easier.

My YouTube Channel: Darkmyre Gaming (Australian LSPDFR patrols, plugins in development, and other games)

My Discord Server | AusGamer Network

 

Please do not PM me for technical support or bug reports, use the appropriate forum or plugin's comments instead.

  • The topic was pinned
  • The topic was unpinned
  • 2 weeks later...

This is a great guide. One thing however. The way you have your Process method setup, it is will continuously spawn pursuits and add the ped. this is game breaking and requires you to restart the game. To fix it I added a isPursuit bool, set it to false in OnBeforeCalloutAccepted then the first time the it creates the pursuit I set it to true, and add "&& !isPursuit" to the if statement. other than that, your guide is simple, and extremely appreciated!

Edited by nicedude80

The best LSPD officer in San Andreas.

  • 3 weeks later...

Hey I registered to leave this comment,

 

When i created the project I named it something else. Its worth noting in the documentation that, if the user names the project something else, they must change the references.

Main.cs, Line 11

PursuitinProgress.cs, Line 9

 

Additionally, I had to change Main Ln 11 to read as below for it to build correctly.

#using Projectname.Callouts;

 

I am not a native of C#, all my experience is with C++, so I may have fubar'd something along the way. Let me know if I did something wrong here.

 

Also, police IRL, so I am planning on making some realistic callouts! Thanks for the post!!:thumbsup:

  • 2 weeks later...

@Fenix2525WOT I'm not Toastin but it doesn't change your question xD

1) Tick - you might use 2 kind of events from RPH:

 a) Game.FrameRender - let you call native functions inside but might cause textures flickering,

 b) Game.RawFrameRender - doesn't let to call natives inside but does not produce rendering issues.

2) Detecting keys - there's no event like KeyPressed that you might know from ScriptHookDotNet or Community ScriptHook .NET, you need to use those functions to get status of given keys:

 - Game.IsKeyDown()

 - Game.IsKeyDownRightNow()

 - Game.GetKeyboardState()

there are also useful properties to get state of modifier keys, you'll find them here: http://docs.ragepluginhook.net/?topic=html/3F4C794D.htm

You can use a relative path so wherever GTA V is installed it'll be the same. You got to know that despite of the fact plugins are located in \Plugins they all work from the level of GTA root folder (RPH is there and load those libraries) = to save a file called "picture.jpg" you got to use this path:

string path = @".\Plugins\picture.jpg";

"@" sign is there to prevent interpreting >escape sequences< (google that) inside the string.

Guest
This topic is now closed to further replies.

Recently Browsing 0

  • No registered users viewing this page.

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.