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);
}
}
}