Exemplo n.º 1
0
        /// <summary>
        /// Teleport to the specified <see cref="pos"/>.
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="safeModeDisabled"></param>
        /// <returns></returns>
        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 BaseScript.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.Debug("Waiting for the scene to load is taking too long (more than 1s). Breaking from wait loop.");
                            break;
                        }

                        await BaseScript.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.Debug("Waiting for the collision is taking too long (more than 1s). Breaking from wait loop.");
                            break;
                        }
                        await BaseScript.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.Debug($"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 BaseScript.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);

                    // Notify the user that the ground z coord couldn't be found, so we will place them on a nearby road instead.
                    Notify.Alert("Could not find a safe ground coord. Placing you on the nearest road instead.");
                    Log.Debug("Could not find a safe ground coord. Placing you on the nearest road instead.");

                    // 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);
            }

            // Disable safe teleporting and go straight to the specified coords.
            else
            {
                RequestCollisionAtCoord(pos.X, pos.Y, pos.Z);

                // Teleport directly to the coords without trying to get a safe z pos.
                if (Game.PlayerPed.IsInVehicle() && GetVehicle().Driver == Game.PlayerPed)
                {
                    SetEntityCoords(GetVehicle().Handle, pos.X, pos.Y, pos.Z, false, false, false, true);
                }
                else
                {
                    SetEntityCoords(Game.PlayerPed.Handle, pos.X, pos.Y, pos.Z, false, false, false, true);
                }
            }
        }