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.

Creating an INI file?

Featured Replies

How do you go abouts with starting to create an INI file for a Callouts plugin? If someone could just inform me of how to start it out and where to put the code I can obviously code the inner parts of it, I just need to know where abouts to start it, if you need a new VS project for it, etc. I can't find any previous topics on how to do this so that's why I'm creating this one. Thank you for the help!

 

Don't know if you guys will need any code to help me, but here's my Main.cs:

using ToastyCallouts.Callouts;
using LSPD_First_Response.Mod.API;
using Rage;

public class Main : Plugin
{
    public override void Initialize()
    {
        Functions.OnOnDutyStateChanged += OnOnDutyStateChangedHandler;
        Game.DisplayNotification("Unlimited Callouts v1.3 successfully loaded, thank you for downloading!");
        Game.LogTrivial("Plugin Unlimited Callouts " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + " has been initialised.");
        Game.LogTrivial("Go on duty to fully load Unlimited Callouts.");
    }
    public override void Finally()
    {
        Game.LogTrivial("Unlimited Callouts has been cleaned up.");

    }
    private static void OnOnDutyStateChangedHandler(bool OnDuty)
    {
        if (OnDuty)
        {
            RegisterCallouts();
            Game.DisplayNotification("~r~Unlimited~w~ ~b~Callouts~w~ v1.3 has loaded successfully, thank you for downloading!");
        }
    }
    private static void RegisterCallouts()
    {
        /*Functions.RegisterCallout(typeof(RecklessDriver));
        Functions.RegisterCallout(typeof(GrandTheftAuto));
        Functions.RegisterCallout(typeof(PossibleProstitute));
        Functions.RegisterCallout(typeof(PrisonEscapee));
        Functions.RegisterCallout(typeof(PursuitinProgress));
        Functions.RegisterCallout(typeof(SuspiciousVehicle));
        Functions.RegisterCallout(typeof(PossibleMugging));*/
        Functions.RegisterCallout(typeof(NoiseComplaint));
    }
}  

 

  • Author
24 minutes ago, AlconH said:

Looks great, but let me ask you this: Can I do this exact same thing for a Callouts mod? As that looks like a RAGE only mod, while mine is LSPDFR. And, where does this go? Do I just create a new VS Project? Thanks! :)

What I usually do is I create a static class called Settings, with static fields for all of the settings I want with default values declared. Then I create a LoadSettings method which loads each setting (if possible) or sets the default otherwise. Makes it easy to keep all the settings in one place. 

[REL] Coastal Callouts: An action-packed mod with new vehicles, maps, capabilities, and callouts in and around the waters of Los Santos

[REL] Police Tape: Make your scenes more realistic while stopping peds and traffic

[REL] Better EMS: Realistic and dynamic EMS response

Join the Parks Benefactor Program to support my work and get early beta access!

  • Author

If in my INI File I'm trying to make an option so that you can set Play Music to true or false so if you don't want to hear the loud music it's false if you do then it's true, so you have the option.. how would I initialise that? The part where it plays the song is in NoiseComplaint.cs under folder Callouts while the ini file coding is in Main.cs under the project directory.

Here's my INI coding in Main.cs:

namespace UnlimitedCalloutsINI
{
    public class EntryPoint
    {
        /// <summary>
        /// In this method, we load up the .ini file so other methods can use it.
        /// </summary>
        /// <returns></returns>
        public static InitializationFile initialiseFile()
        {
            //InitializationFile is a Rage class.
            InitializationFile ini = new InitializationFile("Plugins/LSPDFR/UnlimitedCallouts.ini");
            ini.Create();
            return ini;
        }

        /// <summary>
        /// In this method, we load up the ini file with the method above and we read one of the values: in this case, a keybinding.
        /// </summary>
        /// <returns></returns>
        public static String getTalkKey()
        {
            InitializationFile ini = initialiseFile();

            //ReadString takes 3 parameters: the first is the category, the second is the name of the entry and the third is the default value should the user leave the field blank.
            //Take a look at the example .ini file to understand this better.
            string keyBinding = ini.ReadString("Keybindings", "TalkKey", "T");
            return keyBinding;
        }

        /// <summary>
        /// In this method, we read the player's name from the .ini file. If it is too long, we let the player know in a somewhat subtle way.
        /// </summary>
        /// <returns></returns>
        public static String getOfficerName()
        {
            //We use the first method we created
            InitializationFile ini = initialiseFile();

            //ReadString takes 3 parameters: the first is the category, the second is the name of the entry and the third is the default value should the user leave the field blank.
            //Take a look at the example .ini file to understand this better.
            string playerName = ini.ReadString("Settings", "OfficerName", "NoNameSet");

            //If the name has more than 12 characters
            if (playerName.Length > 12)
            {
                playerName = "NameTooLong";
            }
            return playerName;
        }

        public static String getPlay_Music_in_Noise_Complaint_Callout()
        {
            InitializationFile ini = initialiseFile();
            string PlayMusic = ini.ReadString("Settings", "Play_Music_in_Noise_Complaint_Callout", "True");
            return PlayMusic;
        }

        /// <summary>
        /// In the main method, we attempt to read all the information from the .ini file. To convert a string to a System.Windows.Forms.Keys, we use a KeyConverter.
        /// Do not forget to add a reference to System.Windows.Forms, which can be done via project> add reference> assemblies> framework.
        /// I also added using System.Windows.Keys; at the beginning of the project so we don't have to type that every time we use one of its methods.
        /// </summary>
        public static void Main()
        {
            //A keysconverter is used to convert a string to a key.
            KeysConverter kc = new KeysConverter();

            //We create two variables: one is a System.Windows.Keys, the other is a string.
            Keys TalkKey;
            string OfficerName;


            //Use a try/catch, because reading values from files is risky: we can never be sure what we're going to get and we don't want our plugin to crash.
            try
            {
                //We assign myKeyBinding the value of the string read by the method getMyKeyBinding(). We then use the kc.ConvertFromString method to convert this to a key.
                //If the string does not represent a valid key (see .ini file for a link) an exception is thrown. That's why we need a try/catch.
                TalkKey = (Keys)kc.ConvertFromString(getTalkKey());

                //For the playerName, we don't need to convert the value to a Key, so we can simply assign playerName to the return value of getPlayerName(). 
                //Remember we've already made sure the name can't be longer than 12 characters.
                OfficerName = getOfficerName();
            }
            //If there was an error reading the values, we set them to their defaults. We also let the user know via a notification.
            catch
            {
                TalkKey = Keys.T;
                OfficerName = "DefaultName";
                Game.DisplayNotification("There was an error reading the .ini file. Setting defaults...");
            }

            //Now you can do whatever you like with them! To finish off the example, we create a notification with our name when we press our keybinding.

            //We create a new GameFiber to listen for our key input. 
            GameFiber.StartNew(delegate
            {
                //This loop runs until it's broken
                while (true)
                {
                    //If our key has been pressed
                    if (Game.IsKeyDown(TalkKey))
                    {
                        //Create a notification displaying our name.
                        Game.DisplayNotification("Your name is: " + OfficerName + ".");
                        //And break out of the loop.
                        break;
                    }

                    //Let other GameFibers do their job by sleeping this one for a bit.
                    GameFiber.Yield();
                }
            });
        }
    }
}

Here's the part I want optional

GameFiber.Sleep(7500);
                ABlip.Delete();
                Suspect.Tasks.PlayAnimation(("amb@world_human_musician@guitar@male@base"), ("base"), 1, AnimationFlags.Loop);
                Game.LogTrivial("Created Animation.");
                propMethod();
                propGuitar.AttachTo(Suspect, Suspect.GetBoneIndex(PedBoneId.LeftPhHand), Vector3.Zero, Rotator.Zero);
                Game.LogTrivial("Created propGuitar.");
                Functions.PlayScannerAudioUsingPosition("PLAY_GUITAR_SONG", this.spawnPoint);
                Game.DisplaySubtitle("~r~Suspect~w~: Runnin.. by the riverrrr, babyyyyy!", 10000);
                Game.LogTrivial("Displayed Song Subtitle.");
                GameFiber.Sleep(5000);
                Game.DisplaySubtitle("~b~Officer~w~: Please, stop playing this shit right now!", 5000);
                GameFiber.Sleep(5000);
                Game.DisplaySubtitle("~y~Suspect~w~: Yes, sorry sir.", 5000);
                GameFiber.Sleep(1500);
                propGuitar.Detach();
                Suspect.Tasks.PlayAnimation(("move_action@p_m_zero@unarmed@idle@low_energy@a"), ("idle"), 1, AnimationFlags.Idle);
                GameFiber.Sleep(3000);
                Game.DisplayNotification("~b~Dispatch~w~: Take appropriate action, Officer.");
                GameFiber.Sleep(5000);
                Game.DisplayHelp("With a situation like this, it's recommended that you leave the suspect off with a warning, letting them know that you are serious.");

  

 It makes it a lot easier to keep things organized when you break out code for different features into different functions. I try to keep as little as possible in EntryPoint, for what it's worth. 

To elaborate on my suggestion from earlier: 

 Create a new file called Settings.cs. 

Within it, create a static class called Settings. Add all of the settings you'll want in your file as static fields: 

internal static class Settings 
{
	internal static bool PlayMusic = true;
	internal static bool DoSomeOtherThing = false;
	internal static Keys SomeHotKeyOrWhatever = Keys.D8;
	internal static string PlayerName = "Name Not Set"; 
} 

  Now add a static method called LoadSettings to load the settings: 

internal static class Settings 
{
	internal static bool PlayMusic = true;
	internal static bool DoSomeOtherThing = false;
	internal static Keys SomeHotKeyOrWhatever = Keys.D8;
	internal static string PlayerName = "Name Not Set"; 
  
	internal static void LoadSettings() 
    {
    	// initialize the INI file
      
        // read each setting and set it
      
        // For details on how to do all this, look at docs.ragepluginhook.net under the InitializationFile class
        // I don't have access to look it up myself at this moment but will update this example later. 
    }
} 

In your Initialize function, call Settings.LoadSettings(). Then, whenever you need to use a setting in your code, just call it like so: 

if(Settings.PlayMusic) 
{
	// Play the music   
}

Edited by PNWParksFan

[REL] Coastal Callouts: An action-packed mod with new vehicles, maps, capabilities, and callouts in and around the waters of Los Santos

[REL] Police Tape: Make your scenes more realistic while stopping peds and traffic

[REL] Better EMS: Realistic and dynamic EMS response

Join the Parks Benefactor Program to support my work and get early beta access!

  • Author
57 minutes ago, PNWParksFan said:

  

 It makes it a lot easier to keep things organized when you break out code for different features into different functions. I try to keep as little as possible in EntryPoint, for what it's worth. 

To elaborate on my suggestion from earlier: 

 Create a new file called Settings.cs. 

Within it, create a static class called Settings. Add all of the settings you'll want in your file as static fields: 


internal static class Settings 
{
	internal static bool PlayMusic = true;
	internal static bool DoSomeOtherThing = false;
	internal static Keys SomeHotKeyOrWhatever = Keys.D8;
	internal static string PlayerName = "Name Not Set"; 
} 

  Now add a static method called LoadSettings to load the settings: 


internal static class Settings 
{
	internal static bool PlayMusic = true;
	internal static bool DoSomeOtherThing = false;
	internal static Keys SomeHotKeyOrWhatever = Keys.D8;
	internal static string PlayerName = "Name Not Set"; 
  
	internal static void LoadSettings() 
    {
    	// initialize the INI file
      
        // read each setting and set it
      
        // For details on how to do all this, look at docs.ragepluginhook.net under the InitializationFile class
        // I don't have access to look it up myself at this moment but will update this example later. 
    }
} 

In your Initialize function, call Settings.LoadSettings(). Then, whenever you need to use a setting in your code, just call it like so: 


if(Settings.PlayMusic) 
{
	// Play the music   
}

 

Thanks man. Working on it right now, was just compiling audio files I'll be using in future callouts for over an hour or so. I'll post here if I have anymore questions :)

  • Author

I put this in NoiseComplaint.cs:

                if (Settings.PlayMusic)
                {
                    Functions.PlayScannerAudio("PLAY_GUITAR_SONG");
                }

 

And have this in Settings.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Rage;
using Rage.Native;
using LSPD_First_Response;

namespace UnlimitedCallouts
{
    internal static class Settings
    {
        internal static bool PlayMusic = true;
        //internal static bool DoSomeOtherThing = false;
        //internal static Keys SomeHotKeyOrWhatever = Keys.D8;
        //internal static string PlayerName = "Name Not Set";

        internal static void LoadSettings()
        {
            // initialize the INI file
            Settings.LoadSettings();
            InitializationFile ini = new InitializationFile("Plugins/LSPDFR/UnlimitedCallouts.ini");
            ini.Create();

            // read each setting and set it

            // For details on how to do all this, look at docs.ragepluginhook.net under the InitializationFile class
            // I don't have access to look it up myself at this moment but will update this example later. 
        }
    }
}

Not really sure how to read each setting and set it, and I tested it with ini file below and it didn't work. I'd imagine I'm suppose to read it and set it as you said but I don't know how to do that. I probably have the ini file wrong too, I don't know lol.

PlayMusic = false

 

and it doesn't work, it still plays the music even when I set in the ini file to false.

Edited by ToastinYou

Here is a look at how I do it, with just a few of the settings shown for the sake of example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Rage;
using System.Windows.Forms;

namespace BetterEMS
{
    internal static class Settings
    {
        internal static Keys DispatchKey = Keys.OemSemicolon;
        internal static ControllerButtons DispatchButton = ControllerButtons.RightShoulder;
        internal static float DriveSpeed = 20f;
        internal static bool ShowReport = true;
        
        internal static void LoadSettings()
        {
            LogWrapper.LogTrivial("Loading EMS settings");

            string path = "Plugins/LSPDFR/BetterEMS.ini";
            InitializationFile ini = new InitializationFile(path);
            ini.Create();

            DispatchKey = ini.ReadEnum<Keys>("Keybindings", "DispatchEMSKey", Keys.OemSemicolon);
            DispatchButton = ini.ReadEnum<ControllerButtons>("Keybindings", "DispatchEMSControllerButton", ControllerButtons.RightShoulder);
            DriveSpeed = (float)ini.ReadDouble("Response", "DriveSpeed", 20f);
            ShowReport = ini.ReadBoolean("Cleanup", "ShowIncidentReport", true);
        }

    }
}

Basically, you need to create an InitializationFile object, then call various Read[Something] methods to get the values you want. Details here: http://docs.ragepluginhook.net/html/DEEB0B71.htm 

There are methods to read pretty much all common types of values. For enums you have to specify what type of enum you're trying to read in the <brackets>, as shown for the key and button examples in the code snippet above. If you want to read a float you need to coerce it from a double, as shown. 

Your INI file should be layed out with sections and variables then, like so. Note how the [section names] are the first parameter in the Read functions above, and the variable names are the second. 

[Keybindings]

DispatchEMSKey = OEMSemicolon
DispatchEMSControllerButton = RightShoulder

[Response]

DriveSpeed = 20

[Cleanup]

ShowIncidentReport = true

 

You also need to make sure that you actually call that LoadSettings() method at some point. For example, in your Initialize() method would be a good place to put Settings.LoadSettings(). If you don't do that, you'll never actually load the settings, so it'll always use the defaults. 

[REL] Coastal Callouts: An action-packed mod with new vehicles, maps, capabilities, and callouts in and around the waters of Los Santos

[REL] Police Tape: Make your scenes more realistic while stopping peds and traffic

[REL] Better EMS: Realistic and dynamic EMS response

Join the Parks Benefactor Program to support my work and get early beta access!

@PNWParksFan FWIW you can actually merge the declaration and loading bits for even cleaner code, as I've done below (not my idea, but something I learned from one of the older threads in here, I think it may've been Stealth22's example originally?)

    internal static class Settings
    {
        internal static InitializationFile INIFile = new InitializationFile(@"Plugins\LSPDFR\AgencyCallouts.ini");

        internal static bool RestrictCallouts { get { return Settings.INIFile.ReadBoolean("Settings", "RestrictCallouts", true); } }
        internal static String EnhanceLoadout { get { return Settings.INIFile.ReadString("Settings", "EnhanceLoadout", "yes"); } }
        internal static String ForceAgency { get { return Settings.INIFile.ReadString("Settings", "ForceAgency", "None"); } }
        internal static Keys Interaction { get { return Settings.INIFile.ReadEnum<Keys>("Keys", "Interaction", Keys.T); } }
        internal static Keys Menu { get { return Settings.INIFile.ReadEnum<Keys>("Keys", "Menu", Keys.End); } }
        internal static Keys Action { get { return Settings.INIFile.ReadEnum<Keys>("Keys", "Action", Keys.Y); } }
        internal static Keys CPR { get { return Settings.INIFile.ReadEnum<Keys>("Keys", "CPR", Keys.U); } }
    }

I assume its possible to define the set action in the same line to update the ini file, but I haven't messed around with that myself yet.

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.

12 minutes ago, Darkmyre said:

@PNWParksFan FWIW you can actually merge the declaration and loading bits for even cleaner code, as I've done below (not my idea, but something I learned from one of the older threads in here, I think it may've been Stealth22's example originally?)


    internal static class Settings
    {
        internal static InitializationFile INIFile = new InitializationFile(@"Plugins\LSPDFR\AgencyCallouts.ini");

        internal static bool RestrictCallouts { get { return Settings.INIFile.ReadBoolean("Settings", "RestrictCallouts", true); } }
        internal static String EnhanceLoadout { get { return Settings.INIFile.ReadString("Settings", "EnhanceLoadout", "yes"); } }
        internal static String ForceAgency { get { return Settings.INIFile.ReadString("Settings", "ForceAgency", "None"); } }
        internal static Keys Interaction { get { return Settings.INIFile.ReadEnum<Keys>("Keys", "Interaction", Keys.T); } }
        internal static Keys Menu { get { return Settings.INIFile.ReadEnum<Keys>("Keys", "Menu", Keys.End); } }
        internal static Keys Action { get { return Settings.INIFile.ReadEnum<Keys>("Keys", "Action", Keys.Y); } }
        internal static Keys CPR { get { return Settings.INIFile.ReadEnum<Keys>("Keys", "CPR", Keys.U); } }
    }

I assume its possible to define the set action in the same line to update the ini file, but I haven't messed around with that myself yet.

I don't prefer this method because it requires reading the file every time, vs reading it once when you start the mod. I expect you'd get better performance not having to read a file every time you check a setting.  

[REL] Coastal Callouts: An action-packed mod with new vehicles, maps, capabilities, and callouts in and around the waters of Los Santos

[REL] Police Tape: Make your scenes more realistic while stopping peds and traffic

[REL] Better EMS: Realistic and dynamic EMS response

Join the Parks Benefactor Program to support my work and get early beta access!

  • Author
10 hours ago, khorio said:

He writes a 2 page explanation, showing his own code, and you go nevermind? :p

I was asking him a question because I got another error but I fixed it.. see where the post says "Edited". So I changed it from my question to that.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

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.