Decompiled source of HornetCloakColor v1.15.0

plugins/HornetCloakColor.dll

Decompiled 2 months 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.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HornetCloakColor.Client;
using HornetCloakColor.Interop;
using HornetCloakColor.Shared;
using Microsoft.CodeAnalysis;
using Silksong.ModMenu.Elements;
using Silksong.ModMenu.Models;
using Silksong.ModMenu.Plugin;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
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: AssemblyCompany("HornetCloakColor")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.15.0.0")]
[assembly: AssemblyInformationalVersion("1.15.0+0eba9b2e5df530332fab65160f0c51fed55f93df")]
[assembly: AssemblyProduct("HornetCloakColor")]
[assembly: AssemblyTitle("HornetCloakColor")]
[assembly: InternalsVisibleTo("HornetCloakColor.SSMP")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.15.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace HornetCloakColor
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("hornet.cloak.color", "HornetCloakColor", "1.15.0")]
	public class HornetCloakColorPlugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <AllowlistMissingMaskSweepRoutine>d__18 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public float[] delaysSec;

			private float[] <>7__wrap1;

			private int <>7__wrap2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <AllowlistMissingMaskSweepRoutine>d__18(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>7__wrap1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_004e: Expected O, but got Unknown
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					goto IL_005e;
				}
				<>1__state = -1;
				<>7__wrap1 = delaysSec;
				<>7__wrap2 = 0;
				goto IL_0071;
				IL_005e:
				CloakMaskManager.SweepAllowlistedLoadedTexturesForMissingMaskDumps();
				<>7__wrap2++;
				goto IL_0071;
				IL_0071:
				if (<>7__wrap2 < <>7__wrap1.Length)
				{
					float num2 = <>7__wrap1[<>7__wrap2];
					if (num2 > 0f)
					{
						<>2__current = (object)new WaitForSecondsRealtime(num2);
						<>1__state = 1;
						return true;
					}
					goto IL_005e;
				}
				<>7__wrap1 = null;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <PostConnectSsmpColorResendRoutine>d__22 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <PostConnectSsmpColorResendRoutine>d__22(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					SSMPBridge.ResendStoredLocalColorsToServer();
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <SsmpIntegrationRetryRoutine>d__19 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public HornetCloakColorPlugin <>4__this;

			private int <i>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SsmpIntegrationRetryRoutine>d__19(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				HornetCloakColorPlugin hornetCloakColorPlugin = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<i>5__2 = 0;
					break;
				case 1:
					<>1__state = -1;
					<i>5__2++;
					break;
				}
				if (<i>5__2 < 180)
				{
					if (SSMPBridge.IsAvailable)
					{
						UsernameColorHarmonyPatcher.Apply();
					}
					if (SSMPBridge.IsAvailable && !SSMPBridge.IsRegistered)
					{
						hornetCloakColorPlugin.TryRegisterSsmpSatelliteAndLog();
					}
					hornetCloakColorPlugin.RefreshLocalUsernameVisual();
					if (SSMPBridge.IsRegistered && UsernameNetworkDelegates.TryResolveUsernameTransform != null)
					{
						hornetCloakColorPlugin.PushLocalUsernameToNetwork();
						hornetCloakColorPlugin.PushLocalCloakColorToNetwork();
						return false;
					}
					<>2__current = null;
					<>1__state = 1;
					return true;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public const string ModVersion = "1.15.0";

		public const string Id = "hornet.cloak.color";

		internal static HornetCloakColorPlugin? Instance { get; private set; }

		internal static ManualLogSource? LogSource { get; private set; }

		internal CloakColorConfig ColorConfig { get; private set; }

		internal UsernameColorConfig UsernameColorConfig { get; private set; }

		public static string Name => "HornetCloakColor";

		public static string Version => "1.15.0";

		private void Awake()
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			Instance = this;
			LogSource = ((BaseUnityPlugin)this).Logger;
			CloakPaletteConfig.Load();
			float[] missingMaskDumpAllowlistSweepDelaysSec = CloakPaletteConfig.MissingMaskDumpAllowlistSweepDelaysSec;
			if (missingMaskDumpAllowlistSweepDelaysSec != null && missingMaskDumpAllowlistSweepDelaysSec.Length != 0)
			{
				((MonoBehaviour)this).StartCoroutine(AllowlistMissingMaskSweepRoutine(missingMaskDumpAllowlistSweepDelaysSec));
			}
			CloakSceneScanner.EnsureCreated();
			ColorConfig = new CloakColorConfig(((BaseUnityPlugin)this).Config);
			UsernameColorConfig = new UsernameColorConfig(((BaseUnityPlugin)this).Config);
			CloakColorApplier.SetLocalSceneColor(ColorConfig.EffectiveColor);
			MapMaskHarmonyPatcher.Apply();
			CloakSpawnHookHarmonyPatcher.Apply();
			CloakTk2dHarmonyPatcher.Apply();
			CloakMeshRendererEnableHarmonyPatcher.Apply();
			CloakHeroDamageHarmonyPatcher.Apply();
			UsernameColorHarmonyPatcher.Apply();
			TryRegisterSsmpSatelliteAndLog();
			((MonoBehaviour)this).StartCoroutine(SsmpIntegrationRetryRoutine());
			ColorConfig.ColorChanged += OnConfigColorChanged;
			UsernameColorConfig.UsernameTintChanged += OnUsernameTintConfigChanged;
			HeroController.OnHeroInstanceSet += new HeroSetDelegate(OnHeroInstanceSet);
			((BaseUnityPlugin)this).Logger.LogInfo((object)(Name + " v1.15.0 loaded."));
		}

		[IteratorStateMachine(typeof(<AllowlistMissingMaskSweepRoutine>d__18))]
		private IEnumerator AllowlistMissingMaskSweepRoutine(float[] delaysSec)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <AllowlistMissingMaskSweepRoutine>d__18(0)
			{
				delaysSec = delaysSec
			};
		}

		[IteratorStateMachine(typeof(<SsmpIntegrationRetryRoutine>d__19))]
		private IEnumerator SsmpIntegrationRetryRoutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SsmpIntegrationRetryRoutine>d__19(0)
			{
				<>4__this = this
			};
		}

		private void TryRegisterSsmpSatelliteAndLog()
		{
			if (SSMPBridge.IsAvailable && SSMPBridge.TryRegister())
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"SSMP detected — multiplayer cloak + username sync enabled.");
				PushLocalCloakColorToNetwork();
				PushLocalUsernameToNetwork();
			}
		}

		internal static void SchedulePostConnectColorResend()
		{
			if (!((Object)(object)Instance == (Object)null))
			{
				((MonoBehaviour)Instance).StartCoroutine(Instance.PostConnectSsmpColorResendRoutine());
			}
		}

		[IteratorStateMachine(typeof(<PostConnectSsmpColorResendRoutine>d__22))]
		private IEnumerator PostConnectSsmpColorResendRoutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PostConnectSsmpColorResendRoutine>d__22(0);
		}

		private void PushLocalCloakColorToNetwork()
		{
			if (SSMPBridge.IsRegistered)
			{
				SSMPBridge.NotifyLocalCloakAppearanceChanged(BuildLocalCloakNetAppearance());
			}
		}

		private CloakNetAppearance BuildLocalCloakNetAppearance()
		{
			return new CloakNetAppearance(ColorConfig.EffectiveColor, CloakNetAppearance.CentiFromMultiplier(ColorConfig.TextureSaturationBoost.Value));
		}

		private void OnHeroInstanceSet(HeroController hero)
		{
			CloakColor effectiveColor = ColorConfig.EffectiveColor;
			CloakColorApplier.Apply(((Component)hero).gameObject, effectiveColor);
			CloakColorApplier.SetLocalSceneColor(effectiveColor);
			PushLocalCloakColorToNetwork();
			PushLocalUsernameToNetworkAndVisual();
		}

		private void OnConfigColorChanged(CloakColor color)
		{
			if (CloakPaletteConfig.DebugLogging)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Cloak color changed to {color}");
			}
			if ((Object)(object)HeroController.SilentInstance != (Object)null)
			{
				CloakColorApplier.Apply(((Component)HeroController.SilentInstance).gameObject, color);
			}
			CloakColorApplier.SetLocalSceneColor(color);
			PushLocalCloakColorToNetwork();
			LocalMapMaskTint.Refresh(GameManager.instance?.gameMap, color);
			MapMaskTint.BroadcastLocalColor(color);
			PushLocalUsernameToNetworkAndVisual();
		}

		private void OnUsernameTintConfigChanged()
		{
			PushLocalUsernameToNetworkAndVisual();
		}

		private void PushLocalUsernameToNetwork()
		{
			if (SSMPBridge.IsRegistered)
			{
				if (UsernameColorConfig.IsDisabled)
				{
					SSMPBridge.ClearLocalUsernameColorOnNetwork();
				}
				else
				{
					SSMPBridge.NotifyLocalUsernameColorChanged(UsernameColorConfig.ResolveRgb(ColorConfig));
				}
			}
		}

		private void RefreshLocalUsernameVisual()
		{
			UsernameTintCoordinator.ForceRefreshLocalHeroUsername();
		}

		private void PushLocalUsernameToNetworkAndVisual()
		{
			PushLocalUsernameToNetwork();
			RefreshLocalUsernameVisual();
		}
	}
	internal static class SSMPBridge
	{
		public const string SSMPGuid = "ssmp";

		private static bool _registered;

		private static Action<CloakNetAppearance>? _notifyCloakAppearanceChanged;

		private static Action<CloakColor>? _notifyUsernameColorChanged;

		private static Action? _clearLocalUsernameColorOnNetwork;

		private static Action? _resendStoredLocalColorsToServer;

		private static Func<ushort, CloakColor>? _getRemoteMapColor;

		public static bool IsAvailable => Chainloader.PluginInfos.ContainsKey("ssmp");

		public static bool IsRegistered => _registered;

		public static bool TryRegister()
		{
			if (_registered)
			{
				return true;
			}
			if (!IsAvailable)
			{
				return false;
			}
			try
			{
				if (!TryBindSatellite())
				{
					return false;
				}
				_registered = true;
				return true;
			}
			catch (Exception ex)
			{
				Log.Warn("SSMP detected but addon registration failed: " + ex.Message + ". Multiplayer cloak sync disabled; local recolor still works.");
				return false;
			}
		}

		public static void NotifyLocalCloakAppearanceChanged(CloakNetAppearance appearance)
		{
			if (_registered)
			{
				_notifyCloakAppearanceChanged?.Invoke(appearance);
			}
		}

		public static void NotifyLocalUsernameColorChanged(CloakColor color)
		{
			if (_registered)
			{
				_notifyUsernameColorChanged?.Invoke(color);
			}
		}

		public static void ClearLocalUsernameColorOnNetwork()
		{
			if (_registered)
			{
				_clearLocalUsernameColorOnNetwork?.Invoke();
			}
		}

		public static CloakColor GetRemoteMapColorOrDefault(ushort playerId)
		{
			return _getRemoteMapColor?.Invoke(playerId) ?? CloakColor.Default;
		}

		public static void ResendStoredLocalColorsToServer()
		{
			if (_registered)
			{
				_resendStoredLocalColorsToServer?.Invoke();
			}
		}

		internal static void SchedulePostConnectColorResend()
		{
			if (_registered)
			{
				HornetCloakColorPlugin.SchedulePostConnectColorResend();
			}
		}

		private static bool TryBindSatellite()
		{
			string directoryName = Path.GetDirectoryName(typeof(HornetCloakColorPlugin).Assembly.Location);
			if (string.IsNullOrEmpty(directoryName))
			{
				Log.Warn("Could not resolve plugin directory; cannot load HornetCloakColor.SSMP.dll.");
				return false;
			}
			string text = Path.Combine(directoryName, "HornetCloakColor.SSMP.dll");
			if (!File.Exists(text))
			{
				Log.Warn("SSMP is loaded but HornetCloakColor.SSMP.dll was not found next to HornetCloakColor.dll. Reinstall the mod so both DLLs are in the same folder.");
				return false;
			}
			Type type = Assembly.LoadFrom(text).GetType("HornetCloakColor.SSMPIntegration.SatelliteEntry");
			if (type == null)
			{
				Log.Warn("HornetCloakColor.SSMP.dll is missing the integration entry type.");
				return false;
			}
			type.GetMethod("Register", BindingFlags.Static | BindingFlags.Public)?.Invoke(null, null);
			MethodInfo method = type.GetMethod("NotifyLocalCloakAppearanceChanged", BindingFlags.Static | BindingFlags.Public);
			if (method != null)
			{
				_notifyCloakAppearanceChanged = (Action<CloakNetAppearance>)Delegate.CreateDelegate(typeof(Action<CloakNetAppearance>), method);
			}
			MethodInfo method2 = type.GetMethod("GetRemoteMapColorOrDefault", BindingFlags.Static | BindingFlags.Public);
			if (method2 != null)
			{
				_getRemoteMapColor = (Func<ushort, CloakColor>)Delegate.CreateDelegate(typeof(Func<ushort, CloakColor>), method2);
			}
			MethodInfo method3 = type.GetMethod("NotifyLocalUsernameColorChanged", BindingFlags.Static | BindingFlags.Public);
			if (method3 != null)
			{
				_notifyUsernameColorChanged = (Action<CloakColor>)Delegate.CreateDelegate(typeof(Action<CloakColor>), method3);
			}
			MethodInfo method4 = type.GetMethod("ClearLocalUsernameColorOnNetwork", BindingFlags.Static | BindingFlags.Public);
			if (method4 != null)
			{
				_clearLocalUsernameColorOnNetwork = (Action)Delegate.CreateDelegate(typeof(Action), method4);
			}
			MethodInfo method5 = type.GetMethod("ResendStoredLocalColorsToServer", BindingFlags.Static | BindingFlags.Public);
			if (method5 != null)
			{
				_resendStoredLocalColorsToServer = (Action)Delegate.CreateDelegate(typeof(Action), method5);
			}
			return true;
		}
	}
	internal readonly struct UsernameResolveResult
	{
		internal bool Found { get; }

		internal bool IsLocal { get; }

		internal ushort PlayerId { get; }

		internal UsernameResolveResult(bool found, bool isLocal, ushort playerId)
		{
			Found = found;
			IsLocal = isLocal;
			PlayerId = playerId;
		}
	}
	internal static class UsernameNetworkDelegates
	{
		internal static Func<Transform, UsernameResolveResult>? TryResolveUsernameTransform;

		internal static Func<ushort, CloakColor?>? GetRemoteUsernameTintOrNull;

		internal static Func<bool>? GetTeamsEnabled;

		internal static Func<bool>? GetServerCustomUsernameOverridesTeam;

		internal static Func<int>? GetLocalPlayerTeamOrdinal;

		internal static void Clear()
		{
			TryResolveUsernameTransform = null;
			GetRemoteUsernameTintOrNull = null;
			GetTeamsEnabled = null;
			GetServerCustomUsernameOverridesTeam = null;
			GetLocalPlayerTeamOrdinal = null;
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "hornet.cloak.color";

		public const string PLUGIN_NAME = "HornetCloakColor";

		public const string PLUGIN_VERSION = "1.15.0";
	}
}
namespace HornetCloakColor.Shared
{
	public readonly struct CloakColor : IEquatable<CloakColor>
	{
		public byte R { get; }

		public byte G { get; }

		public byte B { get; }

		public static CloakColor Default => new CloakColor(byte.MaxValue, byte.MaxValue, byte.MaxValue);

		public CloakColor(byte r, byte g, byte b)
		{
			R = r;
			G = g;
			B = b;
		}

		public Color ToUnityColor()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)(int)R / 255f, (float)(int)G / 255f, (float)(int)B / 255f, 1f);
		}

		public void ToHSV(out float h, out float s, out float v)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Color.RGBToHSV(ToUnityColor(), ref h, ref s, ref v);
		}

		public static CloakColor FromUnityColor(Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			return new CloakColor((byte)Mathf.Clamp(Mathf.RoundToInt(color.r * 255f), 0, 255), (byte)Mathf.Clamp(Mathf.RoundToInt(color.g * 255f), 0, 255), (byte)Mathf.Clamp(Mathf.RoundToInt(color.b * 255f), 0, 255));
		}

		public static bool TryParse(string value, out CloakColor color)
		{
			color = Default;
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			string text = value.Trim();
			if (text.Contains(","))
			{
				string[] array = text.Split(',');
				if (array.Length != 3)
				{
					return false;
				}
				if (!byte.TryParse(array[0].Trim(), out var result))
				{
					return false;
				}
				if (!byte.TryParse(array[1].Trim(), out var result2))
				{
					return false;
				}
				if (!byte.TryParse(array[2].Trim(), out var result3))
				{
					return false;
				}
				color = new CloakColor(result, result2, result3);
				return true;
			}
			string text2 = (text.StartsWith("#") ? text.Substring(1) : text);
			if (text2.Length != 6)
			{
				return false;
			}
			try
			{
				byte r = Convert.ToByte(text2.Substring(0, 2), 16);
				byte g = Convert.ToByte(text2.Substring(2, 2), 16);
				byte b = Convert.ToByte(text2.Substring(4, 2), 16);
				color = new CloakColor(r, g, b);
				return true;
			}
			catch
			{
				return false;
			}
		}

		public override string ToString()
		{
			return $"#{R:X2}{G:X2}{B:X2}";
		}

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

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

		public override int GetHashCode()
		{
			return (R << 16) | (G << 8) | B;
		}

		public static bool operator ==(CloakColor a, CloakColor b)
		{
			return a.Equals(b);
		}

		public static bool operator !=(CloakColor a, CloakColor b)
		{
			return !a.Equals(b);
		}
	}
	public readonly struct CloakNetAppearance : IEquatable<CloakNetAppearance>
	{
		public CloakColor Color { get; }

		public byte TextureSaturationCenti { get; }

		public static CloakNetAppearance Default => new CloakNetAppearance(CloakColor.Default, 100);

		public float TextureSaturationMultiplier => (float)(int)TextureSaturationCenti / 100f;

		public CloakNetAppearance(CloakColor color, byte textureSaturationCenti)
		{
			Color = color;
			TextureSaturationCenti = textureSaturationCenti;
		}

		public static byte CentiFromMultiplier(float multiplier)
		{
			return (byte)Mathf.Clamp(Mathf.RoundToInt(multiplier * 100f), 0, 200);
		}

		public bool Equals(CloakNetAppearance other)
		{
			if (Color.Equals(other.Color))
			{
				return TextureSaturationCenti == other.TextureSaturationCenti;
			}
			return false;
		}

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

		public override int GetHashCode()
		{
			return (Color.GetHashCode() * 397) ^ TextureSaturationCenti;
		}

		public static bool operator ==(CloakNetAppearance a, CloakNetAppearance b)
		{
			return a.Equals(b);
		}

		public static bool operator !=(CloakNetAppearance a, CloakNetAppearance b)
		{
			return !a.Equals(b);
		}
	}
	internal static class Log
	{
		private static ManualLogSource? _fallback;

		private static ManualLogSource Source => HornetCloakColorPlugin.LogSource ?? _fallback ?? (_fallback = Logger.CreateLogSource("HornetCloakColor"));

		public static void Info(string msg)
		{
			Source.LogInfo((object)msg);
		}

		public static void Warn(string msg)
		{
			Source.LogWarning((object)msg);
		}

		public static void Error(string msg)
		{
			Source.LogError((object)msg);
		}

		public static void Debug(string msg)
		{
			Source.LogDebug((object)msg);
		}
	}
}
namespace HornetCloakColor.Interop
{
	public static class HornetCloakColorInterop
	{
		public static event Func<CloakColor, CloakColor>? ModifyLocalCloakColorBeforeApply;

		public static event Func<UsernameTintContext, Color?>? ResolveLocalUsernameTint;

		public static event Func<UsernameTintContext, Color?>? ResolveRemoteUsernameTint;

		public static CloakColor ApplyCloakColorModifiers(CloakColor color)
		{
			if (HornetCloakColorInterop.ModifyLocalCloakColorBeforeApply == null)
			{
				return color;
			}
			CloakColor cloakColor = color;
			Delegate[] invocationList = HornetCloakColorInterop.ModifyLocalCloakColorBeforeApply.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				cloakColor = ((Func<CloakColor, CloakColor>)invocationList[i])(cloakColor);
			}
			return cloakColor;
		}

		internal static Color? InvokeLocalUsernameInterop(in UsernameTintContext ctx)
		{
			return InvokeUsernameChain(HornetCloakColorInterop.ResolveLocalUsernameTint, in ctx);
		}

		internal static Color? InvokeRemoteUsernameInterop(in UsernameTintContext ctx)
		{
			return InvokeUsernameChain(HornetCloakColorInterop.ResolveRemoteUsernameTint, in ctx);
		}

		private static Color? InvokeUsernameChain(Func<UsernameTintContext, Color?>? chain, in UsernameTintContext ctx)
		{
			if (chain == null)
			{
				return null;
			}
			Delegate[] invocationList = chain.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Color? result = ((Func<UsernameTintContext, Color?>)invocationList[i])(ctx);
				if (result.HasValue)
				{
					return result;
				}
			}
			return null;
		}

		public static CloakColor GetLocalCloakColorAfterModifiers()
		{
			return HornetCloakColorPlugin.Instance?.ColorConfig.EffectiveColor ?? CloakColor.Default;
		}

		public static CloakColor GetSyncedRemoteUsernameRgbOrDefault(ushort networkPlayerId)
		{
			return UsernameNetworkDelegates.GetRemoteUsernameTintOrNull?.Invoke(networkPlayerId) ?? CloakColor.Default;
		}
	}
	public readonly struct UsernameTintContext
	{
		public bool IsLocalPlayer { get; }

		public ushort NetworkPlayerId { get; }

		public int TeamOrdinal { get; }

		public bool TeamsEnabledOnServer { get; }

		public bool ServerAllowsCustomUsernameOverTeam { get; }

		public Color ProposedColor { get; }

		public UsernameTintContext(bool isLocalPlayer, ushort networkPlayerId, int teamOrdinal, bool teamsEnabledOnServer, bool serverAllowsCustomUsernameOverTeam, Color proposedColor)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			IsLocalPlayer = isLocalPlayer;
			NetworkPlayerId = networkPlayerId;
			TeamOrdinal = teamOrdinal;
			TeamsEnabledOnServer = teamsEnabledOnServer;
			ServerAllowsCustomUsernameOverTeam = serverAllowsCustomUsernameOverTeam;
			ProposedColor = proposedColor;
		}
	}
}
namespace HornetCloakColor.Client
{
	internal static class CloakColorApplier
	{
		public static void Apply(GameObject? playerObject, CloakColor color, float? textureSaturationBoostOverride = null)
		{
			if (!((Object)(object)playerObject == (Object)null))
			{
				bool useCloakShader = (Object)(object)CloakShaderManager.Shader != (Object)null;
				CloakRecolor.AttachOrUpdate(playerObject, color, useCloakShader, textureSaturationBoostOverride);
			}
		}

		public static void SetLocalSceneColor(CloakColor color)
		{
			CloakSceneScanner.SetColor(color);
			CloakSpriteRendererTint.SetColor(color);
		}
	}
	internal class CloakColorConfig
	{
		public enum Preset
		{
			Custom,
			Default,
			Crimson,
			Scarlet,
			Amber,
			Gold,
			Emerald,
			Teal,
			Azure,
			Royal,
			Violet,
			Magenta,
			Obsidian,
			Ivory
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static MenuElementGenerator <0>__TryCreateElement;
		}

		private static readonly CloakColor DefaultCustom = new CloakColor(200, 60, 60);

		public ConfigEntry<Preset> PresetChoice { get; }

		public ConfigEntry<string> CustomHex { get; }

		public ConfigEntry<float> TextureSaturationBoost { get; }

		public CloakColor CurrentColor => Resolve(PresetChoice.Value, CustomHex.Value);

		public CloakColor EffectiveColor => HornetCloakColorInterop.ApplyCloakColorModifiers(CurrentColor);

		public event Action<CloakColor>? ColorChanged;

		public CloakColorConfig(ConfigFile config)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_008a: 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_0095: Expected O, but got Unknown
			PresetChoice = config.Bind<Preset>("Appearance", "Cloak Color Preset", Preset.Default, "Choose 'Custom' for your own hex.");
			CustomHex = config.Bind<string>("Appearance", "Custom Cloak Color", DefaultCustom.ToString(), "Custom cloak color used when preset is set to 'Custom'. Accepts #RRGGBB, RRGGBB, or 'r,g,b' (0-255 each).");
			AcceptableValueRange<float> obj = new AcceptableValueRange<float>(0f, 2f);
			object[] array = new object[1];
			object obj2 = <>O.<0>__TryCreateElement;
			if (obj2 == null)
			{
				MenuElementGenerator val = CloakTextureSaturationModMenuElement.TryCreateElement;
				<>O.<0>__TryCreateElement = val;
				obj2 = (object)val;
			}
			array[0] = obj2;
			TextureSaturationBoost = config.Bind<float>("Appearance", "Cloak Texture Saturation Boost", 1f, new ConfigDescription("Multiplies each masked pixel's saturation after the hue shift (1 = unchanged). Values above 1 punch up washed-out cloak art. This is not the same as your hex's HSV saturation (e.g. #00FF00 is already fully saturated as a color pick).", (AcceptableValueBase)(object)obj, array));
			PresetChoice.SettingChanged += delegate
			{
				this.ColorChanged?.Invoke(EffectiveColor);
			};
			CustomHex.SettingChanged += delegate
			{
				this.ColorChanged?.Invoke(EffectiveColor);
			};
			TextureSaturationBoost.SettingChanged += delegate
			{
				this.ColorChanged?.Invoke(EffectiveColor);
			};
		}

		internal static CloakColor ResolvePresetColor(Preset preset, string customHex)
		{
			return Resolve(preset, customHex);
		}

		private static CloakColor Resolve(Preset preset, string customHex)
		{
			CloakColor color;
			return preset switch
			{
				Preset.Default => CloakColor.Default, 
				Preset.Crimson => new CloakColor(156, 36, 48), 
				Preset.Scarlet => new CloakColor(220, 56, 72), 
				Preset.Amber => new CloakColor(230, 140, 40), 
				Preset.Gold => new CloakColor(232, 190, 64), 
				Preset.Emerald => new CloakColor(56, 170, 90), 
				Preset.Teal => new CloakColor(60, 180, 180), 
				Preset.Azure => new CloakColor(64, 148, 230), 
				Preset.Royal => new CloakColor(72, 92, 210), 
				Preset.Violet => new CloakColor(140, 80, 210), 
				Preset.Magenta => new CloakColor(220, 80, 180), 
				Preset.Obsidian => new CloakColor(40, 40, 55), 
				Preset.Ivory => new CloakColor(240, 230, 205), 
				Preset.Custom => CloakColor.TryParse(customHex, out color) ? color : CloakColor.Default, 
				_ => CloakColor.Default, 
			};
		}
	}
	internal static class CloakDiskNames
	{
		public const string NoCollectionFolder = "_NoCollection";

		public static string CollectionFolder(string? tk2dCollectionName)
		{
			if (!string.IsNullOrWhiteSpace(tk2dCollectionName))
			{
				return SanitizeFileStem(tk2dCollectionName);
			}
			return "_NoCollection";
		}

		public static string? MaskCollectionNameForLookup(string? tk2dCollectionName)
		{
			if (string.Equals(tk2dCollectionName, "Player Prefab", StringComparison.OrdinalIgnoreCase))
			{
				return "Knight";
			}
			return tk2dCollectionName;
		}

		public static string SanitizeFileStem(string? name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return "tex";
			}
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			StringBuilder stringBuilder = new StringBuilder(Math.Max(name.Length, 4));
			foreach (char c in name)
			{
				if (Array.IndexOf(invalidFileNameChars, c) >= 0 || c < ' ')
				{
					stringBuilder.Append('_');
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			string text = stringBuilder.ToString();
			if (text.Length == 0 || text == "." || text == "..")
			{
				return "tex";
			}
			return text;
		}
	}
	internal static class CloakHeroDamageHarmonyPatcher
	{
		private const string HarmonyId = "hornet.cloak.color.hero-damage";

		private static bool _applied;

		internal static void Apply()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			if (_applied)
			{
				return;
			}
			Harmony val = new Harmony("hornet.cloak.color.hero-damage");
			bool flag = false;
			MethodInfo[] methods = typeof(HealthManager).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != "TakeDamage") && !methodInfo.IsAbstract)
				{
					val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(CloakHeroDamageHarmonyPatcher), "AfterHeroHealthEvent", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.Info("HornetCloakColor: patched HealthManager." + methodInfo.Name + " for post-damage cloak refresh.");
					flag = true;
					break;
				}
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(HeroController), "DoSpecialDamage", new Type[1] { typeof(bool) }, (Type[])null);
			if (methodInfo2 != null)
			{
				val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(CloakHeroDamageHarmonyPatcher), "AfterHeroHealthEvent", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.Info("HornetCloakColor: patched HeroController.DoSpecialDamage for post-damage cloak refresh.");
				flag = true;
			}
			if (!flag)
			{
				Log.Warn("HornetCloakColor: no damage hooks patched (API mismatch). Cloak may briefly lose tint after hazards.");
			}
			_applied = true;
		}

		private static void AfterHeroHealthEvent()
		{
			try
			{
				HeroController instance = HeroController.instance;
				if (!((Object)(object)instance == (Object)null))
				{
					CloakRecolor.NotifyHeroPossibleSpriteRebuild(instance);
				}
			}
			catch (Exception ex)
			{
				Log.Warn("CloakHeroDamageHarmonyPatcher: refresh threw: " + ex.Message);
			}
		}
	}
	internal static class CloakMaskManager
	{
		[CompilerGenerated]
		private sealed class <EnumerateCandidateMaskPaths>d__19 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private string <>2__current;

			private int <>l__initialThreadId;

			private string primaryPath;

			public string <>3__primaryPath;

			private string aliasPath;

			public string <>3__aliasPath;

			string IEnumerator<string>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <EnumerateCandidateMaskPaths>d__19(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = primaryPath;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (!string.Equals(primaryPath, aliasPath, StringComparison.OrdinalIgnoreCase))
					{
						<>2__current = aliasPath;
						<>1__state = 2;
						return true;
					}
					break;
				case 2:
					<>1__state = -1;
					break;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<string> IEnumerable<string>.GetEnumerator()
			{
				<EnumerateCandidateMaskPaths>d__19 <EnumerateCandidateMaskPaths>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<EnumerateCandidateMaskPaths>d__ = this;
				}
				else
				{
					<EnumerateCandidateMaskPaths>d__ = new <EnumerateCandidateMaskPaths>d__19(0);
				}
				<EnumerateCandidateMaskPaths>d__.primaryPath = <>3__primaryPath;
				<EnumerateCandidateMaskPaths>d__.aliasPath = <>3__aliasPath;
				return <EnumerateCandidateMaskPaths>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<string>)this).GetEnumerator();
			}
		}

		[CompilerGenerated]
		private sealed class <EnumerateCandidateMaskPathsWithBranch>d__20 : IEnumerable<(string path, string branch)>, IEnumerable, IEnumerator<(string path, string branch)>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private (string path, string branch) <>2__current;

			private int <>l__initialThreadId;

			private string primaryPath;

			public string <>3__primaryPath;

			private string aliasPath;

			public string <>3__aliasPath;

			(string, string) IEnumerator<(string, string)>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <EnumerateCandidateMaskPathsWithBranch>d__20(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (primaryPath, "primary(raw collection folder)");
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (!string.Equals(primaryPath, aliasPath, StringComparison.OrdinalIgnoreCase))
					{
						<>2__current = (aliasPath, "aliasCompat(e.g. Knight for Player Prefab)");
						<>1__state = 2;
						return true;
					}
					break;
				case 2:
					<>1__state = -1;
					break;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<(string path, string branch)> IEnumerable<(string, string)>.GetEnumerator()
			{
				<EnumerateCandidateMaskPathsWithBranch>d__20 <EnumerateCandidateMaskPathsWithBranch>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<EnumerateCandidateMaskPathsWithBranch>d__ = this;
				}
				else
				{
					<EnumerateCandidateMaskPathsWithBranch>d__ = new <EnumerateCandidateMaskPathsWithBranch>d__20(0);
				}
				<EnumerateCandidateMaskPathsWithBranch>d__.primaryPath = <>3__primaryPath;
				<EnumerateCandidateMaskPathsWithBranch>d__.aliasPath = <>3__aliasPath;
				return <EnumerateCandidateMaskPathsWithBranch>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<(string, string)>)this).GetEnumerator();
			}
		}

		private static readonly Dictionary<string, Texture2D> ByMaskFilePath = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, Texture2D?> ByTextureCollectionKey = new Dictionary<string, Texture2D>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Texture2D?> ByTexture2DMaskName = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase);

		private static string? _pluginDir;

		private static Texture2D? _blackWeight1x1;

		private static readonly HashSet<string> DumpedOriginalPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<string> DumpedTemplatePaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

		public static Texture2D BlackWeightMask
		{
			get
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0043: Expected O, but got Unknown
				//IL_0044: Expected O, but got Unknown
				if ((Object)(object)_blackWeight1x1 != (Object)null)
				{
					return _blackWeight1x1;
				}
				Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false, true);
				val.SetPixel(0, 0, Color.clear);
				val.Apply(false, true);
				((Object)val).name = "CloakMask:BlackFallback";
				_blackWeight1x1 = val;
				return val;
			}
		}

		private static string PluginDir
		{
			get
			{
				if (_pluginDir != null)
				{
					return _pluginDir;
				}
				string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
				_pluginDir = (string.IsNullOrEmpty(directoryName) ? string.Empty : directoryName);
				return _pluginDir;
			}
		}

		private static string TextureCollectionCacheKey(int textureInstanceId, string collectionStem)
		{
			return textureInstanceId + "\u001f" + collectionStem;
		}

		internal static bool IsMaskConfirmedAbsentForBinding(Texture mainTex, string? tk2dCollectionName)
		{
			if ((Object)(object)mainTex == (Object)null || mainTex.width <= 0)
			{
				return false;
			}
			string collectionStem = CloakDiskNames.CollectionFolder(tk2dCollectionName);
			string key = TextureCollectionCacheKey(((Object)mainTex).GetInstanceID(), collectionStem);
			if (ByTextureCollectionKey.TryGetValue(key, out Texture2D value))
			{
				return (Object)(object)value == (Object)null;
			}
			return false;
		}

		public static void OnPaletteReloaded()
		{
			foreach (Texture2D value in ByMaskFilePath.Values)
			{
				if ((Object)(object)value != (Object)null && (Object)(object)value != (Object)(object)_blackWeight1x1)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			ByMaskFilePath.Clear();
			ByTextureCollectionKey.Clear();
			ByTexture2DMaskName.Clear();
			DumpedOriginalPaths.Clear();
			DumpedTemplatePaths.Clear();
			MaskDiagLoggedCompositeKeys.Clear();
			CloakMaterialApplier.InvalidateAll();
		}

		public static bool TryGetTexture2DMask(Texture? texture, string? spriteName, out Texture2D mask)
		{
			mask = BlackWeightMask;
			if (string.IsNullOrEmpty(PluginDir))
			{
				return false;
			}
			string text = (((Object)(object)texture != (Object)null) ? ((Object)texture).name : null);
			if (TryGetTexture2DMaskByName(text, texture, out mask))
			{
				return true;
			}
			if (!string.Equals(spriteName, text, StringComparison.Ordinal) && TryGetTexture2DMaskByName(spriteName, texture, out mask))
			{
				return true;
			}
			return false;
		}

		private static bool TryGetTexture2DMaskByName(string? name, Texture? referenceTexture, out Texture2D mask)
		{
			mask = BlackWeightMask;
			if (string.IsNullOrWhiteSpace(name))
			{
				return false;
			}
			string text = CloakDiskNames.SanitizeFileStem(name);
			string text2 = Path.Combine(PluginDir, "CloakMasks", "Texture2D", text + ".png");
			if (ByTexture2DMaskName.TryGetValue(text2, out Texture2D value))
			{
				if ((Object)(object)value == (Object)null)
				{
					return false;
				}
				mask = value;
				return true;
			}
			if (!File.Exists(text2))
			{
				if ((Object)(object)referenceTexture != (Object)null && referenceTexture.width > 0 && referenceTexture.height > 0 && CloakPaletteConfig.ShouldAutoDumpMissingMask(name))
				{
					MaybeDumpDiscoveredTextureFiles(referenceTexture, text2, createEmptyMask: true, missingMaskAutoDump: true);
				}
				ByTexture2DMaskName[text2] = null;
				return false;
			}
			Texture2D val = LoadMaskFromDisk(text2);
			if ((Object)(object)val == (Object)null)
			{
				ByTexture2DMaskName[text2] = null;
				return false;
			}
			if ((Object)(object)referenceTexture != (Object)null && (((Texture)val).width != referenceTexture.width || ((Texture)val).height != referenceTexture.height))
			{
				Log.Warn($"[CloakMasks] '{text2}' size {((Texture)val).width}x{((Texture)val).height} does not match texture " + $"'{((Object)referenceTexture).name}' ({referenceTexture.width}x{referenceTexture.height}). Ignoring this SpriteRenderer mask.");
				Object.Destroy((Object)(object)val);
				ByTexture2DMaskName[text2] = null;
				return false;
			}
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)1;
			((Object)val).name = "CloakMask:Texture2D/" + text;
			ByTexture2DMaskName[text2] = val;
			if ((Object)(object)referenceTexture != (Object)null && CloakPaletteConfig.ShouldAutoDumpMissingMask(name))
			{
				MaybeDumpDiscoveredTextureFiles(referenceTexture, text2, createEmptyMask: false);
			}
			mask = val;
			return true;
		}

		public static void SweepAllowlistedLoadedTexturesForMissingMaskDumps()
		{
			HashSet<string> missingMaskDumpAllowlist = CloakPaletteConfig.MissingMaskDumpAllowlist;
			if (missingMaskDumpAllowlist == null || missingMaskDumpAllowlist.Count == 0 || string.IsNullOrEmpty(PluginDir) || !CloakPaletteConfig.DumpDiscoveredTextures)
			{
				return;
			}
			Texture2D[] array = Resources.FindObjectsOfTypeAll<Texture2D>();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			Texture2D[] array2 = array;
			foreach (Texture2D val in array2)
			{
				if ((Object)(object)val == (Object)null || ((Texture)val).width <= 0 || ((Texture)val).height <= 0)
				{
					continue;
				}
				string text = CloakDiskNames.SanitizeFileStem(((Object)val).name);
				if (text.Length != 0 && missingMaskDumpAllowlist.Contains(text))
				{
					num2++;
					string text2 = Path.Combine(PluginDir, "CloakMasks", "Texture2D", text + ".png");
					if (File.Exists(text2))
					{
						num3++;
					}
					else if (CloakPaletteConfig.ShouldAutoDumpMissingMask(((Object)val).name))
					{
						MaybeDumpDiscoveredTextureFiles((Texture)(object)val, text2, createEmptyMask: true, missingMaskAutoDump: true);
						num++;
					}
				}
			}
			string text3 = $"[CloakMasks] allowlistSweep: scanned {array.Length} Texture2D asset(s); " + $"allowlisted stem(s) seen in memory: {num2}; " + $"skipped {num3} (CloakMasks/Texture2D/*.png already exists); " + $"wrote {num} missing-mask dump(s).";
			if (num2 == 0)
			{
				text3 += " No loaded Texture2D.name matched your allowlist — runtime names often differ from dumped PNG filenames, or those atlases only appear under tk2d (masks live in CloakMasks/<collection>/<stem>.png, not Texture2D/). Trigger the effect in-game and watch [CloakMasks] missingMaskDump lines, or grep logs for the real tex name.";
			}
			Log.Info(text3);
		}

		public static bool TryGetMaskForMainTexture(Texture? mainTex, string? tk2dCollectionName, out Texture2D mask)
		{
			mask = BlackWeightMask;
			if ((Object)(object)mainTex == (Object)null || string.IsNullOrEmpty(PluginDir))
			{
				return false;
			}
			if (mainTex.width <= 0 || mainTex.height <= 0)
			{
				return false;
			}
			string text = CloakDiskNames.CollectionFolder(tk2dCollectionName);
			string text2 = CloakDiskNames.CollectionFolder(CloakDiskNames.MaskCollectionNameForLookup(tk2dCollectionName));
			int instanceID = ((Object)mainTex).GetInstanceID();
			string compositeKey = TextureCollectionCacheKey(instanceID, text);
			bool logDetail = CloakPaletteConfig.LogMaskResolutionDiagnostics && !MaskDiagLoggedCompositeKeys.Contains(compositeKey);
			if (ByTextureCollectionKey.TryGetValue(compositeKey, out Texture2D value))
			{
				if ((Object)(object)value == (Object)null)
				{
					if (logDetail)
					{
						Log.Info($"[CloakMasksDiag] cache hit — no mask for compositeKey={compositeKey} tex='{((Object)mainTex).name}' id={instanceID}");
						FinishMaskDiag();
					}
					return false;
				}
				mask = value;
				return true;
			}
			string text3 = CloakDiskNames.SanitizeFileStem(string.IsNullOrEmpty(((Object)mainTex).name) ? $"tex_{((Object)mainTex).GetInstanceID()}" : ((Object)mainTex).name);
			string path = Path.Combine(PluginDir, "CloakMasks");
			string text4 = Path.Combine(path, text, text3 + ".png");
			string text5 = Path.Combine(path, text2, text3 + ".png");
			foreach (string item in EnumerateCandidateMaskPaths(text4, text5))
			{
				if (ByMaskFilePath.TryGetValue(item, out Texture2D value2) && (Object)(object)value2 != (Object)null)
				{
					ByTextureCollectionKey[compositeKey] = value2;
					mask = value2;
					return true;
				}
			}
			if (logDetail)
			{
				string text6 = tk2dCollectionName ?? "(null)";
				string text7 = (string.Equals(text, text2, StringComparison.Ordinal) ? "" : (" stemAlias='" + text2 + "'"));
				Log.Info($"[CloakMasksDiag] disk resolve start compositeKey={compositeKey} tex='{((Object)mainTex).name}' id={instanceID} size={mainTex.width}x{mainTex.height} collectionRaw='{text6}' stemPrimary='{text}'{text7} texStem='{text3}'");
				Log.Info($"[CloakMasksDiag]   primary exists={File.Exists(text4)} → {text4}");
				if (!string.Equals(text4, text5, StringComparison.OrdinalIgnoreCase))
				{
					Log.Info($"[CloakMasksDiag]   aliasCompat exists={File.Exists(text5)} → {text5}");
				}
			}
			Texture2D val = null;
			string text8 = null;
			string text9 = null;
			foreach (var (text10, text11) in EnumerateCandidateMaskPathsWithBranch(text4, text5))
			{
				if (File.Exists(text10))
				{
					text8 = text10;
					text9 = text11;
					break;
				}
			}
			if (logDetail)
			{
				Log.Info("[CloakMasksDiag]   branch=" + (text9 ?? "(none)") + " resolvedPath=" + (text8 ?? "(null)"));
			}
			if (text8 != null)
			{
				val = LoadMaskFromDisk(text8);
				if ((Object)(object)val != (Object)null && (((Texture)val).width != mainTex.width || ((Texture)val).height != mainTex.height))
				{
					Log.Warn($"[CloakMasks] '{text8}' size {((Texture)val).width}x{((Texture)val).height} does not match atlas " + $"'{((Object)mainTex).name}' ({mainTex.width}x{mainTex.height}). Using zero mask (no recolor) for this atlas.");
					if (logDetail)
					{
						Log.Info("[CloakMasksDiag] RESULT: rejected loaded mask — size mismatch after branch=" + text9);
					}
					Object.Destroy((Object)(object)val);
					val = null;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				MaybeDumpDiscoveredTextureFiles(mainTex, text4, createEmptyMask: true, CloakPaletteConfig.ShouldAutoDumpMissingMask(((Object)mainTex).name));
				ByTextureCollectionKey[compositeKey] = null;
				if (logDetail)
				{
					Log.Info("[CloakMasksDiag] RESULT: no mask (texStem=" + text3 + ")");
					FinishMaskDiag();
				}
				return false;
			}
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)1;
			string text12 = Path.GetFileName(Path.GetDirectoryName(text8)) ?? text;
			((Object)val).name = "CloakMask:" + text12 + "/" + text3;
			ByMaskFilePath[text4] = val;
			if (!string.Equals(text4, text8, StringComparison.OrdinalIgnoreCase))
			{
				ByMaskFilePath[text8] = val;
			}
			ByTextureCollectionKey[compositeKey] = val;
			MaybeDumpDiscoveredTextureFiles(mainTex, text4, createEmptyMask: false);
			mask = val;
			if (logDetail)
			{
				Log.Info("[CloakMasksDiag] RESULT: OK mask loaded bytes→texture name=" + ((Object)val).name);
				FinishMaskDiag();
			}
			return true;
			void FinishMaskDiag()
			{
				if (logDetail)
				{
					MaskDiagLoggedCompositeKeys.Add(compositeKey);
				}
			}
		}

		[IteratorStateMachine(typeof(<EnumerateCandidateMaskPaths>d__19))]
		private static IEnumerable<string> EnumerateCandidateMaskPaths(string primaryPath, string aliasPath)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <EnumerateCandidateMaskPaths>d__19(-2)
			{
				<>3__primaryPath = primaryPath,
				<>3__aliasPath = aliasPath
			};
		}

		[IteratorStateMachine(typeof(<EnumerateCandidateMaskPathsWithBranch>d__20))]
		private static IEnumerable<(string path, string branch)> EnumerateCandidateMaskPathsWithBranch(string primaryPath, string aliasPath)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <EnumerateCandidateMaskPathsWithBranch>d__20(-2)
			{
				<>3__primaryPath = primaryPath,
				<>3__aliasPath = aliasPath
			};
		}

		private static void MaybeDumpDiscoveredTextureFiles(Texture mainTex, string maskPath, bool createEmptyMask, bool missingMaskAutoDump = false)
		{
			if (!CloakPaletteConfig.DumpDiscoveredTextures)
			{
				return;
			}
			string logTag = (missingMaskAutoDump ? "missingMaskDump" : "dumpDiscoveredTextures");
			string directoryName = Path.GetDirectoryName(maskPath);
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(maskPath);
			if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(fileNameWithoutExtension))
			{
				return;
			}
			int width = mainTex.width;
			int height = mainTex.height;
			if (width > 0 && height > 0)
			{
				Directory.CreateDirectory(directoryName);
				MaybeWriteOriginalTexture(mainTex, Path.Combine(directoryName, fileNameWithoutExtension + "-original.png"), width, height, logTag);
				if (createEmptyMask)
				{
					MaybeWriteEmptyMask(maskPath, width, height, logTag);
				}
			}
		}

		private static void MaybeWriteOriginalTexture(Texture mainTex, string outPath, int w, int h, string logTag)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			if (DumpedOriginalPaths.Contains(outPath) || File.Exists(outPath))
			{
				return;
			}
			RenderTexture temporary = RenderTexture.GetTemporary(w, h, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)1);
			RenderTexture active = RenderTexture.active;
			try
			{
				Graphics.Blit(mainTex, temporary);
				RenderTexture.active = temporary;
				Texture2D val = new Texture2D(w, h, (TextureFormat)4, false, true);
				val.ReadPixels(new Rect(0f, 0f, (float)w, (float)h), 0, 0);
				val.Apply(false, false);
				File.WriteAllBytes(outPath, ImageConversion.EncodeToPNG(val));
				DumpedOriginalPaths.Add(outPath);
				Log.Info("[CloakMasks] " + logTag + ": wrote '" + outPath + "'.");
				Object.Destroy((Object)(object)val);
			}
			catch (Exception ex)
			{
				Log.Warn("[CloakMasks] " + logTag + ": could not write '" + outPath + "': " + ex.Message);
			}
			finally
			{
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
			}
		}

		private static void MaybeWriteEmptyMask(string maskPath, int w, int h, string logTag)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			if (DumpedTemplatePaths.Contains(maskPath) || File.Exists(maskPath))
			{
				return;
			}
			try
			{
				Texture2D val = new Texture2D(w, h, (TextureFormat)4, false, true);
				Color32[] pixels = (Color32[])(object)new Color32[w * h];
				val.SetPixels32(pixels);
				val.Apply(false, false);
				File.WriteAllBytes(maskPath, ImageConversion.EncodeToPNG(val));
				DumpedTemplatePaths.Add(maskPath);
				Log.Info("[CloakMasks] " + logTag + ": wrote empty mask template '" + maskPath + "'.");
				Object.Destroy((Object)(object)val);
			}
			catch (Exception ex)
			{
				Log.Warn("[CloakMasks] " + logTag + ": could not write empty mask '" + maskPath + "': " + ex.Message);
			}
		}

		private static Texture2D? LoadMaskFromDisk(string path)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			try
			{
				byte[] array = File.ReadAllBytes(path);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false, true);
				if (!ImageConversion.LoadImage(val, array))
				{
					Object.Destroy((Object)(object)val);
					return null;
				}
				return val;
			}
			catch (Exception ex)
			{
				Log.Warn("[CloakMasks] Failed to read '" + path + "': " + ex.Message);
				return null;
			}
		}
	}
	internal static class CloakMaterialApplier
	{
		private enum AppliedMode : byte
		{
			None,
			CloakApplied,
			NoMaskRestored,
			VertexTinted
		}

		private readonly struct AppliedState
		{
			public readonly int SharedMatInstanceId;

			public readonly CloakColor Color;

			public readonly AppliedMode Mode;

			public readonly float TextureSaturationBoost;

			public AppliedState(int sharedMatInstanceId, CloakColor color, AppliedMode mode, float textureSaturationBoost = 0f)
			{
				SharedMatInstanceId = sharedMatInstanceId;
				Color = color;
				Mode = mode;
				TextureSaturationBoost = textureSaturationBoost;
			}
		}

		private static readonly Dictionary<int, AppliedState> AppliedByRenderer = new Dictionary<int, AppliedState>();

		public static void InvalidateAll()
		{
			AppliedByRenderer.Clear();
		}

		public static void InvalidateRenderer(MeshRenderer? renderer)
		{
			if (!((Object)(object)renderer == (Object)null))
			{
				AppliedByRenderer.Remove(((Object)renderer).GetInstanceID());
			}
		}

		public static void InvalidateSubtree(Transform? root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			MeshRenderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					InvalidateRenderer(val);
				}
			}
		}

		internal static tk2dBaseSprite? ResolveTk2dSprite(MeshRenderer renderer)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				return null;
			}
			Transform transform = ((Component)renderer).transform;
			tk2dBaseSprite component = ((Component)transform).GetComponent<tk2dBaseSprite>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			if ((Object)(object)transform.parent != (Object)null)
			{
				tk2dBaseSprite component2 = ((Component)transform.parent).GetComponent<tk2dBaseSprite>();
				if ((Object)(object)component2 != (Object)null)
				{
					return component2;
				}
				for (int i = 0; i < transform.parent.childCount; i++)
				{
					Transform child = transform.parent.GetChild(i);
					if (!((Object)(object)child == (Object)(object)transform))
					{
						component = ((Component)child).GetComponent<tk2dBaseSprite>();
						if ((Object)(object)component != (Object)null)
						{
							return component;
						}
					}
				}
			}
			return ((Component)transform).GetComponentInParent<tk2dBaseSprite>();
		}

		public static void Apply(MeshRenderer renderer, tk2dBaseSprite? sprite, CloakColor color, bool useCloakShader, Dictionary<MeshRenderer, Shader> originalShaderByRenderer, float? textureSaturationBoostOverride = null)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				return;
			}
			Material sharedMaterial = ((Renderer)renderer).sharedMaterial;
			if ((Object)(object)sharedMaterial == (Object)null || !sharedMaterial.HasProperty(CloakShaderManager.MainTexId))
			{
				return;
			}
			int instanceID = ((Object)renderer).GetInstanceID();
			int instanceID2 = ((Object)sharedMaterial).GetInstanceID();
			bool flag = useCloakShader && (Object)(object)CloakShaderManager.Shader != (Object)null;
			float num = textureSaturationBoostOverride ?? GetTextureSaturationBoost();
			if (AppliedByRenderer.TryGetValue(instanceID, out var value) && value.SharedMatInstanceId == instanceID2)
			{
				if (flag)
				{
					if (value.Mode == AppliedMode.CloakApplied && value.Color.Equals(color) && Mathf.Approximately(value.TextureSaturationBoost, num))
					{
						return;
					}
					if (value.Mode == AppliedMode.NoMaskRestored)
					{
						Texture mainTexture = sharedMaterial.mainTexture;
						tk2dBaseSprite val = sprite ?? ResolveTk2dSprite(renderer);
						string tk2dCollectionName = (((Object)(object)((val != null) ? val.Collection : null) != (Object)null) ? ((Object)val.Collection).name : null);
						if ((Object)(object)mainTexture != (Object)null && CloakMaskManager.IsMaskConfirmedAbsentForBinding(mainTexture, tk2dCollectionName))
						{
							return;
						}
					}
				}
				else if (value.Mode == AppliedMode.VertexTinted && value.Color.Equals(color))
				{
					return;
				}
			}
			if (sprite == null)
			{
				sprite = ResolveTk2dSprite(renderer);
			}
			Material material = ((Renderer)renderer).material;
			if ((Object)(object)material == (Object)null)
			{
				return;
			}
			if (flag && (Object)(object)material.mainTexture == (Object)null)
			{
				AppliedByRenderer.Remove(instanceID);
				return;
			}
			CloakColor color2 = color;
			AppliedMode appliedMode;
			if (flag)
			{
				string tk2dCollectionName2 = (((Object)(object)((sprite != null) ? sprite.Collection : null) != (Object)null) ? ((Object)sprite.Collection).name : null);
				if (!CloakMaskManager.TryGetMaskForMainTexture(material.mainTexture, tk2dCollectionName2, out Texture2D mask))
				{
					RestoreOriginalShader(renderer, material, originalShaderByRenderer);
					appliedMode = AppliedMode.NoMaskRestored;
					color2 = default(CloakColor);
				}
				else
				{
					EnsureCloakShader(renderer, material, originalShaderByRenderer);
					ApplyShaderProperties(material, sprite, color, mask, num);
					appliedMode = AppliedMode.CloakApplied;
				}
			}
			else
			{
				RestoreOriginalShader(renderer, material, originalShaderByRenderer);
				ApplyVertexTint(material, sprite, color);
				appliedMode = AppliedMode.VertexTinted;
			}
			Material sharedMaterial2 = ((Renderer)renderer).sharedMaterial;
			if ((Object)(object)sharedMaterial2 != (Object)null)
			{
				float textureSaturationBoost = ((appliedMode == AppliedMode.CloakApplied) ? num : 0f);
				AppliedByRenderer[instanceID] = new AppliedState(((Object)sharedMaterial2).GetInstanceID(), color2, appliedMode, textureSaturationBoost);
			}
		}

		public static void Restore(MeshRenderer renderer, Dictionary<MeshRenderer, Shader> originalShaderByRenderer)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				return;
			}
			Material sharedMaterial = ((Renderer)renderer).sharedMaterial;
			if ((Object)(object)sharedMaterial == (Object)null || !sharedMaterial.HasProperty(CloakShaderManager.MainTexId))
			{
				return;
			}
			int instanceID = ((Object)renderer).GetInstanceID();
			int instanceID2 = ((Object)sharedMaterial).GetInstanceID();
			if (AppliedByRenderer.TryGetValue(instanceID, out var value) && value.SharedMatInstanceId == instanceID2 && value.Mode == AppliedMode.NoMaskRestored)
			{
				return;
			}
			Material material = ((Renderer)renderer).material;
			if (!((Object)(object)material == (Object)null))
			{
				RestoreOriginalShader(renderer, material, originalShaderByRenderer);
				Material sharedMaterial2 = ((Renderer)renderer).sharedMaterial;
				if ((Object)(object)sharedMaterial2 != (Object)null)
				{
					AppliedByRenderer[instanceID] = new AppliedState(((Object)sharedMaterial2).GetInstanceID(), default(CloakColor), AppliedMode.NoMaskRestored);
				}
			}
		}

		private static void EnsureCloakShader(MeshRenderer renderer, Material mat, Dictionary<MeshRenderer, Shader> map)
		{
			Shader shader = CloakShaderManager.Shader;
			if (!((Object)(object)mat.shader == (Object)(object)shader))
			{
				if (!map.ContainsKey(renderer))
				{
					map[renderer] = mat.shader;
				}
				Texture mainTexture = mat.mainTexture;
				mat.shader = shader;
				if ((Object)(object)mainTexture != (Object)null)
				{
					mat.mainTexture = mainTexture;
				}
			}
		}

		private static void RestoreOriginalShader(MeshRenderer renderer, Material mat, Dictionary<MeshRenderer, Shader> map)
		{
			if (map.TryGetValue(renderer, out Shader value) && !((Object)(object)value == (Object)null) && !((Object)(object)mat.shader == (Object)(object)value))
			{
				Texture mainTexture = mat.mainTexture;
				mat.shader = value;
				if ((Object)(object)mainTexture != (Object)null)
				{
					mat.mainTexture = mainTexture;
				}
			}
		}

		private static void ApplyShaderProperties(Material mat, tk2dBaseSprite? sprite, CloakColor color, Texture2D mask, float satBoost)
		{
			if (color.Equals(CloakColor.Default))
			{
				mat.SetFloat(CloakShaderManager.StrengthId, 0f);
				mat.SetTexture(CloakShaderManager.CloakMaskTexId, (Texture)(object)mask);
				return;
			}
			color.ToHSV(out var h, out var s, out var v);
			mat.SetFloat(CloakShaderManager.TargetHueId, h);
			mat.SetFloat(CloakShaderManager.TargetSatId, (s <= 0.001f) ? 0f : satBoost);
			mat.SetFloat(CloakShaderManager.TargetValId, Mathf.Lerp(0.6f, 1.4f, v));
			mat.SetFloat(CloakShaderManager.StrengthId, 1f);
			mat.SetTexture(CloakShaderManager.CloakMaskTexId, (Texture)(object)mask);
		}

		internal static float GetTextureSaturationBoost()
		{
			HornetCloakColorPlugin instance = HornetCloakColorPlugin.Instance;
			if (instance?.ColorConfig == null)
			{
				return 1f;
			}
			return Mathf.Clamp(instance.ColorConfig.TextureSaturationBoost.Value, 0f, 2f);
		}

		internal static bool Tk2dSpriteVertexColorsSafeToMutate(tk2dBaseSprite sprite)
		{
			if ((Object)(object)sprite == (Object)null)
			{
				return false;
			}
			MeshFilter component = ((Component)sprite).GetComponent<MeshFilter>();
			Mesh val = (((Object)(object)component != (Object)null) ? component.sharedMesh : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			int vertexCount = val.vertexCount;
			if (vertexCount <= 0)
			{
				return false;
			}
			Color[] colors = val.colors;
			if (colors != null && colors.Length != 0 && colors.Length != vertexCount)
			{
				return false;
			}
			return true;
		}

		private static void ApplyVertexTint(Material mat, tk2dBaseSprite? sprite, CloakColor color)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			Color color2 = (mat.color = color.ToUnityColor());
			if ((Object)(object)sprite != (Object)null && Tk2dSpriteVertexColorsSafeToMutate(sprite))
			{
				sprite.color = color2;
			}
		}

		public static void PruneDestroyed(Dictionary<MeshRenderer, Shader> map)
		{
			if (map.Count == 0)
			{
				return;
			}
			List<MeshRenderer> list = null;
			foreach (MeshRenderer key in map.Keys)
			{
				if (!Object.op_Implicit((Object)(object)key))
				{
					(list ?? (list = new List<MeshRenderer>())).Add(key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (MeshRenderer item in list)
			{
				map.Remove(item);
			}
		}
	}
	internal static class CloakMeshRendererEnableHarmonyPatcher
	{
		private const string HarmonyId = "hornet.cloak.color.renderer-visibility";

		private static bool _applied;

		private static int _visibilityHookDepth;

		private static readonly List<MeshRenderer> SetActiveMeshScratch = new List<MeshRenderer>();

		internal static void Apply()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			if (_applied)
			{
				return;
			}
			_applied = true;
			Harmony val = new Harmony("hornet.cloak.color.renderer-visibility");
			bool flag = false;
			MethodInfo methodInfo = typeof(Renderer).GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public)?.GetSetMethod(nonPublic: true);
			if (methodInfo != null)
			{
				try
				{
					val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(CloakMeshRendererEnableHarmonyPatcher), "Renderer_Enabled_Setter_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.Info("HornetCloakColor: hooked Renderer.enabled setter for same-frame hero cloak tint.");
					flag = true;
				}
				catch (Exception ex)
				{
					Log.Warn("CloakMeshRendererEnableHarmonyPatcher: Renderer.enabled setter patch failed (" + ex.GetType().Name + ": " + ex.Message + ")");
				}
			}
			else
			{
				Log.Warn("CloakMeshRendererEnableHarmonyPatcher: Renderer.enabled setter not found.");
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(GameObject), "SetActive", new Type[1] { typeof(bool) }, (Type[])null);
			if (methodInfo2 != null)
			{
				try
				{
					val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(CloakMeshRendererEnableHarmonyPatcher), "GameObject_SetActive_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.Info("HornetCloakColor: hooked GameObject.SetActive for hero cloak tint when subtrees activate.");
					flag = true;
				}
				catch (Exception ex2)
				{
					Log.Warn("CloakMeshRendererEnableHarmonyPatcher: GameObject.SetActive patch failed (" + ex2.GetType().Name + ": " + ex2.Message + ")");
				}
			}
			if (!flag)
			{
				Log.Warn("CloakMeshRendererEnableHarmonyPatcher: no visibility hooks applied — Witch/dash cloak tint may lag until LateUpdate/pre-render.");
			}
		}

		private static void Renderer_Enabled_Setter_Postfix(Renderer __instance, bool value)
		{
			if (!value)
			{
				return;
			}
			MeshRenderer val = (MeshRenderer)(object)((__instance is MeshRenderer) ? __instance : null);
			if (val == null || _visibilityHookDepth > 0)
			{
				return;
			}
			_visibilityHookDepth++;
			try
			{
				TryRefreshUnderRecolor(val);
			}
			finally
			{
				_visibilityHookDepth--;
			}
		}

		private static void GameObject_SetActive_Postfix(GameObject __instance, bool value)
		{
			if (!value || (Object)(object)__instance == (Object)null || _visibilityHookDepth > 0)
			{
				return;
			}
			_visibilityHookDepth++;
			try
			{
				CloakRecolor componentInParent = __instance.GetComponentInParent<CloakRecolor>();
				if ((Object)(object)componentInParent == (Object)null)
				{
					return;
				}
				SetActiveMeshScratch.Clear();
				__instance.GetComponentsInChildren<MeshRenderer>(true, SetActiveMeshScratch);
				foreach (MeshRenderer item in SetActiveMeshScratch)
				{
					if ((Object)(object)item != (Object)null)
					{
						componentInParent.RefreshMeshRendererNow(item);
					}
				}
			}
			catch (Exception ex)
			{
				Log.Warn("CloakMeshRendererEnableHarmonyPatcher: SetActive postfix threw on '" + ((Object)__instance).name + "': " + ex.Message);
			}
			finally
			{
				_visibilityHookDepth--;
			}
		}

		private static void TryRefreshUnderRecolor(MeshRenderer meshRenderer)
		{
			try
			{
				CloakRecolor componentInParent = ((Component)meshRenderer).GetComponentInParent<CloakRecolor>();
				if (!((Object)(object)componentInParent == (Object)null))
				{
					componentInParent.RefreshMeshRendererNow(meshRenderer);
				}
			}
			catch (Exception ex)
			{
				Log.Warn("CloakMeshRendererEnableHarmonyPatcher: enabled postfix threw on '" + ((Object)meshRenderer).name + "': " + ex.Message);
			}
		}
	}
	internal static class CloakPaletteConfig
	{
		public static bool DebugLogging { get; private set; }

		public static bool MaskResolutionDebugLogging { get; private set; }

		internal static bool LogMaskResolutionDiagnostics
		{
			get
			{
				if (!DebugLogging)
				{
					return MaskResolutionDebugLogging;
				}
				return true;
			}
		}

		public static bool MapIconDebugLogging { get; private set; }

		public static bool LogMapIconDiagnostics
		{
			get
			{
				if (!DebugLogging)
				{
					return MapIconDebugLogging;
				}
				return true;
			}
		}

		public static bool DumpDiscoveredTextures { get; private set; }

		public static HashSet<string>? MissingMaskDumpAllowlist { get; private set; }

		public static float[]? MissingMaskDumpAllowlistSweepDelaysSec { get; private set; }

		public static int HeroMeshRescanIntervalFrames { get; private set; }

		public static void Load()
		{
			ApplyDefaults();
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			if (!string.IsNullOrEmpty(directoryName))
			{
				string text = Path.Combine(directoryName, "cloak_palette.json");
				if (File.Exists(text))
				{
					try
					{
						if (TryApplyPaletteJson(File.ReadAllText(text)))
						{
							Log.Info("Loaded cloak runtime config from " + text + ".");
							if (MapIconDebugLogging)
							{
								Log.Info("[MapIcon] mapIconDebugLogging is true — tracing map/compass sync; grep log for \"[MapIcon]\".");
							}
							if (MaskResolutionDebugLogging)
							{
								Log.Info("[CloakMasksDiag] maskResolutionDebugLogging is true — tracing mask path resolution; grep \"[CloakMasksDiag]\".");
							}
						}
						else
						{
							Log.Warn("cloak_palette.json was not valid; using built-in defaults from the mod DLL.");
						}
					}
					catch (Exception ex)
					{
						Log.Warn("Could not read cloak_palette.json: " + ex.Message + ". Using defaults.");
					}
				}
			}
			CloakMaskManager.OnPaletteReloaded();
			LogMissingMaskDumpConfigSummary();
		}

		private static void ApplyDefaults()
		{
			DebugLogging = false;
			MaskResolutionDebugLogging = false;
			MapIconDebugLogging = false;
			HeroMeshRescanIntervalFrames = 4;
			DumpDiscoveredTextures = false;
			MissingMaskDumpAllowlist = null;
			MissingMaskDumpAllowlistSweepDelaysSec = null;
		}

		private static void LogMissingMaskDumpConfigSummary()
		{
			int num = MissingMaskDumpAllowlist?.Count ?? 0;
			float[] missingMaskDumpAllowlistSweepDelaysSec = MissingMaskDumpAllowlistSweepDelaysSec;
			string arg = ((missingMaskDumpAllowlistSweepDelaysSec == null || missingMaskDumpAllowlistSweepDelaysSec.Length == 0) ? "sweep=off" : ("sweep=" + string.Join(",", missingMaskDumpAllowlistSweepDelaysSec) + "s"));
			Log.Info($"[CloakMasks] texture dump policy: dumpDiscoveredTextures={DumpDiscoveredTextures} " + $"(off = no PNG dumps); allowlist={num} stems, {arg} " + "(when dumps on: allowlist + stem match → missing-mask dump; else no missing-mask dump).");
		}

		internal static bool ShouldAutoDumpMissingMask(string? textureName)
		{
			string text = NormalizeMaskDumpStem(textureName);
			if (text.Length == 0)
			{
				return false;
			}
			if (MissingMaskDumpAllowlist != null && MissingMaskDumpAllowlist.Count > 0)
			{
				return MissingMaskDumpAllowlist.Contains(text);
			}
			return false;
		}

		internal static string NormalizeMaskDumpStem(string? textureName)
		{
			if (string.IsNullOrWhiteSpace(textureName))
			{
				return string.Empty;
			}
			string text = textureName.Trim();
			if (text.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
			{
				string text2 = text;
				text = text2.Substring(0, text2.Length - 4);
			}
			return CloakDiskNames.SanitizeFileStem(text);
		}

		private static bool TryApplyPaletteJson(string json)
		{
			if (string.IsNullOrWhiteSpace(json))
			{
				return false;
			}
			string text = json.TrimStart();
			if (!text.StartsWith("{", StringComparison.Ordinal))
			{
				return false;
			}
			if (TryExtractBool(text, "debugLogging", out var value))
			{
				DebugLogging = value;
			}
			if (TryExtractBool(text, "maskResolutionDebugLogging", out var value2))
			{
				MaskResolutionDebugLogging = value2;
			}
			if (TryExtractBool(text, "mapIconDebugLogging", out var value3))
			{
				MapIconDebugLogging = value3;
			}
			if (TryExtractInt(text, "heroMeshRescanIntervalFrames", out var value4) && value4 > 0 && value4 <= 600)
			{
				HeroMeshRescanIntervalFrames = value4;
			}
			if (TryExtractBool(text, "dumpDiscoveredTextures", out var value5))
			{
				DumpDiscoveredTextures = value5;
			}
			if (TryExtractStringArray(text, "missingMaskDumpAllowlist", out string[] values))
			{
				MissingMaskDumpAllowlist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
				string[] array = values;
				for (int i = 0; i < array.Length; i++)
				{
					string text2 = NormalizeMaskDumpStem(array[i]);
					if (text2.Length > 0)
					{
						MissingMaskDumpAllowlist.Add(text2);
					}
				}
			}
			if (TryExtractFloatArray(text, "missingMaskDumpAllowlistSweepDelaysSec", out float[] values2))
			{
				MissingMaskDumpAllowlistSweepDelaysSec = ((values2.Length != 0) ? values2 : null);
			}
			return true;
		}

		private static bool TryExtractInt(string json, string key, out int value)
		{
			value = 0;
			string pattern = "\"" + Regex.Escape(key) + "\"\\s*:\\s*(?<n>-?[0-9]+)";
			Match match = Regex.Match(json, pattern, RegexOptions.CultureInvariant);
			if (!match.Success)
			{
				return false;
			}
			return int.TryParse(match.Groups["n"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
		}

		private static bool TryExtractBool(string json, string key, out bool value)
		{
			value = false;
			string pattern = "\"" + Regex.Escape(key) + "\"\\s*:\\s*(?<v>true|false)";
			Match match = Regex.Match(json, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
			if (!match.Success)
			{
				return false;
			}
			value = match.Groups["v"].Value.Equals("true", StringComparison.OrdinalIgnoreCase);
			return true;
		}

		private static bool TryExtractStringArray(string json, string key, out string[] values)
		{
			values = Array.Empty<string>();
			if (!TryExtractJsonArraySlice(json, key, out string inner))
			{
				return false;
			}
			List<string> list = new List<string>();
			foreach (Match item in Regex.Matches(inner, "\"((?:\\\\.|[^\"\\\\])*)\"", RegexOptions.CultureInvariant))
			{
				list.Add(UnescapeJsonString(item.Groups[1].Value));
			}
			values = list.ToArray();
			return true;
		}

		private static bool TryExtractFloatArray(string json, string key, out float[] values)
		{
			values = Array.Empty<float>();
			if (!TryExtractJsonArraySlice(json, key, out string inner))
			{
				return false;
			}
			string[] array = inner.Split(',');
			List<float> list = new List<float>(array.Length);
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				string text = array2[i].Trim();
				if (text.Length != 0)
				{
					if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						return false;
					}
					list.Add(result);
				}
			}
			values = list.ToArray();
			return true;
		}

		private static bool TryExtractJsonArraySlice(string json, string key, out string inner)
		{
			inner = string.Empty;
			string text = "\"" + key + "\"";
			int num = json.IndexOf(text, StringComparison.Ordinal);
			if (num < 0)
			{
				return false;
			}
			int num2 = json.IndexOf('[', num + text.Length);
			if (num2 < 0)
			{
				return false;
			}
			int num3 = 0;
			int num4 = -1;
			for (int i = num2; i < json.Length; i++)
			{
				switch (json[i])
				{
				case '[':
					num3++;
					continue;
				case ']':
					num3--;
					if (num3 != 0)
					{
						continue;
					}
					break;
				default:
					continue;
				}
				num4 = i;
				break;
			}
			if (num4 < 0)
			{
				return false;
			}
			inner = json.Substring(num2 + 1, num4 - num2 - 1);
			return true;
		}

		private static string UnescapeJsonString(string s)
		{
			if (string.IsNullOrEmpty(s) || s.IndexOf('\\') < 0)
			{
				return s;
			}
			return s.Replace("\\\"", "\"", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal);
		}
	}
	[DefaultExecutionOrder(32000)]
	[DisallowMultipleComponent]
	internal class CloakRecolor : MonoBehaviour
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static CameraCallback <0>__OnBuiltInCameraPreRender;

			public static Action<ScriptableRenderContext, Camera> <1>__OnSrpBeginCameraRendering;
		}

		private static readonly List<CloakRecolor> ActiveInstances = new List<CloakRecolor>();

		private static byte _preDrawHookRegistration;

		private static int _lastPreDrawTintFrame = -1;

		private float? _textureSaturationBoostOverride;

		private readonly Dictionary<MeshRenderer, Shader> _originalShaderByRenderer = new Dictionary<MeshRenderer, Shader>();

		private readonly List<MeshRenderer> _meshCache = new List<MeshRenderer>();

		private readonly HashSet<MeshRenderer> _meshCacheSet = new HashSet<MeshRenderer>();

		private int _meshRescanCountdown;

		private bool _meshCacheInvalid = true;

		public CloakColor Color { get; private set; } = CloakColor.Default;


		public bool UseCloakShader { get; private set; } = true;


		internal void ApplyFromTk2dPipeline(tk2dBaseSprite sprite)
		{
			MeshRenderer component = ((Component)sprite).GetComponent<MeshRenderer>();
			if (!((Object)(object)component == (Object)null))
			{
				EnsureMeshRendererTracked(component);
				CloakMaterialApplier.InvalidateRenderer(component);
				CloakMaterialApplier.Apply(component, sprite, Color, UseCloakShader, _originalShaderByRenderer, _textureSaturationBoostOverride);
			}
		}

		internal static void NotifyHeroPossibleSpriteRebuild(HeroController? hero)
		{
			if (!((Object)(object)hero == (Object)null))
			{
				((Component)hero).GetComponent<CloakRecolor>()?.ForceHierarchyRefresh();
			}
		}

		private void OnEnable()
		{
			_meshCacheInvalid = true;
			if (!ActiveInstances.Contains(this))
			{
				ActiveInstances.Add(this);
			}
			EnsurePreDrawTintHooks();
		}

		private void OnDisable()
		{
			ActiveInstances.Remove(this);
			TeardownPreDrawTintHooksIfIdle();
		}

		private static void EnsurePreDrawTintHooks()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_001d: 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)
			//IL_0028: Expected O, but got Unknown
			if (_preDrawHookRegistration == 0)
			{
				CameraCallback onPreRender = Camera.onPreRender;
				object obj = <>O.<0>__OnBuiltInCameraPreRender;
				if (obj == null)
				{
					CameraCallback val = OnBuiltInCameraPreRender;
					<>O.<0>__OnBuiltInCameraPreRender = val;
					obj = (object)val;
				}
				Camera.onPreRender = (CameraCallback)Delegate.Combine((Delegate?)(object)onPreRender, (Delegate?)obj);
				_preDrawHookRegistration |= 1;
				if ((Object)(object)GraphicsSettings.defaultRenderPipeline != (Object)null)
				{
					RenderPipelineManager.beginCameraRendering += OnSrpBeginCameraRendering;
					_preDrawHookRegistration |= 2;
				}
			}
		}

		private static void TeardownPreDrawTintHooksIfIdle()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			if (ActiveInstances.Count > 0 || _preDrawHookRegistration == 0)
			{
				return;
			}
			if (((uint)_preDrawHookRegistration & (true ? 1u : 0u)) != 0)
			{
				CameraCallback onPreRender = Camera.onPreRender;
				object obj = <>O.<0>__OnBuiltInCameraPreRender;
				if (obj == null)
				{
					CameraCallback val = OnBuiltInCameraPreRender;
					<>O.<0>__OnBuiltInCameraPreRender = val;
					obj = (object)val;
				}
				Camera.onPreRender = (CameraCallback)Delegate.Remove((Delegate?)(object)onPreRender, (Delegate?)obj);
			}
			if ((_preDrawHookRegistration & 2u) != 0)
			{
				RenderPipelineManager.beginCameraRendering -= OnSrpBeginCameraRendering;
			}
			_preDrawHookRegistration = 0;
		}

		private static void OnBuiltInCameraPreRender(Camera cam)
		{
			if (IsGameplayCameraForPreDrawTint(cam))
			{
				RunPreDrawTintPass();
			}
		}

		private static void OnSrpBeginCameraRendering(ScriptableRenderContext _, Camera cam)
		{
			if (IsGameplayCameraForPreDrawTint(cam))
			{
				RunPreDrawTintPass();
			}
		}

		private static bool IsGameplayCameraForPreDrawTint(Camera? cam)
		{
			if ((Object)(object)cam == (Object)null || !((Behaviour)cam).enabled)
			{
				return false;
			}
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				return cam == main;
			}
			return true;
		}

		private static void RunPreDrawTintPass()
		{
			int frameCount = Time.frameCount;
			if (frameCount == _lastPreDrawTintFrame)
			{
				return;
			}
			_lastPreDrawTintFrame = frameCount;
			for (int num = ActiveInstances.Count - 1; num >= 0; num--)
			{
				CloakRecolor cloakRecolor = ActiveInstances[num];
				if (!Object.op_Implicit((Object)(object)cloakRecolor))
				{
					ActiveInstances.RemoveAt(num);
				}
				else
				{
					CloakMaterialApplier.PruneDestroyed(cloakRecolor._originalShaderByRenderer);
					cloakRecolor.ApplyToCachedMeshRenderersCore();
				}
			}
		}

		internal void ForceHierarchyRefresh()
		{
			_meshCacheInvalid = true;
			RebuildMeshCache();
			CloakMaterialApplier.InvalidateSubtree(((Component)this).transform);
			ApplyToCachedMeshRenderersCore();
		}

		private void LateUpdate()
		{
			CloakMaterialApplier.PruneDestroyed(_originalShaderByRenderer);
			MaybeRefreshMeshCache();
			ApplyToCachedMeshRenderersCore();
		}

		public void Configure(CloakColor color, bool useCloakShader, float? textureSaturationBoostOverride = null)
		{
			Color = color;
			UseCloakShader = useCloakShader;
			_textureSaturationBoostOverride = textureSaturationBoostOverride;
			CloakSceneScanner.EnsureCreated();
			CloakSceneScanner.ReleaseEligibleUnderTransform(((Component)this).transform);
			_meshCacheInvalid = true;
			RebuildMeshCache();
			CloakMaterialApplier.InvalidateSubtree(((Component)this).transform);
			ApplyToCachedMeshRenderersCore();
		}

		public void SetColor(CloakColor color)
		{
			Color = color;
			ApplyToCachedMeshRenderersCore();
		}

		private void MaybeRefreshMeshCache()
		{
			if (_meshCacheInvalid)
			{
				RebuildMeshCache();
			}
			else if (--_meshRescanCountdown <= 0)
			{
				RebuildMeshCache();
			}
		}

		private void RebuildMeshCache()
		{
			_meshCacheInvalid = false;
			_meshRescanCountdown = Mathf.Max(1, CloakPaletteConfig.HeroMeshRescanIntervalFrames);
			_meshCache.Clear();
			_meshCacheSet.Clear();
			MeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<MeshRenderer>(true);
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return;
			}
			MeshRenderer[] array = componentsInChildren;
			foreach (MeshRenderer val in array)
			{
				if (!((Object)(object)val == (Object)null) && !IsUnderSsmpUsernameObject(((Component)val).transform))
				{
					_meshCache.Add(val);
					_meshCacheSet.Add(val);
				}
			}
		}

		private void EnsureMeshRendererTracked(MeshRenderer meshRenderer)
		{
			if (!((Object)(object)meshRenderer == (Object)null) && !IsUnderSsmpUsernameObject(((Component)meshRenderer).transform) && _meshCacheSet.Add(meshRenderer))
			{
				_meshCache.Add(meshRenderer);
			}
		}

		internal void RefreshMeshRendererNow(MeshRenderer mr)
		{
			if (!((Object)(object)mr == (Object)null) && !IsUnderSsmpUsernameObject(((Component)mr).transform))
			{
				EnsureMeshRendererTracked(mr);
				CloakMaterialApplier.InvalidateRenderer(mr);
				CloakMaterialApplier.Apply(mr, CloakMaterialApplier.ResolveTk2dSprite(mr), Color, UseCloakShader, _originalShaderByRenderer, _textureSaturationBoostOverride);
			}
		}

		private void ApplyToCachedMeshRenderersCore()
		{
			foreach (MeshRenderer item in _meshCache)
			{
				if ((Object)(object)item == (Object)null)
				{
					_meshCacheInvalid = true;
				}
				else if (!IsUnderSsmpUsernameObject(((Component)item).transform))
				{
					CloakMaterialApplier.Apply(item, null, Color, UseCloakShader, _originalShaderByRenderer, _textureSaturationBoostOverride);
				}
			}
		}

		private static bool IsUnderSsmpUsernameObject(Transform t)
		{
			Transform val = t;
			while ((Object)(object)val != (Object)null)
			{
				if (((Object)val).name == "Username")
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		public static CloakRecolor? AttachOrUpdate(GameObject? playerObject, CloakColor color, bool useCloakShader, float? textureSaturationBoostOverride = null)
		{
			if ((Object)(object)playerObject == (Object)null)
			{
				return null;
			}
			CloakRecolor cloakRecolor = playerObject.GetComponent<CloakRecolor>();
			if ((Object)(object)cloakRecolor == (Object)null)
			{
				cloakRecolor = playerObject.AddComponent<CloakRecolor>();
			}
			cloakRecolor.Configure(color, useCloakShader, textureSaturationBoostOverride);
			return cloakRecolor;
		}
	}
	[DefaultExecutionOrder(10000)]
	[DisallowMultipleComponent]
	internal class CloakSceneScanner : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <RebuildEligibleCacheCo>d__29 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public CloakSceneScanner <>4__this;

			private Stopwatch <sw>5__2;

			private SpriteRenderer[] <>7__wrap2;

			private int <>7__wrap3;

			private tk2dSprite[] <>7__wrap4;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <RebuildEligibleCacheCo>d__29(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<sw>5__2 = null;
				<>7__wrap2 = null;
				<>7__wrap4 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				CloakSceneScanner cloakSceneScanner = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					<sw>5__2 = Stopwatch.StartNew();
					SpriteRenderer[] array2 = Object.FindObjectsByType<SpriteRenderer>((FindObjectsInactive)1, (FindObjectsSortMode)0);
					if (array2 != null)
					{
						<>7__wrap2 = array2;
						<>7__wrap3 = 0;
						goto IL_00c5;
					}
					goto IL_00dc;
				}
				case 1:
					<>1__state = -1;
					<sw>5__2.Restart();
					goto IL_00b7;
				case 2:
				{
					<>1__state = -1;
					tk2dSprite[] array = Object.FindObjectsByType<tk2dSprite>((FindObjectsSortMode)0);
					if (array == null)
					{
						break;
					}
					<>7__wrap4 = array;
					<>7__wrap3 = 0;
					goto IL_0185;
				}
				case 3:
					{
						<>1__state = -1;
						<sw>5__2.Restart();
						goto IL_0177;
					}
					IL_0185:
					if (<>7__wrap3 < <>7__wrap4.Length)
					{
						tk2dSprite val = <>7__wrap4[<>7__wrap3];
						if ((Object)(object)val != (Object)null)
						{
							cloakSceneScanner.TryEnrollEligible((tk2dBaseSprite)(object)val, "Rescan");
						}
						if (<sw>5__2.Elapsed.TotalMilliseconds > 1.5)
						{
							<>2__current = null;
							<>1__state = 3;
							return true;
						}
						goto IL_0177;
					}
					<>7__wrap4 = null;
					break;
					IL_00dc:
					<>2__current = null;
					<>1__state = 2;
					return true;
					IL_00b7:
					<>7__wrap3++;
					goto IL_00c5;
					IL_00c5:
					if (<>7__wrap3 < <>7__wrap2.Length)
					{
						SpriteRenderer val2 = <>7__wrap2[<>7__wrap3];
						if ((Object)(object)val2 != (Object)null)
						{
							CloakSpriteRendererTint.Watch(val2);
						}
						if (<sw>5__2.Elapsed.TotalMilliseconds > 1.5)
						{
							<>2__current = null;
							<>1__state = 1;
							return true;
						}
						goto IL_00b7;
					}
					<>7__wrap2 = null;
					goto IL_00dc;
					IL_0177:
					<>7__wrap3++;
					goto IL_0185;
				}
				cloakSceneScanner._rebuildCo = null;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const float RescanIntervalSec = 2f;

		private const double RebuildBudgetMs = 1.5;

		private CloakColor _color = CloakColor.Default;

		private readonly Dictionary<MeshRenderer, Shader> _originalShaderByRenderer = new Dictionary<MeshRenderer, Shader>();

		private readonly HashSet<int> _loggedTextureIds = new HashSet<int>();

		private readonly HashSet<int> _loggedRendererIds = new HashSet<int>();

		private readonly List<MeshRenderer> _eligibleCache = new List<MeshRenderer>();

		private readonly Dictionary<MeshRenderer, tk2dBaseSprite> _eligibleSprite = new Dictionary<MeshRenderer, tk2dBaseSprite>();

		private readonly HashSet<MeshRenderer> _eligibleSet = new HashSet<MeshRenderer>();

		private bool _eligibleCacheDirty = true;

		private float _nextRescanTime;

		private Coroutine? _rebuildCo;

		public static CloakSceneScanner? Instance { get; private set; }

		public static void EnsureCreated()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!((Object)(object)Instance != (Object)null))
			{
				GameObject val = new GameObject("HornetCloakColorSceneScanner");
				Object.DontDestroyOnLoad((Object)val);
				Instance = val.AddComponent<CloakSceneScanner>();
			}
		}

		internal static void ReleaseEligibleUnderTransform(Transform? heroRoot)
		{
			if (!((Object)(object)Instance == (Object)null) && !((Object)(object)heroRoot == (Object)null))
			{
				Instance.RemoveEligibleUnderRoot(heroRoot);
			}
		}

		public static void SetColor(CloakColor color)
		{
			EnsureCreated();
			Instance._color = color;
			CloakMaterialApplier.InvalidateAll();
		}

		private void OnEnable()
		{
			SceneManager.activeSceneChanged += OnActiveSceneChanged;
		}

		private void OnDisable()
		{
			SceneManager.activeSceneChanged -= OnActiveSceneChanged;
		}

		private void OnActiveSceneChanged(Scene from, Scene to)
		{
			_eligibleCacheDirty = true;
			CloakMaterialApplier.InvalidateAll();
			if (_rebuildCo != null)
			{
				((MonoBehaviour)this).StopCoroutine(_rebuildCo);
				_rebuildCo = null;
			}
			_eligibleCache.Clear();
			_eligibleSet.Clear();
			_eligibleSprite.Clear();
		}

		private void LateUpdate()
		{
			CloakMaterialApplier.PruneDestroyed(_originalShaderByRenderer);
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_rebuildCo == null && (_eligibleCacheDirty || realtimeSinceStartup >= _nextRescanTime))
			{
				_eligibleCacheDirty = false;
				_nextRescanTime = realtimeSinceStartup + 2f;
				_rebuildCo = ((MonoBehaviour)this).StartCoroutine(RebuildEligibleCacheCo());
			}
			ApplyEligibleCache();
		}

		private void RemoveEligibleUnderRoot(Transform root)
		{
			for (int num = _eligibleCache.Count - 1; num >= 0; num--)
			{
				MeshRenderer val = _eligibleCache[num];
				if ((Object)(object)val == (Object)null)
				{
					_eligibleCache.RemoveAt(num);
				}
				else if (IsTransformUnderAncestor(((Component)val).transform, root))
				{
					_eligibleCache.RemoveAt(num);
					_eligibleSet.Remove(val);
					_eligibleSprite.Remove(val);
					CloakMaterialApplier.InvalidateRenderer(val);
				}
			}
		}

		internal static void OnSpriteSpawned(tk2dSprite sprite)
		{
			if (!((Object)(object)Instance == (Object)null) && !((Object)(object)sprite == (Object)null))
			{
				Instance.TryEnrollEligible((tk2dBaseSprite)(object)sprite, "Spawn");
			}
		}

		internal static void OnTk2dPipelineComplete(tk2dBaseSprite sprite)
		{
			if ((Object)(object)Instance == (Object)null || (Object)(object)sprite == (Object)null || (Object)(object)((Component)sprite).GetComponentInParent<CloakRecolor>() != (Object)null || IsUnderSsmpPlayerContainer(((Component)sprite).transform))
			{
				return;
			}
			MeshRenderer component = ((Component)sprite).GetComponent<MeshRenderer>();
			if (!((Object)(object)component == (Object)null))
			{
				CloakMaterialApplier.InvalidateRenderer(component);
				if (Instance._eligibleSet.Contains(component))
				{
					Instance._eligibleSprite[component] = sprite;
					CloakMaterialApplier.Apply(component, sprite, Instance._color, useCloakShader: true, Instance._originalShaderByRenderer);
				}
				else
				{
					Instance.TryEnrollEligible(sprite, "Tk2dPipeline");
				}
			}
		}

		private static bool TryEvaluateScannerEligibility(MeshRenderer renderer, tk2dBaseSprite sprite, out bool wouldLogMissingMask)
		{
			wouldLogMissingMask = false;
			if ((Object)(object)renderer == (Object)null || (Object)(object)sprite == (Object)null)
			{
				return false;
			}
			Material sharedMaterial = ((Renderer)renderer).sharedMaterial;
			if ((Object)(object)sharedMaterial == (Object)null)
			{
				return false;
			}
			if (!sharedMaterial.HasProperty(CloakShaderManager.MainTexId))
			{
				return false;
			}
			Texture mainTexture = sharedMaterial.mainTexture;
			if ((Object)(object)mainTexture == (Object)null)
			{
				return false;
			}
			if (IsUnderSsmpPlayerContainer(((Component)renderer).transform))
			{
				return false;
			}
			if (IsUnderLocalHero(((Component)renderer).transform))
			{
				return false;
			}
			if ((Object)(object)((Component)renderer).GetComponentInParent<CloakRecolor>() != (Object)null)
			{
				return false;
			}
			if (IsCompassIcon(((Component)renderer).transform))
			{
				return false;
			}
			string tk2dCollectionName = (((Object)(object)sprite.Collection != (Object)null) ? (((Object)sprite.Collection).name ?? string.Empty) : string.Empty);
			if (CloakMaskManager.TryGetMaskForMainTexture(mainTexture, tk2dCollectionName, out Texture2D _))
			{
				return true;
			}
			wouldLogMissingMask = true;
			return false;
		}

		private void TryEnrollEligible(tk2dBaseSprite sprite, string source)
		{
			MeshRenderer component = ((Component)sprite).GetComponent<MeshRenderer>();
			if ((Object)(object)component == (Object)null || _eligibleSet.Contains(component))
			{
				return;
			}
			if (!TryEvaluateScannerEligibility(component, sprite, out var wouldLogMissingMask))
			{
				if (wouldLogMissingMask)
				{
					MaybeLogIgnoredTexture(component, sprite);
				}
				return;
			}
			_eligibleSet.Add(component);
			_eligibleCache.Add(component);
			_eligibleSprite[component] = sprite;
			if (CloakPaletteConfig.DebugLogging && _loggedRendererIds.Add(((Object)component).GetInstanceID()))
			{
				Texture val = (((Object)(object)((Renderer)component).sharedMaterial != (Object)null) ? ((Renderer)component).sharedMaterial.mainTexture : null);
				string arg = (((Object)(object)val != (Object)null) ? ((Object)val).name : "(null)");
				int num = (((Object)(object)val != (Object)null) ? ((Object)val).GetInstanceID() : 0);
				string arg2 = (((Object)(object)sprite.Collection != (Object)null) ? (((Object)sprite.Collection).name ?? string.Empty) : string.Empty);
				Log.Info("[Scanner/" + source + "] Tinting masked orphan renderer '" + FormatTransformPath(((Component)component).transform) + "' " + $"(tex={arg}, collection='{arg2}', id={num})");
			}
			CloakMaterialApplier.Apply(component, sprite, _color, useCloakShader: true, _originalShaderByRenderer);
		}

		private void MaybeLogIgnoredTexture(MeshRenderer renderer, tk2dBaseSprite sprite)
		{
			if (!CloakPaletteConfig.DebugLogging)
			{
				return;
			}
			Material sharedMaterial = ((Renderer)renderer).sharedMaterial;
			if (!((Object)(object)sharedMaterial == (Object)null) && sharedMaterial.HasProperty(CloakShaderManager.MainTexId))
			{
				Texture mainTexture = sharedMaterial.mainTexture;
				if (!((Object)(object)mainTexture == (Object)null) && _loggedTextureIds.Add(((Object)mainTexture).GetInstanceID()))
				{
					string text = (((Object)(object)sprite.Collection != (Object)null) ? (((Object)sprite.Collection).name ?? string.Empty) : string.Empty);
					Log.Info("[Scanner] Ignored texture (no CloakMasks PNG): " + ((Object)mainTexture).name + " " + $"(id={((Object)mainTexture).GetInstanceID()}) on '{FormatTransformPath(((Component)renderer).transform)}' " + "(collection='" + text + "')");
				}
			}
		}

		[IteratorStateMachine(typeof(<RebuildEligibleCacheCo>d__29))]
		private IEnumerator RebuildEligibleCacheCo()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <RebuildEligibleCacheCo>d__29(0)
			{
				<>4__this = this
			};
		}

		private void ApplyEligibleCache()
		{
			for (int num = _eligibleCache.Count - 1; num >= 0; num--)
			{
				MeshRenderer val = _eligibleCache[num];
				if ((Object)(object)val == (Object)null)
	

plugins/HornetCloakColor.SSMP.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using HornetCloakColor.Client;
using HornetCloakColor.Server;
using HornetCloakColor.Shared;
using Microsoft.CodeAnalysis;
using SSMP.Api.Client;
using SSMP.Api.Client.Networking;
using SSMP.Api.Server;
using SSMP.Api.Server.Networking;
using SSMP.Game;
using SSMP.Networking.Packet;
using SSMP.Networking.Packet.Data;
using UnityEngine;

[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("HornetCloakColor.SSMP")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+0eba9b2e5df530332fab65160f0c51fed55f93df")]
[assembly: AssemblyProduct("HornetCloakColor.SSMP")]
[assembly: AssemblyTitle("HornetCloakColor.SSMP")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 HornetCloakColor.SSMPIntegration
{
	public static class SatelliteEntry
	{
		public static void Register()
		{
			ClientAddon clientAddon = new ClientAddon();
			ServerAddon serverAddon = new ServerAddon();
			ClientAddon.RegisterAddon((ClientAddon)(object)clientAddon);
			ServerAddon.RegisterAddon((ServerAddon)(object)serverAddon);
		}

		public static void NotifyLocalCloakAppearanceChanged(CloakNetAppearance appearance)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			ClientAddon.Instance?.SetLocalCloakAppearance(appearance);
		}

		public static CloakColor GetRemoteMapColorOrDefault(ushort playerId)
		{
			//IL_0010: 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)
			return ClientAddon.Instance?.GetRemoteMapColorOrDefault(playerId) ?? CloakColor.Default;
		}

		public static void NotifyLocalUsernameColorChanged(CloakColor color)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			ClientAddon.Instance?.SetLocalUsernameTint(hasCustomUsernameTint: true, color);
		}

		public static void ClearLocalUsernameColorOnNetwork()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			ClientAddon.Instance?.SetLocalUsernameTint(hasCustomUsernameTint: false, default(CloakColor));
		}

		public static void ResendStoredLocalColorsToServer()
		{
			ClientAddon.Instance?.ForceResendLocalColorsToServer();
		}
	}
}
namespace HornetCloakColor.Shared
{
	internal enum PacketId
	{
		CloakColorUpdate,
		UsernameColorUpdate,
		ServerUsernameColorRules
	}
	internal class CloakColorPacket : IPacketData
	{
		public ushort PlayerId;

		public CloakColor Color;

		public byte TextureSaturationCenti = 100;

		public bool IsReliable => true;

		public bool DropReliableDataIfNewerExists => true;

		public void WriteData(IPacket packet)
		{
			packet.Write(PlayerId);
			packet.Write(((CloakColor)(ref Color)).R);
			packet.Write(((CloakColor)(ref Color)).G);
			packet.Write(((CloakColor)(ref Color)).B);
			packet.Write(TextureSaturationCenti);
		}

		public void ReadData(IPacket packet)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			PlayerId = packet.ReadUShort();
			byte b = packet.ReadByte();
			byte b2 = packet.ReadByte();
			byte b3 = packet.ReadByte();
			Color = new CloakColor(b, b2, b3);
			TextureSaturationCenti = packet.ReadByte();
		}
	}
	internal class UsernameColorPacket : IPacketData
	{
		public ushort PlayerId;

		public CloakColor Color;

		public bool HasCustomUsernameTint;

		public bool IsReliable => true;

		public bool DropReliableDataIfNewerExists => true;

		public void WriteData(IPacket packet)
		{
			packet.Write(PlayerId);
			packet.Write(((CloakColor)(ref Color)).R);
			packet.Write(((CloakColor)(ref Color)).G);
			packet.Write(((CloakColor)(ref Color)).B);
			packet.Write(HasCustomUsernameTint);
		}

		public void ReadData(IPacket packet)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			PlayerId = packet.ReadUShort();
			byte b = packet.ReadByte();
			byte b2 = packet.ReadByte();
			byte b3 = packet.ReadByte();
			Color = new CloakColor(b, b2, b3);
			HasCustomUsernameTint = packet.ReadBool();
		}
	}
	internal class ServerUsernameColorRulesPacket : IPacketData
	{
		public bool CustomUsernameColorsOverrideTeamColors;

		public bool IsReliable => true;

		public bool DropReliableDataIfNewerExists => true;

		public void WriteData(IPacket packet)
		{
			packet.Write(CustomUsernameColorsOverrideTeamColors);
		}

		public void ReadData(IPacket packet)
		{
			CustomUsernameColorsOverrideTeamColors = packet.ReadBool();
		}
	}
	internal static class ServerAddonReceivePacketFactory
	{
		public static IPacketData Instantiate(PacketId id)
		{
			return (IPacketData)(id switch
			{
				PacketId.CloakColorUpdate => new CloakColorPacket(), 
				PacketId.UsernameColorUpdate => new UsernameColorPacket(), 
				PacketId.ServerUsernameColorRules => new ServerUsernameColorRulesPacket(), 
				_ => new CloakColorPacket(), 
			});
		}
	}
	internal static class ClientAddonReceivePacketFactory
	{
		public static IPacketData Instantiate(PacketId id)
		{
			return (IPacketData)(id switch
			{
				PacketId.CloakColorUpdate => new PacketDataCollection<CloakColorPacket>(), 
				PacketId.UsernameColorUpdate => new PacketDataCollection<UsernameColorPacket>(), 
				PacketId.ServerUsernameColorRules => new ServerUsernameColorRulesPacket(), 
				_ => new PacketDataCollection<CloakColorPacket>(), 
			});
		}
	}
}
namespace HornetCloakColor.Server
{
	internal sealed class PlayerCosmeticsTracker
	{
		private readonly Dictionary<ushort, CloakNetAppearance> _cloaks = new Dictionary<ushort, CloakNetAppearance>();

		private readonly Dictionary<ushort, CloakColor> _usernameTints = new Dictionary<ushort, CloakColor>();

		internal IReadOnlyDictionary<ushort, CloakNetAppearance> Cloaks => _cloaks;

		internal IReadOnlyDictionary<ushort, CloakColor> UsernameTints => _usernameTints;

		internal void SetCloak(ushort playerId, CloakNetAppearance appearance)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			_cloaks[playerId] = appearance;
		}

		internal void SetUsernameTint(ushort playerId, bool hasCustomTint, CloakColor color)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (!hasCustomTint)
			{
				_usernameTints.Remove(playerId);
			}
			else
			{
				_usernameTints[playerId] = color;
			}
		}

		internal void RemovePlayer(ushort playerId)
		{
			_cloaks.Remove(playerId);
			_usernameTints.Remove(playerId);
		}
	}
	internal class ServerAddon : ServerAddon
	{
		private readonly PlayerCosmeticsTracker _tracker = new PlayerCosmeticsTracker();

		private readonly bool _customUsernameColorsOverrideTeamColors = ServerUsernameRulesStore.LoadCustomUsernameColorsOverrideTeamColors();

		private readonly HashSet<ushort> _sceneCatchUpSent = new HashSet<ushort>();

		private IServerApi? _api;

		protected override string Name => "HornetCloakColor";

		protected override string Version => "1.15.0";

		public override uint ApiVersion => 1u;

		public override bool NeedsNetwork => true;

		public override void Initialize(IServerApi serverApi)
		{
			_api = serverApi;
			ServerCosmeticsPacketSender.Init(serverApi.NetServer.GetNetworkSender<PacketId>((ServerAddon)(object)this));
			IServerAddonNetworkReceiver<PacketId> networkReceiver = serverApi.NetServer.GetNetworkReceiver<PacketId>((ServerAddon)(object)this, (Func<PacketId, IPacketData>)ServerAddonReceivePacketFactory.Instantiate);
			networkReceiver.RegisterPacketHandler<CloakColorPacket>(PacketId.CloakColorUpdate, (GenericServerPacketHandler<CloakColorPacket>)OnCloakColorUpdate);
			networkReceiver.RegisterPacketHandler<UsernameColorPacket>(PacketId.UsernameColorUpdate, (GenericServerPacketHandler<UsernameColorPacket>)OnUsernameColorUpdate);
			serverApi.ServerManager.PlayerConnectEvent += OnPlayerConnect;
			serverApi.ServerManager.PlayerEnterSceneEvent += OnPlayerEnterSceneFirstCatchUp;
			serverApi.ServerManager.PlayerDisconnectEvent += OnPlayerDisconnect;
			Log.Info(_customUsernameColorsOverrideTeamColors ? "Server addon initialized (custom username colors may override team colors)." : "Server addon initialized (team colors take precedence over custom username colors when teams are used).");
		}

		private void OnPlayerConnect(IServerPlayer player)
		{
			PushJoinSnapshotTo(player, "PlayerConnect");
		}

		private void OnPlayerEnterSceneFirstCatchUp(IServerPlayer player)
		{
			if (_sceneCatchUpSent.Add(player.Id))
			{
				PushJoinSnapshotTo(player, "first scene catch-up");
			}
		}

		private void PushJoinSnapshotTo(IServerPlayer player, string reason)
		{
			ServerCosmeticsPacketSender.SendUsernameRulesTo(player.Id, _customUsernameColorsOverrideTeamColors);
			ServerCosmeticsPacketSender.SendAllOtherPlayersCloaksTo(player.Id, _tracker);
			ServerCosmeticsPacketSender.SendAllOtherPlayersUsernameTintsTo(player.Id, _tracker);
			Log.Info($"Cosmetics snapshot ({reason}) → player {player.Id} (rules + others' cloak/username).");
		}

		private void OnPlayerDisconnect(IServerPlayer player)
		{
			_tracker.RemovePlayer(player.Id);
			_sceneCatchUpSent.Remove(player.Id);
		}

		private void OnCloakColorUpdate(ushort senderId, CloakColorPacket data)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			CloakNetAppearance appearance = default(CloakNetAppearance);
			((CloakNetAppearance)(ref appearance))..ctor(data.Color, data.TextureSaturationCenti);
			_tracker.SetCloak(senderId, appearance);
			if (_api != null)
			{
				ServerCosmeticsPacketSender.BroadcastCloakToOthers(senderId, appearance, _api.ServerManager.Players);
			}
		}

		private void OnUsernameColorUpdate(ushort senderId, UsernameColorPacket data)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			_tracker.SetUsernameTint(senderId, data.HasCustomUsernameTint, data.Color);
			if (_api != null)
			{
				ServerCosmeticsPacketSender.BroadcastUsernameTintToOthers(senderId, data.Color, data.HasCustomUsernameTint, _api.ServerManager.Players);
			}
		}
	}
	internal static class ServerCosmeticsPacketSender
	{
		private static IServerAddonNetworkSender<PacketId>? _sender;

		internal static void Init(IServerAddonNetworkSender<PacketId> sender)
		{
			_sender = sender;
		}

		internal static void SendUsernameRulesTo(ushort recipientId, bool customUsernameColorsOverrideTeamColors)
		{
			if (_sender != null)
			{
				_sender.SendSingleData(PacketId.ServerUsernameColorRules, (IPacketData)(object)new ServerUsernameColorRulesPacket
				{
					CustomUsernameColorsOverrideTeamColors = customUsernameColorsOverrideTeamColors
				}, recipientId);
			}
		}

		internal static void SendAllOtherPlayersCloaksTo(ushort recipientId, PlayerCosmeticsTracker tracker)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (_sender == null)
			{
				return;
			}
			foreach (KeyValuePair<ushort, CloakNetAppearance> cloak in tracker.Cloaks)
			{
				if (cloak.Key != recipientId)
				{
					IServerAddonNetworkSender<PacketId>? sender = _sender;
					CloakColorPacket obj = new CloakColorPacket
					{
						PlayerId = cloak.Key
					};
					CloakNetAppearance value = cloak.Value;
					obj.Color = ((CloakNetAppearance)(ref value)).Color;
					value = cloak.Value;
					obj.TextureSaturationCenti = ((CloakNetAppearance)(ref value)).TextureSaturationCenti;
					sender.SendCollectionData<CloakColorPacket>(PacketId.CloakColorUpdate, obj, recipientId);
				}
			}
		}

		internal static void SendAllOtherPlayersUsernameTintsTo(ushort recipientId, PlayerCosmeticsTracker tracker)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			if (_sender == null)
			{
				return;
			}
			foreach (KeyValuePair<ushort, CloakColor> usernameTint in tracker.UsernameTints)
			{
				if (usernameTint.Key != recipientId)
				{
					_sender.SendCollectionData<UsernameColorPacket>(PacketId.UsernameColorUpdate, new UsernameColorPacket
					{
						PlayerId = usernameTint.Key,
						Color = usernameTint.Value,
						HasCustomUsernameTint = true
					}, recipientId);
				}
			}
		}

		internal static void BroadcastCloakToOthers(ushort senderId, CloakNetAppearance appearance, IReadOnlyCollection<IServerPlayer> players)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (_sender == null)
			{
				return;
			}
			foreach (IServerPlayer player in players)
			{
				if (player.Id != senderId)
				{
					_sender.SendCollectionData<CloakColorPacket>(PacketId.CloakColorUpdate, new CloakColorPacket
					{
						PlayerId = senderId,
						Color = ((CloakNetAppearance)(ref appearance)).Color,
						TextureSaturationCenti = ((CloakNetAppearance)(ref appearance)).TextureSaturationCenti
					}, player.Id);
				}
			}
		}

		internal static void BroadcastUsernameTintToOthers(ushort senderId, CloakColor color, bool hasCustomUsernameTint, IReadOnlyCollection<IServerPlayer> players)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (_sender == null)
			{
				return;
			}
			foreach (IServerPlayer player in players)
			{
				if (player.Id != senderId)
				{
					_sender.SendCollectionData<UsernameColorPacket>(PacketId.UsernameColorUpdate, new UsernameColorPacket
					{
						PlayerId = senderId,
						Color = color,
						HasCustomUsernameTint = hasCustomUsernameTint
					}, player.Id);
				}
			}
		}
	}
	internal static class ServerUsernameRulesStore
	{
		private const string FileName = "HornetCloakColor.server.json";

		internal static bool LoadCustomUsernameColorsOverrideTeamColors()
		{
			try
			{
				string directoryName = Path.GetDirectoryName(typeof(ServerAddon).Assembly.Location);
				if (string.IsNullOrEmpty(directoryName))
				{
					return false;
				}
				string path = Path.Combine(directoryName, "HornetCloakColor.server.json");
				if (!File.Exists(path))
				{
					return false;
				}
				Match match = Regex.Match(File.ReadAllText(path), "\"customUsernameColorsOverrideTeamColors\"\\s*:\\s*(true|false)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
				return match.Success && string.Equals(match.Groups[1].Value, "true", StringComparison.OrdinalIgnoreCase);
			}
			catch
			{
				return false;
			}
		}
	}
}
namespace HornetCloakColor.Client
{
	internal class ClientAddon : ClientAddon
	{
		private const string UsernameObjectName = "Username";

		private readonly Dictionary<ushort, CloakNetAppearance> _playerCloakAppearances = new Dictionary<ushort, CloakNetAppearance>();

		private readonly Dictionary<ushort, CloakColor> _playerUsernameColors = new Dictionary<ushort, CloakColor>();

		private IClientApi? _api;

		private CloakNetAppearance _localCloakAppearance = CloakNetAppearance.Default;

		private CloakColor _localUsernameColor = CloakColor.Default;

		private bool _localUsernameHasCustomTint;

		private bool _serverCustomUsernameOverridesTeam;

		protected override string Name => "HornetCloakColor";

		protected override string Version => "1.15.0";

		public override uint ApiVersion => 1u;

		public override bool NeedsNetwork => true;

		internal static ClientAddon? Instance { get; private set; }

		public override void Initialize(IClientApi clientApi)
		{
			Instance = this;
			_api = clientApi;
			IClientAddonNetworkSender<PacketId> networkSender = clientApi.NetClient.GetNetworkSender<PacketId>((ClientAddon)(object)this);
			ClientCosmeticsPacketSender.Init(clientApi, networkSender);
			IClientAddonNetworkReceiver<PacketId> networkReceiver = clientApi.NetClient.GetNetworkReceiver<PacketId>((ClientAddon)(object)this, (Func<PacketId, IPacketData>)ClientAddonReceivePacketFactory.Instantiate);
			networkReceiver.RegisterPacketHandler<CloakColorPacket>(PacketId.CloakColorUpdate, (GenericClientPacketHandler<CloakColorPacket>)OnCloakColorUpdate);
			networkReceiver.RegisterPacketHandler<UsernameColorPacket>(PacketId.UsernameColorUpdate, (GenericClientPacketHandler<UsernameColorPacket>)OnUsernameColorUpdate);
			networkReceiver.RegisterPacketHandler<ServerUsernameColorRulesPacket>(PacketId.ServerUsernameColorRules, (GenericClientPacketHandler<ServerUsernameColorRulesPacket>)OnServerUsernameColorRules);
			clientApi.ClientManager.ConnectEvent += OnConnected;
			clientApi.ClientManager.DisconnectEvent += OnDisconnected;
			clientApi.ClientManager.PlayerEnterSceneEvent += OnPlayerEnterScene;
			clientApi.ClientManager.PlayerDisconnectEvent += OnPlayerDisconnect;
			PushUsernameDelegates();
			Log.Info("Client addon initialized.");
		}

		private void PushUsernameDelegates()
		{
			UsernameNetworkDelegates.TryResolveUsernameTransform = TryResolveUsernameTransform;
			UsernameNetworkDelegates.GetRemoteUsernameTintOrNull = GetRemoteUsernameTintOrNull;
			UsernameNetworkDelegates.GetTeamsEnabled = () => _api.ClientManager.ServerSettings.TeamsEnabled;
			UsernameNetworkDelegates.GetServerCustomUsernameOverridesTeam = () => _serverCustomUsernameOverridesTeam;
			UsernameNetworkDelegates.GetLocalPlayerTeamOrdinal = () => (int)_api.ClientManager.PlayerManager.LocalPlayerTeam;
		}

		public void SetLocalCloakAppearance(CloakNetAppearance appearance)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			_localCloakAppearance = appearance;
			ClientCosmeticsPacketSender.TrySendCloakUpdate(_localCloakAppearance);
		}

		public void SetLocalUsernameTint(bool hasCustomUsernameTint, CloakColor color)
		{
			//IL_0008: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			_localUsernameHasCustomTint = hasCustomUsernameTint;
			_localUsernameColor = color;
			ClientCosmeticsPacketSender.TrySendUsernameUpdate(_localUsernameHasCustomTint, _localUsernameColor);
		}

		internal CloakColor GetRemoteMapColorOrDefault(ushort playerId)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (!_playerCloakAppearances.TryGetValue(playerId, out var value))
			{
				return CloakColor.Default;
			}
			return ((CloakNetAppearance)(ref value)).Color;
		}

		internal CloakColor? GetRemoteUsernameTintOrNull(ushort playerId)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (!_playerUsernameColors.TryGetValue(playerId, out var value))
			{
				return null;
			}
			return value;
		}

		private UsernameResolveResult TryResolveUsernameTransform(Transform t)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)t == (Object)null)
			{
				return default(UsernameResolveResult);
			}
			if ((Object)(object)HeroController.instance != (Object)null && t.IsChildOf(HeroController.instance.transform))
			{
				if (TryGetLocalNetworkPlayerId(out var id))
				{
					return new UsernameResolveResult(true, true, id);
				}
				return new UsernameResolveResult(true, true, (ushort)0);
			}
			if (TryResolveRemoteByPlayerContainerAncestor(t, out var playerId))
			{
				return new UsernameResolveResult(true, false, playerId);
			}
			if (_api == null)
			{
				return default(UsernameResolveResult);
			}
			foreach (IClientPlayer player in _api.ClientManager.Players)
			{
				if (!((Object)(object)player.PlayerContainer == (Object)null) && t.IsChildOf(player.PlayerContainer.transform))
				{
					return new UsernameResolveResult(true, false, player.Id);
				}
			}
			return default(UsernameResolveResult);
		}

		private static bool TryResolveRemoteByPlayerContainerAncestor(Transform t, out ushort playerId)
		{
			playerId = 0;
			Transform val = t;
			while ((Object)(object)val != (Object)null)
			{
				string name = ((Object)val).name;
				if (name.StartsWith("Player Container ", StringComparison.Ordinal))
				{
					return ushort.TryParse(name.Substring("Player Container ".Length), NumberStyles.Integer, CultureInfo.InvariantCulture, out playerId);
				}
				val = val.parent;
			}
			return false;
		}

		private bool TryGetLocalNetworkPlayerId(out ushort id)
		{
			id = 0;
			if (_api == null || (Object)(object)HeroController.instance == (Object)null)
			{
				return false;
			}
			foreach (IClientPlayer player in _api.ClientManager.Players)
			{
				if ((Object)(object)player.PlayerObject != (Object)null && player.PlayerObject == ((Component)HeroController.instance).gameObject)
				{
					id = player.Id;
					return true;
				}
			}
			return false;
		}

		internal void ForceResendLocalColorsToServer()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			ClientCosmeticsPacketSender.TrySendStoredLocal(_localCloakAppearance, _localUsernameHasCustomTint, _localUsernameColor);
		}

		private void OnConnected()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			PushUsernameDelegates();
			ClientCosmeticsPacketSender.TrySendStoredLocal(_localCloakAppearance, _localUsernameHasCustomTint, _localUsernameColor);
			SSMPBridge.SchedulePostConnectColorResend();
		}

		private void OnDisconnected()
		{
			UsernameNetworkDelegates.Clear();
			_playerUsernameColors.Clear();
			_serverCustomUsernameOverridesTeam = false;
		}

		private void OnPlayerEnterScene(IClientPlayer player)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (_playerCloakAppearances.TryGetValue(player.Id, out var value) && (Object)(object)player.PlayerObject != (Object)null)
			{
				CloakColorApplier.Apply(player.PlayerObject, ((CloakNetAppearance)(ref value)).Color, (float?)((CloakNetAppearance)(ref value)).TextureSaturationMultiplier);
				if (!IsLocalHeroPlayer(player))
				{
					MpRemoteCloakReapply.Schedule(player.PlayerObject, value);
				}
			}
			if (!IsLocalHeroPlayer(player))
			{
				RefreshRemoteUsernameVisual(player.Id);
			}
		}

		private static bool IsLocalHeroPlayer(IClientPlayer player)
		{
			HeroController instance = HeroController.instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)player.PlayerObject != (Object)null)
			{
				return player.PlayerObject == ((Component)instance).gameObject;
			}
			return false;
		}

		private void OnPlayerDisconnect(IClientPlayer player)
		{
			_playerCloakAppearances.Remove(player.Id);
			_playerUsernameColors.Remove(player.Id);
		}

		private void OnCloakColorUpdate(CloakColorPacket data)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			CloakNetAppearance val = default(CloakNetAppearance);
			((CloakNetAppearance)(ref val))..ctor(data.Color, data.TextureSaturationCenti);
			_playerCloakAppearances[data.PlayerId] = val;
			PlayerMapMaskTintRegistry.SetColor(data.PlayerId, ((CloakNetAppearance)(ref val)).Color);
			IClientApi? api = _api;
			IClientPlayer val2 = ((api != null) ? api.ClientManager.GetPlayer(data.PlayerId) : null);
			if ((Object)(object)((val2 != null) ? val2.PlayerObject : null) != (Object)null)
			{
				CloakColorApplier.Apply(val2.PlayerObject, ((CloakNetAppearance)(ref val)).Color, (float?)((CloakNetAppearance)(ref val)).TextureSaturationMultiplier);
				MpRemoteCloakReapply.Schedule(val2.PlayerObject, val);
			}
		}

		private void OnServerUsernameColorRules(ServerUsernameColorRulesPacket data)
		{
			_serverCustomUsernameOverridesTeam = data.CustomUsernameColorsOverrideTeamColors;
		}

		private void OnUsernameColorUpdate(UsernameColorPacket data)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (!data.HasCustomUsernameTint)
			{
				_playerUsernameColors.Remove(data.PlayerId);
			}
			else
			{
				_playerUsernameColors[data.PlayerId] = data.Color;
			}
			RefreshRemoteUsernameVisual(data.PlayerId);
		}

		private void RefreshRemoteUsernameVisual(ushort playerId)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected I4, but got Unknown
			IClientPlayer val = default(IClientPlayer);
			if (_api == null || !_api.ClientManager.TryGetPlayer(playerId, ref val) || val == null)
			{
				return;
			}
			GameObject playerContainer = val.PlayerContainer;
			if ((Object)(object)playerContainer == (Object)null)
			{
				return;
			}
			GameObject val2 = FindDeepChildByName(playerContainer, "Username");
			if ((Object)(object)val2 == (Object)null)
			{
				return;
			}
			Component val3 = UsernameTmpCompat.FindOnGameObject(val2);
			if (!((Object)(object)val3 == (Object)null))
			{
				SsmpUsernameVanillaColors.ApplyTeamColor(val3, val.Team);
				if (_playerUsernameColors.ContainsKey(playerId))
				{
					UsernameTintCoordinator.ApplyRemoteUsernameAfterSync(val3, playerId, (int)val.Team);
				}
			}
		}

		private static GameObject? FindDeepChildByName(GameObject root, string name)
		{
			Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (((Object)val).name == name)
				{
					return ((Component)val).gameObject;
				}
			}
			return null;
		}
	}
	internal static class ClientCosmeticsPacketSender
	{
		private static IClientApi? _api;

		private static IClientAddonNetworkSender<PacketId>? _sender;

		internal static void Init(IClientApi api, IClientAddonNetworkSender<PacketId> sender)
		{
			_api = api;
			_sender = sender;
		}

		internal static void TrySendCloakUpdate(CloakNetAppearance appearance)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (_api == null || _sender == null || !_api.NetClient.IsConnected)
			{
				return;
			}
			try
			{
				_sender.SendSingleData(PacketId.CloakColorUpdate, (IPacketData)(object)new CloakColorPacket
				{
					PlayerId = 0,
					Color = ((CloakNetAppearance)(ref appearance)).Color,
					TextureSaturationCenti = ((CloakNetAppearance)(ref appearance)).TextureSaturationCenti
				});
			}
			catch (Exception ex)
			{
				Log.Warn("HornetCloakColor: failed to send cloak color to server (" + ex.GetType().Name + ": " + ex.Message + ").");
			}
		}

		internal static void TrySendUsernameUpdate(bool hasCustomUsernameTint, CloakColor color)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (_api == null || _sender == null || !_api.NetClient.IsConnected)
			{
				return;
			}
			try
			{
				_sender.SendSingleData(PacketId.UsernameColorUpdate, (IPacketData)(object)new UsernameColorPacket
				{
					PlayerId = 0,
					Color = color,
					HasCustomUsernameTint = hasCustomUsernameTint
				});
			}
			catch (Exception ex)
			{
				Log.Warn("HornetCloakColor: failed to send username tint to server (" + ex.GetType().Name + ": " + ex.Message + ").");
			}
		}

		internal static void TrySendStoredLocal(CloakNetAppearance cloak, bool hasCustomUsernameTint, CloakColor usernameColor)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			TrySendCloakUpdate(cloak);
			TrySendUsernameUpdate(hasCustomUsernameTint, usernameColor);
		}
	}
	internal static class SsmpUsernameVanillaColors
	{
		internal static void ApplyTeamColor(Component? textMeshObject, Team team)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected I4, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			Color white = default(Color);
			switch (team - 1)
			{
			case 0:
				((Color)(ref white))..ctor(0f, 0.5882353f, 0f);
				break;
			case 1:
				((Color)(ref white))..ctor(40f / 51f, 0.5882353f, 0f);
				break;
			case 2:
				((Color)(ref white))..ctor(50f / 51f, 10f / 51f, 10f / 51f);
				break;
			case 3:
				((Color)(ref white))..ctor(10f / 51f, 0.5882353f, 40f / 51f);
				break;
			default:
				white = Color.white;
				break;
			}
			UsernameTmpCompat.SetColor(textMeshObject, white);
		}
	}
}