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

FREE BuoyantHelicopters : 1.3.3 1.3.3

Нет прав для скачивания
C#:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core.Plugins;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Oxide.Core;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Buoyant Helicopters", "BlackLightning", "1.3.3")]
    [Description("Allows helicopters to float in water.")]
    internal class BuoyantHelicopters : CovalencePlugin
    {
        #region Fields

        private const string InnerTubePrefab = "assets/prefabs/misc/summer_dlc/inner_tube/innertube.deployed.prefab";

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

        private Configuration _config;
        private SavedData _data;
        private ProtectionProperties _immortalProtection;
        private readonly BuoyancyComponentManager _componentManager;
        private readonly VehicleInfoManager _vehicleInfoManager;
        private readonly TrackedCoroutine _trackedCoroutine;
        private readonly HookCollection _delayedHooks;
        private readonly HookCollection _permissionRelatedHooks;

        private List<BaseEntity> _reusableVehicleList = new List<BaseEntity>();
        private Coroutine _activeCoroutine;

        public BuoyantHelicopters()
        {
            _componentManager = new BuoyancyComponentManager(this);
            _vehicleInfoManager = new VehicleInfoManager(this);
            _trackedCoroutine = new TrackedCoroutine(this);

            _delayedHooks = new HookCollection(this, new[]
            {
                nameof(OnEntitySpawned),
                nameof(OnVehicleOwnershipChanged)
            });

            _permissionRelatedHooks = new HookCollection(this, new[]
            {
                nameof(OnServerSave),
                nameof(OnGroupPermissionGranted),
                nameof(OnGroupPermissionRevoked),
                nameof(OnUserPermissionGranted),
                nameof(OnUserPermissionRevoked),
                nameof(OnEntityMounted),
            }, () => _vehicleInfoManager.RequiresPermission);
        }

        #endregion

        #region Hooks

        private void Init()
        {
            _delayedHooks.Unsubscribe();
            _permissionRelatedHooks.Unsubscribe();
        }

        private void OnServerInitialized()
        {
            _vehicleInfoManager.OnServerInitialized();
            _immortalProtection = ScriptableObject.CreateInstance<ProtectionProperties>();
            _immortalProtection.name = $"{nameof(BuoyantHelicopters)}Protection";
            _immortalProtection.Add(1);

            if (_vehicleInfoManager.RequiresPermission)
            {
                _data = SavedData.Load();
                _data.CleanEntitiesNotFound();
            }

            RefreshBuoyancyForAllVehicles();

            _delayedHooks.Refresh();
            _permissionRelatedHooks.Refresh();
        }

        private void Unload()
        {
            _data?.SaveIfChanged();
            UnityEngine.Object.Destroy(_immortalProtection);
            CancelActiveCoroutine();
            ServerMgr.Instance.StartCoroutine(_componentManager.UnloadRoutine());
        }

        // Only subscribed while permissions are required.private void OnServerSave()
        {
            _data?.SaveIfChanged();
        }

        private void OnEntitySpawned(BaseHelicopter vehicle)
        {
            var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(vehicle);
            if (vehicleInfo == null || !vehicleInfo.Config.Enabled)
                return;

            // Copy variables after conditions to delay closure creation.var vehicle2 = vehicle;
            var vehicleInfo2 = vehicleInfo;

            // Delay since plugins may set vehicle ownership on a delay.NextTick(() =>
            {
                if (vehicle2 == null || vehicle2.IsDestroyed)
                    return;

                if (!VehicleHasPermission(vehicle2, vehicleInfo2))
                    return;

                _componentManager.EnsureBuoyancy(vehicle2, vehicleInfo2);
            });
        }

        // Only subscribed while permissions are required.private void OnEntityMounted(BaseVehicleSeat seat)
        {
            var vehicle = seat.GetParentEntity() as BaseVehicle;
            if ((object)vehicle == null)
                return;

            var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(vehicle);
            if (vehicleInfo == null || !vehicleInfo.Config.Enabled)
                return;

            RefreshBuoyancy(vehicle);
        }

        private void OnGroupPermissionGranted(string groupName, string perm) => HandlePermissionChange(perm);
        private void OnGroupPermissionRevoked(string groupName, string perm) => HandlePermissionChange(perm);
        private void OnUserPermissionGranted(string userId, string perm) => HandlePermissionChange(perm);
        private void OnUserPermissionRevoked(string userId, string perm) => HandlePermissionChange(perm);

        // Hook from ClaimVehicle plugin.private void OnVehicleOwnershipChanged(BaseCombatEntity vehicle) => RefreshBuoyancy(vehicle);

        #endregion

        #region API

        [HookMethod(nameof(API_IsBuoyant))]
        public object API_IsBuoyant(BaseCombatEntity vehicle)
        {
            return BooleanNoAlloc(_componentManager.IsBuoyant(vehicle));
        }

        [HookMethod(nameof(API_AddBuoyancy))]
        public void API_AddBuoyancy(BaseCombatEntity vehicle)
        {
            var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(vehicle);
            if (vehicleInfo == null)
                throw new InvalidOperationException($"{nameof(API_AddBuoyancy)} does not support vehicle: {vehicle.PrefabName}");

            if (vehicleInfo.Config.Enabled)
                throw new InvalidOperationException($"{nameof(API_AddBuoyancy)} cannot add buoyancy to vehicles that are disabled in the config.");

            _componentManager.EnsureBuoyancy(vehicle, vehicleInfo, force: true);
        }

        [HookMethod(nameof(API_RemoveBuoyancy))]
        public void API_RemoveBuoyancy(BaseCombatEntity vehicle)
        {
            _componentManager.RemoveBuoyancy(vehicle);
        }

        #endregion

        #region Exposed Hooks

        private static class ExposedHooks
        {
            public static object OnVehicleBuoyancyAdd(BaseEntity vehicle)
            {
                return Interface.CallHook("OnVehicleBuoyancyAdd", vehicle);
            }

            public static void OnVehicleBuoyancyAdded(BaseEntity vehicle)
            {
                Interface.CallHook("OnVehicleBuoyancyAdded", vehicle);
            }

            public static void OnVehicleBuoyancyRemoved(BaseEntity vehicle)
            {
                Interface.CallHook("OnVehicleBuoyancyRemoved", vehicle);
            }
        }

        #endregion

        #region Helpers

        private static object BooleanNoAlloc(bool value) => value ? True : False;

        private bool OwnerHasPermission(BaseEntity entity, IVehicleInfo vehicleInfo)
        {
            return entity.OwnerID != 0
                && permission.UserHasPermission(entity.OwnerID.ToString(), vehicleInfo.OwnerPermission);
        }

        private bool PilotHasPermission(BaseEntity entity, IVehicleInfo vehicleInfo)
        {
            var vehicle = entity as BaseVehicle;
            if ((object)vehicle == null)
                return false;

            var pilotUserId = vehicle.GetDriver()?.UserIDString;
            if (pilotUserId == null && !_data.TryGetLastPilot(entity, out pilotUserId))
                return false;

            return permission.UserHasPermission(pilotUserId, vehicleInfo.PilotPermission);
        }

        private bool VehicleHasPermission(BaseEntity vehicle, IVehicleInfo vehicleInfo)
        {
            if (vehicleInfo.Config.RequirePermission)
            {
                return OwnerHasPermission(vehicle, vehicleInfo)
                    || PilotHasPermission(vehicle, vehicleInfo);
            }

            return true;
        }

        private bool RefreshBuoyancy(BaseEntity vehicle)
        {
            var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(vehicle);
            if (vehicleInfo == null || !vehicleInfo.Config.Enabled)
                return false;

            if (VehicleHasPermission(vehicle, vehicleInfo))
            {
                if (_vehicleInfoManager.RequiresPermission)
                {
                    var pilot = (vehicle as BaseVehicle)?.GetDriver();
                    if (pilot != null && pilot.userID.IsSteamId())
                    {
                        _data.SetLastPilot(vehicle, pilot.UserIDString);
                    }
                }

                return _componentManager.EnsureBuoyancy(vehicle, vehicleInfo);
            }

            return !_componentManager.IsForciblyBuoyant(vehicle) && _componentManager.RemoveBuoyancy(vehicle);
        }

        private IEnumerator RefreshBuoyancyRoutine()
        {
            _reusableVehicleList.Clear();

            foreach (var networkable in BaseNetworkable.serverEntities)
            {
                var entity = networkable as BaseEntity;
                if ((object)entity == null)
                    continue;

                var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(entity);
                if (vehicleInfo == null || !vehicleInfo.Config.Enabled)
                    continue;

                _reusableVehicleList.Add(entity);
            }

            foreach (var entity in _reusableVehicleList)
            {
                if (entity == null || entity.IsDestroyed)
                    continue;

                if (RefreshBuoyancy(entity))
                    yield return null;
            }

            _reusableVehicleList.Clear();
        }

        private void CancelActiveCoroutine()
        {
            if (_activeCoroutine != null)
            {
                ServerMgr.Instance.StopCoroutine(_activeCoroutine);
            }
        }

        private void RefreshBuoyancyForAllVehicles()
        {
            CancelActiveCoroutine();
            _activeCoroutine = ServerMgr.Instance.StartCoroutine(_trackedCoroutine.WithEnumerator(RefreshBuoyancyRoutine()));
        }

        private void HandlePermissionChange(string perm)
        {
            if (!perm.StartsWith(nameof(BuoyantHelicopters), StringComparison.OrdinalIgnoreCase))
                return;

            RefreshBuoyancyForAllVehicles();
        }

        #endregion

        #region Utilities

        private class HookCollection
        {
            private BuoyantHelicopters _plugin;
            private string[] _hookNames;
            private Func<bool> _shouldSubscribe;
            private bool _isSubscribed;

            public HookCollection(BuoyantHelicopters plugin, string[] hookNames, Func<bool> shouldSubscribe = null)
            {
                _plugin = plugin;
                _hookNames = hookNames;
                _shouldSubscribe = shouldSubscribe ?? (() => true);
            }

            public void Refresh()
            {
                if (_shouldSubscribe())
                {
                    if (!_isSubscribed)
                    {
                        Subscribe();
                    }
                }
                else if (_isSubscribed)
                {
                    Unsubscribe();
                }
            }

            public void Subscribe()
            {
                foreach (var hookName in _hookNames)
                {
                    _plugin.Subscribe(hookName);
                }

                _isSubscribed = true;
            }

            public void Unsubscribe()
            {
                foreach (var hookName in _hookNames)
                {
                    _plugin.Unsubscribe(hookName);
                }

                _isSubscribed = false;
            }
        }

        private class TrackedCoroutine : IEnumerator
        {
            private Plugin _plugin;
            private IEnumerator _inner;

            public TrackedCoroutine(Plugin plugin, IEnumerator inner = null)
            {
                _plugin = plugin;
                _inner = inner;
            }

            public object Current => _inner.Current;

            public bool MoveNext()
            {
                bool result;
                _plugin.TrackStart();

                try
                {
                    result = _inner.MoveNext();
                }
                finally
                {
                    _plugin.TrackEnd();
                }

                return result;
            }

            public void Reset()
            {
                throw new NotImplementedException();
            }

            public TrackedCoroutine WithEnumerator(IEnumerator inner)
            {
                _inner = inner;
                return this;
            }
        }

        #endregion

        #region Vehicles

        private interface IVehicleInfo
        {
            string PrefabPath { get; }
            BuoyancyConfig Config { get; }
            uint PrefabId { get; }
            string OwnerPermission { get; }
            string PilotPermission { get; }
            float DefaultDrag { get; }
            float DefaultAngularDrag { get; }
            void OnServerInitialized(BuoyantHelicopters plugin);
            bool IsCorrectType(BaseEntity entity);
            Rigidbody GetRigidBody(BaseEntity entity);
        }

        private class VehicleInfo<T> : IVehicleInfo where T : BaseEntity
        {
            public string PermissionSuffix { private get; set; }
            public string PrefabPath { get; set; }
            public BuoyancyConfig Config { get; set; }
            public Func<T, Rigidbody> FindRigidBody { private get; set; }
            public uint PrefabId { get; private set; }
            public string OwnerPermission { get; private set; }
            public string PilotPermission { get; private set; }
            public float DefaultDrag { get; private set; }
            public float DefaultAngularDrag { get; private set; }

            private GameObject _prefab;

            public void OnServerInitialized(BuoyantHelicopters plugin)
            {
                OwnerPermission = $"{nameof(BuoyantHelicopters)}.owner.{PermissionSuffix}".ToLower();
                plugin.permission.RegisterPermission(OwnerPermission, plugin);

                PilotPermission = $"{nameof(BuoyantHelicopters)}.pilot.{PermissionSuffix}".ToLower();
                plugin.permission.RegisterPermission(PilotPermission, plugin);

                _prefab = GameManager.server.FindPrefab(PrefabPath);
                var defaultRigidBody = _prefab.GetComponent<Rigidbody>();
                DefaultDrag = defaultRigidBody.drag;
                DefaultAngularDrag = defaultRigidBody.angularDrag;

                var entity = _prefab.GetComponent<BaseEntity>();
                if (entity != null)
                {
                    PrefabId = entity.prefabID;
                }
            }

            public bool IsCorrectType(BaseEntity entity)
            {
                return entity is T;
            }

            public Rigidbody GetRigidBody(BaseEntity entity)
            {
                var entityOfType = entity as T;
                if ((object)entityOfType == null)
                    return null;

                return FindRigidBody(entityOfType);
            }
        }

        private class VehicleInfoManager
        {
            public bool RequiresPermission
            {
                get
                {
                    if (_allVehicles == null)
                        return false;

                    foreach (var vehicleInfo in _allVehicles)
                    {
                        if (vehicleInfo.Config.RequirePermission)
                            return true;
                    }

                    return false;
                }
            }

            private readonly BuoyantHelicopters _plugin;
            private readonly Dictionary<uint, IVehicleInfo> _prefabIdToVehicleInfo = new Dictionary<uint, IVehicleInfo>();
            private IVehicleInfo[] _allVehicles;

            private Configuration _config => _plugin._config;

            public VehicleInfoManager(BuoyantHelicopters plugin)
            {
                _plugin = plugin;
            }

            public void OnServerInitialized()
            {
                _allVehicles = new IVehicleInfo[]
                {
                    new VehicleInfo<Minicopter>
                    {
                        PermissionSuffix = "minicopter",
                        PrefabPath = "assets/content/vehicles/minicopter/minicopter.entity.prefab",
                        Config = _config.Minicopter,
                        FindRigidBody = mini => mini.rigidBody
                    },
                    new VehicleInfo<ScrapTransportHelicopter>
                    {
                        PermissionSuffix = "scraptransport",
                        PrefabPath = "assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab",
                        Config = _config.ScrapTransportHelicopter,
                        FindRigidBody = scrapHeli => scrapHeli.rigidBody,
                    },
                    new VehicleInfo<AttackHelicopter>
                    {
                        PermissionSuffix = "attackhelicopter",
                        PrefabPath = "assets/content/vehicles/attackhelicopter/attackhelicopter.entity.prefab",
                        Config = _config.AttackHelicopter,
                        FindRigidBody = heli => heli.rigidBody,
                    },
                    #if FEATURE_CH47
                    new VehicleInfo<CH47Helicopter>
                    {
                        PermissionSuffix = "chinook",
                        PrefabPath = "assets/prefabs/npc/ch47/ch47.entity.prefab",
                        Config = _config.CH47,
                        FindRigidBody = ch47 => ch47.rigidBody,
                    },
                    #endif
                };

                foreach (var vehicleInfo in _allVehicles)
                {
                    vehicleInfo.OnServerInitialized(_plugin);

                    if (vehicleInfo.PrefabId != 0)
                    {
                        _prefabIdToVehicleInfo[vehicleInfo.PrefabId] = vehicleInfo;
                    }
                    else
                    {
                        _plugin.LogError($"Unable to determine Prefab ID for prefab: {vehicleInfo.PrefabPath}");
                    }
                }
            }

            public IVehicleInfo GetVehicleInfo(BaseEntity entity)
            {
                IVehicleInfo vehicleInfo;
                return _prefabIdToVehicleInfo.TryGetValue(entity.prefabID, out vehicleInfo) && vehicleInfo.IsCorrectType(entity)
                    ? vehicleInfo
                    : null;
            }
        }

        #endregion

        #region Buoyancy

        private class BuoyancyComponent : FacepunchBehaviour
        {
            private const float DecorationPersistSeconds = 5;

            public static bool TryAddToEntity(BuoyancyComponentManager manager, BaseEntity vehicle, IVehicleInfo vehicleInfo, out BuoyancyComponent component)
            {
                component = null;

                var config = vehicleInfo.Config;
                if (config.Points == null)
                    return false;

                var gameObject = vehicle.gameObject;
                var rigidBody = vehicleInfo.GetRigidBody(vehicle);
                if (rigidBody == null)
                    return false;

                component = gameObject.AddComponent<BuoyancyComponent>();
                component._manager = manager;
                component._entity = vehicle;
                component._netId = vehicle.net.ID.Value;
                component._vehicleInfo = vehicleInfo;

                var buoyancy = gameObject.AddComponent<Buoyancy>();
                component.Buoyancy = buoyancy;
                buoyancy.rigidBody = rigidBody;
                buoyancy.buoyancyScale = 1;

                if (config.UnderwaterDrag > 0)
                {
                    buoyancy.useUnderwaterDrag = true;
                    buoyancy.underwaterDrag = config.UnderwaterDrag;
                }

                buoyancy.points = new BuoyancyPoint[config.Points.Length];

                var numDecorations = 0;
                var numDynamicDecorations = 0;
                var lowestBuoyancyPointY = float.MaxValue;

                for (var i = 0; i < config.Points.Length; i++)
                {
                    var pointConfig = config.Points[i];
                    buoyancy.points[i] = CreateBuoyancyPoint(buoyancy, pointConfig);

                    lowestBuoyancyPointY = Mathf.Min(lowestBuoyancyPointY, pointConfig.Position.y);

                    if (!pointConfig.Decoration.Enabled)
                        continue;

                    numDecorations++;

                    if (pointConfig.Decoration.Dynamic)
                    {
                        numDynamicDecorations++;
                    }
                }

                buoyancy.SavePointData(false);

                if (lowestBuoyancyPointY != float.MaxValue)
                {
                    var centerOfMass = rigidBody.centerOfMass;
                    var desiredCenterOfMassY = lowestBuoyancyPointY - 0.3f;
                    if (desiredCenterOfMassY < centerOfMass.y)
                    {
                        component._originalCenterOfMassY = centerOfMass.y;
                        component._customCenterOfMassY = desiredCenterOfMassY;
                        if (buoyancy.submergedFraction > 0)
                        {
                            component.SetCenterOfMassY(desiredCenterOfMassY);
                        }
                    }
                }

                buoyancy.SubmergedChanged = component.SubmergedChanged;

                if (numDecorations > 0)
                {
                    component._decorations = new BaseEntity[numDecorations];

                    for (var i = 0; i < config.Points.Length; i++)
                    {
                        var pointConfig = config.Points[i];
                        if (!pointConfig.Decoration.Enabled)
                            continue;

                        component._decorations[i] = CreateDecoration(manager, vehicle, pointConfig.Decoration);
                    }
                }

                if (numDynamicDecorations > 0)
                {
                    component._checkDecorations = component.CheckDecorations;
                }

                return true;
            }

            private static BuoyancyPoint CreateBuoyancyPoint(Buoyancy buoyancy, PointConfig pointConfig)
            {
                var childObject = buoyancy.gameObject.CreateChild();
                childObject.name = "Buoyancy";
                childObject.transform.localPosition = pointConfig.Position;

                var buoyancyPoint = childObject.AddComponent<BuoyancyPoint>();
                buoyancyPoint.buoyancyForce = pointConfig.Force;
                buoyancyPoint.size = pointConfig.Size;

                return buoyancyPoint;
            }

            private static BaseEntity CreateDecoration(BuoyancyComponentManager manager, BaseEntity vehicle, DecorationConfig decorationConfig)
            {
                var decorationEntity = GameManager.server.CreateEntity(decorationConfig.PrefabPath, decorationConfig.Position, decorationConfig.Rotation);
                if (decorationEntity == null)
                    return null;

                var combatEntity = decorationEntity as BaseCombatEntity;
                if (combatEntity == null)
                {
                    manager.Plugin.LogError($"Decoration entity with prefab {decorationConfig.PrefabPath} does not derive from BaseCombatEntity.");
                    Destroy(combatEntity);
                    return null;
                }

                var mountable = decorationEntity as BaseMountable;
                if ((object)mountable != null)
                {
                    mountable.mountAnchor = null;
                    mountable.isMobile = false;
                }

                DestroyImmediate(combatEntity.gameObject.GetComponent<GroundWatch>());
                DestroyImmediate(combatEntity.gameObject.GetComponent<Buoyancy>());
                DestroyImmediate(combatEntity.gameObject.GetComponent<EntityCollisionMessage>());

                var rigidBody = combatEntity.gameObject.GetComponent<Rigidbody>();
                if (rigidBody != null)
                {
                    rigidBody.useGravity = false;
                    rigidBody.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
                    rigidBody.isKinematic = true;
                }

                foreach (var collider in combatEntity.gameObject.GetComponentsInChildren<Collider>())
                {
                    DestroyImmediate(collider);
                }

                foreach (var trigger in combatEntity.gameObject.GetComponentsInChildren<TriggerBase>())
                {
                    DestroyImmediate(trigger);
                }

                combatEntity.baseProtection = manager.Plugin._immortalProtection;
                combatEntity.pickup.enabled = false;
                combatEntity.EnableSaving(false);
                combatEntity.SetFlag(BaseEntity.Flags.Busy, true);
                combatEntity.SetFlag(BaseEntity.Flags.Disabled, decorationConfig.Dynamic);
                combatEntity.SetParent(vehicle);
                combatEntity.Spawn();
                return combatEntity;
            }

            public Buoyancy Buoyancy;
            private BuoyancyComponentManager _manager;
            private BaseEntity _entity;
            private ulong _netId;
            private IVehicleInfo _vehicleInfo;
            private BaseEntity[] _decorations;
            private bool _decorationsVisible;
            private Action _checkDecorations;
            private float _originalCenterOfMassY = float.MaxValue;
            private float _customCenterOfMassY = float.MaxValue;

            public void DestroyImmediate() => DestroyImmediate(this);

            private void CheckDecorations()
            {
                var shouldShowDecorations = Buoyancy.submergedFraction > 0;
                if (_decorationsVisible == shouldShowDecorations)
                    return;

                var pointConfigList = _vehicleInfo.Config.Points;
                var decorationIndex = 0;

                foreach (var pointConfig in pointConfigList)
                {
                    var decorationConfig = pointConfig.Decoration;
                    if (!decorationConfig.Enabled)
                        continue;

                    if (decorationConfig.Dynamic)
                    {
                        var decoration = _decorations[decorationIndex];
                        if (decoration != null && !decoration.IsDestroyed)
                        {
                            decoration.SetFlag(BaseEntity.Flags.Disabled, !shouldShowDecorations);
                        }
                    }

                    decorationIndex++;
                }

                _decorationsVisible = shouldShowDecorations;
            }

            private void RestoreDefaultDrag()
            {
                var rigidbody = _vehicleInfo.GetRigidBody(_entity);
                if (rigidbody == null)
                    return;

                rigidbody.drag = _vehicleInfo.DefaultDrag;
                rigidbody.angularDrag = _vehicleInfo.DefaultAngularDrag;
            }

            private void SetCenterOfMassY(float value)
            {
                // If the value is float.MaxValue, that means
                if (value == float.MaxValue)
                    return;

                var rigidbody = _vehicleInfo.GetRigidBody(_entity);
                if (rigidbody == null)
                    return;

                rigidbody.centerOfMass = rigidbody.centerOfMass.WithY(value);
            }

            private void SubmergedChanged(bool nowSubmerged)
            {
                if (nowSubmerged)
                {
                    SetCenterOfMassY(_customCenterOfMassY);

                    if (_checkDecorations != null)
                    {
                        _checkDecorations.Invoke();
                        CancelInvoke(_checkDecorations);
                    }
                }
                else
                {
                    RestoreDefaultDrag();
                    SetCenterOfMassY(_originalCenterOfMassY);

                    if (_checkDecorations != null)
                    {
                        Invoke(_checkDecorations, DecorationPersistSeconds);
                    }
                }
            }

            private void OnDestroy()
            {
                _manager.HandleComponentDestroyed(_entity);

                if (_entity == null || _entity.IsDestroyed)
                {
                    _manager.Plugin._data?.RemoveLastPilot(_netId);
                    return;
                }

                DestroyImmediate(Buoyancy);

                foreach (var point in Buoyancy.points)
                {
                    if (point == null)
                        continue;

                    DestroyImmediate(point.gameObject);
                }

                // Restore initial drag, but only if there aren't other buoyancy components attached.if (Buoyancy.submergedFraction > 0 && _entity.GetComponent<Buoyancy>() == null)
                {
                    RestoreDefaultDrag();
                }

                if (_decorations != null)
                {
                    foreach (var decorationEntity in _decorations)
                    {
                        if (decorationEntity != null && !decorationEntity.IsDestroyed)
                        {
                            decorationEntity.Kill();
                        }
                    }
                }

                ExposedHooks.OnVehicleBuoyancyRemoved(_entity);
            }
        }

        private class BuoyancyComponentManager
        {
            private const float DebugDrawDistance = 30;
            private const float DebugDrawDuration = 30;

            public readonly BuoyantHelicopters Plugin;
            private readonly Dictionary<BaseEntity, BuoyancyComponent> _buoyancyComponents = new Dictionary<BaseEntity, BuoyancyComponent>();
            private readonly HashSet<BaseEntity> _forciblyBuoyantHelicopters = new HashSet<BaseEntity>();

            public BuoyancyComponentManager(BuoyantHelicopters plugin)
            {
                Plugin = plugin;
            }

            public bool EnsureBuoyancy(BaseEntity vehicle, IVehicleInfo vehicleInfo, bool force = false)
            {
                if (_buoyancyComponents.ContainsKey(vehicle))
                {
                    if (force)
                    {
                        _forciblyBuoyantHelicopters.Add(vehicle);
                    }

                    return false;
                }

                var hookResult = ExposedHooks.OnVehicleBuoyancyAdd(vehicle);
                if (hookResult is bool && !(bool)hookResult)
                    return false;

                BuoyancyComponent component;
                if (!BuoyancyComponent.TryAddToEntity(this, vehicle, vehicleInfo, out component))
                    return false;

                _buoyancyComponents[vehicle] = component;

                if (force)
                {
                    _forciblyBuoyantHelicopters.Add(vehicle);
                }

                ExposedHooks.OnVehicleBuoyancyAdded(vehicle);

                if (Plugin._config.AdminDebug)
                {
                    ShowAdminDebug(vehicle, component);
                }

                return true;
            }

            public bool RemoveBuoyancy(BaseEntity vehicle)
            {
                var component = GetComponent(vehicle);
                if ((object)component == null)
                    return false;

                component.DestroyImmediate();
                return true;
            }

            public bool IsBuoyant(BaseEntity vehicle)
            {
                return _buoyancyComponents.ContainsKey(vehicle);
            }

            public bool IsForciblyBuoyant(BaseEntity vehicle)
            {
                return _forciblyBuoyantHelicopters.Contains(vehicle);
            }

            public BuoyancyComponent GetComponent(BaseEntity vehicle)
            {
                BuoyancyComponent component;
                return _buoyancyComponents.TryGetValue(vehicle, out component)
                    ? component
                    : null;
            }

            public void HandleComponentDestroyed(BaseEntity vehicle)
            {
                _buoyancyComponents.Remove(vehicle);
                _forciblyBuoyantHelicopters.Remove(vehicle);
            }

            public IEnumerator UnloadRoutine()
            {
                foreach (var component in _buoyancyComponents.Values.ToArray())
                {
                    component.DestroyImmediate();
                    yield return null;
                }
            }

            private void ShowAdminDebug(BaseEntity vehicle, BuoyancyComponent component)
            {
                var entityPosition = vehicle.transform.position;

                foreach (var player in BasePlayer.activePlayerList)
                {
                    if (player.IsAdmin && Vector3.Distance(entityPosition, player.transform.position) <= DebugDrawDistance)
                    {
                        for (var i = 0; i < component.Buoyancy.points.Length; i++)
                        {
                            var point = component.Buoyancy.points[i];
                            var pointPosition = point.transform.position;
                            player.SendConsoleCommand("ddraw.sphere", DebugDrawDuration, Color.green, pointPosition, point.size);
                            player.SendConsoleCommand("ddraw.text", DebugDrawDuration, Color.green, pointPosition, $"{i+1}: {point.buoyancyForce}");
                        }
                    }
                }
            }
        }

        #endregion

        #region Data

        [JsonObject(MemberSerialization.OptIn)]
        private class SavedData
        {
            public static SavedData Load()
            {
                var data = Interface.Oxide.DataFileSystem.ReadObject<SavedData>(nameof(BuoyantHelicopters)) ?? new SavedData { _dirty = true };
                data.SaveIfChanged();
                return data;
            }

            private bool _dirty;

            [JsonProperty("LastPilot")]
            private Dictionary<ulong, string> LastPilotByVehicle = new Dictionary<ulong, string>();

            public bool TryGetLastPilot(BaseEntity vehicle, out string lastPilotId)
            {
                return LastPilotByVehicle.TryGetValue(vehicle.net.ID.Value, out lastPilotId);
            }

            public void SetLastPilot(BaseEntity vehicle, string userId)
            {
                var netId = vehicle.net.ID.Value;

                string lastPilotId;
                if (LastPilotByVehicle.TryGetValue(netId, out lastPilotId) && lastPilotId == userId)
                    return;

                LastPilotByVehicle[netId] = userId;
                _dirty = true;
            }

            public void RemoveLastPilot(ulong netId)
            {
                _dirty |= LastPilotByVehicle.Remove(netId);
            }

            public void SaveIfChanged()
            {
                if (!_dirty)
                    return;

                Interface.Oxide.DataFileSystem.WriteObject(nameof(BuoyantHelicopters), this);
                _dirty = false;
            }

            public void CleanEntitiesNotFound()
            {
                var netIdsToClean = new List<ulong>();

                foreach (var netId in LastPilotByVehicle.Keys)
                {
                    var entity = BaseNetworkable.serverEntities.Find(new NetworkableId(netId));
                    if (entity == null || entity.IsDestroyed)
                    {
                        netIdsToClean.Add(netId);
                    }
                }

                _dirty |= netIdsToClean.Count > 0;

                foreach (var netId in netIdsToClean)
                {
                    LastPilotByVehicle.Remove(netId);
                }
            }
        }

        #endregion

        #region Configuration

        [JsonObject(MemberSerialization.OptIn)]
        private class DecorationConfig
        {
            [JsonProperty("Enabled")]
            public bool Enabled;

            [JsonProperty("Dynamic")]
            public bool Dynamic;

            [JsonProperty("Position")]
            public Vector3 Position;

            [JsonProperty("Prefab")]
            public string PrefabPath;

            [JsonProperty("Rotation angles")]
            public Vector3 RotationAngles;

            public Quaternion Rotation => Quaternion.Euler(RotationAngles);
        }

        [JsonObject(MemberSerialization.OptIn)]
        private class PointConfig
        {
            [JsonProperty("Position")]
            public Vector3 Position;

            [JsonProperty("Size")]
            public float Size;

            [JsonProperty("Force")]
            public float Force;

            [JsonProperty("Decoration")]
            public DecorationConfig Decoration = new DecorationConfig();
        }

        [JsonObject(MemberSerialization.OptIn)]
        private class BuoyancyConfig
        {
            [JsonProperty("Enabled")]
            public bool Enabled;

            [JsonProperty("Require permission")]
            public bool RequirePermission;

            [JsonProperty("Require owner permission")]
            private bool DeprecatedRequireOwnerPermission { set { RequirePermission = value; } }

            [JsonProperty("Underwater drag")]
            public float UnderwaterDrag;

            [JsonProperty("Buoyancy points")]
            public PointConfig[] Points;
        }

        [JsonObject(MemberSerialization.OptIn)]
        private class Configuration : BaseConfiguration
        {
            [JsonProperty("Admin debug")]
            public bool AdminDebug;

            [JsonProperty("Minicopter")]
            public BuoyancyConfig Minicopter = new BuoyancyConfig
            {
                Enabled = true,
                UnderwaterDrag = 2,
                Points = new[]
                {
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 650,
                        Position = new Vector3(0, -0.1f, 2.1f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(0, 0.25f, 1.75f),
                        },
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 950,
                        Position = new Vector3(1.18f, -0.1f, -0.73f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(1.18f, 0.3f, -0.73f),
                            RotationAngles = new Vector3(285, 270, 0),
                        }
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 950,
                        Position = new Vector3(-1.18f, -0.1f, -0.73f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(-1.18f, 0.3f, -0.73f),
                            RotationAngles = new Vector3(285, 90, 0),
                        }
                    },
                },
            };

            [JsonProperty("Scrap Transport Helicopter")]
            public BuoyancyConfig ScrapTransportHelicopter = new BuoyancyConfig
            {
                Enabled = true,
                UnderwaterDrag = 2,
                Points = new[]
                {
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 10200,
                        Position = new Vector3(0, 0.4f, 4.2f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(0f, 1, 4.6f),
                            RotationAngles = new Vector3(270, 180, 0),
                        }
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 5100,
                        Position = new Vector3(1.83f, 0.4f, -2.58f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(1.48f, 1.1f, 0.15f),
                            RotationAngles = new Vector3(270, 270, 0),
                        }
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 5100,
                        Position = new Vector3(-1.83f, 0.4f, -2.58f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(-1.48f, 1.1f, 0.15f),
                            RotationAngles = new Vector3(270, 90, 0),
                        }
                    },
                },
            };

            [JsonProperty("Attack Helicopter")]
            public BuoyancyConfig AttackHelicopter = new BuoyancyConfig
            {
                Enabled = true,
                UnderwaterDrag = 2,
                Points = new[]
                {
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 1790,
                        Position = new Vector3(1, 0.4f, 1.725f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(0.725f, 1, 0.02f),
                            RotationAngles = new Vector3(270, 270, 0),
                        }
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 1790,
                        Position = new Vector3(-1, 0.4f, 1.725f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(-0.725f, 1, 0.02f),
                            RotationAngles = new Vector3(270, 90, 0),
                        }
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 1790,
                        Position = new Vector3(1, 0.4f, -2.275f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = false,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                        }
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 1790,
                        Position = new Vector3(-1,0.4f, -2.275f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = false,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                        }
                    },
                }
            };

            #if FEATURE_CH47
            [JsonProperty("CH47 Helicopter")]
            public BuoyancyConfig CH47 = new BuoyancyConfig
            {
                Enabled = true,
                UnderwaterDrag = 2,
                Points = new[]
                {
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 1000,
                        Position = new Vector3(-1.5f, 0.85f, 3.4f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(-2.05f, 1.5f, 3.5f),
                            RotationAngles = new Vector3(270, 0, 90),
                        },
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 1000,
                        Position = new Vector3(1.5f, 0.85f, 3.5f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(2.0f, 1.5f, 3.4f),
                            RotationAngles = new Vector3(270, 0, 270),
                        },
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 1000,
                        Position = new Vector3(1.5f, 0.85f, -3.5f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
                            PrefabPath = InnerTubePrefab,
                            Position = new Vector3(2.05f, 1.5f, -3.5f),
                            RotationAngles = new Vector3(270, 0, 270),
                        },
                    },
                    new PointConfig
                    {
                        Size = 0.2f,
                        Force = 1000,
                        Position = new Vector3(-1.5f, 0.85f, -3.5f),
                        Decoration = new DecorationConfig
                        {
                            Enabled = true,
                            Dynamic = true,
            
// ... [Code preview truncated due to length. Full source in attached file] ...

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

Другие ресурсы пользователя 15FPS

Сверху