using DominionBase.Enums; using DominionBase.Players; using DominionBase.Properties; using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; namespace DominionBase.Cards.Nocturne { public class Crypt : Card { private readonly List> _cryptedCards = new List>(); private int _activeIndex = -1; public Crypt() : base(Categories.Night | Categories.Duration, Source.Nocturne, Location.Kingdom) { BaseCost = new Cost(5); } public override bool CanCleanUp { get => !_cryptedCards.Any(p => p.Any()) && base.CanCleanUp; } public override void TearDown(IGame game) { Contract.Requires(game != null, "game cannot be null"); base.TearDown(game); _cryptedCards.Clear(); foreach (var player in game.Players) player.TurnStarted -= Player_TurnStarted; } public override IEnumerable LookThrough(Predicate predicate) { return _cryptedCards.SelectMany(ic => ic.Where(c => predicate(c))); } public override bool IsStackable { get { return _cryptedCards.Sum(l => l.Count) == 0; } } public override DisplayableCollection Stack() { var cc = new DisplayableCollection(); _cryptedCards.ForEach(l => l.ForEach(c => cc.Add(Universal.Utility.GenerateCardBack(c.CardBack)))); cc.Add(this); return cc; } public override void AddedTo(DeckLocation location, IPlayer player) { base.AddedTo(location, player); if (location == DeckLocation.InPlay) { _cryptedCards.Clear(); CanCleanUpPlayed.Clear(); } } public override void FollowInstructions(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); base.FollowInstructions(player); var treasures = player.InPlayAndSetAside[Categories.Treasure]; var choiceSetAside = new Choice(Resource.SetAsideTreasures, this, treasures, ChoiceOutcome.Select, player, minimum: 0, maximum: treasures.Count); var resultSetAside = player.MakeChoice(choiceSetAside); var cryptedCards = player.RetrieveCardsFrom(DeckLocation.InPlayAndSetAside, resultSetAside.Cards); _activeIndex = _cryptedCards.Count; _cryptedCards.Add(cryptedCards); CanCleanUpPlayed.Add(!cryptedCards.Any()); player._Game.SendMessage(player, this, cryptedCards.ToArray()); if (CanCleanUpPlayed.Any(cup => !cup)) player.TurnStarted += Player_TurnStarted; } public override void ResolveDuration(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); base.ResolveDuration(player); AddCryptedCardToHand(player); } private void AddCryptedCardToHand(IPlayer player) { if (_cryptedCards.Count - 1 >= _activeIndex) { var cryptPile = _cryptedCards[_activeIndex]; var choice = new Choice(Resource.CardToAddToHand, this, cryptPile, ChoiceOutcome.Select, player); var result = player.MakeChoice(choice); if (result.Cards.Any()) { cryptPile.Remove(result.Cards[0]); player.AddCardToHand(result.Cards[0]); CanCleanUpPlayed[_activeIndex] = !cryptPile.Any(); } } else { player.TurnStarted -= Player_TurnStarted; } } public override void PerformEndgameCalculations(IPlayer player, PointsCollection collection) { Contract.Requires(collection != null, "collection cannot be null"); // Add back any Crypted cards that are still on this foreach (var cards in _cryptedCards) collection.AddRange(cards); _cryptedCards.Clear(); } private void Player_TurnStarted(object sender, TurnStartedEventArgs e) { for (var i = 0; i < _cryptedCards.Count; i++) { var key = $"{UniqueId}_{i}"; // Skip empty crypted piles (they'll be eventually removed when Crypt is removed from play) // as well as keys we've already resolved if (_cryptedCards[i].Count == 0 || e.HandledBy.Contains(key)) continue; if (!e.Resolvers.ContainsKey(key)) e.Resolvers[key] = new TurnStartedResolver( e.Player, this, Resource.ResolveCard.Replace("{card}", PhysicalCard.ToString()), (IPlayer player, ref TurnStartedEventArgs eAction) => { _activeIndex = (int)eAction.Resolvers[key].Data; ResolveDuration(eAction.Player); eAction.HandledBy.Add(key); }, true, i); } } } }