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

FREE BetterElevators : 1.2.17 1.2.17

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

namespace Oxide.Plugins
{
    [Info("Better Elevators", "WhiteThunder", "1.2.17")]
    [Description("Allows elevators to be taller, faster, powerless, and more.")]
    internal class BetterElevators : CovalencePlugin
    {
        #region Fields

        private const string PermissionPowerless = "betterelevators.powerless";
        private const string PermissionLiftCounter = "betterelevators.liftcounter";

        private const string PermissionMaxFloorsPrefix = "betterelevators.maxfloors";
        private const string PermissionSpeedPrefix = "betterelevators.speed";

        private const string PrefabElevator = "assets/prefabs/deployable/elevator/elevator.prefab";
        private const string PrefabPowerCounter = "assets/prefabs/deployable/playerioents/counter/counter.prefab";

        private const int ElevatorPowerInputSlot = 2;
        private const int VanillaMaxFloors = 6;
        private const float ElevatorHeight = 3;
        private const float ElevatorLiftLocalOffsetY = 1;
        private const float MaxCounterUpdateFrequency = 0.4f;

        private readonly object False = false;

        private readonly Vector3 LiftCounterPosition = new(-1.18f, -0.16f, -0.1f);
        private readonly Quaternion LiftCounterRotation = Quaternion.Euler(0, 90, 0);

        private readonly Vector3 StaticLiftCounterPosition = new(1.183f, -0.09f, -0.92f);
        private readonly Quaternion StaticLiftCounterRotation = Quaternion.Euler(0, -90, 0);

        private readonly Dictionary<NetworkableId, Action> _liftTimerActions = new();
        private ProtectionProperties _immortalProtection;
        private Configuration _config;

        #endregion

        #region Hooks

        private void Init()
        {
            permission.RegisterPermission(PermissionPowerless, this);
            permission.RegisterPermission(PermissionLiftCounter, this);

            foreach (var maxFloorsAmount in _config.MaxFloorsRequiringPermission)
            {
                permission.RegisterPermission(GetMaxFloorsPermission(maxFloorsAmount), this);
            }

            foreach (var speedConfig in _config.SpeedsRequiringPermission)
            {
                if (!string.IsNullOrWhiteSpace(speedConfig.Name))
                {
                    permission.RegisterPermission(GetSpeedPermission(speedConfig.Name), this);
                }
            }

            Unsubscribe(nameof(OnEntitySpawned));
        }

        private void OnServerInitialized()
        {
            _immortalProtection = ScriptableObject.CreateInstance<ProtectionProperties>();
            _immortalProtection.name = "BetterElevatorsCounterProtection";
            _immortalProtection.Add(1);

            foreach (var entity in BaseNetworkable.serverEntities)
            {
                var elevator = entity as Elevator;
                if (elevator != null)
                {
                    OnEntitySpawned(elevator);
                    continue;
                }

                var lift = entity as ElevatorLift;
                if (lift != null)
                {
                    if (lift.owner is ElevatorStatic { IsTop: true } elevatorStatic && elevatorStatic.IsInvoking(elevatorStatic.UpdateFloorPositions))
                    {
                        // Fixes possible ArgumentOutOfRangeException when LiftPositionToFloor is called by
                        // OnEntitySpawned(lift), due to the floor positions not being set up yet.
                        elevatorStatic.CancelInvoke(elevatorStatic.UpdateFloorPositions);
                        elevatorStatic.UpdateFloorPositions();
                    }

                    OnEntitySpawned(lift);
                    continue;
                }
            }

            Subscribe(nameof(OnEntitySpawned));
        }

        private void Unload()
        {
            UnityEngine.Object.Destroy(_immortalProtection);

            var liftCounters = new List<PowerCounter>();

            foreach (var entity in BaseNetworkable.serverEntities)
            {
                var lift = entity as ElevatorLift;
                if (lift != null)
                {
                    if (lift is not ElevatorLiftStatic)
                    {
                        CustomParentTrigger.RemoveFromLift(lift);
                    }

                    continue;
                }

                var counter = entity as PowerCounter;
                if (counter != null)
                {
                    if (IsLiftCounter(counter))
                    {
                        liftCounters.Add(counter);
                    }

                    continue;
                }
            }

            foreach (var counter in liftCounters)
            {
                counter.Kill();
            }
        }

        private void OnEntitySpawned(Elevator elevator)
        {
            var topElevator = GetTopElevator(elevator);
            if (topElevator != null)
            {
                if (IsPowerlessElevator(topElevator))
                {
                    topElevator.UpdateHasPower(topElevator.ConsumptionAmount(), ElevatorPowerInputSlot);
                }

                MaybeToggleLiftCounter(topElevator);
            }

            // This is required to allow placement to succeed above 6 floors
            // Note: This doesn't contribute to the placement guides appearing client-side
            var elevatorSockets = elevator.GetEntityLinks().Select(link => link.socket).OfType<ConstructionSocket_Elevator>();
            foreach (var socket in elevatorSockets)
            {
                socket.MaxFloor = 999;
            }
        }

        private void OnEntitySpawned(ElevatorLift lift)
        {
            var topElevator = lift.owner;
            if (topElevator == null)
                return;

            if (lift is not ElevatorLiftStatic)
            {
                CustomParentTrigger.AddToLift(lift);
            }

            // Add a counter to the lift when it spawns
            // Check for an existing counter since this is also called when loading a save
            if (AllowLiftCounter(lift, topElevator) && GetLiftCounter(lift) == null)
            {
                AddLiftCounter(lift, topElevator.LiftPositionToFloor() + 1, topElevator.OwnerID, startPowered: ElevatorHasPower(topElevator));
            }
        }

        private object CanBuild(Planner planner, Construction construction, Construction.Target target)
        {
            if (planner == null || construction == null)
                return null;

            var elevatorBelow = target.entity as Elevator;
            if (elevatorBelow == null)
                return null;

            if (construction.deployable?.fullName != PrefabElevator)
                return null;

            var deployingPlayer = planner.GetOwnerPlayer();
            if (deployingPlayer == null)
                return null;

            var maxFloors = GetPlayerMaxFloors(deployingPlayer.UserIDString);
            if (elevatorBelow.Floor + 1 >= maxFloors)
            {
                ChatMessage(deployingPlayer, Lang.NoPermissionToFloor, maxFloors);
                return False;
            }

            return null;
        }

        private void OnEntityBuilt(Planner plan, GameObject go)
        {
            var entity = go.ToBaseEntity();
            if (entity == null)
                return;

            var topElevator = entity as Elevator;
            if (topElevator == null)
                return;

            var elevatorBelow = topElevator.GetElevatorInDirection(Elevator.Direction.Down);
            if (elevatorBelow == null)
                return;

            if (_config.EnsureConsistentOwner && elevatorBelow.OwnerID != 0)
            {
                topElevator.OwnerID = elevatorBelow.OwnerID;
            }

            if (!_config.MaintainLiftPositionWhenHeightChanges
                || !elevatorBelow.liftEntity.TryGet(true, out var lift)
                || lift == null)
                return;

            var didStopMovement = TryStopLiftMovement(lift, elevatorBelow, out var targetFloor);

            lift.SetParent(topElevator, worldPositionStays: true, sendImmediate: true);
            lift.SetOwnerElevator(topElevator);
            topElevator.liftEntity.Set(lift);
            elevatorBelow.liftEntity.Set(null);
            topElevator.ClearBusy();

            if (didStopMovement)
            {
                NextTick(() =>
                {
                    if (topElevator == null)
                        return;

                    topElevator.RequestMoveLiftTo(targetFloor, out _, topElevator);
                });
            }
        }

        private void OnEntityKill(Elevator elevator)
        {
            if (!_config.MaintainLiftPositionWhenHeightChanges || elevator == null || elevator.Floor == 0)
                return;

            var elevatorBelow = elevator.GetElevatorInDirection(Elevator.Direction.Down);
            if (elevatorBelow == null)
                return;

            var topElevator = GetTopElevator(elevator);
            if (!topElevator.liftEntity.TryGet(true, out var lift))
                return;

            // One reason for the lift being null is that another OnEntityKill call removed it from the top elevator
            if (lift == null)
                return;

            var liftFloor = topElevator.LiftPositionToFloor();

            // If the lift is above the next top elevator, allow it to be destroyed and recreated like normal
            if (liftFloor > elevatorBelow.Floor)
                return;

            var didStopMovement = TryStopLiftMovement(lift, topElevator, out var targetFloor);

            lift.SetParent(elevatorBelow, worldPositionStays: true, sendImmediate: true);
            elevatorBelow.liftEntity.Set(lift);
            topElevator.liftEntity.Set(null);
            topElevator.ClearBusy();

            if (didStopMovement)
            {
                elevatorBelow.RequestMoveLiftTo(Math.Min(targetFloor, elevatorBelow.Floor), out _, elevator);
            }
        }

        private void OnEntityKill(ElevatorLift lift)
        {
            _liftTimerActions.Remove(lift.net.ID);
        }

        private object OnElevatorMove(Elevator topElevator, int targetFloor)
        {
            if (!topElevator.liftEntity.TryGet(true, out var lift)
                || lift == null)
                return null;

            var liftFloor = topElevator.LiftPositionToFloor();
            if (targetFloor == liftFloor)
                return null;

            if (!CanElevatorMoveToFloor(topElevator, targetFloor))
                return null;

            var worldSpaceFloorPosition = topElevator.GetWorldSpaceFloorPosition(targetFloor);
            var distance = Mathf.Abs(lift.transform.position.y - worldSpaceFloorPosition.y);
            var timeToTravel = distance / topElevator.LiftSpeedPerMetre;

            if (!TryGetSpeedConfig(topElevator, out var speedConfig))
            {
                if (GetLiftCounter(lift) != null && timeToTravel > 0)
                {
                    StartUpdatingLiftCounter(lift, timeToTravel);
                }

                return null;
            }

            // Custom movement starts here.
            topElevator.OnMoveBegin();

            LeanTweenType leanTweenType;
            switch (speedConfig.EaseType)
            {
                case EaseType.Quadratic:
                    timeToTravel = Convert.ToSingle(Math.Sqrt(distance)) / speedConfig.BaseSpeed;
                    leanTweenType = LeanTweenType.easeInOutQuad;
                    break;

                case EaseType.Cubic:
                    timeToTravel = Convert.ToSingle(Math.Pow(distance, 1.0 / 3.0)) / speedConfig.BaseSpeed;
                    leanTweenType = LeanTweenType.easeInOutCubic;
                    break;

                default:
                    timeToTravel = distance / speedConfig.GetSpeedForLevels(Math.Abs(targetFloor - liftFloor));
                    leanTweenType = LeanTweenType.linear;
                    break;
            }

            LeanTween.moveY(lift.gameObject, worldSpaceFloorPosition.y, timeToTravel).setEase(leanTweenType);

            // Duplicating vanilla logic since this is replacing default movement
            if (targetFloor < topElevator.Floor)
            {
                lift.ToggleHurtTrigger(true);
            }

            using var liftScope = lift.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);
            liftScope.Set(BaseEntity.Flags.Busy, true);
            topElevator.Invoke(topElevator.ClearBusy, timeToTravel);
            lift.NotifyNewFloor(targetFloor, topElevator.Floor);

            using var elevatorScope = topElevator.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);
            elevatorScope.Set(BaseEntity.Flags.Busy, true);
            topElevator.SendChangedToRoot(forceUpdate: true);

            if (GetLiftCounter(lift) != null && timeToTravel > 0)
            {
                StartUpdatingLiftCounter(lift, timeToTravel);
            }

            return False;
        }

        private void OnEntitySaved(Elevator elevator, BaseNetworkable.SaveInfo info)
        {
            if (elevator.IsStatic)
                return;

            // This is where the magic happens... thanks to @JakeRich
            if (!info.forDisk)
            {
                info.msg.elevator.floor = 1;
            }
        }

        private object OnCounterTargetChange(PowerCounter counter, BasePlayer player, int amount)
        {
            var lift = counter.GetParentEntity() as ElevatorLift;
            if (lift == null)
                return null;

            var topElevator = lift.owner;
            if (topElevator == null)
                return null;

            // After this point, we return false to disable the default action since we know the counter is attached to an elevator
            if (topElevator.IsBusy())
                return False;

            if (!ElevatorHasPower(topElevator))
                return False;

            // The lift is parented to the top elevator so elevator.Floor is always the top floor
            var targetFloor = Math.Min(Math.Max(0, amount - 1), topElevator.Floor);
            if (player.IsBuildingBlocked() || FloorSelectionWasBlocked(lift, player, targetFloor))
                return False;

            topElevator.RequestMoveLiftTo(targetFloor, out _, topElevator);

            return False;
        }

        private object OnCounterModeToggle(PowerCounter counter, BasePlayer player, bool doShowPassthrough)
        {
            // Prevent lift counter from being toggled to "show passthrough" mode
            if (doShowPassthrough && IsLiftCounter(counter))
                return False;

            return null;
        }

        private void OnInputUpdate(Elevator elevator, int inputAmount, int inputSlot)
        {
            if (inputSlot != ElevatorPowerInputSlot)
                return;

            var topElevator = GetTopElevator(elevator);
            if (topElevator == null)
                return;

            if (!topElevator.liftEntity.TryGet(true, out var lift))
                return;

            var shouldGivePower = elevator.IsTop && IsPowerlessElevator(topElevator);
            var topElevator2 = topElevator;

            NextTick(() =>
            {
                if (shouldGivePower && topElevator2 != null && !topElevator2.IsPowered())
                {
                    topElevator2.UpdateHasPower(topElevator2.ConsumptionAmount(), ElevatorPowerInputSlot);
                }

                // Get the elevator again since the lift could have changed parent
                var nextTopElevator = lift.owner;
                if (nextTopElevator == null)
                    return;

                // Update the power state of the lift counter to match elevator power state
                MaybeToggleLiftCounter(nextTopElevator);
            });
        }

        #endregion

        #region Helper Methods

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

        private bool FloorSelectionWasBlocked(ElevatorLift lift, BasePlayer player, int targetFloor)
        {
            return Interface.CallHook("OnElevatorFloorSelect", lift, player, targetFloor) is false;
        }

        private string GetSpeedPermission(string permissionName) => $"{PermissionSpeedPrefix}.{permissionName}";

        private string GetMaxFloorsPermission(int maxFloors) => $"{PermissionMaxFloorsPrefix}.{maxFloors}";

        private bool CanElevatorMoveToFloor(Elevator topElevator, int targetFloor)
        {
            // Duplicating vanilla logic.
            if (topElevator.IsBusy())
                return false;

            if (!topElevator.IsStatic && !topElevator.IsPowered())
                return false;

            if (!topElevator.IsValidFloor(targetFloor))
                return false;

            if (!topElevator.liftEntity.TryGet(true, out var topElevatorLiftEntity)
                || !topElevatorLiftEntity.CanMove())
                return false;

            if (topElevator.LiftPositionToFloor() == targetFloor)
            {
                (topElevator as ElevatorStatic)?.OnLiftArrivedAtFloor();
                return false;
            }

            return true;
        }

        private bool TryStopLiftMovement(ElevatorLift lift, Elevator topElevator, out int targetFloor)
        {
            var tweens = LeanTween.descriptions(lift.gameObject);
            targetFloor = 0;

            if (tweens.Length == 0)
                return false;

            // we only expect one tween to be running for each elevator at a time
            var tween = tweens[0];
            var originalY = tween.to.x;

            var totalElevatorHeight = ElevatorHeight * (topElevator.Floor + 1);
            var bottomElevatorLiftPosition = topElevator.transform.position.y - totalElevatorHeight + ElevatorLiftLocalOffsetY;
            var localHeight = originalY - bottomElevatorLiftPosition;

            targetFloor = (int)(localHeight / ElevatorHeight) - 1;

            LeanTween.cancel(tween.uniqueId);
            return true;
        }

        private bool AllowLiftCounter(ElevatorLift lift, Elevator topElevator)
        {
            if (topElevator.IsStatic)
            {
                return _config.StaticElevators.EnableLiftCounter
                    && !lift.ShortPrefabName.Contains("elevator_office_lift.static")
                    && lift is not ApartmentElevatorLift;
            }

            var ownerId = topElevator.OwnerID;
            return !_config.RequirePermissionForLiftCounter
                || ownerId != 0 && permission.UserHasPermission(ownerId.ToString(), PermissionLiftCounter);
        }

        private bool AllowPowerless(Elevator topElevator)
        {
            if (topElevator.IsStatic)
                return false;

            var ownerId = topElevator.OwnerID;
            return !_config.RequirePermissionForPowerless
                || ownerId != 0 && permission.UserHasPermission(ownerId.ToString(), PermissionPowerless);
        }

        private Elevator GetTopElevator(Elevator elevator)
        {
            return GetFarthestElevatorInDirection(elevator, Elevator.Direction.Up);
        }

        private bool IsPowerlessElevator(Elevator elevator)
        {
            return AllowPowerless(elevator);
        }

        private bool IsLiftCounter(PowerCounter counter)
        {
            return counter.GetParentEntity() is ElevatorLift;
        }

        private bool ElevatorHasPower(Elevator topElevator)
        {
            return topElevator.IsStatic || topElevator.IsPowered();
        }

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

        private void HideInputsAndOutputs(IOEntity ioEntity)
        {
            // Trick to hide the inputs and outputs on the client
            foreach (var input in ioEntity.inputs)
            {
                input.type = IOEntity.IOType.Generic;
            }

            foreach (var output in ioEntity.outputs)
            {
                output.type = IOEntity.IOType.Generic;
            }
        }

        private void AddLiftCounter(ElevatorLift lift, int currentDisplayFloor, ulong ownerId, bool startPowered = false)
        {
            var position = LiftCounterPosition;
            var rotation = LiftCounterRotation;

            if (lift is ElevatorLiftStatic)
            {
                position = StaticLiftCounterPosition;
                rotation = StaticLiftCounterRotation;
            }

            var counter = GameManager.server.CreateEntity(PrefabPowerCounter, position, rotation) as PowerCounter;
            if (counter == null)
                return;

            RemoveGroundWatch(counter);
            HideInputsAndOutputs(counter);

            counter.pickup.enabled = false;
            counter.baseProtection = _immortalProtection;
            counter.OwnerID = ownerId;
            counter.SetParent(lift);
            counter.Spawn();

            if (startPowered)
            {
                InitializeCounter(counter, currentDisplayFloor);
            }
        }

        private PowerCounter GetLiftCounter(ElevatorLift lift)
        {
            foreach (var child in lift.children)
            {
                var counter = child as PowerCounter;
                if (counter != null)
                    return counter;
            }

            return null;
        }

        private void UpdateFloorCounter(ElevatorLift lift, PowerCounter counter)
        {
            // Get the elevator on every update, since the lift can be re-parented
            var topElevator = lift.owner;
            if (topElevator == null || counter == null)
                return;

            var floor = topElevator.LiftPositionToFloor() + 1;

            if (counter.counterNumber == floor)
                return;

            counter.counterNumber = floor;
            counter.targetCounterNumber = floor;
            counter.SendNetworkUpdate();
        }

        private void StartUpdatingLiftCounter(ElevatorLift lift, float timeToTravel)
        {
            var liftCounter = GetLiftCounter(lift);
            if (liftCounter == null)
                return;

            if (_liftTimerActions.TryGetValue(lift.net.ID, out var existingTimerAction))
            {
                lift.CancelInvoke(existingTimerAction);
            }

            var lastCounterUpdateTime = Time.time;
            Action timerAction = null;
            var stepsRemaining = timeToTravel / MaxCounterUpdateFrequency;
            timerAction = () =>
            {
                stepsRemaining--;

                var reachedEnd = stepsRemaining <= 0;
                if (reachedEnd || Time.time >= lastCounterUpdateTime + MaxCounterUpdateFrequency)
                {
                    UpdateFloorCounter(lift, liftCounter);
                    lastCounterUpdateTime = Time.time;
                }

                if (reachedEnd)
                {
                    lift.CancelInvoke(timerAction);
                    _liftTimerActions.Remove(lift.net.ID);
                }
            };
            lift.InvokeRepeating(timerAction, MaxCounterUpdateFrequency, MaxCounterUpdateFrequency);
            _liftTimerActions[lift.net.ID] = timerAction;
        }

        private Elevator GetFarthestElevatorInDirection(Elevator elevator, Elevator.Direction direction)
        {
            var currentElevator = elevator;

            Elevator nextElevator;
            while ((nextElevator = currentElevator.GetElevatorInDirection(direction)) != null)
            {
                currentElevator = nextElevator;
            }

            return currentElevator;
        }

        private void InitializeCounter(PowerCounter counter, int floor)
        {
            using var flagsScope = counter.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);
            flagsScope.Set(IOEntity.Flag_HasPower, true);
            flagsScope.Set(BaseEntity.Flags.Busy, false);
            counter.counterNumber = floor;
            counter.targetCounterNumber = floor;
            counter.SendNetworkUpdate();
        }

        private void ResetCounter(PowerCounter counter)
        {
            using var flagsScope = counter.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);
            flagsScope.Set(IOEntity.Flag_HasPower, false);
            flagsScope.Set(BaseEntity.Flags.Busy, true);
            counter.counterNumber = 0;
            counter.targetCounterNumber = 0;
            counter.SendNetworkUpdate();
        }

        private void MaybeToggleLiftCounter(Elevator topElevator)
        {
            if (!topElevator.liftEntity.TryGet(true, out var lift)
                || lift == null)
                return;

            var liftCounter = GetLiftCounter(lift);
            if (liftCounter == null)
                return;

            if (ElevatorHasPower(topElevator))
            {
                InitializeCounter(liftCounter, topElevator.LiftPositionToFloor() + 1);
            }
            else
            {
                ResetCounter(liftCounter);
            }
        }

        #endregion

        #region Custom Parent Trigger

        private class CustomParentTrigger : TriggerParentElevator
        {
            public static void AddToLift(ElevatorLift lift)
            {
                var originalTrigger = GetChildComponent<TriggerParentElevator>(lift);
                if (originalTrigger == null)
                    return;

                var customTrigger = originalTrigger.gameObject.AddComponent<CustomParentTrigger>();
                customTrigger._original = originalTrigger;

                customTrigger.contents = originalTrigger.contents?.ToHashSet();
                customTrigger.entityContents = originalTrigger.entityContents?.ToHashSet();

                // TriggerBase fields.
                customTrigger.interestLayers = originalTrigger.interestLayers;

                // TriggerParent fields.
                customTrigger.associatedMountable = originalTrigger.associatedMountable;
                customTrigger.parentMountedPlayers = originalTrigger.parentMountedPlayers;
                customTrigger.ParentNPCPlayers = originalTrigger.ParentNPCPlayers;
                customTrigger.overrideOtherTriggers = originalTrigger.overrideOtherTriggers;

                // TriggerParentEnclosed fields.
                customTrigger.Padding = originalTrigger.Padding;
                customTrigger.intersectionMode = originalTrigger.intersectionMode;
                customTrigger.CheckBoundsOnUnparent = originalTrigger.CheckBoundsOnUnparent;

                if (customTrigger.entityContents != null)
                {
                    foreach (var entity in customTrigger.entityContents)
                    {
                        if (entity == null)
                            continue;

                        customTrigger.OnEntityEnter(entity);
                    }
                }

                originalTrigger.enabled = false;
            }

            public static void RemoveFromLift(ElevatorLift lift)
            {
                var customTrigger = GetChildComponent<CustomParentTrigger>(lift);
                if (customTrigger == null)
                    return;

                var originalTrigger = customTrigger._original;
                originalTrigger.enabled = true;

                originalTrigger.contents = customTrigger.contents?.ToHashSet();
                originalTrigger.entityContents = customTrigger.entityContents?.ToHashSet();

                if (originalTrigger.entityContents != null)
                {
                    foreach (var entity in originalTrigger.entityContents)
                    {
                        if (entity == null)
                            continue;

                        originalTrigger.OnEntityEnter(entity);
                    }
                }

                DestroyImmediate(customTrigger);
            }

            private static T GetChildComponent<T>(UnityEngine.Component component) where T : UnityEngine.Component
            {
                foreach (Transform child in component.transform)
                {
                    var childComponent = child.GetComponent<T>();
                    if (childComponent != null && childComponent.GetType() == typeof(T))
                        return childComponent;
                }

                return null;
            }

            // Remove the Deployed layer from the clip mask to avoid issues with clipping through the elavator at high speed.
            private const int ClipMask = CLIP_CHECK_MASK & ~Rust.Layers.Mask.Deployed;

            private TriggerParentEnclosed _original;

            #if OXIDE_PUBLICIZED
            public override bool IsClipping(BaseEntity ent)
            #else
            protected override bool IsClipping(BaseEntity ent)
            #endif
            {
                if (AllowHorsesToBypassClippingChecks && ent is RidableHorse)
                    return false;

                return GamePhysics.CheckOBB(ent.WorldSpaceBounds(), ClipMask, QueryTriggerInteraction.Ignore);
            }
        }

        #endregion

        #region Configuration

        private int GetPlayerMaxFloors(string userIdString)
        {
            if (_config.MaxFloorsRequiringPermission == null || _config.MaxFloorsRequiringPermission.Length == 0)
                return _config.DefaultMaxFloors;

            for (var i = _config.MaxFloorsRequiringPermission.Length - 1; i >= 0; i--)
            {
                var floorAmount = _config.MaxFloorsRequiringPermission[i];
                if (permission.UserHasPermission(userIdString, GetMaxFloorsPermission(floorAmount)))
                    return floorAmount;
            }

            return _config.DefaultMaxFloors;
        }

        private SpeedConfig GetPlayerSpeedConfig(ulong ownerId)
        {
            if (ownerId == 0 || _config.SpeedsRequiringPermission == null || _config.SpeedsRequiringPermission.Length == 0)
                return _config.DefaultSpeed;

            var userIdString = ownerId.ToString();

            for (var i = _config.SpeedsRequiringPermission.Length - 1; i >= 0; i--)
            {
                var speedConfig = _config.SpeedsRequiringPermission[i];
                if (!string.IsNullOrWhiteSpace(speedConfig.Name) &&
                    permission.UserHasPermission(userIdString, GetSpeedPermission(speedConfig.Name)))
                {
                    return speedConfig;
                }
            }

            return _config.DefaultSpeed;
        }

        private bool TryGetSpeedConfig(Elevator topElevator, out SpeedConfig speedConfig)
        {
            if (topElevator is ElevatorStatic)
            {
                if (_config.StaticElevators.EnableCustomSpeed)
                {
                    speedConfig = _config.StaticElevators.Speed;
                    return true;
                }
            }
            else if (_config.EnableSpeedOptions)
            {
                speedConfig = GetPlayerSpeedConfig(topElevator.OwnerID);
                return true;
            }

            speedConfig = null;
            return false;
        }

        // Don't rename these since they are used in the config.
        private enum EaseType { Linear, Quadratic, Cubic }

        private class SpeedConfig
        {
            [JsonProperty("Name", DefaultValueHandling = DefaultValueHandling.Ignore)]
            public string Name;

            [JsonProperty("BaseSpeed")]
            public float BaseSpeed = 5f;

            [JsonProperty("SpeedIncreasePerFloor", DefaultValueHandling = DefaultValueHandling.Ignore)]
            public float SpeedPerAdditionalFloor = 0;

            [JsonProperty("MaxSpeed", DefaultValueHandling = DefaultValueHandling.Ignore)]
            [DefaultValue(1.5f)]
            public float MaxSpeed = 1.5f;

            [JsonProperty("EaseType")]
            [JsonConverter(typeof(StringEnumConverter))]
            public EaseType EaseType = EaseType.Linear;

            public float GetSpeedForLevels(int levels)
            {
                return Math.Min(Math.Max(BaseSpeed, MaxSpeed), BaseSpeed + (levels - 1) * SpeedPerAdditionalFloor);
            }
        }

        private class StaticElevatorConfig
        {
            [JsonProperty("EnableCustomSpeed")]
            public bool EnableCustomSpeed = false;

            [JsonProperty("Speed")]
            public SpeedConfig Speed = new()
            {
                BaseSpeed = 3.5f,
            };

            [JsonProperty("EnableLiftCounter")]
            public bool EnableLiftCounter = false;
        }

        private class Configuration : BaseConfiguration
        {
            [JsonProperty("DefaultMaxFloors")]
            public int DefaultMaxFloors = VanillaMaxFloors;

            [JsonProperty("MaxFloorsRequiringPermission")]
            public int[] MaxFloorsRequiringPermission = { 10, 15, 20, 100 };

            [JsonProperty("RequirePermissionForPowerless")]
            public bool RequirePermissionForPowerless = true;

            [JsonProperty("RequirePermissionForLiftCounter")]
            public bool RequirePermissionForLiftCounter = true;

            [JsonProperty("MaintainLiftPositionWhenHeightChanges")]
            public bool MaintainLiftPositionWhenHeightChanges = false;

            [JsonProperty("EnsureConsistentOwner")]
            public bool EnsureConsistentOwner = true;

            [JsonProperty("EnableSpeedOptions")]
            public bool EnableSpeedOptions = true;

            [JsonProperty("DefaultSpeed")]
            public SpeedConfig DefaultSpeed = new()
            {
                BaseSpeed = 5f,
            };

            [JsonProperty("SpeedsRequiringPermission")]
            public SpeedConfig[] SpeedsRequiringPermission =
            {
                new()
                {
                    Name = "2x",
                    BaseSpeed = 10f,
                },
                new()
                {
                    Name = "3x",
                    BaseSpeed = 15f,
                },
                new()
                {
                    Name = "4x",
                    BaseSpeed = 20f,
                },
                new()
                {
                    Name = "1x.quadratic",
                    BaseSpeed = 0.86f,
                    EaseType = EaseType.Quadratic,
                },
                new()
                {
                    Name = "1.5x.quadratic",
                    BaseSpeed = 1.29f,
                    EaseType = EaseType.Quadratic,
                },
                new()
                {
                    Name = "2x.quadratic",
                    BaseSpeed = 1.72f,
                    EaseType = EaseType.Quadratic,
                },
                new()
                {
                    Name = "1x.cubic",
                    BaseSpeed = 0.72f,
                    EaseType = EaseType.Cubic,
                },
                new()
                {
                    Name = "2x.cubic",
                    BaseSpeed = 1.44f,
                    EaseType = EaseType.Cubic,
                },
            };

            [JsonProperty("StaticElevators")]
            public StaticElevatorConfig StaticElevators = new();
        }

        private Configuration GetDefaultConfig() => new();

        #region Configuration Helpers

        private class BaseConfiguration
        {
            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(BaseConfiguration 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)
            {
                if (currentRaw.TryGetValue(key, out var currentRawValue))
                {
                    var currentDictValue = currentRawValue as Dictionary<string, object>;
                    if (currentWithDefaults[key] is Dictionary<string, object> defaultDictValue)
                    {
                        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

        #region Localization

        private string GetMessage(IPlayer player, string messageName, params object[] args)
        {
            var message = lang.GetMessage(messageName, this, player.Id);
            return args.Length > 0 ? string.Format(message, args) : message;
        }

        private void ChatMessage(BasePlayer player, string messageName, params object[] args)
        {
            player.ChatMessage(string.Format(GetMessage(player.IPlayer, messageName), args));
        }

        private static class Lang
        {
            public const string NoPermissionToFloor = "Deploy.Error.NoPermissionToFloor";
        }

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                [Lang.NoPermissionToFloor] = "Error: You don't have permission to build elevators taller than {0} floors.",
            }, this);

            lang.RegisterMessages(new Dictionary<string, string>
            {
                [Lang.NoPermissionToFloor] = "Erro: você não tem permissão para construir elevadores com mais de {0} andares.",
            }, this, "pt-BR");
        }

        #endregion
    }
}

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