Decompiled source of KarmaWarnings v1.0.0

plugins/KarmaWarnings/KarmaWarnings.dll

Decompiled a day ago
using 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)
		{
		}
	}
}