Иконка ресурса

FREE SpawnHeli : 3.2.0 3.2.0

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Facepunch;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Spawn Heli", "SpooksAU", "3.2.0")]
    [Description("Allows players to spawn helicopters")]
    internal class SpawnHeli : CovalencePlugin
    {
        #region Fields

        private const string LegacyPluginName = "SpawnMini";
        private const string LegacyPermissionPrefix = "spawnmini.";

        private const string PermissionMinicopter = "minicopter";
        private const string PermissionScrapHeli = "scraptransport";
        private const string PermissionAttackHeli = "attackhelicopter";

        private const int SpawnPointLayerMask = Rust.Layers.Solid | Rust.Layers.Mask.Water;
        private const int SpaceCheckLayerMask = Rust.Layers.Solid;

        private const float VerticalSpawnOffset = 1;

        private SaveData _data;
        private Configuration _config;
        private readonly VehicleInfoManager _vehicleInfoManager;

        private readonly object True = true;
        private readonly object False = false;

        public SpawnHeli()
        {
            _vehicleInfoManager = new VehicleInfoManager(this);
        }

        #endregion

        #region Hooks

        private void Init()
        {
            _data = SaveData.Load();
            _config.Init();

            foreach (var perm in VehicleInfo.All.GetAllPermissions())
            {
                permission.RegisterPermission(perm, this);
            }

            _vehicleInfoManager.Init();

            if (!_vehicleInfoManager.AnyOwnerOnly)
            {
                Unsubscribe(nameof(CanMountEntity));
            }

            if (!_vehicleInfoManager.AnyDespawnOnDisconnect)
            {
                Unsubscribe(nameof(OnPlayerDisconnected));
                Unsubscribe(nameof(OnEntityDismounted));
            }

            if (!_vehicleInfoManager.AnyInstantTakeoff)
            {
                Unsubscribe(nameof(OnEngineStarted));
            }
        }

        private void OnServerInitialized()
        {
            if (plugins.PluginManager.GetPlugin(LegacyPluginName) != null)
            {
                LogWarning($"Detected conflicting plugin {LegacyPluginName}.Please remove that plugin to avoid issues.");
            }

            _vehicleInfoManager.OnServerInitialized();

            foreach (var networkable in BaseNetworkable.serverEntities)
            {
                var heli = networkable as PlayerHelicopter;
                if (heli == null)
                    continue;

                var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(heli);
                if (vehicleInfo == null || !vehicleInfo.Data.HasVehicle(heli))
                    continue;

                SetupHeli(heli);

                if (heli.OwnerID != 0 && HasPermission(heli.OwnerID.ToString(), vehicleInfo.Permissions.UnlimitedFuel, VehicleInfo.All.UnlimitedFuel))
                {
                    EnableUnlimitedFuel(heli);
                }
            }

            _data.Clean();
        }

        private void Unload()
        {
            _data.SaveIfChanged();
        }

        private void OnServerSave()
        {
            _data.SaveIfChanged();
        }

        private void OnNewSave()
        {
            _data.Reset();
            _data.SaveIfChanged();
        }

        private void OnEntityKill(PlayerHelicopter heli)
        {
            var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(heli);
            if (vehicleInfo == null)
                return;

            var ownerIdString = heli.OwnerID.ToString();
            var playerVehicle = vehicleInfo.Data.GetVehicle(ownerIdString);
            if (playerVehicle == null || playerVehicle != heli)
                return;

            _data.UnregisterVehicle(vehicleInfo, ownerIdString);

            var basePlayer = BasePlayer.FindByID(heli.OwnerID);
            if (basePlayer != null)
            {
                basePlayer.ChatMessage(GetMessage(basePlayer.UserIDString, vehicleInfo.Messages.Destroyed));
            }
        }

        private object OnEntityTakeDamage(PlayerHelicopter heli, HitInfo info)
        {
            if (heli == null || info == null || heli.OwnerID == 0)
                return null;

            if (!IsPlayerVehicle(heli, out var vehicleInfo))
                return null;

            if (info.damageTypes.Has(Rust.DamageType.Decay)
                && HasPermission(heli.OwnerID.ToString(), vehicleInfo.Permissions.NoDecay, VehicleInfo.All.NoDecay))
                return True;

            return null;
        }

        private object CanMountEntity(BasePlayer player, BaseVehicleMountPoint mountPoint)
        {
            if (player == null || mountPoint == null)
                return null;

            var heli = mountPoint.GetParentEntity() as PlayerHelicopter;
            if (heli == null
                || heli.OwnerID == 0
                || !IsPlayerVehicle(heli, out var vehicleInfo)
                || !vehicleInfo.Config.OnlyOwnerAndTeamCanMount)
                return null;

            // Vehicle owner is allowed to mount.if (heli.OwnerID == player.userID)
                return null;

            // Team members are allowed to mount.if (player.Team != null && player.Team.members.Contains(heli.OwnerID))
                return null;

            player.ChatMessage(GetMessage(player.UserIDString, LangEntry.ErrorCannotMount));
            return False;
        }

        private void OnPlayerDisconnected(BasePlayer player)
        {
            if (player == null)
                return;

            if (_config.Minicopter.DespawnOnDisconnect)
            {
                var heli = _data.Minicopter.GetVehicle(player.UserIDString);
                if (heli != null)
                {
                    ScheduleDespawnVehicleIfUnmounted(heli);
                }
            }

            if (_config.ScrapTransportHelicopter.DespawnOnDisconnect)
            {
                var heli = _data.ScrapTransportHelicopter.GetVehicle(player.UserIDString);
                if (heli != null)
                {
                    ScheduleDespawnVehicleIfUnmounted(heli);
                }
            }

            if (_config.AttackHelicopter.DespawnOnDisconnect)
            {
                var heli = _data.AttackHelicopter.GetVehicle(player.UserIDString);
                if (heli != null)
                {
                    ScheduleDespawnVehicleIfUnmounted(heli);
                }
            }
        }

        private void ScheduleDespawnVehicleIfUnmounted(PlayerHelicopter heli)
        {
            NextTick(() =>
            {
                // Despawn vehicle when the owner disconnects.// If mounted, we will despawn it later when all players dismount.if (heli == null || heli.AnyMounted())
                    return;

                heli.Kill();
            });
        }

        private void OnEntityDismounted(BaseVehicleSeat seat)
        {
            if (seat == null)
                return;

            var heli = seat.GetParentEntity() as PlayerHelicopter;
            if (heli == null
                || !heli.AnyMounted()
                || !IsPlayerVehicle(heli, out var vehicleInfo)
                || !vehicleInfo.Config.DespawnOnDisconnect)
                return;

            // Despawn minicopter when fully dismounted, if the owner player has disconnected.var ownerPlayer = BasePlayer.FindByID(heli.OwnerID);
            if (ownerPlayer != null && ownerPlayer.IsConnected)
                return;

            heli.Kill();
        }

        private void CanLootEntity(BasePlayer player, StorageContainer container)
        {
            if (container == null || !container.IsLocked())
                return;

            var heli = container.GetParentEntity() as PlayerHelicopter;
            if (heli == null || !IsPlayerVehicle(heli, out var vehicleInfo))
                return;

            if (!HasPermission(heli.OwnerID.ToString(), vehicleInfo.Permissions.UnlimitedFuel, VehicleInfo.All.UnlimitedFuel))
                return;

            player.ChatMessage(GetMessage(player.UserIDString, LangEntry.ErrorUnlimitedFuel));
        }

        private void OnEngineStarted(PlayerHelicopter heli, BasePlayer player)
        {
            if (!heli.engineController.IsStarting)
                return;

            if (!IsPlayerVehicle(heli, out var vehicleInfo))
                return;

            if (!vehicleInfo.Config.InstantTakeoff.Enabled)
                return;

            if (vehicleInfo.Config.InstantTakeoff.RequirePermission
                && !HasPermission(player, vehicleInfo.Permissions.InstantTakeoff, VehicleInfo.All.InstantTakeoff))
                return;

            heli.CancelInvoke(heli.engineController.FinishStartingEngine);
            heli.engineController.FinishStartingEngine();
        }

        #endregion

        #region API

        [HookMethod(nameof(API_GetMinicopter))]
        public PlayerHelicopter API_GetMinicopter(BasePlayer player)
        {
            return FindPlayerVehicle(_vehicleInfoManager.Minicopter, player);
        }

        [HookMethod(nameof(API_GetScrapTransportHelicopter))]
        public PlayerHelicopter API_GetScrapTransportHelicopter(BasePlayer player)
        {
            return FindPlayerVehicle(_vehicleInfoManager.ScrapTransportHelicopter, player);
        }

        [HookMethod(nameof(API_GetAttackHelicopter))]
        public PlayerHelicopter API_GetAttackHelicopter(BasePlayer player)
        {
            return FindPlayerVehicle(_vehicleInfoManager.AttackHelicopter, player);
        }

        [HookMethod(nameof(API_SpawnMinicopter))]
        public PlayerHelicopter API_SpawnMinicopter(BasePlayer player, Dictionary<string, object> rawOptions)
        {
            return SpawnHeliForApi(_vehicleInfoManager.Minicopter, player, rawOptions);
        }

        [HookMethod(nameof(API_SpawnScrapTransportHelicopter))]
        public PlayerHelicopter API_SpawnScrapTransportHelicopter(BasePlayer player, Dictionary<string, object> rawOptions)
        {
            return SpawnHeliForApi(_vehicleInfoManager.ScrapTransportHelicopter, player, rawOptions);
        }

        [HookMethod(nameof(API_SpawnAttackHelicopter))]
        public PlayerHelicopter API_SpawnAttackHelicopter(BasePlayer player, Dictionary<string, object> rawOptions)
        {
            return SpawnHeliForApi(_vehicleInfoManager.AttackHelicopter, player, rawOptions);
        }

        private PlayerHelicopter SpawnHeliForApi(VehicleInfo vehicleInfo, BasePlayer player, Dictionary<string, object> rawOptions)
        {
            SpawnOptions parsedOptions;
            if (rawOptions == null)
            {
                parsedOptions = default;
            }
            else if (!SpawnOptions.TryParseApiOptions(rawOptions, out parsedOptions))
            {
                return null;
            }

            return AttemptSpawnOrFetchHeli(vehicleInfo, player.IPlayer, player, parsedOptions);
        }

        private struct SpawnOptions
        {
            private static readonly string FieldPosition = "Position";
            private static readonly string FieldRotation = "Rotation";
            private static readonly string FieldCheckHooks = "CheckHooks";
            private static readonly string FieldCheckCooldown = "CheckCooldown";
            private static readonly string FieldCheckBuildingBlocked = "CheckBuildingBlocked";
            private static readonly string FieldCheckSpace = "CheckSpace";
            private static readonly string FieldAutoMount = "AutoMount";
            private static readonly string FieldStartCooldown = "StartCooldown";
            private static readonly string FieldAutoRepair = "AutoRepair";
            private static readonly string FieldAutoFetch = "AutoFetch";
            private static readonly string FieldEnforceHelicopterLimit = "EnforceHelicopterLimit";
            private static readonly string FieldAutoDespawnOtherHelicopterTypes = "AutoDespawnOtherHelicopterTypes";
            private static readonly string FieldAllowWhileOccupied = "AllowWhileOccupied";
            private static readonly string FieldMaxFetchDistance = "MaxFetchDistance";

            private static readonly HashSet<string> KnownFields = new()
            {
                FieldPosition, FieldRotation, FieldCheckHooks, FieldCheckCooldown,
                FieldCheckBuildingBlocked, FieldCheckSpace, FieldAutoMount, FieldStartCooldown,
                FieldAutoRepair, FieldAutoFetch, FieldEnforceHelicopterLimit,
                FieldAllowWhileOccupied, FieldMaxFetchDistance,
            };

            public static bool TryParseApiOptions(Dictionary<string, object> rawOptions, out SpawnOptions parsedOptions)
            {
                parsedOptions = new SpawnOptions();
                if (!TryGetOption(rawOptions, FieldPosition, out parsedOptions.Position)
                    || !TryGetOption(rawOptions, FieldRotation, out parsedOptions.Rotation)
                    || !TryGetOption(rawOptions, FieldCheckHooks, out parsedOptions.CheckHooks)
                    || !TryGetOption(rawOptions, FieldCheckCooldown, out parsedOptions.CheckCooldown)
                    || !TryGetOption(rawOptions, FieldCheckBuildingBlocked, out parsedOptions.CheckBuildingBlocked)
                    || !TryGetOption(rawOptions, FieldCheckSpace, out parsedOptions.CheckSpace)
                    || !TryGetOption(rawOptions, FieldAutoMount, out parsedOptions.AutoMount)
                    || !TryGetOption(rawOptions, FieldStartCooldown, out parsedOptions.StartCooldown)
                    || !TryGetOption(rawOptions, FieldAutoRepair, out parsedOptions.AutoRepair)
                    || !TryGetOption(rawOptions, FieldAutoFetch, out parsedOptions.AutoFetch)
                    || !TryGetOption(rawOptions, FieldEnforceHelicopterLimit, out parsedOptions.EnforceHelicopterLimit)
                    || !TryGetOption(rawOptions, FieldAutoDespawnOtherHelicopterTypes, out parsedOptions.AutoDespawnOtherHelicopterTypes)
                    || !TryGetOption(rawOptions, FieldAllowWhileOccupied, out parsedOptions.AllowWhileOccupied)
                    || !TryGetOption(rawOptions, FieldMaxFetchDistance, out parsedOptions.MaxFetchDistance))
                    return false;

                foreach (var key in rawOptions.Keys)
                {
                    if (!KnownFields.Contains(key))
                    {
                        LogWarning($"Unknown option '{key}' in API options.Known options are: {string.Join(", ", KnownFields)}");
                    }
                }

                return true;
            }

            private static bool TryGetOption<T>(Dictionary<string, object> dict, string key, out T result)
            {
                result = default;

                // Missing fields are OK since all are optional.if (!dict.TryGetValue(key, out var value))
                    return true;

                if (value is not T valueOfType)
                {
                    LogError($"Invalid type for option '{key}': expected {typeof(T).Name}, got {value.GetType().Name}");
                    return false;
                }

                result = valueOfType;
                return true;
            }

            public Vector3? Position;
            public Quaternion? Rotation;
            public bool? CheckHooks;
            public bool? CheckCooldown;
            public bool? CheckSpace;
            public bool? CheckBuildingBlocked;
            public bool? AutoMount;
            public bool? StartCooldown;
            public bool? AutoRepair;
            public bool? AutoFetch;
            public bool? EnforceHelicopterLimit;
            public bool? AutoDespawnOtherHelicopterTypes;
            public bool? AllowWhileOccupied;
            public float? MaxFetchDistance;
        }

        #endregion

        #region Commands

        private void CommandSpawnMinicopter(IPlayer player, string cmd, string[] args)
        {
            SpawnCommandInternal(_vehicleInfoManager.Minicopter, player, cmd, args);
        }

        private void CommandSpawnScrapTransportHelicopter(IPlayer player, string cmd, string[] args)
        {
            SpawnCommandInternal(_vehicleInfoManager.ScrapTransportHelicopter, player, cmd, args);
        }

        private void CommandSpawnAttackHelicopter(IPlayer player, string cmd, string[] args)
        {
            SpawnCommandInternal(_vehicleInfoManager.AttackHelicopter, player, cmd, args);
        }

        private PlayerHelicopter AttemptSpawnOrFetchHeli(VehicleInfo vehicleInfo, IPlayer player, BasePlayer basePlayer, SpawnOptions options = default)
        {
            var heli = FindPlayerVehicle(vehicleInfo, basePlayer);
            if (heli != null)
            {
                if ((options.AutoFetch ?? vehicleInfo.Config.AutoFetch)
                    && HasPermission(player, vehicleInfo.Permissions.Fetch, VehicleInfo.All.Fetch))
                {
                    if (TryFetchVehicle(vehicleInfo, player, basePlayer, heli, options))
                        return heli;
                }
                else
                {
                    player.Reply(GetMessage(player.Id, vehicleInfo.Messages.AlreadySpawned));
                }

                return null;
            }

            if (options.EnforceHelicopterLimit ?? _config.LimitPlayersToOneHelicopterType)
            {
                foreach (var otherVehicleInfo in _vehicleInfoManager.AllVehicles)
                {
                    if (otherVehicleInfo == vehicleInfo)
                        continue;

                    var otherVehicle = otherVehicleInfo.Data.GetVehicle(player.Id);
                    if (otherVehicle == null || otherVehicle.IsDestroyed)
                        continue;

                    if (!TryDespawnConflictingHeli(otherVehicleInfo, otherVehicle, basePlayer, options))
                    {
                        player.Reply(GetMessage(player.Id, LangEntry.ErrorConflictingHeli));
                        return null;
                    }

                    otherVehicle.Kill();
                }
            }

            if (options.CheckCooldown != false
                && !VerifyOffCooldown(vehicleInfo, basePlayer, vehicleInfo.Config.SpawnCooldowns, vehicleInfo.Data.SpawnCooldowns))
                return null;

            if ((options.CheckBuildingBlocked ?? !vehicleInfo.Config.CanSpawnBuildingBlocked)
                && !VerifyNotBuildingBlocked(player, basePlayer))
                return null;

            if (options.CheckHooks != false
                && SpawnWasBlocked(vehicleInfo, basePlayer))
                return null;

            if (!VerifyValidSpawnOrFetchPosition(vehicleInfo, basePlayer, out var position, out var rotation, options))
                return null;

            heli = SpawnVehicle(vehicleInfo, basePlayer, position, rotation, options);
            if (heli == null)
                return null;

            if (options.StartCooldown != false
                && !HasPermission(basePlayer, vehicleInfo.Permissions.NoCooldown, VehicleInfo.All.NoCooldown))
            {
                _data.StartSpawnCooldown(vehicleInfo, basePlayer);
            }

            return heli;
        }

        private void SpawnCommandInternal(VehicleInfo vehicleInfo, IPlayer player, string command, string[] args)
        {
            if (vehicleInfo == null
                || !VerifyPlayer(player, out var basePlayer)
                || !VerifyPermission(player, vehicleInfo.Permissions.Spawn, VehicleInfo.All.Spawn))
                return;

            AttemptSpawnOrFetchHeli(vehicleInfo, player, basePlayer);
        }

        private void CommandFetchMinicopter(IPlayer player, string cmd, string[] args)
        {
            FetchCommandInternal(_vehicleInfoManager.Minicopter, player, cmd, args);
        }

        private void CommandFetchScrapTransportHelicopter(IPlayer player, string cmd, string[] args)
        {
            FetchCommandInternal(_vehicleInfoManager.ScrapTransportHelicopter, player, cmd, args);
        }

        private void CommandFetchAttackHelicopter(IPlayer player, string cmd, string[] args)
        {
            FetchCommandInternal(_vehicleInfoManager.AttackHelicopter, player, cmd, args);
        }

        private void FetchCommandInternal(VehicleInfo vehicleInfo, IPlayer player, string command, string[] args)
        {
            if (vehicleInfo == null
                || !VerifyPlayer(player, out var basePlayer)
                || !VerifyPermission(player, vehicleInfo.Permissions.Fetch, VehicleInfo.All.Fetch)
                || !VerifyVehicleExists(player, basePlayer, vehicleInfo, out var heli))
                return;

            TryFetchVehicle(vehicleInfo, player, basePlayer, heli);
        }

        private void CommandDespawnMinicopter(IPlayer player, string cmd, string[] args)
        {
            DespawnCommandInternal(_vehicleInfoManager.Minicopter, player, cmd, args);
        }

        private void CommandDespawnScrapTransportHelicopter(IPlayer player, string cmd, string[] args)
        {
            DespawnCommandInternal(_vehicleInfoManager.ScrapTransportHelicopter, player, cmd, args);
        }

        private void CommandDespawnAttackHelicopter(IPlayer player, string cmd, string[] args)
        {
            DespawnCommandInternal(_vehicleInfoManager.AttackHelicopter, player, cmd, args);
        }

        private void DespawnCommandInternal(VehicleInfo vehicleInfo, IPlayer player, string command, string[] args)
        {
            if (vehicleInfo == null
                || !VerifyPlayer(player, out var basePlayer)
                || !VerifyPermission(player, vehicleInfo.Permissions.Despawn, VehicleInfo.All.Despawn)
                || !VerifyVehicleExists(player, basePlayer, vehicleInfo, out var heli))
                return;

            if (!vehicleInfo.Config.CanDespawnWhileOccupied && IsHeliOccupied(heli))
            {
                player.Reply(GetMessage(player.Id, LangEntry.ErrorHeliOccupied));
                return;
            }

            if (!VerifyVehicleWithinDistance(player, basePlayer, heli, vehicleInfo.Config.MaxDespawnDistance)
                || DespawnWasBlocked(vehicleInfo, basePlayer, heli))
                return;

            heli.Kill();
        }

        // Old command for backwards compatibility.[Command("spawnmini.give")]
        private void CommandGiveMinicopter(IPlayer player, string cmd, string[] args)
        {
            GiveCommandInternal(_vehicleInfoManager.Minicopter, player, cmd, args);
        }

        private void CommandGiveScrapTransportHelicopter(IPlayer player, string cmd, string[] args)
        {
            GiveCommandInternal(_vehicleInfoManager.ScrapTransportHelicopter, player, cmd, args);
        }

        private void CommandGiveAttackHelicopter(IPlayer player, string cmd, string[] args)
        {
            GiveCommandInternal(_vehicleInfoManager.AttackHelicopter, player, cmd, args);
        }

        private void GiveCommandInternal(VehicleInfo vehicleInfo, IPlayer player, string cmd, string[] args)
        {
            if (!player.IsServer)
                return;

            if (args.Length < 1)
            {
                PrintError($"Syntax: {cmd} <name or steamid>");
                return;
            }

            var recipientPlayer = BasePlayer.Find(args[0]);
            if (recipientPlayer == null)
            {
                PrintError($"{cmd}: No player found matching '{args[0]}'");
                return;
            }

            if (args.Length > 1)
            {
                if (args.Length < 4 ||
                    !float.TryParse(args[1], out var x) ||
                    !float.TryParse(args[2], out var y) ||
                    !float.TryParse(args[3], out var z))
                {
                    Puts($"Syntax: {cmd} <name or steamid> <x> <y> <z>");
                    return;
                }

                GiveVehicle(vehicleInfo, recipientPlayer, new Vector3(x, y, z));
                return;
            }

            GiveVehicle(vehicleInfo, recipientPlayer);
        }

        #endregion

        #region Helpers/Functions

        private static class StringUtils
        {
            public static string StripPrefix(string subject, string prefix)
            {
                return subject.StartsWith(prefix) ? subject[prefix.Length..] : subject;
            }

            public static string StripPrefixes(string subject, params string[] prefixes)
            {
                foreach (var prefix in prefixes)
                {
                    subject = StripPrefix(subject, prefix);
                }

                return subject;
            }
        }

        private static class NetworkUtils
        {
            public static void SendUpdateImmediateRecursive(BaseEntity entity)
            {
                entity.SendNetworkUpdateImmediate();

                foreach (var child in entity.children)
                {
                    SendUpdateImmediateRecursive(child);
                }
            }
        }

        private static class Ddraw
        {
            public static void Sphere(BasePlayer player, Vector3 origin, float radius, Color color, float duration) =>
                player.SendConsoleCommand("ddraw.sphere", duration, color, origin, radius);

            public static void Line(BasePlayer player, Vector3 origin, Vector3 target, Color color, float duration) =>
                player.SendConsoleCommand("ddraw.line", duration, color, origin, target);

            public static void Box(BasePlayer player, Vector3 center, Quaternion rotation, Vector3 extents, Color color, float duration)
            {
                var sphereRadius = 0.1f;

                var forwardUpperLeft = center + rotation * extents.WithX(-extents.x);
                var forwardUpperRight = center + rotation * extents;
                var forwardLowerLeft = center + rotation * extents.WithX(-extents.x).WithY(-extents.y);
                var forwardLowerRight = center + rotation * extents.WithY(-extents.y);

                var backLowerRight = center + rotation * -extents.WithX(-extents.x);
                var backLowerLeft = center + rotation * -extents;
                var backUpperRight = center + rotation * -extents.WithX(-extents.x).WithY(-extents.y);
                var backUpperLeft = center + rotation * -extents.WithY(-extents.y);

                Sphere(player, forwardUpperLeft, sphereRadius, color, duration);
                Sphere(player, forwardUpperRight, sphereRadius, color, duration);
                Sphere(player, forwardLowerLeft, sphereRadius, color, duration);
                Sphere(player, forwardLowerRight, sphereRadius, color, duration);

                Sphere(player, backLowerRight, sphereRadius, color, duration);
                Sphere(player, backLowerLeft, sphereRadius, color, duration);
                Sphere(player, backUpperRight, sphereRadius, color, duration);
                Sphere(player, backUpperLeft, sphereRadius, color, duration);

                Line(player, forwardUpperLeft, forwardUpperRight, color, duration);
                Line(player, forwardLowerLeft, forwardLowerRight, color, duration);
                Line(player, forwardUpperLeft, forwardLowerLeft, color, duration);
                Line(player, forwardUpperRight, forwardLowerRight, color, duration);

                Line(player, backUpperLeft, backUpperRight, color, duration);
                Line(player, backLowerLeft, backLowerRight, color, duration);
                Line(player, backUpperLeft, backLowerLeft, color, duration);
                Line(player, backUpperRight, backLowerRight, color, duration);

                Line(player, forwardUpperLeft, backUpperLeft, color, duration);
                Line(player, forwardLowerLeft, backLowerLeft, color, duration);
                Line(player, forwardUpperRight, backUpperRight, color, duration);
                Line(player, forwardLowerRight, backLowerRight, color, duration);
            }

            public static void Box(BasePlayer player, OBB obb, Color color, float duration)
            {
                Box(player, obb.position, obb.rotation, obb.extents, color, duration);
            }
        }

        public static void LogWarning(string message) => Interface.Oxide.LogWarning($"[Spawn Heli] {message}");
        public static void LogError(string message) => Interface.Oxide.LogError($"[Spawn Heli] {message}");

        private static bool VerifyPlayer(IPlayer player, out BasePlayer basePlayer)
        {
            if (player.IsServer)
            {
                basePlayer = null;
                return false;
            }

            basePlayer = player.Object as BasePlayer;
            return true;
        }

        private static void TryMountPlayer(BasePlayer player, PlayerHelicopter heli)
        {
            foreach (var mountPoint in heli.mountPoints)
            {
                if (mountPoint.isDriver)
                {
                    mountPoint.mountable.AttemptMount(player, doMountChecks: false);
                    break;
                }
            }
        }

        private bool HasPermission(string playerId, string perm1, string perm2 = null)
        {
            return permission.UserHasPermission(playerId, perm1)
                   || (perm2 != null && permission.UserHasPermission(playerId, perm2));
        }

        private bool HasPermission(BasePlayer player, string perm1, string perm2 = null)
        {
            return HasPermission(player.UserIDString, perm1, perm2);
        }

        private bool HasPermission(IPlayer player, string perm1, string perm2 = null)
        {
            return HasPermission(player.Id, perm1, perm2);
        }

        private bool VerifyPermission(IPlayer player, string perm1, string perm2 = null)
        {
            if (HasPermission(player, perm1, perm2))
                return true;

            player.Reply(GetMessage(player.Id, LangEntry.ErrorNoPermission));
            return false;
        }

        private bool VerifyVehicleExists(IPlayer player, BasePlayer basePlayer, VehicleInfo vehicleInfo, out PlayerHelicopter heli)
        {
            heli = FindPlayerVehicle(vehicleInfo, basePlayer);
            if (heli != null)
                return true;

            player.Reply(GetMessage(player.Id, vehicleInfo.Messages.NotFound));
            return false;
        }

        private bool VerifyVehicleWithinDistance(IPlayer player, BasePlayer basePlayer, PlayerHelicopter heli, float maxDistance)
        {
            if (maxDistance < 0 || Vector3.Distance(basePlayer.transform.position, heli.transform.position) < maxDistance)
                return true;

            player.Reply(GetMessage(player.Id, LangEntry.ErrorHeliDistance));
            return false;
        }

        private static bool CheckBox(OBB obb, int layerMask, BaseEntity ignoreEntity = null)
        {
            if (ignoreEntity == null)
                return Physics.CheckBox(obb.position, obb.extents, obb.rotation, layerMask, QueryTriggerInteraction.Ignore);

            var colliderList = Pool.Get<List<Collider>>();
            Vis.Colliders(obb, colliderList, layerMask, QueryTriggerInteraction.Ignore);
            var hitSomething = false;

            foreach (var collider in colliderList)
            {
                var hitEntity = collider.ToBaseEntity();
                if (hitEntity == ignoreEntity || hitEntity?.GetParentEntity() == ignoreEntity)
                    continue;

                hitSomething = true;
                break;
            }

            Pool.FreeUnmanaged(ref colliderList);
            return hitSomething;
        }

        private bool VerifyValidSpawnOrFetchPosition(VehicleInfo vehicleInfo, BasePlayer player, out Vector3 position, out Quaternion rotation, SpawnOptions options, PlayerHelicopter existingHeli = null)
        {
            position = Vector3.zero;
            rotation = Quaternion.identity;

            if (vehicleInfo.Config.FixedSpawnDistanceConfig.Enabled)
            {
                position = options.Position ?? GetFixedPositionForPlayer(vehicleInfo, player);
                rotation = options.Rotation ?? GetFixedRotationForPlayer(vehicleInfo, player);
            }
            else if (options.Position.HasValue)
            {
                position = options.Position.Value;
            }
            else
            {
                if (!Physics.Raycast(player.eyes.HeadRay(), out var hit, Mathf.Infinity, SpawnPointLayerMask))
                {
                    player.ChatMessage(GetMessage(player.UserIDString, LangEntry.ErrorNoSpawnLocationFound));
                    return false;
                }

                if (hit.distance > vehicleInfo.Config.MaxSpawnDistance)
                {
                    player.ChatMessage(GetMessage(player.UserIDString, LangEntry.ErrorSpawnDistance));
                    return false;
                }

                position = hit.point + Vector3.up * VerticalSpawnOffset;
            }

            var mainExtents = vehicleInfo.Bounds.extents;
            var mainCenter = position + rotation * vehicleInfo.Bounds.center;
            var mainObb = new OBB(mainCenter, 2 * mainExtents, rotation);

            var playerObb = new OBB(
                mainCenter - new Vector3(0, mainExtents.y + VerticalSpawnOffset / 2f, 0),
                2 * mainExtents.WithY(VerticalSpawnOffset / 2f),
                rotation
            );

            var tailCenter = mainCenter + rotation * new Vector3(0, 0, -mainExtents.z - vehicleInfo.TailLength / 2);
            tailCenter.y = position.y + vehicleInfo.TailYOffset;
            var tailExtents = new Vector3(vehicleInfo.TailThickness, vehicleInfo.TailThickness, vehicleInfo.TailLength / 2);
            var tailObb = new OBB(tailCenter, 2 * tailExtents, rotation);

            if (_config.AdminDebugBounds && player.IsAdmin)
            {
                Ddraw.Box(player, mainObb, Color.magenta, 5f);
                Ddraw.Box(player, tailObb, Color.magenta, 5f);
                Ddraw.Box(player, playerObb, Color.cyan, 5f);
            }

            if (options.CheckSpace != false && (
                    CheckBox(mainObb, SpaceCheckLayerMask, existingHeli)
                    || CheckBox(tailObb, SpaceCheckLayerMask, existingHeli)
                    || CheckBox(playerObb, Rust.Layers.Mask.Player_Server, player)))
            {
                player.ChatMessage(GetMessage(player.UserIDString, LangEntry.InsufficientSpace));
                return false;
            }

            return true;
        }

        private bool VerifyOffCooldown(VehicleInfo vehicleInfo, BasePlayer player, CooldownConfig cooldownConfig, Dictionary<string, DateTime> cooldownMap)
        {
            if (!cooldownMap.TryGetValue(player.UserIDString, out var cooldownStart)
                || HasPermission(player.UserIDString, vehicleInfo.Permissions.NoCooldown))
                return true;

            var timeRemaining = CeilingTimeSpan(cooldownStart.AddSeconds(GetPlayerCooldownSeconds(cooldownConfig, player)) - DateTime.Now);
            if (timeRemaining.TotalSeconds <= 0)
            {
                _data.RemoveCooldown(cooldownMap, player);
                return true;
            }

            player.ChatMessage(GetMessage(player.UserIDString, LangEntry.ErrorOnCooldown, timeRemaining.ToString("g")));
            return false;
        }

        private bool VerifyNotBuildingBlocked(IPlayer player, BasePlayer basePlayer)
        {
            if (!basePlayer.IsBuildingBlocked())
                return true;

            player.Reply(GetMessage(basePlayer.UserIDString, LangEntry.ErrorBuildingBlocked));
            return false;
        }

        private static bool SpawnWasBlocked(VehicleInfo vehicleInfo, BasePlayer player)
        {
            return Interface.CallHook(vehicleInfo.Hooks.Spawn, player) is false;
        }

        private static bool FetchWasBlocked(VehicleInfo vehicleInfo, BasePlayer player, PlayerHelicopter heli)
        {
            return Interface.CallHook(vehicleInfo.Hooks.Fetch, player, heli) is false;
        }

        private static bool DespawnWasBlocked(VehicleInfo vehicleInfo, BasePlayer player, PlayerHelicopter heli)
        {
            return Interface.CallHook(vehicleInfo.Hooks.Despawn, player, heli) is false;
        }

        private static TimeSpan CeilingTimeSpan(TimeSpan timeSpan)
        {
            return new TimeSpan((long)Math.Ceiling(1.0 * timeSpan.Ticks / 10000000) * 10000000);
        }

        private static Vector3 GetFixedPositionForPlayer(VehicleInfo vehicleInfo, BasePlayer player)
        {
            var forward = player.eyes.BodyForward();
            forward.y = 0;
            return player.transform.position + forward.normalized * vehicleInfo.Config.FixedSpawnDistanceConfig.Distance + Vector3.up * VerticalSpawnOffset;
        }

        private static Quaternion GetFixedRotationForPlayer(VehicleInfo vehicleInfo, BasePlayer player)
        {
            return Quaternion.Euler(0, player.eyes.rotation.eulerAngles.y - vehicleInfo.Config.FixedSpawnDistanceConfig.RotationAngle, 0);
        }

        private static void SetupHeli(PlayerHelicopter heli)
        {
            UnityEngine.Object.Destroy(heli.GetComponent<MagnetLiftable>());
        }

        private static void EnableUnlimitedFuel(PlayerHelicopter heli)
        {
            if (heli.GetFuelSystem() is not EntityFuelSystem fuelSystem)
                return;

            fuelSystem.cachedHasFuel = true;
            fuelSystem.nextFuelCheckTime = float.MaxValue;
            fuelSystem.GetFuelContainer().SetFlag(BaseEntity.Flags.Locked, true);
        }

        private static bool AnyParentedPlayers(PlayerHelicopter heli)
        {
            foreach (var entity in heli.children)
            {
                if (entity is BasePlayer)
                    return true;
            }

            return false;
        }

        private static bool IsHeliOccupied(PlayerHelicopter heli)
        {
            return heli.AnyMounted() || AnyParentedPlayers(heli);
        }

        private static void UnparentPlayers(PlayerHelicopter heli)
        {
            var tempList = Pool.Get<List<BasePlayer>>();

            try
            {
                foreach (var entity in heli.children)
                {
                    var player = entity as BasePlayer;
                    if (player == null)
                        continue;

                    tempList.Add(player);
                }

                foreach (var player in tempList)
                {
                    player.SetParent(null, worldPositionStays: true);
                }
            }
            finally
            {
                Pool.FreeUnmanaged(ref tempList);
            }
        }

        private bool IsPlayerVehicle(PlayerHelicopter heli, out VehicleInfo vehicleInfo)
        {
            vehicleInfo = _vehicleInfoManager.GetVehicleInfo(heli);
            return vehicleInfo != null && vehicleInfo.Data.HasVehicle(heli);
        }

        private PlayerHelicopter FindPlayerVehicle(VehicleInfo vehicleInfo, BasePlayer player)
        {
            if (!vehicleInfo.Data.Vehicles.TryGetValue(player.UserIDString, out var heliNetId))
                return null;

            var heli = BaseNetworkable.serverEntities.Find(new NetworkableId(heliNetId)) as PlayerHelicopter;
            if (heli == null)
            {
                _data.UnregisterVehicle(vehicleInfo, player.UserIDString);
            }

            return heli;
        }

        private bool ShouldAutoMount(VehicleInfo vehicleInfo, BasePlayer player)
        {
            var mountConfig = vehicleInfo.Config.AutoMount;
            if (!mountConfig.Enabled)
                return false;

            return !mountConfig.RequirePermission
                   || HasPermission(player, vehicleInfo.Permissions.AutoMount, VehicleInfo.All.AutoMount);
        }

        private void MaybeAutoMount(VehicleInfo vehicleInfo, BasePlayer player, PlayerHelicopter heli, SpawnOptions options)
        {
            if (!(options.AutoMount ?? ShouldAutoMount(vehicleInfo, player)))
                return;

            TryMountPlayer(player, heli);
        }

        private bool TryFetchVehicle(VehicleInfo vehicleInfo, IPlayer player, BasePlayer basePlayer, PlayerHelicopter heli, SpawnOptions options = default)
        {
            var isOccupied = IsHeliOccupied(heli);
            var canFetchWhileOccupied = options.AllowWhileOccupied ?? vehicleInfo.Config.CanFetchWhileOccupied;
            if (isOccupied && (!canFetchWhileOccupied
                               || basePlayer.GetMountedVehicle() == heli
                               || basePlayer.GetParentEntity() == heli))
            {
                basePlayer.ChatMessage(GetMessage(basePlayer.UserIDString, LangEntry.ErrorHeliOccupied));
                return false;
            }

            var maxFetchDistance = options.MaxFetchDistance ?? vehicleInfo.Config.MaxFetchDistance;
            if (!VerifyVehicleWithinDistance(player, basePlayer, heli, maxFetchDistance))
                return false;

            if (options.CheckCooldown != false
                && !VerifyOffCooldown(vehicleInfo, basePlayer, vehicleInfo.Config.FetchCooldowns, vehicleInfo.Data.FetchCooldowns))
                return false;

            if ((options.CheckBuildingBlocked ?? !vehicleInfo.Config.CanFetchBuildingBlocked)
                && !VerifyNotBuildingBlocked(player, basePlayer))
                return false;

            if (options.CheckHooks != false
                && FetchWasBlocked(vehicleInfo, basePlayer, heli))
                return false;

            if (!VerifyValidSpawnOrFetchPosition(vehicleInfo, basePlayer, out var position, out var rotation, options, heli))
                return false;

            if (isOccupied)
            {
                foreach (var mountPoint in heli.mountPoints)
                {
                    mountPoint.mountable?.DismountAllPlayers();
                }
            }

            if (AnyParentedPlayers(heli))
            {
                UnparentPlayers(heli);
            }

            if ((options.AutoRepair ?? vehicleInfo.Config.RepairOnFetch)
                && vehicleInfo.Config.SpawnHealth > 0)
            {
                heli.SetHealth(Math.Max(heli.Health(), vehicleInfo.Config.SpawnHealth));
            }

            // Terminate on client so that it doesn't animate from the previous location, since that can hinder stealth.heli.TerminateOnClient(BaseNetworkable.DestroyMode.None);

            heli.rigidBody.velocity = Vector3.zero;
            heli.transform.SetPositionAndRotation(position, rotation);
            heli.rigidBody.WakeUp();
            heli.timeSinceLastPush = 0f;
            heli.UpdateNetworkGroup();
            NetworkUtils.SendUpdateImmediateRecursive(heli);

            if (options.StartCooldown != false
                && !HasPermission(basePlayer, vehicleInfo.Permissions.NoCooldown, VehicleInfo.All.NoCooldown))
            {
                _data.StartFetchCooldown(vehicleInfo, basePlayer);
            }

            MaybeAutoMount(vehicleInfo, basePlayer, heli, options);
            return true;
        }

        private PlayerHelicopter SpawnVehicle(VehicleInfo vehicleInfo, BasePlayer player, Vector3 position, Quaternion rotation, SpawnOptions options)
        {
            var heli = GameManager.server.CreateEntity(vehicleInfo.PrefabPath, position, rotation) as PlayerHelicopter;
            if (heli == null)
                return null;

            heli.OwnerID = player.userID;
            if (vehicleInfo.Config.SpawnHealth > 0)
            {
                heli.startHealth = vehicleInfo.Config.SpawnHealth;
            }

            SetupHeli(heli);
            heli.Spawn();

            if (HasPermission(player, vehicleInfo.Permissions.UnlimitedFuel, VehicleInfo.All.UnlimitedFuel))
            {
                EnableUnlimitedFuel(heli);
            }
            else
            {
                AddInitialFuel(vehicleInfo, heli, player);
            }

            _data.RegisterVehicle(vehicleInfo, player.UserIDString, heli);
            MaybeAutoMount(vehicleInfo, player, heli, options);

            return heli;
        }

        private void GiveVehicle(VehicleInfo vehicleInfo, BasePlayer player, Vector3? customPosition = null)
        {
            // Note: The give command does not auto fetch, but that could be changed in the future.if (FindPlayerVehicle(vehicleInfo, player) != null)
            {
                player.ChatMessage(GetMessage(player.UserIDString, vehicleInfo.Messages.AlreadySpawned));
                return;
            }

            var position = customPosition ?? GetFixedPositionForPlayer(vehicleInfo, player);
            var rotation = customPosition.HasValue ? Quaternion.identity : GetFixedRotationForPlayer(vehicleInfo, player);
            SpawnVehicle(vehicleInfo, player, position, rotation, new SpawnOptions { AutoMount = false });
        }

        private bool TryDespawnConflictingHeli(VehicleInfo vehicleInfo, PlayerHelicopter heli, BasePlayer basePlayer, SpawnOptions options)
        {
            var autoDespawn = options.AutoDespawnOtherHelicopterTypes ?? _config.AutoDespawnOtherHelicopterTypes;
            if (!autoDespawn)
                return false;

            var allowWhileOccupied = options.AllowWhileOccupied ?? vehicleInfo.Config.CanDespawnWhileOccupied;
            if (!allowWhileOccupied && IsHeliOccupied(heli))
                return false;

            if (DespawnWasBlocked(vehicleInfo, basePlayer, heli))
                return false;

            return true;
        }

        private float GetPlayerCooldownSeconds(CooldownConfig cooldownConfig, BasePlayer player)
        {
            var profileList = cooldownConfig.CooldownProfiles;
            if (profileList != null)
            {
                for (var i = profileList.Length - 1; i >= 0; i--)
                {
                    var profile = profileList[i];
                    if (profile.Permission != null && HasPermission(player, profile.Permission))
                        return profile.CooldownSeconds;
                }
            }

            return cooldownConfig.DefaultCooldown;
        }

        private int GetPlayerAllowedFuel(VehicleInfo vehicleInfo, BasePlayer player)
        {
            var fuelConfig = vehicleInfo.Config.FuelConfig;
            var profileList = fuelConfig.FuelProfiles;
            if (profileList != null)
            {
                for (var i = profileList.Length - 1; i >= 0; i--)
                {
                    var profile = profileList[i];
                    if (profile.Permission != null && HasPermission(player, profile.Permission))
                        return profile.FuelAmount;
                }
            }

            return fuelConfig.DefaultFuelAmount;
        }

        private void AddInitialFuel(VehicleInfo vehicleInfo, PlayerHelicopter heli, BasePlayer player)
        {
            var fuelAmount = GetPlayerAllowedFuel(vehicleInfo, player);
            if (fuelAmount == 0)
                return;

            if (heli.GetFuelSystem() is not EntityFuelSystem fuelSystem)
                return;

            var fuelContainer = fuelSystem.GetFuelContainer();
  
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху