using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; using Photon; using Photon.Pun; using System; public class SelectHandEffect : MonoBehaviourPunCallbacks { public void SetUp( Player selectPlayer, Func canTargetCondition, Func, CardSource, bool> canTargetCondition_ByPreSelecetedList, Func, bool> canEndSelectCondition, int maxCount, bool canNoSelect, bool canEndNotMax, bool isShowOpponent, Func selectCardCoroutine, Func, IEnumerator> afterSelectCardCoroutine, Mode mode, ICardEffect cardEffect) { _selectPlayer = selectPlayer; _canTargetCondition = canTargetCondition; _canTargetCondition_ByPreSelecetedList = canTargetCondition_ByPreSelecetedList; _canEndSelectCondition = canEndSelectCondition; _maxCount = maxCount; _canNoSelect = canNoSelect; _canEndNotMax = canEndNotMax; _isShowOpponent = isShowOpponent; _selectCardCoroutine = selectCardCoroutine; _afterSelectCardCoroutine = afterSelectCardCoroutine; _mode = mode; _cardEffect = cardEffect; _showCard = true; _digiXros = false; _customMessage_ShowCard = null; _customMessage = ""; _customMessage_Enemy = ""; _showOpponentMessage = true; _isLocal = false; _isFaceUp = false; } public void SetIsLocal() { _isLocal = true; } public void SetNotShowCard() { _showCard = false; } public void SetDigiXros() { _digiXros = true; } public void SetIsFaceup() { _isFaceUp = true; } public void SetReducedCostTuple((int reduceCost, Func reduceCostCardCondition)? reduceCostTuple) { _reduceCostTuple = reduceCostTuple; } public void SetFixedCostTuple((int fixedCost, Func fixedCostCardCondition)? fixedCostTuple) { _fixedCostTuple = fixedCostTuple; } public void SetUpCustomMessage_ShowCard(string CustomMessage_ShowCard) { _customMessage_ShowCard = CustomMessage_ShowCard; } public void SetUpCustomMessage(string CustomMessage, string CustomMessage_Enemy) { _customMessage = CustomMessage; _customMessage_Enemy = CustomMessage_Enemy; } public void SetNotShowOpponentMessage() { _showOpponentMessage = false; } //The player who selects the hand Player _selectPlayer; //Conditions of the cards that can be selected Func _canTargetCondition = null; //Whether the card can be selected with the current selection list status Func, CardSource, bool> _canTargetCondition_ByPreSelecetedList = null; //Conditions under which a selection can be terminated (see list of selection termination points) Func, bool> _canEndSelectCondition = null; //Maximum number of sheets to be selected int _maxCount = 0; //Whether you can choose not to choose bool _canNoSelect = false; //Whether you can finish your selection with less than the maximum number bool _canEndNotMax = false; //Whether or not to show it to the other player bool _isShowOpponent = false; //(Limited to Mode.Custom) Processing to be performed by selecting Func _selectCardCoroutine = null; //Processing to be done after selection Func, IEnumerator> _afterSelectCardCoroutine = null; //Classification of processing to be done by selection Mode _mode = Mode.Custom; //Skill in making card selections ICardEffect _cardEffect; bool _showCard = true; bool _digiXros = false; bool _isLocal = false; bool _isFaceUp = false; (int reduceCost, Func reduceCostCardCondition)? _reduceCostTuple = null; (int fixedCost, Func fixedCostCardCondition)? _fixedCostTuple = null; public enum Mode { Discard, PutLibraryTop, PutLibraryBottom, PutSecurityBottom, PlayForFree, PlayForCost, Custom } //Selected Hand Card List List _targetCards = new List(); //No Selection Flag bool _noSelect = false; string _customMessage_ShowCard = null; string _customMessage = null; string _customMessage_Enemy = null; bool _showOpponentMessage = true; #region Whether choice is possible public bool active() { if (_maxCount <= 0) return false; if (_selectPlayer != null) { if (_selectPlayer.HandCards.Count((cardSource) => _canTargetCondition(cardSource)) > 0) { return true; } } return false; } #endregion public virtual IEnumerator Activate() { bool oldIsSelecting = GManager.instance.turnStateMachine.IsSelecting; List playCharas = new List(); List discardHands = new List(); List putClockCards = new List(); _noSelect = false; if (active()) { if (_maxCount == 0) { _canNoSelect = true; } _targetCards = new List(); foreach (Player player in GManager.instance.turnStateMachine.gameContext.Players) { GManager.instance.turnStateMachine.OffFieldCardTarget(player); GManager.instance.turnStateMachine.OffHandCardTarget(player); } if (_selectPlayer.isYou) { GManager.instance.sideBar.SetUpSideBar(); GManager.instance.turnStateMachine.IsSelecting = true; #region Message Display if (!string.IsNullOrEmpty(_customMessage)) { GManager.instance.commandText.OpenCommandText(_customMessage, _digiXros); } else { string message = ""; switch (_mode) { case Mode.Discard: message = "Select cards to discard."; break; case Mode.PutLibraryTop: message = "Select cards to put on top of the deck."; break; case Mode.PutLibraryBottom: if (_maxCount >= 2) { message = "Select cards to put on bottom of the deck\n(cards will be placed back to the bottom of the deck so that cards with lower numbers are on top)."; } else { message = "Select cards to put on bottom of the deck."; } break; case Mode.PutSecurityBottom: string str = _isFaceUp ? "faceup" : "facedown"; message = $"Select card(s) to put on bottom of the security {str}."; break; case Mode.PlayForFree: message = "Select cards to play for without paying the cost."; break; case Mode.PlayForCost: message = "Select cards to play."; break; case Mode.Custom: message = "Select cards in your hand."; break; } if (!string.IsNullOrEmpty(message)) { GManager.instance.commandText.OpenCommandText(message, _digiXros); } } #endregion List PreSelectedHandCards = new List(); foreach (HandCard handCard in _selectPlayer.HandCardObjects) { if (handCard != null) { if (_canTargetCondition(handCard.cardSource)) { handCard.AddClickTarget(OnClickHandCard); } } } CheckEndSelect(); #region Processing when a card in the hand is clicked void OnClickHandCard(HandCard handCard) { if (PreSelectedHandCards.Contains(handCard.cardSource)) { PreSelectedHandCards.Remove(handCard.cardSource); } else { if (_canTargetCondition_ByPreSelecetedList != null) { List _PreSelectedList = new List(); for (int i = 0; i < PreSelectedHandCards.Count; i++) { if (PreSelectedHandCards.Count >= _maxCount && i == PreSelectedHandCards.Count - 1) { continue; } _PreSelectedList.Add(PreSelectedHandCards[i]); } if (!_canTargetCondition_ByPreSelecetedList(_PreSelectedList, handCard.cardSource)) { return; } } if (PreSelectedHandCards.Count < _maxCount) { PreSelectedHandCards.Add(handCard.cardSource); } else { if (PreSelectedHandCards.Count > 0) { PreSelectedHandCards.RemoveAt(PreSelectedHandCards.Count - 1); PreSelectedHandCards.Add(handCard.cardSource); } } if (!ContinuousController.instance.checkBeforeEndingSelection && !_canNoSelect && !_canEndNotMax && _maxCount == PreSelectedHandCards.Count) { EndSelect_RPC(); return; } } CheckEndSelect(); } #endregion void EndSelect_RPC() { foreach (Player player in GManager.instance.turnStateMachine.gameContext.Players) { foreach (Permanent permanent in player.GetFieldPermanents()) { permanent.ShowingPermanentCard.RemoveSelectEffect(); permanent.ShowingPermanentCard.RemoveClickTarget(); } foreach (HandCard handCard in player.HandCardObjects) { if (handCard != null) { handCard.RemoveClickTarget(); handCard.RemoveSelectEffect(); } } } List CardIDs = new List(); foreach (CardSource cardSource in PreSelectedHandCards) { CardIDs.Add(cardSource.CardIndex); } if (!_isLocal) { photonView.RPC("SetTargetHandCards", RpcTarget.All, _selectPlayer.PlayerID, CardIDs.ToArray()); } else { SetTargetHandCards(_selectPlayer.PlayerID, CardIDs.ToArray()); } GManager.instance.BackButton.CloseSelectCommandButton(); } #region Determines if selection can be terminated and displays UI void CheckEndSelect() { #region UI display depending on whether it can be terminated if (CanEndSelect(PreSelectedHandCards)) { GManager.instance.selectCommandPanel.SetUpCommandButton( new List() { new Command_SelectCommand("End Selection", EndSelect_RPC, 0) }); } else { GManager.instance.selectCommandPanel.Off(false); GManager.instance.sideBar.SetUpSideBar(); } #endregion #region Card UI display by selection list foreach (CardSource cardSource in _selectPlayer.HandCards) { if (cardSource.ShowingHandCard != null) { cardSource.ShowingHandCard.RemoveSelectEffect(); cardSource.ShowingHandCard.OffSelectedIndexText(); if (_canTargetCondition(cardSource)) { if (PreSelectedHandCards.Contains(cardSource)) { cardSource.ShowingHandCard.OnSelect(); cardSource.ShowingHandCard.SetOrangeOutline(); cardSource.ShowingHandCard.SetSelectedIndexText(PreSelectedHandCards.IndexOf(cardSource) + 1); } else { cardSource.ShowingHandCard.OnSelect(); cardSource.ShowingHandCard.SetBlueOutline(); if (_canTargetCondition_ByPreSelecetedList != null) { List _PreSelectedList = new List(); for (int i = 0; i < PreSelectedHandCards.Count; i++) { if (PreSelectedHandCards.Count >= _maxCount && i == PreSelectedHandCards.Count - 1) { continue; } _PreSelectedList.Add(PreSelectedHandCards[i]); } if (!_canTargetCondition_ByPreSelecetedList(_PreSelectedList, cardSource)) { cardSource.ShowingHandCard.RemoveSelectEffect(); } } } } } } #endregion if (PreSelectedHandCards.Count >= 1) { GManager.instance.BackButton.CloseSelectCommandButton(); } else { if (_canNoSelect) { GManager.instance.BackButton.OpenSelectCommandButton("No Selection", () => NoSelect_RPC(), 0); void NoSelect_RPC() { if (!_isLocal) { photonView.RPC("SetTargetHandCards", RpcTarget.All, _selectPlayer.PlayerID, null); } else { SetTargetHandCards(_selectPlayer.PlayerID, null); } } } } } #endregion } else { #region Message Display if (_showOpponentMessage) { if (!string.IsNullOrEmpty(_customMessage_Enemy)) { GManager.instance.commandText.OpenCommandText(_customMessage_Enemy, _digiXros); } else { GManager.instance.commandText.OpenCommandText("The opponent is selecting cards.", _digiXros); } } #endregion #region AI if (GManager.instance.IsAI) { List ValidCards = new List(); foreach (CardSource cardSource in _selectPlayer.HandCards) { if (_canTargetCondition(cardSource)) { ValidCards.Add(cardSource); } } if (_canEndNotMax) { _noSelect = true; for (int maxCount = 0; maxCount < _maxCount; maxCount++) { IList indexList = Enumerable.Range(0, ValidCards.Count).ToList(); List CardIDs = null; if (ValidCards.Count >= maxCount) { for (int i = 0; i < 1000; i++) { List GetIndexes = indexList.GetRandom(maxCount).ToList(); List GetCards = new List(); foreach (int index in GetIndexes) { GetCards.Add(ValidCards[index]); } if (CanEndSelect(GetCards)) { CardIDs = new List(); foreach (CardSource cardSource in GetCards) { CardIDs.Add(cardSource.CardIndex); } break; } } } SetTargetHandCards(_selectPlayer.PlayerID, CardIDs != null ? CardIDs.ToArray() : null); } } else { IList indexList = Enumerable.Range(0, ValidCards.Count).ToList(); List CardIDs = null; if (ValidCards.Count >= _maxCount) { for (int i = 0; i < 1000; i++) { List GetIndexes = indexList.GetRandom(_maxCount).ToList(); List GetCards = new List(); foreach (int index in GetIndexes) { GetCards.Add(ValidCards[index]); } if (CanEndSelect(GetCards)) { CardIDs = new List(); foreach (CardSource cardSource in GetCards) { CardIDs.Add(cardSource.CardIndex); } break; } } } SetTargetHandCards(_selectPlayer.PlayerID, CardIDs != null ? CardIDs.ToArray() : null); } } #endregion } #region Determine if you can exit bool CanEndSelect(List PreSelectedHandCards) { //If the number of cards does not meet the requirements if (!(PreSelectedHandCards.Count == _maxCount || (PreSelectedHandCards.Count <= _maxCount && _canEndNotMax))) { return false; } //Failure to meet specified conditions if (_canEndSelectCondition != null) { if (!_canEndSelectCondition(PreSelectedHandCards)) { return false; } } return true; } #endregion //Wait for selection to be completed yield return new WaitUntil(() => _selectPlayer.HasPlayerSelection()); CardSelection cardSeletion = _selectPlayer.DequeuePlayerSelection(); _targetCards = new List(); if (cardSeletion != null) { _noSelect = cardSeletion.CardIDList == null; if (!_noSelect) { foreach (int CardID in cardSeletion.CardIDList) { _targetCards.Add(GManager.instance.turnStateMachine.gameContext.ActiveCardList[CardID]); } } else { GManager.instance.selectCommandPanel.CloseSelectCommandPanel(); } } #region Reset foreach (Player player in GManager.instance.turnStateMachine.gameContext.Players) { GManager.instance.turnStateMachine.OffFieldCardTarget(player); GManager.instance.turnStateMachine.OffHandCardTarget(player); foreach (Permanent chara in player.GetFieldPermanents()) { chara.ShowingPermanentCard.RemoveSelectEffect(); } foreach (HandCard handCard in player.HandCardObjects) { if (handCard != null) { handCard.RemoveClickTarget(); handCard.RemoveSelectEffect(); handCard.OffSelectedIndexText(); } } } GManager.instance.commandText.CloseCommandText(); yield return new WaitWhile(() => GManager.instance.commandText.gameObject.activeSelf); GManager.instance.sideBar.OffSideBar(); #endregion if (!_noSelect) { #region Show selected cards if (_targetCards.Count > 0 && _showCard) { if (_isShowOpponent || _selectPlayer.isYou) { if (!string.IsNullOrEmpty(_customMessage_ShowCard)) { yield return ContinuousController.instance.StartCoroutine(GManager.instance.GetComponent().ShowCardEffect(_targetCards, _customMessage_ShowCard, true, true)); } else { switch (_mode) { case Mode.Discard: yield return ContinuousController.instance.StartCoroutine(GManager.instance.GetComponent().ShowCardEffect(_targetCards, "Discarded cards", true, true)); break; case Mode.PutLibraryTop: yield return ContinuousController.instance.StartCoroutine(GManager.instance.GetComponent().ShowCardEffect(_targetCards, "Cards put on top of deck", true, true)); break; case Mode.PutLibraryBottom: yield return ContinuousController.instance.StartCoroutine(GManager.instance.GetComponent().ShowCardEffect(_targetCards, "Cards put on bottom of deck", true, true)); break; case Mode.PutSecurityBottom: yield return ContinuousController.instance.StartCoroutine(GManager.instance.GetComponent().ShowCardEffect(_targetCards, "Cards put on bottom of security", _isFaceUp, true)); break; case Mode.PlayForFree: case Mode.PlayForCost: yield return ContinuousController.instance.StartCoroutine(GManager.instance.GetComponent().ShowCardEffect(_targetCards, "Played cards", true, true)); break; case Mode.Custom: yield return ContinuousController.instance.StartCoroutine(GManager.instance.GetComponent().ShowCardEffect(_targetCards, "Selected cards", true, true)); break; } } } } #endregion Hashtable hashtable = new Hashtable(); if (_cardEffect != null) { hashtable.Add("CardEffect", _cardEffect); } string log = ""; log += $"\nSelected Hand Card:"; #region Processes the selected card foreach (CardSource cardSource in _targetCards) { log += $"\n{cardSource.BaseENGCardNameFromEntity}({cardSource.CardID})"; if(_selectCardCoroutine != null) yield return StartCoroutine(_selectCardCoroutine(cardSource)); } switch (_mode) { case Mode.Discard: discardHands = _targetCards.Map(cardSource => new IDiscardHand(cardSource, hashtable)); break; case Mode.PutLibraryTop: yield return ContinuousController.instance.StartCoroutine(CardObjectController.AddLibraryTopCards(_targetCards)); break; case Mode.PutLibraryBottom: yield return ContinuousController.instance.StartCoroutine(CardObjectController.AddLibraryBottomCards(_targetCards)); break; case Mode.PutSecurityBottom: foreach (CardSource cardSource in _targetCards) yield return ContinuousController.instance.StartCoroutine(CardObjectController.AddSecurityCard(cardSource,false, _isFaceUp)); break; case Mode.PlayForFree: yield return ContinuousController.instance.StartCoroutine( CardEffectCommons.PlayPermanentCards( cardSources: _targetCards, activateClass: _cardEffect, payCost: false, isTapped: false, root: SelectCardEffect.Root.Hand, activateETB: true)); break; case Mode.PlayForCost: { bool PermanentsCondition(List targetPermanents) { if (targetPermanents == null) { return true; } else { if (targetPermanents.Count((targetPermanent) => targetPermanent != null) == 0) { return true; } } return false; } bool SharedCardCondition(CardSource cardSource) => _targetCards.Contains(cardSource); bool RootCondition(SelectCardEffect.Root root) => true; bool CanUseCondition(Hashtable hashtable) => true; #region reduce cost Func getChangeCostEffect = null; if (_reduceCostTuple != null) { bool CardCondition(CardSource cardSource) { return SharedCardCondition(cardSource) && (_reduceCostTuple.Value.reduceCostCardCondition == null || _reduceCostTuple.Value.reduceCostCardCondition(cardSource)); } int ChangeCost(CardSource cardSource, int Cost, SelectCardEffect.Root root, List targetPermanents) { if (PermanentsCondition(targetPermanents)) { Cost -= _reduceCostTuple.Value.reduceCost; } return Cost; } bool isUpDown() => true; ChangeCostClass changeCostClass = new ChangeCostClass(); changeCostClass.SetUpICardEffect($"Play Cost -{_reduceCostTuple.Value.reduceCost}", CanUseCondition, _cardEffect.EffectSourceCard); changeCostClass.SetUpChangeCostClass(changeCostFunc: ChangeCost, cardSourceCondition: CardCondition, rootCondition: RootCondition, isUpDown: isUpDown, isCheckAvailability: () => false, isChangePayingCost: () => true); getChangeCostEffect = GetCardEffect; ICardEffect GetCardEffect(EffectTiming _timing) { if (_timing == EffectTiming.None) { return changeCostClass; } return null; } if (getChangeCostEffect != null) { _selectPlayer.UntilCalculateFixedCostEffect.Add(getChangeCostEffect); } } #endregion #region set fixed cost Func getFixedCostEffect = null; if (_fixedCostTuple != null) { bool CardCondition(CardSource cardSource) { return SharedCardCondition(cardSource) && (_fixedCostTuple.Value.fixedCostCardCondition == null || _fixedCostTuple.Value.fixedCostCardCondition(cardSource)); } int ChangeCost(CardSource cardSource, int Cost, SelectCardEffect.Root root, List targetPermanents) { if (PermanentsCondition(targetPermanents)) { Cost = _fixedCostTuple.Value.fixedCost; } return Cost; } bool isUpDown() => false; ChangeCostClass changeCostClass = new ChangeCostClass(); changeCostClass.SetUpICardEffect($"Play Cost {_fixedCostTuple.Value.fixedCost}", CanUseCondition, _cardEffect.EffectSourceCard); changeCostClass.SetUpChangeCostClass(changeCostFunc: ChangeCost, cardSourceCondition: CardCondition, rootCondition: RootCondition, isUpDown: isUpDown, isCheckAvailability: () => false, isChangePayingCost: () => true); getFixedCostEffect = GetCardEffect; ICardEffect GetCardEffect(EffectTiming _timing) { if (_timing == EffectTiming.None) { return changeCostClass; } return null; } if (getFixedCostEffect != null) { _selectPlayer.UntilCalculateFixedCostEffect.Add(getFixedCostEffect); } } #endregion yield return ContinuousController.instance.StartCoroutine( CardEffectCommons.PlayPermanentCards( cardSources: _targetCards, activateClass: _cardEffect, payCost: true, isTapped: false, root: SelectCardEffect.Root.Hand, activateETB: true)); #region release effect if (getChangeCostEffect != null) _selectPlayer.UntilCalculateFixedCostEffect.Remove(getChangeCostEffect); #endregion #region release effect if (getFixedCostEffect != null) _selectPlayer.UntilCalculateFixedCostEffect.Remove(getFixedCostEffect); #endregion break; } } #endregion if (discardHands.Count > 0) { yield return ContinuousController.instance.StartCoroutine(new IDiscardHands(discardHands, _cardEffect).DiscardHands()); } #region ���O�lj� if (_isShowOpponent || _selectPlayer.isYou) { if (_targetCards.Count >= 1) { log += "\n"; PlayLog.OnAddLog?.Invoke(log); } } #endregion } if (_afterSelectCardCoroutine != null) { yield return StartCoroutine(_afterSelectCardCoroutine(_targetCards)); } } GManager.instance.turnStateMachine.IsSelecting = oldIsSelecting; } #region ƒJ[ƒh‘I‘ð‚ðŒˆ’è [PunRPC] public void SetTargetHandCards(int playerID, int[] CardIDs) { Player selectionPlayer = GManager.instance.GetPlayerFromID(playerID); if (selectionPlayer == null) { return; } selectionPlayer.QueuePlayerSelection(new CardSelection(CardIDs)); } #endregion }