C# Teleport to marker [Solved]

So I want to be able to teleport to a set marker on the map using C#. when typing a command. I have it “working” I can type /tpm in chat to teleport to a marker but I cant seem to get the ground elevation… I just spawn way to high… I know you need to use

GetGroundZFor_3dCoord(float x, float y, float z, ref float groundZ, bool ignoreWater);

but i cant seem to get this to work correctly… I have a lua script that works that i am trying to convert the code for

    local ped = PlayerPedId()
    local blip = GetFirstBlipInfoId(8)
    if DoesBlipExist(blip) then
        local blipCoords = GetBlipCoords(blip)
        for height = 1, 1000 do
            SetPedCoordsKeepVehicle(ped, blipCoords.x, blipCoords.y, height + 0.0)
            local foundGround, zpos = GetGroundZFor_3dCoord(blipCoords.x, blipCoords.y, height + 0.0)
            if foundGround then
                SetPedCoordsKeepVehicle(ped, blipCoords.x, blipCoords.y, height + 0.0)
                break
            end
            Wait(0)
        end
    end

I have essentially translated this to C# with

    var ped = PlayerPedId();
    var blip = GetFirstBlipInfoId(8);
                
    if(DoesBlipExist(blip))
    {
        var blipCoords = GetBlipCoords(blip);

        for(int height = 1; height <= 1000; height++)
             {
                  SetPedCoordsKeepVehicle(ped, blipCoords.X, blipCoords.Y, 
                                  height + 0.0f);
                float zpos = 0.0f;
                bool foundGround = GetGroundZFor_3dCoord( blipCoords.X, 
                                  blipCoords.Y,  height +0.0f, ref zpos, false);
                        if(foundGround)
                        {
                           SetPedCoordsKeepVehicle(ped, blipCoords.X, 
                                  blipCoords.Y, height + 0.0f);
                            break;
                        }
                        Wait(0);
                    }
                }

so i think i converted the code correctly… it technically works but i teleport waaay above the ground and the GetGroundZFor never returns true… I’m assuming it has something to do with the ref zpos because that’s the only thing that is kind of different between the two pieces of code.
lua

local foundGround, zpos = GetGroundZFor

c#

float zpos = 0.0f // I think this is what i need to fix but not sure how
bool foundGround = GetGroundZFor

Any help would be much appreciated… thanks

Not too sure since I hate C# so I never used it, but does this lua code work? Please test if the Lua code works on your local server.

Reason being is because: "Bear in mind this native can only calculate the elevation when the coordinates are within the client’s render distance."

Maybe try using GetGroundZFor_3dCoord_2 - Natives @ Cfx.re Docs

Start at 1000 first and lower the value and make sure you’re checking collisions have loaded around the player with HasCollisionLoadedAroundEntity() before checking GetGroundZFor_3dCoord().

for height = 1000.0, 1, -20 do
    SetPedCoordsKeepVehicle(ped, blipCoords.x, blipCoords.y, height + 0.0)

    local timer = GetGameTimer()

    while not HasCollisionLoadedAroundEntity(ped) do
        -- Break out of the loop if it takes too long
        if GetGameTimer() - timer > 1000 then
            break
        end
        Citizen.Wait(0)
    end

    local foundGround, zpos = GetGroundZFor_3dCoord(blipCoords.x, blipCoords.y, height + 0.0)
    if foundGround then
        SetPedCoordsKeepVehicle(ped, blipCoords.x, blipCoords.y, height + 0.0)
        break
    end
end

This will cause you to get under/above ground in some cases, you should use zpos as your Z axis when setting ped coords if ground is found.

You won’t need all of this, but vMenu’s teleport to coordinates code should set you in the right direction:

Ok so I sifted through the GitHub repo that @ChristopherM linked and I narrowed down the code for correctly teleporting to a waypoint so this is what I ended up with here is the full code if people are interested

using CitizenFX.Core;
using System.Threading.Tasks;
using static CitizenFX.Core.Native.API;

public class Commands : BaseScript
{
    public Commands()
    {
        RegisterCommand("tpm", new Action<int, List<object>, string> (async (src, args, raw) =>
        {
             TeleportToWaypoint();

        }), false);
    }
    public static async void TeleportToWaypoint()
    {
       if(Game.IsWaypointActive)
       {
            var pos = World.WaypointPosition;
            await TeleportToCoords(pos);
       }
    }
    public static Vehicle GetVehicle(bool lastVehicle = false)
    {
        if (lastVehicle)
        {
              return Game.PlayerPed.LastVehicle;
         }
         else
         {
             if (Game.PlayerPed.IsInVehicle())
             {
                 return Game.PlayerPed.CurrentVehicle;
             }
         }
          return null;
    }
       public static async Task TeleportToCoords(Vector3 pos, bool 
            safeModeDisabled = false)
      {
            if (!safeModeDisabled)
            {
                // Is player in a vehicle and the driver? Then we'll use that to teleport.
                var veh = GetVehicle();
                bool inVehicle() => veh != null && veh.Exists() && Game.PlayerPed == veh.Driver;

                bool vehicleRestoreVisibility = inVehicle() && veh.IsVisible;
                bool pedRestoreVisibility = Game.PlayerPed.IsVisible;

                // Freeze vehicle or player location and fade out the entity to the network.
                if (inVehicle())
                {
                    veh.IsPositionFrozen = true;
                    if (veh.IsVisible)
                    {
                        NetworkFadeOutEntity(veh.Handle, true, false);
                    }
                }
                else
                {
                    ClearPedTasksImmediately(Game.PlayerPed.Handle);
                    Game.PlayerPed.IsPositionFrozen = true;
                    if (Game.PlayerPed.IsVisible)
                    {
                        NetworkFadeOutEntity(Game.PlayerPed.Handle, true, false);
                    }
                }

                // Fade out the screen and wait for it to be faded out completely.
                DoScreenFadeOut(500);
                while (!IsScreenFadedOut())
                {
                    await Delay(0);
                }

                // This will be used to get the return value from the groundz native.
                float groundZ = 850.0f;

                // Bool used to determine if the groundz coord could be found.
                bool found = false;

                // Loop from 950 to 0 for the ground z coord, and take away 25 each time.
                for (float zz = 950.0f; zz >= 0f; zz -= 25f)
                {
                    float z = zz;
                    // The z coord is alternating between a very high number, and a very low one.
                    // This way no matter the location, the actual ground z coord will always be found the fastest.
                    // If going from top > bottom then it could take a long time to reach the bottom. And vice versa.
                    // By alternating top/bottom each iteration, we minimize the time on average for ANY location on the map.
                    if (zz % 2 != 0)
                    {
                        z = 950f - zz;
                    }

                    // Request collision at the coord. I've never actually seen this do anything useful, but everyone keeps telling me this is needed.
                    // It doesn't matter to get the ground z coord, and neither does it actually prevent entities from falling through the map, nor does
                    // it seem to load the world ANY faster than without, but whatever.
                    RequestCollisionAtCoord(pos.X, pos.Y, z);

                    // Request a new scene. This will trigger the world to be loaded around that area.
                    NewLoadSceneStart(pos.X, pos.Y, z, pos.X, pos.Y, z, 50f, 0);

                    // Timer to make sure things don't get out of hand (player having to wait forever to get teleported if something fails).
                    int tempTimer = GetGameTimer();

                    // Wait for the new scene to be loaded.
                    while (IsNetworkLoadingScene())
                    {
                        // If this takes longer than 1 second, just abort. It's not worth waiting that long.
                        if (GetGameTimer() - tempTimer > 1000)
                        {
                           // Log("Waiting for the scene to load is taking too long (more than 1s). Breaking from wait loop.");
                            break;
                        }

                        await Delay(0);
                    }

                    // If the player is in a vehicle, teleport the vehicle to this new position.
                    if (inVehicle())
                    {
                        SetEntityCoords(veh.Handle, pos.X, pos.Y, z, false, false, false, true);
                    }
                    // otherwise, teleport the player to this new position.
                    else
                    {
                        SetEntityCoords(Game.PlayerPed.Handle, pos.X, pos.Y, z, false, false, false, true);
                    }

                    // Reset the timer.
                    tempTimer = GetGameTimer();

                    // Wait for the collision to be loaded around the entity in this new location.
                    while (!HasCollisionLoadedAroundEntity(Game.PlayerPed.Handle))
                    {
                        // If this takes too long, then just abort, it's not worth waiting that long since we haven't found the real ground coord yet anyway.
                        if (GetGameTimer() - tempTimer > 1000)
                        {
                            //Log("Waiting for the collision is taking too long (more than 1s). Breaking from wait loop.");
                            break;
                        }
                        await Delay(0);
                    }

                    // Check for a ground z coord.
                    found = GetGroundZFor_3dCoord(pos.X, pos.Y, z, ref groundZ, false);

                    // If we found a ground z coord, then teleport the player (or their vehicle) to that new location and break from the loop.
                    if (found)
                    {
                       // Log($"Ground coordinate found: {groundZ}");
                        if (inVehicle())
                        {
                            SetEntityCoords(veh.Handle, pos.X, pos.Y, groundZ, false, false, false, true);

                            // We need to unfreeze the vehicle because sometimes having it frozen doesn't place the vehicle on the ground properly.
                            veh.IsPositionFrozen = false;
                            veh.PlaceOnGround();
                            // Re-freeze until screen is faded in again.
                            veh.IsPositionFrozen = true;
                        }
                        else
                        {
                            SetEntityCoords(Game.PlayerPed.Handle, pos.X, pos.Y, groundZ, false, false, false, true);
                        }
                        break;
                    }

                    // Wait 10ms before trying the next location.
                    await Delay(10);
                }

                // If the loop ends but the ground z coord has not been found yet, then get the nearest vehicle node as a fail-safe coord.
                if (!found)
                {
                    var safePos = pos;
                    GetNthClosestVehicleNode(pos.X, pos.Y, pos.Z, 0, ref safePos, 0, 0, 0);

                    // Teleport vehicle, or player.
                    if (inVehicle())
                    {
                        SetEntityCoords(veh.Handle, safePos.X, safePos.Y, safePos.Z, false, false, false, true);
                        veh.IsPositionFrozen = false;
                        veh.PlaceOnGround();
                        veh.IsPositionFrozen = true;
                    }
                    else
                    {
                        SetEntityCoords(Game.PlayerPed.Handle, safePos.X, safePos.Y, safePos.Z, false, false, false, true);
                    }
                }

                // Once the teleporting is done, unfreeze vehicle or player and fade them back in.
                if (inVehicle())
                {
                    if (vehicleRestoreVisibility)
                    {
                        NetworkFadeInEntity(veh.Handle, true);
                        if (!pedRestoreVisibility)
                        {
                            Game.PlayerPed.IsVisible = false;
                        }
                    }
                    veh.IsPositionFrozen = false;
                }
                else
                {
                    if (pedRestoreVisibility)
                    {
                        NetworkFadeInEntity(Game.PlayerPed.Handle, true);
                    }
                    Game.PlayerPed.IsPositionFrozen = false;
                }

                // Fade screen in and reset the camera angle.
                DoScreenFadeIn(500);
                SetGameplayCamRelativePitch(0.0f, 1.0f);
            }

}

This code doe not take permissions into account… my next task lol because i like to build my own stuff.
its safe to say I was missing some things but overall I think this setup is a lot more performant since it is also using async tasks… I have to say credits go to @ChristopherM for mentioning the repo and thanks to the person that made that menu system… even tho I’m not a fan of using other peoples work its nice to have a base to go off of so here is the link to the repo i got this from I just isolated the code… GitHub - TomGrobbe/vMenu: vMenu is a custom server sided trainer/menu, built using a custom version of NativeUI. It has full permissions support, so the server owner can decide who's allowed to do what.

1 Like