Decompiled source of Money and XP Multiplier v1.0.0

MoneyAndXpMultiplier.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using OffTheClock.Shared;
using Rewired;
using RoR2;
using RoR2.UI;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MoneyAndXpMultiplier")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MoneyAndXpMultiplier")]
[assembly: AssemblyTitle("MoneyAndXpMultiplier")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 MoneyAndXpMultiplier
{
	[BepInPlugin("offtheclock.ror2moneyxp", "Money and XP Multiplier", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static ConfigEntry<float> GoldMult;

		internal static ConfigEntry<float> XpMult;

		private ConfigEntry<string> _goldStepsText;

		private ConfigEntry<string> _xpStepsText;

		private ConfigEntry<KeyCode> _goldKey;

		private ConfigEntry<KeyCode> _xpKey;

		private ConfigEntry<bool> _showNotice;

		private float[] _goldSteps;

		private float[] _xpSteps;

		private Notice _notice;

		private bool _inRunSeen;

		private bool _keysBroken;

		private static readonly float[] DefaultGoldSteps = new float[5] { 1f, 2f, 3f, 5f, 10f };

		private static readonly float[] DefaultXpSteps = new float[5] { 1f, 2f, 3f, 5f, 10f };

		private void Awake()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Expected O, but got Unknown
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			_notice = new Notice("Money and XP Multiplier");
			GoldMult = ((BaseUnityPlugin)this).Config.Bind<float>("Multipliers", "Gold", 2f, new ConfigDescription("Gold from kills, barrels and money pickups is multiplied by this (1 = normal, 10 = ten times). Starting money, Shrine of Blood and item gold (Roll of Pennies, Brittle Crown, Executive Card) stay normal.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			XpMult = ((BaseUnityPlugin)this).Config.Bind<float>("Multipliers", "Experience", 2f, new ConfigDescription("Experience your team earns is multiplied by this (1 = normal, 10 = ten times).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			_goldKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "GoldCycleKey", (KeyCode)268, "Cycles the gold multiplier through GoldSteps. None = no key.");
			_xpKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keys", "ExperienceCycleKey", (KeyCode)267, "Cycles the experience multiplier through ExperienceSteps. None = no key.");
			_goldStepsText = ((BaseUnityPlugin)this).Config.Bind<string>("Keys", "GoldSteps", "1, 2, 3, 5, 10", "Values the gold key cycles through (1 to 10, comma-separated).");
			_xpStepsText = ((BaseUnityPlugin)this).Config.Bind<string>("Keys", "ExperienceSteps", "1, 2, 3, 5, 10", "Values the experience key cycles through (1 to 10, comma-separated).");
			_showNotice = ((BaseUnityPlugin)this).Config.Bind<bool>("Keys", "ShowOnScreenNotice", true, "Show the new value on screen for a moment after a key press (it is always written to the log).");
			ParseSteps();
			((BaseUnityPlugin)this).Config.SettingChanged += delegate(object _, SettingChangedEventArgs e)
			{
				if ((object)e.ChangedSetting == _goldStepsText || (object)e.ChangedSetting == _xpStepsText)
				{
					ParseSteps();
				}
				if ((object)e.ChangedSetting == GoldMult || (object)e.ChangedSetting == XpMult)
				{
					Patches.ValueChanged();
				}
			};
			Harmony harmony = new Harmony("offtheclock.ror2moneyxp");
			Honesty.MarkRunChanging(harmony, Log, "Money and XP Multiplier");
			bool flag = PatchGuard.Apply(harmony, Log, "Money and XP Multiplier", "RoR2.TeamManager", "GiveTeamMoney", new Type[2]
			{
				typeof(TeamIndex),
				typeof(uint)
			}, PatchGuard.Own(typeof(Patches), "GiveTeamMoney_Pre"), null);
			bool flag2 = PatchGuard.Apply(harmony, Log, "Money and XP Multiplier", "RoR2.TeamManager", "GiveTeamExperience", new Type[2]
			{
				typeof(TeamIndex),
				typeof(ulong)
			}, PatchGuard.Own(typeof(Patches), "GiveTeamExperience_Pre"), null);
			string text = (flag ? ("gold x" + Steps.F(GoldMult.Value)) : "gold INACTIVE (game function not found)") + ", " + (flag2 ? ("XP x" + Steps.F(XpMult.Value)) : "XP INACTIVE (game function not found)");
			Log.LogInfo((object)("Money and XP Multiplier 1.0.0 loaded - " + text + " in solo and local split-screen runs (never online); " + KeyCheck.KeyName(_goldKey.Value) + " cycles gold (" + Steps.Text(_goldSteps) + "), " + KeyCheck.KeyName(_xpKey.Value) + " cycles XP (" + Steps.Text(_xpSteps) + ")."));
		}

		private void ParseSteps()
		{
			_goldSteps = Steps.Parse(_goldStepsText.Value, 1f, 10f, DefaultGoldSteps, Log, "Money and XP Multiplier", "GoldSteps");
			_xpSteps = Steps.Parse(_xpStepsText.Value, 1f, 10f, DefaultXpSteps, Log, "Money and XP Multiplier", "ExperienceSteps");
		}

		private void Update()
		{
			//IL_003b: 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)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				bool inRun = Session.InRun;
				if (inRun && !_inRunSeen)
				{
					_inRunSeen = true;
					Patches.NewRun();
					KeyCheck.Check(Log, "Money and XP Multiplier", new List<KeyValuePair<string, KeyCode>>
					{
						new KeyValuePair<string, KeyCode>("gold cycle", _goldKey.Value),
						new KeyValuePair<string, KeyCode>("experience cycle", _xpKey.Value)
					});
					if (!Session.IsOffline)
					{
						Log.LogInfo((object)("Money and XP Multiplier: off in this run (" + Session.Describe() + ") - gold and XP stay normal. Works in solo and local split-screen only."));
					}
				}
				else if (!inRun && _inRunSeen)
				{
					_inRunSeen = false;
				}
			}
			catch (Exception ex)
			{
				Log.LogDebug((object)("Money and XP Multiplier: run check skipped (" + ex.Message + ")."));
			}
			if (_keysBroken)
			{
				return;
			}
			try
			{
				if (KeyCheck.Pressed(_goldKey.Value))
				{
					Cycle(gold: true);
				}
				if (KeyCheck.Pressed(_xpKey.Value))
				{
					Cycle(gold: false);
				}
			}
			catch (Exception ex2)
			{
				_keysBroken = true;
				Log.LogWarning((object)("Money and XP Multiplier: key reading stopped (" + ex2.Message + "); the config values still work."));
			}
		}

		private void Cycle(bool gold)
		{
			ConfigEntry<float> val = (gold ? GoldMult : XpMult);
			float num = (val.Value = Steps.NextCycle(gold ? _goldSteps : _xpSteps, val.Value));
			string text = (gold ? "Gold" : "XP");
			bool flag = Session.InRun && !Session.IsOffline;
			Log.LogInfo((object)("Money and XP Multiplier: " + text + " multiplier now x" + Steps.F(num) + " (cycle " + Steps.Text(gold ? _goldSteps : _xpSteps) + ")" + (flag ? " - not applied in online lobbies." : ((num <= 1f) ? " - normal amount." : "."))));
			_notice.Show(flag ? (text + " x" + Steps.F(num) + " (offline only)") : (text + " x" + Steps.F(num)));
		}

		private void OnGUI()
		{
			_notice.Draw(Log, _showNotice.Value);
		}
	}
	internal static class Patches
	{
		private static bool _loggedGold;

		private static bool _loggedXp;

		private static bool _loggedOnline;

		internal static void NewRun()
		{
			_loggedGold = false;
			_loggedXp = false;
			_loggedOnline = false;
		}

		internal static void ValueChanged()
		{
			_loggedGold = false;
			_loggedXp = false;
		}

		internal static void GiveTeamMoney_Pre(TeamIndex teamIndex, ref uint money)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			try
			{
				if ((int)teamIndex != 1 || money == 0)
				{
					return;
				}
				float value = Plugin.GoldMult.Value;
				if (value <= 1f)
				{
					return;
				}
				if (!Session.IsOffline)
				{
					LogOnlineOnce();
					return;
				}
				uint num = money;
				double num2 = Math.Round((double)num * (double)value);
				money = ((num2 >= 4294967295.0) ? uint.MaxValue : ((uint)num2));
				if (!_loggedGold)
				{
					_loggedGold = true;
					Plugin.Log.LogInfo((object)string.Format("{0}: gold x{1} active ({2} -> {3}).", "Money and XP Multiplier", Steps.F(value), num, money));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Money and XP Multiplier: gold multiplier skipped once (" + ex.Message + ")."));
			}
		}

		internal static void GiveTeamExperience_Pre(TeamIndex teamIndex, ref ulong experience)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			try
			{
				if ((int)teamIndex != 1 || experience == 0L)
				{
					return;
				}
				float value = Plugin.XpMult.Value;
				if (value <= 1f)
				{
					return;
				}
				if (!Session.IsOffline)
				{
					LogOnlineOnce();
					return;
				}
				ulong num = experience;
				double num2 = Math.Round((double)num * (double)value);
				experience = ((num2 >= 1.8446744073709552E+19) ? ulong.MaxValue : ((ulong)num2));
				if (!_loggedXp)
				{
					_loggedXp = true;
					Plugin.Log.LogInfo((object)string.Format("{0}: XP x{1} active ({2} -> {3}).", "Money and XP Multiplier", Steps.F(value), num, experience));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Money and XP Multiplier: XP multiplier skipped once (" + ex.Message + ")."));
			}
		}

		private static void LogOnlineOnce()
		{
			if (!_loggedOnline)
			{
				_loggedOnline = true;
				Plugin.Log.LogInfo((object)"Money and XP Multiplier: online lobby - gold and XP left unchanged.");
			}
		}
	}
	internal static class PluginInfo
	{
		public const string PLUGIN_GUID = "offtheclock.ror2moneyxp";

		public const string PLUGIN_NAME = "Money and XP Multiplier";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace OffTheClock.Shared
{
	internal static class Honesty
	{
		private static ManualLogSource _log;

		private static string _feature;

		private static bool _loggedBlock;

		internal static void MarkRunChanging(Harmony harmony, ManualLogSource log, string feature)
		{
			_log = log;
			_feature = feature;
			bool flag = false;
			bool flag2 = false;
			try
			{
				RoR2Application.isModded = true;
				flag = true;
			}
			catch (Exception ex)
			{
				log.LogWarning((object)(feature + ": could not set the game's modded flag: " + ex.Message));
			}
			try
			{
				MethodInfo methodInfo = AccessTools.PropertySetter(typeof(Console), "sessionCheatsEnabled");
				if (methodInfo != null)
				{
					methodInfo.Invoke(null, new object[1] { true });
					flag2 = Console.sessionCheatsEnabled;
				}
				if (!flag2)
				{
					log.LogWarning((object)(feature + ": the game's cheat flag (Console.sessionCheatsEnabled) could not be set; the leaderboard lock below still applies."));
				}
			}
			catch (Exception ex2)
			{
				log.LogWarning((object)(feature + ": could not set the game's cheat flag: " + ex2.Message));
			}
			bool flag3 = PatchGuard.Apply(harmony, log, feature, "RoR2.WeeklyRun", "GetLeaderboardName", new Type[2]
			{
				typeof(int),
				typeof(uint)
			}, null, PatchGuard.Own(typeof(Honesty), "GetLeaderboardName_Post"));
			log.LogInfo((object)(feature + ": Prismatic Trials leaderboard blocked for this session (modded flag " + (flag ? "set" : "NOT set") + ", cheat flag " + (flag2 ? "set" : "NOT set") + ", leaderboard lock " + (flag3 ? "on" : "NOT on") + ")."));
		}

		private static void GetLeaderboardName_Post(ref string __result)
		{
			try
			{
				__result = null;
				if (!_loggedBlock)
				{
					_loggedBlock = true;
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogInfo((object)(_feature + ": Prismatic Trials leaderboard name refused (modded session: no score is submitted and the leaderboard view stays empty)."));
					}
				}
			}
			catch
			{
			}
		}
	}
	internal static class KeyCheck
	{
		internal static void Check(ManualLogSource log, string feature, IList<KeyValuePair<string, KeyCode>> keys)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!ReInput.isReady)
				{
					return;
				}
				List<string> list = new List<string>();
				foreach (LocalUser readOnlyLocalUsers in LocalUserManager.readOnlyLocalUsersList)
				{
					Player val = ((readOnlyLocalUsers != null) ? readOnlyLocalUsers.inputPlayer : null);
					if (val == null)
					{
						continue;
					}
					foreach (ControllerMap allMap in val.controllers.maps.GetAllMaps((ControllerType)0))
					{
						if (allMap == null || !allMap.enabled)
						{
							continue;
						}
						foreach (ActionElementMap allMap2 in allMap.AllMaps)
						{
							if (allMap2 == null || (int)allMap2.keyCode == 0)
							{
								continue;
							}
							foreach (KeyValuePair<string, KeyCode> key in keys)
							{
								if ((int)key.Value != 0 && allMap2.keyCode == key.Value)
								{
									string text = "action " + allMap2.actionId;
									try
									{
										InputAction action = ReInput.mapping.GetAction(allMap2.actionId);
										text = ((action != null) ? action.name : null) ?? text;
									}
									catch
									{
									}
									list.Add(KeyName(key.Value) + " (" + key.Key + ") is also bound to the game's '" + text + "'");
								}
							}
						}
					}
				}
				if (list.Count == 0)
				{
					log.LogInfo((object)(feature + ": key check - " + List(keys) + " not used by your game key bindings."));
				}
				else
				{
					log.LogWarning((object)(feature + ": key clash - " + string.Join("; ", list) + ". Change the key in the config or in the game's Settings > Keyboard."));
				}
			}
			catch (Exception ex)
			{
				log.LogInfo((object)(feature + ": key check skipped (" + ex.Message + ")."));
			}
		}

		internal static string List(IList<KeyValuePair<string, KeyCode>> keys)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, KeyCode> key in keys)
			{
				if ((int)key.Value != 0)
				{
					list.Add(KeyName(key.Value));
				}
			}
			if (list.Count != 0)
			{
				return string.Join(", ", list);
			}
			return "(no keys)";
		}

		internal unsafe static string KeyName(KeyCode k)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected I4, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Invalid comparison between Unknown and I4
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Invalid comparison between Unknown and I4
			//IL_0068: 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_0070: Expected I4, but got Unknown
			if ((int)k != 0)
			{
				switch (k - 266)
				{
				case 2:
					return "Numpad *";
				case 1:
					return "Numpad /";
				case 4:
					return "Numpad +";
				case 3:
					return "Numpad -";
				case 0:
					return "Numpad .";
				case 5:
					return "Numpad Enter";
				default:
					if ((int)k >= 256 && (int)k <= 265)
					{
						return "Numpad " + (k - 256);
					}
					return ((object)(*(KeyCode*)(&k))/*cast due to .constrained prefix*/).ToString();
				}
			}
			return "(no key)";
		}

		internal static bool Pressed(KeyCode k)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			if ((int)k != 0 && Input.GetKeyDown(k))
			{
				return !Session.IsTyping;
			}
			return false;
		}
	}
	internal sealed class Notice
	{
		private const float Seconds = 2.5f;

		private readonly string _feature;

		private string _text;

		private float _until;

		private bool _broken;

		internal Notice(string feature)
		{
			_feature = feature;
		}

		internal void Show(string text)
		{
			_text = text;
			_until = Time.unscaledTime + 2.5f;
		}

		internal void Draw(ManualLogSource log, bool enabled)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			if (_broken || !enabled || _text == null || Time.unscaledTime > _until)
			{
				return;
			}
			try
			{
				float num = Mathf.Max(320f, 14f * (float)_text.Length);
				float num2 = 44f;
				Rect val = new Rect(((float)Screen.width - num) / 2f, (float)Screen.height * 0.12f, num, num2);
				GUI.Box(val, "");
				GUI.Box(val, "");
				GUIStyle label = GUI.skin.label;
				int fontSize = label.fontSize;
				TextAnchor alignment = label.alignment;
				label.fontSize = 24;
				label.alignment = (TextAnchor)4;
				GUI.Label(val, _text, label);
				label.fontSize = fontSize;
				label.alignment = alignment;
			}
			catch (Exception ex)
			{
				_broken = true;
				log.LogWarning((object)(_feature + ": on-screen notice unavailable (" + ex.Message + "); new values are shown in the log only."));
			}
		}
	}
	internal static class PatchGuard
	{
		internal static bool Apply(Harmony harmony, ManualLogSource log, string feature, string typeName, string methodName, Type[] parameters, MethodInfo prefix, MethodInfo postfix)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Type type = AccessTools.TypeByName(typeName);
				if (type == null)
				{
					log.LogWarning((object)(feature + ": game class " + typeName + " not found - this part is skipped (game update?)."));
					return false;
				}
				MethodInfo methodInfo = ((parameters == null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, parameters, (Type[])null));
				if (methodInfo == null)
				{
					log.LogWarning((object)(feature + ": game method " + typeName + "." + methodName + " not found - this part is skipped (game update?)."));
					return false;
				}
				harmony.Patch((MethodBase)methodInfo, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(prefix), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(postfix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				log.LogInfo((object)(feature + ": patched " + typeName + "." + methodName + "."));
				return true;
			}
			catch (Exception ex)
			{
				log.LogWarning((object)(feature + ": could not patch " + typeName + "." + methodName + " - this part is skipped (game update?): " + ex.Message));
				return false;
			}
		}

		internal static MethodInfo Own(Type patchClass, string name)
		{
			return AccessTools.Method(patchClass, name, (Type[])null, (Type[])null);
		}
	}
	internal static class Session
	{
		internal static bool InRun
		{
			get
			{
				try
				{
					return Object.op_Implicit((Object)(object)Run.instance);
				}
				catch
				{
					return false;
				}
			}
		}

		internal static bool IsSolo
		{
			get
			{
				try
				{
					return RoR2Application.isInSinglePlayer;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static bool IsOffline
		{
			get
			{
				try
				{
					return NetworkServer.active && NetworkServer.dontListen;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static bool IsTyping
		{
			get
			{
				try
				{
					return Object.op_Implicit((Object)(object)ConsoleWindow.instance) || ChatBox.IsActive;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static string Describe()
		{
			try
			{
				if (!InRun)
				{
					return "not in a run";
				}
				if (IsSolo)
				{
					return "solo";
				}
				if (IsOffline)
				{
					return "local split-screen";
				}
				return "online lobby";
			}
			catch (Exception)
			{
				return "unknown";
			}
		}
	}
	internal static class Steps
	{
		internal static float[] Parse(string text, float min, float max, float[] fallback, ManualLogSource log, string feature, string name)
		{
			List<float> list = new List<float>();
			string[] array = (text ?? "").Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				if (float.TryParse(array[i].Trim().TrimStart('x', 'X'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result >= min && result <= max && !list.Contains(result))
				{
					list.Add(result);
				}
			}
			if (list.Count == 0)
			{
				log.LogWarning((object)(feature + ": " + name + " has no valid values (" + F(min) + " to " + F(max) + "), using " + Text(fallback) + "."));
				return fallback;
			}
			list.Sort();
			return list.ToArray();
		}

		internal static string F(float v)
		{
			return v.ToString("0.##", CultureInfo.InvariantCulture);
		}

		internal static string Text(float[] steps)
		{
			List<string> list = new List<string>();
			foreach (float v in steps)
			{
				list.Add("x" + F(v));
			}
			return string.Join("/", list);
		}

		internal static float NextCycle(float[] steps, float current)
		{
			foreach (float num in steps)
			{
				if (num > current + 0.001f)
				{
					return num;
				}
			}
			return steps[0];
		}

		internal static float Up(float[] steps, float current)
		{
			foreach (float num in steps)
			{
				if (num > current + 0.001f)
				{
					return num;
				}
			}
			return steps[^1];
		}

		internal static float Down(float[] steps, float current)
		{
			for (int num = steps.Length - 1; num >= 0; num--)
			{
				if (steps[num] < current - 0.001f)
				{
					return steps[num];
				}
			}
			return steps[0];
		}
	}
}