using DunGen; using HarmonyLib; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using UnityEngine; namespace MuzikaGromche { internal class PoweredLightsAnimators { private readonly record struct AnimatorPatch(string AnimatorContainerPath, RuntimeAnimatorController AnimatorController); private readonly record struct TilePatch(string TileName, AnimatorPatch[] Patches) { // We are specifically looking for cloned tiles, not the original prototypes. public readonly string TileCloneName = $"{TileName}(Clone)"; } private readonly record struct AnimatorPatchDescriptor(string AnimatorContainerPath, string AnimatorControllerAssetPath) { public AnimatorPatch Load(AssetBundle assetBundle) { var animationController = assetBundle.LoadAsset(AnimatorControllerAssetPath) ?? throw new FileNotFoundException($"RuntimeAnimatorController not found: {AnimatorControllerAssetPath}", AnimatorControllerAssetPath); return new(AnimatorContainerPath, animationController); } } private readonly record struct TilePatchDescriptor(string[] TileNames, AnimatorPatchDescriptor[] Descriptors) { public TilePatchDescriptor(string TileName, AnimatorPatchDescriptor[] Descriptors) : this([TileName], Descriptors) { } public TilePatch[] Load(AssetBundle assetBundle) { var patches = Descriptors.Select(d => d.Load(assetBundle)).ToArray(); return [.. TileNames.Select(tileName => new TilePatch(tileName, patches))]; } } private static IDictionary Patches = new Dictionary(); public static void Load() { const string BundleFileName = "muzikagromche_poweredlightsanimators"; string bundlePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), BundleFileName); var assetBundle = AssetBundle.LoadFromFile(bundlePath) ?? throw new NullReferenceException("Failed to load bundle"); const string BasePath = "Assets/LethalCompany/Mods/MuzikaGromche/AnimatorControllers/"; const string PointLight4 = $"{BasePath}Point Light (4) (Patched).controller"; const string MineshaftSpotlight = $"{BasePath}MineshaftSpotlight (Patched).controller"; const string LightbulbsLine = $"{BasePath}lightbulbsLineMesh (Patched).controller"; const string CeilingFan = $"{BasePath}CeilingFan (originally GameObject) (Patched).controller"; TilePatchDescriptor[] descriptors = [ // any version new("KitchenTile", [ new("PoweredLightTypeB", PointLight4), new("PoweredLightTypeB (1)", PointLight4), ]), // < v70 new("ManorStartRoom", [ new("ManorStartRoom/Chandelier/PoweredLightTypeB (1)", PointLight4), new("ManorStartRoom/Chandelier2/PoweredLightTypeB", PointLight4), ]), // v70+ new("ManorStartRoomSmall", [ new("ManorStartRoomMesh/Chandelier/PoweredLightTypeB (1)", PointLight4), new("ManorStartRoomMesh/Chandelier2/PoweredLightTypeB", PointLight4), ]), new("BirthdayRoomTile", [ new("Lights/MineshaftSpotlight", MineshaftSpotlight), ]), new("BathroomTileContainer", [ new("MineshaftSpotlight", MineshaftSpotlight), new("LightbulbLine/lightbulbsLineMesh", LightbulbsLine), ]), new(["BedroomTile", "BedroomTileB"], [ new("CeilingFanAnimContainer", CeilingFan), new("MineshaftSpotlight (1)", MineshaftSpotlight), ]), ]; Patches = descriptors .SelectMany(d => d.Load(assetBundle)) .ToDictionary(d => d.TileCloneName, d => d); } public static void Patch(Tile tile) { if (tile == null) { throw new ArgumentNullException(nameof(tile)); } if (Patches.TryGetValue(tile.gameObject.name, out var tilePatch)) { foreach (var patch in tilePatch.Patches) { Transform animationContainerTransform = tile.gameObject.transform.Find(patch.AnimatorContainerPath); if (animationContainerTransform == null) { #if DEBUG throw new NullReferenceException($"{tilePatch.TileName}/{patch.AnimatorContainerPath} Animation Container not found"); #endif #pragma warning disable CS0162 // Unreachable code detected continue; #pragma warning restore CS0162 // Unreachable code detected } var animator = animationContainerTransform.gameObject.GetComponent(); if (animator == null) { #if DEBUG throw new NullReferenceException($"{tilePatch.TileName}/{patch.AnimatorContainerPath} Animation Component not found"); #endif #pragma warning disable CS0162 // Unreachable code detected continue; #pragma warning restore CS0162 // Unreachable code detected } animator.runtimeAnimatorController = patch.AnimatorController; Debug.Log($"{nameof(MuzikaGromche)} {nameof(PoweredLightsAnimatorsPatch)} {tilePatch.TileName}/{patch.AnimatorContainerPath}: Replaced animator controller"); } } } } [HarmonyPatch(typeof(Tile))] internal class PoweredLightsAnimatorsPatch { [HarmonyPatch("AddTriggerVolume")] [HarmonyPostfix] public static void OnAddTriggerVolume(Tile __instance) { PoweredLightsAnimators.Patch(__instance); } } }