using DominionBase.Enums; using DominionBase.Players; using System.Diagnostics.Contracts; using System.Linq; namespace DominionBase.Cards.Renaissance { public class CargoShip : Card { private readonly CardCollection _setAsideCards = new CardCollection(); private int _numberOfTriggers = 0; private int _activeIndex = -1; public CargoShip() : base(Categories.Action | Categories.Duration, Source.Renaissance, Location.Kingdom, Traits.PlusCoin | Traits.ReactToGain | Traits.Terminal) { BaseCost = new Cost(3); Benefit.Currency.Coin.Value = 2; } public override bool CanCleanUp { get => !_setAsideCards.Any() && base.CanCleanUp; } public override void TearDown(IGame game) { Contract.Requires(game != null, "game cannot be null"); base.TearDown(game); _setAsideCards.Clear(); foreach (var player in game.Players) { player.TurnEnded -= Player_TurnEnded; player.TurnStarted -= Player_TurnStarted; } } public override bool IsStackable => _setAsideCards.Count == 0; public override DisplayableCollection Stack() { var cc = new DisplayableCollection(_setAsideCards) { this }; return cc; } public override void FollowInstructions(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); base.FollowInstructions(player); _numberOfTriggers++; player.CardGained += Player_CardGained; if (_numberOfTriggers == 1) player.TurnEnded += Player_TurnEnded; } private void Player_CardGained(object sender, CardGainEventArgs e) { var player = sender as IPlayer; var key = Type.ToString(); var o = ModifiedBy; while (o != null) { key += $".{o.Type}"; o = o.ModifiedBy; } // This is not the card you are looking for if (e.Cancelled || e.IsLostTrackOf || e.Resolvers.ContainsKey(key)) return; // We've already set aside the maximum number of cards if (_numberOfTriggers == _setAsideCards.Count) return; e.Resolvers[key] = new CardGainResolver(player, this, "SetAsideCard", $"Set aside {e.Card} for next turn", Player_SetAside, false); } internal void Player_SetAside(IPlayer player, ref CardGainEventArgs e) { e.Cancelled = true; var card = player.RetrieveCardFrom(e.Location, e.Card); if (card != null) { _setAsideCards.Add(card); CanCleanUpPlayed.Remove(false); } e.IsLostTrackOf = true; player._Game.SendMessage(player, this, "SetAside", card); } private void Player_TurnEnded(object sender, TurnEndedEventArgs e) { for (var i = 0; i < _numberOfTriggers; i++) e.Player.CardGained -= Player_CardGained; e.Player.TurnEnded -= Player_TurnEnded; // Only trigger at start of turn if we have any set-aside cards if (_setAsideCards.Any()) e.Player.TurnStarted += Player_TurnStarted; _numberOfTriggers = 0; } public override void ResolveDuration(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); base.ResolveDuration(player); if (_activeIndex >= 0 && _activeIndex < _setAsideCards.Count) { var cardToRetrieve = _setAsideCards[_activeIndex]; player._Game.SendMessage(player, this, "Retrieve", cardToRetrieve); player.AddCardToHand(cardToRetrieve); _setAsideCards.RemoveAt(_activeIndex); CanCleanUpPlayed.Remove(false); //CanCleanUp = !_setAsideCards.Any(); } } public override void PerformEndgameCalculations(IPlayer player, PointsCollection collection) { Contract.Requires(player != null, "player cannot be null"); Contract.Requires(collection != null, "collection cannot be null"); // Add back any set aside cards that are still on this collection.AddRange(_setAsideCards); _setAsideCards.Clear(); player.TurnEnded -= Player_TurnEnded; player.TurnStarted -= Player_TurnStarted; } private void Player_TurnStarted(object sender, TurnStartedEventArgs e) { for (var i = 0; i < _setAsideCards.Count; i++) { var key = $"{UniqueId}_{_setAsideCards[i].UniqueId}"; if (!e.Resolvers.ContainsKey(key)) e.Resolvers[key] = new TurnStartedResolver( e.Player, this, $"Put {_setAsideCards[i]} into your hand", (IPlayer player, ref TurnStartedEventArgs eAction) => { _activeIndex = (int)eAction.Resolvers[key].Data; ResolveDuration(eAction.Player); if (!_setAsideCards.Any()) e.Player.TurnStarted -= Player_TurnStarted; eAction.HandledBy.Add(key); }, true, i); } } } }