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

FREE VehicleDeployedLocks : 1.14.3 1.14.3

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

namespace Oxide.Plugins
{
    [Info("Vehicle Deployed Locks", "WhiteThunder", "1.14.3")]
    [Description("Allows players to deploy code locks and key locks to vehicles.")]
    internal class VehicleDeployedLocks : CovalencePlugin
    {
        #region Fields

        [PluginReference]
        private Plugin Clans, Friends;

        private const string Permission_CodeLock_Prefix = "vehicledeployedlocks.codelock";
        private const string Permission_KeyLock_Prefix = "vehicledeployedlocks.keylock";
        private const string Permission_MasterKey = "vehicledeployedlocks.masterkey";

        private const string Prefab_CodeLock_DeployedEffect = "assets/prefabs/locks/keypad/effects/lock-code-deploy.prefab";
        private const string Prefab_CodeLock_DeniedEffect = "assets/prefabs/locks/keypad/effects/lock.code.denied.prefab";
        private const string Prefab_CodeLock_UnlockEffect = "assets/prefabs/locks/keypad/effects/lock.code.unlock.prefab";

        private const float MaxDeployDistance = 3;

        private Configuration _config;

        private CooldownManager _craftCodeLockCooldowns;
        private CooldownManager _craftKeyLockCooldowns;

        private readonly VehicleInfoManager _vehicleInfoManager;
        private readonly LockedVehicleTracker _lockedVehicleTracker;
        private readonly AutoUnlockManager _autoUnlockManager;
        private readonly ReskinManager _reskinManager;

        private readonly object False = false;

        private enum PayType { Item, Resources, Free }

        public VehicleDeployedLocks()
        {
            _vehicleInfoManager = new VehicleInfoManager(this);
            _lockedVehicleTracker = new LockedVehicleTracker(_vehicleInfoManager);
            _autoUnlockManager = new AutoUnlockManager(this, _lockedVehicleTracker);
            _reskinManager = new ReskinManager(_vehicleInfoManager, _lockedVehicleTracker);
        }

        #endregion

        #region Hooks

        private void Init()
        {
            permission.RegisterPermission(Permission_MasterKey, this);
            permission.RegisterPermission(LockType.Code.PermissionFree, this);
            permission.RegisterPermission(LockType.Code.PermissionAllVehicles, this);
            permission.RegisterPermission(LockType.Key.PermissionFree, this);
            permission.RegisterPermission(LockType.Key.PermissionAllVehicles, this);

            _craftKeyLockCooldowns = new CooldownManager(_config.CraftCooldownSeconds);
            _craftCodeLockCooldowns = new CooldownManager(_config.CraftCooldownSeconds);

            if (_config.AllowPushWhileLockedOut)
            {
                Unsubscribe(nameof(OnVehiclePush));
            }

            Unsubscribe(nameof(OnEntityKill));
        }

        private void OnServerInitialized()
        {
            _vehicleInfoManager.OnServerInitialized();
            _lockedVehicleTracker.OnServerInitialized();
            _autoUnlockManager.OnServerInitialized(_config.AutoUnlockSettings);

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

                    var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(entity);
                    if (vehicleInfo == null)
                        continue;

                    var lockEntity = GetVehicleLock(entity);
                    if (lockEntity == null)
                        continue;

                    var transform = lockEntity.transform;
                    transform.localPosition = vehicleInfo.LockPosition;
                    transform.localRotation = vehicleInfo.LockRotation;
                    lockEntity.SendNetworkUpdate_Position();
                }
            }

            Subscribe(nameof(OnEntityKill));
        }

        private object CanMountEntity(BasePlayer player, BaseMountable entity)
        {
            // Don't lock taxi modules
            if (entity is ModularCarSeat carSeat && IsTaxiSeat(carSeat))
                return null;

            return CanPlayerInteractWithParentVehicle(player, entity);
        }

        private object CanLootEntity(BasePlayer player, StorageContainer container)
        {
            // Don't lock taxi module shopfronts
            if (container is ModularVehicleShopFront)
                return null;

            return CanPlayerInteractWithParentVehicle(player, container);
        }

        private object CanLootEntity(BasePlayer player, ContainerIOEntity container)
        {
            return CanPlayerInteractWithParentVehicle(player, container);
        }

        private object CanLootEntity(BasePlayer player, RidableHorse horse)
        {
            return CanPlayerInteractWithVehicle(player, horse);
        }

        private object CanLootEntity(BasePlayer player, ModularCarGarage carLift)
        {
            if (carLift == null
                || _config.ModularCarSettings.AllowEditingWhileLockedOut
                || !carLift.PlatformIsOccupied)
                return null;

            return CanPlayerInteractWithVehicle(player, carLift.carOccupant);
        }

        private object OnHorseLead(RidableHorse horse, BasePlayer player)
        {
            return CanPlayerInteractWithVehicle(player, horse);
        }

        private object OnHotAirBalloonToggle(HotAirBalloon hab, BasePlayer player)
        {
            return CanPlayerInteractWithVehicle(player, hab);
        }

        private object OnSwitchToggle(ElectricSwitch electricSwitch, BasePlayer player)
        {
            if (electricSwitch == null)
                return null;

            var autoTurret = electricSwitch.GetParentEntity() as AutoTurret;
            if (autoTurret != null)
                return CanPlayerInteractWithParentVehicle(player, autoTurret);

            return null;
        }

        private object OnTurretAuthorize(AutoTurret entity, BasePlayer player)
        {
            return CanPlayerInteractWithParentVehicle(player, entity);
        }

        private object OnTurretTarget(AutoTurret autoTurret, BasePlayer player)
        {
            if (autoTurret == null || player == null || player.UserIDString == null)
                return null;

            var turretParent = autoTurret.GetParentEntity();
            var vehicle = turretParent as BaseVehicle ?? (turretParent as BaseVehicleModule)?.Vehicle;
            if (vehicle == null)
                return null;

            var baseLock = GetVehicleLock(vehicle);
            if (baseLock == null)
                return null;

            if (CanPlayerBypassLock(player, baseLock, provideFeedback: false))
                return False;

            return null;
        }

        private object CanSwapToSeat(BasePlayer player, ModularCarSeat carSeat)
        {
            // Don't lock taxi modules
            if (IsTaxiSeat(carSeat))
                return null;

            return CanPlayerInteractWithParentVehicle(player, carSeat, provideFeedback: false);
        }

        private object OnVehiclePush(BaseVehicle vehicle, BasePlayer player)
        {
            return CanPlayerInteractWithVehicle(player, vehicle);
        }

        private void OnEntityKill(BaseLock baseLock)
        {
            var vehicle = GetParentVehicle(baseLock);
            if (vehicle == null)
                return;

            _lockedVehicleTracker.OnLockRemoved(vehicle);
        }

        // Handle the case where a cockpit is removed but the car remains
        // If a lock is present, either move the lock to another cockpit or destroy it
        private void OnEntityKill(VehicleModuleSeating seatingModule)
        {
            if (seatingModule == null || !seatingModule.HasADriverSeat())
                return;

            var car = seatingModule.Vehicle as ModularCar;
            if (car == null)
                return;

            var baseLock = seatingModule.GetComponentInChildren<BaseLock>();
            if (baseLock == null)
                return;

            baseLock.SetParent(null);

            var car2 = car;
            var baseLock2 = baseLock;

            NextTick(() =>
            {
                if (car2 == null)
                {
                    _lockedVehicleTracker.OnLockRemoved(car2);
                    baseLock2.Kill();
                }
                else
                {
                    var driverModule = FindFirstDriverModule(car2);
                    if (driverModule == null)
                    {
                        _lockedVehicleTracker.OnLockRemoved(car2);
                        baseLock2.Kill();
                    }
                    else
                    {
                        baseLock2.SetParent(driverModule);
                    }
                }
            });
        }

        // Allow players to deploy locks directly without any commands.
        private object CanDeployItem(BasePlayer basePlayer, Deployer deployer, NetworkableId entityId)
        {
            if (basePlayer == null || deployer == null)
                return null;

            var deployable = deployer.GetDeployable();
            if (deployable == null)
                return null;

            var activeItem = basePlayer.GetActiveItem();
            if (activeItem == null)
                return null;

            var itemId = activeItem.info.itemid;

            LockInfo lockInfo;
            if (itemId == LockInfo.CodeLock.ItemId)
            {
                lockInfo = LockInfo.CodeLock;
            }
            else if (itemId == LockInfo.PilotCodeLock.ItemId)
            {
                lockInfo = LockInfo.PilotCodeLock;
            }
            else if (itemId == LockInfo.KeyLock.ItemId)
            {
                lockInfo = LockInfo.KeyLock;
            }
            else
            {
                return null;
            }

            var vehicleInfo = GetVehicleAndInfo(BaseNetworkable.serverEntities.Find(entityId) as BaseEntity, basePlayer, out var vehicle, fromDeployHook: true);
            if (vehicleInfo == null)
                return null;

            var player = basePlayer.IPlayer;

            // Trick to make sure the replies are in chat instead of console.
            player.LastCommand = CommandType.Chat;

            if (!VerifyCanDeploy(player, vehicle, vehicleInfo, lockInfo.LockType, out _, out var payType)
                || !VerifyDeployDistance(player, vehicle))
                return False;

            DeployLockForPlayer(vehicle, vehicleInfo, lockInfo, basePlayer, payType);
            return False;
        }

        private object OnEntityReskin(Snowmobile snowmobile, ItemSkinDirectory.Skin skin, BasePlayer player)
        {
            var baseLock = GetVehicleLock(snowmobile);
            if (baseLock == null)
                return null;

            if (_vehicleInfoManager.GetVehicleInfo(snowmobile) == null)
                return null;

            if (baseLock.IsLocked() && !CanPlayerBypassLock(player, baseLock, provideFeedback: true))
                return False;

            _reskinManager.HandleReskinPre(snowmobile, baseLock);

            // In case another plugin blocks the pre-hook, add back or destroy the lock.
            NextTick(_reskinManager.CleanupAction);

            return null;
        }

        private void OnEntityReskinned(Snowmobile snowmobile, ItemSkinDirectory.Skin skin, BasePlayer player)
        {
            _reskinManager.HandleReskinPost(snowmobile);
        }

        #endregion

        #region API

        [HookMethod(nameof(API_DeployCodeLock))]
        public CodeLock API_DeployCodeLock(BaseEntity vehicle, BasePlayer player, bool isFree = true)
        {
            return DeployLockForAPI(vehicle, player, LockType.Code, isFree) as CodeLock;
        }

        [HookMethod(nameof(API_DeployKeyLock))]
        public KeyLock API_DeployKeyLock(BaseEntity vehicle, BasePlayer player, bool isFree = true)
        {
            return DeployLockForAPI(vehicle, player, LockType.Key, isFree) as KeyLock;
        }

        [HookMethod(nameof(API_CanPlayerDeployCodeLock))]
        public bool API_CanPlayerDeployCodeLock(BasePlayer player, BaseEntity vehicle)
        {
            return CanPlayerDeployLockForAPI(player, vehicle, LockType.Code);
        }

        [HookMethod(nameof(API_CanPlayerDeployKeyLock))]
        public bool API_CanPlayerDeployKeyLock(BasePlayer player, BaseEntity vehicle)
        {
            return CanPlayerDeployLockForAPI(player, vehicle, LockType.Key);
        }

        [HookMethod(nameof(API_CanAccessVehicle))]
        public bool API_CanAccessVehicle(BasePlayer player, BaseEntity vehicle, bool provideFeedback = true)
        {
            return CanPlayerInteractWithVehicle(player, vehicle, provideFeedback) == null;
        }

        [HookMethod(nameof(API_RegisterCustomVehicleType))]
        public void API_RegisterCustomVehicleType(string vehicleType, Vector3 lockPosition, Quaternion lockRotation, string parentBone, Func<BaseEntity, BaseEntity> determineLockParent)
        {
            _vehicleInfoManager.RegisterCustomVehicleType(this, new VehicleInfo()
            {
                VehicleType = vehicleType,
                LockPosition = lockPosition,
                LockRotation = lockRotation,
                ParentBone = parentBone,
                DetermineLockParent = determineLockParent,
            });
        }

        #endregion

        #region Commands

        [Command("vehiclecodelock", "vcodelock", "vlock")]
        private void CodeLockCommand(IPlayer player, string cmd, string[] args)
        {
            LockCommand(player, LockType.Code);
        }

        [Command("vehiclekeylock", "vkeylock")]
        private void KeyLockCommand(IPlayer player, string cmd, string[] args)
        {
            LockCommand(player, LockType.Key);
        }

        private void LockCommand(IPlayer player, LockType lockType)
        {
            if (player.IsServer)
                return;

            var basePlayer = player.Object as BasePlayer;
            var vehicleInfo = GetVehicleAndInfo(GetLookEntity(basePlayer, MaxDeployDistance), basePlayer, out var vehicle);
            if (vehicleInfo == null)
            {
                ReplyToPlayer(player, Lang.DeployErrorNoVehicleFound);
                return;
            }

            if (!VerifyCanDeploy(player, vehicle, vehicleInfo, lockType, out var lockInfo, out var payType))
                return;

            DeployLockForPlayer(vehicle, vehicleInfo, lockInfo, basePlayer, payType);
        }

        #endregion

        #region Helper Methods - General

        private static bool HasPermissionAny(IPlayer player, params string[] permissionNames)
        {
            foreach (var perm in permissionNames)
            {
                if (player.HasPermission(perm))
                    return true;
            }

            return false;
        }

        private static BaseLock GetVehicleLock(BaseEntity vehicle)
        {
            var baseLock = vehicle.GetSlot(BaseEntity.Slot.Lock) as BaseLock;
            return baseLock is CodeLock or KeyLock ? baseLock : null;
        }

        private static bool IsLockableEntity(BaseEntity entity)
        {
            if (entity.IsBusy())
                return false;

            return entity.HasSlot(BaseEntity.Slot.Lock);
        }

        private static string[] FindPrefabsOfType<T>() where T : BaseEntity
        {
            var prefabList = new List<string>();

            foreach (var assetPath in GameManifest.Current.entities)
            {
                var entity = GameManager.server.FindPrefab(assetPath)?.GetComponent<T>();
                if (entity == null)
                    continue;

                prefabList.Add(entity.PrefabName);
            }

            return prefabList.ToArray();
        }

        private static bool IsTaxiSeat(ModularCarSeat carSeat)
        {
            return carSeat is { associatedSeatingModule.DoorsAreLockable: false };
        }

        private static bool IsSameTeam(BasePlayer player, ulong userId)
        {
            if (player.currentTeam == 0)
                return false;

            var team = RelationshipManager.ServerInstance.FindTeam(player.currentTeam);
            return team != null && team.members.Contains(userId);
        }

        #endregion

        #region Helper Methods - Lock Authorization

        private bool IsPlayerAuthorizedToCodeLock(ulong userID, CodeLock codeLock)
        {
            return codeLock.whitelistPlayers.Contains(userID)
                || codeLock.guestPlayers.Contains(userID);
        }

        private bool IsPlayerAuthorizedToLock(BasePlayer player, BaseLock baseLock)
        {
            return (baseLock as KeyLock)?.HasLockPermission(player)
                ?? IsPlayerAuthorizedToCodeLock(player.userID, baseLock as CodeLock);
        }

        private bool PlayerHasMasterKeyForLock(BasePlayer player)
        {
            return permission.UserHasPermission(player.UserIDString, Permission_MasterKey);
        }

        private bool IsLockSharedWithPlayer(BasePlayer player, BaseLock baseLock)
        {
            var ownerId = baseLock.OwnerID;
            if (ownerId == 0 || ownerId == player.userID)
                return false;

            // In case the owner was locked out for some reason
            var codeLock = baseLock as CodeLock;
            if (codeLock != null && !IsPlayerAuthorizedToCodeLock(ownerId, codeLock))
                return false;

            var sharingSettings = _config.SharingSettings;
            if (sharingSettings.Team && player.currentTeam != 0)
            {
                if (IsSameTeam(player, ownerId))
                    return true;
            }

            if (sharingSettings.Friends && Friends != null)
            {
                var friendsResult = Friends.Call("HasFriend", baseLock.OwnerID, player.userID);
                if (friendsResult is true)
                    return true;
            }

            if ((sharingSettings.Clan || sharingSettings.ClanOrAlly) && Clans != null)
            {
                var clanMethodName = sharingSettings.ClanOrAlly ? "IsMemberOrAlly" : "IsClanMember";
                var clanResult = Clans.Call(clanMethodName, ownerId.ToString(), player.UserIDString);
                if (clanResult is true)
                    return true;
            }

            return false;
        }

        private bool CanPlayerBypassLock(BasePlayer player, BaseLock baseLock, bool provideFeedback)
        {
            var hookResult = Interface.CallHook("CanUseLockedEntity", player, baseLock);
            if (hookResult is bool result)
                return result;

            if (_config.AllowNPCsToBypassLocks && (player.IsNpc || !player.userID.IsSteamId()))
                return true;

            var canAccessLock = IsPlayerAuthorizedToLock(player, baseLock)
                || IsLockSharedWithPlayer(player, baseLock)
                || PlayerHasMasterKeyForLock(player);

            if (canAccessLock)
            {
                if (provideFeedback && !(baseLock is KeyLock))
                {
                    Effect.server.Run(Prefab_CodeLock_UnlockEffect, baseLock, 0, Vector3.zero, Vector3.forward);
                }

                return true;
            }

            if (provideFeedback)
            {
                Effect.server.Run(Prefab_CodeLock_DeniedEffect, baseLock, 0, Vector3.zero, Vector3.forward);
                ChatMessage(player, Lang.GenericErrorVehicleLocked);
            }

            return false;
        }

        private object CanPlayerInteractWithVehicle(BasePlayer player, BaseEntity vehicle, bool provideFeedback = true)
        {
            if (player == null || vehicle == null)
                return null;

            var baseLock = GetVehicleLock(vehicle);
            if (baseLock == null || !baseLock.IsLocked())
                return null;

            if (CanPlayerBypassLock(player, baseLock, provideFeedback))
                return null;

            return False;
        }

        private BaseEntity GetParentVehicle(BaseEntity entity)
        {
            var parent = entity.GetParentEntity();
            if (parent == null)
                return null;

            // Check for a vehicle module first since they are considered vehicles.
            var parentModule = parent as BaseVehicleModule;
            if (parentModule != null)
                return parentModule.Vehicle;

            if (parent is HotAirBalloon or BaseVehicle)
                return parent;

            return _vehicleInfoManager.GetCustomVehicleParent(entity);
        }

        private object CanPlayerInteractWithParentVehicle(BasePlayer player, BaseEntity entity, bool provideFeedback = true)
        {
            return CanPlayerInteractWithVehicle(player, GetParentVehicle(entity), provideFeedback);
        }

        #endregion

        #region Helper Methods - Deploying Locks

        private static bool DeployWasBlocked(BaseEntity vehicle, BasePlayer player, LockType lockType)
        {
            return Interface.CallHook(lockType.PreHookName, vehicle, player) is false;
        }

        private static BaseEntity GetLookEntity(BasePlayer player, float maxDistance)
        {
            if (!Physics.Raycast(player.eyes.HeadRay(), out var hit, maxDistance, Physics.DefaultRaycastLayers, QueryTriggerInteraction.Ignore))
                return null;

            return hit.GetEntity();
        }

        private static bool IsDead(BaseEntity entity)
        {
            return (entity as BaseCombatEntity)?.IsDead() ?? false;
        }

        private static VehicleModuleSeating FindFirstDriverModule(ModularCar car)
        {
            for (var socketIndex = 0; socketIndex < car.TotalSockets; socketIndex++)
            {
                if (car.TryGetModuleAt(socketIndex, out var module))
                {
                    var seatingModule = module as VehicleModuleSeating;
                    if (seatingModule != null && seatingModule.HasADriverSeat())
                        return seatingModule;
                }
            }

            return null;
        }

        private static bool CanCarHaveLock(ModularCar car)
        {
            return FindFirstDriverModule(car) != null;
        }

        private static bool CanVehicleHaveALock(BaseEntity vehicle)
        {
            // Only modular cars have restrictions
            var car = vehicle as ModularCar;
            return car == null || CanCarHaveLock(car);
        }

        private static Item GetPlayerLockItem(BasePlayer player, LockType lockType, out LockInfo lockInfo)
        {
            if (lockType == LockType.Key)
            {
                lockInfo = LockInfo.KeyLock;
                return GetPlayerLockItem(player, LockInfo.KeyLock);
            }

            // Prioritize the original code lock over the pilot code lock.
            var codeLockItem = GetPlayerLockItem(player, LockInfo.CodeLock);
            if (codeLockItem != null)
            {
                lockInfo = LockInfo.CodeLock;
                return codeLockItem;
            }

            var pilotCodeLockItem = GetPlayerLockItem(player, LockInfo.PilotCodeLock);
            if (pilotCodeLockItem != null)
            {
                lockInfo = LockInfo.PilotCodeLock;
                return pilotCodeLockItem;
            }

            lockInfo = LockInfo.CodeLock;
            return null;
        }

        private static Item GetPlayerLockItem(BasePlayer player, LockInfo lockInfo)
        {
            return player.inventory.FindItemByItemID(lockInfo.ItemId);
        }

        private static PayType DeterminePayType(IPlayer player, LockType lockType, out LockInfo lockInfo)
        {
            // Find a matching lock item in order to determine which code lock type should be deployed.
            var lockItem = GetPlayerLockItem(player.Object as BasePlayer, lockType, out lockInfo);
            if (player.HasPermission(lockType.PermissionFree))
                return PayType.Free;

            return lockItem != null
                ? PayType.Item
                : PayType.Resources;
        }

        private static bool CanPlayerAffordLock(BasePlayer player, LockType lockType, out LockInfo lockInfo, out PayType payType)
        {
            payType = DeterminePayType(player.IPlayer, lockType, out lockInfo);
            if (payType != PayType.Resources)
                return true;

            return player.inventory.crafting.CanCraft(lockInfo.ItemDefinition);
        }

        private static RidableHorse GetClosestHorse(HitchTrough hitchTrough, BasePlayer player)
        {
            var closestDistance = float.MaxValue;
            RidableHorse closestHorse = null;

            foreach (var hitchSpot in hitchTrough.hitchSpots)
            {
                if (!hitchSpot.IsOccupied())
                    continue;

                var distance = Vector3.Distance(player.transform.position, hitchSpot.tr.position);
                if (distance < closestDistance)
                {
                    closestDistance = distance;
                    if (hitchSpot.hitchableEntRef.Get(serverside: true) is RidableHorse ridableHorse)
                    {
                        closestHorse = ridableHorse;
                    }
                }
            }

            return closestHorse;
        }

        private static void ClaimVehicle(BaseEntity vehicle, ulong ownerId)
        {
            vehicle.OwnerID = ownerId;
            Interface.CallHook("OnVehicleOwnershipChanged", vehicle);
        }

        private bool IsVehicleConnectedEntity(BasePlayer basePlayer, BaseEntity entity, out BaseEntity vehicle)
        {
            vehicle = null;

            if (entity is BaseVehicleModule module)
            {
                vehicle = module.Vehicle ?? module.GetParentEntity();
                return true;
            }

            if (entity is ModularCarGarage carLift)
            {
                vehicle = carLift.carOccupant;
                return true;
            }

            if (entity is HitchTrough hitchTrough)
            {
                vehicle = GetClosestHorse(hitchTrough, basePlayer);
                return true;
            }

            return false;
        }

        private VehicleInfo GetVehicleAndInfo(BaseEntity entity, BasePlayer basePlayer, out BaseEntity vehicle, bool fromDeployHook = false)
        {
            if (entity == null)
            {
                vehicle = null;
                return null;
            }

            var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(entity);
            if (vehicleInfo != null)
            {
                vehicle = entity;
                return vehicleInfo;
            }

            if (IsVehicleConnectedEntity(basePlayer, entity, out vehicle) && vehicle != null)
                return _vehicleInfoManager.GetVehicleInfo(vehicle);

            if (fromDeployHook && IsLockableEntity(entity))
            {
                // Let the game decide whether to lock the entity, instead of resolving the parent vehicle.
                vehicle = null;
                return null;
            }

            vehicle = entity.GetParentEntity();
            if (vehicle == null)
                return null;

            return _vehicleInfoManager.GetVehicleInfo(vehicle);
        }

        private bool AllowNoOwner(BaseEntity vehicle)
        {
            return _config.AllowIfNoOwner
                || vehicle.OwnerID != 0;
        }

        private bool AllowDifferentOwner(BasePlayer player, BaseEntity vehicle)
        {
            return _config.AllowIfDifferentOwner
                || vehicle.OwnerID == 0
                || vehicle.OwnerID == player.userID
                || (_config.AllowIfOwnedByTeammate && IsSameTeam(player, vehicle.OwnerID));
        }

        private void MaybeChargePlayerForLock(BasePlayer player, LockInfo lockInfo, PayType payType)
        {
            if (payType == PayType.Free)
                return;

            if (payType == PayType.Item)
            {
                // Prefer taking the item they are holding in case they are deploying directly.
                var heldItem = player.GetActiveItem();
                if (heldItem != null && heldItem.info.itemid == lockInfo.ItemId)
                {
                    heldItem.UseItem(1);
                }
                else
                {
                    player.inventory.Take(null, lockInfo.ItemId, 1);
                }

                player.Command("note.inv", lockInfo.ItemId, -1);
                return;
            }

            foreach (var ingredient in lockInfo.Blueprint.ingredients)
            {
                player.inventory.Take(null, ingredient.itemid, (int)ingredient.amount);
                player.Command("note.inv", ingredient.itemid, -ingredient.amount);
                GetCooldownManager(lockInfo.LockType).UpdateLastUsedForPlayer(player.UserIDString);
            }
        }

        private BaseLock DeployLock(BaseEntity vehicle, VehicleInfo vehicleInfo, LockInfo lockInfo, ulong ownerId = 0)
        {
            var parentToEntity = vehicleInfo.DetermineLockParent(vehicle);
            if (parentToEntity == null)
                return null;

            var baseLock = GameManager.server.CreateEntity(lockInfo.Prefab, vehicleInfo.LockPosition, vehicleInfo.LockRotation) as BaseLock;
            if (baseLock == null)
                return null;

            var keyLock = baseLock as KeyLock;
            if (keyLock != null)
            {
                keyLock.keyCode = UnityEngine.Random.Range(1, 100000);
            }

            // Assign lock ownership when the lock is being deployed by/for a player.
            if (ownerId != 0)
            {
                baseLock.OwnerID = ownerId;
            }

            baseLock.SetParent(parentToEntity, vehicleInfo.ParentBone);
            baseLock.Spawn();
            vehicle.SetSlot(BaseEntity.Slot.Lock, baseLock);

            // Auto lock key locks to be consistent with vanilla.
            if (ownerId != 0 && keyLock != null)
            {
                using var flagsScope = keyLock.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate_Flags);
                flagsScope.Set(BaseEntity.Flags.Locked, true);
            }

            Effect.server.Run(Prefab_CodeLock_DeployedEffect, baseLock.transform.position);
            Interface.CallHook("OnVehicleLockDeployed", vehicle, baseLock);
            _lockedVehicleTracker.OnLockAdded(vehicle);

            return baseLock;
        }

        private BaseLock DeployLockForPlayer(BaseEntity vehicle, VehicleInfo vehicleInfo, LockInfo lockInfo, BasePlayer player, PayType payType)
        {
            var originalVehicleOwnerId = vehicle.OwnerID;

            // Temporarily set the player as the owner of the vehicle, for compatibility with AutoCodeLock (OnItemDeployed).
            vehicle.OwnerID = player.userID;

            var baseLock = DeployLock(vehicle, vehicleInfo, lockInfo, player.userID);
            if (baseLock == null)
            {
                vehicle.OwnerID = originalVehicleOwnerId;
                return null;
            }

            // Allow other plugins to detect the code lock being deployed (e.g., to auto lock).
            var lockItem = GetPlayerLockItem(player, lockInfo);
            if (lockItem != null)
            {
                Interface.CallHook("OnItemDeployed", lockItem.GetHeldEntity(), vehicle, baseLock);
            }
            else
            {
                // Temporarily increase the player inventory capacity to ensure there is enough space.
                player.inventory.containerMain.capacity++;
                var temporaryLockItem = ItemManager.CreateByItemID(lockInfo.ItemId);
                if (player.inventory.GiveItem(temporaryLockItem))
                {
                    Interface.CallHook("OnItemDeployed", temporaryLockItem.GetHeldEntity(), vehicle, baseLock);
                    temporaryLockItem.RemoveFromContainer();
                }

                temporaryLockItem.Remove();
                player.inventory.containerMain.capacity--;
            }

            // Revert the vehicle owner to the original, after OnItemDeployed is called.
            vehicle.OwnerID = originalVehicleOwnerId;

            MaybeChargePlayerForLock(player, lockInfo, payType);

            // Potentially assign vehicle ownership when the lock is being deployed by/for a player.
            if (vehicle.OwnerID == 0)
            {
                if (_config.AutoClaimUnownedVehicles)
                {
                    ClaimVehicle(vehicle, player.userID);
                }
            }
            else if (vehicle.OwnerID != player.userID && _config.AutoReplaceVehicleOwnership)
            {
                ClaimVehicle(vehicle, player.userID);
            }

            return baseLock;
        }

        private BaseLock DeployLockForAPI(BaseEntity vehicle, BasePlayer player, LockType lockType, bool isFree)
        {
            if (vehicle == null || IsDead(vehicle))
                return null;

            var vehicleInfo = _vehicleInfoManager.GetVehicleInfo(vehicle);
            if (vehicleInfo == null
                || GetVehicleLock(vehicle) != null
                || !CanVehicleHaveALock(vehicle))
                return null;

            GetPlayerLockItem(player, lockType, out var lockInfo);

            PayType payType;
            if (isFree)
            {
                payType = PayType.Free;
            }
            else if (!VerifyPlayerCanDeployLock(player.IPlayer, lockType, out _, out payType))
            {
                return null;
            }

            if (DeployWasBlocked(vehicle, player, lockType))
                return null;

            return player != null
                ? DeployLockForPlayer(vehicle, vehicleInfo, lockInfo, player, payType)
                : DeployLock(vehicle, vehicleInfo, lockInfo);
        }

        private bool CanPlayerDeployLockForAPI(BasePlayer player, BaseEntity vehicle, LockType lockType)
        {
            return vehicle != null
                && !IsDead(vehicle)
                && _vehicleInfoManager.GetVehicleInfo(vehicle) != null
                && AllowNoOwner(vehicle)
                && AllowDifferentOwner(player, vehicle)
                && player.CanBuild()
                && GetVehicleLock(vehicle) == null
                && CanVehicleHaveALock(vehicle)
                && CanPlayerAffordLock(player, lockType, out _, out _)
                && !DeployWasBlocked(vehicle, player, lockType);
        }

        #endregion

        #region Helper Methods - Command Checks

        private bool VerifyDeployDistance(IPlayer player, BaseEntity vehicle)
        {
            if (vehicle.Distance(player.Object as BasePlayer) <= MaxDeployDistance)
                return true;

            ReplyToPlayer(player, Lang.DeployErrorDistance);
            return false;
        }

        private bool VerifyPermissionToVehicleAndLockType(IPlayer player, VehicleInfo vehicleInfo, LockType lockType)
        {
            var vehiclePerm = lockType == LockType.Code
                ? vehicleInfo.CodeLockPermission
                : vehicleInfo.KeyLockPermission;

            if (vehiclePerm != null && HasPermissionAny(player, lockType.PermissionAllVehicles, vehiclePerm))
                return true;

            ReplyToPlayer(player, Lang.GenericErrorNoPermission);
            return false;
        }

        private bool VerifyVehicleIsNotDead(IPlayer player, BaseEntity vehicle)
        {
            if (!IsDead(vehicle))
                return true;

            ReplyToPlayer(player, Lang.DeployErrorVehicleDead);
            return false;
        }

        private bool VerifyNotForSale(IPlayer player, BaseEntity vehicle)
        {
            var horse = vehicle as RidableHorse;
            if (horse == null || !horse.IsForSale)
                return true;

            ReplyToPlayer(player, Lang.DeployErrorOther);
            return false;
        }

        private bool VerifyNoOwnershipRestriction(IPlayer player, BasePlayer basePlayer, BaseEntity vehicle)
        {
            if (!AllowNoOwner(vehicle))
            {
                ReplyToPlayer(player, Lang.DeployErrorNoOwner);
                return false;
            }

            if (!AllowDifferentOwner(basePlayer, vehicle))
            {
                ReplyToPlayer(player, Lang.DeployErrorDifferentOwner);
                return false;
            }

            return true;
        }

        private bool VerifyCanBuild(IPlayer player, BaseEntity vehicle)
        {
            var basePlayer = player.Object as BasePlayer;
            if (basePlayer == null)
                return false;

            if (vehicle.OwnerID == 0 && _config.RequireTCIfNoOwner)
            {
                if (!basePlayer.IsBuildingAuthed() || !basePlayer.IsBuildingAuthed(vehicle.WorldSpaceBounds()))
                {
                    ReplyToPlayer(player, Lang.DeployErrorNoOwnerRequiresTC);
                    return false;
                }
            }
            else if (basePlayer.IsBuildingBlocked() || basePlayer.IsBuildingBlocked(vehicle.WorldSpaceBounds()))
            {
                ReplyToPlayer(player, Lang.GenericErrorBuildingBlocked);
                return false;
            }

            return true;
        }

        private bool VerifyVehicleHasNoLock(IPlayer player, BaseEntity vehicle)
        {
            if (GetVehicleLock(vehicle) == null)
                return true;

            ReplyToPlayer(player, Lang.DeployErrorHasLock);
            return false;
        }

        private bool VerifyVehicleCanHaveALock(IPlayer player, BaseEntity vehicle)
        {
            if (CanVehicleHaveALock(vehicle))
                return true;

            ReplyToPlayer(player, Lang.DeployErrorModularCarNoCockpit);
            return false;
        }

        private bool VerifyPlayerCanAffordLock(BasePlayer player, LockType lockType, out LockInfo lockInfo, out PayType payType)
        {
            if (CanPlayerAffordLock(player, lockType, out lockInfo, out payType))
                return true;

            ChatMessage(player, Lang.DeployErrorInsufficientResources, lockInfo.ItemDefinition.displayName.translated);
            return false;
        }

        private bool VerifyOffCooldown(IPlayer player, LockType lockType, PayType payType)
        {
            if (payType != PayType.Resources)
                return true;

            var secondsRemaining = GetCooldownManager(lockType).GetSecondsRemaining(player.Id);
            if (secondsRemaining <= 0)
                return true;

            ChatMessage(player.Object as BasePlayer, Lang.GenericErrorCooldown, Math.Ceiling(secondsRemaining));
            return false;
        }

        private bool VerifyPlayerCanDeployLock(IPlayer player, LockType lockType, out LockInfo lockInfo, out PayType payType)
        {
            return VerifyPlayerCanAffordLock(player.Object as BasePlayer, lockType, out lockInfo, out payType)
                && VerifyOffCooldown(player, lockType, payType);
        }

        private bool VerifyNotMounted(IPlayer player, BaseEntity vehicle, VehicleInfo vehicleInfo)
        {
            if (!vehicleInfo.IsMounted(vehicle))
                return true;

            ReplyToPlayer(player, Lang.DeployErrorMounted);
            return false;
        }

        private bool VerifyCanDeploy(IPlayer player, BaseEntity vehicle, VehicleInfo vehicleInfo, LockType lockType, out LockInfo lockInfo, out PayType payType)
        {
            var basePlayer = player.Object as BasePlayer;
            payType = PayType.Item;

            if (!VerifyPermissionToVehicleAndLockType(player, vehicleInfo, lockType)
                || !VerifyVehicleIsNotDead(player, vehicle)
                || !VerifyNotForSale(player, vehicle)
                || !VerifyNoOwnershipRestriction(player, basePlayer, vehicle)
                || !VerifyCanBuild(player, vehicle)
                || !VerifyVehicleHasNoLock(player, vehicle)
                || !VerifyVehicleCanHaveALock(player, vehicle))
            {
                lockInfo = null;
                return false;
            }

            return VerifyPlayerCanDeployLock(player, lockType, out lockInfo, out payType)
                && VerifyNotMounted(player, vehicle, vehicleInfo)
                && !DeployWasBlocked(vehicle, basePlayer, lockType);
        }

        #endregion

        #region Vehicle Info

        private class VehicleInfo
        {
            public string VehicleType;
            public string[] PrefabPaths;
            public Vector3 LockPosition;
            public Quaternion LockRotation;
            public string ParentBone;

            public string CodeLockPermission { get; private set; }
            public string KeyLockPermission { get; private set; }
            public uint[] PrefabIds { get; private set; }

            public Func<BaseEntity, BaseEntity> DetermineLockParent = entity => entity;
            public Func<BaseEntity, float> TimeSinceLastUsed = _ => 0;

            public void OnServerInitialized(VehicleDeployedLocks plugin)
            {
                CodeLockPermission = $"{Permission_CodeLock_Prefix}.{VehicleType}";
                KeyLockPermission = $"{Permission_KeyLock_Prefix}.{VehicleType}";

                if (!plugin.permission.PermissionExists(CodeLockPermission, plugin))
                {
                    plugin.permission.RegisterPermission(CodeLockPermission, plugin);
                }

                if (!plugin.permission.PermissionExists(KeyLockPermission, plugin))
                {
                    plugin.permission.RegisterPermission(KeyLockPermission, plugin);
                }

                // Custom vehicles aren't currently allowed to specify prefabs since they reuse existing prefabs.
                if (PrefabPaths != null)
                {
                    var prefabIds = new List<uint>();
                    foreach (var prefabName in PrefabPaths)
                    {
                        var prefabId = StringPool.Get(prefabName);
                        if (prefabId != 0)
                        {
                            prefabIds.Add(prefabId);
                        }
                        else
                        {
                            plugin.LogError($"Invalid prefab. Please alert the plugin maintainer -- {prefabName}");
                        }
                    }

                    PrefabIds = prefabIds.ToArray();
                }
            }

            // In the future, custom vehicles may be able to pass in a method to override this.
            public bool IsMounted(BaseEntity entity)
            {
                var vehicle = entity as BaseVehicle;
                if (vehicle != null)
                    return vehicle.AnyMounted();

                var mountable = entity as BaseMountable;
                if (mountable != null)
                    return mountable.AnyMounted();

                return false;
            }
        }

        private class VehicleInfoManager
        {
            private readonly VehicleDeployedLocks _plugin;
            private readonly Dictionary<uint, VehicleInfo> _prefabIdToVehicleInfo = new();
            private readonly Dictionary<string, VehicleInfo> _customVehicleTypes = new();

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

            public void OnServerInitialized()
            {
                var allVehicles = new[]
                {
                    new VehicleInfo
                    {
                        VehicleType = "attackhelicopter",
                        PrefabPaths = new[] { "assets/content/vehicles/attackhelicopter/attackhelicopter.entity.prefab" },
                        LockPosition = new Vector3(-0.6f, 1.08f, 1.01f),
                        TimeSinceLastUsed = vehicle => Time.time - (vehicle as AttackHelicopter)?.lastEngineOnTime ?? Time.time,
                    },
                    new VehicleInfo
                    {
                        VehicleType = "chinook",
                        PrefabPaths = new[] { "assets/prefabs/npc/ch47/ch47.entity.prefab" },
                        LockPosition = new Vector3(-1.175f, 2, 6.5f),
                        TimeSinceLastUsed = vehicle => Time.time - (vehicle as CH47Helicopter)?.lastPlayerInputTime ?? Time.time,
                    },
                    new VehicleInfo
                    {
                        VehicleType = "duosub",
                        PrefabPaths = new[] { "assets/content/vehicles/submarine/submarineduo.entity.prefab" },
                        LockPosition = new Vector3(-0.455f, 1.29f, 0.75f),
                        LockRotation = Quaternion.Euler(0, 180, 10),
                        TimeSinceLastUsed = vehicle => (vehicle as SubmarineDuo)?.timeSinceLastUsed ?? 0,
                    },
                    new VehicleInfo
                    {
                        VehicleType = "hotairballoon",
                        PrefabPaths = new[] { "assets/prefabs/deployable/hot air balloon/hotairballoon.prefab" },
                        LockPosition = new Vector3(1.45f, 0.9f, 0),
                        TimeSinceLastUsed = vehicle => Time.time - (vehicle as HotAirBalloon)?.sinceLastBlast ?? Time.time,
                    },
                    new VehicleInfo
                    {
                        VehicleType = "kayak",
                        PrefabPaths = new[] { "assets/content/vehicles/boats/kayak/kayak.prefab" },
                        LockPosition = new Vector3(-0.43f, 0.2f, 0.2f),
                        LockRotation = Quaternion.Euler(0, 90, 90),
                        TimeSinceLastUsed = vehicle => (vehicle as Kayak)?.timeSinceLastUsed ?? 0,
                    },
                    new VehicleInfo
                    {
                        VehicleType = "locomotive",
                        PrefabPaths = new[] { "assets/content/vehicles/trains/locomotive/locomotive.entity.prefab" },
                        LockPosition = new Vector3(-0.11f, 2.89f, 4.95f),
                        TimeSinceLastUsed = vehicle => (vehicle as TrainEngine)?.decayingFor ?? 0,
                    },
                    new VehicleInfo
                    {
                        VehicleType = "magnetcrane",
                        PrefabPaths = new[] { "assets/content/vehicles/crane_magnet/magnetcrane.entity.prefab" },
                        LockPosition = new Vector3(-1.735f, -1.445f, 0.79f),
                        LockRotation = Quaternion.Euler(0, 0, 90),
                        ParentBone = "Top",
                        TimeSinceLastUsed = vehicle => Time.realtimeSinceStartup - (vehicle as MagnetCrane)?.lastDrivenTime ?? Time.realtimeSinceStartup,
                    },
                    new VehicleInfo
                    {
                        VehicleType = "minicopter",
                        PrefabPaths = new[] { "assets/content/vehicles/minicopter/minicopter.entity.prefab" },
                        LockPosition = new Vector3(-0.15f, 0.7f, -0.1f),
                        TimeSinceLastUsed = vehicle => Time.time - (vehicle as Minicopter)?.l
// ... [Code preview truncated due to length. Full source in attached file] ...

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