private void PerformCleanup() { // Sets up card movements to indicate where each card should go. CardMovementCollection cardsToMove = new CardMovementCollection(this.SetAside, c => c.CanCleanUp, DeckLocation.SetAside, DeckLocation.Discard); cardsToMove.AddRange(this.Hand, DeckLocation.Hand, DeckLocation.Discard); cardsToMove.AddRange(this.InPlay, DeckLocation.InPlay, DeckLocation.Discard); foreach (Card durationCard in this.InPlay.Where(c => !c.CanCleanUp)) cardsToMove[durationCard].Destination = DeckLocation.SetAside; IEnumerable<CardMovement> inPlayCards = cardsToMove.Where(cm => cm.CurrentLocation == DeckLocation.InPlay); ParallelQuery<CardMovement> pqCardsToMove = cardsToMove.AsParallel().Where(cm => cm.CurrentLocation == DeckLocation.InPlay && cm.Destination == DeckLocation.SetAside && cm.Card.ModifiedBy != null && cardsToMove.Contains(cm.Card.ModifiedBy.PhysicalCard) && cardsToMove[cm.Card.ModifiedBy.PhysicalCard].Destination == DeckLocation.Discard); pqCardsToMove.ForAll(cm => cardsToMove[cm.Card.ModifiedBy.PhysicalCard].Destination = DeckLocation.SetAside); int drawSize = 5; if (CleaningUp != null) { Boolean cancelled = false; // Possibly changing events that can happen in the game CleaningUpEventArgs cuea = null; do { cuea = new CleaningUpEventArgs(this, 5, ref cardsToMove); cuea.Cancelled |= cancelled; CleaningUp(this, cuea); OptionCollection options = new OptionCollection(); IEnumerable<Type> cardTypes = cuea.Actions.Keys; foreach (Type key in cardTypes) options.Add(new Option(cuea.Actions[key].Text, false)); if (options.Count > 0) { options.Sort(); Choice choice = new Choice("Performing Clean-up", options, this, cuea); ChoiceResult result = this.MakeChoice(choice); if (result.Options.Count > 0) cuea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(this, ref cuea); else break; } else break; cancelled |= cuea.Cancelled; } while (CleaningUp != null); if (cuea != null) cancelled |= cuea.Cancelled; if (cancelled) return; if (cuea.NextPlayer != null) _CurrentTurn.NextPlayer = cuea.NextPlayer; _CurrentTurn.NextGrantedBy = cuea.NextGrantedBy; drawSize = cuea.DrawSize; } // Discard any Revealed cards (should be none?) this.DiscardRevealed(); CardsDiscardAction cdaHand = null; if (cardsToMove.Count(c => c.CurrentLocation == DeckLocation.Hand) > 0) cdaHand = new CardsDiscardAction(this, null, "Discard hand", player_DiscardHand, true) { Data = cardsToMove }; // Discard non-Duration (or Duration-modifying) cards in In Play & Set Aside at the same time this.Discard(DeckLocation.InPlayAndSetAside, cardsToMove.Where(cm => (cm.CurrentLocation == DeckLocation.InPlay || cm.CurrentLocation == DeckLocation.SetAside) && cm.Destination == DeckLocation.Discard).Select<CardMovement, Card>(cm => cm.Card), cdaHand); // Discard Hand this.AddCardsInto(DeckLocation.Discard, this.RetrieveCardsFrom(DeckLocation.Hand, c => cardsToMove[c].CurrentLocation == DeckLocation.Hand && cardsToMove[c].Destination == DeckLocation.Discard)); // Move Duration (and Duration-modifying) cards from In Play into Set Aside this.MoveInPlayToSetAside(c => cardsToMove.Contains(c) && cardsToMove[c].CurrentLocation == DeckLocation.InPlay && cardsToMove[c].Destination == DeckLocation.SetAside); // Move any cards that have had their Destination changed to their appropriate locations IEnumerable<Card> replaceCards = cardsToMove.Where(cm => cm.Destination == DeckLocation.Deck).Select(cm => cm.Card); if (replaceCards.Count() > 0) { Choice replaceChoice = new Choice("Choose order of cards to put back on your deck", null, replaceCards, this, true, replaceCards.Count(), replaceCards.Count()); ChoiceResult replaceResult = this.MakeChoice(replaceChoice); this.RetrieveCardsFrom(DeckLocation.InPlay, c => cardsToMove[c].CurrentLocation == DeckLocation.InPlay && replaceResult.Cards.Contains(c)); this.RetrieveCardsFrom(DeckLocation.SetAside, c => cardsToMove[c].CurrentLocation == DeckLocation.SetAside && replaceResult.Cards.Contains(c)); this.AddCardsToDeck(replaceResult.Cards, DeckPosition.Top); } #if DEBUG if (this.InPlay.Count > 0) throw new Exception("Something happened -- there are cards left in the player's In Play area!"); #endif if (CurrentTurn != null) CurrentTurn.Finished(); _Actions = _Buys = 0; _Currency.Coin.Value = 0; _Currency.Potion.Value = 0; _ActionsPlayed = 0; #if DEBUG // Check to see that there are no duplicate cards anywhere CardCollection allCards = new CardCollection(); allCards.AddRange(this.Hand); allCards.AddRange(this.Revealed); allCards.AddRange(this.Private); allCards.AddRange(this.InPlay); allCards.AddRange(this.SetAside); allCards.AddRange(this.DrawPile.LookThrough(c => true)); allCards.AddRange(this.DiscardPile.LookThrough(c => true)); foreach (CardMat mat in this.PlayerMats.Values) allCards.AddRange(mat); ParallelQuery<Card> duplicateCards = allCards.AsParallel().Where(c => allCards.Count(ct => ct == c) > 1); //IEnumerable<Card> duplicateCards = allCards.FindAll(c => allCards.Count(ct => ct == c) > 1); if (duplicateCards.Count() > 0) { // Ruh Roh throw new Exception("Duplicate cards found! Something went wrong!"); } #endif DrawHand(drawSize); if (CleanedUp != null) { CleanedUp(this, new CleanedUpEventArgs(this, drawSize)); } if (TurnEnded != null) { TurnEnded(this, new TurnEndedEventArgs(this)); } }