Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of EnemySkinManager v0.1.1
EnemySkinManager.dll
Decompiled a year agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("EnemySkinManager")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("A mod library for R.E.P.O. to manage enemy skin variations.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("EnemySkinManager")] [assembly: AssemblyTitle("EnemySkinManager")] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace EnemySkinManager { public class SkinData { public string Identifier { get; set; } public Material[] Materials { get; set; } public Mesh SharedMesh { get; set; } public Dictionary<string, Texture2D> TextureOverrides { get; set; } public bool OverrideProtectedParts { get; set; } = false; public SkinData(string id, Material[] materials = null, Mesh mesh = null, Dictionary<string, Texture2D> textures = null, bool overrideProtectedParts = false) { Identifier = id; Materials = (Material[])(((object)materials) ?? ((object)new Material[0])); SharedMesh = mesh; TextureOverrides = textures ?? new Dictionary<string, Texture2D>(); OverrideProtectedParts = overrideProtectedParts; } } public static class EnemySkinManager { internal static ManualLogSource Log; internal static ConfigEntry<bool> MasterEnableConfig; private static ConfigFile AppConfig; public const string VanillaSkinIdentifier = "Vanilla"; internal static readonly Dictionary<string, List<SkinData>> RegisteredSkins = new Dictionary<string, List<SkinData>>(); private static readonly Dictionary<string, Dictionary<string, ConfigEntry<int>>> enemySkinWeightsConfigs = new Dictionary<string, Dictionary<string, ConfigEntry<int>>>(); public static Func<string, string, bool> IsSkinEnabledCheck = (string enemyId, string skinId) => true; internal static void Initialize(ManualLogSource log, ConfigEntry<bool> masterEnableConfig, ConfigFile config) { Log = log; MasterEnableConfig = masterEnableConfig; AppConfig = config; Log.LogInfo((object)"EnemySkinManager Initialized. Skins will be configured with weights per enemy."); } public static void RegisterSkin(string enemyId, SkinData newSkin, string? friendlyCategoryName = null) { //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown if (string.IsNullOrEmpty(enemyId) || newSkin == null || string.IsNullOrEmpty(newSkin.Identifier)) { Log.LogError((object)"Invalid parameters for RegisterSkin."); return; } if (!RegisteredSkins.ContainsKey(enemyId)) { RegisteredSkins[enemyId] = new List<SkinData>(); } if (!RegisteredSkins[enemyId].Any((SkinData s) => s.Identifier == newSkin.Identifier)) { RegisteredSkins[enemyId].Add(newSkin); Log.LogInfo((object)("Registered skin '" + newSkin.Identifier + "' for enemy '" + enemyId + "'.")); } string text = (string.IsNullOrEmpty(friendlyCategoryName) ? enemyId : friendlyCategoryName); string text2 = text + " Skins"; if (!enemySkinWeightsConfigs.ContainsKey(enemyId)) { Log.LogInfo((object)("Creating new weight configuration section for enemy: " + enemyId + " (displaying as '" + text2 + "')")); enemySkinWeightsConfigs.Add(enemyId, new Dictionary<string, ConfigEntry<int>>()); if (!enemySkinWeightsConfigs[enemyId].ContainsKey("Vanilla")) { Log.LogInfo((object)("Creating weight config for 'Vanilla' skin in section '" + text2 + "'")); ConfigEntry<int> value = AppConfig.Bind<int>(text2, "Vanilla", 10, new ConfigDescription("Spawn weight for 'Vanilla' (original game skin) on " + text + ". 0 = disabled. Higher values increase spawn chance relative to other enabled skins.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); enemySkinWeightsConfigs[enemyId].Add("Vanilla", value); } } if (!enemySkinWeightsConfigs[enemyId].ContainsKey(newSkin.Identifier)) { Log.LogInfo((object)("Creating weight config for skin '" + newSkin.Identifier + "' in section '" + text2 + "'")); ConfigEntry<int> value2 = AppConfig.Bind<int>(text2, newSkin.Identifier, 10, new ConfigDescription("Spawn weight for '" + newSkin.Identifier + "' on " + text + ". 0 = disabled. Higher values increase spawn chance relative to other enabled skins.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); enemySkinWeightsConfigs[enemyId].Add(newSkin.Identifier, value2); } } public static SkinData GetRandomSkinForEnemy(string enemyId) { if (MasterEnableConfig != null && !MasterEnableConfig.Value) { return null; } if (!enemySkinWeightsConfigs.TryGetValue(enemyId, out var value)) { Log.LogWarning((object)("No weight configuration section found for enemy '" + enemyId + "'. Skins may be registered, but weights are missing. Using vanilla.")); return null; } RegisteredSkins.TryGetValue(enemyId, out var value2); value2 = value2 ?? new List<SkinData>(); List<KeyValuePair<SkinData, int>> list = new List<KeyValuePair<SkinData, int>>(); int num = 0; if (value.TryGetValue("Vanilla", out var value3) && value3.Value > 0) { list.Add(new KeyValuePair<SkinData, int>(null, value3.Value)); num += value3.Value; } foreach (SkinData item in value2) { if (value.TryGetValue(item.Identifier, out var value4) && value4.Value > 0) { list.Add(new KeyValuePair<SkinData, int>(item, value4.Value)); num += value4.Value; } } if (list.Count == 0 || num == 0) { return null; } int num2 = Random.Range(0, num); foreach (KeyValuePair<SkinData, int> item2 in list) { if (num2 < item2.Value) { return item2.Key; } num2 -= item2.Value; } if (list.Any((KeyValuePair<SkinData, int> opt) => opt.Key == null && opt.Value > 0)) { return null; } return list.FirstOrDefault((KeyValuePair<SkinData, int> opt) => opt.Key != null).Key; } public static void ApplySkin(GameObject enemyInstance, SkinData skinData) { if ((Object)(object)enemyInstance == (Object)null || skinData == null) { Log.LogWarning((object)"ApplySkin called with null enemyInstance or skinData."); return; } Renderer componentInChildren = enemyInstance.GetComponentInChildren<Renderer>(); if ((Object)(object)componentInChildren == (Object)null) { Log.LogWarning((object)("Could not find a Renderer component on '" + ((Object)enemyInstance).name + "' or its children to apply skin '" + (skinData.Identifier ?? "Unnamed") + "'.")); return; } if (skinData.Materials != null && skinData.Materials.Length != 0) { if (componentInChildren.sharedMaterials.Length != skinData.Materials.Length) { Log.LogWarning((object)("Skin '" + skinData.Identifier + "' for " + ((Object)enemyInstance).name + ": " + $"Provided material count ({skinData.Materials.Length}) " + $"differs from renderer's material slot count ({componentInChildren.sharedMaterials.Length}). " + "Applying materials anyway. This might lead to an incomplete or unexpected appearance if counts don't effectively match.")); } componentInChildren.sharedMaterials = skinData.Materials; if (Plugin.EnableDebugLogging.Value) { Log.LogDebug((object)$"Applied {skinData.Materials.Length} material(s) from skin '{skinData.Identifier}' to {((Object)enemyInstance).name}."); } } else if (Plugin.EnableDebugLogging.Value) { Log.LogDebug((object)("Skin '" + (skinData.Identifier ?? "Unnamed") + "' has no materials defined.")); } if (skinData.TextureOverrides != null && skinData.TextureOverrides.Count > 0) { foreach (KeyValuePair<string, Texture2D> textureOverride in skinData.TextureOverrides) { Material[] sharedMaterials = componentInChildren.sharedMaterials; foreach (Material val in sharedMaterials) { if (val.HasProperty(textureOverride.Key)) { val.SetTexture(textureOverride.Key, (Texture)(object)textureOverride.Value); if (Plugin.EnableDebugLogging.Value) { Log.LogDebug((object)("Applied texture override '" + textureOverride.Key + "' to material '" + ((Object)val).name + "' on '" + ((Object)componentInChildren).name + "'.")); } } } } } else if (Plugin.EnableDebugLogging.Value) { Log.LogDebug((object)("Skin '" + (skinData.Identifier ?? "Unnamed") + "' has no texture overrides defined.")); } if ((Object)(object)skinData.SharedMesh != (Object)null) { MeshFilter component = ((Component)componentInChildren).GetComponent<MeshFilter>(); SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((componentInChildren is SkinnedMeshRenderer) ? componentInChildren : null); if ((Object)(object)component != (Object)null) { component.sharedMesh = skinData.SharedMesh; if (Plugin.EnableDebugLogging.Value) { Log.LogDebug((object)("Applied shared mesh '" + ((Object)skinData.SharedMesh).name + "' to MeshFilter on '" + ((Object)componentInChildren).name + "'.")); } } else if ((Object)(object)val2 != (Object)null) { val2.sharedMesh = skinData.SharedMesh; if (Plugin.EnableDebugLogging.Value) { Log.LogDebug((object)("Applied shared mesh '" + ((Object)skinData.SharedMesh).name + "' to SkinnedMeshRenderer on '" + ((Object)componentInChildren).name + "'.")); } } else { Log.LogWarning((object)("Could not apply mesh '" + ((Object)skinData.SharedMesh).name + "'. No MeshFilter or SkinnedMeshRenderer found on '" + ((Object)componentInChildren).name + "'.")); } } Log.LogInfo((object)("Successfully applied skin '" + (skinData.Identifier ?? "Unnamed") + "' to '" + ((Object)enemyInstance).name + "'.")); } } [BepInPlugin("JustSomeDevGuy.EnemySkinManager.Alpha", "EnemySkinManager (ALPHA)", "0.1.0")] public class Plugin : BaseUnityPlugin { private Harmony harmony; private static ConfigEntry<bool> MasterEnableToggle; internal static ManualLogSource Log { get; private set; } public static ConfigEntry<bool> EnableDebugLogging { get; private set; } private void Awake() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; harmony = new Harmony("JustSomeDevGuy.EnemySkinManager.Alpha"); MasterEnableToggle = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Master Enable", true, "Globally enable or disable all custom skins from this manager."); EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Detailed Logging", false, "Enable detailed debug messages in the log, useful for troubleshooting skin application."); EnemySkinManager.Initialize(Log, MasterEnableToggle, ((BaseUnityPlugin)this).Config); harmony.PatchAll(); Log.LogInfo((object)"Registering default 'Vanilla' skins..."); string[] array = new string[19] { "Duck", "Animal", "Banger", "Bowtie", "Chef", "Clown", "Gnome", "Headman", "Hidden", "Huntsman", "Peeper", "Mentalist", "Reaper", "Robe", "Rugrat", "Shadow Child", "Spewer", "Trudge", "Upscream" }; string[] array2 = array; foreach (string enemyId in array2) { EnemySkinManager.RegisterSkin(enemyId, new SkinData("Vanilla")); } Log.LogInfo((object)"Finished registering default 'Vanilla' skins."); Log.LogInfo((object)"Plugin JustSomeDevGuy.EnemySkinManager.Alpha patched methods."); Log.LogInfo((object)"Plugin JustSomeDevGuy.EnemySkinManager.Alpha is loaded!"); } } public static class PluginInfo { public const string PLUGIN_GUID = "JustSomeDevGuy.EnemySkinManager.Alpha"; public const string PLUGIN_NAME = "EnemySkinManager (ALPHA)"; public const string PLUGIN_VERSION = "0.1.0"; } } namespace EnemySkinManager.Patches { [HarmonyPatch] internal class EnemySpawnPatches { private const string LOG_PREFIX = "[EnemySkinMod] "; private static ManualLogSource Log => Plugin.Log; [HarmonyPatch(typeof(EnemyParent), "Spawn")] [HarmonyPostfix] private static void EnemyParent_Spawn_Postfix(EnemyParent __instance) { Plugin.Log.LogInfo((object)string.Format("{0}--- EnemyParent_Spawn_Postfix executing for instance {1} (Name: {2}) ---", "[EnemySkinMod] ", ((Object)__instance).GetInstanceID(), ((Object)((Component)__instance).gameObject).name)); if (!EnemySkinManager.MasterEnableConfig.Value) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)"EnemySkinManager is disabled via config. Skipping skin application."); } return; } string name = ((Object)((Component)__instance).gameObject).name; string text = name.Replace("(Clone)", "").Trim(); string text2 = text; if (text.StartsWith("Enemy - ")) { text2 = text.Substring("Enemy - ".Length).Trim(); } if (string.IsNullOrEmpty(text2)) { Plugin.Log.LogWarning((object)("Could not get valid Enemy ID from GameObject name: '" + name + "'. Cannot apply skin.")); return; } Plugin.Log.LogInfo((object)("Enemy ID derived from GameObject name: '" + text2 + "' (Original: '" + name + "')")); if (!EnemySkinManager.RegisteredSkins.ContainsKey(text2) || EnemySkinManager.RegisteredSkins[text2].Count == 0) { Plugin.Log.LogInfo((object)("No skins registered for enemy ID '" + text2 + "'. Skipping.")); return; } Plugin.Log.LogInfo((object)("Getting random skin for enemy '" + text2 + "'...")); SkinData randomSkinForEnemy = EnemySkinManager.GetRandomSkinForEnemy(text2); if (randomSkinForEnemy == null || randomSkinForEnemy.Identifier == null) { Plugin.Log.LogInfo((object)("GetRandomSkinForEnemy returned no specific skin (maybe all disabled or only 'Vanilla' exists and was chosen). Enemy ID: '" + text2 + "'. No custom skin applied.")); return; } Plugin.Log.LogInfo((object)("Selected skin '" + randomSkinForEnemy.Identifier + "' for enemy '" + text2 + "'. Applying...")); GameObject gameObject = ((Component)__instance).gameObject; if ((Object)(object)gameObject == (Object)null) { Plugin.Log.LogError((object)"EnemyParent_Spawn_Postfix triggered on a null instance or GameObject."); } else { ApplySkinLogic(gameObject, randomSkinForEnemy); } } private static bool IsSkinEnabledCheck(string enemyId, string skinId) { return EnemySkinManager.IsSkinEnabledCheck(enemyId, skinId); } private static void ApplySkinLogic(GameObject enemyInstance, SkinData skinData) { if ((Object)(object)enemyInstance == (Object)null || skinData.Identifier == null) { Plugin.Log.LogError((object)"ApplySkinLogic: Received null enemyInstance or skinData.Identifier."); return; } if (skinData.TextureOverrides != null && skinData.TextureOverrides.Count > 0) { Plugin.Log.LogInfo((object)string.Format("{0}ApplySkinLogic: Attempting to apply {1} texture overrides using INSTANCED materials.", "[EnemySkinMod] ", skinData.TextureOverrides.Count)); bool flag = false; Renderer[] componentsInChildren = enemyInstance.GetComponentsInChildren<Renderer>(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { Plugin.Log.LogWarning((object)("[EnemySkinMod] ApplySkinLogic: No Renderers found on '" + ((Object)enemyInstance).name + "' or its children.")); } else { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)string.Format("{0}ApplySkinLogic: Found {1} renderers on '{2}'. Iterating...", "[EnemySkinMod] ", componentsInChildren.Length, ((Object)enemyInstance).name)); } foreach (KeyValuePair<string, Texture2D> textureOverride in skinData.TextureOverrides) { string key = textureOverride.Key; Texture2D value = textureOverride.Value; Plugin.Log.LogInfo((object)("[EnemySkinMod] ApplySkinLogic: Processing override: Key='" + key + "', Texture='" + (((Object)(object)value != (Object)null) ? ((Object)value).name : "NULL") + "'")); if (string.IsNullOrEmpty(key) || (Object)(object)value == (Object)null) { Plugin.Log.LogWarning((object)("[EnemySkinMod] ApplySkinLogic: Skipping invalid texture override entry (null key or value). Key: " + key)); continue; } bool flag2 = false; Renderer[] array = componentsInChildren; foreach (Renderer val in array) { Material[] materials = val.materials; for (int j = 0; j < materials.Length; j++) { Material val2 = materials[j]; if (val2.HasProperty(key)) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)string.Format("{0}ApplySkinLogic: Found property '{1}' on material instance '{2}' (Index {3}) of renderer '{4}'. Applying texture '{5}'.", "[EnemySkinMod] ", key, ((Object)val2).name, j, ((Object)val).name, ((Object)value).name)); } val2.SetTexture(key, (Texture)(object)value); flag2 = true; flag = true; } else if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)string.Format("{0}ApplySkinLogic: Material instance '{1}' (Index {2}) of renderer '{3}' does not have property '{4}'.", "[EnemySkinMod] ", ((Object)val2).name, j, ((Object)val).name, key)); } } val.materials = materials; } if (!flag2) { Plugin.Log.LogWarning((object)("[EnemySkinMod] ApplySkinLogic: Texture override key '" + key + "' was not found on ANY material instance of ANY renderer on '" + ((Object)enemyInstance).name + "'.")); } } } if (flag) { Plugin.Log.LogInfo((object)("[EnemySkinMod] ApplySkinLogic: Finished applying texture overrides for '" + skinData.Identifier + "' on '" + ((Object)enemyInstance).name + "'.")); } else { Plugin.Log.LogWarning((object)("[EnemySkinMod] ApplySkinLogic: Finished applying texture overrides for '" + skinData.Identifier + "' on '" + ((Object)enemyInstance).name + "', but no matching properties were found on any materials.")); } } else if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skin '" + skinData.Identifier + "' has no texture overrides defined.")); } if (skinData.Materials != null && skinData.Materials.Length != 0) { Plugin.Log.LogInfo((object)string.Format("{0}ApplySkinLogic: Skin '{1}' has {2} material(s). Attempting to apply.", "[EnemySkinMod] ", skinData.Identifier, skinData.Materials.Length)); bool flag3 = false; Renderer[] componentsInChildren2 = enemyInstance.GetComponentsInChildren<Renderer>(true); if (componentsInChildren2 == null || componentsInChildren2.Length == 0) { Plugin.Log.LogWarning((object)("[EnemySkinMod] ApplySkinLogic: No Renderers found on '" + ((Object)enemyInstance).name + "' or its children for shared material application.")); } else { string text = ((Object)enemyInstance).name; if (text.EndsWith("(Clone)")) { text = text.Substring(0, text.Length - "(Clone)".Length); } if (text.StartsWith("Enemy - ")) { text = text.Substring("Enemy - ".Length); } switch (text) { case "Head": { Plugin.Log.LogInfo((object)"[EnemySkinMod] ApplySkinLogic: Special handling for enemy 'Head'. Targeting 'Eye L Mesh' and 'Eye R Mesh'."); Renderer[] array6 = componentsInChildren2; foreach (Renderer val14 in array6) { if ((((Object)((Component)val14).gameObject).name == "Eye L Mesh" || ((Object)((Component)val14).gameObject).name == "Eye R Mesh") && skinData.Materials.Length != 0) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Applying material '" + ((Object)skinData.Materials[0]).name + "' to '" + ((Object)((Component)val14).gameObject).name + "' on 'Head' enemy.")); } val14.material = skinData.Materials[0]; flag3 = true; } } break; } case "Upscream": { Plugin.Log.LogInfo((object)"[EnemySkinMod] ApplySkinLogic: Special handling for enemy 'Upscream'. Applying head/leg materials to specific parts."); if (skinData.Materials == null || skinData.Materials.Length < 2) { ManualLogSource log = Plugin.Log; string identifier = skinData.Identifier; Material[] materials2 = skinData.Materials; log.LogError((object)string.Format("{0}ApplySkinLogic: 'Upscream' skin '{1}' requires 2 materials (head, legs) but found {2}. Aborting skin application for this enemy.", "[EnemySkinMod] ", identifier, (materials2 != null) ? materials2.Length : 0)); break; } Material val7 = skinData.Materials[0]; Material val8 = skinData.Materials[1]; if ((Object)(object)val7 == (Object)null || (Object)(object)val8 == (Object)null) { Plugin.Log.LogError((object)string.Format("{0}ApplySkinLogic: One or both materials for 'Upscream' skin '{1}' are null. Head: {2}, Legs: {3}. Aborting.", "[EnemySkinMod] ", skinData.Identifier, (Object)(object)val7 == (Object)null, (Object)(object)val8 == (Object)null)); break; } Renderer[] array4 = componentsInChildren2; foreach (Renderer val9 in array4) { string name3 = ((Object)((Component)val9).gameObject).name; switch (name3) { default: if (!(name3 == "eye_R")) { switch (name3) { default: if (!(name3 == "leg front R3")) { goto end_IL_0747; } break; case "leg back L1": case "leg back L2": case "leg back R1": case "leg back R2": case "leg front L1": case "leg front L2": case "leg front R1": case "leg front R2": case "leg back L3": case "leg back R3": case "leg front L3": break; } if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Applying LEGS material '" + ((Object)val8).name + "' to '" + name3 + "' on 'Upscream'.")); } val9.material = val8; flag3 = true; break; } goto case "Upscream_Head - Mouth Closed"; case "Upscream_Head - Mouth Closed": case "Upscream_Head - Mouth Open": case "eye_L": { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Applying HEAD material '" + ((Object)val7).name + "' to '" + name3 + "' on 'Upscream'.")); } val9.material = val7; flag3 = true; break; } end_IL_0747: break; } } break; } case "Duck": { Plugin.Log.LogInfo((object)"[EnemySkinMod] ApplySkinLogic: Special handling for enemy 'Duck'. Applying two materials to specified parts and preserving particles."); if (skinData.Materials == null || skinData.Materials.Length < 2) { ManualLogSource log2 = Plugin.Log; string identifier2 = skinData.Identifier; Material[] materials3 = skinData.Materials; log2.LogWarning((object)string.Format("{0}ApplySkinLogic: SkinData for '{1}' on 'Duck' requires 2 materials, but found {2}. No materials applied.", "[EnemySkinMod] ", identifier2, (materials3 != null) ? materials3.Length : 0)); break; } Material val10 = skinData.Materials[0]; Material val11 = skinData.Materials[1]; Renderer[] array5 = componentsInChildren2; foreach (Renderer val12 in array5) { bool flag6 = false; string name4 = ((Object)((Component)val12).gameObject).name; switch (name4) { default: if (!(name4 == "mesh Mon Mouth Bot")) { break; } goto case "mesh Eye R"; case "mesh Eye R": case "mesh Eye L": case "mesh Mon Eye L": case "mesh Mon Eye R": case "mesh Mon Mouth Top": if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + name4 + "' on 'Duck' to keep its material vanilla (eye or mouth part).")); } flag6 = true; break; } if (!flag6) { string text4 = name4.ToLower(); if (text4.Contains("particle") || text4.Contains("effect") || text4.Contains("vfx")) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + name4 + "' on 'Duck' due to name containing particle/effect keywords.")); } flag6 = true; } if (!flag6 && val12.sharedMaterials != null) { Material[] sharedMaterials3 = val12.sharedMaterials; foreach (Material val13 in sharedMaterials3) { if (!((Object)(object)val13 != (Object)null) || !((Object)(object)val13.shader != (Object)null)) { continue; } string name5 = ((Object)val13.shader).name; if (name5 == "Particles/Standard Unlit" || name5 == "Particles/Standard Surface") { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + name4 + "' on 'Duck' due to material '" + ((Object)val13).name + "' using shader '" + name5 + "'.")); } flag6 = true; break; } } } } if (flag6) { continue; } switch (name4) { default: if (!(name4 == "mesh Mon Head")) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Applying main body material (" + ((Object)val10).name + ") to '" + name4 + "' on 'Duck'.")); } val12.material = val10; flag3 = true; break; } goto case "mesh Mon Body"; case "mesh Mon Body": case "mesh Mon Wing L": case "mesh Mon Wing R": if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Applying 'Monster' material (" + ((Object)val11).name + ") to '" + name4 + "' on 'Duck'.")); } val12.material = val11; flag3 = true; break; } } break; } case "Animal": { Plugin.Log.LogInfo((object)"[EnemySkinMod] ApplySkinLogic: Special handling for enemy 'Animal'. Preserving 'Eyes' and particles."); if (skinData.Materials == null || skinData.Materials.Length == 0 || (Object)(object)skinData.Materials[0] == (Object)null) { Plugin.Log.LogError((object)("[EnemySkinMod] ApplySkinLogic: Skin '" + skinData.Identifier + "' for 'Animal' has no valid material at index 0. Aborting skin application for this instance.")); return; } Material val15 = skinData.Materials[0]; Renderer[] array7 = componentsInChildren2; foreach (Renderer val16 in array7) { bool flag7 = false; string name6 = ((Object)((Component)val16).gameObject).name; if (name6 == "Eyes") { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + name6 + "' on 'Animal' to keep its material vanilla.")); } flag7 = true; } if (!flag7) { string text5 = name6.ToLower(); if (text5.Contains("particle") || text5.Contains("effect") || text5.Contains("vfx")) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + name6 + "' on 'Animal' due to name containing particle/effect keywords.")); } flag7 = true; } if (!flag7 && val16.sharedMaterials != null) { Material[] sharedMaterials4 = val16.sharedMaterials; foreach (Material val17 in sharedMaterials4) { if (!((Object)(object)val17 != (Object)null) || !((Object)(object)val17.shader != (Object)null)) { continue; } string name7 = ((Object)val17.shader).name; if (name7 == "Particles/Standard Unlit" || name7 == "Particles/Standard Surface") { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + name6 + "' on 'Animal' due to material '" + ((Object)val17).name + "' using shader '" + name7 + "'.")); } flag7 = true; break; } } } } if (!flag7) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Applying skin material (" + ((Object)val15).name + ") to '" + name6 + "' on 'Animal'.")); } val16.material = val15; } } break; } case "Bowtie": { Plugin.Log.LogInfo((object)"[EnemySkinMod] ApplySkinLogic: Special handling for enemy 'Bowtie'. Preserving 'bend body' and particle effects."); if (skinData.Materials == null || skinData.Materials.Length == 0) { Plugin.Log.LogWarning((object)("[EnemySkinMod] ApplySkinLogic: SkinData for '" + skinData.Identifier + "' on 'Bowtie' has no materials. No materials applied.")); break; } Renderer[] array3 = componentsInChildren2; foreach (Renderer val5 in array3) { bool flag5 = false; if (((Object)((Component)val5).gameObject).name == "bend body") { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)"[EnemySkinMod] ApplySkinLogic: Skipping renderer 'bend body' on 'Bowtie' to keep its material vanilla."); } flag5 = true; } if (!flag5) { string text3 = ((Object)((Component)val5).gameObject).name.ToLower(); if (text3.Contains("particle") || text3.Contains("effect") || text3.Contains("vfx")) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + ((Object)((Component)val5).gameObject).name + "' on 'Bowtie' due to name containing particle/effect keywords.")); } flag5 = true; } if (!flag5 && val5.sharedMaterials != null) { Material[] sharedMaterials2 = val5.sharedMaterials; foreach (Material val6 in sharedMaterials2) { if (!((Object)(object)val6 != (Object)null) || !((Object)(object)val6.shader != (Object)null)) { continue; } string name2 = ((Object)val6.shader).name; if (name2 == "Particles/Standard Unlit" || name2 == "Particles/Standard Surface") { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + ((Object)((Component)val5).gameObject).name + "' on 'Bowtie' due to material '" + ((Object)val6).name + "' using shader '" + name2 + "'.")); } flag5 = true; break; } } } } if (flag5) { continue; } if (val5.sharedMaterials == null || val5.sharedMaterials.Length == 0) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Renderer '" + ((Object)((Component)val5).gameObject).name + "' on 'Bowtie' has no materials to replace. Skipping.")); } continue; } if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)string.Format("{0}ApplySkinLogic: Applying skin materials to renderer '{1}' on 'Bowtie'. Original material count: {2}, New material count: {3}", "[EnemySkinMod] ", ((Object)((Component)val5).gameObject).name, val5.sharedMaterials.Length, skinData.Materials.Length)); } val5.materials = skinData.Materials; flag3 = true; } break; } default: if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)string.Format("{0}ApplySkinLogic: Found {1} renderers on '{2}' ({3}). Applying shared materials, skipping particle systems if named appropriately.", "[EnemySkinMod] ", componentsInChildren2.Length, ((Object)enemyInstance).name, text)); } if (skinData.Materials != null && skinData.Materials.Length != 0) { Renderer[] array2 = componentsInChildren2; foreach (Renderer val3 in array2) { bool flag4 = false; string text2 = ((Object)((Component)val3).gameObject).name.ToLower(); if (text2.Contains("particle") || text2.Contains("effect") || text2.Contains("vfx")) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + ((Object)((Component)val3).gameObject).name + "' on '" + text + "' due to name containing particle/effect keywords.")); } flag4 = true; } if (!flag4 && val3.sharedMaterials != null) { Material[] sharedMaterials = val3.sharedMaterials; foreach (Material val4 in sharedMaterials) { if (!((Object)(object)val4 != (Object)null) || !((Object)(object)val4.shader != (Object)null)) { continue; } string name = ((Object)val4.shader).name; if (name == "Particles/Standard Unlit" || name == "Particles/Standard Surface") { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skipping renderer '" + ((Object)((Component)val3).gameObject).name + "' on '" + text + "' due to material '" + ((Object)val4).name + "' using shader '" + name + "'.")); } flag4 = true; break; } } } if (flag4) { continue; } if (val3.sharedMaterials == null || val3.sharedMaterials.Length == 0) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Renderer '" + ((Object)((Component)val3).gameObject).name + "' on '" + text + "' has no materials to replace. Skipping.")); } continue; } if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)string.Format("{0}ApplySkinLogic: Applying skin materials to renderer '{1}' on '{2}'. Original material count: {3}, New material count: {4}", "[EnemySkinMod] ", ((Object)((Component)val3).gameObject).name, text, val3.sharedMaterials.Length, skinData.Materials.Length)); } val3.materials = skinData.Materials; flag3 = true; } } else { Plugin.Log.LogWarning((object)("[EnemySkinMod] ApplySkinLogic: SkinData for '" + skinData.Identifier + "' on '" + text + "' has no materials. No general materials applied.")); } break; } } if (flag3) { Plugin.Log.LogInfo((object)("[EnemySkinMod] ApplySkinLogic: Finished applying shared materials for '" + skinData.Identifier + "' on '" + ((Object)enemyInstance).name + "'.")); } else { Plugin.Log.LogWarning((object)("[EnemySkinMod] ApplySkinLogic: Finished applying shared materials for '" + skinData.Identifier + "' on '" + ((Object)enemyInstance).name + "', but no materials were actually applied (e.g., specific renderers not found for 'Head', or no renderers for others, or no materials in skinData).")); } } else if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skin '" + skinData.Identifier + "' has no shared materials defined.")); } if ((Object)(object)skinData.SharedMesh != (Object)null) { Plugin.Log.LogInfo((object)("[EnemySkinMod] ApplySkinLogic: Skin '" + skinData.Identifier + "' has a shared mesh defined. Attempting to apply.")); MeshFilter componentInChildren = enemyInstance.GetComponentInChildren<MeshFilter>(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.mesh = skinData.SharedMesh; Plugin.Log.LogInfo((object)("[EnemySkinMod] ApplySkinLogic: Successfully applied shared mesh '" + ((Object)skinData.SharedMesh).name + "' to MeshFilter on '" + ((Object)((Component)componentInChildren).gameObject).name + "'.")); } else { Plugin.Log.LogWarning((object)("[EnemySkinMod] ApplySkinLogic: No MeshFilter found on '" + ((Object)enemyInstance).name + "' or its children to apply shared mesh.")); } } else if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogDebug((object)("[EnemySkinMod] ApplySkinLogic: Skin '" + skinData.Identifier + "' has no shared mesh defined.")); } Plugin.Log.LogInfo((object)("[EnemySkinMod] ApplySkinLogic completed for '" + ((Object)enemyInstance).name + "' with skin '" + skinData.Identifier + "'. Check logs for details.")); } [HarmonyPatch(typeof(EnemyDirector), "Awake")] [HarmonyPostfix] private static void EnemyDirector_Awake_Patch(EnemyDirector __instance) { Plugin.Log.LogInfo((object)$"--- EnemyDirector_Awake_Patch executing for instance {((Object)__instance).GetInstanceID()} ---"); string name = ((Object)((Component)__instance).gameObject).name; string text = name.Replace("(Clone)", "").Trim(); string text2 = text; if (text.StartsWith("Enemy - ")) { text2 = text.Substring("Enemy - ".Length).Trim(); } if (string.IsNullOrEmpty(text2)) { Plugin.Log.LogWarning((object)(" Could not get valid Enemy ID from GameObject name: '" + name + "'. Cannot apply skin.")); return; } Plugin.Log.LogInfo((object)(" Enemy ID derived from GameObject name: '" + text2 + "' (Original: '" + name + "')")); if (!EnemySkinManager.RegisteredSkins.ContainsKey(text2) || EnemySkinManager.RegisteredSkins[text2].Count == 0) { Plugin.Log.LogInfo((object)(" No skins registered for enemy ID '" + text2 + "'. Skipping.")); return; } SkinData randomSkinForEnemy = EnemySkinManager.GetRandomSkinForEnemy(text2); if (randomSkinForEnemy.Identifier == null) { Plugin.Log.LogInfo((object)(" GetRandomSkinForEnemy returned no skin (likely all disabled or only Vanilla exists and was chosen). Enemy ID: '" + text2 + "'")); return; } if (randomSkinForEnemy.Identifier == "Vanilla") { Plugin.Log.LogInfo((object)(" Selected skin is 'Vanilla'. No custom skin applied for enemy ID: '" + text2 + "'.")); return; } Plugin.Log.LogInfo((object)(" Selected skin '" + randomSkinForEnemy.Identifier + "' for enemy ID '" + text2 + "'. Applying...")); GameObject gameObject = ((Component)__instance).gameObject; ApplySkinLogic(gameObject, randomSkinForEnemy); } } }