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

FREE BagOfHolding : 1.8.3 1.8.3

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
 // #define DEBUG_POOLING
// #define DEBUG_GATHER
// #define FEATURE_BELT_ACTIVATION
// #define FEATURE_EXTRA_POCKETS_CONFIG
// #define CARBON
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using System.ComponentModel;
using System.Reflection;
using System.Text.RegularExpressions;
using static BaseEntity;
using static PlayerInventory;
using Time = UnityEngine.Time;
namespace Oxide.Plugins {
    [Info("Bag of Holding", "BlackLightning", "1.8.3")]
    [Description("Adds portable bags for carrying loot.")]
    internal class BagOfHolding : CovalencePlugin {
#region Fields
        [PluginReference]
        private Plugin Backpacks, Economics, ItemRetriever, Kits, Loottable, ServerRewards, SkillTree, StackModifier;
        private Configuration _config;
        private const string PermissionListBags = "bagofholding.listbags";
        private const string PermissionListContainers = "bagofholding.listcontainers";
        private const string PermissionGiveBag = "bagofholding.givebag";
        private const string PermissionGiveKitBag = "bagofholding.givekitbag";
        private const string PermissionConfig = "bagofholding.config";
        private const string PermissionStats = "bagofholding.stats";
        private const string PermissionManageLoot = "bagofholding.manageloot";
        private const int BagItemId = 479292118;
        private const int SaddleBagItemId = 1400460850;
        private const int ParachuteItemId = 602628465;
        private const int SmallBackpackItemId = 2068884361;
        private const int LargeBackpackItemId = -907422733;
        private const int VisualBackpackItemId = LargeBackpackItemId;
        private const string GenericResizableLootPanelName = "generic_resizable";
        private const Item.Flag FlagHasItems = Item.Flag.Cooking;
        private const Item.Flag FlagGather = Item.Flag.OnFire;
        private const Item.Flag FlagGatherExisting = (Item.Flag)(1 << 20);
        private const Item.Flag UnsearchableItemFlag = (Item.Flag)(1 << 24);
        private const ItemDefinition.Flag SearchableItemDefinitionFlag = (ItemDefinition.Flag)(1 << 24);
        private const string PrefabPlayerCorpse = "assets/prefabs/player/player_corpse.prefab";
        private const string PrefabStorageBox = "assets/prefabs/deployable/woodenbox/woodbox_deployed.prefab";
        private static readonly object True = true;
        private static readonly object False = false;
        private static readonly object CannotAccept = ItemContainer.CanAcceptResult.CannotAccept;
        private UIBuilder _containerUIBuilder = new(15000);
        private BaseEntity _sharedLootEntity;
        private LootableCorpse _sharedCorpseEntity;
        private ProtectionProperties _immortalProtection;
        private BackpacksAdapter _backpacksAdapter;
        private SkillTreeAdapter _skillTreeAdapter;
        private ItemRetrieverAdapter _itemRetrieverAdapter;
        private LimitManager _limitManager;
        private BagManager _bagManager;
        private UIUpdateManager _uiUpdateManager;
        private ItemModBagOfHolding _bagItemMod;
        private ItemModWearableBag _wearableBagItemMod;
        private ItemModContainerDummy _containerDummyMod;
        private ApiInstance _api;
        private PaymentProviderResolver _paymentProviderResolver;
        private CustomItemManager _customItemManager;
        private BeltUIRenderer _beltUIRenderer;
#if FEATURE_BELT_ACTIVATION
        private BeltTracker _beltTracker;
#endif
        private ItemDefinition _bagItemDefinition;
        private bool _preInitialized;
        private bool _initialized;
        private bool _subscribedToOnItemSplit;
        private Item[] _lockerItemBuffer = new Item[Locker.setSize];
        private string[] _delayedHooks;
        private string[] _conditionalHooks;
        private object[] _objectArray1 = new object[1];
        private object[] _objectArray2 = new object[2];
        private string[] _networkingHooks = {
            nameof(OnEntitySaved), nameof(OnInventoryNetworkUpdate),
        }
        ;
        private readonly ProtoBuf.Item _visualBackpackItemData = new() {
            ShouldPool = false, itemid = VisualBackpackItemId, amount = 1,
        }
        ;
        public BagOfHolding() {
            _backpacksAdapter = new BackpacksAdapter(this);
            _skillTreeAdapter = new SkillTreeAdapter(this);
            _itemRetrieverAdapter = new ItemRetrieverAdapter(this);
            _uiUpdateManager = new UIUpdateManager(this);
            _paymentProviderResolver = new PaymentProviderResolver(this);
            _beltUIRenderer = new BeltUIRenderer(this);
#if FEATURE_BELT_ACTIVATION
            _beltTracker = new BeltTracker(this);
#endif
            _delayedHooks = new[] {
                nameof(OnPlayerLootEnd), nameof(OnItemAction), nameof(CanAcceptItem), nameof(CanMoveItem), nameof(OnLockerSwap),
            }
            ;
            _conditionalHooks = new[] {
#if FEATURE_BELT_ACTIVATION
                nameof(OnActiveItemChanged),
#endif
                nameof(CanWearItem), nameof(CanLockerAcceptItem), nameof(OnPlayerSleepEnded), nameof(CanStackItem), nameof(CanCombineDroppedItem), nameof(OnDroppedItemCombined), nameof(OnItemSplit), nameof(OnItemPickup), nameof(CanBeRecycled), nameof(CanRecycle), nameof(OnItemRecycle), nameof(OnMaxStackable)
            }
            .Concat(_networkingHooks).ToArray();
        }
#endregion
#region Hooks
        private void Init() {
            permission.RegisterPermission(PermissionListBags, this);
            permission.RegisterPermission(PermissionListContainers, this);
            permission.RegisterPermission(PermissionGiveBag, this);
            permission.RegisterPermission(PermissionGiveKitBag, this);
            permission.RegisterPermission(PermissionConfig, this);
            permission.RegisterPermission(PermissionStats, this);
            permission.RegisterPermission(PermissionManageLoot, this);
            CustomPool.Reset<BagInfo>(512);
            CustomPool.Reset<List<BagInfo>>(512);
            CustomPool.Reset<DisposableList<BagInfo>>(8);
            CustomPool.Reset<ContainerSupervisor>(512);
            CustomPool.Reset<List<Item>>(8);
            CustomPool.Reset<ItemContainer>(8);
            _limitManager = new LimitManager(this, _config);
            _bagManager = new BagManager(this, _config, _limitManager);
            _api = new ApiInstance(this);
            _customItemManager = new CustomItemManager(_config);
            _config.Init(this);
            foreach (var hookName in _delayedHooks.Concat(_conditionalHooks)) {
                Unsubscribe(hookName);
            }
            if (!_config.LootConfig.Enabled) {
                // OnLootSpawn is intentionally not included in the list of hooks that are always subscribed,
                // so that it is subscribed during server boot.Unsubscribe(nameof(OnLootSpawn));
            }
        }
        private void OnServerInitialize() {
            _preInitialized = true;
            _config.OnServerInitialize();
        }
        private void OnServerInitialized() {
            if (!_preInitialized) {
                OnServerInitialize();
            }
            _config.OnServerInitialized(this);
            _bagItemDefinition = ItemManager.FindItemDefinition(BagItemId);
            if (_bagItemDefinition == null) {
                LogError($"Unable to initialize plugin because item {BagItemId} was not found.");
                return;
            }
            _bagItemDefinition.flags |= SearchableItemDefinitionFlag;
            var oldBagItemMod = _bagItemDefinition.itemMods.FirstOrDefault(itemMod => itemMod.GetType().Name == nameof(ItemModBagOfHolding));
            if (oldBagItemMod != null) {
                oldBagItemMod.GetType().GetMethod(nameof(ItemModBagOfHolding.Unload))?.Invoke(oldBagItemMod, null);
            }
            _immortalProtection = ScriptableObject.CreateInstance<ProtectionProperties>();
            _immortalProtection.name = "BagOfHoldingProtection";
            _immortalProtection.Add(1);
            _sharedLootEntity = CreateLootEntity(_immortalProtection);
            if (_sharedLootEntity == null || _sharedLootEntity.IsDestroyed) {
                LogError($"Failed to create the shared lootable entity.This plugin will not work correctly.There is likely a plugin conflict.");
                return;
            }
            _sharedCorpseEntity = CreateCorpseEntity(_immortalProtection);
            if (_sharedCorpseEntity == null || _sharedCorpseEntity.IsDestroyed) {
                LogError($"Failed to create the shared lootable corpse entity.This plugin will not work correctly.There is likely a plugin conflict.");
                return;
            }
            if (_config.Wearable.Enabled) {
                _wearableBagItemMod = ItemModWearableBag.AddToItemDefinition(_bagItemDefinition);
            }
            _bagItemMod = ItemModBagOfHolding.AddToItemDefinition(_bagItemDefinition, this);
            _containerDummyMod = ItemModContainerDummy.AddToItemDefinition(_bagItemDefinition);
            // Setup container dependencies before discovering items.Most important for backpacks._skillTreeAdapter.HandleLoadedChanged();
            _backpacksAdapter.HandleLoadedChanged();
            _itemRetrieverAdapter.HandleLoadedChanged();
            // Enable networking hooks before sending network updates.if (_config.VisualBackpack.Enabled)
            {
                foreach (var hookName in _networkingHooks) {
                    Subscribe(hookName);
                }
            }
            // Best effort attempt to update all bags.foreach (var entity in BaseNetworkable.serverEntities)
            {
                var ridableAnimal = entity as BaseRidableAnimal;
                if ((object)ridableAnimal != null) {
                    DiscoverBags(ridableAnimal.storageInventory);
                    DiscoverBags(ridableAnimal.equipmentInventory);
                    continue;
                }
                if (entity is IItemContainerEntity containerEntity) {
                    DiscoverBags(containerEntity.inventory);
                    var shopFront = containerEntity as ShopFront;
                    if ((object)shopFront != null) {
                        DiscoverBags(shopFront.customerInventory);
                    }
                    continue;
                }
                var droppedItemContainer = entity as DroppedItemContainer;
                if ((object)droppedItemContainer != null) {
                    DiscoverBags(droppedItemContainer.inventory);
                    continue;
                }
                var worldItem = entity as WorldItem;
                if ((object)worldItem != null) {
                    DiscoverBags(worldItem.item);
                    var droppedItem = entity as DroppedItem;
                    if ((object)droppedItem != null && droppedItem.item != null && IsSkinnedBag(droppedItem.item)) {
                        // Reset removal time.Most important for server restarts._bagItemMod.HandleBagMovedToWorld(droppedItem.item, delayRemovalTimeReset: true);
                    }
                    continue;
                }
                var lootableCorpse = entity as LootableCorpse;
                if ((object)lootableCorpse != null) {
                    DiscoverBags(lootableCorpse.containers);
                    continue;
                }
                var basePlayer = entity as BasePlayer;
                if (basePlayer != null) {
                    var inventory = basePlayer.inventory;
                    if (inventory == null) continue;
                    DiscoverBags(basePlayer.inventory.containerMain);
                    DiscoverBags(basePlayer.inventory.containerBelt);
                    DiscoverBags(basePlayer.inventory.containerWear);
                    if (_config.VisualBackpack.Enabled && ShouldDisplayVisualBackpack(basePlayer)) {
                        inventory.SendUpdatedInventory(PlayerInventory.Type.Wear, inventory.containerWear, bSendInventoryToEveryone : true);
                    }
                    continue;
                }
            }
            foreach (var hookName in _delayedHooks) {
                Subscribe(hookName);
            }
            if (_config.Wearable.Enabled) {
                Subscribe(nameof(CanWearItem));
                Subscribe(nameof(CanLockerAcceptItem));
            }
            if (_config.DroppedBagSettings.EnableOpeningDroppedBags || (_config.Wearable.Enabled && _config.DroppedBagSettings.AutoEquipDroppedBags)) {
                Subscribe(nameof(OnItemPickup));
            }
            if (_config.ContainsRecyclableBags) {
                Subscribe(nameof(CanBeRecycled));
                Subscribe(nameof(CanRecycle));
                Subscribe(nameof(OnItemRecycle));
            }
#if FEATURE_BELT_ACTIVATION
            if (_config.BeltActivation.Enabled) {
                Subscribe(nameof(OnActiveItemChanged));
            }
#endif
            if (_config.UISettings.BeltButton.Enabled) {
                Subscribe(nameof(OnPlayerSleepEnded));
            }
            if (_config.ContainsStackProfileItemOverrides) {
                Subscribe(nameof(OnMaxStackable));
            }
            RegisterCustomItemsWithLootTable();
            _initialized = true;
            // Give stack plugins one frame to update stack sizes.NextTick(() =>
            {
                // Always subscribe to CanStackItem, since StackModifier only handles it in player inventories.Subscribe(nameof(CanStackItem));
                RefreshStackHooks();
                if (_bagItemDefinition.stackable > 1) {
                    TestOnItemSplit();
                }
            }
            );
        }
        private void Unload() {
            foreach (var hookName in _networkingHooks) {
                Unsubscribe(hookName);
            }
            if (_config.VisualBackpack.Enabled || _config.Wearable.Enabled) {
                foreach (var entity in BaseNetworkable.serverEntities) {
                    if (_config.VisualBackpack.Enabled) {
                        var basePlayer = entity as BasePlayer;
                        if ((object)basePlayer != null) {
                            var inventory = basePlayer.inventory;
                            if (inventory == null) continue;
                            if (ShouldDisplayVisualBackpack(basePlayer)) {
                                inventory.SendUpdatedInventory(PlayerInventory.Type.Wear, inventory.containerWear, bSendInventoryToEveryone : true);
                            }
                            continue;
                        }
                    }
                }
            }
            _wearableBagItemMod?.Destroy();
            _containerDummyMod.Destroy();
            _customItemManager.Unload();
            _bagManager.Unload();
            _limitManager.Unload();
            CustomPool.Reset<BagInfo>(0);
            CustomPool.Reset<List<BagInfo>>(0);
            CustomPool.Reset<DisposableList<BagInfo>>(0);
            CustomPool.Reset<ContainerSupervisor>(0);
            CustomPool.Reset<List<Item>>(0);
            CustomPool.Reset<ItemContainer>(0);
        }
        private void OnPluginLoaded(Plugin plugin) {
            if (!_initialized) return;
            switch (plugin.Name) {
                case nameof(Backpacks) : _backpacksAdapter.HandleLoadedChanged();
                return;
                case nameof(ItemRetriever) : _itemRetrieverAdapter.HandleLoadedChanged();
                return;
                case nameof(Loottable) : RefreshStackHooks();
                RegisterCustomItemsWithLootTable();
                return;
                case nameof(SkillTree) : _skillTreeAdapter.HandleLoadedChanged();
                return;
                case nameof(StackModifier) : RefreshStackHooks();
                return;
            }
        }
        private void OnPluginUnloaded(Plugin plugin) {
            if (!_initialized) return;
            switch (plugin.Name) {
                case nameof(Backpacks) : _backpacksAdapter.HandleLoadedChanged();
                return;
                case nameof(ItemRetriever) : _itemRetrieverAdapter.HandleLoadedChanged();
                return;
                case nameof(Loottable) : RefreshStackHooks();
                return;
                case nameof(SkillTree) : _skillTreeAdapter.HandleLoadedChanged();
                return;
                case nameof(StackModifier) : RefreshStackHooks();
                return;
            }
        }
        // Subscribed only while wearable bags are enabled.private object CanWearItem(PlayerInventory playerInventory, Item item, int targetSlot)
        {
            if (item.info.itemid != BagItemId) return null;
            // Disallow wearing vanilla loot bags, necessary because of the ItemModWearable component.if (!_bagManager.IsBag(item))
            return False;
            if (targetSlot == Locker.backpackSlotIndex) {
                if (_config.Wearable.AllowInBackpackSlot) return True;
            }
            else if (_config.Wearable.AllowInNonBackpackSlot) {
                return True;
            }
            // Must return false instead of null, due to the existence of the ItemModWearable component.return False;
        }
        // Subscribed only while wearable bags are enabled.private object CanLockerAcceptItem(Locker locker, Item item, int targetSlot)
        {
            if (!_bagManager.IsBag(item)) return null;
            if (locker.IsBackpackSlot(targetSlot)) return _config.Wearable.AllowInBackpackSlot ? True : null;
            if (locker.GetRowType(targetSlot) == Locker.RowType.Clothing) return _config.Wearable.AllowInNonBackpackSlot ? True : null;
            return null;
        }
        // Always subscribed to avoid bag limit issues when switching non-empty bags.// Also subscribed to allow items to be swapped in clothing slots.private object OnLockerSwap(Locker locker, int outfitIndex, BasePlayer player)
        {
            if (locker.IsEquipping()) return null;
            var startIndex = outfitIndex * 14;
            var wantsToTransferBag = false;
            if (_config.Wearable.Enabled) {
                // Override locker swap if any bag is being worn.for (var i = 0; i < Locker.attireSize; i++)
                {
                    var lockerItem = locker.inventory.GetSlot(startIndex + i);
                    if (lockerItem != null && _bagManager.IsBag(lockerItem)) {
                        wantsToTransferBag = true;
                        break;
                    }
                    var playerItem = player.inventory.containerWear.GetSlot(i);
                    if (playerItem != null && _bagManager.IsBag(playerItem)) {
                        wantsToTransferBag = true;
                        break;
                    }
                }
            }
            if (!wantsToTransferBag) {
                // Override locker swap if any non-empty bag is being transferred.for (var i = 0; i < Locker.beltSize; i++)
                {
                    var lockerItem = locker.inventory.GetSlot(startIndex + i);
                    if (lockerItem != null && _bagManager.IsNonEmptyBag(lockerItem)) {
                        wantsToTransferBag = true;
                        break;
                    }
                    var playerItem = player.inventory.containerBelt.GetSlot(i);
                    if (playerItem != null && _bagManager.IsNonEmptyBag(playerItem)) {
                        wantsToTransferBag = true;
                        break;
                    }
                }
            }
            if (!wantsToTransferBag) return null;
            var didSwapAnything = false;
            // Move attire and belt items to limbo to avoid bag limit issues.for (var i = 0; i < Locker.attireSize; i++)
            {
                var playerItem = player.inventory.containerWear.GetSlot(i);
                if (playerItem == null) continue;
                playerItem.RemoveFromContainer();
                _lockerItemBuffer[i] = playerItem;
            }
            for (var i = 0; i < Locker.beltSize; i++) {
                var playerItem = player.inventory.containerBelt.GetSlot(i);
                if (playerItem == null) continue;
                playerItem.RemoveFromContainer();
                _lockerItemBuffer[i + Locker.attireSize] = playerItem;
            }
            // Move items from locker to player inventory.for (var i = 0; i < Locker.attireSize; i++)
            {
                var lockerItemIndex = startIndex + i;
                var lockerItem = locker.inventory.GetSlot(lockerItemIndex);
                if (lockerItem == null) continue;
                didSwapAnything = true;
                if (lockerItem.info.category != ItemCategory.Attire && !_bagManager.IsBag(lockerItem) || !lockerItem.MoveToContainer(player.inventory.containerWear, i)) {
                    lockerItem.Drop(locker.GetDropPosition(), locker.GetDropVelocity());
                }
            }
            for (var i = 0; i < Locker.beltSize; i++) {
                var lockerItemIndex = startIndex + Locker.attireSize + i;
                var lockerItem = locker.inventory.GetSlot(lockerItemIndex);
                if (lockerItem == null) continue;
                didSwapAnything = true;
                if (!lockerItem.MoveToContainer(player.inventory.containerBelt, i)) {
                    lockerItem.Drop(locker.GetDropPosition(), locker.GetDropVelocity());
                }
            }
            // Move items from limbo to locker.for (var i = 0; i < Locker.attireSize; i++)
            {
                var lockerItemIndex = startIndex + i;
                var playerItem = _lockerItemBuffer[i];
                if (playerItem == null) continue;
                didSwapAnything = true;
                if (playerItem.info.category != ItemCategory.Attire && !_bagManager.IsBag(playerItem) || !playerItem.MoveToContainer(locker.inventory, lockerItemIndex)) {
                    playerItem.Drop(locker.GetDropPosition(), locker.GetDropVelocity());
                }
                _lockerItemBuffer[i] = null;
            }
            for (var i = 0; i < Locker.beltSize; i++) {
                var bufferIndex = Locker.attireSize + i;
                var lockerItem = _lockerItemBuffer[bufferIndex];
                if (lockerItem == null) continue;
                var lockerItemIndex = startIndex + Locker.attireSize + i;
                didSwapAnything = true;
                if (!lockerItem.MoveToContainer(locker.inventory, lockerItemIndex)) {
                    lockerItem.Drop(locker.GetDropPosition(), locker.GetDropVelocity());
                }
                _lockerItemBuffer[bufferIndex] = null;
            }
            if (didSwapAnything) {
                Effect.server.Run(locker.equipSound.resourcePath, player, StringPool.Get("spine3"), Vector3.zero, Vector3.zero);
                locker.SetFlag(Locker.LockerFlags.IsEquipping, true);
                locker.Invoke(locker.ClearEquipping, 1.5f);
            }
            return False;
        }
        // Subscribed only while the visual backpack is enabled.private void OnEntitySaved(BasePlayer player, BaseNetworkable.SaveInfo saveInfo)
        {
            if (!ShouldDisplayVisualBackpack(player)) return;
            AddVisualBackpackItemToContainer(saveInfo.msg.basePlayer.inventory.invWear);
        }
        // Subscribed only while the visual backpack is enabled.private void OnInventoryNetworkUpdate(PlayerInventory playerInventory, ItemContainer container,
        ProtoBuf.UpdateItemContainer payload, PlayerInventory.Type inventoryType) {
            if (inventoryType != PlayerInventory.Type.Wear || !ShouldDisplayVisualBackpack(playerInventory.baseEntity)) return;
            AddVisualBackpackItemToContainer(payload.container[0]);
        }
        // Possible improvement: Dynamically subscribe only while a player is looting a bag.// Possible improvement: Attach components with `PlayerStoppedLooting(BasePlayer)` on-demand and keep them until plugin unload.private void OnPlayerLootEnd(PlayerLoot playerLoot)
        {
            var player = playerLoot.baseEntity;
            if (player == null) return;
            if (!IsLootingBag(player, out var bagInfo)) return;
            _bagManager.HandlePlayerStoppedLooting(player, bagInfo);
        }
#if FEATURE_BELT_ACTIVATION
        // Subscribed only while belt activation is enabled.private void OnActiveItemChanged(BasePlayer player, Item previousItem, Item nextItem)
        {
            if (!IsRealPlayer(player)) return;
            if (nextItem != null) {
                var nextBagInfo = _bagManager.EnsureBagInfoIfEligible(nextItem);
                if (nextBagInfo != null) {
                    if (nextItem == previousItem) {
                        StopLooting(player);
                    }
                    else {
                        if (player.inventory.loot.IsLooting()) {
                            AttemptOpenBag(player, nextBagInfo);
                        }
                        else {
                            // Player might be scrolling through their items, so don't open immediately._beltTracker.AddPlayer(player, nextBagInfo);
                        }
                        // var originalPosition = nextItem.position;
                        // timer.Once(0.1f, () =>
                        // {
                        //     var activeItem = player.GetActiveItem();
                        //     if (nextItem == activeItem && activeItem.position == originalPosition)
                        //     {
                        //         AttemptOpenBag(player, nextBagInfo);
                        //     }
                        // });
                    }
                }
                return;
            }
            if (previousItem == null) return;
            var previousBagInfo = _bagManager.GetBagInfo(previousItem);
            if (previousBagInfo == null) return;
            _beltTracker.RemovePlayer(player);
            var lootContainer = player.inventory.loot.containers.FirstOrDefault();
            if (lootContainer == null || lootContainer != previousItem.contents) return;
            // The following code must be in a block to ensure the delegate doesn't get created for code paths that don't reach this.{
            var originalContainer = previousItem.parent;
            var originalPosition = previousItem.position;
            var originalAmount = previousItem.amount;
            NextTick(() => {
                if (originalContainer != previousItem.parent || originalPosition != previousItem.position || originalAmount != previousItem.amount) return;
                ClosePlayerInventory(player);
            }
            );
        }
    }
#endif
    // Subscribed only while the belt button is enabled.private void OnPlayerSleepEnded(BasePlayer player)
    {
        if (ShouldDisplayBeltUI(player)) {
            _beltUIRenderer.CreateUIIfMissing(player);
        }
    }
    // Always subscribed due to vanilla issues that need to be mitigated, most importantly for dropping locked bags.private object OnItemAction(Item item, string action, BasePlayer player)
    {
        switch (action) {
            // Unlock the bag before it's dropped since otherwise vanilla won't allow the action.case "drop":
            {
                if (!item.IsLocked()) return null;
                var bagInfo = _bagManager.GetBagInfo(item);
                if (bagInfo == null) return null;
                // Set the item unlocked so it can be dropped.bagInfo.SetLocked(false);
                if (bagInfo.Item.amount > 1) {
                    var parentSupervisor = bagInfo.ParentSupervisor;
                    if (parentSupervisor != null) {
                        NextTick(() => parentSupervisor.RefreshBags());
                    }
                }
                return null;
            }
            // Fix issue where wrapping a gift can delete the contents.case "wrap":
            {
                if (item.amount != 1) return null;
                if (!ItemUtils.HasItemMod(item.info, out ItemModWrap itemModWrap)) return null;
                if (!IsInsideBag(item, out var parentContainer)) return null;
                if (!CanPlayerMoveItem(player, item)) return null;
                var childItem = item.contents.GetSlot(0);
                if (childItem == null) return null;
                var position = item.position;
                item.RemoveFromContainer();
                var wrappedItem = ItemManager.Create(itemModWrap.wrappedDefinition);
                if (!childItem.MoveToContainer(wrappedItem.contents)) {
                    wrappedItem.Remove();
                    player.GiveItem(childItem);
                    return null;
                }
                if (!wrappedItem.MoveToContainer(parentContainer, position)) {
                    player.GiveItem(wrappedItem);
                }
                item.Remove();
                if (itemModWrap.successEffect.isValid) {
                    Effect.server.Run(itemModWrap.successEffect.resourcePath, player.eyes.position);
                }
                return False;
            }
            // Fix issue where unwrapping a gift can delete the contents.case "open":
            {
                if (item.amount != 1) return null;
                if (!ItemUtils.HasItemMod(item.info, out ItemModOpenWrapped itemModOpenWrapped)) return null;
                if (!IsInsideBag(item, out var parentContainer)) return null;
                if (!CanPlayerMoveItem(player, item)) return null;
                var childItem = item.contents.GetSlot(0);
                if (childItem == null) return null;
                var position = item.position;
                item.RemoveFromContainer();
                if (!childItem.MoveToContainer(parentContainer, position)) {
                    player.GiveItem(childItem);
                }
                item.Remove();
                if (itemModOpenWrapped.successEffect.isValid) {
                    Effect.server.Run(itemModOpenWrapped.successEffect.resourcePath, player.eyes.position);
                }
                return False;
            }
            default : return null;
        }
    }
    // Always subscribed to prevent containers from accepting bags when bag limit is reached.private object CanAcceptItem(ItemContainer container, Item item, int targetPosition)
    {
        // Allow bags to move around in the same inventory.if (item.parent == container)
        return null;
        if (!EnforceBagLimitsInContainer(container)) return null;
        var bagInfo = _bagManager.EnsureBagInfoIfEligible(item);
        if (bagInfo == null || !bagInfo.HasItems) return null;
        var supervisor = _limitManager.GetSupervisor(container);
        if (supervisor != null) {
            return supervisor.CanAddBag(bagInfo, container) ? null : CannotAccept;
        }
        // If there is no supervisor, assume the container does not have any bags yet.var limitProfile = _limitManager.GetLimitProfile(container);
        if (limitProfile.MaxTotalBags == 0 || limitProfile.GetMaxBagsOfCategory(bagInfo.BagProfile.CategoryNumber) == 0) return CannotAccept;
        return null;
    }
    // Subscribed only while multiple bag profiles share a skin ID, and while no suitable stack plugin is loaded.private Item OnItemSplit(Item sourceItem, int amount)
    {
        var bagInfo = _bagManager.EnsureBagInfoIfEligible(sourceItem);
        if (bagInfo == null) return null;
        sourceItem.amount -= amount;
        sourceItem.MarkDirty();
        return CreateBag(bagInfo.BagProfile, amount);
    }
    // Subscribed only while stack protection is enabled.private object CanStackItem(Item hostItem, Item movedItem)
    {
        if (hostItem.info.stackable <= 1) return null;
        var bagInfo = _bagManager.EnsureBagInfoIfEligible(hostItem);
        if (bagInfo == null) return null;
        var otherBagInfo = _bagManager.EnsureBagInfoIfEligible(movedItem);
        if (otherBagInfo == null) return null;
        if (bagInfo.BagProfile != otherBagInfo.BagProfile) return False;
        if (ItemUtils.HasChildren(movedItem)) return False;
        return null;
    }
    // Subscribed only while no suitable stack plugin is loaded.private object CanCombineDroppedItem(DroppedItem itemEntity, DroppedItem otherItemEntity)
    {
        var bagInfo = _bagManager.EnsureBagInfoIfEligible(itemEntity.item);
        if (bagInfo == null) return null;
        var otherBagInfo = _bagManager.EnsureBagInfoIfEligible(otherItemEntity.item);
        if (otherBagInfo == null) return null;
        if (bagInfo.BagProfile != otherBagInfo.BagProfile) return False;
        if (ItemUtils.HasChildren(itemEntity.item) || ItemUtils.HasChildren(otherItemEntity.item)) return False;
        return null;
    }
    // Always subscribed to mitigate an issue where quick-looting an item from a bag in your inventory would move it back into the bag.// Possible improvement: Dynamically subscribe only while a player is looting a bag in their inventory, and while stack improvements is enabled.private object CanMoveItem(Item item, PlayerInventory playerInventory, ItemContainerId targetContainerId, int targetSlot, int amount)
    {
        if (targetContainerId.Value == 0 && targetSlot == -1 && IsInsideBag(item, out var parentContainer) && parentContainer.playerOwner == playerInventory.baseEntity) {
            // Player is quick moving an item from a bag in their inventory into their inventory.// This needs to be overriden since vanilla logic would move the item back into the bag.if (!playerInventory.GiveItem(item))
            {
                playerInventory.baseEntity.ChatMessage("GiveItem failed!");
            }
            return False;
        }
        // When attempting to move a bag into another bag, try to stack it if the destination bag is closed.if (_config.EnableStackImprovements)
        {
            var bagInfo = _bagManager.GetBagInfo(item);
            if (bagInfo == null) return null;
            // If the bag contains items, only allow moving a partial stack from it onto another bag.if (bagInfo.HasItems && (item.amount == 1 || amount >= item.amount))
            return null;
            // Target container must have a parent item (must be a bag).var targetContainer = playerInventory.FindContainer(targetContainerId);
            if (targetContainer?.parent == null) return null;
            // Target container must be a bag of the same profile as the bag being moved.var destinationBagInfo = _bagManager.GetBagInfo(targetContainer.parent);
            if (destinationBagInfo == null || destinationBagInfo.BagProfile != bagInfo.BagProfile) return null;
            // Don't stack if looting the destination bag (move the bag into it instead).if (playerInventory.loot.containers.FirstOrDefault() == targetContainer)
            return null;
            // Don't allow stacking onto a bag item that doesn't accept items (e.g., that bag is in a loot container).if (targetContainer.PlayerItemInputBlocked())
            return null;
            // Vanilla checks to prohibit taking items in various situations (e.g., from a locked container).if (item.parent.entityOwner is ICanMoveFrom canMoveFrom
            && !canMoveFrom.CanMoveFrom(playerInventory.baseEntity, item)) return null;
            // If amount is negative, the player is trying to move the whole stack.if (amount <= 0)
            {
                amount = item.amount;
            }
            amount = Mathf.Clamp(amount, 1, destinationBagInfo.Item.MaxStackable() - destinationBagInfo.Item.amount);
            if (amount <= 0) return null;
            destinationBagInfo.Item.amount += amount;
            destinationBagInfo.Item.MarkDirty();
            item.amount -= amount;
            if (item.amount > 0) {
                item.MarkDirty();
            }
            else {
                item.RemoveFromContainer();
                item.Remove();
            }
            return False;
        }
        return null;
    }
    // Subscribed only while looting or equipping dropped bags is enabled.private object OnItemPickup(Item item, BasePlayer player)
    {
        var bagInfo = _bagManager.EnsureBagInfoIfEligible(item);
        if (bagInfo == null) return null;
        if (CanAcceptItem(player.inventory.containerMain, item, -1) == null) return null;
        if (_config.Wearable.Enabled && _config.WearableBagLimits.Enabled && _config.DroppedBagSettings.AutoEquipDroppedBags && item.MoveToContainer(player.inventory.containerWear)) return False;
        if (!_config.DroppedBagSettings.EnableOpeningDroppedBags) return null;
        var droppedItem = item.GetWorldEntity() as DroppedItem;
        if (droppedItem == null || droppedItem.IsDestroyed) return null;
        // Reset the removal time to reduce the likelihood of the bag item being removed while it's being looted.ResetRemovalTime(droppedItem);
        AttemptOpenBag(player, bagInfo);
        return False;
    }
    // Subscribed only while loot spawns are enabled.private void OnLootSpawn(LootContainer lootContainer)
    {
        if (lootContainer.OwnerID != 0 || lootContainer.skinID != 0) return;
        if (!_config.LootConfig.SpawnChanceByPrefabId.TryGetValue(lootContainer.prefabID, out var spawnChanceByProfile)) return;
        ScheduleLootSpawn(lootContainer, spawnChanceByProfile);
    }
    // Subscribed only while a bag profile has recycling enabled.private object CanBeRecycled(Item item, Recycler recycler)
    {
        if (item == null) return null;
        var bagInfo = _bagManager.GetBagInfo(item);
        if (bagInfo == null) return null;
        return ObjectCache.Get(bagInfo.BagProfile.CanBeRecycled && !bagInfo.HasItems);
    }
    // Subscribed only while a bag profile has recycling enabled.private object CanRecycle(Recycler recycler, Item item)
    {
        return CanBeRecycled(item, recycler);
    }
    // Subscribed only while a bag profile has recycling enabled.private object OnItemRecycle(Item item, Recycler recycler)
    {
        var recycleInfo = _bagManager.GetBagInfo(item)?.BagProfile.RecycleInfo;
        if (recycleInfo == null || !recycleInfo.Enabled) return null;
        if (recycleInfo.Ingredients == null) return null;
        var recycleAmount = Mathf.CeilToInt(Mathf.Min(item.amount, item.info.stackable * 0.1f));
        item.UseItem(recycleAmount);
        foreach (var ingredientInfo in recycleInfo.Ingredients) {
            if (ingredientInfo.Amount <= 0 || ingredientInfo.ItemDefinition == null) continue;
            var ingredientItem = ItemManager.Create(ingredientInfo.ItemDefinition, ingredientInfo.Amount * recycleAmount, ingredientInfo.SkinId);
            if (ingredientItem == null) continue;
            if (!string.IsNullOrWhiteSpace(ingredientInfo.DisplayName)) {
                ingredientItem.name = ingredientInfo.DisplayName;
            }
            recycler.MoveItemToOutput(ingredientItem);
        }
        return False;
    }
    // Subscribed only while no suitable stack plugin is loaded.private void OnDroppedItemCombined(DroppedItem droppedItem)
    {
        var item = droppedItem.item;
        if (item == null) return;
        var bagInfo = _bagManager.GetBagInfo(item);
        if (bagInfo == null) return;
        // Delay in case another plugin (such as DespawnConfig) is generically resetting despawn time.ResetRemovalTime(droppedItem, delayed: true);
    }
    // Only subscribed while at least one bag profile exists that uses a Stack profile that has item-specific or skin-specific overrides.private object OnMaxStackable(Item item)
    {
        var stackSizeOverride = GetParentBag(item)?.BagProfile?.StackProfile?.GetStackSizeOverride(item);
        if (stackSizeOverride == null) return null;
        var stackSizeInt = (int)stackSizeOverride;
        if (stackSizeInt >= item.info.stackable) {
            // Don't allow increasing max stack size, since that creates some complications.return null;
        }
        if (item.parent != null && item.parent.maxStackSize != 0 && stackSizeInt >= item.parent.maxStackSize) {
            // Don't allow increasing max stack size, since that creates some complications.return null;
        }
        return stackSizeOverride;
    }
    private void OnUserPermissionGranted(string userIdString, string perm) {
        if (perm.StartsWith(PlayerLimitProfile.LimitPrefix, StringComparison.OrdinalIgnoreCase)) {
            _limitManager.RefreshPlayerLimitProfile(userIdString);
        }
        else if (perm.StartsWith(WearableLimitProfile.LimitPrefix, StringComparison.OrdinalIgnoreCase)) {
            _limitManager.RefreshWearableLimitProfile(userIdString);
        }
        else if (perm.StartsWith(BackpackLimitProfile.LimitPrefix, StringComparison.OrdinalIgnoreCase)) {
            _limitManager.RefreshBackpackLimitProfile(userIdString);
        }
        else if (perm.StartsWith(ContainerLimitProfile.LimitPrefix, StringComparison.OrdinalIgnoreCase)) {
            _limitManager.RefreshContainerLimitProfileForPlayer(userIdString);
        }
    }
    private void OnUserPermissionRevoked(string userIdString, string perm) {
        OnUserPermissionGranted(userIdString, perm);
    }
    private void OnGroupPermissionGranted(string groupName, string perm) {
        if (perm.StartsWith(PlayerLimitProfile.LimitPrefix, StringComparison.OrdinalIgnoreCase)) {
            _limitManager.RefreshPlayerLimitProfilesForGroup(groupName);
        }
        else if (perm.StartsWith(WearableLimitProfile.LimitPrefix, StringComparison.OrdinalIgnoreCase)) {
            _limitManager.RefreshWearableLimitProfilesForGroup(groupName);
        }
        else if (perm.StartsWith(BackpackLimitProfile.LimitPrefix, StringComparison.OrdinalIgnoreCase)) {
            _limitManager.RefreshBackpackLimitProfilesForGroup(groupName);
        }
        else if (perm.StartsWith(ContainerLimitProfile.LimitPrefix, StringComparison.OrdinalIgnoreCase)) {
            _limitManager.RefreshContainerLimitProfiles();
        }
    }
    private void OnGroupPermissionRevoked(string groupName, string perm) {
        OnGroupPermissionGranted(groupName, perm);
    }
    private void OnUserGroupAdded(string userIdString, string groupName) {
        _limitManager.RefreshPlayerLimitProfile(userIdString);
        if (_config.WearableBagLimits.Enabled) {
            _limitManager.RefreshWearableLimitProfile(userIdString);
        }
        _limitManager.RefreshBackpackLimitProfile(userIdString);
        _limitManager.RefreshContainerLimitProfileForPlayer(userIdString);
    }
    private void OnUserGroupRemoved(string userIdString, string groupName) {
        OnUserGroupAdded(userIdString, groupName);
    }
#endregion
#region API
    private class ApiInstance {
        public Dictionary<string, object> _apiWrapper {
            get;
        }
        private BagOfHolding _plugin;
        private Configuration _config => _plugin._config;
        private BagManager _bagManager => _plugin._bagManager;
        private LimitManager _limitManager => _plugin._limitManager;
        public ApiInstance(BagOfHolding plugin) {
            _plugin = plugin;
            _apiWrapper = new Dictionary<string, object> {
                [nameof(IsBag)]
                = new Func<Item, bool>(IsBag), [nameof(IsLootingBag)] = new Func<BasePlayer, bool>(IsLootingBag), [nameof(GetBagProfileName)] = new Func<Item, string>(GetBagProfileName), [nameof(OpenBag)] = new Func<BasePlayer, Item, bool>(OpenBag), [nameof(CreateBag)] = new Func<string, int, Item>(CreateBag), [nameof(GiveBag)] = new Func<BasePlayer, string, int, Item>(GiveBag), [nameof(CreateKitBag)] = new Func<string, string, BasePlayer, Item>(CreateKitBag), [nameof(GiveKitBag)] = new Func<BasePlayer, string, string, Item>(GiveKitBag), [nameof(ChangeBagProfile)] = new Func<Item, string, BasePlayer, bool>(ChangeBagProfile), [nameof(UpgradeBag)] = new Func<Item, BasePlayer, bool>(UpgradeBag), [nameof(FindUnlockedBag)] = new Func<ItemContainer, string, int, int, Item>(FindUnlockedBag), [nameof(DiscoverBags)] = new Action<ItemContainer>(DiscoverBags), [nameof(CreateLimitProfile)] = new Func<string, object>(CreateLimitProfile), [nameof(SetLimitProfile)] = new Func<ItemContainer, object, bool>(SetLimitProfile), [nameof(RemoveLimitProfile)] = new Action<ItemContainer>(RemoveLimitProfile),
            }
            ;
        }
        public bool IsBag(Item item) {
            return _bagManager.EnsureBagInfoIfEligible(item) != null;
        }
        public bool IsLootingBag(BasePlayer player) {
            return _plugin.IsLootingBag(player);
        }
        public string GetBagProfileName(Item item) {
            return _bagManager.EnsureBagInfoIfEligible(item)?.BagProfile.Name;
        }
        public bool OpenBag(BasePlayer player, Item item) {
            if (!VerifyValidBag(nameof(API_OpenBag), item, out var bagInfo)) return false;
            return _plugin.AttemptOpenBag(player, bagInfo);
        }
        public Item CreateBag(string profileName, int amount = 1) {
            if (amount <= 0) return null;
            if (!VerifyValidBagProfile(nameof(API_CreateBag), profileName, out var bagProfile)) return null;
            return _plugin.CreateBag(bagProfile, amount);
        }
        public Item GiveBag(BasePlayer player, string profileName, int amount = 1) {
            if (amount <= 0) return null;
            if (!VerifyValidBagProfile(nameof(API_GiveBag), profileName, out var bagProfile)) return null;
            var bagItem = _plugin.CreateBag(bagProfile, amount);
            if (bagItem == null) return null;
            player.GiveItem(bagItem);
            return bagItem;
        }
        public Item CreateKitBag(string profileName, string kitName, BasePlayer initiator = null) {
            if (!VerifyValidBagProfile(nameof(API_CreateKitBag), profileName, out var bagProfile)) return null;
            return _plugin.CreateKitBag(bagProfile, kitName, initiator);
        }
        public Item GiveKitBag(BasePlayer player, string profileName, string kitName) {
            if (!VerifyValidBagProfile(nameof(API_GiveBag), profileName, out var bagProfile)) return null;
            if (!VerifyValidKit(nameof(API_GiveBag), kitName)) return null;
            var bagItem = _plugin.CreateKitBag(bagProfile, kitName, player);
            if (bagItem == null) return null;
            player.GiveItem(bagItem);
            return bagItem;
        }
        public bool ChangeBagProfile(Item item, st
// ... [Code preview truncated due to length. Full source in attached file] ...

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