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

FREE AlphaLoot : 3.1.41 3.1.41

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using HarmonyLib;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Rust;
using Steamworks;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Oxide.Plugins {
    [Info("AlphaLoot", "k1lly0u", "3.1.41")]
    class AlphaLoot : RustPlugin {
#region Fields
        [PluginReference]
        Plugin CustomLootSpawns, EventLoot, FancyDrop;
        private StoredData storedData;
        private StoredData bradleyData;
        private StoredData heliData;
        private DynamicConfigFile data, bradley, heli, skinData;
        private bool updateContainerCapacities = false;
        private static Hash<string, HashSet<SkinEntry>> weightedSkinIds;
        private static Hash<string, List<ulong>> importedSkinIds;
        private static Hash<string, int> defaultScrapAmounts;
        private readonly Hash<string, string> shortnameReplacements = new Hash<string, string> {
            ["chocholate"]
            = "chocolate"
        }
        ;
        private const string ADMIN_PERMISSION = "alphaloot.admin";
        private const string HELI_CRATE = "heli_crate";
        private const string BRADLEY_CRATE = "bradley_crate";
        private const string UNDERWATER_LABS = "underwater_labs/";
#endregion
#region Oxide Hooks
        private void Loaded() {
            importedSkinIds = new Hash<string, List<ulong>>();
            defaultScrapAmounts = new Hash<string, int>();
            permission.RegisterPermission(ADMIN_PERMISSION, this);
            LoadData();
            if (updateContainerCapacities) SetCapacityLimits();
            Unsubscribe(nameof(OnLootSpawn));
        }
        private void OnServerInitialized() {
            PopulateContainerDefinitions(ref storedData, ref heliData, ref bradleyData);
            SaveData();
            Puts($"Loaded {storedData.loot_advanced.Count + storedData.loot_simple.Count} loot container definitions, {storedData.npcs_advanced.Count + storedData.npcs_simple.Count} npc loot definitions and {storedData.custom_advanced.Count + storedData.custom_simple.Count} custom loot definitions");
            Puts($"Loaded {heliData.loot_advanced.Count + heliData.loot_simple.Count} heli loot profiles");
            Puts($"Loaded {bradleyData.loot_advanced.Count + bradleyData.loot_simple.Count} bradley loot profiles");
            if (configData.AutoUpdate) AutoUpdateItemLists();
            FindAndRemoveInvalidItems();
            Subscribe(nameof(OnLootSpawn));
            if (configData.UseSkinboxSkins || configData.UseApprovedSkins) {
                if ((Steamworks.SteamInventory.Definitions?.Length ?? 0) == 0) {
                    PrintWarning("Waiting for Steamworks to initialize to load item skins");
                    Steamworks.SteamInventory.OnDefinitionsUpdated += LoadSkins;
                }
                else LoadSkins();
            }
            else RefreshLootContents();
        }
        private void OnEntitySpawned(BradleyAPC bradleyApc) {
            if (bradleyApc && configData.BradleyCrates > 0) bradleyApc.maxCratesToSpawn = configData.BradleyCrates;
        }
        private void OnEntitySpawned(PatrolHelicopter baseHelicopter) {
            if (baseHelicopter && configData.HelicopterCrates > 0) baseHelicopter.maxCratesToSpawn = configData.HelicopterCrates;
        }
        private object OnCorpsePopulate(BaseEntity entity, LootableCorpse corpse) {
            if (!entity || !corpse) return null;
            object obj = Interface.CallHook("CanPopulateLoot", entity, corpse);
            if (obj != null) return null;
            return PopulateLoot(entity, corpse) ? corpse : null;
        }
        private object OnLootSpawn(LootContainer container) {
            if (!container) return null;
            if (CustomLootSpawns && (bool)CustomLootSpawns.Call("IsLootBox", container as BaseEntity)) return null;
            if (EventLoot && (bool)EventLoot.Call("IsEventLootContainer", container as BaseEntity)) return null;
            if (FancyDrop && container is SupplyDrop && !configData.OverrideFancyDrop) return null;
            object obj = Interface.CallHook("CanPopulateLoot", container);
            if (obj != null) return null;
            if (PopulateLoot(container)) return true;
            return null;
        }
        private object OnItemUnwrap(Item item, BasePlayer player, ItemModUnwrap itemModUnwrap) {
            if (PopulateLoot(itemModUnwrap, item, player)) {
                item.UseItem(1);
                if (itemModUnwrap.successEffect.isValid) Effect.server.Run(itemModUnwrap.successEffect.resourcePath, player.eyes.position, new Vector3(), null, false);
                return true;
            }
            return null;
        }
        private void OnHammerHit(BasePlayer player, HitInfo info) {
            if (!player || info == null || !info.HitEntity) return;
            if (!permission.UserHasPermission(player.UserIDString, ADMIN_PERMISSION)) return;
            LootContainer lootContainer = info.HitEntity.GetComponent<LootContainer>();
            if (!lootContainer) return;
            player.ChatMessage($"Viewing loot generation for: <color=#ffff00>{ToProfileName(lootContainer)}</color>");
            lootContainer.gameObject.AddComponent<LootCycler>();
            player.inventory.loot.StartLootingEntity(lootContainer, false);
            player.inventory.loot.AddContainer(lootContainer.inventory);
            player.inventory.loot.SendImmediate();
            player.ClientRPCPlayer(null, player, "RPC_OpenLootPanel", lootContainer.panelName);
        }
        private void OnLootEntityEnd(BasePlayer player, StorageContainer container) {
            if (!container) return;
            LootCycler lootCycler = container.GetComponent<LootCycler>();
            if (lootCycler) UnityEngine.Object.Destroy(lootCycler);
        }
        private void Unload() {
            RefreshLootContents(null, true);
            LootCycler[] lootCyclers = UnityEngine.Object.FindObjectsOfType<LootCycler>();
            for (int i = 0; i < lootCyclers?.Length; i++) UnityEngine.Object.Destroy(lootCyclers[i]);
            configData = null;
            weightedSkinIds = null;
            importedSkinIds = null;
            defaultScrapAmounts = null;
        }
#endregion
#region Skins
        private void LoadSkins() {
            Steamworks.SteamInventory.OnDefinitionsUpdated -= LoadSkins;
            if (configData.UseApprovedSkins) PopulateSkinListFromApproved();
            RefreshLootContents();
        }
        private void OnSkinBoxSkinsLoaded(Hash<string, HashSet<ulong>> skinList) {
            if (!configData.UseSkinboxSkins) return;
            foreach (KeyValuePair<string, HashSet<ulong>> kvp in skinList) {
                if (!importedSkinIds.TryGetValue(kvp.Key, out List<ulong> list)) list = importedSkinIds[kvp.Key] = new List<ulong>();
                foreach(ulong skinId in kvp.Value) {
                    if (!list.Contains(skinId)) list.Add(skinId);
                }
            }
        }
        private void OnPlayerSkinsSkinsLoaded(Hash<string, HashSet<ulong>> skinList) {
            if (!configData.UsePlayerSkinsSkins) return;
            foreach (KeyValuePair<string, HashSet<ulong>> kvp in skinList) {
                if (!importedSkinIds.TryGetValue(kvp.Key, out List<ulong> list)) list = importedSkinIds[kvp.Key] = new List<ulong>();
                foreach (ulong skinId in kvp.Value) {
                    if (!list.Contains(skinId)) list.Add(skinId);
                }
            }
        }
        private void PopulateSkinListFromApproved() {
            if (importedSkinIds == null) importedSkinIds = new Hash<string, List<ulong>>();
            List<int> itemSkinDirectory = Pool.Get<List<int>>();
            itemSkinDirectory.AddRange(ItemSkinDirectory.Instance.skins.Select(x => x.id));
            int count = 0;
            foreach (InventoryDef item in Steamworks.SteamInventory.Definitions) {
                string shortname = item.GetProperty("itemshortname");
                if (string.IsNullOrEmpty(shortname) || item.Id < 100) continue;
                ulong wsid;
                if (itemSkinDirectory.Contains(item.Id)) wsid = (ulong)item.Id;
                else {
                    if (!ulong.TryParse(item.GetProperty("workshopid"), out wsid)) continue;
                }
                if (!importedSkinIds.ContainsKey(shortname)) importedSkinIds[shortname] = new List<ulong>();
                if (!importedSkinIds[shortname].Contains(wsid)) {
                    importedSkinIds[shortname].Add(wsid);
                    count++;
                }
            }
            Puts($"Imported {count} approved skins that weren't already in the list");
            Pool.FreeUnmanaged(ref itemSkinDirectory);
        }
#endregion
#region Container Population
        private void RefreshLootContents(ConsoleSystem.Arg arg = null, bool setDefaultScrap = false) {
            LootContainer[] lootContainers = UnityEngine.Object.FindObjectsOfType<LootContainer>();
            if (arg != null) SendReply(arg, $"Repopulating loot for {lootContainers?.Length} containers");
            else Puts($"Repopulating loot for {lootContainers?.Length} containers");
            for (int i = 0; i < lootContainers?.Length; i++) {
                LootContainer lootContainer = lootContainers[i];
                if (lootContainer && !lootContainer.IsDestroyed) {
                    if (lootContainer.inventory == null) {
                        lootContainer.CreateInventory(true);
                        lootContainer.OnInventoryFirstCreated(lootContainer.inventory);
                    }
                    if (setDefaultScrap) {
                        if (defaultScrapAmounts.TryGetValue(ToProfileName(lootContainer), out int scrapAmount)) lootContainer.scrapAmount = scrapAmount;
                    }
                    lootContainer.inventory.capacity = lootContainer.inventorySlots;
                    lootContainer.CancelInvoke(lootContainer.SpawnLoot);
                    lootContainer.Invoke(lootContainer.SpawnLoot, UnityEngine.Random.Range(1f, 20f));
                }
            }
        }
        private void CreateLootDefinitionFor(LootContainer lootContainer, ref StoredData storedData, ref StoredData heliData, ref StoredData bradleyData) {
            string profileName = ToProfileName(lootContainer);
            if (!defaultScrapAmounts.ContainsKey(profileName)) defaultScrapAmounts[profileName] = lootContainer.scrapAmount;
            if (profileName.Equals(HELI_CRATE)) {
                if (storedData.TryGetLootProfile(HELI_CRATE, out BaseLootContainerProfile lootContainerProfile)) {
                    heliData.CloneLootProfile(HELI_CRATE, lootContainerProfile);
                    storedData.RemoveProfile(HELI_CRATE);
                    Debug.LogWarning($"Helicopter loot profiles have been removed from your loot table and placed in its own data file.(/data/AlphaLoot/LootProfiles/{configData.HeliProfileName}.json)");
                }
                else {
                    if (!heliData.HasAnyProfiles) heliData.CreateDefaultLootProfile(lootContainer);
                }
            }
            else if (profileName.Equals(BRADLEY_CRATE)) {
                if (storedData.TryGetLootProfile(BRADLEY_CRATE, out BaseLootContainerProfile lootContainerProfile)) {
                    bradleyData.CloneLootProfile(BRADLEY_CRATE, lootContainerProfile);
                    storedData.RemoveProfile(BRADLEY_CRATE);
                    Debug.LogWarning($"Bradley loot profiles have been removed from your loot table and placed in its own data file.(/data/AlphaLoot/LootProfiles/{configData.BradleyProfileName}.json)");
                }
                else {
                    if (!bradleyData.HasAnyProfiles) bradleyData.CreateDefaultLootProfile(lootContainer);
                }
            }
            else {
                storedData.CreateDefaultLootProfile(lootContainer);
            }
        }
        private void CreateLootDefinitionFor(NPCPlayer npcPlayer, ref StoredData storedData) {
            global : : HumanNPC humanNPC = npcPlayer as global : : HumanNPC;
            if (humanNPC) {
                storedData.CreateDefaultLootProfile(npcPlayer.ShortPrefabName, humanNPC.LootSpawnSlots, npcPlayer.loadouts);
                return;
            }
            ScarecrowNPC scarecrowNPC = npcPlayer as ScarecrowNPC;
            if (scarecrowNPC) {
                storedData.CreateDefaultLootProfile(npcPlayer.ShortPrefabName, scarecrowNPC.LootSpawnSlots, npcPlayer.loadouts);
                return;
            }
        }
        private void PopulateContainerDefinitions(ref StoredData storedData, ref StoredData heliData, ref StoredData bradleyData) {
            storedData.IsBaseLootTable = true;
            int loot = 0;
            int npc = 0;
            int item = 0;
            foreach (KeyValuePair<string, Object> kvp in FileSystem.Backend.cache) {
                if (kvp.Value is GameObject gameObject) {
                    LootContainer lootContainer = gameObject.GetComponent<LootContainer>();
                    if (lootContainer) {
                        loot++;
                        CreateLootDefinitionFor(lootContainer, ref storedData, ref heliData, ref bradleyData);
                        continue;
                    }
                    NPCPlayer npcPlayer = gameObject.GetComponent<NPCPlayer>();
                    if (npcPlayer) {
                        npc++;
                        CreateLootDefinitionFor(npcPlayer, ref storedData);
                        continue;
                    }
                }
            }
            foreach(ItemDefinition itemDefinition in ItemManager.itemList) {
                ItemModUnwrap itemModUnwrap = itemDefinition.GetComponentInChildren<ItemModUnwrap>();
                if (itemModUnwrap) {
                    storedData.CreateDefaultLootProfile(itemDefinition, itemModUnwrap);
                    item++;
                }
            }
            Debug.Log($"Found {loot} loot containers, {npc} NPC prefabs and {item} unwrapable items in bundles");
        }
        private bool PopulateLoot(LootContainer container) {
            string profileName = ToProfileName(container);
            if (configData.TryGetContainerOverride(profileName, out string overrideProfile)) profileName = overrideProfile;
            return PopulateLoot(container, profileName);
        }
        private bool PopulateLoot(LootContainer container, string profileName) {
            BaseLootContainerProfile lootProfile;
            if (profileName.Equals(HELI_CRATE)) {
                if (heliData.GetRandomLootProfile(out lootProfile)) {
                    PopulateLootContainer(container, lootProfile);
                    return true;
                }
            }
            else if (profileName.Equals(BRADLEY_CRATE)) {
                if (bradleyData.GetRandomLootProfile(out lootProfile)) {
                    PopulateLootContainer(container, lootProfile);
                    return true;
                }
            }
            else {
                if (storedData.TryGetLootProfile(profileName, out lootProfile) && lootProfile.Enabled) {
                    PopulateLootContainer(container, lootProfile);
                    return true;
                }
            }
            return false;
        }
        private bool PopulateLoot(ItemModUnwrap itemModUnwrap, Item item, BasePlayer player) {
            if (storedData.TryGetLootProfile(item.info.shortname, out BaseLootContainerProfile lootProfile) && lootProfile.Enabled) {
                int attempts = UnityEngine.Random.Range(itemModUnwrap.minTries, itemModUnwrap.maxTries + 1);
                for (int i = 0; i < attempts; i++) lootProfile.PopulateLoot(player.inventory.containerMain);
                return true;
            }
            return false;
        }
        private bool PopulateLoot(BaseEntity entity, LootableCorpse corpse) {
            string profileName = entity.ShortPrefabName;
            if (configData.TryGetContainerOverride(profileName, out string overrideProfile)) profileName = overrideProfile;
            if (storedData.TryGetNPCProfile(profileName, out BaseLootProfile lootProfile)) {
                if (!lootProfile.Enabled) return false;
                string loadoutName = entity is global : : HumanNPC humanNpc ? humanNpc.GetLoadoutName() : string.Empty;
                lootProfile.PopulateLoot(corpse.containers[0], loadoutName);
                return true;
            }
            return false;
        }
        private bool PopulateLoot(ItemContainer container, string profileName) {
            if (storedData.TryGetLootProfile(profileName, out BaseLootContainerProfile lootContainerProfile)) {
                if (!lootContainerProfile.Enabled) return false;
                lootContainerProfile.PopulateLoot(container);
                return true;
            }
            if (storedData.TryGetNPCProfile(profileName, out BaseLootProfile lootProfile)) {
                if (!lootProfile.Enabled) return false;
                lootProfile.PopulateLoot(container, profileName);
                return true;
            }
            if (storedData.TryGetCustomProfile(profileName, out lootProfile)) {
                if (!lootProfile.Enabled) return false;
                lootProfile.PopulateLoot(container);
                return true;
            }
            return false;
        }
        private bool ProfileExists(string name) => storedData.Exists(name);
        private void PopulateLootContainer(LootContainer container, BaseLootContainerProfile lootProfile) {
            container.destroyOnEmpty = lootProfile.DestroyOnEmpty;
            lootProfile.PopulateLoot(container.inventory);
            container.CancelInvoke(container.SpawnLoot);
            if (lootProfile.ShouldRefreshContents) container.Invoke(container.SpawnLoot, Mathf.Max(60, UnityEngine.Random.Range(lootProfile.MinSecondsBetweenRefresh, lootProfile.MaxSecondsBetweenRefresh)));
        }
#endregion
#region Functions
        private string ToShortName(string name) {
            return name.Split('/').Last().Replace(".prefab", "");
        }
        private static string ToProfileName(LootContainer container) {
            if (container.PrefabName.Contains(UNDERWATER_LABS)) return UNDERWATER_LABS + container.ShortPrefabName;
            return container.ShortPrefabName;
        }
        private LootContainer FindContainer(BasePlayer player) {
            if (Physics.Raycast(player.eyes.HeadRay(), out RaycastHit raycastHit, 20f)) {
                LootContainer lootContainer = raycastHit.GetEntity() as LootContainer;
                return lootContainer;
            }
            return null;
        }
        private object WantsToHandleFancyDropLoot() => configData.OverrideFancyDrop ? (object)true : null;
#endregion
#region Auto-Updater
        private void AutoUpdateItemLists() {
            const string lastDefaultTable = "AlphaLoot/AutoUpdater/do_not_edit_this_file";
            ItemList lastItemList;
            if (Interface.Oxide.DataFileSystem.ExistsDatafile(lastDefaultTable)) {
                lastItemList = Interface.Oxide.DataFileSystem.GetFile(lastDefaultTable).ReadObject<ItemList>();
                if (lastItemList != null) {
                    if (lastItemList.protocol == Rust.Protocol.printable) {
                        Debug.Log("[AlphaLoot Auto Updater] - Last item list protocol matches current protocol.No new items added");
                        return;
                    }
                    List<int> newItems = Pool.Get<List<int>>();
                    ItemManager.itemList.ForEach(x => {
                        if (!lastItemList.itemIds.Contains(x.itemid)) newItems.Add(x.itemid);
                    }
                    );
                    if (newItems.Count > 0) {
                        Debug.Log($"[AlphaLoot Auto Updater] - Found {newItems.Count} new game items.Adding them to your loot table");
                        AddItemsToLootTable(newItems, out int additions);
                        if (additions > 0) {
                            Debug.Log($"[AlphaLoot Auto Updater] - Added {additions} new loot definitions to the loot table");
                            SaveData();
                            Interface.Oxide.DataFileSystem.WriteObject<ItemList>(lastDefaultTable, new ItemList() {
                                itemIds = ItemManager.itemDictionary.Keys.ToList(), protocol = Rust.Protocol.printable
                            }
                            );
                        }
                    }
                    else Debug.Log("[AlphaLoot Auto Updater] - No new items in game");
                    Pool.FreeUnmanaged(ref newItems);
                }
            }
            else {
                Debug.Log("[AlphaLoot Auto Updater] - Generating item list for auto-updater.Future game updates will automatically add new items to your loot table.You can disable this feature in the config");
                Interface.Oxide.DataFileSystem.WriteObject<ItemList>(lastDefaultTable, new ItemList() {
                    itemIds = ItemManager.itemDictionary.Keys.ToList(), protocol = Rust.Protocol.printable
                }
                );
            }
        }
        private void AddSpecifiedItemsToLootTable(params string[] args) {
            List<int> items = Pool.Get<List<int>>();
            foreach(string str in args) {
                ItemDefinition itemDefinition = ItemManager.FindItemDefinition(str);
                if (itemDefinition) items.Add(itemDefinition.itemid);
            }
            if (items.Count > 0) {
                Debug.Log($"[AlphaLoot] - Adding {items.Count} specified items to your loot table");
                AddItemsToLootTable(items, out int additions);
                if (additions > 0) {
                    Debug.Log($"[AlphaLoot] - Successfully added {additions} new loot definitions to the loot table");
                    SaveData();
                }
                else Debug.Log($"[AlphaLoot] - Failed to find any loot tables with the specified items");
            }
            else Debug.Log("[AlphaLoot] - Failed to find item definitions for the shortname's supplied");
            Pool.FreeUnmanaged(ref items);
        }
        private void AddItemsToLootTable(List<int> items, out int additions) {
            additions = 0;
            StoredData defaultLootTable = new StoredData();
            StoredData defaultHeliLootTable = new StoredData();
            StoredData defaultBradleyLootTable = new StoredData();
            PopulateContainerDefinitions(ref defaultLootTable, ref defaultHeliLootTable, ref defaultBradleyLootTable);
            AddItemsToLootTable(defaultLootTable, storedData, items, ref additions);
            AddItemsToLootTable(defaultHeliLootTable, heliData, items, ref additions);
            AddItemsToLootTable(defaultBradleyLootTable, bradleyData, items, ref additions);
        }
        private void AddItemsToLootTable(StoredData source, StoredData dest, List<int> items, ref int additions) {
            foreach (int itemId in items) {
                string shortname = ItemManager.itemDictionary[itemId].shortname;
                foreach (KeyValuePair<string, AdvancedLootContainerProfile> kvp in source.loot_advanced) {
                    NewLootItem newLootItem = new NewLootItem();
                    CalculateItemScore(shortname, kvp.Value.LootSpawnSlots, ref newLootItem);
                    if (newLootItem.HasItems) {
                        if (dest.loot_advanced.TryGetValue(kvp.Key, out AdvancedLootContainerProfile lootProfile)) {
                            InsertLootSpawnSlot(kvp.Key, shortname, newLootItem, ref lootProfile.LootSpawnSlots, ref additions);
                        }
                        else if (dest.loot_simple.TryGetValue(kvp.Key, out SimpleLootContainerProfile simpleLootProfile)) {
                            InsertItemAmountSpawnsWith(kvp.Key, shortname, newLootItem, ref simpleLootProfile.Items, ref additions);
                        }
                    }
                }
                foreach (KeyValuePair<string, AdvancedNPCLootProfile> kvp in source.npcs_advanced) {
                    NewLootItem newLootItem = new NewLootItem();
                    CalculateItemScore(shortname, kvp.Value.LootSpawnSlots, ref newLootItem);
                    if (newLootItem.HasItems) {
                        if (dest.npcs_advanced.TryGetValue(kvp.Key, out AdvancedNPCLootProfile lootProfile)) {
                            InsertLootSpawnSlot(kvp.Key, shortname, newLootItem, ref lootProfile.LootSpawnSlots, ref additions);
                        }
                        else if (dest.npcs_simple.TryGetValue(kvp.Key, out SimpleNPCLootProfile simpleLootProfile)) {
                            InsertItemAmountSpawnsWith(kvp.Key, shortname, newLootItem, ref simpleLootProfile.Items, ref additions);
                        }
                    }
                }
            }
        }
        private void CalculateItemScore(string shortname, LootSpawnSlot[] source, ref NewLootItem newLootItem) {
            foreach (LootSpawnSlot lootSpawnSlot in source) {
                if (lootSpawnSlot?.LootDefinition == null) continue;
                // See if the item is in its own loot spawn slot
                if (!ShouldDuplicateLootSpawnSlot(shortname, lootSpawnSlot, ref newLootItem)) {
                    if (lootSpawnSlot.Eras?.Length > 0) newLootItem.AddEras(lootSpawnSlot.Eras);
                    // Search recursively through the loot spawn slot and calculate a probability
                    if (!string.IsNullOrEmpty(lootSpawnSlot.OnlyWithLoadoutNamed)) newLootItem.RequiredLoadout = lootSpawnSlot.OnlyWithLoadoutNamed;
                    FindItemAndCalculateProbabilityRecursive(lootSpawnSlot.LootDefinition, shortname, newLootItem, lootSpawnSlot.Probability);
                }
            }
        }
        private bool ShouldDuplicateLootSpawnSlot(string shortname, LootSpawnSlot source, ref NewLootItem newLootItem) {
            if (source.LootDefinition.SubSpawn.Length == 0 && source.LootDefinition.Items.Length > 0) {
                if (source.LootDefinition.Items.Any(x => x.Shortname == shortname)) {
                    newLootItem.AddItems(source.LootDefinition.Items);
                    newLootItem.Probability = source.Probability;
                    newLootItem.RequiredLoadout = source.OnlyWithLoadoutNamed;
                    return true;
                }
            }
            return false;
        }
        private void FindItemAndCalculateProbabilityRecursive(LootSpawn lootSpawn, string shortname, NewLootItem newLootItem, float probability) {
            if (lootSpawn.SubSpawn.Length > 0) {
                float subspawnWeight = lootSpawn.SubSpawn.Sum(x => x.Weight);
                foreach(LootSpawn.Entry lootSpawnEntry in lootSpawn.SubSpawn) {
                    if (lootSpawnEntry.RestrictedEras?.Length > 0) newLootItem.AddEras(lootSpawnEntry.RestrictedEras);
                    FindItemAndCalculateProbabilityRecursive(lootSpawnEntry.Category, shortname, newLootItem, probability * ((float)lootSpawnEntry.Weight / (float)subspawnWeight));
                }
            }
            else if (lootSpawn.Items.Length > 0) {
                int occurances = 0;
                float minAmount = 0;
                float maxAmount = 0;
                foreach(ItemAmountRanged itemAmountRanged in lootSpawn.Items) {
                    if (itemAmountRanged.Shortname == shortname) {
                        minAmount += itemAmountRanged.MinAmount;
                        maxAmount += itemAmountRanged.MaxAmount;
                        occurances++;
                    }
                }
                if (occurances > 0) {
                    ItemAmountRanged item = new ItemAmountRanged {
                        Shortname = shortname, MinAmount = minAmount / (float)occurances, MaxAmount = maxAmount / (float)occurances
                    }
                    ;
                    newLootItem.AddItem(item);
                    newLootItem.Probability = probability;
                }
            }
        }
        private void InsertLootSpawnSlot(string container, string shortname, NewLootItem newLootItem, ref LootSpawnSlot[] dest, ref int additions) {
            LootSpawnSlot newLootSpawnSlot = new LootSpawnSlot {
                LootDefinition = new LootSpawn() {
                    Items = newLootItem.Items.ToArray(), SubSpawn = new LootSpawn.Entry[0]
                }
                , NumberToSpawn = 1, Probability = newLootItem.Probability, OnlyWithLoadoutNamed = newLootItem.RequiredLoadout, Eras = newLootItem.RequiredEras?.ToArray() ?? new Era[0]
            }
            ;
            int index = dest.Length;
            Array.Resize(ref dest, index + 1);
            dest[index] = newLootSpawnSlot;
            additions++;
            Debug.Log($"[AlphaLoot] - Added {shortname} to advanced loot profile ({container}) with a calculated probability of {newLootItem.Probability}");
        }
        private void InsertItemAmountSpawnsWith(string container, string shortname, NewLootItem newLootItem, ref ItemAmountSpawnsWith[] dest, ref int additions) {
            float weight = dest.Sum(x => x.Weight);
            int index = dest.Length;
            Array.Resize(ref dest, index + newLootItem.Items.Count);
            for (int i = 0; i < newLootItem.Items.Count; i++) {
                ItemAmountRanged itemAmountRanged = newLootItem.Items[i];
                ItemAmountSpawnsWith itemAmountWeighted = new ItemAmountSpawnsWith {
                    Condition = new ItemAmount.ConditionItem(), MaxAmount = itemAmountRanged.MaxAmount, MinAmount = itemAmountRanged.MinAmount, Shortname = itemAmountRanged.Shortname, Weight = Mathf.Max(Mathf.RoundToInt(weight * newLootItem.Probability), 1), RestrictedEras = newLootItem.RequiredEras?.ToArray() ?? new Era[0], SpawnsWith = new ItemAmountWeighted[0]
                }
                ;
                dest[index+i] = itemAmountWeighted;
            }
            additions++;
            Debug.Log($"[AlphaLoot] - Added {shortname} to simple loot profile ({container}) with a calculated probability of {newLootItem.Probability}");
        }
        private class NewLootItem {
            public List<ItemAmountRanged> Items;
            public float Probability;
            public string RequiredLoadout;
            public List<Era> RequiredEras;
            public bool HasItems => Items != null;
            public void AddItem(ItemAmountRanged itemAmountRanged) {
                Items ??= new List<ItemAmountRanged>();
                Items.Add(itemAmountRanged);
            }
            public void AddItems(ItemAmountRanged[] items) {
                Items ??= new List<ItemAmountRanged>();
                Items.AddRange(items);
            }
            public void AddEras(IEnumerable<Era> enumerable) {
                RequiredEras ??= new List<Era>();
                foreach (Era era in enumerable) {
                    if (!RequiredEras.Contains(era)) RequiredEras.Add(era);
                }
            }
        }
        private class ItemList {
            public List<int> itemIds = new List<int>();
            public string protocol;
        }
#endregion
#region Removed Item Scan
        private List<ItemAmountRanged> keepItemsRanged = new List<ItemAmountRanged>();
        private List<ItemAmountSpawnsWith> keepItemsWeighted = new List<ItemAmountSpawnsWith>();
        private void FindAndRemoveInvalidItems() {
            keepItemsRanged.Clear();
            keepItemsWeighted.Clear();
            Puts("Scanning loot tables for removed items...");
            FindAndRemoveInvalidItems(data, storedData, "Loot Table");
            FindAndRemoveInvalidItems(heli, heliData, "Heli Loot Table");
            FindAndRemoveInvalidItems(bradley, bradleyData, "Bradley Loot Table");
            Puts("Removed item scan completed!");
        }
        private void FindAndRemoveInvalidItems(DynamicConfigFile dynamicConfigFile, StoredData data, string table) {
            Puts($"Scanning {table}...");
            bool shouldSave = false;
            foreach (KeyValuePair<string, AdvancedLootContainerProfile> kvp in data.loot_advanced) {
                foreach (LootSpawnSlot lootSpawnSlot in kvp.Value.LootSpawnSlots) {
                    if (lootSpawnSlot?.LootDefinition == null) continue;
                    FindItemRemoveInvalidItemRecursive(kvp.Key, ref lootSpawnSlot.LootDefinition, ref shouldSave);
                }
            }
            foreach (KeyValuePair<string, SimpleLootContainerProfile> kvp in storedData.loot_simple) FindItemRemoveInvalidItemRecursive(kvp.Key, ref kvp.Value.Items, ref shouldSave);
            foreach (KeyValuePair<string, AdvancedNPCLootProfile> kvp in data.npcs_advanced) {
                foreach (LootSpawnSlot lootSpawnSlot in kvp.Value.LootSpawnSlots) {
                    if (lootSpawnSlot?.LootDefinition == null) continue;
                    FindItemRemoveInvalidItemRecursive(kvp.Key, ref lootSpawnSlot.LootDefinition, ref shouldSave);
                }
            }
            foreach (KeyValuePair<string, SimpleNPCLootProfile> kvp in storedData.npcs_simple) FindItemRemoveInvalidItemRecursive(kvp.Key, ref kvp.Value.Items, ref shouldSave);
            foreach (KeyValuePair<string, AdvancedCustomLootProfile> kvp in data.custom_advanced) {
                foreach (LootSpawnSlot lootSpawnSlot in kvp.Value.LootSpawnSlots) {
                    if (lootSpawnSlot?.LootDefinition == null) continue;
                    FindItemRemoveInvalidItemRecursive(kvp.Key, ref lootSpawnSlot.LootDefinition, ref shouldSave);
                }
            }
            foreach (KeyValuePair<string, SimpleCustomLootProfile> kvp in storedData.custom_simple) FindItemRemoveInvalidItemRecursive(kvp.Key, ref kvp.Value.Items, ref shouldSave);
            if (shouldSave) dynamicConfigFile.WriteObject(data);
        }
        private void FindItemRemoveInvalidItemRecursive(string container, ref LootSpawn lootSpawn, ref bool shouldSave) {
            if (lootSpawn.SubSpawn.Length > 0) {
                foreach (LootSpawn.Entry lootSpawnEntry in lootSpawn.SubSpawn) {
                    FindItemRemoveInvalidItemRecursive(container, ref lootSpawnEntry.Category, ref shouldSave);
                }
            }
            else if (lootSpawn.Items.Length > 0) {
                bool shouldUpdate = false;
                for (int i = 0; i < lootSpawn.Items.Length; i++) {
                    ItemAmountRanged itemAmountRanged = lootSpawn.Items[i];
                    if (ItemManager.itemDictionaryByName.ContainsKey(itemAmountRanged.Shortname)) keepItemsRanged.Add(itemAmountRanged);
                    else {
                        if (shortnameReplacements.TryGetValue(itemAmountRanged.Shortname, out string shortname) && ItemManager.itemDictionaryByName.ContainsKey(shortname)) {
                            Puts($"Replacing invalid shortname {itemAmountRanged.Shortname} with {shortname} in {container}");
                            itemAmountRanged.Shortname = shortname;
                            keepItemsRanged.Add(itemAmountRanged);
                            shouldSave = true;
                        }
                        else {
                            shouldSave = true;
                            shouldUpdate = true;
                            Puts($"Removing {itemAmountRanged.Shortname} from {container}");
                        }
                    }
                }
                if (shouldUpdate) lootSpawn.Items = keepItemsRanged.ToArray();
                keepItemsRanged.Clear();
            }
        }
        private void FindItemRemoveInvalidItemRecursive(string container, ref ItemAmountSpawnsWith[] items, ref bool shouldSave) {
            bool shouldUpdate = false;
            for (int i = 0; i < items.Length; i++) {
                ItemAmountSpawnsWith itemAmountSpawnsWith = items[i];
                if (ItemManager.itemDictionaryByName.ContainsKey(itemAmountSpawnsWith.Shortname)) {
                    if (itemAmountSpawnsWith.SpawnsWith?.Length > 0) {
                        for (int i1 = itemAmountSpawnsWith.SpawnsWith.Length - 1; i1 >= 0; i1--) {
                            ItemAmountWeighted spawnsWith = itemAmountSpawnsWith.SpawnsWith[i1];
                            if (!ItemManager.itemDictionaryByName.ContainsKey(spawnsWith.Shortname)) {
                                RemoveFromArray(ref itemAmountSpawnsWith.SpawnsWith, spawnsWith);
                                shouldSave = true;
                            }
                        }
                    }
                    keepItemsWeighted.Add(itemAmountSpawnsWith);
                }
                else {
                    shouldSave = true;
                    shouldUpdate = true;
                    Puts($"Removing '{itemAmountSpawnsWith.Shortname}' from container '{container}'");
                }
            }
            if (shouldUpdate) items = keepItemsWeighted.ToArray();
            keepItemsWeighted.Clear();
        }
#endregion
#region Remove Items By Shortname
        private void FindAndRemoveItems(string[] shortnames) {
            keepItemsRanged.Clear();
            keepItemsWeighted.Clear();
            Puts($"Scanning loot tables for {shortnames.ToSentence()}...");
            int count = 0;
            FindAndRemoveItems(data, storedData, "Loot Table", shortnames, ref count);
            FindAndRemoveItems(heli, heliData, "Heli Loot Table", shortnames, ref count);
            FindAndRemoveItems(bradley, bradleyData, "Bradley Loot Table", shortnames, ref count);
            Puts($"Removed {count} of {shortnames.ToSentence()}!");
        }
        private void FindAndRemoveItems(DynamicConfigFile dynamicConfigFile, StoredData data, string table, string[] shortnames, ref int count) {
            Puts($"Scanning {table}...");
            bool shouldSave = false;
            foreach (KeyValuePair<string, AdvancedLootContainerProfile> kvp in data.loot_advanced) {
                foreach (LootSpawnSlot lootSpawnSlot in kvp.Value.LootSpawnSlots) {
                    if (lootSpawnSlot?.LootDefinition == null) continue;
                    FindItemRemoveItemByNameRecursive(kvp.Key, shortnames, ref lootSpawnSlot.LootDefinition, ref shouldSave, ref count);
                }
            }
            foreach (KeyValuePair<string, SimpleLootContainerProfile> kvp in storedData.loot_simple) FindItemRemoveItemByNameRecursive(kvp.Key, shortnames, ref kvp.Value.Items, ref shouldSave, ref count);
            foreach (KeyValuePair<string, AdvancedNPCLootProfile> kvp in data.npcs_advanced) {
                foreach (LootSpawnSlot lootSpawnSlot in kvp.Value.LootSpawnSlots) {
                    if (lootSpawnSlot?.LootDefinition == null) continue;
                    FindItemRemoveItemByNameRecursive(kvp.Key, shortnames, ref lootSpawnSlot.LootDefinition, ref shouldSave, ref count);
                }
            }
            foreach (KeyValuePair<string, SimpleNPCLootProfile> kvp in storedData.npcs_simple) FindItemRemoveItemByNameRecursive(kvp.Key, shortnames, ref kvp.Value.Items, ref shouldSave, ref count);
            foreach (KeyValuePair<string, AdvancedCustomLootProfile> kvp in data.custom_advanced) {
                foreach (LootSpawnSlot lootSpawnSlot in kvp.Value.LootSpawnSlots) {
                    if (lootSpawnSlot?.LootDefinition == null) continue;
                    FindItemRemoveItemByNameRecursive(kvp.Key, shortnames, ref lootSpawnSlot.LootDefinition, ref shouldSave, ref count);
                }
            }
            foreach (KeyValuePair<string, SimpleCustomLootProfile> kvp in storedData.custom_simple) FindItemRemoveItemByNameRecursive(kvp.Key, shortnames, ref kvp.Value.Items, ref shouldSave, ref count);
            if (shouldSave) dynamicConfigFile.WriteObject(data);
        }
        private void FindItemRemoveItemByNameRecursive(string container, string[] shortnames, ref LootSpawn lootSpawn, ref bool shouldSave, ref int count) {
            if (lootSpawn.SubSpawn.Length > 0) {
                foreach (LootSpawn.Entry lootSpawnEntry in lootSpawn.SubSpawn) {
                    FindItemRemoveItemByNameRecursive(container, shortnames, ref lootSpawnEntry.Category, ref shouldSave, ref count);
                }
            }
            else if (lootSpawn.Items.Length > 0) {
                bool shouldUpdate = false;
                for (int i = 0; i < lootSpawn.Items.Length; i++) {
                    ItemAmountRanged itemAmountRanged = lootSpawn.Items[i];
                    if (!shortnames.Contains(itemAmountRanged.Shortname)) keepItemsRanged.Add(itemAmountRanged);
                    else {
                        count++;
                        shouldSave = true;
                        shouldUpdate = true;
                        Puts($"Removing {itemAmountRanged.Shortname} from {container}");
                    }
                }
                if (shouldUpdate) lootSpawn.Items = keepItemsRanged.ToArray();
                keepItemsRanged.Clear();
            }
        }
        private void FindItemRemoveItemByNameRecursive(string container, string[] shortnames, ref ItemAmountSpawnsWith[] items, ref bool shouldSave, ref int count) {
            bool shouldUpdate = false;
            for (int i = 0; i < items.Length; i++) {
                ItemAmountSpawnsWith itemAmountSpawnsWith = items[i];
                if (!shortnames.Contains(itemAmountSpawnsWith.Shortname)) {
                    if (itemAmountSpawnsWith.SpawnsWith?.Length > 0) {
                        for (int i1 = itemAmountSpawnsWith.SpawnsWith.Length - 1; i1 >= 0; i1--) {
                            ItemAmountWeighted spawnsWith = itemAmountSpawnsWith.SpawnsWith[i1];
                            if (shortnames.Contains(spawnsWith.Shortname)) {
                                RemoveFromArray(ref itemAmountSpawnsWith.SpawnsWith, spawnsWith);
                                count++;
                                shouldSave = true;
                            }
                        }
                    }
                    keepItemsWeighted.Add(itemAmountSpawnsWith);
                }
                else {
                    count++;
                    shouldSave = true;
                    shouldUpdate = true;
                    Puts($"Removing '{itemAmountSpawnsWith.Shortname}' from container '{container}'");
                }
            }
            if (shouldUpdate) items = keepItemsWeighted.ToArray();
            keepItemsWeighted.Clear();
        }
#endregion
#region Remove Empty Definitions
        private void FindAndRemoveEmptyDefinitions() {
            Puts($"Scanning loot tables for empty loot definitions...");
            int count = 0;
            FindAndRemoveEmptyDefinitions(data, storedData, "Loot Table", ref count);
            FindAndRemoveEmptyDefinitions(heli, heliData, "Heli Loot Table", ref count);
            FindAndRemoveEmptyDefinitions(bradley, bradleyData, "Bradley Loot Table", ref count);
            Puts($"Removed {count} empty loot definitions!");
        }
        private void FindAndRemoveEmptyDefinitions(DynamicConfigFile dynamicConfigFile, StoredData data, string table, ref int count) {
            Puts($"Scanning {table}...");
            bool shouldSave = false;
            foreach (KeyValuePair<string, AdvancedLootContainerProfile> kvp in data.loot_advanced) {
                foreach (LootSpawnSlot lootSpawnSlot in kvp.Value.LootSpawnSlots) {
                    if (lootSpawnSlot?.LootDefinition == null) continue;
                    FindRemoveEmptyDefinitionsRecursive(kvp.Key, ref lootSpawnSlot.LootDefinition, ref shouldSave, ref count);
                    if (IsEmptyDefinition(lootSpawnSlot.LootDefinition)) {
                        RemoveFromArray(ref kvp.Value.LootSpawnSlots, lootSpawnSlot);
                        count++;
                        shouldSave = true;
                        Puts($"Removing empty definition from {kvp.Key}");
                    }
                }
            }
            foreach (KeyValuePair<string, AdvancedNPCLootProfile> kvp in data.npcs_advanced) {
                foreach (LootSpawnSlot lootSpawnSlot in kvp.Value.LootSpawnSlots) {
                    if (lootSpawnSlot?.LootDefinition == null) continue;
                    FindRemoveEmptyDefinitionsRecursive(kvp.Key, ref lootSpawnSlot.LootDefinition, ref shouldSave, ref count);
                    if (IsEmptyDefinition(lootSpawnSlot.LootDefinition)) {
                        RemoveFromArray(ref kvp.Value.LootSpawnSlots, lootSpawnSlot);
                        count++;
                        shouldSave = true;
                        Puts($"Removing empty definition from {kvp.Key}");
                    }
                }
            }
            foreach (KeyValuePair<string, AdvancedCustomLootProfile> kvp in data.custom_advanced) {
                foreach (LootSpawnSlot lootSpawnSlot in kvp.Value.LootSpawnSlots) {
                    if (lootSpawnSlot?.LootDefinition == null) continue;
                    FindRemoveEmptyDefinitionsRecursive(kvp.Key, ref lootSpawnSlot.LootDefinition, ref shouldSave, ref count);
                    if (IsEmptyDefinition(lootSpawnSlot.LootDefinition)) {
                        RemoveFromArray(ref kvp.Value.LootSpawnSlots, lootSpawnSlot);
                        count++;
                        shouldSave = true;
                        Puts($"Removing empty definition from {kvp.Key}");
                    }
                }
            }
            if (shouldSave) dynamicConfigFile.WriteObject(data);
        }
        private bool IsEmptyDefinition(LootSpawn lootSpawn) => lootSpawn.SubSpawn?.Length == 0 && lootSpawn.Items?.Length == 0;
        private void FindRemoveEmptyDefinitionsRecursive(string container, ref LootSpawn lootSpawn, ref bool shouldSave, ref int count) {
            if (lootSpawn.SubSpawn.Length > 0) {
                for (int i = lootSpawn.SubSpawn.Length - 1; i >= 0; i--) {
                    LootSpawn.Entry lootSpawnEntry = lootSpawn.SubSpawn[i];
                    if (lootSpawnEntry.Category != null) {
                        FindRemoveEmptyDefinitionsRecursive(container, ref lootSpawnEntry.Category, ref shouldSave, ref count);
                        if (IsEmptyDefinition(lootSpawnEntry.Category)) {
                            RemoveFromArray(ref lootSpawn.SubSpawn, lootSpawnEntry);
                            count++;
                            shouldSave = true;
                            Puts($"Removing empty definition from {container}");
             
// ... [Code preview truncated due to length. Full source in attached file] ...

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