Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of KarmaWarnings v1.0.0
plugins/KarmaWarnings/KarmaWarnings.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using EntityStates.LunarTeleporter; using Microsoft.CodeAnalysis; using RiskOfOptions; using RiskOfOptions.Options; using RoR2; using RoR2.Karma; using RoR2.UI; using TMPro; using UnityEngine; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("KarmaWarnings.Tests")] [assembly: IgnoresAccessChecksTo("RoR2")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("KarmaWarnings")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+e3928c5ede233bb3606c63267034417245f0a61a")] [assembly: AssemblyProduct("KarmaWarnings")] [assembly: AssemblyTitle("KarmaWarnings")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace KarmaWarnings { internal sealed class AimedTarget { public GameObject Root; public GenericInteraction Generic; public PurchaseInteraction Purchase; public SceneExitController Exit; public ShrineCatastropheBehavior Descent; public HalcyoniteShrineInteractable Halcyon; public GenericPickupController Pickup; public ShopTerminalBehavior Shop; public TeleporterInteraction Teleporter; public NetworkIdentity Identity; private EntityStateMachine[] teleporterMachines = Array.Empty<EntityStateMachine>(); public string ContextToken { get { if (!Object.op_Implicit((Object)(object)Generic)) { if (!Object.op_Implicit((Object)(object)Purchase)) { return null; } return Purchase.contextToken; } return Generic.contextToken; } } public bool IsTeleporter { get { if (Object.op_Implicit((Object)(object)Teleporter)) { return (Object)(object)Root == (Object)(object)((Component)Teleporter).gameObject; } return false; } } public bool AimedAtMoon { get { EntityStateMachine[] array = teleporterMachines; foreach (EntityStateMachine val in array) { if (Object.op_Implicit((Object)(object)val) && val.state is LunarTeleporterBaseState && !(val.state is Idle)) { return true; } } return false; } } public void Refresh(GameObject aimed) { if (!((Object)(object)aimed == (Object)(object)Root)) { Root = aimed; Generic = Find<GenericInteraction>(aimed); Purchase = Find<PurchaseInteraction>(aimed); Exit = Find<SceneExitController>(aimed); Descent = Find<ShrineCatastropheBehavior>(aimed); Halcyon = Find<HalcyoniteShrineInteractable>(aimed); Pickup = Find<GenericPickupController>(aimed); Shop = Find<ShopTerminalBehavior>(aimed); Teleporter = Find<TeleporterInteraction>(aimed); Identity = Find<NetworkIdentity>(aimed); teleporterMachines = (IsTeleporter ? ((Component)Teleporter).GetComponentsInChildren<EntityStateMachine>() : Array.Empty<EntityStateMachine>()); } } private static T Find<T>(GameObject root) where T : Component { if (!Object.op_Implicit((Object)(object)root)) { return default(T); } T val = root.GetComponent<T>(); if (!Object.op_Implicit((Object)(object)val)) { val = root.GetComponentInChildren<T>(); } if (!Object.op_Implicit((Object)(object)val)) { val = root.GetComponentInParent<T>(); } return val; } } internal sealed class CombatDetector : IWarningDetector { private const float QuickKillChargeFraction = 0.1f; private const float BossHealthFractionToWarn = 0.5f; private const float NewtKickHealthFraction = 0.99f; private const float SpexKickHealthFraction = 0.9f; private const float AimDistance = 80f; private const float AimToleranceDegrees = 4f; private static readonly string[] BossIds = new string[1] { "KillBossQuickly" }; private static readonly string[] NewtIds = new string[1] { "KickedOutOfBazaar" }; private static readonly string[] SpexIds = new string[1] { "KickedOutOfCompExchange" }; private static readonly string[] LemurianIds = new string[1] { "KilledElementalLemurians" }; private static readonly string[] AwuIds = new string[1] { "SuperRoboBallBossBeaten" }; private BodyIndex newtIndex = (BodyIndex)(-1); private BodyIndex awuIndex = (BodyIndex)(-1); private BodyIndex eggIndex = (BodyIndex)(-1); private readonly Dictionary<CharacterBody, bool> elementalByBody = new Dictionary<CharacterBody, bool>(); private int elementalSceneHandle = -1; public bool TryGetWarning(WarningContext context, out KarmaWarning warning) { switch (context.SceneName) { case "bazaar": return Newt(context, out warning); case "computationalexchange": return Spex(context, out warning); case "goolake": if (ElementalLemurians(context, out warning)) { return true; } break; case "shipgraveyard": if (AlloyWorshipUnit(context, out warning)) { return true; } break; } return TeleporterBoss(context, out warning); } private static bool TeleporterBoss(WarningContext context, out KarmaWarning warning) { warning = default(KarmaWarning); TeleporterInteraction instance = TeleporterInteraction.instance; if (!Object.op_Implicit((Object)(object)instance) || instance.isCharged || !Object.op_Implicit((Object)(object)instance.holdoutZoneController) || !Object.op_Implicit((Object)(object)instance.bossGroup)) { return false; } BossGroup bossGroup = instance.bossGroup; if (!Object.op_Implicit((Object)(object)bossGroup.combatSquad) || bossGroup.combatSquad.memberCount == 0 || bossGroup.totalMaxObservedMaxHealth <= 0f) { return false; } if (instance.chargeFraction > 0.1f) { return false; } if (bossGroup.totalObservedHealth / bossGroup.totalMaxObservedMaxHealth > 0.5f) { return false; } return context.TryMake("when you kill the boss under 10% charge", 80, BossIds, includeLoop: false, out warning); } private bool Newt(WarningContext context, out KarmaWarning warning) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) warning = default(KarmaWarning); CharacterBody val = FindBody(Lookup(ref newtIndex, "ShopkeeperBody")); if (!Object.op_Implicit((Object)(object)val)) { return false; } float healthFraction = val.healthComponent.healthFraction; if (healthFraction <= 0.99f || (healthFraction >= 1f && !IsAimingAt(context.Body, val))) { return false; } return context.TryMake("when the Newt kicks you out", 80, NewtIds, includeLoop: false, out warning); } private static bool Spex(WarningContext context, out KarmaWarning warning) { warning = default(KarmaWarning); ComputationalExchangeController instance = ComputationalExchangeController.instance; if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)instance.solusVendorBody) || !instance.isAwake) { return false; } CharacterBody solusVendorBody = instance.solusVendorBody; float healthFraction = solusVendorBody.healthComponent.healthFraction; if (healthFraction <= 0.9f || (healthFraction >= 1f && !IsAimingAt(context.Body, solusVendorBody))) { return false; } return context.TryMake("when SPEX kicks you out", 80, SpexIds, includeLoop: false, out warning); } private bool ElementalLemurians(WarningContext context, out KarmaWarning warning) { warning = default(KarmaWarning); if (elementalSceneHandle != context.SceneHandle) { elementalSceneHandle = context.SceneHandle; elementalByBody.Clear(); } if (!IsAimingAtElementalLemurian(context.Body)) { return false; } return context.TryMake("when you kill both elemental Elder Lemurians", 80, LemurianIds, includeLoop: false, out warning); } private bool IsAimingAtElementalLemurian(CharacterBody player) { ReadOnlyCollection<CharacterBody> readOnlyInstancesList = CharacterBody.readOnlyInstancesList; for (int i = 0; i < readOnlyInstancesList.Count; i++) { if (Object.op_Implicit((Object)(object)readOnlyInstancesList[i]) && IsElementalLemurian(readOnlyInstancesList[i]) && IsAimingAt(player, readOnlyInstancesList[i])) { return true; } } return false; } private bool IsElementalLemurian(CharacterBody body) { if (elementalByBody.TryGetValue(body, out var value)) { return value; } CharacterMaster master = body.master; if (!Object.op_Implicit((Object)(object)master)) { return false; } value = IsElementalLemurianMaster(((Object)master).name); elementalByBody[body] = value; return value; } private static bool IsElementalLemurianMaster(string name) { if (!name.StartsWith("LemurianBruiserMasterFire")) { return name.StartsWith("LemurianBruiserMasterIce"); } return true; } private bool AlloyWorshipUnit(WarningContext context, out KarmaWarning warning) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) warning = default(KarmaWarning); CharacterBody val = FindBody(Lookup(ref awuIndex, "SuperRoboBallBossBody")); if (Object.op_Implicit((Object)(object)val)) { if (IsAimingAt(context.Body, val)) { return context.TryMake("when you kill the Alloy Worship Unit", 80, AwuIds, includeLoop: false, out warning); } return false; } if (IsAimingAtEgg(context.Body)) { return context.TryMake("when you kill the Alloy Worship Unit that six eggs summon", 80, AwuIds, includeLoop: false, out warning); } return false; } private bool IsAimingAtEgg(CharacterBody player) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) BodyIndex val = Lookup(ref eggIndex, "VultureEggBody"); if ((int)val == -1) { return false; } ReadOnlyCollection<CharacterBody> readOnlyInstancesList = CharacterBody.readOnlyInstancesList; for (int i = 0; i < readOnlyInstancesList.Count; i++) { if (Object.op_Implicit((Object)(object)readOnlyInstancesList[i]) && readOnlyInstancesList[i].bodyIndex == val && IsAimingAt(player, readOnlyInstancesList[i])) { return true; } } return false; } public string Describe(WarningContext context) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Invalid comparison between Unknown and I4 //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); TeleporterInteraction instance = TeleporterInteraction.instance; if (Object.op_Implicit((Object)(object)instance)) { stringBuilder.Append($"teleporter {instance.activationState} at {instance.chargeFraction:P0}"); BossGroup bossGroup = instance.bossGroup; if (Object.op_Implicit((Object)(object)bossGroup) && Object.op_Implicit((Object)(object)bossGroup.combatSquad) && bossGroup.combatSquad.memberCount > 0 && bossGroup.totalMaxObservedMaxHealth > 0f) { stringBuilder.Append($", boss at {bossGroup.totalObservedHealth / bossGroup.totalMaxObservedMaxHealth:P0}"); } } else { stringBuilder.Append("no teleporter"); } switch (context.SceneName) { case "bazaar": { CharacterBody val2 = FindBody(Lookup(ref newtIndex, "ShopkeeperBody")); stringBuilder.Append(Object.op_Implicit((Object)(object)val2) ? $"; Newt at {val2.healthComponent.healthFraction:P1}" : "; no Newt found"); break; } case "computationalexchange": { ComputationalExchangeController instance2 = ComputationalExchangeController.instance; stringBuilder.Append((Object.op_Implicit((Object)(object)instance2) && Object.op_Implicit((Object)(object)instance2.solusVendorBody)) ? $"; SPEX awake={instance2.isAwake} at {instance2.solusVendorBody.healthComponent.healthFraction:P1}" : "; no SPEX found"); break; } case "goolake": { List<string> list = new List<string>(); foreach (CharacterBody readOnlyInstances in CharacterBody.readOnlyInstancesList) { if (Object.op_Implicit((Object)(object)readOnlyInstances) && Object.op_Implicit((Object)(object)readOnlyInstances.master) && ((Object)readOnlyInstances.master).name.Contains("LemurianBruiser")) { list.Add(((Object)readOnlyInstances.master).name + (IsElementalLemurian(readOnlyInstances) ? " (elemental)" : "")); } } stringBuilder.Append((list.Count > 0) ? ("; Elder Lemurian masters: " + string.Join(", ", list)) : "; no Elder Lemurians"); break; } case "shipgraveyard": { BodyIndex val = Lookup(ref eggIndex, "VultureEggBody"); int num = 0; foreach (CharacterBody readOnlyInstances2 in CharacterBody.readOnlyInstancesList) { if (Object.op_Implicit((Object)(object)readOnlyInstances2) && (int)val != -1 && readOnlyInstances2.bodyIndex == val) { num++; } } bool flag = Object.op_Implicit((Object)(object)FindBody(Lookup(ref awuIndex, "SuperRoboBallBossBody"))); stringBuilder.Append(string.Format("; {0} Vulture Eggs left, Alloy Worship Unit {1}", num, flag ? "alive" : "not here")); break; } } return stringBuilder.ToString(); } private static BodyIndex Lookup(ref BodyIndex cached, string bodyName) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected I4, but got Unknown if ((int)cached == -1) { cached = (BodyIndex)(int)BodyCatalog.FindBodyIndex(bodyName); } return cached; } private static CharacterBody FindBody(BodyIndex index) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if ((int)index == -1) { return null; } ReadOnlyCollection<CharacterBody> readOnlyInstancesList = CharacterBody.readOnlyInstancesList; for (int i = 0; i < readOnlyInstancesList.Count; i++) { if (Object.op_Implicit((Object)(object)readOnlyInstancesList[i]) && readOnlyInstancesList[i].bodyIndex == index) { return readOnlyInstancesList[i]; } } return null; } private static bool IsAimingAt(CharacterBody player, CharacterBody target) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)player.inputBank)) { return false; } Vector3 val = target.corePosition - player.inputBank.aimOrigin; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude > 80f || magnitude < 0.01f) { return false; } float num = 4f + Mathf.Atan(target.radius / magnitude) * 57.29578f; return Vector3.Angle(player.inputBank.aimDirection, val) <= num; } } internal sealed class InteractableDetector : IWarningDetector { private const string GoldChestName = "GOLDCHEST_NAME"; private const int InteractionsWhenDraining = 2; private static readonly string[] HiddenChestScenes = new string[3] { "dampcavesimple", "rootjungle", "repurposedcrater" }; private const string CatastropheText = "when you survive the catastrophe teleporter"; private static readonly string[] CatastropheIds = new string[1] { "SurviveMajorCatastrophe" }; private static readonly string[] ChestIds = new string[1] { "OpenHiddenGoldChest" }; private static readonly string[] HalcyonIds = new string[1] { "CompleteHalcyonShrine" }; public bool TryGetWarning(WarningContext context, out KarmaWarning warning) { warning = default(KarmaWarning); AimedTarget target = context.Target; if (!Object.op_Implicit((Object)(object)target.Root)) { return false; } if (Object.op_Implicit((Object)(object)target.Descent) && IsAvailable(target)) { return context.TryMake("when you survive the catastrophe teleporter", 100, CatastropheIds, includeLoop: false, out warning); } if (Object.op_Implicit((Object)(object)target.Teleporter) && !target.Teleporter.isCharged && IsCatastropheTeleporter()) { return context.TryMake("when you survive the catastrophe teleporter", 100, CatastropheIds, includeLoop: false, out warning); } if (IsHiddenChest(context) && IsAvailable(target)) { return context.TryMake("when you open this Legendary Chest", 90, ChestIds, includeLoop: false, out warning); } if (Object.op_Implicit((Object)(object)target.Halcyon)) { bool flag = target.Halcyon.interactions >= 2; string text = (flag ? "Interact again before it fills to avoid Karma" : "when you kill the Halcyonite"); bool result = context.TryMake(text, 90, HalcyonIds, includeLoop: false, out warning); warning.HideHeadline = flag; return result; } return false; } internal static bool IsCatastropheTeleporter() { DLC4ProgressionController instance = DLC4ProgressionController.instance; if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)instance.catastropheProvider)) { return instance.catastropheProvider.WillTriggerMajorCatastrophe(); } return false; } private static bool IsAvailable(AimedTarget target) { if (Object.op_Implicit((Object)(object)target.Purchase)) { return target.Purchase.available; } return true; } private static bool IsHiddenChest(WarningContext context) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) AimedTarget target = context.Target; if (!Object.op_Implicit((Object)(object)target.Purchase) || target.Purchase.displayNameToken != "GOLDCHEST_NAME") { return false; } if (Array.IndexOf(HiddenChestScenes, context.SceneName) < 0) { return false; } if (Object.op_Implicit((Object)(object)target.Identity)) { NetworkSceneId sceneId = target.Identity.sceneId; return ((NetworkSceneId)(ref sceneId)).Value != 0; } return false; } } internal sealed class ItemDetector : IWarningDetector { private const float TouchRange = 5f; private readonly Dictionary<int, string> textByMask = new Dictionary<int, string>(); public bool TryGetWarning(WarningContext context, out KarmaWarning warning) { warning = default(KarmaWarning); if (!Object.op_Implicit((Object)(object)context.Inventory)) { return false; } int num = AimedMask(context) | NearbyMask(context); if (num == 0) { return false; } int num2 = 0; for (int i = 0; i < ItemRules.Ids.Length; i++) { if ((num & (1 << i)) != 0 && !CountedEarlier(num, i)) { num2 += context.AmountFor(ItemRules.Ids[i]); } } num2 = Mathf.Min(num2, KarmaManager.maxKarma - context.Karma); if (num2 <= 0) { return false; } warning = new KarmaWarning { Amount = num2, Text = TextFor(num), Priority = 60 }; return true; } private static bool CountedEarlier(int mask, int index) { for (int i = 0; i < index; i++) { if ((mask & (1 << i)) != 0 && ItemRules.Ids[i] == ItemRules.Ids[index]) { return true; } } return false; } private static int AimedMask(WarningContext context) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) AimedTarget target = context.Target; if (Object.op_Implicit((Object)(object)target.Pickup)) { return MaskFor(context, target.Pickup.pickup); } PurchaseInteraction purchase = target.Purchase; if (Object.op_Implicit((Object)(object)target.Shop) && Object.op_Implicit((Object)(object)purchase) && purchase.available && IsPaidWithMoney(purchase) && CanAfford(context, purchase)) { return MaskFor(context, target.Shop.CurrentPickup()); } return 0; } private static bool CanAfford(WarningContext context, PurchaseInteraction purchase) { Interactor component = ((Component)context.Body).GetComponent<Interactor>(); if (Object.op_Implicit((Object)(object)component)) { return purchase.CanBeAffordedByInteractor(component); } return false; } private static bool IsPaidWithMoney(PurchaseInteraction purchase) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 if ((int)purchase.costType != 1) { return (int)purchase.costType == 3; } return true; } private static int NearbyMask(WarningContext context) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) int num = 0; List<GenericPickupController> instancesList = InstanceTracker.GetInstancesList<GenericPickupController>(); Vector3 corePosition = context.Body.corePosition; for (int i = 0; i < instancesList.Count; i++) { GenericPickupController val = instancesList[i]; if (Object.op_Implicit((Object)(object)val) && !((Object)(object)val == (Object)(object)context.Target.Pickup)) { Vector3 val2 = ((Component)val).transform.position - corePosition; if (!(((Vector3)(ref val2)).sqrMagnitude > 25f) && IsTakenOnTouch(context, val.pickup)) { num |= MaskFor(context, val.pickup); } } } return num; } private static bool IsTakenOnTouch(WarningContext context, UniquePickup pickup) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Invalid comparison between Unknown and I4 //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_0053: Unknown result type (might be due to invalid IL or missing references) PickupDef pickupDef = PickupCatalog.GetPickupDef(pickup.pickupIndex); if (pickupDef == null) { return false; } if ((int)pickupDef.itemIndex != -1) { ItemTierDef itemTierDef = ItemTierCatalog.GetItemTierDef(ItemCatalog.GetItemDef(pickupDef.itemIndex).tier); if (Object.op_Implicit((Object)(object)itemTierDef)) { if ((int)itemTierDef.pickupRules == 2) { return false; } if ((int)itemTierDef.pickupRules == 1 && context.Inventory.GetItemCountEffective(pickupDef.itemIndex) <= 0) { return false; } } } if ((int)pickupDef.equipmentIndex != -1 && (EquipmentCatalog.GetEquipmentDef(pickupDef.equipmentIndex).isLunar || context.Inventory.EquipmentSetFull())) { return false; } return true; } private static int MaskFor(WarningContext context, UniquePickup pickup) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) PickupDef pickupDef = PickupCatalog.GetPickupDef(pickup.pickupIndex); if (pickupDef == null) { return 0; } int num = 0; for (int i = 0; i < ItemRules.Ids.Length; i++) { if (context.Applies(ItemRules.Ids[i]) && ItemRules.WouldMeet(i, context.Inventory, pickupDef, pickup)) { num |= 1 << i; } } return num; } private string TextFor(int mask) { if (!textByMask.TryGetValue(mask, out var value)) { StringBuilder stringBuilder = new StringBuilder("when you take this and have "); bool flag = true; for (int i = 0; i < ItemRules.Phrases.Length; i++) { if ((mask & (1 << i)) != 0) { stringBuilder.Append(flag ? "" : " and ").Append(ItemRules.Phrases[i]); flag = false; } } value = stringBuilder.ToString(); textByMask[mask] = value; } return value; } } internal static class ItemRules { private const int LunarThreshold = 3; private const int MealThreshold = 3; private const int HallowedThreshold = 5; private const int LotsThreshold = 50; public static readonly string[] Ids = new string[7] { "CollectLunarItems", "CollectMealItems", "CollectHallowedItems", "CollectLotsOfItems", "GetEliteAffix", "PledgeLoyalty", "CollectMealItems" }; public static readonly string[] Phrases = new string[7] { "3 Lunar items", "3 Meal items", "5 Hallowed items", "50 items", "an Elite Aspect", "Beads of Fealty", "Sautéed Worms" }; public static ReadOnlyCollection<PlayerCharacterMasterController> PlayerControllers => PlayerCharacterMasterController.instances; public static int IndexOf(string id) { for (int i = 0; i < Ids.Length; i++) { if (Ids[i] == id) { return i; } } return -1; } public static bool Met(int index, Inventory inventory) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) switch (index) { case 0: return LunarCount(inventory, null, default(UniquePickup)) >= 3; case 1: if (inventory.GetTotalItemCountOfTier((ItemTier)10) < 3) { return inventory.GetItemCountPermanent(Items.WyrmOnHit) > 0; } return true; case 2: return inventory.GetTotalItemCountOfTier((ItemTier)11) >= 5; case 3: return CountableItems(inventory) >= 50; case 4: return IsEliteAspect(EquipmentCatalog.GetEquipmentDef(inventory.currentEquipmentIndex)); default: return false; } } public static bool WouldMeet(int index, Inventory inventory, PickupDef def, UniquePickup pickup) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Invalid comparison between Unknown and I4 //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Invalid comparison between Unknown and I4 //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Invalid comparison between Unknown and I4 //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) ItemDef val = (((int)def.itemIndex != -1) ? ItemCatalog.GetItemDef(def.itemIndex) : null); switch (index) { case 0: return LunarCount(inventory, def, pickup) >= 3; case 1: if (Object.op_Implicit((Object)(object)val) && (int)val.tier == 10) { return inventory.GetTotalItemCountOfTier((ItemTier)10) + 1 >= 3; } return false; case 2: if (Object.op_Implicit((Object)(object)val) && (int)val.tier == 11) { return inventory.GetTotalItemCountOfTier((ItemTier)11) + 1 >= 5; } return false; case 3: if (IsCountable(val) && !pickup.isRental && !((UniquePickup)(ref pickup)).isTempItem) { return CountableItems(inventory) + 1 >= 50; } return false; case 4: if ((int)def.equipmentIndex != -1) { return IsEliteAspect(EquipmentCatalog.GetEquipmentDef(def.equipmentIndex)); } return false; case 5: if (def.itemIndex == Items.LunarTrinket.itemIndex) { return !pickup.isRental; } return false; case 6: return def.itemIndex == Items.WyrmOnHit.itemIndex; default: return false; } } private static int LunarCount(Inventory inventory, PickupDef def, UniquePickup pickup) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) int num = inventory.GetTotalItemCountOfTier((ItemTier)3); EquipmentIndex val = inventory.currentEquipmentIndex; if (def != null && (int)def.itemIndex != -1 && (int)ItemCatalog.GetItemDef(def.itemIndex).tier == 3) { num++; } if (def != null && (int)def.equipmentIndex != -1) { val = def.equipmentIndex; } EquipmentDef equipmentDef = EquipmentCatalog.GetEquipmentDef(val); if ((Object)(object)equipmentDef != (Object)null && equipmentDef.isLunar) { num++; } return num; } private static bool IsEliteAspect(EquipmentDef equipment) { if ((Object)(object)equipment != (Object)null && (Object)(object)equipment.passiveBuffDef != (Object)null) { return equipment.passiveBuffDef.isElite; } return false; } private static bool IsCountable(ItemDef item) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 if (Object.op_Implicit((Object)(object)item) && (int)item.tier != 13 && !item.hidden && (int)item.tier != 5) { return !item.isConsumed; } return false; } private static int CountableItems(Inventory inventory) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) int num = 0; List<ItemIndex> itemAcquisitionOrder = inventory.itemAcquisitionOrder; for (int i = 0; i < itemAcquisitionOrder.Count; i++) { if (IsCountable(ItemCatalog.GetItemDef(itemAcquisitionOrder[i]))) { num += inventory.GetItemCountPermanent(itemAcquisitionOrder[i]); } } return num; } } internal interface IWarningDetector { bool TryGetWarning(WarningContext context, out KarmaWarning warning); } internal static class KarmaGate { public static bool TryGetKarma(out int karma) { karma = 0; KarmaManager instance = KarmaManager.instance; if (!Object.op_Implicit((Object)(object)instance) || !instance.NetworkisKarmaEnabled) { return false; } karma = (int)instance.NetworkactualKarma; return karma < KarmaManager.maxKarma; } public static int AmountFor(int reward, int karma) { int num = ((!Object.op_Implicit((Object)(object)Run.instance)) ? 1 : Run.instance.ruleBook.karmaMultiplier); return Mathf.Min(reward * num, KarmaManager.maxKarma - karma); } } internal static class KarmaTriggers { public const string ArtifactWorldComplete = "ArtifactWorldComplete"; public const string BeatChampRushReaper = "BeatChampRushReaper"; public const string CollectHallowedItems = "CollectHallowedItems"; public const string CollectLotsOfItems = "CollectLotsOfItems"; public const string CollectLunarItems = "CollectLunarItems"; public const string CollectMealItems = "CollectMealItems"; public const string CompletedArena = "CompletedArena"; public const string CompleteHalcyonShrine = "CompleteHalcyonShrine"; public const string DefeatAurelionite = "DefeatAurelionite"; public const string DefeatFalseSon = "DefeatFalseSon"; public const string DefeatSolusHeart = "DefeatSolusHeart"; public const string DefeatSolusWing = "DefeatSolusWing"; public const string EnterArena = "EnterArena"; public const string GetEliteAffix = "GetEliteAffix"; public const string KickedOutOfBazaar = "KickedOutOfBazaar"; public const string KickedOutOfCompExchange = "KickedOutOfCompExchange"; public const string KillBossQuickly = "KillBossQuickly"; public const string KilledElementalLemurians = "KilledElementalLemurians"; public const string KillProvidence = "KillProvidence"; public const string LoopAtLeastOnce = "LoopAtLeastOnce"; public const string OpenHiddenGoldChest = "OpenHiddenGoldChest"; public const string PledgeLoyalty = "PledgeLoyalty"; public const string SuperRoboBallBossBeaten = "SuperRoboBallBossBeaten"; public const string SurviveMajorCatastrophe = "SurviveMajorCatastrophe"; public static readonly string[] All = new string[24] { "ArtifactWorldComplete", "BeatChampRushReaper", "CollectHallowedItems", "CollectLotsOfItems", "CollectLunarItems", "CollectMealItems", "CompletedArena", "CompleteHalcyonShrine", "DefeatAurelionite", "DefeatFalseSon", "DefeatSolusHeart", "DefeatSolusWing", "EnterArena", "GetEliteAffix", "KickedOutOfBazaar", "KickedOutOfCompExchange", "KillBossQuickly", "KilledElementalLemurians", "KillProvidence", "LoopAtLeastOnce", "OpenHiddenGoldChest", "PledgeLoyalty", "SuperRoboBallBossBeaten", "SurviveMajorCatastrophe" }; public static KarmaTriggerDef FindInRun(string id) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) KarmaManager instance = KarmaManager.instance; if (!Object.op_Implicit((Object)(object)instance)) { return null; } KarmaTriggerDef val = instance.FindKarmaTriggerByIdentifier(id); if (val == null || !instance.IsRunUsingTrigger(val.karmaTriggerIndex)) { return null; } return val; } } internal static class KarmaTriggerState { private static readonly Dictionary<string, string[]> HomeStages = new Dictionary<string, string[]> { { "arena", new string[2] { "EnterArena", "CompletedArena" } }, { "goldshores", new string[1] { "DefeatAurelionite" } }, { "meridian", new string[1] { "DefeatFalseSon" } }, { "solutionalhaunt", new string[1] { "DefeatSolusWing" } }, { "solusweb", new string[1] { "DefeatSolusHeart" } }, { "champrush", new string[1] { "BeatChampRushReaper" } }, { "ultima", new string[1] { "KillProvidence" } } }; private static readonly HashSet<string> earned = new HashSet<string>(); private static int lastKarma = -1; public static void Install() { Run.onRunStartGlobal += OnRunStart; Run.onRunDestroyGlobal += OnRunDestroy; KarmaManager.onKarmaChangedClient += OnKarmaChanged; } public static void Uninstall() { Run.onRunStartGlobal -= OnRunStart; Run.onRunDestroyGlobal -= OnRunDestroy; KarmaManager.onKarmaChangedClient -= OnKarmaChanged; } private static void OnRunStart(Run run) { earned.Clear(); lastKarma = Math.Max(run.ruleBook.startingKarma, 0); } private static void OnRunDestroy(Run run) { earned.Clear(); lastKarma = -1; } private static void OnKarmaChanged(KarmaManager manager, int karma) { int num = lastKarma; lastKarma = karma; if (num < 0 || karma <= num) { return; } SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene(); if (Object.op_Implicit((Object)(object)sceneDefForCurrentScene) && HomeStages.TryGetValue(sceneDefForCurrentScene.cachedName, out var value)) { string[] array = value; foreach (string item in array) { earned.Add(item); } } } public static bool IsEarned(string id) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) KarmaManager instance = KarmaManager.instance; if (!Object.op_Implicit((Object)(object)instance)) { return false; } KarmaTriggerDef val = instance.FindKarmaTriggerByIdentifier(id); BaseKarmaTrigger val2 = ((val != null) ? instance.FindInstancedTrigger(val.karmaTriggerIndex) : null); if (val2 == null || val2.IsRepeatable()) { return false; } if (val2.isComplete || earned.Contains(id)) { return true; } if (InferFromWorld(id)) { earned.Add(id); return true; } return false; } private static bool InferFromWorld(string id) { if (id == "BeatChampRushReaper") { DLC4ProgressionController instance = DLC4ProgressionController.instance; if (Object.op_Implicit((Object)(object)instance)) { return instance.ReaperBossDefeated; } return false; } int num = ItemRules.IndexOf(id); if (num < 0) { return false; } foreach (PlayerCharacterMasterController playerController in ItemRules.PlayerControllers) { CharacterMaster master = playerController.master; if (Object.op_Implicit((Object)(object)master) && Object.op_Implicit((Object)(object)master.inventory) && ItemRules.Met(num, master.inventory)) { return true; } } return false; } public static string Describe() { StringBuilder stringBuilder = new StringBuilder(); string[] all = KarmaTriggers.All; foreach (string text in all) { KarmaTriggerDef val = KarmaTriggers.FindInRun(text); if (val != null) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(text).Append(" +").Append(val.baseKarmaReward) .Append(IsEarned(text) ? " (earned)" : ""); } } if (stringBuilder.Length <= 0) { return "no triggers in this run"; } return stringBuilder.ToString(); } } internal struct KarmaWarning { public int Amount; public bool HideHeadline; public string Text; public int Priority; } internal sealed class KarmaWarningsController : MonoBehaviour { private static readonly List<KarmaWarningsController> instances = new List<KarmaWarningsController>(); private readonly WarningContext context = new WarningContext(); private readonly PortalDetector portals = new PortalDetector(); private readonly CombatDetector combat = new CombatDetector(); private IWarningDetector[] detectors; private HUD hud; private WarningBanner banner; private CharacterBody driverBody; private InteractionDriver driver; private bool showing; private KarmaWarning current; public static IReadOnlyList<KarmaWarningsController> Instances => instances; public bool Showing => showing; public KarmaWarning Current => current; private void Awake() { hud = ((Component)this).GetComponent<HUD>(); detectors = new IWarningDetector[4] { portals, new InteractableDetector(), combat, new ItemDetector() }; } private void OnEnable() { instances.Add(this); } private void OnDisable() { instances.Remove(this); } private void Update() { if (!Object.op_Implicit((Object)(object)banner)) { if (!Object.op_Implicit((Object)(object)hud.mainContainer)) { return; } banner = WarningBanner.Create(hud); } showing = TryGetWarning(out current); if (showing) { banner.Show(current); } else { banner.Hide(); } } private bool TryGetWarning(out KarmaWarning warning) { warning = default(KarmaWarning); PluginConfig settings = KarmaWarningsPlugin.Settings; if (!settings.Enabled.Value) { return false; } if (TryFindDetectorWarning(out warning)) { return true; } if (settings.DebugAlwaysShow.Value) { warning = new KarmaWarning { Amount = 1, Text = "when DebugAlwaysShow is on", Priority = 0 }; return true; } return false; } private bool TryFindDetectorWarning(out KarmaWarning warning) { warning = default(KarmaWarning); LocalUser localUserViewer = hud.localUserViewer; CharacterBody val = ((localUserViewer != null) ? localUserViewer.cachedBody : null); if (!Object.op_Implicit((Object)(object)val) || !KarmaGate.TryGetKarma(out var karma)) { return false; } if ((Object)(object)driverBody != (Object)(object)val) { driverBody = val; driver = ((Component)val).GetComponent<InteractionDriver>(); } context.Update(val, karma, Object.op_Implicit((Object)(object)driver) ? driver.currentInteractable : null); bool flag = false; IWarningDetector[] array = detectors; for (int i = 0; i < array.Length; i++) { if (array[i].TryGetWarning(context, out var warning2) && (!flag || warning2.Priority > warning.Priority)) { warning = warning2; flag = true; } } return flag; } public string Describe() { LocalUser localUserViewer = hud.localUserViewer; CharacterBody val = ((localUserViewer != null) ? localUserViewer.cachedBody : null); string text = (showing ? $"showing \"{current.Text}\" (+{current.Amount})" : "nothing to show"); string text2 = (Object.op_Implicit((Object)(object)context.Target.Root) ? ((Object)context.Target.Root).name : "nothing"); return string.Format("HUD of {0}: {1}; stage {2} (order {3}), aiming at {4}", Object.op_Implicit((Object)(object)val) ? val.GetDisplayName() : "no body", text, context.SceneName, context.StageOrder, text2) + "\n exit: " + portals.Describe(context) + "\n fights: " + combat.Describe(context); } } [BepInPlugin("sevenstack.KarmaWarnings", "KarmaWarnings", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class KarmaWarningsPlugin : BaseUnityPlugin { public const string PluginGUID = "sevenstack.KarmaWarnings"; public const string PluginName = "KarmaWarnings"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; internal static PluginConfig Settings; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Settings = new PluginConfig(((BaseUnityPlugin)this).Config); if (RiskOfOptionsCompat.Enabled) { RiskOfOptionsCompat.Register(Settings); } KarmaTriggerState.Install(); HUD.onHudTargetChangedGlobal += OnHudTargetChanged; } private void OnDestroy() { KarmaTriggerState.Uninstall(); HUD.onHudTargetChangedGlobal -= OnHudTargetChanged; } private static void OnHudTargetChanged(HUD hud) { if (Object.op_Implicit((Object)(object)hud) && !Object.op_Implicit((Object)(object)((Component)hud).GetComponent<KarmaWarningsController>())) { ((Component)hud).gameObject.AddComponent<KarmaWarningsController>(); } } private void Update() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (Settings.DebugLogging.Value) { KeyboardShortcut value = Settings.DumpKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { DumpKarma(); } } } private static void DumpKarma() { KarmaManager instance = KarmaManager.instance; if (!Object.op_Implicit((Object)(object)instance)) { Log.LogInfo((object)"Karma dump: no KarmaManager (not in a run)"); return; } int num = ((!Object.op_Implicit((Object)(object)Run.instance)) ? 1 : Run.instance.ruleBook.karmaMultiplier); Log.LogInfo((object)$"Karma dump: enabled={instance.NetworkisKarmaEnabled} karma={instance.NetworkactualKarma}/{KarmaManager.maxKarma} multiplier=x{num} canWarn={KarmaGate.TryGetKarma(out var _)}"); Log.LogInfo((object)(" triggers in this run: " + KarmaTriggerState.Describe())); foreach (KarmaWarningsController instance2 in KarmaWarningsController.Instances) { Log.LogInfo((object)(" " + instance2.Describe())); } } } internal sealed class PluginConfig { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> DebugLogging; public readonly ConfigEntry<bool> DebugAlwaysShow; public readonly ConfigEntry<KeyboardShortcut> DumpKey; public PluginConfig(ConfigFile config) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) Enabled = config.Bind<bool>("General", "Enabled", true, "Show a warning above the crosshair before you do something that raises your Karma."); DebugLogging = config.Bind<bool>("Debug", "DebugLogging", false, "Enable the dump key, which logs the Karma state and what the mod sees around you."); DebugAlwaysShow = config.Bind<bool>("Debug", "DebugAlwaysShow", false, "Show a warning even when nothing would raise your Karma, for testing."); DumpKey = config.Bind<KeyboardShortcut>("Debug", "DumpKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "With DebugLogging on, press to log the Karma state, what the warning banner shows, the exit you're aiming at, and what the fight checks for this stage see."); } } internal sealed class PortalDetector : IWarningDetector { private sealed class Row { public readonly string Text; public readonly string[] Ids; public string RequiredObjectName; public string[] AllowedDestinations; public Row(string text, params string[] ids) { Text = text; Ids = ids; } } private static readonly Row Arena = new Row("when you start and finish the Void cells", "EnterArena", "CompletedArena"); private static readonly Row GildedCoast = new Row("when you kill Aurelionite", "DefeatAurelionite"); private static readonly string[] AmbryStages = new string[4] { "artifactworld", "artifactworld01", "artifactworld02", "artifactworld03" }; private static readonly Row Ambry = new Row("when you finish the Artifact Trial", "ArtifactWorldComplete") { RequiredObjectName = "PortalArtifactworld", AllowedDestinations = AmbryStages }; private static readonly Row Threshold = new Row("when you kill the Anima Harvester", "BeatChampRushReaper"); private static readonly Row Lucidity = new Row("when you kill the secret boss", "KillProvidence"); private static readonly Row NeuralSanctum = new Row("when you kill the Solus Heart", "DefeatSolusHeart"); private static readonly Row SolutionalHaunt = new Row("when you kill the Solus Wing", "DefeatSolusWing"); private static readonly Row Meridian = new Row("when you kill False Son", "DefeatFalseSon"); private static readonly Dictionary<string, Row> ByToken = new Dictionary<string, Row> { { "PORTAL_ARENA_CONTEXT", Arena }, { "PORTAL_GOLDSHORES_CONTEXT", GildedCoast }, { "PORTAL_ARTIFACTWORLD_CONTEXT", Ambry }, { "PORTAL_CHAMP_RUSH_CONTEXT", Threshold }, { "PORTAL_ULTIMA_CONTEXT", Lucidity }, { "PORTAL_SOLUSWEB_CONTEXT", NeuralSanctum } }; private static readonly Dictionary<string, Row> ByDestination = BuildByDestination(); private static readonly string[] NoIds = new string[0]; private static Dictionary<string, Row> BuildByDestination() { Dictionary<string, Row> dictionary = new Dictionary<string, Row> { { "arena", Arena }, { "goldshores", GildedCoast }, { "champrush", Threshold }, { "ultima", Lucidity }, { "solusweb", NeuralSanctum }, { "solutionalhaunt", SolutionalHaunt }, { "meridian", Meridian } }; string[] ambryStages = AmbryStages; foreach (string key in ambryStages) { dictionary[key] = Ambry; } return dictionary; } public bool TryGetWarning(WarningContext context, out KarmaWarning warning) { warning = default(KarmaWarning); AimedTarget target = context.Target; if (!Object.op_Implicit((Object)(object)target.Root)) { return false; } bool flag = (Object.op_Implicit((Object)(object)target.Teleporter) ? IsLeavingTeleporter(target) : Object.op_Implicit((Object)(object)target.Exit)); string text = (Object.op_Implicit((Object)(object)target.Exit) ? DestinationName(context, target.Exit) : null); Row value = null; string contextToken = target.ContextToken; if (contextToken != null && ByToken.TryGetValue(contextToken, out value) && !MatchesRow(target, value)) { value = null; } if (value == null && text != null) { ByDestination.TryGetValue(text, out value); } if (value != null && context.TryMake(value.Text, 100, value.Ids, flag, out warning)) { return true; } if (flag && context.LeavesLoopStage) { return LoopWarning(context, out warning); } return false; } private static bool IsLeavingTeleporter(AimedTarget target) { TeleporterInteraction teleporter = target.Teleporter; if (target.IsTeleporter) { if (!teleporter.isIdle) { return teleporter.isCharged; } return true; } return false; } public string Describe(WarningContext context) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) AimedTarget target = context.Target; if (target.IsTeleporter) { TeleporterInteraction teleporter = target.Teleporter; return string.Format("teleporter {0}, aimed at {1}, counts as leaving: {2}, catastrophe: {3}", teleporter.activationState, target.AimedAtMoon ? "the moon" : "the next stage", IsLeavingTeleporter(target), InteractableDetector.IsCatastropheTeleporter()); } if (Object.op_Implicit((Object)(object)target.Exit)) { return "exit to " + (DestinationName(context, target.Exit) ?? "a destination set by the server") + " (token " + (target.ContextToken ?? "none") + ")"; } return "not aiming at an exit"; } private static bool MatchesRow(AimedTarget target, Row row) { if (row.RequiredObjectName != null && (!Object.op_Implicit((Object)(object)target.Generic) || !((Object)target.Generic).name.StartsWith(row.RequiredObjectName))) { return false; } SceneDef val = (Object.op_Implicit((Object)(object)target.Exit) ? target.Exit.destinationScene : null); if (row.AllowedDestinations != null && Object.op_Implicit((Object)(object)val)) { return Array.IndexOf(row.AllowedDestinations, val.cachedName) >= 0; } return true; } private static bool LoopWarning(WarningContext context, out KarmaWarning warning) { warning = default(KarmaWarning); if (!context.Applies("LoopAtLeastOnce")) { return false; } AimedTarget target = context.Target; warning = new KarmaWarning { Amount = context.AmountFor("LoopAtLeastOnce"), Text = ((target.IsTeleporter && target.AimedAtMoon) ? "when you go to the moon" : "when you start a new loop"), Priority = 20 }; return warning.Amount > 0; } private static string DestinationName(WarningContext context, SceneExitController exit) { SceneDef val = exit.destinationScene; if (exit.isAlternatePath) { switch (context.StageOrder) { case 0: val = exit.tier1AlternateDestinationScene; break; case 1: val = exit.tier2AlternateDestinationScene; break; case 2: val = exit.tier3AlternateDestinationScene; break; case 3: val = exit.tier4AlternateDestinationScene; break; case 4: val = exit.tier5AlternateDestinationScene; break; } } if (!Object.op_Implicit((Object)(object)val)) { return null; } return val.cachedName; } } internal static class RiskOfOptionsCompat { public const string GUID = "com.rune580.riskofoptions"; private const string IconResource = "KarmaWarnings.icon.png"; public static bool Enabled => Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions"); [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static void Register(PluginConfig settings) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown ModSettingsManager.SetModDescription("Warns you before you do something that raises your Karma."); Sprite val = LoadIcon(); if (Object.op_Implicit((Object)(object)val)) { ModSettingsManager.SetModIcon(val); } ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.Enabled)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.DebugLogging)); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.DebugAlwaysShow)); ModSettingsManager.AddOption((BaseOption)new KeyBindOption(settings.DumpKey)); } private static Sprite LoadIcon() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) using Stream stream = typeof(RiskOfOptionsCompat).Assembly.GetManifestResourceStream("KarmaWarnings.icon.png"); if (stream == null) { return null; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, memoryStream.ToArray())) { return null; } return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } } internal sealed class WarningBanner : MonoBehaviour { private const float FadeInDuration = 0.075f; private const float FadeOutDuration = 0.15f; private const float Width = 1000f; private const float CrosshairClearance = 60f; private const float HeadlineFontSize = 34f; private const float SublineFontSize = 22f; private const string HeadlineText = "Increases Karma"; private static readonly Color TextColor = Color32.op_Implicit(new Color32((byte)184, (byte)196, byte.MaxValue, byte.MaxValue)); private CanvasGroup canvasGroup; private TextMeshProUGUI headline; private TextMeshProUGUI subline; private bool visible; public static WarningBanner Create(HUD hud) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) Transform transform = hud.mainContainer.transform; GameObject val = new GameObject("KarmaWarningsBanner", new Type[1] { typeof(RectTransform) }) { layer = ((Component)transform).gameObject.layer }; RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(transform, false); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0.5f, 0.5f); val2.anchorMax = val3; val2.anchorMin = val3; val2.pivot = new Vector2(0.5f, 0f); val2.sizeDelta = new Vector2(1000f, 0f); val2.anchoredPosition = new Vector2(0f, 60f); val.AddComponent<LayoutElement>().ignoreLayout = true; VerticalLayoutGroup obj = val.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj).childAlignment = (TextAnchor)7; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; ContentSizeFitter obj2 = val.AddComponent<ContentSizeFitter>(); obj2.horizontalFit = (FitMode)0; obj2.verticalFit = (FitMode)2; CanvasGroup val4 = val.AddComponent<CanvasGroup>(); val4.alpha = 0f; val4.interactable = false; val4.blocksRaycasts = false; WarningBanner warningBanner = val.AddComponent<WarningBanner>(); warningBanner.canvasGroup = val4; warningBanner.headline = CreateText(val2, "Headline", 34f); warningBanner.subline = CreateText(val2, "Subline", 22f); ((Component)warningBanner.subline).gameObject.SetActive(false); return warningBanner; } private static TextMeshProUGUI CreateText(RectTransform parent, string name, float fontSize) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }) { layer = ((Component)parent).gameObject.layer }; val.transform.SetParent((Transform)(object)parent, false); HGTextMeshProUGUI obj = val.AddComponent<HGTextMeshProUGUI>(); ((TMP_Text)obj).richText = true; ((TMP_Text)obj).enableWordWrapping = true; ((TMP_Text)obj).alignment = (TextAlignmentOptions)1026; ((TMP_Text)obj).fontSize = fontSize; ((Graphic)obj).color = TextColor; ((Graphic)obj).raycastTarget = false; return (TextMeshProUGUI)(object)obj; } public void Show(KarmaWarning warning) { visible = true; bool flag = !warning.HideHeadline; if (((Component)headline).gameObject.activeSelf != flag) { ((Component)headline).gameObject.SetActive(flag); } if (flag && ((TMP_Text)headline).text != "Increases Karma") { ((TMP_Text)headline).text = "Increases Karma"; } bool flag2 = !string.IsNullOrEmpty(warning.Text); if (((Component)subline).gameObject.activeSelf != flag2) { ((Component)subline).gameObject.SetActive(flag2); } if (flag2 && ((TMP_Text)subline).text != warning.Text) { ((TMP_Text)subline).text = warning.Text; } } public void Hide() { visible = false; } private void LateUpdate() { float num = (visible ? 0.075f : 0.15f); canvasGroup.alpha = Mathf.MoveTowards(canvasGroup.alpha, visible ? 1f : 0f, Time.unscaledDeltaTime / num); } } internal sealed class WarningContext { public CharacterBody Body; public Inventory Inventory; public int Karma; public SceneDef Scene; public string SceneName = ""; public int StageOrder; public readonly AimedTarget Target = new AimedTarget(); public int SceneHandle { get; private set; } = -1; public bool LeavesLoopStage { get { if (StageOrder == 5 && SceneName != "moon" && SceneName != "moon2") { return !(Run.instance is WeeklyRun); } return false; } } public void Update(CharacterBody body, int karma, GameObject aimed) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) Body = body; Inventory = body.inventory; Karma = karma; Scene activeScene = SceneManager.GetActiveScene(); int handle = ((Scene)(ref activeScene)).handle; if (handle != SceneHandle) { SceneHandle = handle; Scene = SceneCatalog.GetSceneDefForCurrentScene(); SceneName = (Object.op_Implicit((Object)(object)Scene) ? Scene.cachedName : ""); StageOrder = (Object.op_Implicit((Object)(object)Scene) ? Scene.stageOrder : 0); } Target.Refresh(aimed); } public bool Applies(string id) { if (KarmaTriggers.FindInRun(id) != null) { return !KarmaTriggerState.IsEarned(id); } return false; } public int AmountFor(string id) { KarmaTriggerDef val = KarmaTriggers.FindInRun(id); if (val == null) { return 0; } return KarmaGate.AmountFor(val.baseKarmaReward, Karma); } public bool TryMake(string text, int priority, string[] ids, bool includeLoop, out KarmaWarning warning) { int num = 0; foreach (string id in ids) { if (Applies(id)) { num += AmountFor(id); } } if (num > 0 && includeLoop && LeavesLoopStage && Applies("LoopAtLeastOnce")) { num += AmountFor("LoopAtLeastOnce"); } num = Mathf.Min(num, KarmaManager.maxKarma - Karma); warning = new KarmaWarning { Amount = num, Text = text, Priority = priority }; return num > 0; } } internal static class WarningPriority { public const int Stage = 100; public const int Interaction = 90; public const int Combat = 80; public const int Item = 60; public const int Loop = 20; public const int Debug = 0; } internal static class PluginVersionInfo { public const string Version = "1.0.0"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }