From 2bcffc55a94d6462c9c30f1269974e7cb9a237a3 Mon Sep 17 00:00:00 2001 From: Phedg1 Studios Date: Fri, 27 Nov 2020 00:36:07 +1000 Subject: [PATCH 1/4] ItemDropAPI and MonsterItemsAPI Bug Fixes Changed drop list operation lists so they are cleared each time they are used, as a stop gap measure so my other mods would be able to continue functioning normally. I know Rein's methods will make this redundant shortly. Drop lists should never be empty now and will always contain PickupIndex.none instead. This was for increased consistency, as previously these lists could sometimes contain none and sometimes be empty, causing bugs. Added function to determine if a drop list only contained PickupIndex.none, this has been substituted in wherever a check was done previously to see if a drop list was empty. All world unique items are now stored in a list in Catalog and are no longer added to the subset drop lists in DropList by default. When using the command artifact, world unique items will now be listed by themselves unless they are added to the subset drop list of their tier. Boss drops now check their availability against the world unique drop list if their drop is a world unique item. --- R2API/ItemDrop/Catalog.cs | 22 ++--- R2API/ItemDrop/DropList.cs | 111 ++++++++++++++++------- R2API/ItemDrop/InteractableCalculator.cs | 10 +- R2API/ItemDropAPI.cs | 69 +++++++++++--- R2API/MonsterItemsAPI.cs | 26 ++++-- 5 files changed, 158 insertions(+), 80 deletions(-) diff --git a/R2API/ItemDrop/Catalog.cs b/R2API/ItemDrop/Catalog.cs index be8462fc..d832f845 100644 --- a/R2API/ItemDrop/Catalog.cs +++ b/R2API/ItemDrop/Catalog.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; using RoR2; +using UnityEngine; namespace R2API { namespace ItemDropAPITools { public static class Catalog { public static bool Loaded; - public static readonly List SpecialBossItems = new List(); + public static readonly List SpecialItems = new List(); public static readonly Dictionary ScrapItems = new Dictionary(); public static readonly List EliteEquipment = new List(); @@ -18,24 +19,13 @@ public static void PopulateItemCatalog() { if (!Loaded) { foreach (var itemIndex in ItemCatalog.allItems) { var itemDef = ItemCatalog.GetItemDef(itemIndex); - foreach (var itemTag in itemDef.tags) { - if (itemTag == ItemTag.Scrap) { + if (itemDef.tier != ItemTier.NoTier && itemDef.pickupIconSprite != null && itemDef.pickupIconSprite.name != DropList.NullIconTextureName) { + if (itemDef.ContainsTag(ItemTag.Scrap)) { if (!ScrapItems.ContainsKey(itemDef.tier)) { ScrapItems.Add(itemDef.tier, itemIndex); } - } - } - if (!ItemCatalog.tier1ItemList.Contains(itemIndex) && - !ItemCatalog.tier2ItemList.Contains(itemIndex) && - !ItemCatalog.tier3ItemList.Contains(itemIndex) && - !ItemCatalog.lunarItemList.Contains(itemIndex)) { - if (!ScrapItems.ContainsValue(itemIndex) && itemDef.tier != ItemTier.NoTier && - itemDef.pickupIconSprite != null && itemDef.pickupIconSprite.name != DropList.NullIconTextureName) { - foreach (var itemTag in itemDef.tags) { - if (itemTag == ItemTag.WorldUnique) { - SpecialBossItems.Add(itemIndex); - } - } + } else if (itemDef.ContainsTag(ItemTag.WorldUnique)) { + SpecialItems.Add(itemIndex); } } } diff --git a/R2API/ItemDrop/DropList.cs b/R2API/ItemDrop/DropList.cs index 71280dfc..b5690799 100644 --- a/R2API/ItemDrop/DropList.cs +++ b/R2API/ItemDrop/DropList.cs @@ -2,6 +2,8 @@ using System.Linq; using R2API.ItemDrop; using RoR2; +using UnityEngine; + namespace R2API { namespace ItemDropAPITools { @@ -119,14 +121,19 @@ public void DuplicateDropLists(Run run) { LunarEquipmentDropListOriginal = BackupDropList(run.availableLunarEquipmentDropList); BossDropListOriginal = BackupDropList(run.availableBossDropList); - foreach (var bossItem in Catalog.SpecialBossItems) { + /* + foreach (var bossItem in Catalog.SpecialItems) { var pickupIndex = PickupCatalog.FindPickupIndex(bossItem); if (!BossDropListOriginal.Contains(pickupIndex)) { BossDropListOriginal.Add(pickupIndex); } } + */ SpecialItemsOriginal.Clear(); + foreach (var itemIndex in Catalog.SpecialItems) { + SpecialItemsOriginal.Add(PickupCatalog.FindPickupIndex(itemIndex)); + } foreach (var itemIndex in Catalog.ScrapItems.Values) { SpecialItemsOriginal.Add(PickupCatalog.FindPickupIndex(itemIndex)); } @@ -149,25 +156,26 @@ internal void GenerateDropLists( Dictionary> equipmentsToAdd, Dictionary> equipmentsToRemove) { - AvailableTier1DropList = CreateDropList(Tier1DropListOriginal, itemsToAdd[ItemTier.Tier1], itemsToRemove[ItemTier.Tier1]); - AvailableTier2DropList = CreateDropList(Tier2DropListOriginal, itemsToAdd[ItemTier.Tier2], itemsToRemove[ItemTier.Tier2]); - AvailableTier3DropList = CreateDropList(Tier3DropListOriginal, itemsToAdd[ItemTier.Tier3], itemsToRemove[ItemTier.Tier3]); - AvailableLunarDropList = CreateDropList(LunarDropListOriginal, itemsToAdd[ItemTier.Lunar], itemsToRemove[ItemTier.Lunar]); - AvailableSpecialItems = CreateDropList(SpecialItemsOriginal, itemsToAdd[ItemTier.NoTier], itemsToRemove[ItemTier.NoTier]); + AvailableTier1DropList = BackupDropList(CreateDropList(Tier1DropListOriginal, itemsToAdd[ItemTier.Tier1], itemsToRemove[ItemTier.Tier1])); + AvailableTier2DropList = BackupDropList(CreateDropList(Tier2DropListOriginal, itemsToAdd[ItemTier.Tier2], itemsToRemove[ItemTier.Tier2])); + AvailableTier3DropList = BackupDropList(CreateDropList(Tier3DropListOriginal, itemsToAdd[ItemTier.Tier3], itemsToRemove[ItemTier.Tier3])); + AvailableLunarDropList = BackupDropList(CreateDropList(LunarDropListOriginal, itemsToAdd[ItemTier.Lunar], itemsToRemove[ItemTier.Lunar])); + AvailableSpecialItems = BackupDropList(CreateDropList(SpecialItemsOriginal, itemsToAdd[ItemTier.NoTier], itemsToRemove[ItemTier.NoTier])); - AvailableEquipmentDropList = CreateDropList(NormalEquipmentDropListOriginal, - equipmentsToAdd[EquipmentDropType.Normal], equipmentsToRemove[EquipmentDropType.Normal]); + AvailableEquipmentDropList = BackupDropList(CreateDropList(NormalEquipmentDropListOriginal, + equipmentsToAdd[EquipmentDropType.Normal], equipmentsToRemove[EquipmentDropType.Normal])); AvailableNormalEquipmentDropList = AvailableEquipmentDropList; - AvailableLunarEquipmentDropList = CreateDropList(LunarEquipmentDropListOriginal, - equipmentsToAdd[EquipmentDropType.Lunar], equipmentsToRemove[EquipmentDropType.Lunar]); + AvailableLunarEquipmentDropList = BackupDropList(CreateDropList(LunarEquipmentDropListOriginal, + equipmentsToAdd[EquipmentDropType.Lunar], equipmentsToRemove[EquipmentDropType.Lunar])); - AvailableSpecialEquipment = CreateDropList(SpecialEquipmentOriginal, - equipmentsToAdd[EquipmentDropType.Elite], equipmentsToRemove[EquipmentDropType.Elite]); + AvailableSpecialEquipment = BackupDropList(CreateDropList(SpecialEquipmentOriginal, + equipmentsToAdd[EquipmentDropType.Elite], equipmentsToRemove[EquipmentDropType.Elite])); - AvailableBossDropList = CreateDropList(BossDropListOriginal, + AvailableBossDropList = BackupDropList(CreateDropList(BossDropListOriginal, itemsToAdd[ItemTier.Boss], equipmentsToAdd[EquipmentDropType.Boss], - itemsToRemove[ItemTier.Boss], equipmentsToRemove[EquipmentDropType.Boss]); + itemsToRemove[ItemTier.Boss], equipmentsToRemove[EquipmentDropType.Boss])); + } private static List CreateDropList(IEnumerable vanillaDropList, @@ -273,41 +281,67 @@ private static List CreateDropList( } public void SetItems(Run run) { - foreach (var pickupIndex in AvailableTier1DropList) { - run.availableTier1DropList.Add(pickupIndex); - run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + if (IsValidList(AvailableTier1DropList)) { + foreach (var pickupIndex in AvailableTier1DropList) { + run.availableTier1DropList.Add(pickupIndex); + run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + } } - foreach (var pickupIndex in AvailableTier2DropList) { - run.availableTier2DropList.Add(pickupIndex); - run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + if (IsValidList(AvailableTier2DropList)) { + foreach (var pickupIndex in AvailableTier2DropList) { + run.availableTier2DropList.Add(pickupIndex); + run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + } } - foreach (var pickupIndex in AvailableTier3DropList) { - run.availableTier3DropList.Add(pickupIndex); - run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + if (IsValidList(AvailableTier3DropList)) { + foreach (var pickupIndex in AvailableTier3DropList) { + run.availableTier3DropList.Add(pickupIndex); + run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + } + } + + if (IsValidList(AvailableBossDropList)) { + foreach (var pickupIndex in AvailableBossDropList) { + run.availableBossDropList.Add(pickupIndex); + run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + } } - foreach (var pickupIndex in AvailableBossDropList) { - run.availableBossDropList.Add(pickupIndex); - run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + if (IsValidList(AvailableLunarDropList)) { + foreach (var pickupIndex in AvailableLunarDropList) { + run.availableLunarDropList.Add(pickupIndex); + run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + } } - foreach (var pickupIndex in AvailableLunarDropList) { - run.availableLunarDropList.Add(pickupIndex); - run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + if (IsValidList(AvailableSpecialItems)) { + foreach (var pickupIndex in AvailableSpecialItems) { + run.availableItems.Add(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + } } - foreach (var pickupIndex in AvailableEquipmentDropList) { - run.availableEquipmentDropList.Add(pickupIndex); - run.availableEquipment.Add(PickupCatalog.GetPickupDef(pickupIndex).equipmentIndex); + if (IsValidList(AvailableEquipmentDropList)) { + foreach (var pickupIndex in AvailableEquipmentDropList) { + run.availableEquipmentDropList.Add(pickupIndex); + run.availableEquipment.Add(PickupCatalog.GetPickupDef(pickupIndex).equipmentIndex); + } } // high probability of code smell from ror2 code run.availableNormalEquipmentDropList = run.availableEquipmentDropList; - foreach (var pickupIndex in AvailableLunarEquipmentDropList) { - run.availableLunarEquipmentDropList.Add(pickupIndex); - run.availableEquipment.Add(PickupCatalog.GetPickupDef(pickupIndex).equipmentIndex); + if (IsValidList(AvailableLunarEquipmentDropList)) { + foreach (var pickupIndex in AvailableLunarEquipmentDropList) { + run.availableLunarEquipmentDropList.Add(pickupIndex); + run.availableEquipment.Add(PickupCatalog.GetPickupDef(pickupIndex).equipmentIndex); + } + } + + if (IsValidList(AvailableSpecialEquipment)) { + foreach (var pickupIndex in AvailableSpecialEquipment) { + run.availableEquipment.Add(PickupCatalog.GetPickupDef(pickupIndex).equipmentIndex); + } } } @@ -318,6 +352,13 @@ public static List ToPickupIndices(IEnumerable indices) public static List ToPickupIndices(IEnumerable indices) { return indices.Select(PickupCatalog.FindPickupIndex).ToList(); } + + public static bool IsValidList(IEnumerable dropList) { + if (dropList.Count() == 1 && dropList.Contains(PickupIndex.none)) { + return false; + } + return true; + } } } } diff --git a/R2API/ItemDrop/InteractableCalculator.cs b/R2API/ItemDrop/InteractableCalculator.cs index 1ba58ed9..ab687c26 100644 --- a/R2API/ItemDrop/InteractableCalculator.cs +++ b/R2API/ItemDrop/InteractableCalculator.cs @@ -193,11 +193,10 @@ public void CalculateInvalidInteractables(DropList dropList) { } } - foreach (var pickupIndex in dropList.AvailableBossDropList) { + foreach (var pickupIndex in dropList.AvailableSpecialItems) { var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef != null) { if (pickupDef.itemIndex != ItemIndex.None && - !Catalog.ScrapItems.ContainsValue(pickupDef.itemIndex) && Catalog.Pearls.Contains(pickupDef.itemIndex)) { TiersPresent["pearl"] = true; break; @@ -209,8 +208,7 @@ public void CalculateInvalidInteractables(DropList dropList) { var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef != null) { if (pickupDef.itemIndex != ItemIndex.None && - !Catalog.ScrapItems.ContainsValue(pickupDef.itemIndex) && - !Catalog.Pearls.Contains(pickupDef.itemIndex)) { + !Catalog.ScrapItems.ContainsValue(pickupDef.itemIndex)) { TiersPresent["boss"] = true; break; } @@ -262,10 +260,10 @@ public void CalculateInvalidInteractables(DropList dropList) { } } } - if (dropList.AvailableNormalEquipmentDropList.Count > 0) { + if (DropList.IsValidList(dropList.AvailableNormalEquipmentDropList)) { TiersPresent["equipment"] = true; } - if (dropList.AvailableLunarEquipmentDropList.Count > 0) { + if (DropList.IsValidList(dropList.AvailableLunarEquipmentDropList)) { TiersPresent["lunar"] = true; } var interactableTypeKeys = InteractablesTiers.Keys.ToList(); diff --git a/R2API/ItemDropAPI.cs b/R2API/ItemDropAPI.cs index 09280c97..430f99c0 100644 --- a/R2API/ItemDropAPI.cs +++ b/R2API/ItemDropAPI.cs @@ -105,11 +105,28 @@ internal static void UnsetHooks() { private static void RunOnBuildDropTable(On.RoR2.Run.orig_BuildDropTable orig, Run run) { Catalog.PopulateItemCatalog(); orig(run); + PlayerDropList.DuplicateDropLists(run); PlayerDropList.ClearAllLists(run); PlayerDropList.GenerateDropLists(ItemsToAdd, ItemsToRemove, EquipmentsToAdd, EquipmentsToRemove); PlayerDropList.SetItems(run); PlayerInteractables.CalculateInvalidInteractables(PlayerDropList); + + ClearItemOperations(ItemsToAdd); + ClearItemOperations(ItemsToRemove); + ClearEquipmentOperations(EquipmentsToAdd); + ClearEquipmentOperations(EquipmentsToRemove); + } + + public static void ClearItemOperations(Dictionary> givenDict) { + foreach (ItemTier itemTier in givenDict.Keys) { + givenDict[itemTier].Clear(); + } + } + public static void ClearEquipmentOperations(Dictionary> givenDict) { + foreach (EquipmentDropType equipmentDropType in givenDict.Keys) { + givenDict[equipmentDropType].Clear(); + } } private static void PopulateScene(On.RoR2.SceneDirector.orig_PopulateScene orig, SceneDirector sceneDirector) { @@ -216,17 +233,24 @@ private static void DropRewards(On.RoR2.BossGroup.orig_DropRewards orig, BossGro foreach (var bossDrop in bossGroup.bossDrops) { var pickupIndex = bossDrop; bossDrops.Add(pickupIndex); - if (PickupCatalog.GetPickupDef(pickupIndex).itemIndex != ItemIndex.None && PlayerDropList.AvailableBossDropList.Contains(pickupIndex)) { + bool worldUnique = false; + if (PickupCatalog.GetPickupDef(pickupIndex).itemIndex != ItemIndex.None && ItemCatalog.GetItemDef(PickupCatalog.GetPickupDef(pickupIndex).itemIndex).ContainsTag(ItemTag.WorldUnique)) { + worldUnique = true; + } + if ((PlayerDropList.AvailableBossDropList.Contains(pickupIndex) && !worldUnique ) || (PlayerDropList.AvailableSpecialItems.Contains(pickupIndex) && worldUnique)) { bossDropsAdjusted.Add(pickupIndex); } } - var normalCount = Run.instance.availableTier2DropList.Count; + + var dropList = Run.instance.availableTier2DropList; if (bossGroup.forceTier3Reward) { - normalCount = Run.instance.availableTier3DropList.Count; + dropList = Run.instance.availableTier3DropList; } - if (normalCount != 0 || bossDropsAdjusted.Count != 0) { + bool normalListValid = DropList.IsValidList(dropList); + + if (normalListValid || bossDropsAdjusted.Count != 0) { var bossDropChanceOld = bossGroup.bossDropChance; - if (normalCount == 0) { + if (!normalListValid) { DropList.SetDropLists(new List(), new List(), new List(), new List()); bossGroup.bossDropChance = 1; } else if (bossDropsAdjusted.Count == 0) { @@ -235,9 +259,10 @@ private static void DropRewards(On.RoR2.BossGroup.orig_DropRewards orig, BossGro bossGroup.bossDrops = bossDropsAdjusted; orig(bossGroup); + bossGroup.bossDrops = bossDrops; bossGroup.bossDropChance = bossDropChanceOld; - if (normalCount == 0) { + if (!normalListValid) { DropList.RevertDropLists(); } } @@ -256,19 +281,37 @@ private static void SetOptionsServer(On.RoR2.PickupPickerController.orig_SetOpti } if (pickupPickerController.contextString.Contains(CommandCubeContextString)) { if (options.Length > 0) { + optionsAdjusted.Clear(); var itemIndex = PickupCatalog.GetPickupDef(options[0].pickupIndex).itemIndex; + var itemTier = ItemTier.NoTier; if (itemIndex != ItemIndex.None) { itemTier = ItemCatalog.GetItemDef(itemIndex).tier; } - var tierList = PlayerDropList.GetDropList(itemTier); - optionsAdjusted.Clear(); - foreach (var pickupIndex in tierList) { - var newOption = new PickupPickerController.Option { - available = true, pickupIndex = pickupIndex - }; - optionsAdjusted.Add(newOption); + + bool addEntireTier = true; + if (options.Length == 1 && itemIndex != ItemIndex.None && ItemCatalog.GetItemDef(itemIndex).ContainsTag(ItemTag.WorldUnique)) { + addEntireTier = false; + if (itemTier != ItemTier.NoTier && tierList.Contains(options[0].pickupIndex)) { + addEntireTier = true; + } + } + + if (addEntireTier) { + foreach (var pickupIndex in tierList) { + ItemIndex pickupItemIndex = PickupCatalog.GetPickupDef(pickupIndex).itemIndex; + + if (true || pickupItemIndex == ItemIndex.None || ItemCatalog.GetItemDef(pickupItemIndex).DoesNotContainTag(ItemTag.WorldUnique)) { + var newOption = new PickupPickerController.Option { + available = true, + pickupIndex = pickupIndex + }; + optionsAdjusted.Add(newOption); + } + } + } else { + optionsAdjusted.Add(options[0]); } } } diff --git a/R2API/MonsterItemsAPI.cs b/R2API/MonsterItemsAPI.cs index 73f8e7d8..7ab86579 100644 --- a/R2API/MonsterItemsAPI.cs +++ b/R2API/MonsterItemsAPI.cs @@ -85,6 +85,10 @@ private static void RunOnBuildDropTable(On.RoR2.Run.orig_BuildDropTable orig, Ru orig(run); MonsterDropList.DuplicateDropLists(run); MonsterDropList.GenerateDropLists(ItemsToAdd, ItemsToRemove, EquipmentsToAdd, EquipmentsToRemove); + ItemDropAPI.ClearItemOperations(ItemsToAdd); + ItemDropAPI.ClearItemOperations(ItemsToRemove); + ItemDropAPI.ClearEquipmentOperations(EquipmentsToAdd); + ItemDropAPI.ClearEquipmentOperations(EquipmentsToRemove); } private static void GenerateAvailableItemsSet(On.RoR2.Artifacts.MonsterTeamGainsItemsArtifactManager.orig_GenerateAvailableItemsSet orig) { @@ -269,17 +273,19 @@ private static void GiveRandomEquipment(On.RoR2.Inventory.orig_GiveRandomEquipme } public static bool ListContainsValidItems(List forbiddenTags, List givenList) { - foreach (var pickupIndex in givenList) { - var validItem = true; - var itemDef = ItemCatalog.GetItemDef(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); - foreach (var itemTag in forbiddenTags) { - if (itemDef.ContainsTag(itemTag)) { - validItem = false; - break; + if (DropList.IsValidList(givenList)) { + foreach (var pickupIndex in givenList) { + var validItem = true; + var itemDef = ItemCatalog.GetItemDef(PickupCatalog.GetPickupDef(pickupIndex).itemIndex); + foreach (var itemTag in forbiddenTags) { + if (itemDef.ContainsTag(itemTag)) { + validItem = false; + break; + } + } + if (validItem) { + return true; } - } - if (validItem) { - return true; } } return false; From 0524b65bb7af316c87686745f4b7ee7645cb6fb2 Mon Sep 17 00:00:00 2001 From: Phedg1 Studios Date: Fri, 27 Nov 2020 00:42:27 +1000 Subject: [PATCH 2/4] Added bookmark --- R2API/ItemDrop/DropList.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/R2API/ItemDrop/DropList.cs b/R2API/ItemDrop/DropList.cs index b5690799..f2bc2cd6 100644 --- a/R2API/ItemDrop/DropList.cs +++ b/R2API/ItemDrop/DropList.cs @@ -121,6 +121,7 @@ public void DuplicateDropLists(Run run) { LunarEquipmentDropListOriginal = BackupDropList(run.availableLunarEquipmentDropList); BossDropListOriginal = BackupDropList(run.availableBossDropList); + /* foreach (var bossItem in Catalog.SpecialItems) { var pickupIndex = PickupCatalog.FindPickupIndex(bossItem); From 95753648005d827c5732866c48068ece4825670c Mon Sep 17 00:00:00 2001 From: Phedg1 Studios Date: Wed, 2 Dec 2020 07:51:29 +1000 Subject: [PATCH 3/4] Added ability to have items of any tier in any drop list Added ability to add items of any tier to any drop list. Added hooks and functionality so the artifact of command interfact can display a custom list. Added hooks so interactables would not filter drop lists to limit them to specific tiers. Altered tiers in InteractableCalculator so items in a tier of drop list could be differentiated from items of a tier in any drop list. Added functions to Reflection for getting nested field and method infos. Added function to Reflection log all opcodes for a cursor to help with debugging. Added function to Reflection for getting a generic method info when multiple functions with the same name exist. Added Unity physics module dependency for a hook that required the Collision type. Fixed bug in MonsterItemsAPI that occured when the ItemDropAPI drop lists differed from the MonsterItemsAPI drop lists. --- R2API/ItemDrop/InteractableCalculator.cs | 126 ++++-- R2API/ItemDropAPI.cs | 489 ++++++++++++++++++++++- R2API/MonsterItemsAPI.cs | 4 +- R2API/R2API.csproj | 4 + R2API/Utils/Reflection.cs | 78 ++++ R2API/libs/UnityEngine.PhysicsModule.dll | Bin 0 -> 84992 bytes 6 files changed, 655 insertions(+), 46 deletions(-) create mode 100644 R2API/libs/UnityEngine.PhysicsModule.dll diff --git a/R2API/ItemDrop/InteractableCalculator.cs b/R2API/ItemDrop/InteractableCalculator.cs index ab687c26..576f1f6f 100644 --- a/R2API/ItemDrop/InteractableCalculator.cs +++ b/R2API/ItemDrop/InteractableCalculator.cs @@ -13,17 +13,20 @@ public class InteractableCalculator { "ShrineCleanse" }; public static readonly Dictionary TierConversion = new Dictionary { - { "tier1", ItemTier.Tier1 }, - { "tier2", ItemTier.Tier2 }, - { "tier3", ItemTier.Tier3 }, - { "boss", ItemTier.Boss }, - { "lunar", ItemTier.Lunar } + { "tier1Tier", ItemTier.Tier1 }, + { "tier2Tier", ItemTier.Tier2 }, + { "tier3Tier", ItemTier.Tier3 }, + { "bossTier", ItemTier.Boss }, + { "lunarTier", ItemTier.Lunar } }; private readonly List _subsetChests = new List { "CategoryChestDamage", "CategoryChestHealing", "CategoryChestUtility" }; + + // tier1 REPRESENTS A VALID ITEM IN THE TIER1 DROP LIST + // tier1Tier REPRESENTS A VALID TIER1 ITEM THAT CAN DROP FROM ANY DROP LIST public readonly Dictionary> SubsetTiersPresent = new Dictionary>(); public readonly Dictionary TiersPresent = new Dictionary { { "tier1", false }, @@ -31,6 +34,11 @@ public class InteractableCalculator { { "tier3", false }, { "boss", false }, { "lunar", false }, + { "tier1Tier", false }, + { "tier2Tier", false }, + { "tier3Tier", false }, + { "bossTier", false }, + { "lunarTier", false }, { "equipment", false }, { "lunarEquipment", false }, { "damage", false }, @@ -77,21 +85,21 @@ public class InteractableCalculator { { "equipment", false} }}, { "ShrineCleanse", new Dictionary { - { "lunar", false }, + { "lunarTier", false }, { "pearl", false} }}, { "ShrineRestack", new Dictionary { - { "tier1", false }, - { "tier2", false }, - { "tier3", false }, - { "boss", false }, - { "lunar", false } + { "tier1Tier", false }, + { "tier2Tier", false }, + { "tier3Tier", false }, + { "bossTier", false }, + { "lunarTier", false } }}, { "TripleShopEquipment", new Dictionary { { "equipment", false } }}, { "BrokenEquipmentDrone", new Dictionary { - { "equipment", false } + { "equipmentTier", false } }}, { "Chest1Stealthed", new Dictionary { { "tier1", false }, @@ -102,10 +110,10 @@ public class InteractableCalculator { { "tier3", false } }}, { "Scrapper", new Dictionary { - { "tier1", false }, - { "tier2", false }, - { "tier3", false }, - { "boss", false } + { "tier1Tier", false }, + { "tier2Tier", false }, + { "tier3Tier", false }, + { "bossTier", false } }}, { "Duplicator", new Dictionary { { "tier1", false } @@ -160,33 +168,30 @@ public void CalculateInvalidInteractables(DropList dropList) { } } - foreach (var pickupIndex in dropList.AvailableTier1DropList) { - var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); - if (pickupDef != null) { - if (pickupDef.itemIndex != ItemIndex.None && - !Catalog.ScrapItems.ContainsValue(pickupDef.itemIndex)) { + if (DropList.IsValidList(dropList.AvailableTier1DropList)) { + foreach (var pickupIndex in dropList.AvailableTier1DropList) { + var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); + if (pickupDef != null) { TiersPresent["tier1"] = true; break; } } } - foreach (var pickupIndex in dropList.AvailableTier2DropList) { - var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); - if (pickupDef != null) { - if (pickupDef.itemIndex != ItemIndex.None && - !Catalog.ScrapItems.ContainsValue(pickupDef.itemIndex)) { + if (DropList.IsValidList(dropList.AvailableTier2DropList)) { + foreach (var pickupIndex in dropList.AvailableTier2DropList) { + var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); + if (pickupDef != null) { TiersPresent["tier2"] = true; break; } } } - foreach (var pickupIndex in dropList.AvailableTier3DropList) { - var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); - if (pickupDef != null) { - if (pickupDef.itemIndex != ItemIndex.None && - !Catalog.ScrapItems.ContainsValue(pickupDef.itemIndex)) { + if (DropList.IsValidList(dropList.AvailableTier3DropList)) { + foreach (var pickupIndex in dropList.AvailableTier3DropList) { + var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); + if (pickupDef != null) { TiersPresent["tier3"] = true; break; } @@ -204,22 +209,20 @@ public void CalculateInvalidInteractables(DropList dropList) { } } - foreach (var pickupIndex in dropList.AvailableBossDropList) { - var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); - if (pickupDef != null) { - if (pickupDef.itemIndex != ItemIndex.None && - !Catalog.ScrapItems.ContainsValue(pickupDef.itemIndex)) { + if (DropList.IsValidList(dropList.AvailableBossDropList)) { + foreach (var pickupIndex in dropList.AvailableBossDropList) { + var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); + if (pickupDef != null) { TiersPresent["boss"] = true; break; } } } - foreach (var pickupIndex in dropList.AvailableLunarDropList) { - var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); - if (pickupDef != null) { - if (pickupDef.itemIndex != ItemIndex.None && - !Catalog.ScrapItems.ContainsValue(pickupDef.itemIndex)) { + if (DropList.IsValidList(dropList.AvailableLunarDropList)) { + foreach (var pickupIndex in dropList.AvailableLunarDropList) { + var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); + if (pickupDef != null) { TiersPresent["lunar"] = true; break; } @@ -248,11 +251,11 @@ public void CalculateInvalidInteractables(DropList dropList) { interactableName = "CategoryChestUtility"; } if (_subsetChests.Contains(interactableName)) { - if (ItemCatalog.tier1ItemList.Contains(pickupDef.itemIndex)) { + if (dropList.AvailableTier1DropList.Contains(pickupIndex)) { SubsetTiersPresent[interactableName]["tier1"] = true; - } else if (ItemCatalog.tier2ItemList.Contains(pickupDef.itemIndex)) { + } else if (dropList.AvailableTier2DropList.Contains(pickupIndex)) { SubsetTiersPresent[interactableName]["tier2"] = true; - } else if (ItemCatalog.tier3ItemList.Contains(pickupDef.itemIndex)) { + } else if (dropList.AvailableTier3DropList.Contains(pickupIndex)) { SubsetTiersPresent[interactableName]["tier3"] = true; } } @@ -260,6 +263,41 @@ public void CalculateInvalidInteractables(DropList dropList) { } } } + List> allDropLists = new List>() { + dropList.AvailableTier1DropList, + dropList.AvailableTier2DropList, + dropList.AvailableTier3DropList, + dropList.AvailableBossDropList, + dropList.AvailableLunarDropList, + dropList.AvailableSpecialItems, + dropList.AvailableEquipmentDropList, + dropList.AvailableNormalEquipmentDropList, + dropList.AvailableLunarEquipmentDropList, + dropList.AvailableSpecialEquipment, + }; + foreach (List availableDropList in allDropLists) { + foreach (PickupIndex pickupIndex in availableDropList) { + PickupDef pickupDef = PickupCatalog.GetPickupDef(pickupIndex); + if (pickupDef != null) { + ItemIndex itemIndex = pickupDef.itemIndex; + if (itemIndex != ItemIndex.None) { + ItemTier itemTier = ItemCatalog.GetItemDef(itemIndex).tier; + if (itemTier == ItemTier.Tier1) { + TiersPresent["tier1Tier"] = true; + } else if (itemTier == ItemTier.Tier2) { + TiersPresent["tier2Tier"] = true; + } else if (itemTier == ItemTier.Tier3) { + TiersPresent["tier3Tier"] = true; + } else if (itemTier == ItemTier.Boss) { + TiersPresent["bossTier"] = true; + } else if (itemTier == ItemTier.Lunar) { + TiersPresent["lunarTier"] = true; + } + } + } + } + } + if (DropList.IsValidList(dropList.AvailableNormalEquipmentDropList)) { TiersPresent["equipment"] = true; } diff --git a/R2API/ItemDropAPI.cs b/R2API/ItemDropAPI.cs index 430f99c0..e5003afa 100644 --- a/R2API/ItemDropAPI.cs +++ b/R2API/ItemDropAPI.cs @@ -6,8 +6,11 @@ using R2API.ItemDropAPITools; using R2API.MiscHelpers; using R2API.Utils; +using MonoMod.Cil; +using Mono.Cecil.Cil; using RoR2; using UnityEngine; +using UnityEngine.Networking; namespace R2API { // ReSharper disable once InconsistentNaming @@ -38,6 +41,8 @@ public static bool Loaded { private static readonly DropList PlayerDropList = new DropList(); internal static readonly InteractableCalculator PlayerInteractables = new InteractableCalculator(); + private static bool commandArtifact = false; + private static System.Reflection.MethodInfo rouletteGetEntryIndexForTime = typeof(RouletteChestController).GetMethod("GetEntryIndexForTime", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); public static Dictionary> AdditionalItemsReadOnly => ItemsToAdd.Except(ItemsToRemove).ToDictionary(p => p.Key, p => p.Value); @@ -87,6 +92,22 @@ internal static void SetHooks() { On.RoR2.PickupPickerController.SetOptionsServer += SetOptionsServer; On.RoR2.ArenaMissionController.EndRound += EndRound; On.RoR2.GlobalEventManager.OnCharacterDeath += OnCharacterDeath; + IL.RoR2.ShopTerminalBehavior.GenerateNewPickupServer += GenerateNewPickupServer; + + IL.RoR2.ChestBehavior.RollItem += RollItem; + On.RoR2.ChestBehavior.RollEquipment += RollEquipment; + On.RoR2.ChestBehavior.ItemDrop += ItemDrop; + IL.RoR2.ShrineChanceBehavior.AddShrineStack += AddShrineStack; + On.RoR2.RouletteChestController.GenerateEntriesServer += GenerateEntriesServer; + IL.RoR2.BasicPickupDropTable.GenerateDrop += GenerateDrop; + On.RoR2.RouletteChestController.EjectPickupServer += EjectPickupServer; + IL.RoR2.BossGroup.DropRewards += DropRewards; + IL.RoR2.GlobalEventManager.OnCharacterDeath += OnCharacterDeath; + + IL.RoR2.PickupDropletController.CreatePickupDroplet += CreatePickupDroplet; + On.RoR2.PickupDropletController.OnCollisionEnter += OnCollisionEnter; + On.RoR2.PickupDropletController.CreatePickupDroplet += CreatePickupDroplet; + IL.RoR2.UI.PickupPickerPanel.SetPickupOptions += SetPickupOptions; } [R2APISubmoduleInit(Stage = InitStage.UnsetHooks)] @@ -100,9 +121,27 @@ internal static void UnsetHooks() { On.RoR2.PickupPickerController.SetOptionsServer -= SetOptionsServer; On.RoR2.ArenaMissionController.EndRound -= EndRound; On.RoR2.GlobalEventManager.OnCharacterDeath -= OnCharacterDeath; + IL.RoR2.ShopTerminalBehavior.GenerateNewPickupServer -= GenerateNewPickupServer; + + IL.RoR2.ChestBehavior.RollItem -= RollItem; + On.RoR2.ChestBehavior.RollEquipment -= RollEquipment; + On.RoR2.ChestBehavior.ItemDrop -= ItemDrop; + IL.RoR2.ShrineChanceBehavior.AddShrineStack -= AddShrineStack; + On.RoR2.RouletteChestController.GenerateEntriesServer -= GenerateEntriesServer; + IL.RoR2.BasicPickupDropTable.GenerateDrop -= GenerateDrop; + On.RoR2.RouletteChestController.EjectPickupServer -= EjectPickupServer; + IL.RoR2.BossGroup.DropRewards -= DropRewards; + IL.RoR2.GlobalEventManager.OnCharacterDeath -= OnCharacterDeath; + + IL.RoR2.PickupDropletController.CreatePickupDroplet -= CreatePickupDroplet; + On.RoR2.PickupDropletController.OnCollisionEnter -= OnCollisionEnter; + On.RoR2.PickupDropletController.CreatePickupDroplet -= CreatePickupDroplet; + IL.RoR2.UI.PickupPickerPanel.SetPickupOptions -= SetPickupOptions; } private static void RunOnBuildDropTable(On.RoR2.Run.orig_BuildDropTable orig, Run run) { + commandArtifact = run.GetComponent().IsArtifactEnabled(RoR2Content.Artifacts.commandArtifactDef); + Catalog.PopulateItemCatalog(); orig(run); @@ -130,6 +169,11 @@ public static void ClearEquipmentOperations(Dictionary(AllInteractablesResourcesPath); foreach (var spawnCard in allInteractables) { var interactableName = InteractableCalculator.GetSpawnCardName(spawnCard); @@ -171,6 +215,429 @@ private static void PopulateScene(On.RoR2.SceneDirector.orig_PopulateScene orig, orig(sceneDirector); } + + + + + + + + // RETREIVES THE BACKED UP DROP LISTS TO SET THE COMMAND ARTIFACT MENU OPTIONS + + private static List currentPickupList = new List(); + private static bool uniquePickup = false; + private static Dictionary> pickupDropletCommandArtifactLists = new Dictionary>(); + private static Dictionary pickupDropletCommandArtifactUniquePickup = new Dictionary(); + + private static void CreatePickupDroplet(ILContext ilContext) { + var spawnMethodInfo = typeof(UnityEngine.Networking.NetworkServer).GetMethod("Spawn", new[] { typeof(GameObject) }); + + var cursor = new ILCursor(ilContext); + cursor.GotoNext( + x => x.MatchCall(spawnMethodInfo) + ); + cursor.Emit(OpCodes.Dup); + cursor.EmitDelegate>((dropletGameObject) => { + pickupDropletCommandArtifactLists.Add(dropletGameObject.GetComponent(), currentPickupList); + pickupDropletCommandArtifactUniquePickup.Add(dropletGameObject.GetComponent(), uniquePickup); + }); + } + + private static void OnCollisionEnter(On.RoR2.PickupDropletController.orig_OnCollisionEnter orig, PickupDropletController pickupDropletController, Collision collision) { + if (pickupDropletCommandArtifactLists.ContainsKey(pickupDropletController)) { + currentPickupList = pickupDropletCommandArtifactLists[pickupDropletController]; + uniquePickup = pickupDropletCommandArtifactUniquePickup[pickupDropletController]; + } + orig(pickupDropletController, collision); + } + + private static void CreatePickupDroplet(On.RoR2.PickupDropletController.orig_CreatePickupDroplet orig, PickupIndex pickupIndex, Vector3 position, Vector3 velocity) { + if (commandArtifact) { + pickupIndex = AdjustCommandPickupIndex(currentPickupList, pickupIndex); + } + orig(pickupIndex, position, velocity); + } + + private static void SetPickupOptions(ILContext ilContext) { + var pickupIndexFieldInfo = typeof(PickupPickerController.Option).GetField("pickupIndex", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + var getPickupDefMethodInfo = typeof(PickupCatalog).GetMethod("GetPickupDef", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + + var cursor = new ILCursor(ilContext); + cursor.GotoNext( + x => x.MatchLdarg(1), + x => x.MatchLdcI4(0), + x => x.MatchLdelema("RoR2.PickupPickerController/Option"), + x => x.MatchLdfld(pickupIndexFieldInfo), + x => x.MatchCall(getPickupDefMethodInfo), + x => x.MatchDup() + ); + cursor.Index += 1; + cursor.RemoveRange(3); + cursor.EmitDelegate>((options) => { + List pickupList = new List(); + foreach (PickupPickerController.Option option in options) { + pickupList.Add(option.pickupIndex); + } + return AdjustCommandPickupIndex(pickupList, pickupList[0]); + }); + } + + private static PickupIndex AdjustCommandPickupIndex(List pickupList, PickupIndex givenPickupIndex) { + if (PickupListsEqual(Run.instance.availableTier1DropList, pickupList)) { + return PickupCatalog.FindPickupIndex(ItemIndex.ScrapWhite); + } else if (PickupListsEqual(Run.instance.availableTier2DropList, pickupList)) { + return PickupCatalog.FindPickupIndex(ItemIndex.ScrapGreen); + } else if (PickupListsEqual(Run.instance.availableTier3DropList, pickupList)) { + return PickupCatalog.FindPickupIndex(ItemIndex.ScrapRed); + } else if (PickupListsEqual(Run.instance.availableBossDropList, pickupList)) { + return PickupCatalog.FindPickupIndex(ItemIndex.ScrapYellow); + } else if (PickupListsEqual(Run.instance.availableLunarDropList, pickupList)) { + return PickupCatalog.FindPickupIndex(ItemIndex.LunarDagger); + } else if (PickupListsEqual(Run.instance.availableEquipmentDropList, pickupList)) { + return PickupCatalog.FindPickupIndex(EquipmentIndex.CritOnUse); + } + return givenPickupIndex; + } + + private static bool PickupListsEqual(List listA, List listB) { + if (listA.Count == listB.Count) { + bool listsEqual = true; + for (int listIndex = 0; listIndex < listA.Count; listIndex++) { + if (listA[listIndex] != listB[listIndex]) { + listsEqual = false; + break; + } + } + if (listsEqual) { + return true; + } + } + return false; + } + + // WILL BACKUP UP THE DROP LIST SELECTED BY CHESTS FOR USE WITH THE COMMAND ARTIFACT + + private static Dictionary> chestCommandArtifactLists = new Dictionary>(); + + private static void RollItem(ILContext ilContext) { + var findPickupIndexMethodInfo = typeof(PickupCatalog).GetMethod("FindPickupIndex", new [] { typeof(string) }); + var addMethodInfo = typeof(List).GetMethod("Add", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + + var rollItemAddMethodInfo = Utils.Reflection.GetNestedMethod(typeof(ChestBehavior), "g__Add|1"); + var selectorFieldInfo = Utils.Reflection.GetNestedField(typeof(ChestBehavior), "selector"); + var filterListMethodInfo = typeof(ItemDropAPI).GetMethod("FilterList", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + + var instanceMethodInfo = typeof(Run).GetProperty("instance", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).GetMethod; + var treasureRngFieldInfo = typeof(Run).GetField("treasureRng", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + var nextNormalizedMethodInfo = typeof(Xoroshiro128Plus).GetProperty("nextNormalizedFloat", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetMethod; + var evaluateMethodInfo = typeof(WeightedSelection>).GetMethod("Evaluate", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + + var cursor = new ILCursor(ilContext); + + cursor.GotoNext( + x => x.MatchLdloc(0), + x => x.MatchLdstr("LunarCoin.Coin0"), + x => x.MatchCall(findPickupIndexMethodInfo), + x => x.MatchCallvirt(addMethodInfo), + x => x.MatchDup() + ); + + while (cursor.TryGotoNext(x => x.MatchDup())) { + cursor.Index += 1; + cursor.Emit(OpCodes.Ldfld, selectorFieldInfo); + } + + cursor.Index = 0; + while (cursor.TryGotoNext(x => x.MatchCallvirt(rollItemAddMethodInfo))) { + cursor.Remove(); + cursor.Emit(OpCodes.Callvirt, filterListMethodInfo); + } + + cursor.GotoNext( + x => x.MatchLdfld(selectorFieldInfo), + x => x.MatchCall(instanceMethodInfo), + x => x.MatchLdfld(treasureRngFieldInfo), + x => x.MatchCallvirt(nextNormalizedMethodInfo), + x => x.MatchCallvirt(evaluateMethodInfo), + x => x.MatchStloc(1) + ); + cursor.Index += 6; + cursor.Emit(OpCodes.Ldarg_0); + cursor.Emit(OpCodes.Ldloc_1); + cursor.EmitDelegate>>((chestBehavior, dropList) => { + chestCommandArtifactLists.Add(chestBehavior, dropList); + }); + } + + private static void FilterList(WeightedSelection> selector, List dropList, float dropChance) { + if ((double) dropChance <= 0) { + return; + } + List filteredDropList = new List(); + foreach (PickupIndex pickupIndex in dropList) { + filteredDropList.Add(pickupIndex); + } + selector.AddChoice(filteredDropList, dropChance); + } + + private static void RollEquipment(On.RoR2.ChestBehavior.orig_RollEquipment orig, ChestBehavior chestBehavior) { + chestCommandArtifactLists.Add(chestBehavior, Run.instance.availableEquipmentDropList); + orig(chestBehavior); + } + + private static void ItemDrop(On.RoR2.ChestBehavior.orig_ItemDrop orig, ChestBehavior chestBehavior) { + if (chestCommandArtifactLists.ContainsKey(chestBehavior)) { + currentPickupList = chestCommandArtifactLists[chestBehavior]; + chestCommandArtifactLists.Remove(chestBehavior); + uniquePickup = false; + } + orig(chestBehavior); + } + + //WILL BACKUP UP THE DROP LIST SELECTED BY SHRINES OF CHANCE FOR USE WITH THE COMMAND ARTIFACT + + private static List> shrineChanceDropLists = new List>(); + private static WeightedSelection> shrineChanceWeightedSelection; + + private static void AddShrineStack(ILContext ilContext) { + var rngFieldInfo = typeof(ShrineChanceBehavior).GetField("rng", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var instanceMethodInfo = typeof(Run).GetProperty("instance", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).GetMethod; + var addChoiceMethodInfo = typeof(WeightedSelection).GetMethod("AddChoice", new[] { typeof(PickupIndex), typeof(float) }); + var nextNormalizedMethodInfo = typeof(Xoroshiro128Plus).GetProperty("nextNormalizedFloat", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetMethod; + var evaluateMethodInfo = typeof(WeightedSelection).GetMethod("Evaluate", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + + var cursor = new ILCursor(ilContext); + + cursor.EmitDelegate(() => { + shrineChanceDropLists.Clear(); + shrineChanceWeightedSelection = new WeightedSelection>(8); + shrineChanceDropLists.Add(new List() { PickupIndex.none }); + }); + + while (cursor.TryGotoNext( + x => x.MatchLdarg(0), + x => x.MatchLdfld(rngFieldInfo), + x => x.MatchCall(instanceMethodInfo), + x => x.OpCode == OpCodes.Ldfld, + x => x.OpCode == OpCodes.Callvirt + )) { + cursor.Index += 4; + cursor.Emit(OpCodes.Dup); + cursor.EmitDelegate>>((dropList) => { + shrineChanceDropLists.Add(dropList); + }); + } + + List ldloc = new List() { OpCodes.Ldloc, OpCodes.Ldloc_0, OpCodes.Ldloc_1, OpCodes.Ldloc_2, OpCodes.Ldloc_3, OpCodes.Ldloc_S }; + while (cursor.TryGotoNext( + x => ldloc.Contains(x.OpCode), + x => x.MatchLdarg(0), + x => x.OpCode == OpCodes.Ldfld, + x => x.MatchCallvirt(addChoiceMethodInfo) + )) { + cursor.Index += 3; + cursor.Emit(OpCodes.Dup); + cursor.EmitDelegate>((dropChance) => { + shrineChanceWeightedSelection.AddChoice(shrineChanceDropLists[0], dropChance); + shrineChanceDropLists.RemoveAt(0); + }); + } + + cursor.GotoNext( + x => x.MatchLdarg(0), + x => x.MatchLdfld(rngFieldInfo), + x => x.MatchCallvirt(nextNormalizedMethodInfo), + x => x.MatchCallvirt(evaluateMethodInfo) + ); + cursor.Emit(OpCodes.Pop); + cursor.Index += 3; + cursor.Remove(); + cursor.Emit(OpCodes.Ldarg_0); + cursor.Emit(OpCodes.Ldfld, rngFieldInfo); + cursor.Emit(OpCodes.Ldarg_0); + cursor.EmitDelegate>((treasureNormalizedFloat, rng, shrineChanceBehavior) => { + currentPickupList = shrineChanceWeightedSelection.Evaluate(treasureNormalizedFloat); + uniquePickup = false; + return rng.NextElementUniform(currentPickupList); + }); + } + + //WILL BACKUP UP THE DROP LIST SELECTED BY ROULETTE CHESTS FOR USE WITH THE COMMAND ARTIFACT + //WILL REMOVE DROP LIST FILTERING FOR CHESTS + + private static bool rouletteChestEntriesAdding = false; + private static RouletteChestController currentRouletteChestController; + private static Dictionary>> rouletteCommandArtifactLists = new Dictionary>>(); + + + private static void GenerateEntriesServer(On.RoR2.RouletteChestController.orig_GenerateEntriesServer orig, RouletteChestController rouletteChestController, Run.FixedTimeStamp fixedTimeStamp) { + if (commandArtifact) { + rouletteChestEntriesAdding = true; + currentRouletteChestController = rouletteChestController; + } + orig(rouletteChestController, fixedTimeStamp); + rouletteChestEntriesAdding = false; + } + + private static void GenerateDrop(ILContext ilContext) { + var selectorFieldInfo = typeof(BasicPickupDropTable).GetField("selector", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var nextNormalizedMethodInfo = typeof(Xoroshiro128Plus).GetProperty("nextNormalizedFloat", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetMethod; + var evaluateMethodInfo = typeof(WeightedSelection>).GetMethod("Evaluate", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + + + var cursor = new ILCursor(ilContext); + + cursor.GotoNext( + x => x.MatchLdarg(0), + x => x.MatchLdfld(selectorFieldInfo), + x => x.MatchLdarg(1), + x => x.MatchCallvirt(nextNormalizedMethodInfo), + x => x.MatchCallvirt(evaluateMethodInfo) + ); + cursor.Index += 5; + cursor.Emit(OpCodes.Dup); + cursor.EmitDelegate>>((dropList) => { + if (rouletteChestEntriesAdding) { + if (!rouletteCommandArtifactLists.ContainsKey(currentRouletteChestController)) { + rouletteCommandArtifactLists.Add(currentRouletteChestController, new List>()); + } + rouletteCommandArtifactLists[currentRouletteChestController].Add(dropList); + } + }); + } + + private static void EjectPickupServer(On.RoR2.RouletteChestController.orig_EjectPickupServer orig, RouletteChestController rouletteChestController, PickupIndex pickupIndex) { + if (commandArtifact) { + object entryIndexForTimeObject = rouletteGetEntryIndexForTime.Invoke(rouletteChestController, new object[] { Run.FixedTimeStamp.now }); + currentPickupList = rouletteCommandArtifactLists[currentRouletteChestController][(int)entryIndexForTimeObject]; + uniquePickup = false; + } + orig(rouletteChestController, pickupIndex); + } + + //WILL BACKUP UP THE DROP LIST SELECTED BY BOSSES FOR USE WITH THE COMMAND ARTIFACT + + private static void DropRewards(ILContext ilContext) { + var bossDropsFieldInfo = typeof(BossGroup).GetField("bossDrops", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var countMethodInfo = typeof(List).GetProperty("Count", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetMethod; + + var rngFieldInfo = typeof(BossGroup).GetField("rng", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + + + var selectorFieldInfo = typeof(BasicPickupDropTable).GetField("selector", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + var nextNormalizedMethodInfo = typeof(Xoroshiro128Plus).GetProperty("nextNormalizedFloat", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetMethod; + var evaluateMethodInfo = typeof(WeightedSelection>).GetMethod("Evaluate", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + + + var cursor = new ILCursor(ilContext); + + cursor.GotoNext( + x => x.MatchLdarg(0), + x => x.MatchLdfld(bossDropsFieldInfo), + x => x.MatchCallvirt(countMethodInfo), + x => x.MatchLdcI4(0), + x => x.OpCode == OpCodes.Ble_S + ); + cursor.Emit(OpCodes.Ldloc, 1); + cursor.EmitDelegate>>((dropList) => { + uniquePickup = false; + currentPickupList = dropList; + }); + + cursor.GotoNext( + x => x.MatchLdarg(0), + x => x.MatchLdfld(rngFieldInfo), + x => x.MatchLdarg(0), + x => x.MatchLdfld(bossDropsFieldInfo), + x => x.OpCode == OpCodes.Callvirt, + x => x.MatchStloc(2) + ); + cursor.Index += 5; + cursor.Emit(OpCodes.Dup); + cursor.EmitDelegate>((pickupIndex) => { + if (PlayerDropList.AvailableBossDropList.Contains(pickupIndex)) { + currentPickupList = PlayerDropList.AvailableBossDropList; + } else { + currentPickupList = new List() { pickupIndex }; + uniquePickup = true; + } + }); + } + + //WILL BACKUP UP THE DROP LIST SELECTED BY ELITE MONSTERS FOR USE WITH THE COMMAND ARTIFACT + + private static void OnCharacterDeath(ILContext ilContext) { + var checkRollMethodInfo = typeof(RoR2.Util).GetMethod("CheckRoll", new [] { typeof(float), typeof(RoR2.CharacterMaster)}); + var implicitMethodInfo = typeof(UnityEngine.Object).GetMethod("op_Implicit", new[] { typeof(UnityEngine.Object) }); + var isEliteMethodInfo = typeof(RoR2.CharacterBody).GetProperty("isElite", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetMethod; + + + ILLabel ilLabel = null; + ILCursor cursor = new ILCursor(ilContext); + cursor.GotoNext( + //x => x.MatchLdcR4(0.025f), + x => x.OpCode == OpCodes.Ldc_R4, + x => x.OpCode == OpCodes.Ldloc_S, + x => x.MatchCall(checkRollMethodInfo), + x => x.MatchBrfalse(out ilLabel), + x => x.MatchLdloc(2), + x => x.MatchCall(implicitMethodInfo), + x => x.MatchBrfalse(out ilLabel), + x => x.MatchLdloc(2), + x => x.MatchCallvirt(isEliteMethodInfo), + x => x.MatchBrfalse(out ilLabel) + ); + //cursor.Next.Operand = 100f; + + cursor.Index += 10; + var onCharacterDeathLocalVariables = typeof(RoR2.GlobalEventManager).GetMethod("OnCharacterDeath", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetMethodBody().LocalVariables; + var equipmentIndexIndex = 0; + foreach (System.Reflection.LocalVariableInfo localVariableInfo in onCharacterDeathLocalVariables) { + if (localVariableInfo.LocalType == typeof(EquipmentIndex)) { + equipmentIndexIndex = localVariableInfo.LocalIndex; + break; + } + } + cursor.Emit(OpCodes.Ldloc, equipmentIndexIndex); + cursor.EmitDelegate>((equipmentIndex) => { + currentPickupList = new List() { PickupCatalog.FindPickupIndex(equipmentIndex) }; + uniquePickup = true; + }); + } + + //WILL REMOVE DROP LIST FILTERING FOR SHOP TERMINALS + + private static void GenerateNewPickupServer(ILContext ilContext) { + var dropTableFieldInfo = typeof(ShopTerminalBehavior).GetField("dropTable", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + var instanceMethodInfo = typeof(Run).GetProperty("instance", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).GetMethod; + var treasureRngFieldInfo = typeof(Run).GetField("treasureRng", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + var generateDropMethodInfo = typeof(PickupDropTable).GetMethod("GenerateDrop", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + + ILCursor cursor = new ILCursor(ilContext); + cursor.GotoNext( + x => x.MatchLdarg(0), + x => x.MatchLdloc(1), + x => x.OpCode == OpCodes.Callvirt, + x => x.MatchStloc(0) + ); + cursor.Index += 1; + cursor.Emit(OpCodes.Pop); + cursor.Index += 1; + cursor.Remove(); + cursor.Emit(OpCodes.Ldarg_0); + cursor.EmitDelegate, ShopTerminalBehavior, PickupIndex>>((dropList, shopTerminalBehavior) => { + return Run.instance.treasureRng.NextElementUniform(dropList); + }); + } + + + + + + + + private static void GenerateNewPickupServer(On.RoR2.ShopTerminalBehavior.orig_GenerateNewPickupServer orig, ShopTerminalBehavior shopTerminalBehavior) { var shopList = new List(); if (shopTerminalBehavior.itemTier == ItemTier.Tier1) { @@ -184,6 +651,7 @@ private static void GenerateNewPickupServer(On.RoR2.ShopTerminalBehavior.orig_Ge } else if (shopTerminalBehavior.itemTier == ItemTier.Lunar) { shopList = Run.instance.availableLunarDropList; } + if (shopList.Count > 0 || shopTerminalBehavior.dropTable != null) { orig(shopTerminalBehavior); } else { @@ -270,18 +738,36 @@ private static void DropRewards(On.RoR2.BossGroup.orig_DropRewards orig, BossGro private static void SetOptionsServer(On.RoR2.PickupPickerController.orig_SetOptionsServer orig, PickupPickerController pickupPickerController, PickupPickerController.Option[] options) { var optionsAdjusted = new List(); + if (pickupPickerController.contextString.Contains(ScrapperContextString)) { + foreach (var option in options) { + if (PlayerDropList.AvailableSpecialItems.Contains(PickupCatalog.FindPickupIndex(Catalog.GetScrapIndex(ItemCatalog.GetItemDef(PickupCatalog.GetPickupDef(option.pickupIndex).itemIndex).tier)))) { + optionsAdjusted.Add(option); + } + } + } else if (pickupPickerController.contextString.Contains(CommandCubeContextString)) { + foreach (var pickupIndex in currentPickupList) { + ItemIndex pickupItemIndex = PickupCatalog.GetPickupDef(pickupIndex).itemIndex; + var newOption = new PickupPickerController.Option { + available = true, + pickupIndex = pickupIndex + }; + optionsAdjusted.Add(newOption); + } + } + /* foreach (var option in options) { if (pickupPickerController.contextString.Contains(ScrapperContextString)) { if (PlayerDropList.AvailableSpecialItems.Contains(PickupCatalog.FindPickupIndex(Catalog.GetScrapIndex(ItemCatalog.GetItemDef(PickupCatalog.GetPickupDef(option.pickupIndex).itemIndex).tier)))) { optionsAdjusted.Add(option); } } else { - optionsAdjusted.Add(option); + //optionsAdjusted.Add(option); } } if (pickupPickerController.contextString.Contains(CommandCubeContextString)) { if (options.Length > 0) { optionsAdjusted.Clear(); + var itemIndex = PickupCatalog.GetPickupDef(options[0].pickupIndex).itemIndex; var itemTier = ItemTier.NoTier; @@ -315,6 +801,7 @@ private static void SetOptionsServer(On.RoR2.PickupPickerController.orig_SetOpti } } } + */ options = new PickupPickerController.Option[optionsAdjusted.Count]; for (var optionIndex = 0; optionIndex < optionsAdjusted.Count; optionIndex++) { options[optionIndex] = optionsAdjusted[optionIndex]; diff --git a/R2API/MonsterItemsAPI.cs b/R2API/MonsterItemsAPI.cs index 7ab86579..77ecbf9a 100644 --- a/R2API/MonsterItemsAPI.cs +++ b/R2API/MonsterItemsAPI.cs @@ -6,6 +6,7 @@ using R2API.Utils; using RoR2; using RoR2.Artifacts; +using UnityEngine; namespace R2API { [R2APISubmodule] @@ -258,8 +259,9 @@ private static void ScavengerItemGranterStart(On.RoR2.ScavengerItemGranter.orig_ if (!TierValidScav[ItemTier.Tier3]) { scavengerItemGranter.tier3Types = 0; } - + DropList.SetDropLists(MonsterDropList.AvailableTier1DropList, MonsterDropList.AvailableTier2DropList, MonsterDropList.AvailableTier3DropList, MonsterDropList.AvailableEquipmentDropList); orig(scavengerItemGranter); + DropList.RevertDropLists(); scavengerItemGranter.tier1Types = scavTierTypesBackup[0]; scavengerItemGranter.tier2Types = scavTierTypesBackup[1]; diff --git a/R2API/R2API.csproj b/R2API/R2API.csproj index 11cd8747..a4d3cdde 100644 --- a/R2API/R2API.csproj +++ b/R2API/R2API.csproj @@ -75,6 +75,10 @@ libs\UnityEngine.Networking.dll false + + libs\UnityEngine.PhysicsModule.dll + false + libs\UnityEngine.UI.dll false diff --git a/R2API/Utils/Reflection.cs b/R2API/Utils/Reflection.cs index 8b779b04..c43a4d0a 100644 --- a/R2API/Utils/Reflection.cs +++ b/R2API/Utils/Reflection.cs @@ -5,7 +5,9 @@ using System.Linq; using System.Reflection; using Mono.Cecil.Cil; +using MonoMod.Cil; using MonoMod.Utils; +using UnityEngine; namespace R2API.Utils { public static class Reflection { @@ -923,5 +925,81 @@ public static byte ReadLocalIndex(OpCode opCode, object? operand) { } #endregion + + public static System.Reflection.FieldInfo GetNestedField(Type type, string fieldName) { + var nestedTypes = type.GetNestedTypes((System.Reflection.BindingFlags)(-1)); + foreach (Type nestedType in nestedTypes) { + var fieldInfo = nestedType.GetField(fieldName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + if (fieldInfo != null) { + return fieldInfo; + } + fieldInfo = nestedType.GetField(fieldName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + if (fieldInfo != null) { + return fieldInfo; + } + fieldInfo = nestedType.GetField(fieldName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + if (fieldInfo != null) { + return fieldInfo; + } + fieldInfo = nestedType.GetField(fieldName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (fieldInfo != null) { + return fieldInfo; + } + } + return null; + } + + public static System.Reflection.MethodInfo GetNestedMethod(Type type, string methodName) { + var nestedTypes = type.GetNestedTypes((System.Reflection.BindingFlags)(-1)); + foreach (Type nestedType in nestedTypes) { + var methodInfo = nestedType.GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + if (methodInfo != null) { + return methodInfo; + } + methodInfo = nestedType.GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + if (methodInfo != null) { + return methodInfo; + } + methodInfo = nestedType.GetMethod(methodName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + if (methodInfo != null) { + return methodInfo; + } + methodInfo = nestedType.GetMethod(methodName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + if (methodInfo != null) { + return methodInfo; + } + } + return null; + } + public static void LogCursorOpcodes(ILCursor cursor) { + cursor.Goto(0); + while (cursor.Next != null) { + Debug.Log(cursor.Next.OpCode); + cursor.Index++; + cursor.Goto(cursor.Index); + } + } + + public static MethodInfo GetGenericMethod(Type type, string name, Type[] parameters) { + var classMethods = type.GetMethods((System.Reflection.BindingFlags)(-1)); + foreach (System.Reflection.MethodInfo methodInfo in classMethods) { + if (methodInfo.Name == name) { + System.Reflection.ParameterInfo[] parameterInfos = methodInfo.GetParameters(); + if (parameterInfos.Length == parameters.Length) { + bool parameterMatch = true; + for (int parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++) { + if (parameterInfos[parameterIndex].ParameterType.Name != parameters[parameterIndex].Name) { + parameterMatch = false; + break; + } + } + if (parameterMatch) { + return methodInfo; + } + } + } + } + return null; + } } } diff --git a/R2API/libs/UnityEngine.PhysicsModule.dll b/R2API/libs/UnityEngine.PhysicsModule.dll new file mode 100644 index 0000000000000000000000000000000000000000..4839a12c8942c3bbc4cacc063e47db29c7ca63c5 GIT binary patch literal 84992 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 Date: Thu, 3 Dec 2020 07:18:01 +1000 Subject: [PATCH 4/4] Added PR requests. Added proper binding flags to my contributions in Reflection. Steamlined my contributions in Reflection. Enumerated the drop types in InteractableCalculator. Removed some checks against the Run drop lists and increased relaince on InteractableCalculator TiersPresent. Expanded IsValidList so it returns false when given an empty list. --- R2API/ItemDrop/DropList.cs | 2 +- R2API/ItemDrop/DropOdds.cs | 28 +-- R2API/ItemDrop/InteractableCalculator.cs | 259 ++++++++++++----------- R2API/ItemDropAPI.cs | 36 ++-- R2API/Utils/Reflection.cs | 37 +--- 5 files changed, 178 insertions(+), 184 deletions(-) diff --git a/R2API/ItemDrop/DropList.cs b/R2API/ItemDrop/DropList.cs index f2bc2cd6..b37915dd 100644 --- a/R2API/ItemDrop/DropList.cs +++ b/R2API/ItemDrop/DropList.cs @@ -355,7 +355,7 @@ public static List ToPickupIndices(IEnumerable indi } public static bool IsValidList(IEnumerable dropList) { - if (dropList.Count() == 1 && dropList.Contains(PickupIndex.none)) { + if (dropList.Count() == 0 || (dropList.Count() == 1 && dropList.Contains(PickupIndex.none))) { return false; } return true; diff --git a/R2API/ItemDrop/DropOdds.cs b/R2API/ItemDrop/DropOdds.cs index 5b3a04f5..4555c3fb 100644 --- a/R2API/ItemDrop/DropOdds.cs +++ b/R2API/ItemDrop/DropOdds.cs @@ -34,23 +34,23 @@ public static void UpdateChestTierOdds(SpawnCard spawnCard, string interactableN chestBehavior.tier3Chance = ChestTierOdds[interactableName][2]; } if (ItemDropAPI.PlayerInteractables.SubsetTiersPresent.ContainsKey(interactableName)) { - if (!ItemDropAPI.PlayerInteractables.SubsetTiersPresent[interactableName]["tier1"]) { + if (!ItemDropAPI.PlayerInteractables.SubsetTiersPresent[interactableName][InteractableCalculator.DropType.tier1]) { chestBehavior.tier1Chance = 0; } - if (!ItemDropAPI.PlayerInteractables.SubsetTiersPresent[interactableName]["tier2"]) { + if (!ItemDropAPI.PlayerInteractables.SubsetTiersPresent[interactableName][InteractableCalculator.DropType.tier2]) { chestBehavior.tier2Chance = 0; } - if (!ItemDropAPI.PlayerInteractables.SubsetTiersPresent[interactableName]["tier3"]) { + if (!ItemDropAPI.PlayerInteractables.SubsetTiersPresent[interactableName][InteractableCalculator.DropType.tier3]) { chestBehavior.tier3Chance = 0; } } else { - if (!ItemDropAPI.PlayerInteractables.TiersPresent["tier1"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier1]) { chestBehavior.tier1Chance = 0; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["tier2"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier2]) { chestBehavior.tier2Chance = 0; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["tier3"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier3]) { chestBehavior.tier3Chance = 0; } } @@ -81,16 +81,16 @@ public static void UpdateShrineTierOdds(DirectorCard directorCard, string intera shrineBehavior.equipmentWeight = ShrineTierOdds[interactableName][3]; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["tier1"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier1]) { shrineBehavior.tier1Weight = 0; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["tier2"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier2]) { shrineBehavior.tier2Weight = 0; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["tier3"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier3]) { shrineBehavior.tier3Weight = 0; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["equipment"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.equipment]) { shrineBehavior.equipmentWeight = 0; } } @@ -118,16 +118,16 @@ public static void UpdateDropTableTierOdds(SpawnCard spawnCard, string interacta dropTable.tier3Weight = DropTableTierOdds[interactableName][2]; dropTable.equipmentWeight = DropTableTierOdds[interactableName][3]; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["tier1"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier1]) { dropTable.tier1Weight = 0; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["tier2"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier2]) { dropTable.tier2Weight = 0; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["tier3"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier3]) { dropTable.tier3Weight = 0; } - if (!ItemDropAPI.PlayerInteractables.TiersPresent["equipment"]) { + if (!ItemDropAPI.PlayerInteractables.TiersPresent[InteractableCalculator.DropType.equipment]) { dropTable.equipmentWeight = 0; } diff --git a/R2API/ItemDrop/InteractableCalculator.cs b/R2API/ItemDrop/InteractableCalculator.cs index 576f1f6f..95a8f0e3 100644 --- a/R2API/ItemDrop/InteractableCalculator.cs +++ b/R2API/ItemDrop/InteractableCalculator.cs @@ -9,137 +9,148 @@ public class InteractableCalculator { public readonly List InvalidInteractables = new List(); - public static readonly List AllTiersMustBePresent = new List { - "ShrineCleanse" - }; - public static readonly Dictionary TierConversion = new Dictionary { - { "tier1Tier", ItemTier.Tier1 }, - { "tier2Tier", ItemTier.Tier2 }, - { "tier3Tier", ItemTier.Tier3 }, - { "bossTier", ItemTier.Boss }, - { "lunarTier", ItemTier.Lunar } + + + // tier1 REPRESENTS A VALID ITEM IN THE TIER1 DROP LIST + // tier1Tier REPRESENTS A VALID TIER1 ITEM THAT CAN DROP FROM ANY DROP LIST + + public enum DropType { + tier1, + tier2, + tier3, + boss, + lunar, + tier1Tier, + tier2Tier, + tier3Tier, + bossTier, + lunarTier, + equipment, + lunarEquipment, + equipmentTier, + damage, + healing, + utility, + pearl, + drone, + none + } + + public static readonly Dictionary TierConversion = new Dictionary { + { DropType.tier1Tier, ItemTier.Tier1 }, + { DropType.tier2Tier, ItemTier.Tier2 }, + { DropType.tier3Tier, ItemTier.Tier3 }, + { DropType.bossTier, ItemTier.Boss }, + { DropType.lunarTier, ItemTier.Lunar } }; + + public readonly Dictionary TiersPresent = new Dictionary(); + + public readonly Dictionary> SubsetTiersPresent = new Dictionary>(); + private readonly List _subsetChests = new List { "CategoryChestDamage", "CategoryChestHealing", "CategoryChestUtility" }; - // tier1 REPRESENTS A VALID ITEM IN THE TIER1 DROP LIST - // tier1Tier REPRESENTS A VALID TIER1 ITEM THAT CAN DROP FROM ANY DROP LIST - public readonly Dictionary> SubsetTiersPresent = new Dictionary>(); - public readonly Dictionary TiersPresent = new Dictionary { - { "tier1", false }, - { "tier2", false }, - { "tier3", false }, - { "boss", false }, - { "lunar", false }, - { "tier1Tier", false }, - { "tier2Tier", false }, - { "tier3Tier", false }, - { "bossTier", false }, - { "lunarTier", false }, - { "equipment", false }, - { "lunarEquipment", false }, - { "damage", false }, - { "healing", false }, - { "utility", false }, - { "pearl", false }, - { "drone", false } - }; - public readonly Dictionary> InteractablesTiers = new Dictionary> { - { "Chest1", new Dictionary { - { "tier1", false } + public readonly Dictionary> InteractablesTiers = new Dictionary> { + { "Chest1", new Dictionary { + { DropType.tier1, false } //{ "tier2", false }, //{ "tier3", false }, }}, - { "Chest2", new Dictionary { - { "tier2", false } + { "Chest2", new Dictionary { + { DropType.tier2, false } //{ "tier3", false }, }}, - { "EquipmentBarrel", new Dictionary { - { "equipment", false } + { "EquipmentBarrel", new Dictionary { + { DropType.equipment, false } }}, - { "TripleShop", new Dictionary { - { "tier1", false } + { "TripleShop", new Dictionary { + { DropType.tier1, false } }}, - { "LunarChest", new Dictionary { - { "lunar", false } + { "LunarChest", new Dictionary { + { DropType.lunar, false } }}, - { "TripleShopLarge", new Dictionary { - { "tier2", false } + { "TripleShopLarge", new Dictionary { + { DropType.tier2, false } }}, - { "CategoryChestDamage", new Dictionary { - { "damage", false } + { "CategoryChestDamage", new Dictionary { + { DropType.damage, false } }}, - { "CategoryChestHealing", new Dictionary { - { "healing", false } + { "CategoryChestHealing", new Dictionary { + { DropType.healing, false } }}, - { "CategoryChestUtility", new Dictionary { - { "utility", false } + { "CategoryChestUtility", new Dictionary { + { DropType.utility, false } }}, - { "ShrineChance", new Dictionary { - { "tier1", false }, - { "tier2", false }, - { "tier3", false }, - { "equipment", false} + { "ShrineChance", new Dictionary { + { DropType.tier1, false }, + { DropType.tier2, false }, + { DropType.tier3, false }, + { DropType.equipment, false} }}, - { "ShrineCleanse", new Dictionary { - { "lunarTier", false }, - { "pearl", false} + { "ShrineCleanse", new Dictionary { + { DropType.lunarTier, false }, + { DropType.pearl, false} }}, - { "ShrineRestack", new Dictionary { - { "tier1Tier", false }, - { "tier2Tier", false }, - { "tier3Tier", false }, - { "bossTier", false }, - { "lunarTier", false } + { "ShrineRestack", new Dictionary { + { DropType.tier1Tier, false }, + { DropType.tier2Tier, false }, + { DropType.tier3Tier, false }, + { DropType.bossTier, false }, + { DropType.lunarTier, false } }}, - { "TripleShopEquipment", new Dictionary { - { "equipment", false } + { "TripleShopEquipment", new Dictionary { + { DropType.equipment, false } }}, - { "BrokenEquipmentDrone", new Dictionary { - { "equipmentTier", false } + { "BrokenEquipmentDrone", new Dictionary { + { DropType.equipmentTier, false } }}, - { "Chest1Stealthed", new Dictionary { - { "tier1", false }, - { "tier2", false }, - { "tier3", false } + { "Chest1Stealthed", new Dictionary { + { DropType.tier1, false }, + { DropType.tier2, false }, + { DropType.tier3, false } }}, - { "GoldChest", new Dictionary { - { "tier3", false } + { "GoldChest", new Dictionary { + { DropType.tier3, false } }}, - { "Scrapper", new Dictionary { - { "tier1Tier", false }, - { "tier2Tier", false }, - { "tier3Tier", false }, - { "bossTier", false } + { "Scrapper", new Dictionary { + { DropType.tier1Tier, false }, + { DropType.tier2Tier, false }, + { DropType.tier3Tier, false }, + { DropType.bossTier, false } }}, - { "Duplicator", new Dictionary { - { "tier1", false } + { "Duplicator", new Dictionary { + { DropType.tier1, false } }}, - { "DuplicatorLarge", new Dictionary { - { "tier2", false } + { "DuplicatorLarge", new Dictionary { + { DropType.tier2, false } }}, - { "DuplicatorMilitary", new Dictionary { - { "tier3", false } + { "DuplicatorMilitary", new Dictionary { + { DropType.tier3, false } }}, - { "DuplicatorWild", new Dictionary { - { "boss", false } + { "DuplicatorWild", new Dictionary { + { DropType.boss, false } }}, - { "ScavBackpack", new Dictionary { - { "tier1", false }, - { "tier2", false }, - { "tier3", false } + { "ScavBackpack", new Dictionary { + { DropType.tier1, false }, + { DropType.tier2, false }, + { DropType.tier3, false } }}, - { "CasinoChest", new Dictionary { - { "tier1", false }, - { "tier2", false }, - { "tier3", false }, - { "equipment", false } + { "CasinoChest", new Dictionary { + { DropType.tier1, false }, + { DropType.tier2, false }, + { DropType.tier3, false }, + { DropType.equipment, false } }} }; + public static readonly List AllTiersMustBePresent = new List { + "ShrineCleanse" + }; + public static string GetSpawnCardName(SpawnCard givenSpawnCard) { return givenSpawnCard.name.Substring(PrefixLength, givenSpawnCard.name.Length - PrefixLength); } @@ -155,16 +166,16 @@ public static void AddItemsToList(List dst, IEnumerable()); - foreach (var tier in tiersPresentKeys) { - SubsetTiersPresent[subsetChest].Add(tier, false); + SubsetTiersPresent.Add(subsetChest, new Dictionary()); + foreach (DropType dropType in System.Enum.GetValues(typeof(DropType))) { + SubsetTiersPresent[subsetChest].Add(dropType, false); } } @@ -172,7 +183,7 @@ public void CalculateInvalidInteractables(DropList dropList) { foreach (var pickupIndex in dropList.AvailableTier1DropList) { var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef != null) { - TiersPresent["tier1"] = true; + TiersPresent[DropType.tier1] = true; break; } } @@ -182,7 +193,7 @@ public void CalculateInvalidInteractables(DropList dropList) { foreach (var pickupIndex in dropList.AvailableTier2DropList) { var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef != null) { - TiersPresent["tier2"] = true; + TiersPresent[DropType.tier2] = true; break; } } @@ -192,7 +203,7 @@ public void CalculateInvalidInteractables(DropList dropList) { foreach (var pickupIndex in dropList.AvailableTier3DropList) { var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef != null) { - TiersPresent["tier3"] = true; + TiersPresent[DropType.tier3] = true; break; } } @@ -203,7 +214,7 @@ public void CalculateInvalidInteractables(DropList dropList) { if (pickupDef != null) { if (pickupDef.itemIndex != ItemIndex.None && Catalog.Pearls.Contains(pickupDef.itemIndex)) { - TiersPresent["pearl"] = true; + TiersPresent[DropType.pearl] = true; break; } } @@ -213,7 +224,7 @@ public void CalculateInvalidInteractables(DropList dropList) { foreach (var pickupIndex in dropList.AvailableBossDropList) { var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef != null) { - TiersPresent["boss"] = true; + TiersPresent[DropType.boss] = true; break; } } @@ -223,7 +234,7 @@ public void CalculateInvalidInteractables(DropList dropList) { foreach (var pickupIndex in dropList.AvailableLunarDropList) { var pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if (pickupDef != null) { - TiersPresent["lunar"] = true; + TiersPresent[DropType.lunar] = true; break; } } @@ -241,22 +252,22 @@ public void CalculateInvalidInteractables(DropList dropList) { foreach (var itemTag in itemDef.tags) { var interactableName = ""; if (itemTag == ItemTag.Damage) { - TiersPresent["damage"] = true; + TiersPresent[DropType.damage] = true; interactableName = "CategoryChestDamage"; } else if (itemTag == ItemTag.Healing) { - TiersPresent["healing"] = true; + TiersPresent[DropType.healing] = true; interactableName = "CategoryChestHealing"; } else if (itemTag == ItemTag.Utility) { - TiersPresent["utility"] = true; + TiersPresent[DropType.utility] = true; interactableName = "CategoryChestUtility"; } if (_subsetChests.Contains(interactableName)) { if (dropList.AvailableTier1DropList.Contains(pickupIndex)) { - SubsetTiersPresent[interactableName]["tier1"] = true; + SubsetTiersPresent[interactableName][DropType.tier1] = true; } else if (dropList.AvailableTier2DropList.Contains(pickupIndex)) { - SubsetTiersPresent[interactableName]["tier2"] = true; + SubsetTiersPresent[interactableName][DropType.tier2] = true; } else if (dropList.AvailableTier3DropList.Contains(pickupIndex)) { - SubsetTiersPresent[interactableName]["tier3"] = true; + SubsetTiersPresent[interactableName][DropType.tier3] = true; } } } @@ -283,26 +294,30 @@ public void CalculateInvalidInteractables(DropList dropList) { if (itemIndex != ItemIndex.None) { ItemTier itemTier = ItemCatalog.GetItemDef(itemIndex).tier; if (itemTier == ItemTier.Tier1) { - TiersPresent["tier1Tier"] = true; + TiersPresent[DropType.tier1Tier] = true; } else if (itemTier == ItemTier.Tier2) { - TiersPresent["tier2Tier"] = true; + TiersPresent[DropType.tier2Tier] = true; } else if (itemTier == ItemTier.Tier3) { - TiersPresent["tier3Tier"] = true; + TiersPresent[DropType.tier3Tier] = true; } else if (itemTier == ItemTier.Boss) { - TiersPresent["bossTier"] = true; + TiersPresent[DropType.bossTier] = true; } else if (itemTier == ItemTier.Lunar) { - TiersPresent["lunarTier"] = true; + TiersPresent[DropType.lunarTier] = true; } } + EquipmentIndex equipmentIndex = pickupDef.equipmentIndex; + if (equipmentIndex != EquipmentIndex.None) { + TiersPresent[DropType.equipmentTier] = true; + } } } } if (DropList.IsValidList(dropList.AvailableNormalEquipmentDropList)) { - TiersPresent["equipment"] = true; + TiersPresent[DropType.equipment] = true; } if (DropList.IsValidList(dropList.AvailableLunarEquipmentDropList)) { - TiersPresent["lunar"] = true; + TiersPresent[DropType.lunar] = true; } var interactableTypeKeys = InteractablesTiers.Keys.ToList(); foreach (var interactableType in interactableTypeKeys) { @@ -321,7 +336,7 @@ public void CalculateInvalidInteractables(DropList dropList) { } } var scrapTierKeys = InteractablesTiers["Scrapper"].Keys.ToList(); - foreach (string tier in scrapTierKeys) { + foreach (DropType tier in scrapTierKeys) { if (InteractablesTiers["Scrapper"][tier]) { if (Catalog.ScrapItems.ContainsKey(TierConversion[tier])) { if (!dropList.AvailableSpecialItems.Contains(PickupCatalog.FindPickupIndex(Catalog.ScrapItems[TierConversion[tier]]))) { diff --git a/R2API/ItemDropAPI.cs b/R2API/ItemDropAPI.cs index e5003afa..8bef86f5 100644 --- a/R2API/ItemDropAPI.cs +++ b/R2API/ItemDropAPI.cs @@ -639,20 +639,20 @@ private static void GenerateNewPickupServer(ILContext ilContext) { private static void GenerateNewPickupServer(On.RoR2.ShopTerminalBehavior.orig_GenerateNewPickupServer orig, ShopTerminalBehavior shopTerminalBehavior) { - var shopList = new List(); + var dropType = InteractableCalculator.DropType.none; if (shopTerminalBehavior.itemTier == ItemTier.Tier1) { - shopList = Run.instance.availableTier1DropList; + dropType = InteractableCalculator.DropType.tier1; } else if (shopTerminalBehavior.itemTier == ItemTier.Tier2) { - shopList = Run.instance.availableTier2DropList; + dropType = InteractableCalculator.DropType.tier2; } else if (shopTerminalBehavior.itemTier == ItemTier.Tier3) { - shopList = Run.instance.availableTier3DropList; + dropType = InteractableCalculator.DropType.tier3; } else if (shopTerminalBehavior.itemTier == ItemTier.Boss) { - shopList = Run.instance.availableBossDropList; + dropType = InteractableCalculator.DropType.boss; } else if (shopTerminalBehavior.itemTier == ItemTier.Lunar) { - shopList = Run.instance.availableLunarDropList; + dropType = InteractableCalculator.DropType.lunar; } - if (shopList.Count > 0 || shopTerminalBehavior.dropTable != null) { + if (PlayerInteractables.TiersPresent[dropType] || shopTerminalBehavior.dropTable != null) { orig(shopTerminalBehavior); } else { shopTerminalBehavior.SetNoPickup(); @@ -674,19 +674,19 @@ private static GameObject CheckForInvalidInteractables(On.RoR2.DirectorCore.orig private static void FixShrineBehaviour(On.RoR2.ShrineChanceBehavior.orig_AddShrineStack orig, ShrineChanceBehavior shrineChangeBehavior, Interactor interactor) { var tier1Adjusted = PlayerDropList.AvailableTier1DropList; - if (tier1Adjusted.Count == 0) { + if (!PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier1]) { tier1Adjusted = DropList.Tier1DropListOriginal; } var tier2Adjusted = PlayerDropList.AvailableTier2DropList; - if (tier2Adjusted.Count == 0) { + if (!PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier2]) { tier2Adjusted = DropList.Tier2DropListOriginal; } var tier3Adjusted = PlayerDropList.AvailableTier3DropList; - if (tier3Adjusted.Count == 0) { + if (!PlayerInteractables.TiersPresent[InteractableCalculator.DropType.tier3]) { tier3Adjusted = DropList.Tier3DropListOriginal; } var equipmentAdjusted = PlayerDropList.AvailableEquipmentDropList; - if (equipmentAdjusted.Count == 0) { + if (!PlayerInteractables.TiersPresent[InteractableCalculator.DropType.equipment]) { equipmentAdjusted = DropList.EquipmentDropListOriginal; } @@ -710,11 +710,11 @@ private static void DropRewards(On.RoR2.BossGroup.orig_DropRewards orig, BossGro } } - var dropList = Run.instance.availableTier2DropList; + var dropType = InteractableCalculator.DropType.tier2; if (bossGroup.forceTier3Reward) { - dropList = Run.instance.availableTier3DropList; + dropType = InteractableCalculator.DropType.tier3; } - bool normalListValid = DropList.IsValidList(dropList); + bool normalListValid = PlayerInteractables.TiersPresent[dropType]; if (normalListValid || bossDropsAdjusted.Count != 0) { var bossDropChanceOld = bossGroup.bossDropChance; @@ -810,14 +810,14 @@ private static void SetOptionsServer(On.RoR2.PickupPickerController.orig_SetOpti } private static void EndRound(On.RoR2.ArenaMissionController.orig_EndRound orig, ArenaMissionController arenaMissionController) { - var list = Run.instance.availableTier1DropList; + var dropType = InteractableCalculator.DropType.tier1; if (arenaMissionController.currentRound > 4) { - list = Run.instance.availableTier2DropList; + dropType = InteractableCalculator.DropType.tier2; } if (arenaMissionController.currentRound == arenaMissionController.totalRoundsMax) { - list = Run.instance.availableTier3DropList; + dropType = InteractableCalculator.DropType.tier3; } - if (list.Count == 0) { + if (PlayerInteractables.TiersPresent[dropType]) { var rewardSpawnPositionOld = arenaMissionController.rewardSpawnPosition; arenaMissionController.rewardSpawnPosition = null; orig(arenaMissionController); diff --git a/R2API/Utils/Reflection.cs b/R2API/Utils/Reflection.cs index c43a4d0a..05c9daf6 100644 --- a/R2API/Utils/Reflection.cs +++ b/R2API/Utils/Reflection.cs @@ -927,21 +927,9 @@ public static byte ReadLocalIndex(OpCode opCode, object? operand) { #endregion public static System.Reflection.FieldInfo GetNestedField(Type type, string fieldName) { - var nestedTypes = type.GetNestedTypes((System.Reflection.BindingFlags)(-1)); + var nestedTypes = type.GetNestedTypes(AllFlags); foreach (Type nestedType in nestedTypes) { - var fieldInfo = nestedType.GetField(fieldName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - if (fieldInfo != null) { - return fieldInfo; - } - fieldInfo = nestedType.GetField(fieldName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); - if (fieldInfo != null) { - return fieldInfo; - } - fieldInfo = nestedType.GetField(fieldName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); - if (fieldInfo != null) { - return fieldInfo; - } - fieldInfo = nestedType.GetField(fieldName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + var fieldInfo = nestedType.GetField(fieldName, AllFlags); if (fieldInfo != null) { return fieldInfo; } @@ -950,38 +938,29 @@ public static System.Reflection.FieldInfo GetNestedField(Type type, string field } public static System.Reflection.MethodInfo GetNestedMethod(Type type, string methodName) { - var nestedTypes = type.GetNestedTypes((System.Reflection.BindingFlags)(-1)); + var nestedTypes = type.GetNestedTypes(AllFlags); foreach (Type nestedType in nestedTypes) { - var methodInfo = nestedType.GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - if (methodInfo != null) { - return methodInfo; - } - methodInfo = nestedType.GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); - if (methodInfo != null) { - return methodInfo; - } - methodInfo = nestedType.GetMethod(methodName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); - if (methodInfo != null) { - return methodInfo; - } - methodInfo = nestedType.GetMethod(methodName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + var methodInfo = nestedType.GetMethod(methodName, AllFlags); if (methodInfo != null) { return methodInfo; } } return null; } + public static void LogCursorOpcodes(ILCursor cursor) { + int indexOld = cursor.Index; cursor.Goto(0); while (cursor.Next != null) { Debug.Log(cursor.Next.OpCode); cursor.Index++; cursor.Goto(cursor.Index); } + cursor.Index = indexOld; } public static MethodInfo GetGenericMethod(Type type, string name, Type[] parameters) { - var classMethods = type.GetMethods((System.Reflection.BindingFlags)(-1)); + var classMethods = type.GetMethods(AllFlags); foreach (System.Reflection.MethodInfo methodInfo in classMethods) { if (methodInfo.Name == name) { System.Reflection.ParameterInfo[] parameterInfos = methodInfo.GetParameters();