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 Originals = new Dictionary(); private static readonly Dictionary Variants = new Dictionary(); private static readonly Dictionary SlotsByShader = new Dictionary(); private static readonly HashSet ProbedShaders = new HashSet(); 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 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 list = new List(); List list2 = new List(); 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 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 list = new List(); foreach (KeyValuePair 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 list = new List(); 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); } } }