using ABI_RC.Core.Player; using ABI_RC.Core.Savior; using ABI_RC.Systems.ContentClones; using ABI_RC.Systems.GameEventSystem; using ABI_RC.Systems.UI.UILib; using ABI_RC.Systems.UI.UILib.UIObjects; using ABI_RC.Systems.UI.UILib.UIObjects.Components; using ABI.CCK.Components; using MelonLoader; namespace NAK.ShowPlayerInSelfMirror; public class ShowPlayerInSelfMirrorMod : MelonMod { public override void OnInitializeMelon() { PlayerPlayerMirror.Initialize(); } } /// /// Manages adding/removing player clones to/from the personal mirror via the Player Select Page. /// public static class PlayerPlayerMirror { private static readonly Dictionary _clonedPlayers = new(); private static Category _ourCategory; private static ToggleButton _toggle; public static void Initialize() { _ourCategory = QuickMenuAPI.PlayerSelectPage.AddCategory("Show Player In Self Mirror"); _toggle = _ourCategory.AddToggle( "Add To Self Mirrors", "Should this player be shown in your self mirrors?", false ); _toggle.OnValueUpdated += OnToggleChanged; QuickMenuAPI.OnPlayerSelected += OnPlayerSelected; CVRGameEventSystem.Avatar.OnRemoteAvatarClear.AddListener(OnRemoteAvatarCleared); CVRGameEventSystem.Avatar.OnRemoteAvatarLoad.AddListener(OnRemoteAvatarLoad); CVRGameEventSystem.Player.OnLeaveEntity.AddListener(OnRemotePlayerLeave); } private static void OnToggleChanged(bool value) { string playerId = QuickMenuAPI.SelectedPlayerID; if (string.IsNullOrEmpty(playerId)) return; if (value) { if (!_clonedPlayers.TryAdd(playerId, null)) return; if (CVRPlayerManager.Instance.TryGetPlayerBase(playerId, out PlayerBase player)) { if (!TryCreateClone(playerId, player)) { _clonedPlayers.Remove(playerId); _toggle.ToggleValue = false; } } } else { RemoveAndForgetClone(playerId); } } private static void OnPlayerSelected(object _, string playerId) { // If this is us, hide the category entirely if (playerId == MetaPort.Instance.ownerId) { _ourCategory.Hidden = true; return; } // Show the category for other players _ourCategory.Hidden = false; bool enabled = _clonedPlayers.ContainsKey(playerId); _toggle.ToggleValue = enabled; } private static void OnRemoteAvatarCleared(CVRPlayerEntity playerEntity, CVRAvatar _) { string playerId = playerEntity.Uuid; if (!_clonedPlayers.TryGetValue(playerId, out ContentCloneManager.CloneData clone)) return; if (clone != null) { ContentCloneManager.DestroyClone(clone); _clonedPlayers[playerId] = null; } } private static void OnRemoteAvatarLoad(CVRPlayerEntity playerEntity, CVRAvatar _) { string playerId = playerEntity.Uuid; if (!_clonedPlayers.ContainsKey(playerId)) return; if (!CVRPlayerManager.Instance.TryGetPlayerBase(playerId, out PlayerBase player)) return; TryCreateClone(playerId, player); } private static void OnRemotePlayerLeave(CVRPlayerEntity playerEntity) { string playerId = playerEntity.Uuid; RemoveAndForgetClone(playerId); } private static bool TryCreateClone(string playerId, PlayerBase player) { if (!player.AvatarObject) return false; if (_clonedPlayers.TryGetValue(playerId, out ContentCloneManager.CloneData existing) && existing is { IsDestroyed: false }) return true; ContentCloneManager.CloneData clone = ContentCloneManager.CreateClone( player.AvatarObject, ContentCloneManager.CloneOptions.ExtensionOfPlayer ); if (clone == null) return false; _clonedPlayers[playerId] = clone; return true; } private static void RemoveAndForgetClone(string playerId) { if (_clonedPlayers.TryGetValue(playerId, out ContentCloneManager.CloneData clone)) { if (clone != null) ContentCloneManager.DestroyClone(clone); } _clonedPlayers.Remove(playerId); } }