Skip to content
Ghillie edited this page Dec 23, 2022 · 25 revisions

This is a collection of some useful code-snippets made by users. Please feel free to add more!

Quick Jump


Force GroundZ/True Water Depth by Dilapidated

This function will force GroundZ/WaterZ and grab the WaterDepth of areas even if the collisions of the map aren't loaded and the area is far away from the player

    /// <summary>
    ///     Forces Ground Z position even when the location doesn't have collisions loaded
    /// </summary>
    public static Vector3 ForceGroundZ(this Vector3 v)
    {
        float zcoord = 0.0f;
        var outArgb = new OutputArgument();


        float[] firstCheck = new float[] { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 };

        float[] secondCheck = new float[] { 1000, 900, 800, 700, 600, 500,
            400, 300, 200, 100, 0, -100, -200, -300, -400, -500 };

        float[] thirdCheck = new float[] { -500, -400, -300, -200, -100, 0,
            100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};

        if (Function.Call<bool>(Hash.GET_GROUND_Z_FOR_3D_COORD, v.X, v.Y, 1000f, outArgb))
            zcoord = outArgb.GetResult<float>();

        if (zcoord == 0)
        {
            for (int i = 0; i < firstCheck.Length; i++)
            {
                Function.Call(Hash.REQUEST_COLLISION_AT_COORD, v.X, v.Y, firstCheck[i]);
                GTA.Script.Wait(0);
            }

            if (Function.Call<bool>(Hash.GET_GROUND_Z_FOR_3D_COORD, v.X, v.Y, 1000f, outArgb))
                zcoord = outArgb.GetResult<float>();
        }

        if (zcoord == 0)
        {
            Log.Write(true, "ZCoord secondCheck");

            for (int i = 0; i < secondCheck.Length; i++)
            {
                Function.Call(Hash.REQUEST_COLLISION_AT_COORD, v.X, v.Y, secondCheck[i]);
                GTA.Script.Wait(0);
            }

            if (Function.Call<bool>(Hash.GET_GROUND_Z_FOR_3D_COORD, v.X, v.Y, 1000f, outArgb))
                zcoord = outArgb.GetResult<float>();
        }

        if (zcoord == 0)
        {
            Log.Write(true, "ZCoord thirdCheck");

            for (int i = 0; i < thirdCheck.Length; i++)
            {
                Function.Call(Hash.REQUEST_COLLISION_AT_COORD, v.X, v.Y, thirdCheck[i]);
                GTA.Script.Wait(0);
            }

            if (Function.Call<bool>(Hash.GET_GROUND_Z_FOR_3D_COORD, v.X, v.Y, 1000f, outArgb))
                zcoord = outArgb.GetResult<float>();
        }

        return new Vector3(v.X, v.Y, zcoord);
    }

    /// <summary>
    ///     Forces Water Z position even when the location doesn't have collisions loaded
    /// </summary>
    public static Vector3 ForceWaterZ(this Vector3 v)
    {
        float zcoord = -500.0f;
        var outArgb = new OutputArgument();


        float[] firstCheck = new float[] { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 };

        float[] secondCheck = new float[] { 1000, 900, 800, 700, 600, 500,
            400, 300, 200, 100, 0, -100, -200, -300, -400, -500 };

        float[] thirdCheck = new float[] { -500, -400, -300, -200, -100, 0,
            100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};

        if (Function.Call<bool>(Hash.TEST_PROBE_AGAINST_ALL_WATER, v.X, v.Y, 1000f, v.X, v.Y, -500f, 1, outArgb))
            zcoord = outArgb.GetResult<Vector3>().Z;

        if (zcoord == -500)
        {
            for (int i = 0; i < firstCheck.Length; i++)
            {
                Function.Call(Hash.REQUEST_COLLISION_AT_COORD, v.X, v.Y, firstCheck[i]);
                GTA.Script.Wait(0);
            }

            if (Function.Call<bool>(Hash.TEST_PROBE_AGAINST_ALL_WATER, v.X, v.Y, 1000f, v.X, v.Y, -500f, 1, outArgb))
                zcoord = outArgb.GetResult<Vector3>().Z;
        }

        if (zcoord == -500)
        {
            Log.Write(true, "ZCoord secondCheck");

            for (int i = 0; i < secondCheck.Length; i++)
            {
                Function.Call(Hash.REQUEST_COLLISION_AT_COORD, v.X, v.Y, secondCheck[i]);
                GTA.Script.Wait(0);
            }

            if (Function.Call<bool>(Hash.TEST_PROBE_AGAINST_ALL_WATER, v.X, v.Y, 1000f, v.X, v.Y, -500f, 1, outArgb))
                zcoord = outArgb.GetResult<Vector3>().Z;

        }

        if (zcoord == -500)
        {
            Log.Write(true, "ZCoord thirdCheck");

            for (int i = 0; i < thirdCheck.Length; i++)
            {
                Function.Call(Hash.REQUEST_COLLISION_AT_COORD, v.X, v.Y, thirdCheck[i]);
                GTA.Script.Wait(0);
            }

            if (Function.Call<bool>(Hash.TEST_PROBE_AGAINST_ALL_WATER, v.X, v.Y, 1000f, v.X, v.Y, -500f, 1, outArgb))
                zcoord = outArgb.GetResult<Vector3>().Z;
        }

        return new Vector3(v.X, v.Y, zcoord);
    }



    /// <summary>
    ///     Forces Water Depth even when the location doesn't have collisions loaded 
    /// </summary>
    public static float ForceWaterDepth(this Vector3 v)
    {
        float result = 0.0f;
        var ground = v.ForceGroundZ().Z;
        var water = v.ForceWaterZ().Z;

        if (water > ground)
            result = water - ground;

        if (ground == 0)
            result = 500f;

        return result;
    }

}

Tick-System by Nacorpio

For those of you who find it very frustrating to work with ticks, I have come up with a solution. I don't know how many of you who would find this useful, but I do at least.

You just implement this abstract class, and you'll be ready to go.

An example on how you could use IsActive, is when wrapping around peds. This can also be found in my mod, which you can see on my Github page. You could make it tick only when the ped is alive, and so on. IsActive = Ped != null && Ped.IsAlive;

Basically, it organizes the ticks, and makes the whole thing a lot easier. You can also give any instance a lifespan.

If you still don't understand why you should use an updater, check my mod out:

https://github.com/Nacorpio/GTA-Z/blob/master/GTAZ/Controllable/ControllableEntity.cs
https://github.com/Nacorpio/GTA-Z/blob/master/GTAZ/Controllable/ControllablePed.cs
https://github.com/Nacorpio/GTA-Z/blob/master/GTAZ/Controllable/ControllableVehicle.cs
https://github.com/Nacorpio/GTA-Z/blob/master/GTAZ/Peds/ZombiePed.cs

public abstract class Updater
{
    private bool _active = false;

    private int _activeTick;
    private int _inactiveTick;

    private readonly int _startTick;
    private int _tick;

    private Dictionary<int, Action> _actionQueue = new Dictionary<int, Action>(); 

    protected Updater(int startTick = 0)
    {
        _tick = startTick;
        _startTick = startTick;
    }

    public void Tick()
    {
        OnUpdate(_tick);

        if (_actionQueue.ContainsKey(_tick))
        {
            _actionQueue[_tick].Invoke();
        }

        if (_tick == 0 || _tick == _startTick)
        {
            OnFirstUpdate();
        }

        if (_active)
        {
            _inactiveTick = 0;

            OnActiveUpdate(_activeTick, _tick);

            if (_activeTick == 0) {
                OnFirstActiveUpdate(_tick);
            }

            _activeTick++;
        }
        else
        {
            _activeTick = 0;

            OnInactiveUpdate(_inactiveTick, _tick);

            if (_inactiveTick == 0)
            {
                OnFirstInactiveUpdate(_tick);
            }

            _inactiveTick++;
        }

        _tick++;
    }

    /// <summary>
    /// Fired everytime a tick has been made.
    /// </summary>
    /// <param name="tick">The total amount of ticks.</param>
    protected abstract void OnUpdate(int tick);

    /// <summary>
    /// Fired on the absolute first update.
    /// </summary>
    protected abstract void OnFirstUpdate();

    /// <summary>
    /// Fired everytime a tick has been made while active.
    /// </summary>
    /// <param name="activeTick">The current tick on this activity state.</param>
    /// <param name="tick">The total amount of ticks.</param>
    protected abstract void OnActiveUpdate(int activeTick, int tick);

    /// <summary>
    /// Fired everytime a tick has been made while inactive.
    /// </summary>
    /// <param name="inactiveTick">The current tick on this activity state.</param>
    /// <param name="tick">The total amount of ticks.</param>
    protected abstract void OnInactiveUpdate(int inactiveTick, int tick);

    /// <summary>
    /// Fired the first time an active update has been made.
    /// </summary>
    /// <param name="tick">The total amount of ticks.</param>
    protected abstract void OnFirstActiveUpdate(int tick);

    /// <summary>
    /// Fired the first time an inactive update has been made.
    /// </summary>
    /// <param name="tick">The total amount of ticks.</param>
    protected abstract void OnFirstInactiveUpdate(int tick);

    public Dictionary<int, Action> ActionQueue { get { return _actionQueue; } set { _actionQueue = value; }} 

    /// <summary>
    /// Returns the tick of which the ticking started.
    /// This isn't always 0, as the StartTick can be set.
    /// </summary>
    public int StartTick { get { return _startTick; }}

    /// <summary>
    /// Returns how many times this Updating instance has ticked.
    /// </summary>
    public int Tick { get { return _tick; } }

    /// <summary>
    /// If active, returns how many times it has been ticked during its session.
    /// </summary>
    public int ActiveTicks { get { return _activeTick; }}

    /// <summary>
    /// If inactive, returns how many times it has been ticked during its session.
    /// </summary>
    public int InactiveTicks { get { return _inactiveTick; }}

    /// <summary>
    /// Returns whether this Updating instance is active.
    /// </summary>
    public bool IsActive
    {
        get { return _active; }
        protected set { _active = value; }
    }
}

Logger Helper

As you cannot currently log items to the main ScriptHookVDotNet.log file, you can use this helper class to log events to a file of your choosing.

using System;
using System.IO;

/// <summary>
/// Static logger class that allows direct logging of anything to a text file
/// </summary>
public static class Logger
{
    public static void Log(object message)
    {
        File.AppendAllText("MyModLogFile.log", DateTime.Now + " : " + message + Environment.NewLine);
    }
}

Usage:

Logger.Log("This will get written to the log!");

Result:

31/05/2015 15:06:23 : This will get written to the log!

Simple Command/Hotkey Script

This is a basic script which allows you to easily add more commands/hotkeys by adding them to a dictionary. This script is especially useful if you just want to test new features. When you press Comma/Dot on your NumPad, a text-prompt will open and you can input your command.

public class MainScript : Script
{
    private readonly Dictionary<Keys, Action> _hotkeys;
    private readonly Dictionary<string, Action<string[]>> _hotstrings;
    private readonly UIText _statusText;
    private bool _enabled;
    public bool Enabled
    {
        get { return _enabled; }
        set
        {
            _statusText.Caption = "Example Script: " + (value ? "~g~ON" : "~r~OFF");
            _enabled = value;
        }
    }

    public MainScript()
    {
        Tick += OnTick;
        KeyDown += OnKeyDown;
        Interval = 0;

        _statusText = new UIText("Example Script: ~r~OFF", new Point(10, 10), 0.4f, Color.WhiteSmoke, 0, false);
        Enabled = true;

        _hotstrings = new Dictionary<string, Action<string[]>>();
        _hotstrings.Add("zerowanteds", (args) =>
        {
            Game.Player.WantedLevel = 0;
            UI.Notify("Wanteds cleared!");
        });

        _hotkeys = new Dictionary<Keys, Action>();
        _hotkeys.Add(Keys.Decimal, () =>
        {
            string result = Game.GetUserInput(20);
            if (result == null)
                return;
            String[] command = result.Split(' ');;
            if (_hotstrings.ContainsKey(command[0]))
                _hotstrings[command[0]](command.Skip(1).ToArray());
            else
                UI.Notify("Unknown Command");
        });
        _hotkeys.Add(Keys.NumPad1, SpawnVeh);
    }

    private void OnTick(object sender, EventArgs e)
    {
        _statusText.Draw();

        if (!Enabled)
            return;
    }

    private void OnKeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.F5)
            Enabled = !Enabled;

        if (!Enabled)
            return;

        foreach (var hotkey in _hotkeys.Keys.Where(hotkey => e.KeyCode == hotkey))
            _hotkeys[hotkey]();
    }

    public void SpawnVeh()
    {
        //Spawn a Vehicle
    }
}

Key Filter

A key eventHandler will hold events triggered during a Script.Wait(); Once the Wait is complete your key eventHandler will work through the queued key presses.

To Handle the queue you can add a Filter Script like below...

public class KeyFilter : Script
{
        public KeyFilter()
        {
            KeyDown += OnKey;
        }

        private void OnKey(object sender, KeyEventArgs e)
        {
            KeyFilter.Yield();

            if (e != null && !e.Handled) e.Handled = true;
        }
}

The KeyFilter uses Yield() to wait until after your normal key eventHandler has run. In your Main key eventHandler you can check if the key is !e.Handled.

Native Handler Finder

This code finds the handler addresses of native functions and you can see how they exactly work. This is useful only for advanced developers who know how to read x64 assembly. You will need updated native hashes to get addresses of natives. FiveM provides a crossmap as well as the game version array.

This code is a set of definitions to find native addresses. Ported from OpenVHook.

address = FindPattern("\x76\x32\x48\x8B\x53\x40", "xxxxxx"); // you can use the implementation of FindPattern in SHVDN
nativeRegistrationTableAddr = new IntPtr((long*)(*(int*)(address + 9) + address + 13));

public unsafe struct NativeRegistration
{
    public ulong nextRegBase;
    public ulong nextRegKey;
    public fixed ulong handlers[7];
    public uint numEntries1;
    public uint numEntries2;
    public uint _unknown;
    public fixed ulong hashes[7];

    /*
        // decryption
        key = this ^ nextRegKey  // only lower 32 bits
        nextReg = nextRegBase ^ key<<32 ^ key
        // encryption
        key = this ^ nextRegKey  // only lower 32 bits
        nextRegBase = nextReg ^ key<<32 ^ key

        only lower 32 bits of this^nextRegKey are used, higher 32 bits are ignored.
        thus, higher 32 bit of nexRegBase must contain the info of (masked) higher address of next registration.
        the first two members of struct are named as Base/Key respectively in that sense.
    */

    public static unsafe NativeRegistration* GetNextRegistration(NativeRegistration* reg)
    {
        uint key = (uint)(((ulong)reg) ^ reg->nextRegKey);
        return (NativeRegistration*)(reg->nextRegBase ^ ((ulong)(key) << 32) ^ key);
    }

    public static uint GetNumEntries(NativeRegistration* reg)
    {
        return ((uint)(((ulong)(&(reg->numEntries1))) & 0xFFFFFFFF)) ^ reg->numEntries1 ^ reg->numEntries2;
    }

    public static ulong GetHash(NativeRegistration* reg, uint index)
    {
        uint key = ((uint)&reg->hashes[2 * index]) ^ (uint)reg->hashes[2 * index + 1];
        return reg->hashes[2 * index] ^ ((ulong)(key) << 32) ^ key;
    }
}

public static unsafe IntPtr GetNativeHandler(ulong nativeHash)
{
    if (nativeRegistrationTableAddr == IntPtr.Zero)
    {
        return IntPtr.Zero;
    }

    NativeRegistration* table = *(NativeRegistration**)(nativeRegistrationTableAddr + (int)(0x8 * (nativeHash & 0xFF))).ToPointer();

    for (; table != null; table = NativeRegistration.GetNextRegistration(table))
    {
        for (uint i = 0; i < NativeRegistration.GetNumEntries(table); i++)
        {
            if (nativeHash == NativeRegistration.GetHash(table, i))
            {
                return new IntPtr((long)table->handlers[i]);
            }
        }
    }

    return IntPtr.Zero;
}

Usage;

GetNativeHandler(0x7D2B9E6A64637269); // get the handler address of PLAYER_PED_ID (works only in b2372)
GetNativeHandler(0x8339643499D1222E); // get the handler address of 0x8339643499D1222E, a.k.a. _SET_ENTITY_ANGULAR_VELOCITY (the hash will be changed in the versions later than b2372)