Decompiled source of CyberpunkScoutTendrils v0.1.0

BepInEx/plugins/CyberpunkScoutTendrils/CyberpunkScoutTendrils.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CyberpunkScoutTendrils.Config;
using CyberpunkScoutTendrils.Runtime;
using HarmonyLib;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("CyberpunkScoutTendrils")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CyberpunkScoutTendrils")]
[assembly: AssemblyTitle("CyberpunkScoutTendrils")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace CyberpunkScoutTendrils
{
	[BepInPlugin("HawChow.CyberpunkScoutTendrils", "CyberpunkScoutTendrils", "0.1.0")]
	public sealed class Plugin : BasePlugin
	{
		public const string PluginGuid = "HawChow.CyberpunkScoutTendrils";

		public const string PluginName = "CyberpunkScoutTendrils";

		public const string PluginVersion = "0.1.0";

		private static Plugin? _instance;

		private readonly ScoutTendrilEffect _effect = new ScoutTendrilEffect();

		private Harmony? _harmony;

		private FileSystemWatcher? _configWatcher;

		private ConfigEntry<bool>? _enabled;

		private ConfigEntry<bool>? _gradientEnabled;

		private ConfigEntry<bool>? _breathEnabled;

		private ConfigEntry<bool>? _glowEnabled;

		private ConfigEntry<string>? _colors;

		private ConfigEntry<string>? _colorOrder;

		private ConfigEntry<string>? _intervalMode;

		private ConfigEntry<double>? _fixedInterval;

		private ConfigEntry<double>? _randomMin;

		private ConfigEntry<double>? _randomMax;

		private ConfigEntry<double>? _gradientDuration;

		private ConfigEntry<double>? _breathPeriod;

		private ConfigEntry<double>? _breathMin;

		private ConfigEntry<double>? _breathMax;

		private ConfigEntry<double>? _glowIntensity;

		private CyberpunkSettings _settings = CyberpunkSettings.Defaults;

		private int _reloadRequested;

		private long _lastReloadSignal;

		private int _patchInstalled;

		private int _patchWarningLogged;

		private int _patchFailureLogged;

		public override void Load()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			_instance = this;
			_harmony = new Harmony("HawChow.CyberpunkScoutTendrils");
			BindConfiguration();
			_settings = ReadSettings();
			StartConfigWatcher();
			AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
			TryInstallScoutPatch();
			((BasePlugin)this).Log.LogInfo((object)"CyberpunkScoutTendrils loaded; only Scout active tendrils are targeted.");
		}

		public override bool Unload()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
			_configWatcher?.Dispose();
			_configWatcher = null;
			try
			{
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = ((BasePlugin)this).Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(24, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Harmony cleanup failed: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogWarning(val);
			}
			_effect.RestoreAll();
			_harmony = null;
			Interlocked.Exchange(ref _patchInstalled, 0);
			if (_instance == this)
			{
				_instance = null;
			}
			return true;
		}

		private void BindConfiguration()
		{
			_enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch for Scout tendril colors and glow.");
			_gradientEnabled = ((BasePlugin)this).Config.Bind<bool>("Animation", "GradientEnabled", true, "Blend smoothly between palette colors.");
			_breathEnabled = ((BasePlugin)this).Config.Bind<bool>("Animation", "BreathEnabled", true, "Pulse color brightness over time.");
			_glowEnabled = ((BasePlugin)this).Config.Bind<bool>("Glow", "Enabled", true, "Apply emissive color through renderer property blocks.");
			_colors = ((BasePlugin)this).Config.Bind<string>("Colors", "Palette", "#00F5FF,#FF2BD6,#B8FF00,#8A5CFF", "Comma/newline-separated #RRGGBB or #RRGGBBAA colors; add, remove, or edit entries freely.");
			_colorOrder = ((BasePlugin)this).Config.Bind<string>("Colors", "Order", "Fixed", "Palette order: Fixed or Random.");
			_intervalMode = ((BasePlugin)this).Config.Bind<string>("Animation", "IntervalMode", "Fixed", "Color interval mode: Fixed or Random.");
			_fixedInterval = ((BasePlugin)this).Config.Bind<double>("Animation", "FixedIntervalSeconds", 2.5, "Seconds between color changes when IntervalMode=Fixed (0.05–3600).");
			_randomMin = ((BasePlugin)this).Config.Bind<double>("Animation", "RandomIntervalMinSeconds", 1.0, "Minimum random color interval in seconds (0.05–3600).");
			_randomMax = ((BasePlugin)this).Config.Bind<double>("Animation", "RandomIntervalMaxSeconds", 4.0, "Maximum random color interval in seconds (0.05–3600).");
			_gradientDuration = ((BasePlugin)this).Config.Bind<double>("Animation", "GradientDurationSeconds", 1.25, "Gradient duration in seconds (0–3600); capped by the selected color interval.");
			_breathPeriod = ((BasePlugin)this).Config.Bind<double>("Animation", "BreathPeriodSeconds", 2.4, "Breathing cycle duration in seconds (0.1–3600).");
			_breathMin = ((BasePlugin)this).Config.Bind<double>("Animation", "BreathMinIntensity", 0.45, "Brightness multiplier at the dimmest point (0–16).");
			_breathMax = ((BasePlugin)this).Config.Bind<double>("Animation", "BreathMaxIntensity", 1.1, "Brightness multiplier at the brightest point (0–16).");
			_glowIntensity = ((BasePlugin)this).Config.Bind<double>("Glow", "Intensity", 3.0, "Emissive RGB multiplier (0–32); requires a compatible tendril shader and game bloom for post-process halos.");
		}

		private CyberpunkSettings ReadSettings()
		{
			return CyberpunkSettings.Normalize(new SettingsInput
			{
				Enabled = (_enabled?.Value ?? true),
				GradientEnabled = (_gradientEnabled?.Value ?? true),
				BreathEnabled = (_breathEnabled?.Value ?? true),
				GlowEnabled = (_glowEnabled?.Value ?? true),
				ColorList = (_colors?.Value ?? "#00F5FF,#FF2BD6,#B8FF00,#8A5CFF"),
				ColorOrder = (_colorOrder?.Value ?? "Fixed"),
				IntervalMode = (_intervalMode?.Value ?? "Fixed"),
				FixedIntervalSeconds = (_fixedInterval?.Value ?? 2.5),
				RandomIntervalMinSeconds = (_randomMin?.Value ?? 1.0),
				RandomIntervalMaxSeconds = (_randomMax?.Value ?? 4.0),
				GradientDurationSeconds = (_gradientDuration?.Value ?? 1.25),
				BreathPeriodSeconds = (_breathPeriod?.Value ?? 2.4),
				BreathMinIntensity = (_breathMin?.Value ?? 0.45),
				BreathMaxIntensity = (_breathMax?.Value ?? 1.1),
				GlowIntensity = (_glowIntensity?.Value ?? 3.0)
			}, delegate(string message)
			{
				((BasePlugin)this).Log.LogWarning((object)message);
			});
		}

		private void StartConfigWatcher()
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			try
			{
				string fullPath = Path.GetFullPath(((BasePlugin)this).Config.ConfigFilePath);
				string directoryName = Path.GetDirectoryName(fullPath);
				string fileName = Path.GetFileName(fullPath);
				if (!string.IsNullOrEmpty(directoryName) && !string.IsNullOrEmpty(fileName))
				{
					_configWatcher = new FileSystemWatcher(directoryName, fileName)
					{
						NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite),
						IncludeSubdirectories = false,
						EnableRaisingEvents = false
					};
					_configWatcher.Changed += OnConfigFileChanged;
					_configWatcher.Created += OnConfigFileChanged;
					_configWatcher.Renamed += OnConfigFileRenamed;
					_configWatcher.EnableRaisingEvents = true;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = ((BasePlugin)this).Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(70, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Config file watcher unavailable; restart may be required after edits: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogWarning(val);
			}
		}

		private void OnConfigFileChanged(object sender, FileSystemEventArgs args)
		{
			MarkConfigReload();
		}

		private void OnConfigFileRenamed(object sender, RenamedEventArgs args)
		{
			MarkConfigReload();
		}

		private void MarkConfigReload()
		{
			Interlocked.Exchange(ref _lastReloadSignal, Stopwatch.GetTimestamp());
			Interlocked.Exchange(ref _reloadRequested, 1);
		}

		private void ProcessPendingConfigReload()
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			if (Interlocked.Exchange(ref _reloadRequested, 0) == 0)
			{
				return;
			}
			long num = Interlocked.Read(in _lastReloadSignal);
			if (Stopwatch.GetTimestamp() - num < Stopwatch.Frequency / 3)
			{
				Interlocked.Exchange(ref _reloadRequested, 1);
				return;
			}
			try
			{
				((BasePlugin)this).Config.Reload();
				_settings = ReadSettings();
				((BasePlugin)this).Log.LogInfo((object)"CyberpunkScoutTendrils config reloaded.");
			}
			catch (Exception ex)
			{
				ManualLogSource log = ((BasePlugin)this).Log;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(61, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Config reload failed; keeping the previous runtime settings: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogWarning(val);
			}
		}

		private void OnAssemblyLoad(object? sender, AssemblyLoadEventArgs args)
		{
			TryInstallScoutPatch();
		}

		private void TryInstallScoutPatch()
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			if (_harmony == null || Interlocked.CompareExchange(ref _patchInstalled, 1, 0) != 0)
			{
				return;
			}
			try
			{
				Type type = ReflectionAccess.FindLoadedType("Enemies.ScoutAntennaDetection");
				MethodInfo methodInfo = ((type == null) ? null : ReflectionAccess.FindInstanceMethod(type, "UpdateTendrilState"));
				MethodInfo method = typeof(Plugin).GetMethod("ScoutUpdatePostfix", BindingFlags.Static | BindingFlags.NonPublic);
				if (methodInfo == null || method == null)
				{
					Interlocked.Exchange(ref _patchInstalled, 0);
					if (type != null && Interlocked.Exchange(ref _patchWarningLogged, 1) == 0)
					{
						((BasePlugin)this).Log.LogWarning((object)"ScoutAntennaDetection.UpdateTendrilState was not found; waiting for game assembly load.");
					}
				}
				else
				{
					_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					((BasePlugin)this).Log.LogInfo((object)"Installed Scout-only UpdateTendrilState postfix.");
				}
			}
			catch (Exception ex)
			{
				Interlocked.Exchange(ref _patchInstalled, 0);
				if (Interlocked.Exchange(ref _patchFailureLogged, 1) == 0)
				{
					ManualLogSource log = ((BasePlugin)this).Log;
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(57, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Scout hook installation failed; plugin remains inactive: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					log.LogWarning(val);
				}
			}
		}

		private static void ScoutUpdatePostfix(object __instance)
		{
			_instance?.OnScoutUpdated(__instance);
		}

		private void OnScoutUpdated(object scout)
		{
			ProcessPendingConfigReload();
			double now = (double)Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
			_effect.Apply(scout, _settings, now, delegate(string message)
			{
				((BasePlugin)this).Log.LogWarning((object)message);
			});
		}
	}
}
namespace CyberpunkScoutTendrils.Runtime
{
	public sealed class ColorCycleEngine
	{
		private readonly Random _random;

		private CyberpunkSettings? _settings;

		private bool _initialized;

		private int _currentIndex;

		private int _targetIndex;

		private RgbaColor _from;

		private RgbaColor _to;

		private double _transitionStart;

		private double _transitionDuration;

		private double _nextSwitchAt;

		private double _nextInterval;

		public int CurrentIndex => _currentIndex;

		public int TargetIndex => _targetIndex;

		public double LastIntervalSeconds => _nextInterval;

		public ColorCycleEngine(int? randomSeed = null)
		{
			_random = (randomSeed.HasValue ? new Random(randomSeed.Value) : new Random());
		}

		public void Configure(CyberpunkSettings settings, double now)
		{
			_settings = settings ?? throw new ArgumentNullException("settings");
			_currentIndex = ((settings.ColorOrder == ColorOrderMode.Random && settings.Colors.Count > 1) ? _random.Next(settings.Colors.Count) : 0);
			_targetIndex = _currentIndex;
			_from = (_to = settings.Colors[_currentIndex]);
			_transitionStart = now;
			_transitionDuration = 0.0;
			_nextInterval = ChooseInterval(settings);
			_nextSwitchAt = now + _nextInterval;
			_initialized = true;
		}

		public ColorSample Sample(double now)
		{
			if (_settings == null)
			{
				throw new InvalidOperationException("Configure must be called before Sample.");
			}
			CyberpunkSettings settings = _settings;
			if (!_initialized)
			{
				Configure(settings, now);
			}
			int num = 0;
			while (now >= _nextSwitchAt && num++ < 2048)
			{
				double nextSwitchAt = _nextSwitchAt;
				_currentIndex = _targetIndex;
				_from = _to;
				_targetIndex = ChooseNextIndex(settings, _currentIndex);
				_to = settings.Colors[_targetIndex];
				_transitionStart = nextSwitchAt;
				_nextInterval = ChooseInterval(settings);
				_transitionDuration = (settings.GradientEnabled ? Math.Min(settings.GradientDurationSeconds, _nextInterval) : 0.0);
				_nextSwitchAt = nextSwitchAt + _nextInterval;
			}
			if (num >= 2048 && now >= _nextSwitchAt)
			{
				_transitionStart = now;
				_nextSwitchAt = now + ChooseInterval(settings);
			}
			float value = ((_transitionDuration <= 0.0) ? 1f : ((float)((now - _transitionStart) / _transitionDuration)));
			value = RgbaColor.Clamp01(value);
			ColorSample result = ColorSample.Lerp(ColorSample.From(_from), ColorSample.From(_to), value);
			if (!settings.BreathEnabled)
			{
				return result;
			}
			double num2 = 0.5 - 0.5 * Math.Cos(Math.PI * 2.0 * now / settings.BreathPeriodSeconds);
			float multiplier = (float)(settings.BreathMinIntensity + (settings.BreathMaxIntensity - settings.BreathMinIntensity) * num2);
			return result.MultiplyRgb(multiplier);
		}

		private int ChooseNextIndex(CyberpunkSettings settings, int current)
		{
			if (settings.Colors.Count <= 1)
			{
				return 0;
			}
			if (settings.ColorOrder == ColorOrderMode.Fixed)
			{
				return (current + 1) % settings.Colors.Count;
			}
			int num = _random.Next(settings.Colors.Count - 1);
			if (num < current)
			{
				return num;
			}
			return num + 1;
		}

		private double ChooseInterval(CyberpunkSettings settings)
		{
			if (settings.IntervalMode != ColorIntervalMode.Fixed)
			{
				return settings.RandomIntervalMinSeconds + _random.NextDouble() * (settings.RandomIntervalMaxSeconds - settings.RandomIntervalMinSeconds);
			}
			return settings.FixedIntervalSeconds;
		}
	}
	internal static class ReflectionAccess
	{
		private const BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly string[] TraversalNames = new string[18]
		{
			"gameObject", "m_gameObject", "renderer", "m_renderer", "renderers", "m_renderers", "transform", "m_transform", "visual", "m_visual",
			"model", "m_model", "meshRenderer", "m_meshRenderer", "antenna", "m_antenna", "tendril", "m_tendril"
		};

		public static Type? FindLoadedType(string fullName)
		{
			Type type = Type.GetType(fullName, throwOnError: false, ignoreCase: false);
			if (type != null)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					Type type2 = assembly.GetType(fullName, throwOnError: false, ignoreCase: false);
					if (type2 != null)
					{
						return type2;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		public static MethodInfo? FindInstanceMethod(Type type, string name)
		{
			return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
		}

		public static MemberInfo? FindMember(Type? type, string name)
		{
			Type type2 = type;
			while (type2 != null)
			{
				try
				{
					FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						return field;
					}
					PropertyInfo property = type2.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && property.GetIndexParameters().Length == 0)
					{
						return property;
					}
				}
				catch
				{
				}
				type2 = type2.BaseType;
			}
			return null;
		}

		public static object? GetValue(MemberInfo? member, object instance)
		{
			try
			{
				return (member is FieldInfo fieldInfo) ? fieldInfo.GetValue(instance) : ((!(member is PropertyInfo propertyInfo) || !(propertyInfo.GetMethod != null)) ? null : propertyInfo.GetValue(instance));
			}
			catch
			{
				return null;
			}
		}

		public static Type? GetMemberType(MemberInfo? member)
		{
			if (!(member is FieldInfo fieldInfo))
			{
				if (member is PropertyInfo propertyInfo)
				{
					return propertyInfo.PropertyType;
				}
				return null;
			}
			return fieldInfo.FieldType;
		}

		public static bool TrySetValue(MemberInfo? member, object instance, object? value)
		{
			try
			{
				if (!(member is FieldInfo fieldInfo))
				{
					if (member is PropertyInfo propertyInfo && propertyInfo.SetMethod != null)
					{
						propertyInfo.SetValue(instance, value);
						return true;
					}
				}
				else if (!fieldInfo.IsInitOnly)
				{
					fieldInfo.SetValue(instance, value);
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		public static IReadOnlyList<object> ReadActiveTendrils(object scout)
		{
			return ReadCollection(GetValue(FindMember(scout.GetType(), "m_activeTendrils"), scout), 2048);
		}

		public static IReadOnlyList<object> ReadCollection(object? collection, int maxItems = 256)
		{
			if (collection == null || maxItems <= 0)
			{
				return Array.Empty<object>();
			}
			List<object> list = new List<object>();
			if (collection is IEnumerable enumerable)
			{
				try
				{
					foreach (object item in enumerable)
					{
						if (item != null)
						{
							list.Add(item);
						}
						if (list.Count >= maxItems)
						{
							break;
						}
					}
					return list;
				}
				catch
				{
					list.Clear();
				}
			}
			try
			{
				Type type = collection.GetType();
				int val = Convert.ToInt32(GetValue(FindMember(type, "Count"), collection) ?? GetValue(FindMember(type, "Length"), collection) ?? ((object)0));
				PropertyInfo property = type.GetProperty("Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property?.GetMethod == null)
				{
					return list;
				}
				for (int i = 0; i < Math.Min(val, maxItems); i++)
				{
					object value = property.GetValue(collection, new object[1] { i });
					if (value != null)
					{
						list.Add(value);
					}
				}
			}
			catch
			{
			}
			return list;
		}

		public static long Identity(object instance)
		{
			try
			{
				object value = GetValue(FindMember(instance.GetType(), "Pointer"), instance);
				if (value is IntPtr intPtr && intPtr != IntPtr.Zero)
				{
					return intPtr.ToInt64();
				}
				if (value != null)
				{
					long num = Convert.ToInt64(value);
					if (num != 0L)
					{
						return num;
					}
				}
			}
			catch
			{
			}
			return RuntimeHelpers.GetHashCode(instance);
		}

		public static IReadOnlyList<object> FindRenderers(object tendril)
		{
			Type type = FindLoadedType("UnityEngine.Renderer");
			if (type == null)
			{
				return Array.Empty<object>();
			}
			Dictionary<long, object> dictionary = new Dictionary<long, object>();
			HashSet<int> hashSet = new HashSet<int>();
			Stack<(object, int)> stack = new Stack<(object, int)>();
			stack.Push((tendril, 0));
			int num = 256;
			while (stack.Count > 0 && num-- > 0)
			{
				(object, int) tuple = stack.Pop();
				object item = tuple.Item1;
				int item2 = tuple.Item2;
				Type type2 = item.GetType();
				if (type.IsInstanceOfType(item) || HasTypeName(type2, "UnityEngine.Renderer"))
				{
					dictionary[Identity(item)] = item;
				}
				else
				{
					if (item2 >= 5 || IsLeaf(type2) || !hashSet.Add(RuntimeHelpers.GetHashCode(item)))
					{
						continue;
					}
					string[] traversalNames = TraversalNames;
					foreach (string text in traversalNames)
					{
						MemberInfo memberInfo = FindMember(type2, text);
						if (memberInfo == null)
						{
							continue;
						}
						object value = GetValue(memberInfo, item);
						if (value == null || value == item)
						{
							continue;
						}
						if (text.IndexOf("gameObject", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("transform", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							foreach (object componentsInChild in GetComponentsInChildren(value, type))
							{
								dictionary[Identity(componentsInChild)] = componentsInChild;
							}
						}
						PushChild(value, item2 + 1, stack, 64);
					}
				}
			}
			return dictionary.Values.ToArray();
		}

		private static IReadOnlyList<object> GetComponentsInChildren(object gameObject, Type rendererType)
		{
			MethodInfo[] methods;
			try
			{
				methods = gameObject.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			}
			catch
			{
				return Array.Empty<object>();
			}
			MethodInfo[] array = methods;
			foreach (MethodInfo methodInfo in array)
			{
				if (methodInfo.Name != "GetComponentsInChildren")
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				object obj2 = null;
				try
				{
					if (methodInfo.IsGenericMethodDefinition && parameters.Length == 1 && parameters[0].ParameterType == typeof(bool))
					{
						obj2 = methodInfo.MakeGenericMethod(rendererType).Invoke(gameObject, new object[1] { true });
					}
					else if (parameters.Length == 2 && parameters[1].ParameterType == typeof(bool))
					{
						object obj3 = ((parameters[0].ParameterType == typeof(Type)) ? rendererType : MakeIl2CppType(rendererType));
						if (obj3 != null && parameters[0].ParameterType.IsInstanceOfType(obj3))
						{
							obj2 = methodInfo.Invoke(gameObject, new object[2] { obj3, true });
						}
					}
				}
				catch
				{
					continue;
				}
				if (obj2 != null)
				{
					IReadOnlyList<object> readOnlyList = ReadCollection(obj2, 128);
					if (readOnlyList.Count > 0)
					{
						return readOnlyList;
					}
				}
			}
			return Array.Empty<object>();
		}

		private static object? MakeIl2CppType(Type type)
		{
			try
			{
				return (FindLoadedType("Il2CppInterop.Runtime.Il2CppType")?.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "Of" && m.IsGenericMethodDefinition && m.GetParameters().Length == 0))?.MakeGenericMethod(type).Invoke(null, null);
			}
			catch
			{
				return null;
			}
		}

		private static void PushChild(object child, int depth, Stack<(object Value, int Depth)> pending, int maxItems)
		{
			if (child is string || IsLeaf(child.GetType()))
			{
				return;
			}
			if (child is IEnumerable enumerable)
			{
				int num = 0;
				try
				{
					foreach (object item in enumerable)
					{
						if (item != null)
						{
							pending.Push((item, depth));
						}
						if (++num >= maxItems)
						{
							break;
						}
					}
					return;
				}
				catch
				{
				}
			}
			pending.Push((child, depth));
		}

		private static bool IsLeaf(Type type)
		{
			if (!type.IsPrimitive && !type.IsEnum && !(type == typeof(string)) && !(type == typeof(IntPtr)) && !(type == typeof(UIntPtr)) && !(type == typeof(decimal)))
			{
				return type == typeof(DateTime);
			}
			return true;
		}

		private static bool HasTypeName(Type type, string fullName)
		{
			Type type2 = type;
			while (type2 != null)
			{
				if (string.Equals(type2.FullName, fullName, StringComparison.Ordinal))
				{
					return true;
				}
				type2 = type2.BaseType;
			}
			return false;
		}
	}
	internal sealed class ScoutTendrilEffect
	{
		private sealed class TendrilState
		{
			public object Tendril;

			public MemberInfo ColorMember;

			public object? OriginalColor;
		}

		private sealed class RendererState
		{
			public object Renderer;

			public object OriginalBlock;

			public object WorkingBlock;

			public MethodInfo GetBlock;

			public MethodInfo SetBlock;

			public MethodInfo SetColor;

			public string PropertyName = string.Empty;
		}

		private sealed class ScoutState
		{
			public readonly Dictionary<long, TendrilState> Tendrils = new Dictionary<long, TendrilState>();

			public readonly Dictionary<long, RendererState> Renderers = new Dictionary<long, RendererState>();
		}

		private readonly Dictionary<long, ScoutState> _scouts = new Dictionary<long, ScoutState>();

		private readonly HashSet<string> _warned = new HashSet<string>(StringComparer.Ordinal);

		private readonly ColorCycleEngine _cycle = new ColorCycleEngine();

		private CyberpunkSettings? _settings;

		public void Apply(object scoutInstance, CyberpunkSettings settings, double now, Action<string> logWarning)
		{
			if (!settings.Enabled)
			{
				RestoreAll();
				_settings = settings;
				return;
			}
			if (_settings != settings)
			{
				_settings = settings;
				_cycle.Configure(settings, now);
			}
			long key = ReflectionAccess.Identity(scoutInstance);
			if (!_scouts.TryGetValue(key, out ScoutState value))
			{
				value = new ScoutState();
				_scouts.Add(key, value);
			}
			HashSet<long> hashSet = new HashSet<long>();
			HashSet<long> hashSet2 = new HashSet<long>();
			ColorSample color = _cycle.Sample(now);
			foreach (object item in ReflectionAccess.ReadActiveTendrils(scoutInstance))
			{
				MemberInfo memberInfo = ReflectionAccess.FindMember(item.GetType(), "m_colorDefault");
				Type memberType = ReflectionAccess.GetMemberType(memberInfo);
				if (memberInfo == null || memberType == null)
				{
					WarnOnce("color-member", "Could not find Scout tendril m_colorDefault; tint update skipped.", logWarning);
					continue;
				}
				long num = ReflectionAccess.Identity(item);
				hashSet.Add(num);
				if (!value.Tendrils.TryGetValue(num, out TendrilState value2))
				{
					value2 = new TendrilState
					{
						Tendril = item,
						ColorMember = memberInfo,
						OriginalColor = ReflectionAccess.GetValue(memberInfo, item)
					};
					value.Tendrils[num] = value2;
				}
				object obj = CreateUnityColor(memberType, color);
				if (obj != null && !ReflectionAccess.TrySetValue(memberInfo, item, obj))
				{
					WarnOnce("color-write", "Could not write Scout tendril m_colorDefault; renderer glow may still apply.", logWarning);
				}
				if (!settings.GlowEnabled || settings.GlowIntensity <= 0.0)
				{
					continue;
				}
				ColorSample color2 = color.MultiplyRgb((float)settings.GlowIntensity);
				foreach (object item2 in ReflectionAccess.FindRenderers(item))
				{
					long num2 = ReflectionAccess.Identity(item2);
					if (TryApplyEmission(item2, memberType, color2, num2, value, out string error))
					{
						hashSet2.Add(num2);
					}
					else if (!string.IsNullOrEmpty(error))
					{
						WarnOnce("glow-" + error, error, logWarning);
					}
				}
			}
			CleanupStaleTendrils(value, hashSet);
			if (settings.GlowEnabled && settings.GlowIntensity > 0.0)
			{
				CleanupStaleRenderers(value, hashSet2);
			}
			else
			{
				RestoreRenderers(value);
			}
			if (value.Tendrils.Count == 0 && value.Renderers.Count == 0)
			{
				_scouts.Remove(key);
			}
		}

		public void RestoreAll()
		{
			foreach (ScoutState value in _scouts.Values)
			{
				foreach (TendrilState value2 in value.Tendrils.Values)
				{
					if (value2.OriginalColor != null)
					{
						ReflectionAccess.TrySetValue(value2.ColorMember, value2.Tendril, value2.OriginalColor);
					}
				}
				RestoreRenderers(value);
			}
			_scouts.Clear();
		}

		private bool TryApplyEmission(object renderer, Type colorType, ColorSample color, long key, ScoutState scoutState, out string error)
		{
			error = string.Empty;
			try
			{
				object material = ReflectionAccess.GetValue(ReflectionAccess.FindMember(renderer.GetType(), "sharedMaterial"), renderer);
				if (material == null)
				{
					return false;
				}
				MethodInfo hasProperty = material.GetType().GetMethod("HasProperty", new Type[1] { typeof(string) });
				if (hasProperty == null)
				{
					return false;
				}
				string text = new string[2] { "_EmissiveColor", "_EmissionColor" }.FirstOrDefault((string name) => (bool)(hasProperty.Invoke(material, new object[1] { name }) ?? ((object)false)));
				if (text == null)
				{
					return false;
				}
				if (!scoutState.Renderers.TryGetValue(key, out RendererState value))
				{
					Type type = ReflectionAccess.FindLoadedType("UnityEngine.MaterialPropertyBlock");
					if (type == null)
					{
						error = "UnityEngine.MaterialPropertyBlock type is unavailable.";
						return false;
					}
					MethodInfo methodInfo = FindPropertyBlockMethod(renderer.GetType(), "GetPropertyBlock", type);
					MethodInfo methodInfo2 = FindPropertyBlockMethod(renderer.GetType(), "SetPropertyBlock", type);
					MethodInfo method = type.GetMethod("SetColor", new Type[2]
					{
						typeof(string),
						colorType
					});
					if (methodInfo == null || methodInfo2 == null || method == null)
					{
						error = "Renderer emissive property-block API is unavailable.";
						return false;
					}
					object obj = Activator.CreateInstance(type);
					object obj2 = Activator.CreateInstance(type);
					if (obj == null || obj2 == null)
					{
						error = "MaterialPropertyBlock construction failed.";
						return false;
					}
					methodInfo.Invoke(renderer, new object[1] { obj });
					value = new RendererState
					{
						Renderer = renderer,
						OriginalBlock = obj,
						WorkingBlock = obj2,
						GetBlock = methodInfo,
						SetBlock = methodInfo2,
						SetColor = method,
						PropertyName = text
					};
					scoutState.Renderers[key] = value;
				}
				value.GetBlock.Invoke(renderer, new object[1] { value.WorkingBlock });
				object obj3 = CreateUnityColor(colorType, color);
				if (obj3 == null)
				{
					return false;
				}
				value.SetColor.Invoke(value.WorkingBlock, new object[2] { value.PropertyName, obj3 });
				value.SetBlock.Invoke(renderer, new object[1] { value.WorkingBlock });
				return true;
			}
			catch (TargetInvocationException ex)
			{
				error = "Emissive property-block call failed: " + (ex.InnerException?.GetType().Name ?? ex.GetType().Name);
				return false;
			}
			catch (Exception ex2)
			{
				error = "Emissive renderer update failed: " + ex2.GetType().Name;
				return false;
			}
		}

		private static MethodInfo? FindPropertyBlockMethod(Type rendererType, string methodName, Type blockType)
		{
			Type type = rendererType;
			while (type != null)
			{
				MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo candidate) => candidate.Name == methodName && candidate.GetParameters().Length == 1 && candidate.GetParameters()[0].ParameterType.IsAssignableFrom(blockType));
				if (methodInfo != null)
				{
					return methodInfo;
				}
				type = type.BaseType;
			}
			return null;
		}

		private static object? CreateUnityColor(Type colorType, ColorSample color)
		{
			try
			{
				return Activator.CreateInstance(colorType, color.R, color.G, color.B, color.A);
			}
			catch
			{
				try
				{
					object obj2 = Activator.CreateInstance(colorType);
					if (obj2 == null)
					{
						return null;
					}
					SetFloatMember(obj2, "r", color.R);
					SetFloatMember(obj2, "g", color.G);
					SetFloatMember(obj2, "b", color.B);
					SetFloatMember(obj2, "a", color.A);
					return obj2;
				}
				catch
				{
					return null;
				}
			}
		}

		private static void SetFloatMember(object instance, string name, float value)
		{
			MemberInfo memberInfo = ReflectionAccess.FindMember(instance.GetType(), name);
			if (memberInfo is FieldInfo fieldInfo)
			{
				fieldInfo.SetValue(instance, value);
			}
			else if (memberInfo is PropertyInfo propertyInfo && propertyInfo.SetMethod != null)
			{
				propertyInfo.SetValue(instance, value);
			}
		}

		private static void CleanupStaleTendrils(ScoutState scout, HashSet<long> seen)
		{
			long[] array = scout.Tendrils.Keys.Where((long item) => !seen.Contains(item)).ToArray();
			foreach (long key in array)
			{
				TendrilState tendrilState = scout.Tendrils[key];
				if (tendrilState.OriginalColor != null)
				{
					ReflectionAccess.TrySetValue(tendrilState.ColorMember, tendrilState.Tendril, tendrilState.OriginalColor);
				}
				scout.Tendrils.Remove(key);
			}
		}

		private static void CleanupStaleRenderers(ScoutState scout, HashSet<long> seen)
		{
			long[] array = scout.Renderers.Keys.Where((long item) => !seen.Contains(item)).ToArray();
			foreach (long key in array)
			{
				RestoreRenderer(scout, key);
			}
		}

		private static void RestoreRenderers(ScoutState scout)
		{
			long[] array = scout.Renderers.Keys.ToArray();
			foreach (long key in array)
			{
				RestoreRenderer(scout, key);
			}
		}

		private static void RestoreRenderer(ScoutState scout, long key)
		{
			if (scout.Renderers.TryGetValue(key, out RendererState value))
			{
				try
				{
					value.SetBlock.Invoke(value.Renderer, new object[1] { value.OriginalBlock });
				}
				catch
				{
				}
				scout.Renderers.Remove(key);
			}
		}

		private void WarnOnce(string key, string message, Action<string> logWarning)
		{
			if (_warned.Add(key))
			{
				logWarning(message);
			}
		}
	}
}
namespace CyberpunkScoutTendrils.Config
{
	public readonly struct RgbaColor : IEquatable<RgbaColor>
	{
		public readonly byte R;

		public readonly byte G;

		public readonly byte B;

		public readonly byte A;

		public float Red => (float)(int)R / 255f;

		public float Green => (float)(int)G / 255f;

		public float Blue => (float)(int)B / 255f;

		public float Alpha => (float)(int)A / 255f;

		public RgbaColor(byte r, byte g, byte b, byte a = byte.MaxValue)
		{
			R = r;
			G = g;
			B = b;
			A = a;
		}

		public static bool TryParse(string? text, out RgbaColor color)
		{
			color = default(RgbaColor);
			if (text == null)
			{
				return false;
			}
			string text2 = text.Trim();
			if ((text2.Length != 7 && text2.Length != 9) || text2[0] != '#')
			{
				return false;
			}
			if (!byte.TryParse(text2.Substring(1, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result) || !byte.TryParse(text2.Substring(3, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result2) || !byte.TryParse(text2.Substring(5, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result3))
			{
				return false;
			}
			byte result4 = byte.MaxValue;
			if (text2.Length == 9 && !byte.TryParse(text2.Substring(7, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out result4))
			{
				return false;
			}
			color = new RgbaColor(result, result2, result3, result4);
			return true;
		}

		public string ToHex(bool includeAlpha = false)
		{
			if (!includeAlpha && A == byte.MaxValue)
			{
				return $"#{R:X2}{G:X2}{B:X2}";
			}
			return $"#{R:X2}{G:X2}{B:X2}{A:X2}";
		}

		public static RgbaColor Lerp(RgbaColor from, RgbaColor to, float t)
		{
			t = Clamp01(t);
			return new RgbaColor(ToByte((float)(int)from.R + (float)(to.R - from.R) * t), ToByte((float)(int)from.G + (float)(to.G - from.G) * t), ToByte((float)(int)from.B + (float)(to.B - from.B) * t), ToByte((float)(int)from.A + (float)(to.A - from.A) * t));
		}

		private static byte ToByte(float value)
		{
			return (byte)Math.Min(255, Math.Max(0, (int)Math.Round(value, MidpointRounding.AwayFromZero)));
		}

		public static float Clamp01(float value)
		{
			if (!(value < 0f))
			{
				if (!(value > 1f))
				{
					return value;
				}
				return 1f;
			}
			return 0f;
		}

		public bool Equals(RgbaColor other)
		{
			if (R == other.R && G == other.G && B == other.B)
			{
				return A == other.A;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is RgbaColor other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (((((R * 397) ^ G) * 397) ^ B) * 397) ^ A;
		}

		public override string ToString()
		{
			return ToHex(A != byte.MaxValue);
		}
	}
	public readonly struct ColorSample
	{
		public readonly float R;

		public readonly float G;

		public readonly float B;

		public readonly float A;

		public ColorSample(float r, float g, float b, float a)
		{
			R = r;
			G = g;
			B = b;
			A = a;
		}

		public static ColorSample From(RgbaColor color, float multiplier = 1f)
		{
			return new ColorSample(color.Red * multiplier, color.Green * multiplier, color.Blue * multiplier, color.Alpha);
		}

		public static ColorSample Lerp(ColorSample from, ColorSample to, float t)
		{
			t = RgbaColor.Clamp01(t);
			return new ColorSample(from.R + (to.R - from.R) * t, from.G + (to.G - from.G) * t, from.B + (to.B - from.B) * t, from.A + (to.A - from.A) * t);
		}

		public ColorSample MultiplyRgb(float multiplier)
		{
			return new ColorSample(R * multiplier, G * multiplier, B * multiplier, A);
		}
	}
	public enum ColorOrderMode
	{
		Fixed,
		Random
	}
	public enum ColorIntervalMode
	{
		Fixed,
		Random
	}
	public sealed class SettingsInput
	{
		public bool Enabled { get; set; } = true;

		public bool GradientEnabled { get; set; } = true;

		public bool BreathEnabled { get; set; } = true;

		public bool GlowEnabled { get; set; } = true;

		public string ColorList { get; set; } = "#00F5FF,#FF2BD6,#B8FF00,#8A5CFF";

		public string ColorOrder { get; set; } = "Fixed";

		public string IntervalMode { get; set; } = "Fixed";

		public double FixedIntervalSeconds { get; set; } = 2.5;

		public double RandomIntervalMinSeconds { get; set; } = 1.0;

		public double RandomIntervalMaxSeconds { get; set; } = 4.0;

		public double GradientDurationSeconds { get; set; } = 1.25;

		public double BreathPeriodSeconds { get; set; } = 2.4;

		public double BreathMinIntensity { get; set; } = 0.45;

		public double BreathMaxIntensity { get; set; } = 1.1;

		public double GlowIntensity { get; set; } = 3.0;
	}
	public sealed class CyberpunkSettings
	{
		private static readonly RgbaColor[] DefaultPalette = new RgbaColor[4]
		{
			new RgbaColor(0, 245, byte.MaxValue),
			new RgbaColor(byte.MaxValue, 43, 214),
			new RgbaColor(184, byte.MaxValue, 0),
			new RgbaColor(138, 92, byte.MaxValue)
		};

		public bool Enabled { get; }

		public bool GradientEnabled { get; }

		public bool BreathEnabled { get; }

		public bool GlowEnabled { get; }

		public IReadOnlyList<RgbaColor> Colors { get; }

		public ColorOrderMode ColorOrder { get; }

		public ColorIntervalMode IntervalMode { get; }

		public double FixedIntervalSeconds { get; }

		public double RandomIntervalMinSeconds { get; }

		public double RandomIntervalMaxSeconds { get; }

		public double GradientDurationSeconds { get; }

		public double BreathPeriodSeconds { get; }

		public double BreathMinIntensity { get; }

		public double BreathMaxIntensity { get; }

		public double GlowIntensity { get; }

		public static CyberpunkSettings Defaults => Normalize(new SettingsInput());

		private CyberpunkSettings(SettingsInput input, List<RgbaColor> colors, ColorOrderMode order, ColorIntervalMode interval, double fixedInterval, double randomMin, double randomMax, double gradientDuration, double breathPeriod, double breathMin, double breathMax, double glowIntensity)
		{
			Enabled = input.Enabled;
			GradientEnabled = input.GradientEnabled;
			BreathEnabled = input.BreathEnabled;
			GlowEnabled = input.GlowEnabled;
			Colors = colors.AsReadOnly();
			ColorOrder = order;
			IntervalMode = interval;
			FixedIntervalSeconds = fixedInterval;
			RandomIntervalMinSeconds = randomMin;
			RandomIntervalMaxSeconds = randomMax;
			GradientDurationSeconds = gradientDuration;
			BreathPeriodSeconds = breathPeriod;
			BreathMinIntensity = breathMin;
			BreathMaxIntensity = breathMax;
			GlowIntensity = glowIntensity;
		}

		public static CyberpunkSettings Normalize(SettingsInput? input, Action<string>? warning = null)
		{
			if (input == null)
			{
				input = new SettingsInput();
			}
			List<RgbaColor> list = ParseColorList(input.ColorList, warning);
			if (list.Count == 0)
			{
				warning?.Invoke("Colors had no valid Hex entries; built-in neon palette restored.");
				list.AddRange(DefaultPalette);
			}
			ColorOrderMode order = ParseEnum(input.ColorOrder, ColorOrderMode.Fixed, "ColorOrder", warning);
			ColorIntervalMode interval = ParseEnum(input.IntervalMode, ColorIntervalMode.Fixed, "IntervalMode", warning);
			double fixedInterval = ClampFinite(input.FixedIntervalSeconds, 2.5, 0.05, 3600.0, "FixedIntervalSeconds", warning);
			double num = ClampFinite(input.RandomIntervalMinSeconds, 1.0, 0.05, 3600.0, "RandomIntervalMinSeconds", warning);
			double num2 = ClampFinite(input.RandomIntervalMaxSeconds, 4.0, 0.05, 3600.0, "RandomIntervalMaxSeconds", warning);
			if (num > num2)
			{
				warning?.Invoke("Random interval minimum exceeded maximum; values swapped.");
				double num3 = num2;
				num2 = num;
				num = num3;
			}
			double gradientDuration = ClampFinite(input.GradientDurationSeconds, 1.25, 0.0, 3600.0, "GradientDurationSeconds", warning);
			double breathPeriod = ClampFinite(input.BreathPeriodSeconds, 2.4, 0.1, 3600.0, "BreathPeriodSeconds", warning);
			double num4 = ClampFinite(input.BreathMinIntensity, 0.45, 0.0, 16.0, "BreathMinIntensity", warning);
			double num5 = ClampFinite(input.BreathMaxIntensity, 1.1, 0.0, 16.0, "BreathMaxIntensity", warning);
			if (num4 > num5)
			{
				warning?.Invoke("Breath minimum intensity exceeded maximum; values swapped.");
				double num6 = num5;
				num5 = num4;
				num4 = num6;
			}
			double glowIntensity = ClampFinite(input.GlowIntensity, 3.0, 0.0, 32.0, "GlowIntensity", warning);
			return new CyberpunkSettings(input, list, order, interval, fixedInterval, num, num2, gradientDuration, breathPeriod, num4, num5, glowIntensity);
		}

		public static List<RgbaColor> ParseColorList(string? text, Action<string>? warning = null)
		{
			List<RgbaColor> list = new List<RgbaColor>();
			string[] array = (text ?? string.Empty).Split(new char[4] { ',', ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (RgbaColor.TryParse(text2, out var color))
				{
					list.Add(color);
				}
				else
				{
					warning?.Invoke("Ignored invalid color '" + text2 + "'. Use #RRGGBB or #RRGGBBAA.");
				}
			}
			return list;
		}

		private static T ParseEnum<T>(string? raw, T fallback, string key, Action<string>? warning) where T : struct
		{
			if (Enum.TryParse<T>(raw?.Trim(), ignoreCase: true, out var result) && Enum.IsDefined(typeof(T), result))
			{
				return result;
			}
			warning?.Invoke($"Invalid {key} '{raw}'; using {fallback}.");
			return fallback;
		}

		private static double ClampFinite(double value, double fallback, double min, double max, string key, Action<string>? warning)
		{
			if (double.IsNaN(value) || double.IsInfinity(value))
			{
				warning?.Invoke($"Invalid {key}; using {fallback}.");
				value = fallback;
			}
			double num = ((value < min) ? min : ((value > max) ? max : value));
			if (Math.Abs(num - value) > 1E-09)
			{
				warning?.Invoke($"{key} clamped to [{min}, {max}].");
			}
			return num;
		}
	}
}