Decompiled source of SaleStarReminder v1.0.0

plugins/SaleStarReminder/SaleStarReminder.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
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 Microsoft.CodeAnalysis;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.UI;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("RoR2")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SaleStarReminder")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f8a53eb52f28146eeca5f0161efe17cda8f77167")]
[assembly: AssemblyProduct("SaleStarReminder")]
[assembly: AssemblyTitle("SaleStarReminder")]
[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 SaleStarReminder
{
	internal sealed class ChestScanner
	{
		private const string CategorySeparator = " - ";

		private const string ChestWord = "Chest";

		private const string SmallChestToken = "CHEST1_NAME";

		private const string SmallChestName = "Small Chest";

		private int scannedInstanceCount = -1;

		private int scannedCandidateCount = -1;

		private bool scannedHasKey;

		private bool scannedRemoteOp;

		private PurchaseInteraction best;

		public bool AnyUsable => scannedCandidateCount > 0;

		public PurchaseInteraction Best => best;

		public void Refresh(Inventory inventory, CharacterBody body)
		{
			bool flag = HasRustedKey(inventory);
			bool flag2 = IsRemoteOp(body);
			List<PurchaseInteraction> instancesList = InstanceTracker.GetInstancesList<PurchaseInteraction>();
			int num = 0;
			foreach (PurchaseInteraction item in instancesList)
			{
				if (IsCandidate(item, flag, flag2))
				{
					num++;
				}
			}
			bool flag3 = num == 0 || (Object.op_Implicit((Object)(object)best) && IsCandidate(best, flag, flag2));
			if (!(instancesList.Count == scannedInstanceCount && num == scannedCandidateCount && flag == scannedHasKey && flag2 == scannedRemoteOp && flag3))
			{
				scannedInstanceCount = instancesList.Count;
				scannedCandidateCount = num;
				scannedHasKey = flag;
				scannedRemoteOp = flag2;
				best = FindBest(instancesList, flag, flag2, null);
			}
		}

		public PurchaseInteraction GetBestExcluding(PurchaseInteraction exclude)
		{
			if ((Object)(object)best != (Object)(object)exclude)
			{
				return best;
			}
			return FindBest(InstanceTracker.GetInstancesList<PurchaseInteraction>(), scannedHasKey, scannedRemoteOp, exclude);
		}

		public static string GetDisplayName(PurchaseInteraction chest)
		{
			string text = Language.GetString(chest.displayNameToken);
			if (Language.currentLanguageName != "en")
			{
				return text;
			}
			if (chest.displayNameToken == "CHEST1_NAME")
			{
				return "Small Chest";
			}
			int num = text.IndexOf(" - ", StringComparison.Ordinal);
			if (num < 0)
			{
				return text;
			}
			string text2 = text.Substring(0, num);
			if (!text2.EndsWith("Chest", StringComparison.Ordinal))
			{
				return text;
			}
			string text3 = text.Substring(num + " - ".Length).Trim();
			return text2.Substring(0, text2.Length - "Chest".Length) + text3 + " Chest";
		}

		public static void DumpAll(Inventory inventory, CharacterBody body)
		{
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			bool flag = HasRustedKey(inventory);
			bool flag2 = IsRemoteOp(body);
			List<PurchaseInteraction> instancesList = InstanceTracker.GetInstancesList<PurchaseInteraction>();
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine($"{instancesList.Count} purchasable objects, Rusted Key held: {flag}," + $" remote operation: {flag2}");
			foreach (PurchaseInteraction item in instancesList)
			{
				if (Object.op_Implicit((Object)(object)item))
				{
					stringBuilder.AppendLine("  " + ((Object)((Component)item).gameObject).name + ": token=" + item.displayNameToken + $" compatible={item.saleStarCompatible} available={item.available}" + $" cost={item.cost} costType={item.costType} tier={ChestTiers.Get(item)}" + $" allowRemoteOp={item.allowRemoteOpInteraction}" + $" candidate={IsCandidate(item, flag, flag2)}");
				}
			}
			SaleStarReminderPlugin.Log.LogInfo((object)stringBuilder.ToString());
		}

		private static bool IsCandidate(PurchaseInteraction chest, bool hasKey, bool remoteOp)
		{
			if (Object.op_Implicit((Object)(object)chest) && chest.saleStarCompatible && chest.available && (hasKey || chest.displayNameToken != "LOCKBOX_NAME"))
			{
				if (remoteOp)
				{
					return chest.allowRemoteOpInteraction;
				}
				return true;
			}
			return false;
		}

		private static bool IsRemoteOp(CharacterBody body)
		{
			if (Object.op_Implicit((Object)(object)body))
			{
				return body.isRemoteOp;
			}
			return false;
		}

		private static bool HasRustedKey(Inventory inventory)
		{
			if (Object.op_Implicit((Object)(object)inventory))
			{
				return inventory.GetItemCountEffective(Items.TreasureCache) > 0;
			}
			return false;
		}

		private static PurchaseInteraction FindBest(List<PurchaseInteraction> all, bool hasKey, bool remoteOp, PurchaseInteraction exclude)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			PurchaseInteraction val = null;
			ChestTier chestTier = ChestTier.Unknown;
			foreach (PurchaseInteraction item in all)
			{
				if (!((Object)(object)item == (Object)(object)exclude) && IsCandidate(item, hasKey, remoteOp))
				{
					ChestTier chestTier2 = ChestTiers.Get(item);
					if (!Object.op_Implicit((Object)(object)val) || chestTier2 > chestTier || (chestTier2 == chestTier && item.costType == val.costType && item.cost > val.cost))
					{
						val = item;
						chestTier = chestTier2;
					}
				}
			}
			return val;
		}
	}
	internal enum ChestTier
	{
		Unknown,
		Lesser,
		Small,
		Adaptive,
		Large,
		Legendary
	}
	internal static class ChestTiers
	{
		public const string RustyLockboxToken = "LOCKBOX_NAME";

		private static readonly Dictionary<string, ChestTier> ByToken = new Dictionary<string, ChestTier>
		{
			{
				"EQUIPMENTBARREL_NAME",
				ChestTier.Lesser
			},
			{
				"HOLLOWCHEST_NAME",
				ChestTier.Lesser
			},
			{
				"CHEST1_NAME",
				ChestTier.Small
			},
			{
				"CHEST1_STEALTHED_NAME",
				ChestTier.Small
			},
			{
				"CATEGORYCHEST_DAMAGE_NAME",
				ChestTier.Small
			},
			{
				"CATEGORYCHEST_HEALING_NAME",
				ChestTier.Small
			},
			{
				"CATEGORYCHEST_UTILITY_NAME",
				ChestTier.Small
			},
			{
				"CASINOCHEST_NAME",
				ChestTier.Adaptive
			},
			{
				"CHEST2_NAME",
				ChestTier.Large
			},
			{
				"CATEGORYCHEST2_DAMAGE_NAME",
				ChestTier.Large
			},
			{
				"CATEGORYCHEST2_HEALING_NAME",
				ChestTier.Large
			},
			{
				"CATEGORYCHEST2_UTILITY_NAME",
				ChestTier.Large
			},
			{
				"LOCKBOX_NAME",
				ChestTier.Large
			},
			{
				"GOLDCHEST_NAME",
				ChestTier.Legendary
			}
		};

		public static ChestTier Get(PurchaseInteraction chest)
		{
			string displayNameToken = chest.displayNameToken;
			if (displayNameToken == null || !ByToken.TryGetValue(displayNameToken, out var value))
			{
				return ChestTier.Unknown;
			}
			return value;
		}

		public static bool IsMockable(ChestTier tier)
		{
			if (tier != ChestTier.Lesser)
			{
				return tier == ChestTier.Small;
			}
			return true;
		}
	}
	internal static class EnglishArticle
	{
		private static readonly string[] ConsonantSoundPrefixes = new string[12]
		{
			"uni", "use", "usu", "uti", "ure", "uri", "uro", "ubiq", "eu", "ewe",
			"one", "once"
		};

		private static readonly string[] VowelSoundPrefixes = new string[5] { "hour", "honest", "honor", "honour", "heir" };

		public static string For(string phrase)
		{
			string text = phrase.TrimStart().ToLowerInvariant();
			if (text.Length == 0)
			{
				return "a";
			}
			if (StartsWithAny(text, ConsonantSoundPrefixes))
			{
				return "a";
			}
			if (StartsWithAny(text, VowelSoundPrefixes))
			{
				return "an";
			}
			if ("aeiou".IndexOf(text[0]) < 0)
			{
				return "a";
			}
			return "an";
		}

		private static bool StartsWithAny(string word, string[] prefixes)
		{
			foreach (string value in prefixes)
			{
				if (word.StartsWith(value, StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class MockLines
	{
		[Serializable]
		internal sealed class LinesFile
		{
			public LineSet smallChest;

			public LineSet betterChest;
		}

		[Serializable]
		internal sealed class LineSet
		{
			public string[] clean;

			public string[] vulgar;
		}

		public const string FileName = "MockLines.json";

		private static string[] smallChest = Array.Empty<string>();

		private static string[] smallChestVulgar = Array.Empty<string>();

		private static string[] betterChest = Array.Empty<string>();

		private static string[] betterChestVulgar = Array.Empty<string>();

		public static void Load(string directory)
		{
			string text = Path.Combine(directory, "MockLines.json");
			if (!File.Exists(text))
			{
				SaleStarReminderPlugin.Log.LogWarning((object)(text + " is missing, so the mocking message is off."));
				return;
			}
			LinesFile linesFile;
			try
			{
				linesFile = JsonUtility.FromJson<LinesFile>(File.ReadAllText(text));
			}
			catch (Exception ex)
			{
				SaleStarReminderPlugin.Log.LogError((object)("Couldn't read " + text + ", so the mocking message is off: " + ex.Message));
				return;
			}
			if (linesFile == null)
			{
				SaleStarReminderPlugin.Log.LogWarning((object)(text + " is empty, so the mocking message is off."));
				return;
			}
			smallChest = Clean(linesFile.smallChest?.clean);
			smallChestVulgar = Clean(linesFile.smallChest?.vulgar);
			betterChest = Clean(linesFile.betterChest?.clean);
			betterChestVulgar = Clean(linesFile.betterChest?.vulgar);
			if (SaleStarReminderPlugin.Settings.DebugLogging.Value)
			{
				SaleStarReminderPlugin.Log.LogInfo((object)($"Loaded mocking lines from {text}: {smallChest.Length} + " + $"{smallChestVulgar.Length} vulgar headlines, {betterChest.Length} + {betterChestVulgar.Length}" + " vulgar better-chest lines."));
			}
		}

		public static string PickSmallChest(bool includeVulgar)
		{
			return Pick(smallChest, smallChestVulgar, includeVulgar);
		}

		public static string PickBetterChest(bool includeVulgar, string chestName)
		{
			return Pick(betterChest, betterChestVulgar, includeVulgar)?.Replace("{0}", chestName);
		}

		private static string Pick(string[] clean, string[] vulgar, bool includeVulgar)
		{
			int num = (includeVulgar ? vulgar.Length : 0);
			int num2 = clean.Length + num;
			if (num2 == 0)
			{
				return null;
			}
			int num3 = Random.Range(0, num2);
			if (num3 >= clean.Length)
			{
				return vulgar[num3 - clean.Length];
			}
			return clean[num3];
		}

		private static string[] Clean(string[] lines)
		{
			if (lines != null)
			{
				return lines.Where((string line) => !string.IsNullOrWhiteSpace(line)).ToArray();
			}
			return Array.Empty<string>();
		}
	}
	internal sealed class PluginConfig
	{
		public readonly ConfigEntry<bool> ShowReminder;

		public readonly ConfigEntry<float> ReminderFontSize;

		public readonly ConfigEntry<float> SubheaderFontSize;

		public readonly ConfigEntry<float> ReminderOffsetY;

		public readonly ConfigEntry<float> NearChestScale;

		public readonly ConfigEntry<bool> PulseNearChest;

		public readonly ConfigEntry<bool> ShowMostValuableChest;

		public readonly ConfigEntry<bool> MockSmallChest;

		public readonly ConfigEntry<bool> MockIncludeVulgar;

		public readonly ConfigEntry<float> MockDurationSeconds;

		public readonly ConfigEntry<bool> DebugLogging;

		public readonly ConfigEntry<bool> DebugAlwaysShow;

		public readonly ConfigEntry<KeyboardShortcut> DumpChestsKey;

		public PluginConfig(ConfigFile config)
		{
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			ShowReminder = config.Bind<bool>("Reminder", "ShowReminder", true, "Show large red text at the top of the screen while you hold an unused Sale Star and the stage has a chest it works on.");
			ReminderFontSize = config.Bind<float>("Reminder", "ReminderFontSize", 80f, "Font size of the main alert, in UI points.");
			SubheaderFontSize = config.Bind<float>("Reminder", "SubheaderFontSize", 20f, "Font size of the smaller line under the main alert, in UI points.");
			ReminderOffsetY = config.Bind<float>("Reminder", "ReminderOffsetY", 8f, "Gap between the bottom of the item box at the top of the screen and the reminder text, in UI pixels.");
			NearChestScale = config.Bind<float>("Reminder", "NearChestScale", 1.3f, "How much bigger the main alert gets while the game circles a chest your Sale Star works on (when you look at it up close), if that chest is worse than a Large chest or better still on the stage. 1 turns this off.");
			PulseNearChest = config.Bind<bool>("Reminder", "PulseNearChest", true, "Fade the main alert in and out, twice per second, at the same times it grows (see NearChestScale). Turn this off if any pulsing or flashing bothers you.");
			ShowMostValuableChest = config.Bind<bool>("MostValuableChest", "ShowMostValuableChest", false, "Under the reminder, name the most valuable unopened chest on the stage that the Sale Star works on. This includes chests you haven't found yet.");
			MockSmallChest = config.Bind<bool>("Mock", "MockSmallChest", false, "Make fun of you for a few seconds after you spend a Sale Star on a small chest, an Equipment Barrel or a Concept Reliquary while a Large Chest, Rusty Lockbox or Legendary Chest was still available. With ShowMostValuableChest on, also names that chest. The lines come from MockLines.json next to the mod's DLL.");
			MockIncludeVulgar = config.Bind<bool>("Mock", "MockIncludeVulgar", false, "Also pick from the vulgar lines when making fun of you.");
			MockDurationSeconds = config.Bind<float>("Mock", "MockDurationSeconds", 6f, "How long the mocking message stays on screen, in seconds.");
			DebugLogging = config.Bind<bool>("Debug", "DebugLogging", false, "Log each Sale Star use the mod detects, and enable the chest dump key.");
			DebugAlwaysShow = config.Bind<bool>("Debug", "DebugAlwaysShow", false, "Show the reminder even without a Sale Star or a usable chest, for testing.");
			DumpChestsKey = config.Bind<KeyboardShortcut>("Debug", "DumpChestsKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "With DebugLogging on, press to log every purchasable object in the current stage and how the mod ranks it.");
		}
	}
	internal sealed class ReminderBanner : MonoBehaviour
	{
		private const float FadeDuration = 0.2f;

		private const float SideMargin = 40f;

		private const float EmphasisFadeDuration = 0.15f;

		private const float PulseFrequency = 2f;

		private const float PulseMinAlpha = 0.35f;

		private const string ReminderHeadline = "UNUSED SALE STAR";

		private static readonly Color HeadlineColor = new Color(1f, 0.17f, 0.17f);

		private static readonly Color SubheaderColor = new Color(1f, 0.92f, 0.92f);

		private RectTransform rectTransform;

		private ItemInventoryDisplay itemDisplay;

		private readonly Vector3[] corners = (Vector3[])(object)new Vector3[4];

		private CanvasGroup canvasGroup;

		private TextMeshProUGUI headline;

		private TextMeshProUGUI subheader;

		private bool visible;

		private bool emphasized;

		private float emphasis;

		private float pulseTime;

		private float shownHeadlineSize = -1f;

		private float shownSubheaderSize = -1f;

		public static ReminderBanner 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = hud.mainContainer.transform;
			GameObject val = new GameObject("SaleStarReminderBanner", new Type[1] { typeof(RectTransform) })
			{
				layer = ((Component)transform).gameObject.layer
			};
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(transform, false);
			val2.anchorMin = new Vector2(0f, 1f);
			val2.anchorMax = new Vector2(1f, 1f);
			val2.pivot = new Vector2(0.5f, 1f);
			val2.sizeDelta = new Vector2(-80f, 0f);
			val.AddComponent<LayoutElement>().ignoreLayout = true;
			VerticalLayoutGroup obj = val.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj).childAlignment = (TextAnchor)1;
			((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 val3 = val.AddComponent<CanvasGroup>();
			val3.alpha = 0f;
			val3.interactable = false;
			val3.blocksRaycasts = false;
			ReminderBanner reminderBanner = val.AddComponent<ReminderBanner>();
			reminderBanner.rectTransform = val2;
			reminderBanner.itemDisplay = hud.itemInventoryDisplay;
			reminderBanner.canvasGroup = val3;
			reminderBanner.headline = CreateText(val2, "Headline", HeadlineColor);
			reminderBanner.subheader = CreateText(val2, "Subheader", SubheaderColor);
			((Component)reminderBanner.subheader).gameObject.SetActive(false);
			return reminderBanner;
		}

		private static TextMeshProUGUI CreateText(RectTransform parent, string name, Color color)
		{
			//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_0056: 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)258;
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return (TextMeshProUGUI)(object)obj;
		}

		public void ShowReminder(string subline, bool nearChest)
		{
			Show("UNUSED SALE STAR", subline);
			emphasized = nearChest;
		}

		public void ShowMock(string line, string subline)
		{
			Show(line, subline);
			emphasized = false;
		}

		public void Hide()
		{
			visible = false;
			emphasized = false;
		}

		private void Show(string line, string subline)
		{
			visible = true;
			if (((TMP_Text)headline).text != line)
			{
				((TMP_Text)headline).text = line;
			}
			bool flag = !string.IsNullOrEmpty(subline);
			if (((Component)subheader).gameObject.activeSelf != flag)
			{
				((Component)subheader).gameObject.SetActive(flag);
			}
			if (flag && ((TMP_Text)subheader).text != subline)
			{
				((TMP_Text)subheader).text = subline;
			}
		}

		private void LateUpdate()
		{
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			PluginConfig settings = SaleStarReminderPlugin.Settings;
			emphasis = Mathf.MoveTowards(emphasis, emphasized ? 1f : 0f, Time.unscaledDeltaTime / 0.15f);
			float num = settings.ReminderFontSize.Value * Mathf.Lerp(1f, settings.NearChestScale.Value, emphasis);
			if (num != shownHeadlineSize)
			{
				shownHeadlineSize = num;
				((TMP_Text)headline).fontSize = num;
			}
			UpdatePulse(settings.PulseNearChest.Value);
			float value = settings.SubheaderFontSize.Value;
			if (value != shownSubheaderSize)
			{
				shownSubheaderSize = value;
				((TMP_Text)subheader).fontSize = value;
			}
			float num2 = (visible ? 1f : 0f);
			if (visible || canvasGroup.alpha > 0f)
			{
				rectTransform.anchoredPosition = new Vector2(0f, GetItemDisplayBottom() - settings.ReminderOffsetY.Value);
			}
			canvasGroup.alpha = Mathf.MoveTowards(canvasGroup.alpha, num2, Time.unscaledDeltaTime / 0.2f);
		}

		private void UpdatePulse(bool enabled)
		{
			float num = 1f;
			if (enabled && emphasis > 0f)
			{
				pulseTime += Time.unscaledDeltaTime;
				float num2 = 0.5f - 0.5f * Mathf.Cos(MathF.PI * 4f * pulseTime);
				num = Mathf.Lerp(1f, 0.35f, num2 * emphasis);
			}
			else
			{
				pulseTime = 0f;
			}
			if (((TMP_Text)headline).alpha != num)
			{
				((TMP_Text)headline).alpha = num;
			}
		}

		private float GetItemDisplayBottom()
		{
			//IL_0032: 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_004f: 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)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)itemDisplay))
			{
				Transform parent = ((Transform)rectTransform).parent;
				RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
				if (val != null)
				{
					((RectTransform)((Component)itemDisplay).transform).GetWorldCorners(corners);
					float y = ((Transform)val).InverseTransformPoint(corners[0]).y;
					Rect rect = val.rect;
					return y - ((Rect)(ref rect)).yMax;
				}
			}
			return 0f;
		}
	}
	internal static class RiskOfOptionsCompat
	{
		public const string GUID = "com.rune580.riskofoptions";

		private const string IconResource = "SaleStarReminder.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_0039: 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)
			//IL_004f: 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)
			//IL_006a: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Expected O, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_00f7: 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_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Expected O, but got Unknown
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Expected O, but got Unknown
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Expected O, but got Unknown
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Expected O, but got Unknown
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Expected O, but got Unknown
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Expected O, but got Unknown
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Reminds you in large red text while you hold an unused Sale Star.");
			Sprite val = LoadIcon();
			if (Object.op_Implicit((Object)(object)val))
			{
				ModSettingsManager.SetModIcon(val);
			}
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.ShowReminder));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(settings.ReminderFontSize, new StepSliderConfig
			{
				min = 20f,
				max = 160f,
				increment = 1f,
				FormatString = "{0:0}"
			}));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(settings.SubheaderFontSize, new StepSliderConfig
			{
				min = 10f,
				max = 80f,
				increment = 1f,
				FormatString = "{0:0}"
			}));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(settings.ReminderOffsetY, new StepSliderConfig
			{
				min = -50f,
				max = 300f,
				increment = 1f,
				FormatString = "{0:0}px"
			}));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(settings.NearChestScale, new StepSliderConfig
			{
				min = 1f,
				max = 2f,
				increment = 0.05f,
				FormatString = "{0:0.00}x"
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.PulseNearChest));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.ShowMostValuableChest));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.MockSmallChest));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.MockIncludeVulgar));
			ModSettingsManager.AddOption((BaseOption)new StepSliderOption(settings.MockDurationSeconds, new StepSliderConfig
			{
				min = 1f,
				max = 15f,
				increment = 0.5f,
				FormatString = "{0:0.0}s"
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.DebugLogging));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(settings.DebugAlwaysShow));
		}

		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("SaleStarReminder.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 SaleStarReminderController : MonoBehaviour
	{
		private const float UsedChestGrace = 1.5f;

		private readonly ChestScanner scanner = new ChestScanner();

		private HUD hud;

		private ReminderBanner banner;

		private CharacterBody driverBody;

		private InteractionDriver driver;

		private GameObject aimedObject;

		private PurchaseInteraction aimedChest;

		private PurchaseInteraction lastAimedChest;

		private float lastAimedTime = float.NegativeInfinity;

		private Inventory trackedInventory;

		private int lastStars;

		private int lastConsumed;

		private float mockUntil = float.NegativeInfinity;

		private string mockLine;

		private string mockSubline;

		private PurchaseInteraction describedBest;

		private string bestSubline;

		private void Awake()
		{
			hud = ((Component)this).GetComponent<HUD>();
		}

		private void Update()
		{
			if (!Object.op_Implicit((Object)(object)banner))
			{
				if (!Object.op_Implicit((Object)(object)hud.mainContainer))
				{
					return;
				}
				banner = ReminderBanner.Create(hud);
			}
			PluginConfig settings = SaleStarReminderPlugin.Settings;
			LocalUser localUserViewer = hud.localUserViewer;
			CharacterMaster val = ((localUserViewer != null) ? localUserViewer.cachedMaster : null);
			Inventory val2 = (Object.op_Implicit((Object)(object)val) ? val.inventory : null);
			CharacterBody val3 = ((localUserViewer != null) ? localUserViewer.cachedBody : null);
			scanner.Refresh(val2, val3);
			TrackAimedChest(val3);
			DetectStarUse(val2, settings);
			if (Time.time < mockUntil)
			{
				banner.ShowMock(mockLine, mockSubline);
				return;
			}
			bool flag = Object.op_Implicit((Object)(object)val3) && Object.op_Implicit((Object)(object)val2) && val2.GetItemCountEffective(Items.LowerPricedChests) > 0;
			if (settings.ShowReminder.Value && ((flag && scanner.AnyUsable) || settings.DebugAlwaysShow.Value))
			{
				banner.ShowReminder(settings.ShowMostValuableChest.Value ? DescribeBest(scanner.Best) : null, flag && IsAboutToWasteStar());
			}
			else
			{
				banner.Hide();
			}
		}

		private bool IsAboutToWasteStar()
		{
			if (!IsGameCirclingChest() || !Object.op_Implicit((Object)(object)aimedChest))
			{
				return false;
			}
			PurchaseInteraction best = scanner.Best;
			if (!Object.op_Implicit((Object)(object)best))
			{
				return false;
			}
			ChestTier chestTier = ChestTiers.Get(best);
			if (chestTier >= ChestTier.Large)
			{
				return ChestTiers.Get(aimedChest) < chestTier;
			}
			return false;
		}

		private bool IsGameCirclingChest()
		{
			if (Object.op_Implicit((Object)(object)driver))
			{
				return Object.op_Implicit((Object)(object)driver.saleStarEffect);
			}
			return false;
		}

		private string DescribeBest(PurchaseInteraction best)
		{
			if ((Object)(object)best != (Object)(object)describedBest)
			{
				describedBest = best;
				bestSubline = (Object.op_Implicit((Object)(object)best) ? DescribeChestOnStage(ChestScanner.GetDisplayName(best)) : null);
			}
			return bestSubline;
		}

		private static string DescribeChestOnStage(string name)
		{
			return "There is " + EnglishArticle.For(name) + " " + name + " on this stage";
		}

		private void TrackAimedChest(CharacterBody body)
		{
			if ((Object)(object)body != (Object)(object)driverBody)
			{
				driverBody = body;
				driver = (Object.op_Implicit((Object)(object)body) ? ((Component)body).GetComponent<InteractionDriver>() : null);
			}
			GameObject val = (Object.op_Implicit((Object)(object)driver) ? driver.currentInteractable : null);
			if ((Object)(object)val != (Object)(object)aimedObject)
			{
				aimedObject = val;
				PurchaseInteraction val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<PurchaseInteraction>() : null);
				aimedChest = ((Object.op_Implicit((Object)(object)val2) && val2.saleStarCompatible) ? val2 : null);
			}
			if (Object.op_Implicit((Object)(object)aimedChest))
			{
				lastAimedChest = aimedChest;
				lastAimedTime = Time.time;
			}
		}

		private void DetectStarUse(Inventory inventory, PluginConfig settings)
		{
			int num = (Object.op_Implicit((Object)(object)inventory) ? inventory.GetItemCountTotal(Items.LowerPricedChests) : 0);
			int num2 = (Object.op_Implicit((Object)(object)inventory) ? inventory.GetItemCountTotal(Items.LowerPricedChestsConsumed) : 0);
			bool num3 = (Object)(object)inventory == (Object)(object)trackedInventory && Object.op_Implicit((Object)(object)inventory) && num < lastStars && num2 > lastConsumed;
			trackedInventory = inventory;
			lastStars = num;
			lastConsumed = num2;
			if (num3)
			{
				OnStarUsed(settings);
			}
		}

		private void OnStarUsed(PluginConfig settings)
		{
			PurchaseInteraction val = ((Object.op_Implicit((Object)(object)lastAimedChest) && Time.time - lastAimedTime <= 1.5f) ? lastAimedChest : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				if (settings.DebugLogging.Value)
				{
					SaleStarReminderPlugin.Log.LogInfo((object)"Sale Star spent, but no recently aimed-at chest to blame.");
				}
				return;
			}
			ChestTier chestTier = ChestTiers.Get(val);
			PurchaseInteraction bestExcluding = scanner.GetBestExcluding(val);
			ChestTier chestTier2 = (Object.op_Implicit((Object)(object)bestExcluding) ? ChestTiers.Get(bestExcluding) : ChestTier.Unknown);
			if (settings.DebugLogging.Value)
			{
				SaleStarReminderPlugin.Log.LogInfo((object)($"Sale Star spent on {((Object)((Component)val).gameObject).name} ({val.displayNameToken}, tier {chestTier}); best other" + string.Format(" chest: {0} (tier {1}).", Object.op_Implicit((Object)(object)bestExcluding) ? bestExcluding.displayNameToken : "none", chestTier2)));
			}
			if (settings.MockSmallChest.Value && ChestTiers.IsMockable(chestTier) && chestTier2 >= ChestTier.Large)
			{
				bool value = settings.MockIncludeVulgar.Value;
				string text = MockLines.PickSmallChest(value);
				if (text != null)
				{
					string text2 = (settings.ShowMostValuableChest.Value ? MockLines.PickBetterChest(value, ChestScanner.GetDisplayName(bestExcluding)) : null);
					mockLine = text;
					mockSubline = text2;
					mockUntil = Time.time + settings.MockDurationSeconds.Value;
				}
			}
		}
	}
	[BepInPlugin("sevenstack.SaleStarReminder", "SaleStarReminder", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class SaleStarReminderPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "sevenstack.SaleStarReminder";

		public const string PluginName = "SaleStarReminder";

		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);
			MockLines.Load(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location));
			if (RiskOfOptionsCompat.Enabled)
			{
				RiskOfOptionsCompat.Register(Settings);
			}
			HUD.onHudTargetChangedGlobal += OnHudTargetChanged;
		}

		private void OnDestroy()
		{
			HUD.onHudTargetChangedGlobal -= OnHudTargetChanged;
		}

		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.DumpChestsKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
					CharacterMaster val = ((firstLocalUser != null) ? firstLocalUser.cachedMaster : null);
					ChestScanner.DumpAll(Object.op_Implicit((Object)(object)val) ? val.inventory : null, (firstLocalUser != null) ? firstLocalUser.cachedBody : null);
				}
			}
		}

		private static void OnHudTargetChanged(HUD hud)
		{
			if (Object.op_Implicit((Object)(object)hud) && !Object.op_Implicit((Object)(object)((Component)hud).GetComponent<SaleStarReminderController>()))
			{
				((Component)hud).gameObject.AddComponent<SaleStarReminderController>();
			}
		}
	}
	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)
		{
		}
	}
}