Decompiled source of PrideEnemies v1.0.0

BepInEx/plugins/PrideEnemies/PrideEnemies.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("coruscnium")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Replaces enemy textures with randomly assigned pride flags")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0.0")]
[assembly: AssemblyProduct("PrideEnemies")]
[assembly: AssemblyTitle("PrideEnemies")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PrideEnemies
{
	internal static class EnemyPainter
	{
		private static readonly string[] PreferredSlots = new string[6] { "_MainTex", "_BaseMap", "_BaseColorMap", "_MainTexture", "_AlbedoMap", "_Albedo" };

		private static readonly string[] NonArtworkSlots = new string[12]
		{
			"normal", "bump", "mask", "metallic", "smoothness", "roughness", "occlusion", "height", "displacement", "detail",
			"specular", "noise"
		};

		private static readonly Dictionary<Renderer, Material[]> Originals = new Dictionary<Renderer, Material[]>();

		private static readonly Dictionary<long, Material> Variants = new Dictionary<long, Material>();

		private static readonly Dictionary<int, int[]> SlotsByShader = new Dictionary<int, int[]>();

		private static readonly HashSet<int> ProbedShaders = new HashSet<int>();

		private static Shader _unlitShader;

		private static bool _unlitMissingLogged;

		private static bool _loggedPaintError;

		public static int PaintedRenderers => Originals.Count;

		private static int IdOf(Object obj)
		{
			return obj.GetInstanceID();
		}

		public static void Paint(EnemyPart part)
		{
			try
			{
				if ((Object)(object)part == (Object)null || FlagLibrary.Count == 0)
				{
					return;
				}
				List<TargetMesh> meshes = part.Meshes;
				if (meshes == null)
				{
					return;
				}
				EnemyBrain brain = part.Brain;
				int num = FlagLibrary.IndexFor(((Object)(object)brain != (Object)null) ? IdOf((Object)(object)brain) : IdOf((Object)(object)part));
				Texture2D val = FlagLibrary.Get(num);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				for (int i = 0; i < meshes.Count; i++)
				{
					TargetMesh val2 = meshes[i];
					if (!((Object)(object)val2 == (Object)null))
					{
						PaintRenderer(val2.Renderer, val, num);
					}
				}
			}
			catch (Exception arg)
			{
				if (!_loggedPaintError)
				{
					_loggedPaintError = true;
					Plugin.Logger.LogError((object)$"Failed to paint an enemy: {arg}");
				}
			}
		}

		private static void PaintRenderer(Renderer renderer, Texture2D flag, int flagIndex)
		{
			if ((Object)(object)renderer == (Object)null || Originals.ContainsKey(renderer))
			{
				return;
			}
			Material[] sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials != null && sharedMaterials.Length != 0)
			{
				Material[] array = (Material[])(object)new Material[sharedMaterials.Length];
				bool flag2 = false;
				for (int i = 0; i < sharedMaterials.Length; i++)
				{
					Material val = VariantOf(sharedMaterials[i], flag, flagIndex);
					array[i] = val ?? sharedMaterials[i];
					flag2 |= (Object)(object)val != (Object)null;
				}
				if (flag2)
				{
					Originals[renderer] = sharedMaterials;
					renderer.sharedMaterials = array;
				}
			}
		}

		private static Material VariantOf(Material source, Texture2D flag, int flagIndex)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source == (Object)null || (Object)(object)source.shader == (Object)null)
			{
				return null;
			}
			long key = ((long)IdOf((Object)(object)source) << 8) | (uint)(flagIndex & 0xFF);
			if (Variants.TryGetValue(key, out var value))
			{
				if ((Object)(object)value != (Object)null)
				{
					return value;
				}
				Variants.Remove(key);
			}
			Probe(source.shader);
			int[] array = SlotsFor(source.shader);
			Material val;
			if (array.Length != 0)
			{
				val = new Material(source)
				{
					name = ((Object)source).name + " [PrideEnemies]"
				};
				int[] array2 = array;
				foreach (int num in array2)
				{
					val.SetTexture(num, (Texture)(object)flag);
					val.SetTextureScale(num, Vector2.one);
					val.SetTextureOffset(num, Vector2.zero);
				}
			}
			else
			{
				val = UnlitVariant(source, flag);
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
			}
			((Object)val).hideFlags = (HideFlags)61;
			Variants[key] = val;
			return val;
		}

		private static Material UnlitVariant(Material source, Texture2D flag)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			if ((Object)(object)_unlitShader == (Object)null)
			{
				_unlitShader = Shader.Find("Universal Render Pipeline/Unlit");
			}
			if ((Object)(object)_unlitShader == (Object)null)
			{
				if (!_unlitMissingLogged)
				{
					_unlitMissingLogged = true;
					Plugin.Logger.LogWarning((object)"'Universal Render Pipeline/Unlit' is not in this build, so enemies using an untextured shader can't be flagged. They will look normal.");
				}
				return null;
			}
			Material val = new Material(_unlitShader)
			{
				name = ((Object)source).name + " [PrideEnemies Unlit]"
			};
			val.SetTexture(Shader.PropertyToID("_BaseMap"), (Texture)(object)flag);
			val.SetColor(Shader.PropertyToID("_BaseColor"), Color.white);
			return val;
		}

		private static int[] SlotsFor(Shader shader)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Invalid comparison between Unknown and I4
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			int key = IdOf((Object)(object)shader);
			if (SlotsByShader.TryGetValue(key, out var value))
			{
				return value;
			}
			List<int> list = new List<int>();
			List<int> list2 = new List<int>();
			int propertyCount = shader.GetPropertyCount();
			for (int i = 0; i < propertyCount; i++)
			{
				if ((int)shader.GetPropertyType(i) != 4 || (int)shader.GetPropertyTextureDimension(i) != 2)
				{
					continue;
				}
				string propertyName = shader.GetPropertyName(i);
				int propertyNameId = shader.GetPropertyNameId(i);
				if (Array.IndexOf(PreferredSlots, propertyName) >= 0)
				{
					list.Add(propertyNameId);
					continue;
				}
				string text = propertyName.ToLowerInvariant();
				bool flag = false;
				string[] nonArtworkSlots = NonArtworkSlots;
				foreach (string value2 in nonArtworkSlots)
				{
					if (text.Contains(value2))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					list2.Add(propertyNameId);
				}
			}
			int[] array = ((list.Count > 0) ? list : list2).ToArray();
			SlotsByShader[key] = array;
			return array;
		}

		private static void Probe(Shader shader)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			int item = IdOf((Object)(object)shader);
			if (!ProbedShaders.Add(item))
			{
				return;
			}
			try
			{
				StringBuilder stringBuilder = new StringBuilder();
				int propertyCount = shader.GetPropertyCount();
				int num = 0;
				for (int i = 0; i < propertyCount; i++)
				{
					if ((int)shader.GetPropertyType(i) == 4)
					{
						if (num++ > 0)
						{
							stringBuilder.Append(", ");
						}
						stringBuilder.Append(shader.GetPropertyName(i)).Append(" (").Append(shader.GetPropertyTextureDimension(i))
							.Append(')');
					}
				}
				Plugin.Logger.LogInfo((object)((num > 0) ? $"[probe] Shader '{((Object)shader).name}': {num} texture slot(s) — {stringBuilder}" : ("[probe] Shader '" + ((Object)shader).name + "': no texture slots, falling back to an unlit material.")));
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("[probe] Could not inspect shader '" + ((Object)shader).name + "': " + ex.Message));
			}
		}

		public static void Restore()
		{
			foreach (KeyValuePair<Renderer, Material[]> original in Originals)
			{
				if (!((Object)(object)original.Key == (Object)null))
				{
					try
					{
						original.Key.sharedMaterials = original.Value;
					}
					catch (Exception ex)
					{
						Plugin.Logger.LogWarning((object)("Could not restore an enemy's materials: " + ex.Message));
					}
				}
			}
			Originals.Clear();
			foreach (Material value in Variants.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			Variants.Clear();
		}

		public static void DropDestroyed()
		{
			List<Renderer> list = new List<Renderer>();
			foreach (KeyValuePair<Renderer, Material[]> original in Originals)
			{
				if ((Object)(object)original.Key == (Object)null)
				{
					list.Add(original.Key);
				}
			}
			foreach (Renderer item in list)
			{
				Originals.Remove(item);
			}
		}
	}
	internal static class FlagLibrary
	{
		private static readonly string[] SupportedExtensions = new string[3] { ".png", ".jpg", ".jpeg" };

		private static string[] _paths = new string[0];

		private static Texture2D[] _textures = (Texture2D[])(object)new Texture2D[0];

		public static int Count => _paths.Length;

		private static string FlagFolder
		{
			get
			{
				string location = Assembly.GetExecutingAssembly().Location;
				if (!string.IsNullOrEmpty(location))
				{
					return Path.Combine(Path.GetDirectoryName(location), "flags");
				}
				return null;
			}
		}

		public static void Scan()
		{
			string flagFolder = FlagFolder;
			List<string> list = new List<string>();
			try
			{
				if (Directory.Exists(flagFolder))
				{
					string[] files = Directory.GetFiles(flagFolder);
					foreach (string text in files)
					{
						if (Array.IndexOf(SupportedExtensions, Path.GetExtension(text).ToLowerInvariant()) >= 0)
						{
							list.Add(text);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("Could not read '" + flagFolder + "': " + ex.Message));
			}
			list.Sort(StringComparer.OrdinalIgnoreCase);
			_paths = list.ToArray();
			_textures = (Texture2D[])(object)new Texture2D[_paths.Length];
			if (_paths.Length == 0)
			{
				Plugin.Logger.LogWarning((object)("No flag images found in '" + flagFolder + "' — enemies are left alone."));
			}
		}

		public static int IndexFor(int key)
		{
			if (_paths.Length == 0)
			{
				return -1;
			}
			return (int)((uint)(key * -1640531535) % (uint)_paths.Length);
		}

		public static string NameAt(int index)
		{
			if (index >= 0 && index < _paths.Length)
			{
				return Path.GetFileNameWithoutExtension(_paths[index]);
			}
			return "?";
		}

		public static Texture2D Get(int index)
		{
			if (index < 0 || index >= _paths.Length)
			{
				return null;
			}
			Texture2D val = _textures[index];
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			_textures[index] = Load(_paths[index]);
			return _textures[index];
		}

		private static Texture2D Load(string path)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			try
			{
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true);
				if (!ImageConversion.LoadImage(val, File.ReadAllBytes(path)))
				{
					Plugin.Logger.LogWarning((object)("Could not decode '" + Path.GetFileName(path) + "' as an image."));
					Object.Destroy((Object)(object)val);
					return null;
				}
				((Texture)val).wrapMode = (TextureWrapMode)0;
				((Texture)val).filterMode = (FilterMode)1;
				((Object)val).hideFlags = (HideFlags)61;
				((Object)val).name = "PrideEnemies:" + Path.GetFileNameWithoutExtension(path);
				return val;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogWarning((object)("Could not load '" + path + "': " + ex.Message));
				return null;
			}
		}
	}
	[MycoMod(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("coruscnium.prideenemies", "PrideEnemies", "1.0.0.0")]
	[BepInProcess("Mycopunk.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "coruscnium.prideenemies";

		public const string PluginName = "PrideEnemies";

		public const string PluginVersion = "1.0.0.0";

		internal static ManualLogSource Logger;

		private const float CleanupInterval = 10f;

		private Harmony _harmony;

		private float _sinceCleanup;

		private void Awake()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			FlagLibrary.Scan();
			_harmony = new Harmony("coruscnium.prideenemies");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			Logger.LogInfo((object)string.Format("{0} loaded with {1} flag(s).", "PrideEnemies", FlagLibrary.Count));
		}

		private void Update()
		{
			_sinceCleanup += Time.unscaledDeltaTime;
			if (!(_sinceCleanup < 10f))
			{
				_sinceCleanup = 0f;
				EnemyPainter.DropDestroyed();
			}
		}

		private void OnDestroy()
		{
			EnemyPainter.Restore();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	[HarmonyPatch(typeof(EnemyPart), "Initialize")]
	internal static class EnemyPartInitializePatch
	{
		[HarmonyPostfix]
		private static void Postfix(EnemyPart __instance)
		{
			EnemyPainter.Paint(__instance);
		}
	}
	[HarmonyPatch(typeof(EnemyPart), "SetupRenderersOnAttach")]
	internal static class EnemyPartAttachPatch
	{
		[HarmonyPostfix]
		private static void Postfix(EnemyPart __instance)
		{
			EnemyPainter.Paint(__instance);
		}
	}
}