Decompiled source of SoloCrates v1.2.1

BepInEx/plugins/SoloCrates.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
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.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SoloShared;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("HappyTheLegend")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 HappyTheLegend")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+9aa58e96d80619b89d8c2741314ca206bd368d36")]
[assembly: AssemblyProduct("SoloCrates")]
[assembly: AssemblyTitle("SoloCrates")]
[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 SoloShared
{
	internal static class Loc
	{
		private static Dictionary<string, string> _table = new Dictionary<string, string>(StringComparer.Ordinal);

		private static string _folder;

		private static string _mod = "Solo";

		private static string _loaded = "";

		private static float _nextCheck;

		private static Action<string> _log = delegate
		{
		};

		private static Action<string> _warn = delegate
		{
		};

		private static readonly SortedSet<string> _seen = new SortedSet<string>(StringComparer.Ordinal);

		private static bool _collect;

		internal static string Language
		{
			get
			{
				if (_loaded.Length != 0)
				{
					return _loaded;
				}
				return "en";
			}
		}

		internal static void Init(string modName, string dllPath, Action<string> log, Action<string> warn, bool collectStrings = false)
		{
			_mod = modName ?? "Solo";
			_log = log ?? ((Action<string>)delegate
			{
			});
			_warn = warn ?? ((Action<string>)delegate
			{
			});
			_collect = collectStrings;
			try
			{
				_folder = Path.GetDirectoryName(dllPath);
			}
			catch
			{
				_folder = null;
			}
			Reload(CurrentLanguage());
		}

		internal static string T(string english)
		{
			if (string.IsNullOrEmpty(english))
			{
				return english;
			}
			if (_collect)
			{
				lock (_seen)
				{
					_seen.Add(english);
				}
			}
			if (!_table.TryGetValue(english, out var value) || value.Length <= 0)
			{
				return english;
			}
			return value;
		}

		internal static string F(string english, params object[] args)
		{
			string format = T(english);
			try
			{
				return string.Format(format, args);
			}
			catch
			{
				try
				{
					return string.Format(english, args);
				}
				catch
				{
					return english;
				}
			}
		}

		internal static void Tick(float unscaledTime)
		{
			if (!(unscaledTime < _nextCheck))
			{
				_nextCheck = unscaledTime + 2f;
				string text = CurrentLanguage();
				if (text != _loaded)
				{
					Reload(text);
				}
			}
		}

		private static string CurrentLanguage()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Locale selectedLocale = LocalizationSettings.SelectedLocale;
				object obj;
				if (!((Object)(object)selectedLocale != (Object)null))
				{
					obj = "";
				}
				else
				{
					LocaleIdentifier identifier = selectedLocale.Identifier;
					obj = ((LocaleIdentifier)(ref identifier)).Code;
				}
				string text = (string)obj;
				return string.IsNullOrEmpty(text) ? "en" : text;
			}
			catch
			{
				return "en";
			}
		}

		private static void Reload(string code)
		{
			_loaded = code ?? "en";
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			if (!string.IsNullOrEmpty(_folder) && !_loaded.StartsWith("en", StringComparison.OrdinalIgnoreCase))
			{
				string text = Path.Combine(_folder, "lang");
				foreach (string item in Candidates(_loaded, text))
				{
					string path = Path.Combine(text, item + ".txt");
					if (File.Exists(path))
					{
						int num = Read(path, dictionary);
						_log($"[{_mod}] Language {item}: {num} line(s) from {Path.GetFileName(path)}.");
						break;
					}
				}
			}
			_table = dictionary;
			if (dictionary.Count > 0)
			{
				_log("[" + _mod + "] Following the game's language: " + _loaded + ".");
			}
			if (dictionary.Count == 0 && !_loaded.StartsWith("en", StringComparison.OrdinalIgnoreCase))
			{
				_log("[" + _mod + "] No translation file for '" + _loaded + "' — showing English. Add lang/" + _loaded + ".txt beside the mod to change that.");
			}
		}

		private static IEnumerable<string> Candidates(string code, string dir)
		{
			yield return code;
			string language = code;
			int num = code.IndexOf('-');
			if (num > 0)
			{
				language = code.Substring(0, num);
				yield return language;
			}
			string[] array;
			try
			{
				array = (Directory.Exists(dir) ? Directory.GetFiles(dir, "*.txt") : new string[0]);
			}
			catch
			{
				yield break;
			}
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(array2[i]);
				if (fileNameWithoutExtension.StartsWith(language + "-", StringComparison.OrdinalIgnoreCase))
				{
					yield return fileNameWithoutExtension;
				}
			}
		}

		private static int Read(string path, Dictionary<string, string> into)
		{
			int num = 0;
			try
			{
				string text = null;
				string[] array = File.ReadAllLines(path, Encoding.UTF8);
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = array[i].Trim();
					if (text2.Length == 0 || text2[0] == '#')
					{
						continue;
					}
					if (text2[0] == '=')
					{
						text = Unescape(text2.Substring(1).Trim());
					}
					else if (text2[0] == '>' && text != null)
					{
						string text3 = Unescape(text2.Substring(1).Trim());
						if (text3.Length > 0)
						{
							into[text] = text3;
							num++;
						}
						text = null;
					}
				}
			}
			catch (Exception ex)
			{
				_warn("[" + _mod + "] Couldn't read " + Path.GetFileName(path) + ": " + ex.Message);
			}
			return num;
		}

		private static string Unescape(string s)
		{
			return s.Replace("\\n", "\n");
		}

		private static string Escape(string s)
		{
			return s.Replace("\n", "\\n");
		}

		internal static void DumpSeen()
		{
			if (!_collect || string.IsNullOrEmpty(_folder))
			{
				return;
			}
			try
			{
				string text = Path.Combine(_folder, "lang");
				Directory.CreateDirectory(text);
				string path = Path.Combine(text, "_english.txt");
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("# Every line " + _mod + " has shown this session.");
				stringBuilder.AppendLine("# Copy this file to <language code>.txt and fill in the > lines.");
				stringBuilder.AppendLine("# Leave a > line empty to keep the English.");
				stringBuilder.AppendLine();
				lock (_seen)
				{
					foreach (string item in _seen)
					{
						stringBuilder.Append("= ").AppendLine(Escape(item));
						stringBuilder.AppendLine("> ");
					}
					File.WriteAllText(path, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
					_log($"[{_mod}] Wrote {_seen.Count} line(s) to lang/_english.txt.");
				}
			}
			catch (Exception ex)
			{
				_warn("[" + _mod + "] Couldn't write the English list: " + ex.Message);
			}
		}
	}
}
namespace SoloCrates
{
	internal static class CrateSettings
	{
		[HarmonyPatch(typeof(MenuPageSettingsPage), "Start")]
		internal static class GameplayPagePatch
		{
			private static void Postfix(MenuPageSettingsPage __instance)
			{
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Invalid comparison between Unknown and I4
				if (!Plugin.Ready || (Object)(object)__instance == (Object)null || !Plugin.ShowInGameSettings.Value || (int)__instance.settingType != 1)
				{
					return;
				}
				try
				{
					((MonoBehaviour)__instance).StartCoroutine(AddTo(__instance));
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("[SoloCrates] Gameplay page hook failed: " + ex.Message));
				}
			}
		}

		private const string Tag = "SoloCrates Row";

		private const string Prefix = "";

		internal static readonly int[] KeepSteps = new int[22]
		{
			1, 2, 3, 4, 5, 6, 8, 10, 12, 15,
			20, 25, 30, 40, 50, 75, 100, 150, 200, 300,
			500, 1000
		};

		private static IEnumerator AddTo(MenuPageSettingsPage page)
		{
			yield return null;
			yield return null;
			Loc.Tick(Time.unscaledTime);
			if ((Object)(object)page == (Object)null || ((Component)page).GetComponentsInChildren<Transform>(true).Any((Transform t) => (Object)(object)t != (Object)null && ((Object)t).name == "SoloCrates Row"))
			{
				yield break;
			}
			MenuSlider[] componentsInChildren = ((Component)page).GetComponentsInChildren<MenuSlider>(true);
			MenuSlider val = ((IEnumerable<MenuSlider>)componentsInChildren).FirstOrDefault((Func<MenuSlider, bool>)((MenuSlider s) => (Object)(object)s != (Object)null && !s.hasCustomOptions && ((Object)s).name != "SoloCrates Row")) ?? componentsInChildren.FirstOrDefault();
			MenuTwoOptions val2 = ((IEnumerable<MenuTwoOptions>)((Component)page).GetComponentsInChildren<MenuTwoOptions>(true)).FirstOrDefault((Func<MenuTwoOptions, bool>)((MenuTwoOptions t) => (Object)(object)t != (Object)null && ((Object)t).name != "SoloCrates Row"));
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[SoloCrates] Gameplay page has no row to copy — leaving it alone.");
				yield break;
			}
			List<Transform> list = (from t in ((Component)page).GetComponentsInChildren<Transform>(true)
				where (Object)(object)t != (Object)null && ((Object)(object)((Component)t).GetComponent<MenuSlider>() != (Object)null || (Object)(object)((Component)t).GetComponent<MenuTwoOptions>() != (Object)null)
				orderby t.localPosition.y descending
				select t).ToList();
			Transform obj = list[list.Count - 1];
			Transform parent = obj.parent;
			float num = ((list.Count > 1) ? Mathf.Abs(list[0].localPosition.y - list[1].localPosition.y) : 34f);
			if (num < 1f)
			{
				num = 34f;
			}
			float y = obj.localPosition.y;
			foreach (Transform item in parent)
			{
				Transform val3 = item;
				if ((Object)(object)val3 != (Object)null && ((Object)val3).name.EndsWith(" Row", StringComparison.Ordinal) && val3.localPosition.y < y)
				{
					y = val3.localPosition.y;
				}
			}
			int num2 = 0;
			y -= num * 1.5f;
			Header((Component)(object)val2, TitleOf((Component)(object)val2), parent, y, "SOLOCRATES", major: true, "SoloCrates Row");
			y -= num;
			if (AddSlider(val, parent, y, "Crates You Can Keep", KeepSteps.Select((int n) => (n < 1000) ? n.ToString() : "1000 (no cap)").ToArray(), () => Nearest(KeepSteps, Plugin.MaxCosmeticsPerLevel.Value), delegate(int i)
			{
				Plugin.MaxCosmeticsPerLevel.Value = KeepSteps[i];
			}))
			{
				num2++;
			}
			y -= num;
			if (AddSlider(val, parent, y, "Extra Crates Per Spawn", (from n in Enumerable.Range(0, 21)
				select (n != 0) ? n.ToString() : "OFF").ToArray(), () => Mathf.Clamp(Plugin.ExtraPerCrate.Value, 0, 20), delegate(int i)
			{
				Plugin.ExtraPerCrate.Value = i;
			}))
			{
				num2++;
			}
			y -= num;
			if (AddToggle(val2, parent, y, "Extra Crates Only When Hosting", () => Plugin.HostOnlyEverything.Value, delegate(bool v)
			{
				Plugin.HostOnlyEverything.Value = v;
			}))
			{
				num2++;
			}
			MenuScrollBox[] componentsInChildren2 = ((Component)page).GetComponentsInChildren<MenuScrollBox>(true);
			foreach (MenuScrollBox val4 in componentsInChildren2)
			{
				if (!((Object)(object)val4 == (Object)null))
				{
					try
					{
						val4.RecalculateScrollHeight();
					}
					catch
					{
					}
				}
			}
			Plugin.Log.LogInfo((object)$"[SoloCrates] Added {num2} row(s) to the game's Gameplay settings.");
		}

		private static int Nearest(int[] steps, int value)
		{
			int num = 0;
			for (int i = 0; i < steps.Length; i++)
			{
				if (Mathf.Abs(steps[i] - value) < Mathf.Abs(steps[num] - value))
				{
					num = i;
				}
			}
			return num;
		}

		internal static GameObject CloneDormant(Component template, Transform parent, float y, string tag)
		{
			//IL_003a: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			GameObject gameObject = template.gameObject;
			bool activeSelf = gameObject.activeSelf;
			gameObject.SetActive(false);
			GameObject val;
			try
			{
				val = Object.Instantiate<GameObject>(gameObject, parent);
			}
			finally
			{
				gameObject.SetActive(activeSelf);
			}
			((Object)val).name = tag;
			val.transform.localPosition = new Vector3(template.transform.localPosition.x, y, template.transform.localPosition.z);
			val.transform.localScale = template.transform.localScale;
			val.transform.localRotation = template.transform.localRotation;
			MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val2 in componentsInChildren)
			{
				if (!((Object)(object)val2 == (Object)null) && ((object)val2).GetType().Name.IndexOf("Localize", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					Object.DestroyImmediate((Object)(object)val2);
				}
			}
			return val;
		}

		private static bool AddSlider(MenuSlider template, Transform parent, float y, string name, string[] labels, Func<int> read, Action<int> write)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			try
			{
				GameObject val = CloneDormant((Component)(object)template, parent, y, "SoloCrates Row");
				MenuSlider component = val.GetComponent<MenuSlider>();
				if ((Object)(object)component == (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
					return false;
				}
				Strip<MenuSetting>(val);
				Strip<MenuInputPercentSetting>(val);
				Strip<MenuBigSettingText>(val);
				component.onChange = new UnityEvent();
				component.customOptions = new List<CustomOption>();
				component.hasCustomOptions = false;
				component.hasCustomValues = false;
				component.hasBar = true;
				component.wrapAround = false;
				component.buttonSegmentJump = 1;
				component.pointerSegmentJump = 1;
				component.startValue = 0;
				component.endValue = labels.Length - 1;
				component.stringAtStartOfValue = "";
				component.stringAtEndOfValue = "";
				component.elementName = Loc.T(name) ?? "";
				int num = Mathf.Clamp(read(), 0, labels.Length - 1);
				component.settingsValue = (float)num / (float)(labels.Length - 1);
				PruneClicks(val, (Component)(object)component, "OnIncrease", "OnDecrease");
				val.AddComponent<CrateSliderRow>().Bind(component, Loc.T(name) ?? "", labels, write);
				val.SetActive(true);
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloCrates] Couldn't add the '" + name + "' row: " + ex.Message));
				return false;
			}
		}

		private static bool AddToggle(MenuTwoOptions template, Transform parent, float y, string name, Func<bool> read, Action<bool> write)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Expected O, but got Unknown
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			try
			{
				GameObject val = CloneDormant((Component)(object)template, parent, y, "SoloCrates Row");
				MenuTwoOptions two = val.GetComponent<MenuTwoOptions>();
				if ((Object)(object)two == (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
					return false;
				}
				two.onOption1 = new UnityEvent();
				two.onOption2 = new UnityEvent();
				two.fetchSetting = new UnityEvent();
				two.customEvents = true;
				two.customFetch = true;
				two.settingSet = false;
				two.settingName = Loc.T(name) ?? "";
				two.fetchSetting.AddListener((UnityAction)delegate
				{
					try
					{
						two.startSettingFetch = read();
					}
					catch
					{
					}
				});
				two.onOption1.AddListener((UnityAction)delegate
				{
					try
					{
						write(obj: true);
					}
					catch
					{
					}
				});
				two.onOption2.AddListener((UnityAction)delegate
				{
					try
					{
						write(obj: false);
					}
					catch
					{
					}
				});
				PruneClicks(val, (Component)(object)two, "OnOption1", "OnOption2");
				val.AddComponent<CrateToggleRow>().Bind(two, Loc.T(name) ?? "");
				val.SetActive(true);
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloCrates] Couldn't add the '" + name + "' row: " + ex.Message));
				return false;
			}
		}

		internal static TextMeshProUGUI TitleOf(Component row)
		{
			HashSet<TextMeshProUGUI> exclude = new HashSet<TextMeshProUGUI>();
			MenuTwoOptions val = (MenuTwoOptions)(object)((row is MenuTwoOptions) ? row : null);
			if (val != null)
			{
				exclude.Add(val.option1TextMesh);
				exclude.Add(val.option2TextMesh);
			}
			MenuBigButton component = row.GetComponent<MenuBigButton>();
			if ((Object)(object)component != (Object)null && (Object)(object)component.menuButton != (Object)null)
			{
				exclude.Add(component.menuButton.buttonText);
			}
			return ((IEnumerable<TextMeshProUGUI>)row.GetComponentsInChildren<TextMeshProUGUI>(true)).FirstOrDefault((Func<TextMeshProUGUI, bool>)((TextMeshProUGUI t) => (Object)(object)t != (Object)null && !exclude.Contains(t)));
		}

		internal static void Header(Component row, TextMeshProUGUI title, Transform parent, float y, string text, bool major, string tag)
		{
			//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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0042: 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_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)row == (Object)null || (Object)(object)title == (Object)null || (Object)(object)parent == (Object)null)
			{
				return;
			}
			try
			{
				Vector3 val = parent.InverseTransformPoint(((TMP_Text)title).transform.position);
				float x = val.x;
				float num = val.y - row.transform.localPosition.y;
				GameObject val2 = Object.Instantiate<GameObject>(((Component)title).gameObject, parent);
				((Object)val2).name = tag;
				foreach (Transform item in ((IEnumerable)val2.transform).Cast<Transform>().ToList())
				{
					Object.DestroyImmediate((Object)(object)((Component)item).gameObject);
				}
				Component[] components = val2.GetComponents<Component>();
				foreach (Component val3 in components)
				{
					if (!((Object)(object)val3 == (Object)null) && !(val3 is Transform) && !(val3 is TextMeshProUGUI) && !(val3 is CanvasRenderer))
					{
						Object.DestroyImmediate((Object)(object)val3);
					}
				}
				val2.transform.localPosition = new Vector3(x, y + num, val.z);
				val2.transform.localRotation = ((((TMP_Text)title).transform.rotation == Quaternion.identity) ? Quaternion.identity : (Quaternion.Inverse(parent.rotation) * ((TMP_Text)title).transform.rotation));
				val2.transform.localScale = (Vector3)((((TMP_Text)title).transform.lossyScale.x > 0f && parent.lossyScale.x > 0f) ? new Vector3(((TMP_Text)title).transform.lossyScale.x / parent.lossyScale.x, ((TMP_Text)title).transform.lossyScale.y / parent.lossyScale.y, 1f) : Vector3.one);
				TextMeshProUGUI component = val2.GetComponent<TextMeshProUGUI>();
				text = Loc.T(text);
				((TMP_Text)component).text = (major ? text : ("— " + text + " —"));
				((TMP_Text)component).fontStyle = (FontStyles)1;
				((TMP_Text)component).enableWordWrapping = false;
				((TMP_Text)component).overflowMode = (TextOverflowModes)0;
				if (major)
				{
					((TMP_Text)component).fontSize = ((TMP_Text)title).fontSize * 1.25f;
					((Graphic)component).color = new Color(0.68f, 0.84f, 0.51f, 1f);
				}
				else
				{
					((Graphic)component).color = new Color(((Graphic)component).color.r, ((Graphic)component).color.g, ((Graphic)component).color.b, ((Graphic)component).color.a * 0.75f);
				}
				val2.SetActive(true);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloCrates] Couldn't add the '" + text + "' heading: " + ex.Message));
			}
		}

		private static void PruneClicks(GameObject clone, Component keep, params string[] keepMethods)
		{
			MenuButton[] componentsInChildren = clone.GetComponentsInChildren<MenuButton>(true);
			foreach (MenuButton val in componentsInChildren)
			{
				ButtonClickedEvent val2 = (((Object)(object)val != (Object)null && (Object)(object)val.button != (Object)null) ? val.button.onClick : null);
				if (val2 == null)
				{
					continue;
				}
				for (int j = 0; j < ((UnityEventBase)val2).GetPersistentEventCount(); j++)
				{
					if ((object)((UnityEventBase)val2).GetPersistentTarget(j) != keep || Array.IndexOf(keepMethods, ((UnityEventBase)val2).GetPersistentMethodName(j)) < 0)
					{
						((UnityEventBase)val2).SetPersistentListenerState(j, (UnityEventCallState)0);
					}
				}
			}
		}

		private static void Strip<T>(GameObject go) where T : Component
		{
			T[] componentsInChildren = go.GetComponentsInChildren<T>(true);
			foreach (T val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
			}
		}

		private static string[] Steps(int first, int step, int count, string suffix)
		{
			string[] array = new string[count];
			for (int i = 0; i < count; i++)
			{
				array[i] = first + i * step + suffix;
			}
			return array;
		}
	}
	internal sealed class CrateSliderRow : MonoBehaviour
	{
		private MenuSlider _slider;

		private string _name;

		private string[] _labels;

		private Action<int> _write;

		private int _shown = -1;

		public void Bind(MenuSlider slider, string name, string[] labels, Action<int> write)
		{
			_slider = slider;
			_name = name;
			_labels = labels;
			_write = write;
		}

		private void LateUpdate()
		{
			if ((Object)(object)_slider == (Object)null || _labels == null)
			{
				return;
			}
			if ((Object)(object)_slider.elementNameText != (Object)null && ((TMP_Text)_slider.elementNameText).text != _name)
			{
				((TMP_Text)_slider.elementNameText).text = _name;
			}
			int num = Mathf.Clamp(_slider.currentValue, 0, _labels.Length - 1);
			if (_shown < 0)
			{
				_shown = num;
				try
				{
					_slider.SetBarScaleInstant();
				}
				catch
				{
				}
			}
			else if (num != _shown)
			{
				_shown = num;
				try
				{
					_write(num);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("[SoloCrates] '" + _name + "' failed to apply: " + ex.Message));
				}
			}
			string text = _labels[num];
			if ((Object)(object)_slider.segmentText != (Object)null && ((TMP_Text)_slider.segmentText).text != text)
			{
				((TMP_Text)_slider.segmentText).text = text;
			}
			if ((Object)(object)_slider.segmentMaskText != (Object)null && ((TMP_Text)_slider.segmentMaskText).text != text)
			{
				((TMP_Text)_slider.segmentMaskText).text = text;
			}
		}
	}
	internal sealed class CrateToggleRow : MonoBehaviour
	{
		private string _name;

		private TextMeshProUGUI[] _titles = (TextMeshProUGUI[])(object)new TextMeshProUGUI[0];

		public void Bind(MenuTwoOptions two, string name)
		{
			_name = name;
			_titles = (from t in ((Component)two).GetComponentsInChildren<TextMeshProUGUI>(true)
				where (Object)(object)t != (Object)null && (Object)(object)t != (Object)(object)two.option1TextMesh && (Object)(object)t != (Object)(object)two.option2TextMesh
				select t).ToArray();
			LateUpdate();
		}

		private void LateUpdate()
		{
			TextMeshProUGUI[] titles = _titles;
			foreach (TextMeshProUGUI val in titles)
			{
				if ((Object)(object)val != (Object)null && ((TMP_Text)val).text != _name)
				{
					((TMP_Text)val).text = _name;
				}
			}
		}
	}
	internal static class HostGate
	{
		public static bool Active
		{
			get
			{
				if (!Plugin.Ready)
				{
					return false;
				}
				if (!Plugin.HostOnlyEverything.Value)
				{
					return true;
				}
				try
				{
					return SemiFunc.IsMasterClientOrSingleplayer();
				}
				catch
				{
					return false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(CosmeticWorldObject), "ExtractRPC")]
	internal static class CrateExtractWindowPatch
	{
		internal static bool InExtract;

		private static void Prefix()
		{
			InExtract = true;
		}

		private static void Finalizer()
		{
			InExtract = false;
		}
	}
	[HarmonyPatch(typeof(ValuableDirector), "CosmeticWorldObjectLevelLoopsClampedGet")]
	internal static class CrateCapPatch
	{
		private static void Postfix(ref int __result)
		{
			if (Plugin.Ready && CrateExtractWindowPatch.InExtract)
			{
				__result = Mathf.Max(__result, Plugin.MaxCosmeticsPerLevel.Value);
			}
		}
	}
	[HarmonyPatch(typeof(ValuableDirector), "SpawnCosmeticWorldObject")]
	internal static class CrateMultiplierPatch
	{
		private static bool _reentry;

		private static void Postfix(ValuableDirector __instance, Rarity _cosmeticRarity, int[] _volumeIndex, List<ValuableVolume>[] _volumes)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (_reentry || !HostGate.Active)
			{
				return;
			}
			int value = Plugin.ExtraPerCrate.Value;
			if (value <= 0)
			{
				return;
			}
			_reentry = true;
			try
			{
				for (int i = 0; i < value; i++)
				{
					__instance.SpawnCosmeticWorldObject(_cosmeticRarity, _volumeIndex, _volumes);
				}
			}
			finally
			{
				_reentry = false;
			}
		}
	}
	[BepInPlugin("com.solo.repo.solocrates", "SoloCrates", "1.2.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.solo.repo.solocrates";

		public const string Name = "SoloCrates";

		public const string Version = "1.2.1";

		internal static ManualLogSource Log;

		internal static bool Ready;

		internal static ConfigEntry<bool> HostOnlyEverything;

		internal static ConfigEntry<int> ExtraPerCrate;

		internal static ConfigEntry<int> MaxCosmeticsPerLevel;

		internal static ConfigEntry<bool> ShowInGameSettings;

		internal static ConfigEntry<bool> DumpTranslationList;

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			HostOnlyEverything = ((BaseUnityPlugin)this).Config.Bind<bool>("Host", "HostOnlyEverything", true, "Only spawn extra crates when you are hosting or playing alone — spawning is the host's job in R.E.P.O. The higher extraction limit is not affected: it only decides which of the crates in the level count towards YOUR cosmetics, so it works as a guest too.");
			Log = ((BaseUnityPlugin)this).Logger;
			ExtraPerCrate = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ExtraCratesPerSpawn", 0, new ConfigDescription("For every cosmetic crate the game spawns naturally, spawn this many extra. Real total is still capped by how many cosmetic spawn spots the level has. 0 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>()));
			MaxCosmeticsPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxCosmeticsPerLevel", 20, new ConfigDescription("How many cosmetic crates you're allowed to EXTRACT per level. Vanilla caps this very low (crates past the cap silently give no token), so this is the setting that actually controls how many cosmetics you get.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>()));
			ShowInGameSettings = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowInGameSettings", true, "Add these settings to the game's own Gameplay settings page, under a SOLOCRATES heading.");
			Ready = true;
			DumpTranslationList = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "WriteTranslationList", false, "Write every line this mod shows to lang/_english.txt as you play, ready to be copied into a new language file. For translating; off otherwise.");
			Loc.Init("SoloCrates", ((BaseUnityPlugin)this).Info.Location, (Action<string>)Log.LogInfo, (Action<string>)Log.LogWarning, DumpTranslationList.Value);
			Harmony val = new Harmony("com.solo.repo.solocrates");
			val.PatchAll(typeof(Plugin).Assembly);
			int count = new List<MethodBase>(val.GetPatchedMethods()).Count;
			Log.LogInfo((object)string.Format("{0} v{1} loaded; {2} patch(es). Extra crates per spawn: {3}.", "SoloCrates", "1.2.1", count, ExtraPerCrate.Value));
			if (count == 0)
			{
				Log.LogWarning((object)"[SoloCrates] Patched nothing — the cosmetic spawn hook did not apply.");
			}
		}
	}
	internal static class TokenRescue
	{
		[HarmonyPatch(typeof(ResultScreenUI), "Update")]
		internal static class WatchForEnd
		{
			private static bool _wasResult;

			private static void Postfix()
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Invalid comparison between Unknown and I4
				bool flag = (Object)(object)GameDirector.instance != (Object)null && (int)GameDirector.instance.currentState == 4;
				if (_wasResult && !flag)
				{
					Flush("the result screen ended");
				}
				_wasResult = flag;
			}
		}

		[HarmonyPatch(typeof(ResultScreenUI), "Disable")]
		internal static class OnDisable
		{
			private static void Prefix()
			{
				Flush("the result screen was closed");
			}
		}

		[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
		internal static class OnLevelChange
		{
			private static void Prefix()
			{
				Flush("the level changed");
			}
		}

		internal static void Flush(string why)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Plugin.Ready)
				{
					return;
				}
				ResultScreenUI instance = ResultScreenUI.instance;
				RoundDirector instance2 = RoundDirector.instance;
				MetaManager instance3 = MetaManager.instance;
				if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || (Object)(object)instance3 == (Object)null)
				{
					return;
				}
				List<Rarity> cosmeticWorldObjectsExtracted = instance2.cosmeticWorldObjectsExtracted;
				if (cosmeticWorldObjectsExtracted == null || cosmeticWorldObjectsExtracted.Count == 0)
				{
					return;
				}
				int num = instance.cosmeticIndex;
				if (!instance.cosmeticIndexRewarded && num > 0)
				{
					num--;
				}
				int num2 = cosmeticWorldObjectsExtracted.Count - num;
				if (num2 > 0)
				{
					for (int i = num; i < cosmeticWorldObjectsExtracted.Count; i++)
					{
						instance3.CosmeticTokenAdd(cosmeticWorldObjectsExtracted[i]);
					}
					instance.cosmeticIndex = cosmeticWorldObjectsExtracted.Count;
					instance.cosmeticIndexRewarded = true;
					Plugin.Log.LogInfo((object)($"[SoloCrates] Paid out {num2} cosmetic token(s) the result screen " + "didn't reach (" + why + ")."));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[SoloCrates] Couldn't pay out the remaining tokens: " + ex.Message));
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		internal IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}