using HarmonyLib; using MIU; using System.Collections.Generic; using System.Reflection; using UnityEngine; namespace CustomCosmeticLoader.Patches { internal static class MarbleHolderValues { private static FieldInfo _mbcField = typeof(MarbleHolder).GetField("mbc", BindingFlags.NonPublic | BindingFlags.Instance); public static FieldInfo MbcField { get { return _mbcField; } } } /* * Applies the custom skin texture to the marble in both singleplayer and multiplayer, if the cosmetic chosen is the * one we're hijacking. In multiplayer, do some extra checks to only apply the custom texture to our marble */ [HarmonyPatch(typeof(MarbleHolder), nameof(MarbleHolder.SetMarble))] internal class MarbleHolderSetMarblePatch { static void Postfix(MarbleHolder __instance, Cosmetic marbleObject) { if (!Config.enabled) return; if (marbleObject.Id != Config.skinNameToHijack && Config.skinNameToHijack != "*") return; if (!Config.inCosmeticMenu && __instance == CosmeticPanel.cosmHolder) return; MarbleController controller = MarbleHolderValues.MbcField.GetValue(__instance) as MarbleController; if (controller == null) return; Texture2D skin = null; if (NetworkManager.IsMultiplayer) { if (controller.isMyClientMarble()) { skin = Config.skins[Config.currentSkin]; } else { if (!Config.otherPlayers.ContainsKey(controller.nickname)) return; skin = Config.skins[Config.otherPlayers[controller.nickname]]; } } else { if (MarbleManager.Replaying && !Config.inAllReplays) { string replayName = null; GamePlayManager.Get().GetCurrentReplays(delegate (List replays) { if (replays.Count > 0) replayName = replays[0].Player; }); Shared.Log("MarbleHolderSetMarblePatch Replay name: " + replayName); if (replayName == Player.Current.Name) { skin = Config.skins[Config.currentSkin]; } else { if (Config.otherPlayers.ContainsKey(replayName)) skin = Config.skins[Config.otherPlayers[replayName]]; } } else { skin = Config.skins[Config.currentSkin]; } } if (skin != null) { Shared.ApplyCustomTexture(__instance.currentMarble, skin); } } } /* * In multiplayer, hijack the current marble and set it to be the marble we're hijacking. Later, the actual texture * will be replaced by the patch above */ [HarmonyPatch(typeof(MarbleHolder), nameof(MarbleHolder.CheckSet))] internal class MarbleHolderCheckSetPatch { static void Postfix(MarbleHolder __instance, Cosmetic.Set cs) { if (!Config.enabled) return; if (!NetworkManager.IsMultiplayer) return; MarbleController controller = MarbleHolderValues.MbcField.GetValue(__instance) as MarbleController; if (controller == null) return; if (!controller.isMyClientMarble()) { if (!Config.otherPlayers.ContainsKey(controller.nickname)) return; } // Don't hijack the soccer ball or zombie skins if (cs.skin == "SoccerBall_V4" || cs.skin == "Zombie") return; __instance.SetMarble(Shared.SkinToHijack); __instance.CosmeticSet.skin = cs.skin; } } }