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

FREE MiniCopterOptions : 2.5.6 2.5.6

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

namespace Oxide.Plugins
{
    [Info("Mini-Copter Options", "Pho3niX90", "2.5.6")]
    [Description("Provide a number of additional options for Mini-Copters, including storage and seats.")]
    internal class MiniCopterOptions : CovalencePlugin
    {
        #region Fields

        private readonly string minicopterPrefab = "assets/content/vehicles/minicopter/minicopter.entity.prefab";
        private readonly string storagePrefab = "assets/prefabs/deployable/hot air balloon/subents/hab_storage.prefab";
        private readonly string storageLargePrefab = "assets/content/vehicles/boats/rhib/subents/rhib_storage.prefab";
        private readonly string autoturretPrefab = "assets/prefabs/npc/autoturret/autoturret_deployed.prefab";
        private readonly string switchPrefab = "assets/prefabs/deployable/playerioents/simpleswitch/switch.prefab";
        private readonly string searchLightPrefab = "assets/prefabs/deployable/search light/searchlight.deployed.prefab";
        private readonly string flasherBluePrefab = "assets/prefabs/deployable/playerioents/lights/flasherlight/electric.flasherlight.deployed.prefab";

        private const string resizableLootPanelName = "generic_resizable";
        private const int MinStorageCapacity = 6;
        private const int MaxStorageCapacity = 48;

        private static readonly Vector3 TurretPosition = new(0, 0, 2.47f);
        private static readonly Vector3 TurretSwitchPosition = new(0, 0.36f, 0.32f);

        private static readonly Vector3 SearchLightPosition = new(0, 0.24f, 1.8f);
        private static readonly Quaternion SearchLightRotation = Quaternion.Euler(-20, 180, 180);
        private static readonly Vector3 SearchLightTransformScale = Vector3.one * 0.1f;

        private static readonly Vector3 TailLightPosition = new(0, 1.2f, -2.0f);
        private static readonly Quaternion TailLightRotation = Quaternion.Euler(33, 180, 0);

        private readonly object False = false;

        private Configuration config;
        private MiniCopterDefaults copterDefaults;
        private readonly HashSet<DroppedItemContainer> droppedItemContainers = new();
        private bool lastRanAtNight;
        private int setupTimeHooksAttempts;
        private TOD_Sky time;
        private float sunrise;
        private float sunset;
        private float lastNightCheck;
        private float[] stageFuelPerSec = new float[1];
        private float[] stageLiftFraction = new float[1];

        #endregion

        #region Hooks

        private void Init()
        {
            if (config.storageContainers > 3)
            {
                PrintWarning($"Storage Containers configuration value {config.storageContainers} exceeds the maximum, setting to 3.");
                config.storageContainers = 3;
            }
            else if (config.storageContainers < 0)
            {
                PrintWarning($"Storage Containers cannot be a negative value, setting to 0.");
                config.storageContainers = 0;
            }

            if (config.storageLargeContainers > 2)
            {
                PrintWarning($"Large Storage Containers configuration value {config.storageLargeContainers} exceeds the maximum, setting to 2.");
                config.storageLargeContainers = 2;
            }
            else if (config.storageLargeContainers < 0)
            {
                PrintWarning($"Large Storage Containers cannot be a negative value, setting to 0.");
                config.storageLargeContainers = 0;
            }

            if (config.largeStorageSize > MaxStorageCapacity)
            {
                PrintWarning($"Large Storage Containers Capacity configuration value {config.largeStorageSize} exceeds the maximum, setting to {MaxStorageCapacity}.");
                config.largeStorageSize = MaxStorageCapacity;
            }
            else if (config.largeStorageSize < MinStorageCapacity)
            {
                PrintWarning($"Storage Containers Capacity cannot be a smaller than {MinStorageCapacity}, setting to {MinStorageCapacity}.");
            }

            Unsubscribe(nameof(OnEntitySpawned));

            if (!config.autoturret)
            {
                Unsubscribe(nameof(OnTurretTarget));
            }
        }

        private void OnServerInitialized()
        {
            StoreMiniCopterDefaults();

            if (config.lightTail)
            {
                SetupTimeHooks();
            }

            if (!config.addSearchLight)
            {
                Unsubscribe(nameof(OnServerCommand));
            }

            foreach (var copter in BaseNetworkable.serverEntities.OfType<Minicopter>())
            {
                foreach (var meshCollider in copter.GetComponentsInChildren<MeshCollider>())
                {
                    UnityEngine.Object.DestroyImmediate(meshCollider);
                }

                foreach (var groundWatch in copter.GetComponentsInChildren<GroundWatch>())
                {
                    UnityEngine.Object.DestroyImmediate(groundWatch);
                }

                OnEntitySpawned(copter);
            }

            Subscribe(nameof(OnEntitySpawned));
        }

        private void Unload()
        {
            if (config.lightTail && time != null)
            {
                time.Components.Time.OnHour -= OnHour;
            }

            // If the plugin is unloaded before OnServerInitialized() ran, don't revert minicopters to 0 values.
            if (copterDefaults != null)
            {
                foreach (var copter in BaseNetworkable.serverEntities.OfType<Minicopter>())
                {
                    if (config.restoreDefaults && CanModifyMiniCopter(copter))
                    {
                        RestoreMiniCopter(copter, config.reloadStorage);
                    }
                }
            }
        }

        private void OnEntitySpawned(Minicopter copter)
        {
            ScheduleModifyMiniCopter(copter);
        }

        private void OnEntityKill(BaseCombatEntity entity)
        {
            if (entity is DroppedItemContainer droppedContainer)
            {
                droppedItemContainers.Remove(droppedContainer);
                return;
            }

            if (!config.dropStorage || entity is not Minicopter)
                return;

            var containers = entity.GetComponentsInChildren<StorageContainer>();
            foreach (var container in containers)
            {
                if (DropItems(container) is { } droppedItemContainer)
                {
                    droppedItemContainers.Add(droppedItemContainer);
                }
            }

            var turrets = entity.GetComponentsInChildren<AutoTurret>();
            foreach (var turret in turrets)
            {
                if (DropItems(turret) is { } droppedItemContainer)
                {
                    droppedItemContainers.Add(droppedItemContainer);
                }
            }
        }

        // Allow dropped item containers to be looted (by anyone) if they were dropped by this plugin.
        // Note: This approach is a little bit hacky, might be better to harmony patch OnStartBeingLooted.
        // This won't work if the plugin is reloaded (or the server is restarted) while the dropped item container is
        // still around, but that's probably an acceptable edge case (rare).
        private void CanLootEntity(BasePlayer player, DroppedItemContainer droppedItemContainer)
        {
            if (!droppedItemContainers.Contains(droppedItemContainer)
                || player.userID == droppedItemContainer.playerSteamID
                || !player.InSafeZone() && !droppedItemContainer.InSafeZone())
                return;

            droppedItemContainer.playerSteamID = player.userID;
        }

        private void OnSwitchToggled(ElectricSwitch electricSwitch, BasePlayer player)
        {
            var turret = electricSwitch.GetParentEntity() as AutoTurret;
            if (turret == null)
                return;

            var mini = turret.GetParentEntity() as Minicopter;
            if (mini == null)
            {
                // Ignore if the turret isn't on a mini, to avoid plugin conflicts.
                return;
            }

            using var flagsScope = turret.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);

            if (electricSwitch.IsOn())
            {
                flagsScope.Set(IOEntity.Flag_HasPower, true);
                turret.InitiateStartup();
            }
            else
            {
                flagsScope.Set(IOEntity.Flag_HasPower, false);
                turret.InitiateShutdown();
            }
        }

        private object OnTurretTarget(AutoTurret turret, BaseCombatEntity target)
        {
            if (target == null)
                return null;

            var mini = turret.GetParentEntity() as Minicopter;
            if (mini == null)
                return null;

            if (!config.autoTurretTargetsAnimals && target is BaseAnimalNPC)
                return False;

            var basePlayer = target as BasePlayer;
            if (basePlayer != null)
            {
                if (!config.autoTurretTargetsNPCs && basePlayer.IsNpc)
                    return False;

                if (!config.autoTurretTargetsPlayers && basePlayer.userID.IsSteamId())
                    return False;

                if (basePlayer.InSafeZone() && (basePlayer.IsNpc || !basePlayer.IsHostile()))
                    return False;
            }

            return null;
        }

        private object OnServerCommand(ConsoleSystem.Arg arg)
        {
            if (arg.Connection == null || arg.cmd.FullName != "inventory.lighttoggle")
                return null;

            var player = arg.Player();
            if (player == null)
                return null;

            var mini = player.GetMountedVehicle() as Minicopter;
            if (mini == null)
                return null;

            if (!mini.IsDriver(player))
                return null;

            foreach (var child in mini.children)
            {
                var light = child as SearchLight;
                if (light == null)
                    continue;

                using var flagsScope = light.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);
                flagsScope.Set(IOEntity.Flag_HasPower, !light.IsPowered());

                // Prevent other lights from toggling.
                return False;
            }

            return null;
        }

        private void OnEntityDismounted(BaseNetworkable entity, BasePlayer player)
        {
            if (config.flyHackPause > 0 && entity.GetParentEntity() is Minicopter)
            {
                player.PauseFlyHackDetection(config.flyHackPause);
            }
        }

        private void OnItemDeployed(Deployer deployer, StorageContainer container, BaseLock baseLock)
        {
            if (container == null || baseLock == null)
                return;

            var parent = container.GetParentEntity();
            if (parent == null || parent is not Minicopter)
                return;

            if (container.PrefabName != storageLargePrefab)
                return;

            baseLock.transform.localPosition = new Vector3(0.0f, 0.3f, 0.298f);
            baseLock.transform.localRotation = Quaternion.Euler(new Vector3(0, 90, 0));
            baseLock.SendNetworkUpdateImmediate();
        }

        // When another plugin wants to modify a specific property, adjust the proposed value, using the delta between
        // the vanilla default and the configured value.
        private void OnMinicopterFuelPerSecChange(Plugin plugin, Minicopter copter, float[] fuelPerSec)
        {
            if (plugin.Name == nameof(MiniCopterOptions)
                || copterDefaults == null
                || config.fuelPerSec < 0
                || IsFuelSystemLocked(copter)
                || !CanModifyMiniCopter(copter))
                return;

            // For example, vanilla is 0.5, configured is 0.1, so multiply by 0.2.
            fuelPerSec[0] *= config.fuelPerSec / copterDefaults.fuelPerSec;
        }

        private void OnMinicopterLiftFractionChange(Plugin plugin, Minicopter copter, float[] liftFraction)
        {
            if (plugin.Name == nameof(MiniCopterOptions)
                || copterDefaults == null
                || config.liftFraction < 0
                || !CanModifyMiniCopter(copter))
                return;

            // For example, vanilla is 0.25, configured is 0.5, so multiply by 2.
            liftFraction[0] *= config.liftFraction / copterDefaults.liftFraction;
        }

        #endregion

        #region Exposed Hooks

        private static class ExposedHooks
        {
            public static object OnMiniCopterOptions(Minicopter copter)
            {
                return Interface.CallHook("OnMiniCopterOptions", copter);
            }

            public static object OnMinicopterFuelPerSecChange(Plugin plugin, Minicopter copter, float[] fuelPerSec)
            {
                return Interface.CallHook("OnMinicopterFuelPerSecChange", plugin, copter, fuelPerSec);
            }

            public static object OnMinicopterLiftFractionChange(Plugin plugin, Minicopter copter, float[] liftFraction)
            {
                return Interface.CallHook("OnMinicopterLiftFractionChange", plugin, copter, liftFraction);
            }
        }

        #endregion

        #region Helpers

        // Copy of StorageContainer.DropItems, but returns the dropped item container if not dropped individually.
        // The `initiator` logic was also removed since it's only relevant to loot containers (e.g., loot barrels).
        public static DroppedItemContainer DropItems(IItemContainerEntity containerEntity)
        {
            var itemContainer = containerEntity.inventory;
            if (itemContainer?.itemList == null || itemContainer.itemList.Count == 0 || !containerEntity.DropsLoot)
                return null;

            if (containerEntity.ShouldDropItemsIndividually() || (itemContainer.itemList.Count == 1 && !containerEntity.DropFloats))
            {
                DropUtil.DropItems(itemContainer, containerEntity.GetDropPosition());
                return null;
            }

            var prefab = containerEntity.DropFloats ? "assets/prefabs/misc/item drop/item_drop_buoyant.prefab" : "assets/prefabs/misc/item drop/item_drop.prefab";
            return itemContainer.Drop(prefab, containerEntity.GetDropPosition(), containerEntity.Transform.rotation, containerEntity.DestroyLootPercent);
        }

        private bool IsNight()
        {
            if (time == null)
                return false;

            var hour = time.Cycle.Hour;
            return hour > sunset || hour < sunrise;
        }

        private void OnHour()
        {
            var hour = time.Cycle.Hour;
            var isNight = IsNight();

            //Puts($"OnHour: hour is now {hour}, and it is night {isNight}");
            if ((isNight == lastRanAtNight) || (lastNightCheck == hour))
                return;

            //Puts($"OnHour Called: Night:{isNight} LastRanAtNight:{lastRanAtNight}");
            lastNightCheck = hour;

            var minis = BaseNetworkable.serverEntities.OfType<Minicopter>().ToArray();
            //Puts($"OnHour Called: Minis to modify {minis.Count}");\
            foreach (var mini in minis)
            {
                var tailLight = mini.GetComponentInChildren<FlasherLight>();
                if (tailLight != null)
                {
                    using var flagsScope = tailLight.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);
                    flagsScope.Set(IOEntity.Flag_HasPower, isNight);
                }
            }

            lastRanAtNight ^= true;
        }

        private void SetupTimeHooks()
        {
            time = TOD_Sky.Instance;

            if (time == null)
            {
                if (setupTimeHooksAttempts++ >= 10)
                {
                    PrintError("Unable to detect time system. Tail light will not follow time of day.");;
                    return;
                }

                timer.Once(1, SetupTimeHooks);
                return;
            }

            sunrise = time.SunriseTime;
            sunset = time.SunsetTime;

            time.Components.Time.OnHour += OnHour;
        }

        private StorageContainer[] GetStorage(Minicopter copter)
        {
            return copter.GetComponentsInChildren<StorageContainer>()
                .Where(x => x.name == storagePrefab || x.name == storageLargePrefab)
                .ToArray();
        }

        private void AddLargeStorageBox(Minicopter copter)
        {
            if (config.storageLargeContainers == 1)
            {
                AddStorageBox(copter, storageLargePrefab, new Vector3(0.0f, 0.07f, -1.05f), Quaternion.Euler(0, 180f, 0));
            }
            else if (config.storageLargeContainers >= 2)
            {
                AddStorageBox(copter, storageLargePrefab, new Vector3(-0.48f, 0.07f, -1.05f), Quaternion.Euler(0, 180f, 0));
                AddStorageBox(copter, storageLargePrefab, new Vector3(0.48f, 0.07f, -1.05f), Quaternion.Euler(0, 180f, 0));
            }

        }

        private void AddRearStorageBox(Minicopter copter)
        {
            AddStorageBox(copter, storagePrefab, new Vector3(0, 0.75f, -1f));
        }

        private void AddSideStorageBoxes(Minicopter copter)
        {
            AddStorageBox(copter, storagePrefab, new Vector3(0.6f, 0.24f, -0.35f));
            AddStorageBox(copter, storagePrefab, new Vector3(-0.6f, 0.24f, -0.35f));
        }

        private void AddStorageBox(Minicopter copter, string prefab, Vector3 position)
        {
            AddStorageBox(copter, prefab, position, Quaternion.identity);
        }

        private void SetupStorage(StorageContainer box)
        {
            if (box.PrefabName.Equals(storageLargePrefab))
            {
                box.isLockable = config.largeStorageLockable;
                box.inventory.capacity = config.largeStorageSize;
                box.panelName = resizableLootPanelName;
            }
        }

        private void AddStorageBox(Minicopter copter, string prefab, Vector3 position, Quaternion rotation)
        {
            var box = GameManager.server.CreateEntity(prefab, position, rotation) as StorageContainer;
            if (box == null)
                return;

            box.Spawn();
            box.SetParent(copter);

            SetupStorage(box);
            box.SendNetworkUpdateImmediate();
        }

        private void SetupInvincibility(BaseCombatEntity entity)
        {
            entity._maxHealth = 99999999f;
            entity._health = 99999999f;
            entity.SendNetworkUpdate();
        }

        private void SetupTailLight(FlasherLight tailLight)
        {
            tailLight.pickup.enabled = false;
            DestroyGroundComp(tailLight);
            using var flagsScope = tailLight.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);
            flagsScope.Set(IOEntity.Flag_HasPower, IsNight());
        }

        private void AddTailLight(Minicopter copter)
        {
            var tailLight = GameManager.server.CreateEntity(flasherBluePrefab, TailLightPosition, TailLightRotation) as FlasherLight;
            if (tailLight == null)
                return;

            SetupTailLight(tailLight);
            tailLight.SetParent(copter);
            tailLight.Spawn();
            SetupInvincibility(tailLight);
        }

        private void SetupSearchLight(SearchLight searchLight)
        {
            searchLight.pickup.enabled = false;
            DestroyMeshCollider(searchLight);
            DestroyGroundComp(searchLight);
        }

        private void AddSearchLight(Minicopter copter)
        {
            var searchLight = GameManager.server.CreateEntity(searchLightPrefab, SearchLightPosition, SearchLightRotation) as SearchLight;
            if (searchLight == null)
                return;

            searchLight.transform.localScale = SearchLightTransformScale;
            searchLight.networkEntityScale = true;
            searchLight.SetParent(copter);
            SetupSearchLight(searchLight);
            searchLight.Spawn();

            SetupInvincibility(searchLight);

            using var flagsScope = searchLight.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);
            flagsScope.Set(BaseEntity.Flags.Reserved5, true);
            flagsScope.Set(BaseEntity.Flags.Busy, true);
        }

        private void SetupAutoTurret(AutoTurret turret)
        {
            turret.pickup.enabled = false;
            turret.sightRange = config.turretRange;
            DestroyMeshCollider(turret);
            DestroyGroundComp(turret);
        }

        private void AddTurret(Minicopter copter)
        {
            var turret = GameManager.server.CreateEntity(autoturretPrefab, TurretPosition) as AutoTurret;
            if (turret == null)
                return;

            SetupAutoTurret(turret);
            turret.SetParent(copter);
            turret.Spawn();

            var player = BasePlayer.FindByID(copter.OwnerID);
            if (player != null)
            {
                turret.authorizedPlayers.Add(player.userID);
                turret.UpdateMaxAuthCapacity();
                turret.SendNetworkUpdate();
            }

            AddSwitch(turret);
        }

        private void SetupSwitch(ElectricSwitch electricSwitch)
        {
            electricSwitch.pickup.enabled = false;
            DestroyMeshCollider(electricSwitch);
            DestroyGroundComp(electricSwitch);

            if (electricSwitch.HasParent())
            {
                var transform = electricSwitch.transform;
                if (transform.localPosition != TurretSwitchPosition)
                {
                    transform.localPosition = TurretSwitchPosition;
                    electricSwitch.InvalidateNetworkCache();
                    electricSwitch.SendNetworkUpdate_Position();
                }
            }
        }

        private void AddSwitch(AutoTurret turret)
        {
            var turretTransform = turret.transform;
            var switchPosition = turretTransform.TransformPoint(TurretSwitchPosition);
            var switchRotation = turretTransform.rotation;

            var electricSwitch = GameManager.server.CreateEntity(switchPrefab, switchPosition, switchRotation) as ElectricSwitch;
            if (electricSwitch != null)
            {
                SetupSwitch(electricSwitch);
                electricSwitch.Spawn();
                SetupInvincibility(electricSwitch);

                // Spawning the switch at the desired world position and then parenting it, allows it to render correctly initially.
                electricSwitch.SetParent(turret, worldPositionStays: true);
            }
        }

        private void DestroyGroundComp(BaseEntity ent)
        {
            UnityEngine.Object.DestroyImmediate(ent.GetComponent<DestroyOnGroundMissing>());
            UnityEngine.Object.DestroyImmediate(ent.GetComponent<GroundWatch>());
        }

        private void DestroyMeshCollider(BaseEntity ent)
        {
            foreach (var mesh in ent.GetComponentsInChildren<MeshCollider>())
            {
                UnityEngine.Object.DestroyImmediate(mesh);
            }
        }

        private void RestoreMiniCopter(Minicopter copter, bool removeStorage = false)
        {
            if (copterDefaults != null)
            {
                var proposedFuelPerSec = copterDefaults.fuelPerSec;
                if (config.fuelPerSec >= 0 && CanModifyFuelPerSec(copter, ref proposedFuelPerSec))
                {
                    copter.fuelPerSec = proposedFuelPerSec;
                }

                var proposedLiftFraction = copterDefaults.liftFraction;
                if (config.liftFraction >= 0 && CanModifyLiftFraction(copter, ref proposedLiftFraction))
                {
                    copter.liftFraction = proposedLiftFraction;
                }

                if (config.torqueScalePitch >= 0)
                {
                    copter.torqueScale.x = copterDefaults.torqueScale.x;
                }

                if (config.torqueScaleYaw >= 0)
                {
                    copter.torqueScale.y = copterDefaults.torqueScale.y;
                }

                if (config.torqueScaleRoll >= 0)
                {
                    copter.torqueScale.z = copterDefaults.torqueScale.z;
                }
            }

            if (removeStorage)
            {
                foreach (var child in copter.children.FindAll(child => child.name == storagePrefab || child.name == storageLargePrefab || child.name == autoturretPrefab))
                {
                    child.Kill();
                }
            }
        }

        private void ModifyMiniCopter(Minicopter copter)
        {
            if (copterDefaults != null)
            {
                var proposedFuelPerSec = config.fuelPerSec;
                if (config.fuelPerSec >= 0 && CanModifyFuelPerSec(copter, ref proposedFuelPerSec))
                {
                    copter.fuelPerSec = proposedFuelPerSec;
                }

                var proposedLiftFraction = config.liftFraction;
                if (config.liftFraction >= 0 && CanModifyLiftFraction(copter, ref proposedLiftFraction))
                {
                    copter.liftFraction = proposedLiftFraction;
                }

                if (config.torqueScalePitch >= 0)
                {
                    copter.torqueScale.x = config.torqueScalePitch;
                }

                if (config.torqueScaleYaw >= 0)
                {
                    copter.torqueScale.y = config.torqueScaleYaw;
                }

                if (config.torqueScaleRoll >= 0)
                {
                    copter.torqueScale.z = config.torqueScaleRoll;
                }
            }

            // Override the inertia tensor since if the server had rebooted while there were attachments, it would have been snapshotted to an unreasonable value.
            // This is the vanilla amount while the prevent building object is inactive.
            // To determine this value, simply deactivate the prevent building object, call rigidBody.ResetInertiaTensor(), then print the value.
            copter.rigidBody.inertiaTensor = new Vector3(407.1f, 279.6f, 173.2f);

            if (config.autoturret)
            {
                // Setup existing turret, or add a new one.
                var turret = copter.GetComponentInChildren<AutoTurret>();
                if (turret != null)
                {
                    SetupAutoTurret(turret);

                    // Setup existing switch, but don't add a new one.
                    var turretSwitch = turret.GetComponentInChildren<ElectricSwitch>();
                    if (turretSwitch != null)
                    {
                        SetupSwitch(turretSwitch);
                    }
                }
                else
                {
                    AddTurret(copter);
                }
            }

            var existingStorage = GetStorage(copter);
            if (existingStorage.Length > 0)
            {
                // Existing storage found, update its state and don't add any more storage.
                foreach (var storage in existingStorage)
                {
                    SetupStorage(storage);
                }
            }
            else
            {
                // Add storage since none was found.
                AddLargeStorageBox(copter);

                switch (config.storageContainers)
                {
                    case 1:
                        AddRearStorageBox(copter);
                        break;
                    case 2:
                        AddSideStorageBoxes(copter);
                        break;
                    case 3:
                        AddRearStorageBox(copter);
                        AddSideStorageBoxes(copter);
                        break;
                }
            }

            if (config.addSearchLight)
            {
                // Setup existing search light, or add a new one.
                var searchLight = copter.GetComponentInChildren<SearchLight>();
                if (searchLight != null)
                {
                    // Remove the legacy sphere used to resize the search light.
                    var sphereEntity = searchLight.GetParentEntity() as SphereEntity;
                    if (sphereEntity != null)
                    {
                        searchLight.SetParent(copter, worldPositionStays: true, sendImmediate: true);
                        sphereEntity.Kill();
                    }

                    if (!searchLight.networkEntityScale || searchLight.transform.localScale != SearchLightTransformScale)
                    {
                        searchLight.transform.localScale = SearchLightTransformScale;
                        searchLight.networkEntityScale = true;
                        searchLight.SendNetworkUpdate();
                    }

                    SetupSearchLight(searchLight);
                    SetupInvincibility(searchLight);
                }
                else
                {
                    AddSearchLight(copter);
                }
            }

            if (config.lightTail)
            {
                // Setup existing tail light, or add a new one.
                var tailLight = copter.GetComponentInChildren<FlasherLight>();
                if (tailLight != null)
                {
                    SetupTailLight(tailLight);
                    SetupInvincibility(tailLight);
                }
                else
                {
                    AddTailLight(copter);
                }
            }
        }

        private bool CanModifyMiniCopter(Minicopter copter)
        {
            if (ExposedHooks.OnMiniCopterOptions(copter) is false)
                return false;

            return true;
        }

        private bool IsFuelSystemLocked(Minicopter copter)
        {
            return copter.GetFuelSystem() is EntityFuelSystem fuelSystem
                   && fuelSystem.GetFuelContainer() is { } container
                   && container.IsLocked();
        }

        private bool CanModifyFuelPerSec(Minicopter copter, ref float proposedFuelPerSec)
        {
            if (IsFuelSystemLocked(copter))
                return false;

            stageFuelPerSec[0] = proposedFuelPerSec;

            if (ExposedHooks.OnMinicopterFuelPerSecChange(this, copter, stageFuelPerSec) is false)
                return false;

            proposedFuelPerSec = stageFuelPerSec[0];
            return true;
        }

        private bool CanModifyLiftFraction(Minicopter copter, ref float proposedLiftFraction)
        {
            stageLiftFraction[0] = proposedLiftFraction;

            if (ExposedHooks.OnMinicopterLiftFractionChange(this, copter, stageLiftFraction) is false)
                return false;

            proposedLiftFraction = stageLiftFraction[0];
            return true;
        }

        private void ScheduleModifyMiniCopter(Minicopter copter)
        {
            // Delay to allow plugins to detect the Mini and save its ID, so they can block modification via hooks.
            NextTick(() =>
            {
                if (copter == null || copter.IsDestroyed)
                    return;

                if (!CanModifyMiniCopter(copter))
                    return;

                ModifyMiniCopter(copter);
            });
        }

        private void StoreMiniCopterDefaults()
        {
            var copter = GameManager.server.FindPrefab(minicopterPrefab)?.GetComponent<Minicopter>();
            if (copter == null)
                return;

            //Puts($"Defaults for copters saved as \nfuelPerSecond = {copter.fuelPerSec}\nliftFraction = {copter.liftFraction}\ntorqueScale = {copter.torqueScale}");
            copterDefaults = new MiniCopterDefaults
            {
                fuelPerSec = copter.fuelPerSec,
                liftFraction = copter.liftFraction,
                torqueScale = copter.torqueScale
            };
        }

        #endregion

        #region Configuration

        private class MiniCopterDefaults
        {
            public float fuelPerSec;
            public float liftFraction;
            public Vector3 torqueScale;
        }

        private class Configuration : SerializableConfiguration
        {
            // Populated with Rust defaults.
            [JsonProperty("Fuel per Second")]
            public float fuelPerSec = 0.5f;

            [JsonProperty("Lift Fraction")]
            public float liftFraction = 0.25f;

            [JsonProperty("Pitch Torque Scale")]
            public float torqueScalePitch = 400f;

            [JsonProperty("Yaw Torque Scale")]
            public float torqueScaleYaw = 400f;

            [JsonProperty("Roll Torque Scale")]
            public float torqueScaleRoll = 200f;

            [JsonProperty("Storage Containers")]
            public int storageContainers = 0;

            [JsonProperty("Large Storage Containers")]
            public int storageLargeContainers = 0;

            [JsonProperty("Restore Defaults")]
            public bool restoreDefaults = true;

            [JsonProperty("Reload Storage")]
            public bool reloadStorage = false;

            [JsonProperty("Drop Storage Loot On Death")]
            public bool dropStorage = true;

            [JsonProperty("Large Storage Lockable")]
            public bool largeStorageLockable = true;

            [JsonProperty("Large Storage Size (Max 48)")]
            public int largeStorageSize = 48;

            [JsonProperty("Large Storage Size (Max 42)")]
            public int largeStorageSizeDeprecated {
                set => largeStorageSize = value;
            }

            [JsonProperty("Seconds to pause flyhack when dismount from heli.")]
            public int flyHackPause = 1;

            [JsonProperty("Add auto turret to heli")]
            public bool autoturret = false;

            [JsonProperty("Auto turret targets players")]
            public bool autoTurretTargetsPlayers = true;

            [JsonProperty("Auto turret targets NPCs")]
            public bool autoTurretTargetsNPCs = true;

            [JsonProperty("Auto turret targets animals")]
            public bool autoTurretTargetsAnimals = true;

            [JsonProperty("Mini Turret Range (Default 30)")]
            public float turretRange = 30f;

            [JsonProperty("Light: Add Searchlight to heli")]
            public bool addSearchLight = true;

            [JsonProperty("Light: Add Nightitme Tail Light")]
            public bool lightTail = false;
        }

        private Configuration GetDefaultConfig() => new();

        #region Configuration Helpers

        private class SerializableConfiguration
        {
            public string ToJson() => JsonConvert.SerializeObject(this);

            public Dictionary<string, object> ToDictionary() => JsonHelper.Deserialize(ToJson()) as Dictionary<string, object>;
        }

        private static class JsonHelper
        {
            public static object Deserialize(string json) => ToObject(JToken.Parse(json));

            private static object ToObject(JToken token)
            {
                switch (token.Type)
                {
                    case JTokenType.Object:
                        return token.Children<JProperty>()
                                    .ToDictionary(prop => prop.Name,
                                                  prop => ToObject(prop.Value));

                    case JTokenType.Array:
                        return token.Select(ToObject).ToList();

                    default:
                        return ((JValue)token).Value;
                }
            }
        }

        private bool MaybeUpdateConfig(SerializableConfiguration config)
        {
            var currentWithDefaults = config.ToDictionary();
            var currentRaw = Config.ToDictionary(x => x.Key, x => x.Value);
            return MaybeUpdateConfigDict(currentWithDefaults, currentRaw);
        }

        private bool MaybeUpdateConfigDict(Dictionary<string, object> currentWithDefaults, Dictionary<string, object> currentRaw)
        {
            var changed = false;

            foreach (var key in currentWithDefaults.Keys)
            {
                object currentRawValue;
                if (currentRaw.TryGetValue(key, out currentRawValue))
                {
                    var defaultDictValue = currentWithDefaults[key] as Dictionary<string, object>;
                    var currentDictValue = currentRawValue as Dictionary<string, object>;

                    if (defaultDictValue != null)
                    {
                        if (currentDictValue == null)
                        {
                            currentRaw[key] = currentWithDefaults[key];
                            changed = true;
                        }
                        else if (MaybeUpdateConfigDict(defaultDictValue, currentDictValue))
                            changed = true;
                    }
                }
                else
                {
                    currentRaw[key] = currentWithDefaults[key];
                    changed = true;
                }
            }

            return changed;
        }

        protected override void LoadDefaultConfig() => config = GetDefaultConfig();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null)
                {
                    throw new JsonException();
                }

                if (MaybeUpdateConfig(config))
                {
                    LogWarning("Configuration appears to be outdated; updating and saving");
                    SaveConfig();
                }
            }
            catch (Exception e)
            {
                LogError(e.Message);
                LogWarning($"Configuration file {Name}.json is invalid; using defaults");
                LoadDefaultConfig();
            }
        }

        protected override void SaveConfig()
        {
            Log($"Configuration changes saved to {Name}.json");
            Config.WriteObject(config, true);
        }

        #endregion

        #endregion
    }
}

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