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

FREE Loottable : 1.0.20 1.0.20

Нет прав для скачивания
C#:
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Globalization;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Cui;
using Oxide.Plugins.LoottableExtensions;
using UnityEngine;
using UnityEngine.Networking;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using StaticLootableModels = Oxide.Plugins.LoottableExtensions.StaticLootableModels;
/**
 * VERSION HISTORY
 * 
 * V 1.0.1
 * - replaced FindObjectsOfType with BaseNetworkable.serverEntities.Find
 * 
 * V 1.0.2
 * - added gui item selection
 * - auto refresh loot after closing the editor
 * - added gather manager ui
 * - fixed editor sometimes not opening bug
 * - loot distribution rework
 * - stacksize bugfixes
 * 
 * V 1.0.3
 * - fixed category item distribution
 * - item.stackable now adjusts with stacksize
 * - fixed item amount in crate config ignored
 * 
 * V 1.0.4
 * - updated input fields to work with latest oxide version
 * - changed input field clamping to match min/max values
 * - fixed npc corpse custom loot profiles
 * - added gather rates for animals
 * 
 * V 1.0.5
 * - fixed plugin not loading on server startup
 * - fixed players without admin permission unable to open editor
 * 
 * V 1.0.6
 * - fixed invalid cast exception when adjusting item count of npcs
 * - added page navigation to crate loot editor
 * - added hooks for container population
 * - fixed invisible cursor in some input fields
 * - improved image loading
 * 
 * V 1.0.7
 * - added flags to bypass editor restrictions
 * - added addition loot option
 * - added default loot config for npcs and some crates
 * - added the option to import loot tables
 * - fixed a bug where the despawn time of a locked crate would be reset upon reload
 * 
 * V 1.0.7 HOTFIX / V 1.0.8
 * - fixed plugin not working after Rust update
 * 
 * V 1.0.9
 * - fixed several stacksize related bugs
 * - added spray can decal item to ignore list
 * - excluded player quarries form quarry config
 * - fixed max item amount of crates ignored in some cases
 * - loot distribution improvements (again)
 * - fixed editor not opening without image library
 * - removed stacking of items w/ durability field as it caused confusion
 * - replaced GatherControl and StacksizeControl lootable with GenericLootable
 * - wear container now only accepts 1 item (no stacked clothes)
 * - heli fuel can now be adjusted in the editor
 * - smelting speed can now be controlled in the editor
 * - updated images for some crates
 * 
 * V 1.0.10
 * - added bbq and campfire to smelting config
 * - new icons for mini and srappy fuel config
 * - added the option to multiply an entire loot table
 * - slightly changed category section to look better
 * - fixed invalid cast exception when editing category item amounts
 * - fixed typo in 'UnlockGahterMultiplier' that caused an error when changing gather multipliers
 * - fixed recycler not stopping when items are removed
 * - fixed a bug where too much items are in a crate (again)
 * - allowed multiple items with same itemid but different skin id
 * - added the option to add extra items to an item
 * - added air drop config options
 * - added scientistnpc_roam to npc config
 * - fixed gather items not droping when inventory is full
 * - added support for static lootables (wip)
 * - disable quick smelt if speed multiplier is 1
 * - added category stack size multipliers
 * 
 * V 1.0.11
 * - fixed plugin not initializing
 * 
 * V 1.0.12
 * - fixed server freezing (caused by infinite loop)
 * - fixed unable to edit gather items
 * - items can now have drop chances as low as 0.01% (was 1% before)
 * - added the option to multiply items of collectables
 * - added the option to load the default config for collectables
 * - improved loot distribution
 * - added indicator for extra items
 * 
 * V 1.0.13
 * - fixed unable to edit excavator/quarry items
 * - allowed multiple items in excavator config
 * - support for static lootables (beta)
 * - changed ui parent from hud.menu to overlay
 * - fixed recycler bugs
 * - added mixing table to smelting speed config
 * - added drop chances for gather items
 * 
 * V 1.0.14
 * - fixed some typos
 * - fixed missing delete button when editing items
 * - fixed supply drop bug with loot defender (and fancy drop)
 * - added charcoal config for furnaces
 * - more config options for static lootables (wip)
 * 
 * V 1.0.15
 * - removed deprecated hook OnCollectiblePickup and replaced it
 * - cleaned up api code
 * - fixed weird console/debug message behavior
 * - fixed null reference exception when populating npc corpses
 * - added invisible crates
 * - slightly changed loot refresh function and included loot wagons
 * - added train wagon loot config
 * 
 * V 1.0.16
 * - fixed null reference exception in InitializeEditorConfig
 * - fixed null reference exception in OnCollectiblePickup
 * - added locomotive to list of ignored items
 * - added support for custom items
 * - fixed issue with xlevels OnCollectible pickup
 * - fixed issue when splitting clones
 * - fixed issues with simple splitter
 * - added hook OnCargoPlaneSignaled to custom supply drops
 * - fixed null reference exception when calling a supply drop
 * 
 * V 1.0.17
 * - patched for rust update
 * 
 * V 1.0.18
 * - fixed problems caused by update
 * - re-enabled supply drop
 * - re-enabled mixing table
 * - fixed npc loot issues
 * - fixed error when creating items
 * - fixed null reference exception in OnEntitySpawned
 * 
 * V 1.0.19
 * - fix IndexOutOfRangeException when selecting last category in stack size editor
 * - added TracedAction to find origin of NullReferenceException in timer callback in OnEntitySpawned(LootableCorpse corpse)
 * - fixed furnaces not working after server reboot
 * - fixed stacking of world items with different skins
 * 
 * V 1.0.20
 * - fix bug with category stack size multiplier
 * - fix error when npc corpse of BetterNpc NPC spawned
 * - added flag to disable stacking hooks
 * 
**/
namespace Oxide.Plugins {
    [Info("Loottable", "nuclear", "1.0.20")]
    [Description("A configurable loot table with GUI")]
    public class Loottable : CovalencePlugin {
#region Fields
        [PluginReference]
        private Plugin ImageLibrary, StaticLootables, LootDefender, FancyDrop, SimpleSplitter, BetterNpc;
        private static bool UseStaticLootables => (bool)_instance.StaticLootables;
        private const int configVersion = 6;
        private const string COMMAND = "loottable";
        private const string PERM_EDIT = "loottable.edit";
        private const string PERM_DEBUG = "loottable.debug";
        private static readonly NumberFormatInfo DEFAULT_NUMBER_FORMAT = new CultureInfo("en", false).NumberFormat;
        private static NumberFormatInfo NUMBER_FORMAT = DEFAULT_NUMBER_FORMAT;
        private static new string Name => ((CovalencePlugin)_instance).Name;
        private static EditorConfig Flags;
        private static Loottable _instance;
#endregion
#region Editor Config
        private static void InitializeEditorConfig() {
            Flags = Interface.Oxide.DataFileSystem.ReadObject<EditorConfig>(DataFilePath("editorconfig"));
            if (Flags == null) {
                CErr("Editor config is corrupted, creating a new one");
                Flags = new EditorConfig();
            }
            Flags.Validate();
            SaveEditorConfig();
        }
        private static void SaveEditorConfig() {
            Interface.Oxide.DataFileSystem.WriteObject<EditorConfig>(DataFilePath("editorconfig"), Flags);
        }
        private class EditorConfig {
            [JsonProperty("version")]
            private int version = configVersion;
            public int ItemLimit {
                get;
                private set;
            }
            = 1000000;
            public MinMaxFloat GatherMultiplier {
                get;
                private set;
            }
            = new MinMaxFloat(1f, 1000f);
            public MinMaxFloat FurnaceMultiplier {
                get;
                private set;
            }
            = new MinMaxFloat(0.1f, 100f);
            public MinMaxFloat ItemMultiplier {
                get;
                private set;
            }
            = new MinMaxFloat(0.1f, 10f);
            [JsonIgnore]
            public bool Debug => flags["Debug"];
            [JsonIgnore]
            public bool UnlockGatherMultiplier => flags["UnlockGatherMultiplier"];
            [JsonIgnore]
            public bool DisableItemLimit => flags["DisableItemLimit"];
            [JsonIgnore]
            public bool UnlockFurnaceMultiplier => flags["UnlockFurnaceMultiplier"];
            [JsonIgnore]
            public bool UnlockItemMultiplier => flags["UnlockItemMultiplier"];
            [JsonIgnore]
            public bool DisableStackingHooks => flags["DisableStackingHooks"];
            [JsonIgnore]
            public bool Update {
                get;
            }
            = false;
            public Dictionary<string, bool> flags {
                get;
                private set;
            }
            = new Dictionary<string, bool> {
                {
                    "Debug", false
                }
                , {
                    "UnlockGatherMultiplier", false
                }
                , {
                    "DisableItemLimit", false
                }
                , {
                    "UnlockFurnaceMultiplier", false
                }
                , {
                    "UnlockItemMultiplier", false
                }
                , {
                    "DisableStackingHooks", false
                }
            }
            ;
            public EditorConfig() {
            }
            [JsonConstructor]
            public EditorConfig(int version, int ItemLimit, int ItemLimitUnlocked, MinMaxFloat GatherMultiplier, MinMaxFloat FurnaceMultiplier, Dictionary<string, bool> flags) {
                if (version != configVersion) Update = true;
                this.version = configVersion;
                this.ItemLimit = ItemLimit;
                this.GatherMultiplier = GatherMultiplier;
                this.FurnaceMultiplier = FurnaceMultiplier;
                foreach(var flag in flags) this.flags[flag.Key] = flag.Value;
                Save();
                if (Update) CPrint("Update detected, performing reload of remote content");
            }
            public void Validate() {
                EditorConfig validator = new EditorConfig();
                if (ItemLimit == 0) ItemLimit = validator.ItemLimit;
                if (GatherMultiplier == MinMaxFloat.Null) GatherMultiplier = validator.GatherMultiplier;
                if (FurnaceMultiplier == MinMaxFloat.Null) FurnaceMultiplier = validator.FurnaceMultiplier;
                foreach (var flag in flags.ToList()) {
                    if (!validator.flags.ContainsKey(flag.Key)) flags.Remove(flag.Key);
                }
                foreach (var vflag in validator.flags) {
                    if (!flags.ContainsKey(vflag.Key)) flags.Add(vflag.Key, false);
                }
            }
            public bool TrySetFlag(string flag, string value, out bool newValue) {
                bool val;
                newValue = false;
                value = value.Replace("0", Boolean.FalseString).Replace("1", Boolean.TrueString);
                if (!flags.ContainsKey(flag)) return false;
                if (!Boolean.TryParse(value, out val)) return false;
                flags[flag] = val;
                newValue = val;
                Save();
                return true;
            }
            public void Save() => SaveEditorConfig();
        }
        private static string DataFilePath(string file) => String.Format("{0}/{1}", Name, file);
        private static string DataFilePath(string folder, string file) => String.Format("{0}/{1}/{2}", Name, folder, file);
        private static long CurrentUnixTime() => ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
#endregion
#region Image Library
        private void LoadImages() {
            Dictionary<string, string> images = LootManager.GetImageDictionary();
            images = images.MergeWith(ConfigManager.GetCustomImages());
            Puts($"Import {images.Count} images");
            timer.In(1f, () => {
                if (!ImageLibrary) PrintError("ERROR: ImageLibrary not found, you can download it here: https://umod.org/plugins/image-library");
                else ImageLibrary?.Call("ImportImageList", Title, images, 0UL, Flags?.Update ?? false);
            }
            );
        }
        private CuiImageComponent CreateItemImage(int itemid, ulong skin = 0) {
            if (skin == 0) return new CuiImageComponent {
                ItemId = itemid
            }
            ;
            else return new CuiImageComponent {
                ItemId = itemid, SkinId = skin
            }
            ;
        }
        private CuiRawImageComponent CreateImage(string key) {
            return new CuiRawImageComponent {
                Png = ImageLibrary?.Call<string>("GetImage", key)
            }
            ;
        }
#endregion
#region Custom Hooks
        private static bool CanPopulateContainerHook(LootContainer container) {
            object result = Interface.CallHook("OnContainerPopulate", container);
            return result == null;
        }
        private static bool CanPopulateCorpseHook(LootableCorpse corpse) {
            object result = Interface.CallHook("OnCorpsePopulate", corpse);
            return result == null;
        }
#endregion
#region Hooks
#region Loot
        object OnLootSpawn(LootContainer container) {
            LootableConfig lootConfig = LootManager.GetContainerConfig(container);
            if (lootConfig == null) return null;
            if (!CanPopulateContainerHook(container)) return null;
            if (lootConfig.Enabled) {
                if (lootConfig.lootType == LootableConfig.LootType.Custom) {
                    lootConfig.ApplyToCrate(container);
                    return false;
                }
                else {
                    timer.In(0.1f, () => {
                        lootConfig.ApplyToCrate(container);
                    }
                    );
                    return null;
                }
            }
            return null;
        }
#endregion
#region Quarries
        void OnQuarryToggled(MiningQuarry quarry, BasePlayer player) {
            if (!quarry.isStatic) return;
            var config = LootManager.GetQuarryConfig(quarry);
            if (quarry.IsEngineOn()) config.QuarryInit(ref quarry);
            else config.ResetQuarry(ref quarry);
        }
        void OnQuarryGather(MiningQuarry quarry, Item item) {
            if (!quarry.isStatic) return;
            var config = LootManager.GetQuarryConfig(quarry);
            config.QuarryUpdate(ref quarry, ref item);
        }
        void OnExcavatorMiningToggled(ExcavatorArm arm) {
            var config = LootManager.GetExcavatorConfig();
            config.Apply(ref arm);
        }
        object OnExcavatorGather(ExcavatorArm arm, Item item) {
            var config = LootManager.GetExcavatorConfig();
            config.GatherUpdate(ref arm, item);
            return null;
        }
#endregion
#region NPCs
        void OnEntitySpawned(LootableCorpse corpse) {
            var config = LootManager.GetNpcConfig(corpse);
            if (config == null) return;
            if (!CanPopulateCorpseHook(corpse)) return;
            if (config.Enabled) {
                timer.In(0.1f, () => {
                    if (!corpse.IsDestroyed) {
                        config.ApplyToCorpse(corpse?.containers[0], true);
                    }
                }
                );
            }
        }
        void OnEntitySpawned(BasePlayer npc) {
            if (npc.IsNpc) LootManager.CacheNpc(npc);
        }
#endregion
#region Gathering
        object OnCollectiblePickup(CollectibleEntity entity, BasePlayer player) {
            if (player == null || entity == null) return null;
            CollectibleConfig config = LootManager.GetCollectibleConfig(entity);
            if (config == null) return null;
            if (!config.enabled) return null;
            config.DoPickup(entity, player);
            return false;
        }
        object OnDispenserGather(ResourceDispenser dispenser, BasePlayer player, Item item) {
            BaseEntity ent = dispenser.GetComponent<BaseEntity>();
            GatherConfig config = GatherManager.gatherConfig;
            if (config == null) return null;
            if (!config.enabled) return null;
            item.amount = config.GetNewAmount(ent.ShortPrefabName, item.info.itemid, item.amount);
            return null;
        }
        void OnDispenserBonus(ResourceDispenser dispenser, BasePlayer player, Item item) {
            BaseEntity ent = dispenser.GetComponent<BaseEntity>();
            GatherConfig config = GatherManager.gatherConfig;
            if (config == null) return;
            if (!config.enabled) return;
            item.amount = config.GetNewAmount(ent.ShortPrefabName, item.info.itemid, item.amount);
        }
#endregion
#region Stacking
        bool? CanStackItem(Item item, Item target, bool combine = false) {
            if (Flags.DisableStackingHooks) return null;
            if ((item.GetOwnerPlayer().IsUnityNull() || target.GetOwnerPlayer().IsUnityNull()) && !combine) return null;
            if (item.info.itemid != target.info.itemid) return false;
            if (item.skin != target.skin) return false;
            if (item.name != target.name) return false;
            if (item.condition != target.condition) return false;
            if (item.info.amountType == ItemDefinition.AmountType.Genetics || target.info.amountType == ItemDefinition.AmountType.Genetics) {
                if ((item.instanceData?.dataInt ?? -1) != (target.instanceData?.dataInt ?? -1)) {
                    return false;
                }
            }
            if (target.GetHeldEntity() is BaseProjectile && !combine) {
                BaseProjectile.Magazine mag = (target.GetHeldEntity() as BaseProjectile).primaryMagazine;
                if (mag.contents > 0) {
                    item.parent.GetOwnerPlayer().GiveItem(ItemManager.Create(mag.ammoType, mag.contents));
                    mag.contents = 0;
                }
            }
            if (target.GetHeldEntity() is Chainsaw && !combine) {
                Chainsaw chainsaw = target.GetHeldEntity() as Chainsaw;
                if (chainsaw.ammo > 0) {
                    item.GetOwnerPlayer().GiveItem(ItemManager.Create(chainsaw.fuelType, chainsaw.ammo));
                    chainsaw.ammo = 0;
                }
            }
            if (target.GetHeldEntity() is FlameThrower && !combine) {
                FlameThrower flameThrower = target.GetHeldEntity() as FlameThrower;
                if (flameThrower.ammo > 0) {
                    item.GetOwnerPlayer().GiveItem(ItemManager.Create(flameThrower.fuelType, flameThrower.ammo));
                    flameThrower.ammo = 0;
                }
            }
            if ((target.info.shortname == "hat.miner" || target.info.shortname == "hat.candle") && (target.contents?.itemList.Count ?? 0) > 0 && !combine) {
                Item content = target.contents.itemList.First();
                Item drop = ItemManager.Create(content.info, content.amount, content.skin);
                target.GetOwnerPlayer().GiveItem(drop);
            }
            if (target.GetHeldEntity() is BaseLiquidVessel) {
                if (!target.contents.IsEmpty() || !item.contents.IsEmpty()) return false;
            }
            return true;
        }
        Item OnItemSplit(Item item, int amount) {
            if (Flags.DisableStackingHooks) return null;
            item.amount -= amount;
            Item split = ItemManager.Create(item.info, amount, item.skin);
            split.name = item.name;
            split.condition = item.condition;
            if (item.IsBlueprint()) split.blueprintTarget = item.blueprintTarget;
            item.MarkDirty();
            if (item.info.amountType == ItemDefinition.AmountType.Genetics && item.instanceData != null && item.instanceData.dataInt != 0) {
                split.instanceData = new ProtoBuf.Item.InstanceData() {
                    dataInt = item.instanceData.dataInt, ShouldPool = false
                }
                ;
            }
            if (split.GetHeldEntity() is BaseProjectile) {
                BaseProjectile weapon = split.GetHeldEntity() as BaseProjectile;
                weapon.primaryMagazine.contents = 0;
                weapon.SendNetworkUpdateImmediate();
            }
            if (split.GetHeldEntity() is Chainsaw) {
                Chainsaw chainsaw = split.GetHeldEntity() as Chainsaw;
                if (chainsaw.ammo > 0) {
                    chainsaw.ammo = 0;
                    chainsaw.SendNetworkUpdateImmediate();
                }
            }
            if (split.GetHeldEntity() is FlameThrower) {
                FlameThrower flameThrower = split.GetHeldEntity() as FlameThrower;
                if (flameThrower.ammo > 0) {
                    flameThrower.ammo = 0;
                    flameThrower.SendNetworkUpdateImmediate();
                }
            }
            if (split.GetHeldEntity() is BaseLiquidVessel) {
                split.contents.ForceClear();
                //item.contents.Clear();
            }
            split.MarkDirty();
            return split;
        }
        private void OnItemAddedToContainer(ItemContainer container, Item item) {
            if (Flags.DisableStackingHooks) return;
            BasePlayer player = container.GetOwnerPlayer();
            if (player?.IsNpc ?? true) return;
            if (player.inventory.containerWear.uid == container.uid && item.amount > 1) {
                int amount = item.amount - 1;
                item.amount = 1;
                Item split = ItemManager.Create(item.info, amount, item.skin);
                split.name = item.name;
                split.condition = item.condition;
                item.MarkDirty();
                split.MarkDirty();
                split.MoveToContainer(player.inventory.containerMain);
            }
        }
        bool? CanCombineDroppedItem(WorldItem witem1, WorldItem witem2) => CanStackItem(witem1.item, witem2.item, true);
#endregion
#region Heli Fuel
        private void OnEntitySpawned(MiniCopter heli) {
            if (!ConfigManager.IsReady) return;
            NextTick(() => {
                if (heli.creatorEntity == null) return;
                EntityFuelSystem fs = heli?.GetFuelSystem();
                if (fs == null) return;
                int fuel = heli is ScrapTransportHelicopter ? ConfigManager.AirwolfConfig.scrapHeliFuel : ConfigManager.AirwolfConfig.minicopterFuel;
                if (fuel < 0) return;
                Item fuelItem = fs.GetFuelItem();
                if (fuelItem != null && fuelItem.amount != fuel) {
                    fuelItem.amount = fuel;
                    fuelItem.MarkDirty();
                }
            }
            );
        }
#endregion
#region Smelting & Mixing
        private object OnOvenToggle(StorageContainer furnace, BasePlayer player) {
            if (!ConfigManager.IsReady || SimpleSplitter != null) return null;
            BaseOven oven = furnace.GetComponent<BaseOven>();
            if (oven == null) return null;
            ConfigManager.FurnaceConfig.ToggleOven(oven);
            return null;
        }
        private void OnMixingTableToggle(MixingTable table, BasePlayer player) {
            if (!ConfigManager.IsReady) return;
            ConfigManager.FurnaceConfig.ToggleMixingTable(table);
        }
#endregion
#region Recycler
        object OnRecyclerToggle(Recycler recycler, BasePlayer player) {
            if ((ConfigManager.FurnaceConfig?.recyclingSpeedMultiplier ?? 1) == 1) return null;
            ConfigManager.FurnaceConfig.ToggleRecycler(recycler);
            return null;
        }
#endregion
#region Air Drop
        void OnExplosiveDropped(BasePlayer player, BaseEntity entity, ThrownWeapon item) => OnExplosiveThrown(player, entity, item);
        void OnExplosiveThrown(BasePlayer player, BaseEntity entity, ThrownWeapon item) {
            if (!ConfigManager.IsReady) return;
            if (entity == null) return;
            if (!(entity is SupplySignal)) return;
            ConfigManager.AirdropConfig.OnAirdropThrown(player, entity);
        }
#endregion
#region General
        void OnServerInitialized() {
            LootManager.onReady = LoadImages;
            LootManager.Initialize();
            StackManager.Initialize();
            GatherManager.Initialize();
            ConfigManager.Initialize();
            TrainLootConfig.Initialize();
            LootManager.PopulateNpcCache();
            LootManager.RefreshCrateLoot(null);
        }
        void Init() {
            _instance = this;
            InitializeEditorConfig();
            permission.RegisterPermission(PERM_EDIT, this);
            permission.RegisterPermission(PERM_DEBUG, this);
            UiHelper.EditorCache.Clear();
            CustomItemStorage.LoadCustomItems();
            if (Flags.DisableStackingHooks) {
                Unsubscribe(nameof(CanStackItem));
                Unsubscribe(nameof(CanCombineDroppedItem));
                Unsubscribe(nameof(OnItemSplit));
                Unsubscribe(nameof(OnItemAddedToContainer));
            }
        }
        void Unload() {
            SaveEditorConfig();
            UiHelper.DestroyUI();
            ConfigManager.FurnaceConfig.ResetBurnable();
            StackManager.RevertStacksize(true);
            LootManager.RefreshCrateLoot(null, true);
            CustomItemStorage.SaveCustomItems();
        }
        object CanSpectateTarget(BasePlayer player, string filter) {
            if (permission.UserHasPermission(player.UserIDString, PERM_EDIT) && UiHelper.uiUser == player.userID) {
                return false;
            }
            return null;
        }
        void OnPluginUnloaded(Plugin name) {
            CustomItemStorage.RemoveCustomItems(name);
        }
#endregion
#endregion
#region API
        private void AddCustomItem(Plugin plugin, int itemId, ulong skinId) => AddCustomItem(plugin, itemId, skinId, null);
        private void AddCustomItem(Plugin plugin, int itemId, ulong skinId, string customName) {
            CustomItemStorage.AddCustomItem(new CustomItemStorage.CustomItem {
                plugin = plugin.Name, itemId = itemId, skinId = skinId, customName = customName
            }
            );
        }
#endregion
#region Custom Items
        private class CustomItemStorage {
            [JsonProperty]
            public List<CustomItem> customItems = new List<CustomItem>();
            [JsonProperty]
            private DateTime lastSave;
            [JsonIgnore]
            public const string itemCategory = "custom";
            [JsonIgnore]
            private static CustomItemStorage _storage;
            [JsonIgnore]
            public static List<CustomItem> CustomItems {
                get;
                private set;
            }
            = new List<CustomItem>();
            public static void LoadCustomItems() {
                _storage = Interface.Oxide.DataFileSystem.ReadObject<CustomItemStorage>(DataFilePath("custom_items"));
                if (_storage == null || _storage.customItems == null || DateTime.UtcNow.Subtract(_storage.lastSave).TotalSeconds > 20) return;
                CustomItems = _storage.customItems;
            }
            public static void AddCustomItem(CustomItem item) {
                CustomItems.Add(item);
                DPrint($"{item.plugin} added custom item {item.customName}");
            }
            public static void RemoveCustomItems(Plugin plugin) {
                CustomItems.RemoveAll(x => x.plugin == plugin.Name);
            }
            public static void SaveCustomItems() {
                _storage = new CustomItemStorage {
                    customItems = CustomItems, lastSave = DateTime.UtcNow
                }
                ;
                Interface.Oxide.DataFileSystem.WriteObject(DataFilePath("custom_items"), _storage);
            }
            public static void SortItemList() {
                CustomItems = CustomItems.OrderBy(x => x.plugin).ToList();
            }
            public class CustomItem {
                public string plugin;
                public int itemId;
                public ulong skinId;
                public string customName;
            }
        }
#endregion
#region StackManager
        private static class StackManager {
            // Vanilla stack size file https://pastebin.com/raw/vtjnscHq
            public static bool stacksizeIsReverted {
                get;
                private set;
            }
            = true;
            public static StacksizeConfig stacksizeConfig {
                get;
                private set;
            }
            public static Dictionary<string, List<int>> itemCategoryList {
                get;
                private set;
            }
            public static List<ItemDefinition> itemList {
                get;
                private set;
            }
            public static readonly List<int> ignoreItems = new List<int> {
                -1779180711, -277057363,  // Water + Salt water
                -1759188988, 1426574435, -1884328185,  //HAB, MC, ScrapHeli repair
                -810326667, -1364246987, 1768112091, 1394042569, 1878053256, -1449152644,  //Wcart, Snowmobile, TSnowmobile, RHIB, Boat, MLRS
                1770744540, 878301596, -44066790, -44066823, -44066600,  //Generic VChassis, Generic VModule, 4x chassis, 3x chassis, 2x chassis
                1015352446, -187031121, 550753330,  // Duo/Solo Sub, Snowball Ammo
                996757362, -1366326648, -2027988285  // train wagon, spray can decal, locomotive
            }
            ;
            public static bool StacksizeIsVanilla(int itemid) => stacksizeConfig.GetVanillaStacksize(itemid) == stacksizeConfig.GetCustomStacksize(itemid);
            public static int GetCurrentStacksize(int itemid) => GetCurrentStacksize(ItemManager.FindItemDefinition(itemid));
            public static int GetCurrentStacksize(ItemDefinition itemDefinition) {
                int itemid = itemDefinition.itemid;
                if (ignoreItems.Contains(itemid)) return itemDefinition.stackable;
                int vs = stacksizeConfig.GetVanillaStacksize(itemid), cs = stacksizeConfig.GetCustomStacksize(itemid);
                if (stacksizeConfig.enabled) {
                    float cat_mpl = stacksizeConfig.categoryMultipliers[MapItemCategory(itemDefinition.category)];
                    if (vs == cs) return Mathf.RoundToInt(vs * stacksizeConfig.globalMultiplier * cat_mpl);
                    return cs;
                }
                else {
                    return vs;
                }
            }
            public static void Initialize() {
                itemCategoryList = new Dictionary<string, List<int>>();
                LoadData();
                RefreshItemLists();
                ApplyStacksize();
            }
            public static void Save() {
                SaveData();
            }
            private static void RefreshItemLists() {
                itemCategoryList = new Dictionary<string, List<int>>();
                itemList = new List<ItemDefinition>();
                if (!itemCategoryList.ContainsKey(CustomItemStorage.itemCategory)) itemCategoryList.Add(CustomItemStorage.itemCategory, new List<int>());
                foreach (var item in ItemManager.itemList) {
                    if (ignoreItems.Contains(item.itemid)) continue;
                    var cat = item.category.ToString();
                    if (!itemCategoryList.ContainsKey(cat)) {
                        itemCategoryList[cat] = new List<int>();
                    }
                    itemList.Add(item);
                    itemCategoryList[cat].Add(item.itemid);
                }
            }
            public static void RevertStacksize(bool force = false) {
                if (stacksizeIsReverted && !force) return;
                foreach (ItemDefinition item in ItemManager.itemList) {
                    if (ignoreItems.Contains(item.itemid)) continue;
                    item.stackable = stacksizeConfig.GetVanillaStacksize(item.itemid);
                }
                stacksizeIsReverted = true;
            }
            public static void ApplyStacksize() {
                if (!stacksizeConfig.enabled) {
                    RevertStacksize();
                    return;
                }
                stacksizeIsReverted = false;
                foreach (ItemDefinition item in ItemManager.itemList) {
                    if (ignoreItems.Contains(item.itemid)) continue;
                    item.stackable = GetCurrentStacksize(item);
                }
            }
            private static int MapItemCategory(ItemCategory itemCat) {
                int cat = 0;
                List<int> cats = new List<int>();
                foreach (ItemDefinition itemdef in ItemManager.itemList) {
                    if (!cats.Contains((int)itemdef.category)) {
                        cats.Add((int)itemdef.category);
                        if (itemdef.category == itemCat) return cat;
                        cat++;
                    }
                }
                return -1;
            }
            private static void LoadData() {
                StacksizeConfig config = Interface.Oxide.DataFileSystem.ReadObject<StacksizeConfig>($"{Name}/stacksize");
                stacksizeConfig = config;
            }
            private static void SaveData() {
                Interface.Oxide.DataFileSystem.WriteObject($"{Name}/stacksize", stacksizeConfig);
            }
        }
        private class StacksizeConfig {
            [JsonProperty("ssc")]
            private Dictionary<int, int> itemStacksize;
            [JsonProperty("ssv")]
            private Dictionary<int, int> vanillaStacksize;
            [JsonProperty("vanilla")]
            public bool enabled {
                get;
                private set;
            }
            [JsonProperty("catMpl")]
            public Dictionary<int, float> categoryMultipliers;
            [JsonProperty("multiplier")]
            public float globalMultiplier;
            [JsonConstructor]
            public StacksizeConfig(Dictionary<int, int> itemStacksize, Dictionary<int, int> vanillaStacksize, bool enabled, Dictionary<int, float> categoryMultipliers, float globalMultiplier) {
                this.itemStacksize = itemStacksize;
                this.vanillaStacksize = vanillaStacksize;
                this.enabled = enabled;
                this.categoryMultipliers = categoryMultipliers;
                this.globalMultiplier = globalMultiplier;
                if (this.categoryMultipliers.IsNullOrEmpty()) InitCategoryMultipliers();
            }
            public StacksizeConfig() {
                itemStacksize = new Dictionary<int, int>();
                vanillaStacksize = new Dictionary<int, int>();
                enabled = false;
                InitCategoryMultipliers();
                globalMultiplier = 1f;
                SaveVanillaStacksize();
            }
            public int GetVanillaStacksize(int itemid) {
                if (!vanillaStacksize.ContainsKey(itemid)) vanillaStacksize[itemid] = ItemManager.FindItemDefinition(itemid).stackable;
                return vanillaStacksize[itemid];
            }
            public int GetCustomStacksize(int itemid) {
                if (!itemStacksize.ContainsKey(itemid)) itemStacksize[itemid] = ItemManager.FindItemDefinition(itemid).stackable;
                return itemStacksize[itemid];
            }
            public void SetStacksize(int itemid, int amount) {
                amount = Mathf.Clamp(amount, 1, Int32.MaxValue);
                itemStacksize[itemid] = amount;
            }
            public void ResetCategory(int catid) {
                foreach(int itemid in StackManager.itemCategoryList.Values.ToList()[catid]) {
                    itemStacksize[itemid] = vanillaStacksize[itemid];
                }
            }
            public void TryEnable(bool enabled) => this.enabled = enabled;
            private void SaveVanillaStacksize() {
                foreach(ItemDefinition item in ItemManager.itemList) {
                    if (StackManager.ignoreItems.Contains(item.itemid)) continue;
                    vanillaStacksize[item.itemid] = item.stackable;
                    itemStacksize[item.itemid] = item.stackable;
                }
            }
            private void InitCategoryMultipliers() {
                categoryMultipliers = new Dictionary<int, float>();
                int cat = 0;
                List<int> cats = new List<int>();
                foreach(ItemDefinition itemdef in ItemManager.itemList) {
                    if (!cats.Contains((int)itemdef.category)) {
                        cats.Add((int)itemdef.category);
                        categoryMultipliers[cat] = 1f;
                        cat++;
                    }
                }
            }
        }
#endregion
#region GatherManager
        private static class GatherManager {
            public static GatherConfig gatherConfig {
                get;
                private set;
            }
            public static void Initialize() {
                LoadData();
            }
            public static void Save() => SaveData();
            private static void LoadData() {
                GatherConfig config = Interface.Oxide.DataFileSystem.ReadObject<GatherConfig>($"{Name}/gatherconfig");
                gatherConfig = config;
            }
            private static void SaveData() {
                Interface.Oxide.DataFileSystem.WriteObject($"{Name}/gatherconfig", gatherConfig);
            }
            public static Dictionary<string, Dictionary<int, float>> GetGatherItems() {
                var temp = new Dictionary<string, Dictionary<int, float>>();
                temp["default"] = new Dictionary<int, float> {
                    {
                        -151838493, 1f
                    }
                    , {
                        -2099697608, 1f
                    }
                    , {
                        -4031221, 1f
                    }
                    , {
                        -1982036270, 1f
                    }
                    , {
                        -1157596551, 1f
                    }
                }
                ;
                temp["bear.corpse"] = new Dictionary<int, float> {
                    {
                        -1018587433, 1f
                    }
                    , {
                        1719978075, 1f
                    }
                    , {
                        1381010055, 1f
                    }
                    , {
                        -858312878, 1f
                    }
                    , {
                        -1520560807, 1f
                    }
                }
                ;
                temp["polarbear.corpse"] = new Dictionary<int, float> {
                    {
                        -1018587433, 1f
                    }
                    , {
                        1719978075, 1f
                    }
                    , {
                        1381010055, 1f
                    }
                    , {
                        -858312878, 1f
                    }
                    , {
                        -1520560807, 1f
                    }
                }
                ;
                temp["boar.corpse"] = new Dictionary<int, float> {
                    {
                        -1018587433, 1f
                    }
                    , {
                        1719978075, 1f
                    }
                    , {
                        1381010055, 1f
                    }
                    , {
                        -858312878, 1f
                    }
                    , {
                        621915341, 1f
                    }
                }
                ;
                temp["wolf.corpse"] = new Dictionary<int, float> {
                    {
                        -1018587433, 1f
                    }
                    , {
                        1719978075, 1f
                    }
                    , {
                        1381010055, 1f
                    }
                    , {
                        -858312878, 1f
                    }
                    , {
                        -395377963, 1f
                    }
                    , {
                        2048317869, 1f
                    }
                }
                ;
                temp["stag.corpse"] = new Dictionary<int, float> {
                    {
                        -1018587433, 1f
                    }
                    , {
                        1719978075, 1f
                    }
                    , {
                        1381010055, 1f
                    }
                    , {
                        -858312878, 1f
                    }
                    , {
                        1422530437, 1f
                    }
                }
                ;
                temp["chicken.corpse"] = new Dictionary<int, float> {
                    {
                        -858312878, 1f
                    }
                    , {
                        1719978075, 1f
                    }
                    , {
                        -1440987069, 1f
                    }
                }
                ;
                return temp;
            }
        }
        private class GatherConfig {
            [JsonProperty("g")]
            public float globalMultiplier;
            [JsonProperty("mpl")]
            public Dictionary<string, Dictionary<int, float>> multipliers;
            [JsonProperty("enabled")]
            public bool enabled {
                get;
                private set;
            }
            [JsonConstructor]
            public GatherConfig(Dictionary<string, Dictionary<int, float>> multipliers, bool enabled, float globalMultiplier) {
                this.multipliers = multipliers;
                this.enabled = enabled;
                this.globalMultiplier = globalMultiplier;
            }
            public GatherConfig() {
                this.multipliers = new Dictionary<string, Dictionary<int, float>>();
                this.enabled = false;
                this.globalMultiplier = 1f;
                SetDefaults();
            }
            private void SetDefaults() => multipliers = GatherManager.GetGatherItems();
            public void TryEnable(bool enabled) => this.enabled = enabled;
            public void Reset() => SetDefaults();
            public void SetMultiplier(string prefabName, int itemid, float multi) {
                multipliers[prefabName][itemid] = multi;
            }
            public int GetNewAmount(string prefabName, int itemid, int amount) {
                if (!enabled) return amount;
                if (multipliers.ContainsKey(prefabName)) {
                    if (multipliers[prefabName][itemid] == 1f) return Mathf.RoundToInt(amount * globalMultiplier);
                    return Mathf.RoundToInt(amount * multipliers[prefabName][itemid]);
                }
                else if (multipliers["default"].ContainsKey(itemid)) {
                    if (multipliers["default"][itemid] == 1f) return Mathf.RoundToInt(amount * globalMultiplier);
                    return Mathf.RoundToInt(amount * multipliers["default"][itemid]);
                }
                return Mathf.RoundToInt(amount * globalMultiplier);
            }
        }
#endregion
#region ConfigManager
        private static class ConfigManager {
            public static readonly List<GenericLootable> genericLootableList = new List<GenericLootable> {
                new GenericLootable(LootManager.Lootables.StackSizeControl, "stacksize", "Stack Size Control", "https://cdn.icon-icons.com/icons2/2518/PNG/512/stack_icon_151083.png"), new GenericLootable(LootManager.Lootables.GatherConfig, "gathering", "Gather Control", "https://static.wikia.nocookie.net/play-rust/images/8/86/Pick_Axe_icon.png"), new GenericLootable(LootManager.Lootables.FurnaceConfig, "smelting", "Furnace Config", "https://static.wikia.nocookie.net/play-rust/images/e/ee/Large_Furnace_icon.png"), new GenericLootable(LootManager.Lootables.AirwolfConfig, "airwolf", "Air Wolf Config", "https://static.wikia.nocookie.net/play-rust/images/9/95/494EDB03-BAEA-42BB-8FAE-748F0D2B50A9.png"), new GenericLootable(LootManager.Lootables.RecyclerConfig, "recycling", "Recycler Config", "https://static.wikia.nocookie.net/play-rust/images/e/ef/Recycler_icon.png"), new GenericLootable(LootManager.Lootables.AirDropConfig, "airdrop", "Air Drop Config", "https://static.wikia.nocookie.net/play-rust/images/2/24/Supply_Signal_icon.png")
            }
            ;
            public static readonly LootManager.Lootables[] labelIgnore = new LootManager.Lootables[] {
                LootManager.Lootables.StackSizeControl, LootManager.Lootables.AirwolfConfig, LootManager.Lootables.FurnaceConfig, LootManager.Lootables.RecyclerConfig, LootManager.Lootables.AirDropConfig
            }
            ;
            public static bool IsReady {
                get;
                private set;
            }
            = false;
            private static string FurnaceConfigFile => DataFilePath("config_smelting");
            private static string AirwolfConfigFile => DataFilePath("config_airwolf");
            private static string AirdropConfigFile => DataFilePath("config_airdrop");
            public static FurnaceConfiguration FurnaceConfig {
                get;
                private set;
            }
            public static AirwolfConfiguration AirwolfConfig {
                get;
                private set;
            }
            public static AirDropConfiguration AirdropConfig {
                get;
                private set;
            }
            public static void Initialize() {
                LoadData();
                FurnaceConfig.UpdateBurnable();
                FurnaceConfig.InitializeFurnaces();
                IsReady = true;
         
// ... [Code preview truncated due to length. Full source in attached file] ...

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