using System; using System.Collections; 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.Logging; using HarmonyLib; using ModdingUtils.Utils; using UnboundLib; using UnboundLib.GameModes; using UnboundLib.Networking; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")] [assembly: AssemblyCompany("Rounds2Mod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Rounds2Mod")] [assembly: AssemblyTitle("Rounds2Mod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Rounds2Mod; internal static class CardDeleteLog { private const string Sep = "====================="; public static void Section(string title) { Plugin.Logger.LogInfo((object)"====================="); Plugin.Logger.LogInfo((object)("[Rounds2Mod] " + title)); Plugin.Logger.LogInfo((object)"====================="); } public static void Line(string message) { Plugin.Logger.LogInfo((object)("[Rounds2Mod] " + message)); } public static void Warn(string message) { Plugin.Logger.LogWarning((object)("[Rounds2Mod] " + message)); } public static void Error(string message) { Plugin.Logger.LogError((object)("[Rounds2Mod] " + message)); } } public class CardBarDeleteButton : MonoBehaviour, IPointerClickHandler, IEventSystemHandler, IPointerEnterHandler, IPointerExitHandler { private static readonly FieldInfo s_cardField = AccessTools.Field(typeof(CardBarButton), "card"); private Image _icon; private Color _originalColor; private void Awake() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) _icon = ((Component)this).GetComponentInChildren(); if ((Object)(object)_icon != (Object)null) { _originalColor = ((Graphic)_icon).color; } } public void OnPointerEnter(PointerEventData eventData) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (CardDeleteManager.isPickPhase && CardDeleteManager.IsLocalPlayerTurn()) { if ((Object)(object)_icon != (Object)null) { ((Graphic)_icon).color = new Color(1f, 0.28f, 0.28f, _originalColor.a); } CardInfo cardInfo = GetCardInfo(); if ((Object)(object)cardInfo != (Object)null) { CardDeleteLog.Line("Hover (delete mode): '" + cardInfo.cardName + "'"); } } } public void OnPointerExit(PointerEventData eventData) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_icon != (Object)null) { ((Graphic)_icon).color = _originalColor; } } public void OnPointerClick(PointerEventData eventData) { CardDeleteLog.Section("Card bar click"); if (!CardDeleteManager.isPickPhase) { CardDeleteLog.Line("Ignored: not in pick phase."); return; } if (!CardDeleteManager.IsLocalPlayerTurn()) { CardDeleteLog.Line("Ignored: not local player's turn."); return; } if ((Object)(object)((Component)this).GetComponent() == (Object)null) { CardDeleteLog.Warn("Ignored: no CardBarButton on this object."); return; } CardInfo cardInfo = GetCardInfo(); if ((Object)(object)cardInfo == (Object)null) { CardDeleteLog.Warn("Ignored: CardBarButton.card is null."); return; } CardDeleteLog.Line("Valid delete click on '" + cardInfo.cardName + "'."); CardDeleteManager.instance?.RequestDelete(cardInfo); } private CardInfo GetCardInfo() { CardBarButton component = ((Component)this).GetComponent(); if ((Object)(object)component == (Object)null) { return null; } object? value = s_cardField.GetValue(component); return (CardInfo)((value is CardInfo) ? value : null); } private void OnDestroy() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_icon != (Object)null) { ((Graphic)_icon).color = _originalColor; } } } public class CardDeleteManager : MonoBehaviour { public static CardDeleteManager instance; public static bool isPickPhase; public static int currentPickerID = -1; private static readonly MethodInfo s_rpca_assignCard = AccessTools.Method(typeof(Cards), "RPCA_AssignCard", new Type[7] { typeof(string), typeof(int), typeof(bool), typeof(string), typeof(float), typeof(float), typeof(bool) }, (Type[])null); private static readonly MethodInfo s_rpca_donePicking = AccessTools.Method(typeof(CardChoice), "RPCA_DonePicking", (Type[])null, (Type[])null); private static readonly FieldInfo s_spawnedCardsField = AccessTools.Field(typeof(CardChoice), "spawnedCards"); private static readonly FieldInfo s_isPlayingField = AccessTools.Field(typeof(CardChoice), "isPlaying"); private static readonly FieldInfo s_picksField = AccessTools.Field(typeof(CardChoice), "picks"); private static readonly FieldInfo s_cardBarButtonCard = AccessTools.Field(typeof(CardBarButton), "card"); private static MethodInfo s_photonDestroy; private static PropertyInfo s_photonOfflineMode; private void Start() { instance = this; GameModeManager.AddHook("PlayerPickStart", (Func)OnPlayerPickStart); GameModeManager.AddHook("PlayerPickEnd", (Func)OnPlayerPickEnd); CardDeleteLog.Section("CardDeleteManager initialized"); } private IEnumerator OnPlayerPickStart(IGameModeHandler gm) { CardDeleteLog.Section("HookPlayerPickStart (info only)"); CardDeleteLog.Line("gameMode=" + (((gm != null) ? gm.Name : null) ?? "unknown")); yield break; } private IEnumerator OnPlayerPickEnd(IGameModeHandler gm) { CardDeleteLog.Section("Pick phase ended (HookPlayerPickEnd)"); CardDeleteLog.Line($"Was picker playerID={currentPickerID}"); isPickPhase = false; currentPickerID = -1; DisableDeleteMode(); yield break; } public void BeginPickPhase(int pickerID, string source) { if (pickerID < 0) { CardDeleteLog.Warn($"BeginPickPhase ignored (pickerID={pickerID})."); return; } if (isPickPhase && currentPickerID == pickerID) { CardDeleteLog.Line($"BeginPickPhase skipped — already active for player {pickerID}."); return; } currentPickerID = pickerID; isPickPhase = true; CardDeleteLog.Section("Pick phase active"); CardDeleteLog.Line($"pickerID={pickerID}, source={source}"); EnableDeleteMode(pickerID); } public void RequestDelete(CardInfo card) { CardDeleteLog.Section("Delete requested (local click)"); CardDeleteLog.Line($"Card='{card?.cardName}', pickerID={currentPickerID}"); NetworkingManager.RPC(typeof(CardDeleteManager), "URPC_SyncDelete", new object[2] { currentPickerID, ((Object)card).name }); } [UnboundRPC] public static void URPC_SyncDelete(int playerID, string cardObjectName) { CardDeleteLog.Section("URPC_SyncDelete (all clients)"); CardDeleteLog.Line($"playerID={playerID}, card='{cardObjectName}'"); Player val = PlayerManager.instance.players.Find((Player p) => p.playerID == playerID); if ((Object)(object)val == (Object)null) { CardDeleteLog.Error($"Player {playerID} not found."); return; } CardInfo val2 = val.data.currentCards?.Find((CardInfo c) => (Object)(object)c != (Object)null && ((Object)c).name == cardObjectName); if ((Object)(object)val2 == (Object)null) { CardDeleteLog.Error($"Card '{cardObjectName}' not found on player {playerID}."); EndPickPhase(); return; } try { Cards.instance.RemoveCardFromPlayer(val, val2, (SelectionType)2); CardDeleteLog.Line("Removed '" + cardObjectName + "' via RemoveCardFromPlayer."); } catch (Exception arg) { CardDeleteLog.Error($"RemoveCardFromPlayer failed: {arg}"); FallbackRemove(val, cardObjectName); } EndPickPhase(); } private static void FallbackRemove(Player player, string cardObjectName) { CardDeleteLog.Warn("Using fallback removal method."); List list = (from c in player.data.currentCards ?? new List() where (Object)(object)c != (Object)null && ((Object)c).name != cardObjectName select ((Object)c).name).ToList(); try { Cards.RPCA_FullReset(player.playerID); } catch (Exception ex) { CardDeleteLog.Error("FullReset: " + ex.Message); } try { Cards.RPCA_ClearCardBar(player.playerID); } catch (Exception ex2) { CardDeleteLog.Error("ClearCardBar: " + ex2.Message); } if (s_rpca_assignCard == null) { CardDeleteLog.Error("RPCA_AssignCard not found."); return; } foreach (string item in list) { try { s_rpca_assignCard.Invoke(null, new object[7] { item, player.playerID, false, "", 0f, 0f, true }); CardDeleteLog.Line("Reapplied '" + item + "'."); } catch (Exception ex3) { CardDeleteLog.Error("Failed to reapply '" + item + "': " + ex3.Message); } } } private static void EndPickPhase() { CardDeleteLog.Section("EndPickPhase"); CardChoice val = CardChoice.instance; if ((Object)(object)val == (Object)null) { CardDeleteLog.Error("CardChoice.instance is null."); return; } ((MonoBehaviour)val).StopAllCoroutines(); s_isPlayingField?.SetValue(val, false); s_picksField?.SetValue(val, 0); CleanupSpawnedDraftCards(val); if (s_rpca_donePicking != null) { try { s_rpca_donePicking.Invoke(val, null); CardDeleteLog.Line("Invoked RPCA_DonePicking."); return; } catch (Exception ex) { CardDeleteLog.Error("RPCA_DonePicking failed: " + ex.Message); val.IsPicking = false; return; } } val.IsPicking = false; } private static void CleanupSpawnedDraftCards(CardChoice cc) { if (s_spawnedCardsField == null || !(s_spawnedCardsField.GetValue(cc) is List list) || list.Count == 0) { return; } CardDeleteLog.Line($"Cleaning {list.Count} draft card(s)."); foreach (GameObject item in list.ToList()) { if ((Object)(object)item == (Object)null) { continue; } try { CardVisuals componentInChildren = item.GetComponentInChildren(); if (componentInChildren != null) { componentInChildren.Leave(); } } catch { } DestroyDraftCardObject(item); } list.Clear(); } private static void DestroyDraftCardObject(GameObject go) { if ((Object)(object)go == (Object)null) { return; } try { if (TryPhotonDestroy(go)) { return; } } catch { } Object.Destroy((Object)(object)go); } private static bool TryPhotonDestroy(GameObject go) { if (s_photonDestroy == null) { Type type = AccessTools.TypeByName("Photon.Pun.PhotonNetwork"); if (type == null) { return false; } s_photonOfflineMode = type.GetProperty("OfflineMode", BindingFlags.Static | BindingFlags.Public); s_photonDestroy = type.GetMethod("Destroy", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(GameObject) }, null); } if (s_photonDestroy == null) { return false; } if (s_photonOfflineMode != null && (bool)s_photonOfflineMode.GetValue(null, null)) { return false; } Type type2 = AccessTools.TypeByName("Photon.Pun.PhotonView"); if (type2 == null || (Object)(object)go.GetComponent(type2) == (Object)null) { return false; } s_photonDestroy.Invoke(null, new object[1] { go }); return true; } private void EnableDeleteMode(int pickerID) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown if ((Object)(object)CardBarHandler.instance == (Object)null) { CardDeleteLog.Warn("CardBarHandler.instance is null."); return; } CardBar[] value = Traverse.Create((object)CardBarHandler.instance).Field("cardBars").GetValue(); if (value == null || pickerID < 0 || pickerID >= value.Length) { CardDeleteLog.Warn($"Invalid pickerID={pickerID}."); return; } int num = 0; foreach (Transform item in ((Component)value[pickerID]).transform) { Transform val = item; if (((Component)val).gameObject.activeSelf) { CardBarButton component = ((Component)val).GetComponent(); if (!((Object)(object)component == (Object)null) && !((Object)/*isinst with value type is only supported in some contexts*/ == (Object)null) && (Object)(object)((Component)val).GetComponent() == (Object)null) { ((Component)val).gameObject.AddComponent(); num++; } } } CardDeleteLog.Line($"EnableDeleteMode: attached={num} for player {pickerID}."); } private void DisableDeleteMode() { CardBarDeleteButton[] array = Object.FindObjectsOfType(); CardBarDeleteButton[] array2 = array; for (int i = 0; i < array2.Length; i++) { Object.Destroy((Object)(object)array2[i]); } CardDeleteLog.Line($"DisableDeleteMode: removed {array.Length} button(s)."); } public static bool IsLocalPlayerTurn() { if (currentPickerID < 0) { return false; } Player val = PlayerManager.instance.players.Find((Player p) => p.playerID == currentPickerID); if ((Object)(object)val == (Object)null) { return false; } object value = AccessTools.Field(typeof(CharacterData), "view").GetValue(val.data); if (value == null) { return true; } return (bool)AccessTools.Property(value.GetType(), "IsMine").GetValue(value, null); } } [HarmonyPatch] internal static class CardDeletePatches { [HarmonyPatch(typeof(CardChoiceVisuals), "Show")] [HarmonyPrefix] private static void CardChoiceVisuals_Show_Prefix(int pickerID, bool animateIn) { CardDeleteManager.instance?.BeginPickPhase(pickerID, "CardChoiceVisuals.Show"); } [HarmonyPatch(typeof(CardChoice), "StartPick")] [HarmonyPostfix] private static void CardChoice_StartPick_Postfix(int picksToSet, int pickerIDToSet) { CardDeleteManager.instance?.BeginPickPhase(pickerIDToSet, "CardChoice.StartPick"); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("Rounds2Mod", "Mod for ROUNDS", "1.0.0")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Logger; private void Awake() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) Logger = ((BaseUnityPlugin)this).Logger; CardDeleteLog.Section("Plugin Awake"); new Harmony("Rounds2Mod").PatchAll(); ((Component)this).gameObject.AddComponent(); CardDeleteLog.Line("Plugin Rounds2Mod loaded."); } }