using DominionBase.Currencies;
using DominionBase.Enums;
using DominionBase.Interfaces;
using DominionBase.Piles;
using DominionBase.Players;
using DominionBase.Properties;
using DominionBase.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Xml;
namespace DominionBase.Cards.Empires
{
public static class TypeClass
{
public static readonly Type DebtToken = typeof(DebtToken);
public static readonly Type Archive = typeof(Archive);
public static readonly Type BustlingVillage = typeof(BustlingVillage);
public static readonly Type Capital = typeof(Capital);
public static readonly Type Castles = typeof(Castles);
public static readonly Type CatapultRocks = typeof(CatapultRocks);
public static readonly Type Catapult = typeof(Catapult);
public static readonly Type ChariotRace = typeof(ChariotRace);
public static readonly Type Charm = typeof(Charm);
public static readonly Type CityQuarter = typeof(CityQuarter);
public static readonly Type Crown = typeof(Crown);
public static readonly Type CrumblingCastle = typeof(CrumblingCastle);
public static readonly Type Emporium = typeof(Emporium);
public static readonly Type EncampmentPlunder = typeof(EncampmentPlunder);
public static readonly Type Encampment = typeof(Encampment);
public static readonly Type Enchantress = typeof(Enchantress);
public static readonly Type Engineer = typeof(Engineer);
public static readonly Type FarmersMarket = typeof(FarmersMarket);
public static readonly Type Fortune = typeof(Fortune);
public static readonly Type Forum = typeof(Forum);
public static readonly Type GladiatorFortune = typeof(GladiatorFortune);
public static readonly Type Gladiator = typeof(Gladiator);
public static readonly Type GrandCastle = typeof(GrandCastle);
public static readonly Type Groundskeeper = typeof(Groundskeeper);
public static readonly Type HauntedCastle = typeof(HauntedCastle);
public static readonly Type HumbleCastle = typeof(HumbleCastle);
public static readonly Type KingsCastle = typeof(KingsCastle);
public static readonly Type Legionary = typeof(Legionary);
public static readonly Type OpulentCastle = typeof(OpulentCastle);
public static readonly Type Overlord = typeof(Overlord);
public static readonly Type PatricianEmporium = typeof(PatricianEmporium);
public static readonly Type Patrician = typeof(Patrician);
public static readonly Type Plunder = typeof(Plunder);
public static readonly Type Rocks = typeof(Rocks);
public static readonly Type RoyalBlacksmith = typeof(RoyalBlacksmith);
public static readonly Type Sacrifice = typeof(Sacrifice);
public static readonly Type SettlersBustlingVillage = typeof(SettlersBustlingVillage);
public static readonly Type Settlers = typeof(Settlers);
public static readonly Type SmallCastle = typeof(SmallCastle);
public static readonly Type SprawlingCastle = typeof(SprawlingCastle);
public static readonly Type Temple = typeof(Temple);
public static readonly Type Villa = typeof(Villa);
public static readonly Type WildHunt = typeof(WildHunt);
public static readonly Type Advance = typeof(Advance);
public static readonly Type Annex = typeof(Annex);
public static readonly Type Banquet = typeof(Banquet);
public static readonly Type Conquest = typeof(Conquest);
public static readonly Type Delve = typeof(Delve);
public static readonly Type Dominate = typeof(Dominate);
public static readonly Type Donate = typeof(Donate);
public static readonly Type Ritual = typeof(Ritual);
public static readonly Type SaltTheEarth = typeof(SaltTheEarth);
public static readonly Type Tax = typeof(Tax);
public static readonly Type Triumph = typeof(Triumph);
public static readonly Type Wedding = typeof(Wedding);
public static readonly Type Windfall = typeof(Windfall);
public static readonly Type Aqueduct = typeof(Aqueduct);
public static readonly Type Arena = typeof(Arena);
public static readonly Type BanditFort = typeof(BanditFort);
public static readonly Type Basilica = typeof(Basilica);
public static readonly Type Baths = typeof(Baths);
public static readonly Type Battlefield = typeof(Battlefield);
public static readonly Type Colonnade = typeof(Colonnade);
public static readonly Type DefiledShrine = typeof(DefiledShrine);
public static readonly Type Fountain = typeof(Fountain);
public static readonly Type Keep = typeof(Keep);
public static readonly Type Labyrinth = typeof(Labyrinth);
public static readonly Type MountainPass = typeof(MountainPass);
public static readonly Type Museum = typeof(Museum);
public static readonly Type ObeliskMarker = typeof(ObeliskMarker);
public static readonly Type Obelisk = typeof(Obelisk);
public static readonly Type Orchard = typeof(Orchard);
public static readonly Type Palace = typeof(Palace);
public static readonly Type Tomb = typeof(Tomb);
public static readonly Type Tower = typeof(Tower);
public static readonly Type TriumphalArch = typeof(TriumphalArch);
public static readonly Type Wall = typeof(Wall);
public static readonly Type WolfDen = typeof(WolfDen);
}
public class DebtToken : Token
{
public DebtToken()
: base("", "Debt token")
{
}
public override string Title => "Each token represents a coin that needs to be repaid";
public override bool IsPlayable => true;
public override IEnumerable PlayablePhases => new List { PhaseEnum.BuyTreasure, PhaseEnum.Buy, PhaseEnum.Cleanup };
///
/// Used internally by the base Card class -- Don't use this.
///
internal override void Play(IPlayer player, int count)
{
if (count <= 0)
throw new ArgumentOutOfRangeException(nameof(count), "Count must be positive.");
player.ReceiveBenefit(this, new CardBenefit { Currency = new Currency(-count) });
}
}
public class Archive : Card
{
private readonly List> _archivedCards = new List>();
private int _activeIndex = -1;
public Archive()
: base(Categories.Action | Categories.Duration, Source.Empires, Location.Kingdom, Traits.PlusAction | Traits.PlusCard | Traits.Cantrip | Traits.NetCardDraw)
{
BaseCost = new Cost(5);
Benefit.Actions = 1;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
_archivedCards.Clear();
CanCleanUpPlayed.Clear();
foreach (var player in game.Players)
{
player.TurnEnded -= Player_TurnEnded;
player.TurnStarted -= Player_TurnStarted;
}
}
public override IEnumerable LookThrough(Predicate predicate)
{
return _archivedCards.SelectMany(ic => ic.Where(c => predicate(c)));
}
public override bool IsStackable { get { return _archivedCards.Sum(l => l.Count) == 0; } }
public override DisplayableCollection Stack()
{
var cc = new DisplayableCollection();
_archivedCards.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)
{
_archivedCards.Clear();
CanCleanUpPlayed.Clear();
}
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var archivedCards = player.RetrieveCardsFrom(DeckLocation.Deck, c => true, 3);
// Show the cards to the owner
player.AddCardsInto(DeckLocation.Private, archivedCards);
player.RetrieveCardsFrom(DeckLocation.Private, archivedCards);
_activeIndex = _archivedCards.Count;
_archivedCards.Add(archivedCards);
CanCleanUpPlayed.Add(!archivedCards.Any());
player._Game.SendMessage(player, this, archivedCards.ToArray());
AddArchivedCardToHand(player);
player.TurnEnded += Player_TurnEnded;
}
private void Player_TurnEnded(object sender, TurnEndedEventArgs e)
{
e.Player.TurnEnded -= Player_TurnEnded;
e.Player.TurnStarted += Player_TurnStarted;
}
public override void ResolveDuration(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.ResolveDuration(player);
AddArchivedCardToHand(player);
}
private void AddArchivedCardToHand(IPlayer player)
{
if (_archivedCards.Count - 1 >= _activeIndex)
{
var archivePile = _archivedCards[_activeIndex];
var choice = new Choice(Resource.CardToAddToHand, this, archivePile, ChoiceOutcome.Select, player);
var result = player.MakeChoice(choice);
if (result.Cards.Any())
{
archivePile.Remove(result.Cards[0]);
player.AddCardToHand(result.Cards[0]);
}
CanCleanUpPlayed[_activeIndex] = !archivePile.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 Archived cards that are still on this
foreach (var cards in _archivedCards)
collection.AddRange(cards);
_archivedCards.Clear();
CanCleanUpPlayed.Clear();
}
private void Player_TurnStarted(object sender, TurnStartedEventArgs e)
{
for (var i = 0; i < _archivedCards.Count; i++)
{
var key = $"{UniqueId}_{i}";
// Skip empty archived piles (they'll be eventually removed when Archive is removed from play)
if (_archivedCards[i].Count == 0)
continue;
if (!e.HandledBy.Contains(key) && !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);
}
}
}
public class BustlingVillage : Card
{
public BustlingVillage()
: base(Categories.Action, Source.Empires, Location.Special, Traits.PlusCard | Traits.PlusAction | Traits.PlusMultipleActions | Traits.Cantrip)
{
BaseCost = new Cost(5);
Benefit.Cards = 1;
Benefit.Actions = 3;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var discardPileCards = player.DiscardPile.LookThrough(c => true);
player._Game.SendMessage(player, this, "LookThroughDiscard", discardPileCards.ToArray());
var settlersCount = discardPileCards.Count(c => c is Settlers);
if (settlersCount > 0)
{
var choice = Choice.CreateYesNoChoice(Resource.ShouldRevealSettlers, this, player);
var result = player.MakeChoice(choice);
if (result.Options[0] == Resource.Yes)
{
player.AddCardsInto(DeckLocation.Revealed, player.RetrieveCardsFrom(DeckLocation.Discard, TypeClass.Settlers, 1));
player.AddCardsToHand(DeckLocation.Revealed);
}
}
}
}
public class Capital : Card
{
private CardsDiscardingEventHandler _cardsDiscardingEventHandler;
public Capital()
: base(Categories.Treasure, Source.Empires, Location.Kingdom, Traits.Component | Traits.PlusCoin | Traits.PlusBuy | Traits.Debts)
{
BaseCost = new Cost(5);
Benefit.Currency.Coin.Value = 6;
Benefit.Buys = 1;
OwnerChanged += new OwnerChangedEventHandler(Capital_OwnerChanged);
}
public override void TearDown(IGame game)
{
Capital_OwnerChanged(this, new OwnerChangedEventArgs(Owner, null));
base.TearDown(game);
OwnerChanged -= new OwnerChangedEventHandler(Capital_OwnerChanged);
}
private void Capital_OwnerChanged(object sender, OwnerChangedEventArgs e)
{
if (_cardsDiscardingEventHandler != null && e.OldOwner != null)
{
e.OldOwner.CardsDiscarding -= _cardsDiscardingEventHandler;
_cardsDiscardingEventHandler = null;
}
if (e.NewOwner != null)
{
_cardsDiscardingEventHandler = new CardsDiscardingEventHandler(Player_CardsDiscarding);
e.NewOwner.CardsDiscarding += _cardsDiscardingEventHandler;
}
}
private void Player_CardsDiscarding(object sender, CardsDiscardEventArgs e)
{
if (!e.Cards.Contains(PhysicalCard) || e.GetResolver(TypeClass.Capital) != null || e.HandledBy.Contains(this) ||
(e.FromLocation != DeckLocation.InPlay && e.FromLocation != DeckLocation.SetAside && e.FromLocation != DeckLocation.InPlayAndSetAside))
return;
e.AddResolver(TypeClass.Capital, new CardsDiscardResolver(sender as IPlayer, this, "Take 6", Player_Action, true));
}
internal void Player_Action(IPlayer player, ref CardsDiscardEventArgs e)
{
player.AddTokens(new TokenCollection() { new DebtToken(), new DebtToken(), new DebtToken(), new DebtToken(), new DebtToken(), new DebtToken() });
e.HandledBy.Add(this);
var debtCount = Math.Min(player.TokenPiles[TypeClass.DebtToken].Count, player.Currency.Coin.Value);
if (debtCount > 0)
{
var options = new List();
for (var i = 0; i <= debtCount; i++)
options.Add(i.ToString(CultureInfo.InvariantCulture));
var choice = new Choice("How many Debt tokens would you like to pay off?", this, this, options, player);
var result = player.MakeChoice(choice);
var number = int.Parse(result.Options[0], CultureInfo.InvariantCulture);
player.PlayTokens(player._Game, TypeClass.DebtToken, number);
}
}
public override bool Finalize(IGame game, ISupply supply)
{
Contract.Requires(game != null, "game cannot be null");
if (base.Finalize(game, supply))
return true;
foreach (var player in game.Players)
if (!player.TokenPiles.ContainsKey(TypeClass.DebtToken))
player.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
return false;
}
}
public abstract class Castle : Card
{
internal Castle(Categories category, Traits group)
: base(category | Categories.Victory | Categories.Castle, Source.Empires, Location.Special, group)
{
}
}
public class Castles : Card
{
public Castles()
: base(Categories.Victory | Categories.Castle, Source.Empires, Location.Kingdom, Traits.Randomizer | Traits.VariableVPs)
{
BaseCost = new Cost(3);
}
public override void SetupSupply(IGame game, ISupply supply)
{
Contract.Requires(game != null, "game cannot be null");
Contract.Requires(supply != null, "supply cannot be null");
base.SetupSupply(game, supply);
supply.Empty();
var cards = new CardCollection { new KingsCastle() };
if (game.Players.Count > 2)
cards.Add(new KingsCastle());
cards.Add(new GrandCastle());
cards.Add(new SprawlingCastle());
cards.Add(new OpulentCastle());
if (game.Players.Count > 2)
cards.Add(new OpulentCastle());
cards.Add(new HauntedCastle());
cards.Add(new SmallCastle());
if (game.Players.Count > 2)
cards.Add(new SmallCastle());
cards.Add(new CrumblingCastle());
cards.Add(new HumbleCastle());
if (game.Players.Count > 2)
cards.Add(new HumbleCastle());
supply.AddTo(cards);
}
}
public class CatapultRocks : Card
{
public CatapultRocks()
: base(Categories.Action | Categories.Attack, Source.Empires, Location.Kingdom, Traits.PlusCoin | Traits.Gainer | Traits.ReactToGain | Traits.ReactToTrashing | Traits.Trasher | Traits.RemoveCurses | Traits.Terminal | Traits.PlusCurses | Traits.Discard | Traits.AffectOthers | Traits.DeckReduction | Traits.SplitPile | Traits.RemoveFromHand)
{
BaseCost = new Cost(3);
}
public override void SetupSupply(IGame game, ISupply supply)
{
Contract.Requires(supply != null, "supply cannot be null");
base.SetupSupply(game, supply);
supply.Empty();
var cards = new CardCollection
{
new Rocks(),
new Rocks(),
new Rocks(),
new Rocks(),
new Rocks(),
new Catapult(),
new Catapult(),
new Catapult(),
new Catapult(),
new Catapult()
};
supply.AddTo(cards);
}
}
public class Catapult : Card
{
public Catapult()
: base(Categories.Action | Categories.Attack, Source.Empires, Location.Special, Traits.PlusCoin | Traits.Trasher | Traits.RemoveCurses | Traits.Terminal | Traits.PlusCurses | Traits.Discard | Traits.AffectOthers | Traits.DeckReduction | Traits.RemoveFromHand)
{
BaseCost = new Cost(3);
Benefit.Currency.Coin.Value = 1;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var choiceTrash = new Choice(Resource.ChooseACardToTrash, this, player.Hand, ChoiceOutcome.Trash, player);
var resultTrash = player.MakeChoice(choiceTrash);
player.Trash(this, player.RetrieveCardsFrom(DeckLocation.Hand, resultTrash.Cards));
if (resultTrash.Cards.Any())
{
var trashedCardCost = player._Game.ComputeCost(resultTrash.Cards[0]);
if (trashedCardCost >= new Coin(3))
{
var enumerator = player._Game.GetPlayersStartingWithEnumerator(player);
enumerator.MoveNext();
while (enumerator.MoveNext())
{
var attackee = enumerator.Current;
// Skip if the attack is blocked (Moat, Lighthouse, etc.)
if (IsAttackBlocked[attackee])
continue;
attackee.Gain(player._Game.Table.Curse, this);
}
}
if (resultTrash.Cards[0].Category.HasFlag(Categories.Treasure))
{
var enumerator = player._Game.GetPlayersStartingWithEnumerator(player);
enumerator.MoveNext();
while (enumerator.MoveNext())
{
var attackee = enumerator.Current;
// Skip if the attack is blocked (Moat, Lighthouse, etc.)
if (IsAttackBlocked[attackee])
continue;
var choice = new Choice(Resource.DiscardDownTo3Cards, this, attackee.Hand, ChoiceOutcome.Discard, attackee, minimum: attackee.Hand.Count - 3, maximum: attackee.Hand.Count - 3);
var result = attackee.MakeChoice(choice);
attackee.Discard(DeckLocation.Hand, result.Cards);
}
}
}
}
}
public class ChariotRace : Card
{
public ChariotRace()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.PlusAction | Traits.PlusCard | Traits.ConditionalBenefit | Traits.Component | Traits.VPTokens | Traits.Cantrip)
{
BaseCost = new Cost(3);
Benefit.Actions = 1;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var card = player.Draw(DeckLocation.Revealed);
if (card != null)
player.AddCardToHand(player.RetrieveCardFrom(DeckLocation.Revealed, card));
// Get the player to my left
var playerToLeft = player._Game.GetPlayerFromIndex(player, 1);
playerToLeft.Draw(DeckLocation.Revealed);
var ptlCard = playerToLeft.Revealed.FirstOrDefault();
playerToLeft.AddCardsToDeck(playerToLeft.RetrieveCardsFrom(DeckLocation.Revealed), DeckPosition.Top);
if (card != null && ptlCard != null && player._Game.ComputeCost(card) > playerToLeft._Game.ComputeCost(ptlCard))
player.ReceiveBenefit(this, new CardBenefit { Currency = new Currency(1), VictoryPoints = 1 });
}
}
public class Charm : Card
{
public Charm()
: base(Categories.Treasure, Source.Empires, Location.Kingdom, Traits.ConditionalBenefit | Traits.PlusCoin | Traits.PlusBuy | Traits.Gainer | Traits.ReactToBuy)
{
BaseCost = new Cost(5);
Benefit.Currency.Coin.IsVariable = true;
}
public override void TearDown(IGame game)
{
if (Owner != null)
{
Owner.CardBought -= Player_CardBought;
Owner.CardBuyFinished -= Player_ResetTrigger;
}
base.TearDown(game);
}
private void Player_CardBought(object sender, CardBuyEventArgs e)
{
var player = sender as IPlayer;
// Already been cancelled -- don't need to process this one
if (e.Cancelled || e.Resolvers.ContainsKey(TypeClass.Charm) || !e.Card.Type.IsSubclassOf(typeof(Card)) || e.HandledBy.Contains(this))
return;
var gainCardCost = e.Game.ComputeCost(e.Card);
var gainCardName = e.Card.Name;
var gainableSupplies = new SupplyCollection(player._Game.Table.TableEntities.FindAll(
supply =>
supply.CanGain()
&& supply.CurrentCost == gainCardCost
&& supply.TopCard.Name != gainCardName
));
if (gainableSupplies.Any())
e.Resolvers[TypeClass.Charm] = new CardBuyResolver(Owner, this, "Gain another card", Player_GainCard, false);
}
internal void Player_GainCard(IPlayer player, ref CardBuyEventArgs e)
{
var gainCardCost = e.Game.ComputeCost(e.Card);
var gainCardName = e.Card.Name;
var gainableSupplies = new SupplyCollection(player._Game.Table.TableEntities.FindAll(
supply =>
supply.CanGain()
&& supply.CurrentCost == gainCardCost
&& supply.TopCard.Name != gainCardName
));
var choice = new Choice($"Gain a differently-named card costing {Name}", this, gainableSupplies, ChoiceOutcome.Gain, player, false);
var result = player.MakeChoice(choice);
if (result.Supply != null)
player.Gain(result.Supply, this);
e.HandledBy.Add(this);
}
internal void Player_ResetTrigger(object sender, CardBuyEventArgs e)
{
var player = sender as IPlayer;
player.CardBought -= Player_CardBought;
player.CardBuyFinished -= Player_ResetTrigger;
}
public override void RemovedFrom(DeckLocation location, IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.RemovedFrom(location, player);
player.CardBought -= Player_CardBought;
player.CardBuyFinished -= Player_ResetTrigger;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var choice = new Choice(Resource.ChooseOne, this, this, new List { "+1 Buy and +2", "The next time you buy a card this turn, you may also gain a differently named card with the same cost" }, player);
var result = player.MakeChoice(choice);
if (result.Options.Contains("+1 Buy and +2"))
player.ReceiveBenefit(this, new CardBenefit { Buys = 1, Currency = new Currency(2) });
if (result.Options.Contains("The next time you buy a card this turn, you may also gain a differently named card with the same cost"))
{
player.CardBought += Player_CardBought;
player.CardBuyFinished += Player_ResetTrigger;
}
}
}
public class CityQuarter : Card
{
public CityQuarter()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.Component | Traits.PlusAction | Traits.PlusMultipleActions | Traits.PlusCard | Traits.ConditionalBenefit | Traits.Debts)
{
BaseCost = new Cost(debtCost: 8);
Benefit.Actions = 2;
}
public override bool Finalize(IGame game, ISupply supply)
{
Contract.Requires(game != null, "game cannot be null");
if (base.Finalize(game, supply))
return true;
foreach (var player in game.Players)
if (!player.TokenPiles.ContainsKey(TypeClass.DebtToken))
player.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
return false;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
player.ReturnHand(player.RevealHand());
player.ReceiveBenefit(this, new CardBenefit { Cards = player.Hand[Categories.Action].Count });
}
}
public class Crown : Card
{
private readonly List CardsPlayed = new List();
public Crown()
: base(Categories.Action | Categories.Treasure, Source.Empires, Location.Kingdom, Traits.Multiplier)
{
BaseCost = new Cost(5);
}
public override bool CanCleanUp
{
// If Crown played a Duration card but the Duration card only did something once (e.g. Tactician), then Crown can be cleaned up
get => CardsPlayed.All(cp => cp.CanCleanUpPlayed.Count(cup => !cup) < 2) && base.CanCleanUp;
}
public override void AddedTo(DeckLocation location, IPlayer player)
{
base.AddedTo(location, player);
CardsPlayed.Clear();
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
if (player.Phase == PhaseEnum.Action || player.Phase == PhaseEnum.ActionTreasure)
{
var choice = new Choice(Resource.ChooseActionPlay2x, this, player.Hand[Categories.Action], ChoiceOutcome.Select, player, minimum: 0);
var result = player.MakeChoice(choice);
if (result.Cards.Any())
{
var cardToPlayTwice = result.Cards[0];
CardsPlayed.Add(cardToPlayTwice);
cardToPlayTwice.ModifiedBy = this;
player.Actions++;
var previousPlayerMode = player.PutCardIntoPlay(cardToPlayTwice);
var logicalCard = cardToPlayTwice.LogicalCard;
player.PlayCard(cardToPlayTwice.LogicalCard, previousPlayerMode);
player.Actions++;
previousPlayerMode = player.PutCardIntoPlay(cardToPlayTwice, Resource.AgainFromCard.Replace("{card}", ToString()));
player.PlayCard(logicalCard, previousPlayerMode);
}
else
player.PlayNothing();
}
else if (player.Phase == PhaseEnum.Buy || player.Phase == PhaseEnum.BuyTreasure)
{
var choice = new Choice(Resource.ChooseTreasurePlay2x, this, player.Hand[Categories.Treasure], ChoiceOutcome.Select, player, minimum: 0);
var result = player.MakeChoice(choice);
if (result.Cards.Any())
{
var card = result.Cards[0];
player.PlayCardInternal(card);
player.PlayCardInternal(card, modifier: Resource.AgainFromCard.Replace("{card}", ToString()));
}
else
player.PlayNothing();
}
}
protected override void ModifyDuration(IPlayer player, Card card)
{
base.ModifyDuration(player, card);
base.ModifyDuration(player, card);
}
}
public class CrumblingCastle : Castle
{
public CrumblingCastle()
: base(Categories.Unknown, Traits.ReactToGain | Traits.ReactToTrashing | Traits.Gainer | Traits.Component | Traits.VPTokens)
{
BaseCost = new Cost(4);
VictoryPoints = 1;
}
public override void SetupCard(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.SetupCard(game);
foreach (var player in game.Players)
{
player.CardGained += Player_CardGained;
player.Trashed += Player_Trashed;
}
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
{
player.CardGained -= Player_CardGained;
player.Trashed -= Player_Trashed;
}
}
private void Player_Trashed(object sender, TrashEventArgs e)
{
// Already being processed or been handled -- don't need to process this one
if (!e.TrashedCards.Contains(this) || e.Resolvers.ContainsKey(TypeClass.CrumblingCastle) || e.HandledBy.Contains(this))
return;
if (e.TrashedCards.Contains(PhysicalCard))
e.Resolvers[TypeClass.CrumblingCastle] = new TrashResolver(e.CurrentPlayer, this, Resource.Gain1VPAndSilver, Player_CardTrashed, true);
}
internal void Player_CardTrashed(IPlayer player, ref TrashEventArgs e)
{
GainTrashBenefit(player);
e.HandledBy.Add(this);
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key = TypeClass.CrumblingCastle.ToString();
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(key) || e.HandledBy.Contains(this))
return;
e.Resolvers[key] = new CardGainResolver(player, this, "Gain", Resource.Gain1VPAndSilver, Player_GainCrumblingCastle, true);
}
internal void Player_GainCrumblingCastle(IPlayer player, ref Players.CardGainEventArgs e)
{
GainTrashBenefit(player);
e.HandledBy.Add(this);
}
private void GainTrashBenefit(IPlayer player)
{
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = 1 });
player.Gain(player._Game.Table.Silver, this);
}
}
public class Emporium : Card
{
public Emporium()
: base(Categories.Action, Source.Empires, Location.Special, Traits.PlusCard | Traits.PlusAction | Traits.PlusCoin | Traits.Component | Traits.VPTokens | Traits.ConditionalBenefit | Traits.Cantrip)
{
BaseCost = new Cost(5);
Benefit.Cards = 1;
Benefit.Actions = 1;
Benefit.Currency.Coin.Value = 1;
}
public override void SetupCard(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.SetupCard(game);
foreach (var player in game.Players)
player.CardGained += Player_CardGained;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
player.CardGained -= Player_CardGained;
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var key = TypeClass.Emporium.ToString();
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(key) || e.HandledBy.Contains(this))
return;
var player = sender as IPlayer;
if (player.InPlayAndSetAside[Categories.Action].Count >= 5)
e.Resolvers[key] = new CardGainResolver(player, this, "GainVPs", "+2", Player_GainEmporium, true);
}
internal void Player_GainEmporium(IPlayer player, ref Players.CardGainEventArgs e)
{
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = 2 });
e.HandledBy.Add(this);
}
}
public class EncampmentPlunder : Card
{
public EncampmentPlunder()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.Randomizer | Traits.PlusCoin | Traits.VPTokens | Traits.Component | Traits.PlusCard | Traits.PlusAction | Traits.PlusMultipleActions | Traits.Cantrip | Traits.SplitPile)
{
BaseCost = new Cost(2);
}
public override void SetupSupply(IGame game, ISupply supply)
{
Contract.Requires(supply != null, "supply cannot be null");
base.SetupSupply(game, supply);
supply.Empty();
var cards = new CardCollection
{
new Plunder(),
new Plunder(),
new Plunder(),
new Plunder(),
new Plunder(),
new Encampment(),
new Encampment(),
new Encampment(),
new Encampment(),
new Encampment()
};
supply.AddTo(cards);
}
}
public class Encampment : Card
{
private bool _returnToSupply;
public Encampment()
: base(Categories.Action, Source.Empires, Location.Special, Traits.PlusCard | Traits.PlusAction | Traits.PlusMultipleActions | Traits.Cantrip)
{
BaseCost = new Cost(2);
Benefit.Cards = 2;
Benefit.Actions = 2;
}
public override void AddedTo(DeckLocation location, IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.AddedTo(location, player);
switch (location)
{
case DeckLocation.SetAside:
player.CleaningUp += Player_CleaningUp;
_returnToSupply = true;
break;
}
}
private void Player_CleaningUp(object sender, CleaningUpEventArgs cuea)
{
if (!cuea.CurrentPlayer.SetAside.Contains(PhysicalCard) || cuea.Resolvers.ContainsKey(TypeClass.Encampment))
return;
if (_returnToSupply)
cuea.Resolvers[TypeClass.Encampment] = new CleaningUpResolver(
this,
$"Return {PhysicalCard} to the Supply",
(IPlayer player, ref CleaningUpEventArgs e) =>
{
e.CardsMovements.Remove(e.CardsMovements.Find(cm => cm.Card == PhysicalCard));
var cardToReturn = e.CurrentPlayer.RetrieveCardFrom(DeckLocation.SetAside, PhysicalCard);
player.Lose(cardToReturn);
var supply = (ISupply)player._Game.Table[cardToReturn];
supply.AddTo(cardToReturn);
player._Game.SendMessage(player, this, "Return", supply);
},
true);
}
public override void RemovedFrom(DeckLocation location, IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.RemovedFrom(location, player);
player.CleaningUp -= Player_CleaningUp;
_returnToSupply = false;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var goldAndPlunders = player.Hand[c => c is Universal.Gold || c.Type == TypeClass.Plunder];
var choice = new Choice("You may reveal a Gold or Plunder card. Otherwise, set this aside and return it to the Supply at the start of Clean-up.", this, goldAndPlunders, ChoiceOutcome.Select, player, minimum: 0);
var result = player.MakeChoice(choice);
if (result.Cards.Count == 0)
{
if (player.InPlay.Contains(PhysicalCard))
{
player.MoveInPlayToSetAside(c => c == PhysicalCard);
player._Game.SendMessage(player, this, "SetAside", PhysicalCard);
}
}
else
{
player.AddCardInto(DeckLocation.Revealed, player.RetrieveCardFrom(DeckLocation.Hand, result.Cards.First()));
player.AddCardInto(DeckLocation.Hand, player.RetrieveCardFrom(DeckLocation.Revealed, result.Cards.First()));
}
}
}
public class Enchantress : Card
{
public Enchantress()
: base(Categories.Action | Categories.Attack | Categories.Duration, Source.Empires, Location.Kingdom, Traits.PlusCard | Traits.AffectOthers | Traits.Terminal | Traits.NetCardDraw)
{
BaseCost = new Cost(3);
DurationBenefit.Cards = 2;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
{
player.CardFollowingInstructions -= Attackee_CardFollowingInstructions;
player.TurnEnded -= Player_TurnEnded;
player.TurnStarted -= Player_TurnStarted;
}
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var enumerator = player._Game.GetPlayersStartingWithEnumerator(player);
enumerator.MoveNext();
while (enumerator.MoveNext())
{
var attackee = enumerator.Current;
// Skip if the attack is blocked (Moat, Lighthouse, etc.)
if (IsAttackBlocked[attackee])
continue;
attackee.CardFollowingInstructions -= Attackee_CardFollowingInstructions;
attackee.CardFollowingInstructions += Attackee_CardFollowingInstructions;
}
CanCleanUpPlayed.Add(false);
player.TurnEnded += Player_TurnEnded;
}
private void Player_TurnEnded(object sender, TurnEndedEventArgs e)
{
e.Player.TurnEnded -= Player_TurnEnded;
e.Player.TurnStarted += Player_TurnStarted;
}
private void Player_TurnStarted(object sender, TurnStartedEventArgs e)
{
var key = ToString();
if (!e.Resolvers.ContainsKey(key))
e.Resolvers[key] = new TurnStartedResolver(e.Player, this, Resource.ResolveCard.Replace("{card}", PhysicalCard.ToString()), Player_Action, true);
}
internal void Player_Action(IPlayer player, ref TurnStartedEventArgs e)
{
ResolveDuration(e.Player);
CanCleanUpPlayed.Remove(false);
foreach (var attackee in player._Game.Players)
attackee.CardFollowingInstructions -= Attackee_CardFollowingInstructions;
e.Player.TurnStarted -= Player_TurnStarted;
}
private void Attackee_CardFollowingInstructions(object sender, CardFollowingInstructionsEventArgs e)
{
var player = (IPlayer)sender;
if (e.Cancelled
|| e.HandledBy.Contains(this)
|| !e.Card.Category.HasFlag(Categories.Action)
|| player._Game.ActivePlayer != player
|| player.CurrentTurn.CardsPlayed.Count != 1
|| player.CurrentTurn.CardsPlayed.FirstOrDefault(c => c.Category.HasFlag(Categories.Action)) != e.Card
)
return;
e.Resolvers[TypeClass.Enchantress] = new CardFollowingInstructionsResolver(
this,
Resource.ResolveViaCard.Replace("{card}", Name),
(IPlayer playerAction, ref CardFollowingInstructionsEventArgs eAction) =>
{
eAction.Cancelled = true;
playerAction.ReceiveBenefit(this, new CardBenefit { Cards = 1, Actions = 1 });
eAction.HandledBy.Add(this);
},
true);
}
public override void ResolveDuration(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.ResolveDuration(player);
var enumerator = player._Game.GetPlayersStartingWithEnumerator(player);
enumerator.MoveNext();
while (enumerator.MoveNext())
enumerator.Current.CardFollowingInstructions -= Attackee_CardFollowingInstructions;
}
}
public class Engineer : Card
{
public Engineer()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.Gainer | Traits.Terminal | Traits.Trasher | Traits.DeckReduction | Traits.Component | Traits.Debts)
{
BaseCost = new Cost(debtCost: 4);
}
public override bool Finalize(IGame game, ISupply supply)
{
Contract.Requires(game != null, "game cannot be null");
if (base.Finalize(game, supply))
return true;
foreach (var player in game.Players)
if (!player.TokenPiles.ContainsKey(TypeClass.DebtToken))
player.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
return false;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var gainableSupplies = new SupplyCollection(player._Game.Table.TableEntities.FindAll(supply => supply.CanGain() && supply.CurrentCost <= new Coin(4)));
var choice = new Choice(Resource.GainUpTo4, this, gainableSupplies, ChoiceOutcome.Gain, player, false);
var result = player.MakeChoice(choice);
if (result.Supply != null)
player.Gain(result.Supply, this);
if (player.InPlay.Contains(PhysicalCard))
{
choice = Choice.CreateYesNoChoice("Do you want to trash this card to gain a card costing up to 4?", this, player);
result = player.MakeChoice(choice);
if (result.Options[0] == Resource.Yes)
{
player.Trash(this, player.RetrieveCardFrom(DeckLocation.InPlay, PhysicalCard));
gainableSupplies = new SupplyCollection(player._Game.Table.TableEntities.FindAll(supply => supply.CanGain() && supply.CurrentCost <= new Coin(4)));
choice = new Choice(Resource.GainUpTo4, this, gainableSupplies, ChoiceOutcome.Gain, player, false);
result = player.MakeChoice(choice);
if (result.Supply != null)
player.Gain(result.Supply, this);
}
}
}
}
public class FarmersMarket : Card
{
public FarmersMarket()
: base(Categories.Action | Categories.Gathering, Source.Empires, Location.Kingdom, Traits.PlusBuy | Traits.Terminal | Traits.Trasher | Traits.DeckReduction | Traits.ConditionalBenefit | Traits.Component | Traits.VPTokens)
{
BaseCost = new Cost(3);
Benefit.Buys = 1;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var fmSupply = (ISupply)player._Game.Table.TableEntities[TypeClass.FarmersMarket];
var victoryTokens = fmSupply.Tokens.OfType().ToList();
if (victoryTokens.Count >= 4)
{
victoryTokens.ForEach(t => fmSupply.RemoveToken(t));
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = victoryTokens.Count });
player.Trash(this, player.RetrieveCardFrom(DeckLocation.InPlay, PhysicalCard));
}
else
{
fmSupply.AddToken(new Prosperity.VictoryToken());
player.ReceiveBenefit(this, new CardBenefit { Currency = new Currency(victoryTokens.Count + 1) });
}
}
}
public class Fortune : Card
{
public Fortune()
: base(Categories.Treasure, Source.Empires, Location.Special, Traits.PlusBuy | Traits.PlusCoin | Traits.ConditionalBenefit | Traits.Component | Traits.Debts | Traits.Gainer)
{
BaseCost = new Cost(coinCost: 8, debtCost: 8);
Benefit.Buys = 1;
Benefit.Currency.Coin.SpecialDisplay = "x2";
}
public override void SetupCard(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.SetupCard(game);
foreach (var player in game.Players)
player.CardGained += Player_CardGained;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
player.CardGained -= Player_CardGained;
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key = TypeClass.Fortune.ToString();
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(key) || e.HandledBy.Contains(this))
return;
e.Resolvers[key] = new CardGainResolver(player, this, "GainGold", "Gain Gold per Gladiator", Player_GainFortune, true);
}
internal void Player_GainFortune(IPlayer player, ref Players.CardGainEventArgs e)
{
foreach (var gladiator in player.InPlayAndSetAside[TypeClass.Gladiator])
player.Gain(player._Game.Table.Gold, this);
e.HandledBy.Add(this);
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
if (player.CurrentTurn.CardsResolved.Count(c => c.LogicalCard.Type == TypeClass.Fortune) == 1)
player.ReceiveBenefit(this, new CardBenefit { Currency = new Currency(player.Currency.Coin) });
}
}
public class Forum : Card
{
private readonly Dictionary _cardBoughtHandlers = new Dictionary();
public Forum()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.PlusCard | Traits.PlusAction | Traits.Discard | Traits.PlusBuy | Traits.ConditionalBenefit | Traits.Cantrip | Traits.ReactToBuy | Traits.NetCardDraw)
{
BaseCost = new Cost(5);
Benefit.Cards = 3;
Benefit.Actions = 1;
}
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _cardBoughtHandlers.Keys)
playerLoop.CardBought -= _cardBoughtHandlers[playerLoop];
_cardBoughtHandlers.Clear();
}
public override void AddedToSupply(IGame game, ISupply supply)
{
Contract.Requires(game != null, "game cannot be null");
base.AddedToSupply(game, supply);
ResetTriggers(game);
}
private void ResetTriggers(IGame game)
{
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_cardBoughtHandlers[enumPlayers.Current] = new CardBoughtEventHandler(Player_CardBought);
enumPlayers.Current.CardBought += _cardBoughtHandlers[enumPlayers.Current];
}
}
private void Player_CardBought(object sender, CardBuyEventArgs e)
{
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(TypeClass.Forum) || !e.Card.Type.IsSubclassOf(typeof(Card)))
return;
e.Resolvers[TypeClass.Forum] = new CardBuyResolver(Owner, this, "+1 Buy", Player_BuyForum, true);
}
internal void Player_BuyForum(IPlayer player, ref CardBuyEventArgs e)
{
player.ReceiveBenefit(this, new CardBenefit { Buys = 1 });
e.HandledBy.Add(TypeClass.Forum);
// Clear out the Event Triggers -- this only happens when its Bought, so we don't care any more
foreach (var playerLoop in _cardBoughtHandlers.Keys)
playerLoop.CardBought -= _cardBoughtHandlers[playerLoop];
_cardBoughtHandlers.Clear();
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var choice = new Choice(Resource.Discard2Cards, this, player.Hand, ChoiceOutcome.Discard, player, minimum: 2, maximum: 2);
var result = player.MakeChoice(choice);
player.Discard(DeckLocation.Hand, result.Cards);
}
}
public class GladiatorFortune : Card
{
public GladiatorFortune()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.Randomizer | Traits.PlusBuy | Traits.PlusCoin | Traits.ConditionalBenefit | Traits.Component | Traits.Debts | Traits.Gainer | Traits.Trasher | Traits.AffectOthers | Traits.Terminal | Traits.SplitPile)
{
BaseCost = new Cost(3);
}
public override void SetupSupply(IGame game, ISupply supply)
{
Contract.Requires(supply != null, "supply cannot be null");
base.SetupSupply(game, supply);
supply.Empty();
var cards = new CardCollection
{
new Fortune(),
new Fortune(),
new Fortune(),
new Fortune(),
new Fortune(),
new Gladiator(),
new Gladiator(),
new Gladiator(),
new Gladiator(),
new Gladiator()
};
supply.AddTo(cards);
}
public override bool Finalize(IGame game, ISupply supply)
{
Contract.Requires(game != null, "game cannot be null");
if (base.Finalize(game, supply))
return true;
foreach (var player in game.Players)
if (!player.TokenPiles.ContainsKey(TypeClass.DebtToken))
player.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
return false;
}
}
public class Gladiator : Card
{
public Gladiator()
: base(Categories.Action, Source.Empires, Location.Special, Traits.PlusCoin | Traits.Trasher | Traits.AffectOthers | Traits.ConditionalBenefit | Traits.Terminal)
{
BaseCost = new Cost(3);
Benefit.Currency.Coin.Value = 2;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var choiceCard = new Choice(Resource.RevealCardFromHand, this, player.Hand, ChoiceOutcome.Select, player);
var resultCard = player.MakeChoice(choiceCard);
var playerToLeftRevealedCopy = false;
if (resultCard.Cards.Any())
{
var revealedCard = resultCard.Cards[0];
var shownCard = player.RetrieveCardFrom(DeckLocation.Hand, revealedCard.Type);
player.AddCardInto(DeckLocation.Revealed, shownCard);
player.AddCardInto(DeckLocation.Hand, player.RetrieveCardFrom(DeckLocation.Revealed, shownCard));
// Get the player to my left
var playerToLeft = player._Game.GetPlayerFromIndex(player, 1);
if (playerToLeft.Hand[revealedCard.Type].Any())
{
var revealCopy = Choice.CreateYesNoChoice($"Reveal a copy of {revealedCard}?", this, player);
var revealCopyResult = playerToLeft.MakeChoice(revealCopy);
if (revealCopyResult.Options[0] == Resource.Yes)
{
playerToLeftRevealedCopy = true;
var shownCardPtl = playerToLeft.RetrieveCardFrom(DeckLocation.Hand, revealedCard.Type);
playerToLeft.AddCardInto(DeckLocation.Revealed, shownCardPtl);
playerToLeft.AddCardInto(DeckLocation.Hand, playerToLeft.RetrieveCardFrom(DeckLocation.Revealed, shownCardPtl));
}
}
}
if (!playerToLeftRevealedCopy)
{
player.ReceiveBenefit(this, new CardBenefit { Currency = new Currency(1) });
var gladiatorFortuneSupply = (ISupply)player._Game.Table[TypeClass.GladiatorFortune];
if (gladiatorFortuneSupply.TopCard != null && gladiatorFortuneSupply.TopCard.Type == TypeClass.Gladiator)
player.Trash(this, gladiatorFortuneSupply);
}
}
}
public class GrandCastle : Castle
{
public GrandCastle()
: base(Categories.Unknown, Traits.ReactToGain | Traits.Component | Traits.VPTokens)
{
BaseCost = new Cost(9);
VictoryPoints = 5;
}
public override void SetupCard(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.SetupCard(game);
foreach (var player in game.Players)
player.CardGained += Player_CardGained;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
player.CardGained -= Player_CardGained;
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key = TypeClass.GrandCastle.ToString();
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(key) || e.HandledBy.Contains(this))
return;
e.Resolvers[key] = new CardGainResolver(player, this, "RevealHand", "Reveal your hand", Player_GainGrandCastle, true);
}
internal void Player_GainGrandCastle(IPlayer player, ref Players.CardGainEventArgs e)
{
player.ReturnHand(player.RevealHand());
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = player.Hand[Categories.Victory].Count + player.InPlayAndSetAside[Categories.Victory].Count });
e.HandledBy.Add(this);
}
}
public class Groundskeeper : Card
{
private CardGainedEventHandler _cardGainedEventHandler;
public Groundskeeper()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.PlusCard | Traits.PlusAction | Traits.Component | Traits.VPTokens)
{
BaseCost = new Cost(5);
Benefit.Cards = 1;
Benefit.Actions = 1;
}
public override void AddedTo(DeckLocation location, IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.AddedTo(location, player);
if (location == DeckLocation.InPlay)
{
if (_cardGainedEventHandler != null)
player.CardGained -= _cardGainedEventHandler;
_cardGainedEventHandler = new CardGainedEventHandler(Player_CardGained);
player.CardGained += _cardGainedEventHandler;
}
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
// Already been cancelled or processed -- don't need to process this one
if (e.Cancelled || e.HandledBy.Contains(this))
return;
e.HandledBy.Add(this);
var player = (IPlayer)sender;
if (e.Card.Category.HasFlag(Categories.Victory))
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = 1 });
}
public override void RemovedFrom(DeckLocation location, IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.RemovedFrom(location, player);
if (_cardGainedEventHandler != null)
player.CardGained -= _cardGainedEventHandler;
_cardGainedEventHandler = null;
}
}
public class HauntedCastle : Castle
{
public HauntedCastle()
: base(Categories.Unknown, Traits.ReactToGain | Traits.Gainer | Traits.AffectOthers)
{
BaseCost = new Cost(6);
VictoryPoints = 2;
}
public override void SetupCard(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.SetupCard(game);
foreach (var player in game.Players)
player.CardGained += Player_CardGained;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
player.CardGained -= Player_CardGained;
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key = TypeClass.HauntedCastle.ToString();
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(key) || e.HandledBy.Contains(this) || e.Game.ActivePlayer != player)
return;
e.Resolvers[key] = new CardGainResolver(player, this, "GainGold", "Gain a Gold", Player_GainHauntedCastle, true);
}
internal void Player_GainHauntedCastle(IPlayer player, ref Players.CardGainEventArgs e)
{
player.Gain(e.Game.Table.Gold, this);
var enumerator = player._Game.GetPlayersStartingWithEnumerator(player);
enumerator.MoveNext();
while (enumerator.MoveNext())
{
var attackee = enumerator.Current;
if (attackee.Hand.Count < 5)
continue;
var choice = new Choice("Put 2 cards on top of your deck.", this, attackee.Hand, ChoiceOutcome.Select, player, isOrdered: true, minimum: 2, maximum: 2);
var result = attackee.MakeChoice(choice);
attackee.RetrieveCardsFrom(DeckLocation.Hand, result.Cards);
attackee.AddCardsToDeck(result.Cards, DeckPosition.Top);
}
e.HandledBy.Add(this);
}
}
public class HumbleCastle : Castle
{
public HumbleCastle()
: base(Categories.Treasure, Traits.PlusCoin | Traits.VariableVPs)
{
BaseCost = new Cost(3);
Benefit.Currency.Coin.Value = 1;
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
return base.ComputeVictoryPoints(player, collection) +
collection.Count(c => c.Category.HasFlag(Categories.Castle));
}
}
public class KingsCastle : Castle
{
public KingsCastle()
: base(Categories.Unknown, Traits.VariableVPs)
{
BaseCost = new Cost(10);
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
return base.ComputeVictoryPoints(player, collection) +
2 * collection.Count(c => c.Category.HasFlag(Categories.Castle));
}
}
public class Legionary : Card
{
public Legionary()
: base(Categories.Action | Categories.Attack, Source.Empires, Location.Kingdom, Traits.PlusCoin | Traits.Terminal | Traits.AffectOthers)
{
BaseCost = new Cost(5);
Benefit.Currency.Coin.Value = 3;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
if (player.Hand[Universal.TypeClass.Gold].Any())
{
var choice = Choice.CreateYesNoChoice("You may reveal a Gold card to make your opponents discard. Do you want to reveal?", this, player);
var result = player.MakeChoice(choice);
if (result.Options.Contains(Resource.Yes))
{
var singleGold = player.RetrieveCardFrom(DeckLocation.Hand, Universal.TypeClass.Gold);
player.AddCardInto(DeckLocation.Revealed, singleGold);
var enumerator = player._Game.GetPlayersStartingWithEnumerator(player);
enumerator.MoveNext();
while (enumerator.MoveNext())
{
var attackee = enumerator.Current;
// Skip if the attack is blocked (Moat, Lighthouse, etc.)
if (IsAttackBlocked[attackee])
continue;
choice = new Choice("Choose cards to discard. You must discard down to 2 cards in hand (then draw a card)", this, attackee.Hand, ChoiceOutcome.Discard, attackee, minimum: attackee.Hand.Count - 2, maximum: attackee.Hand.Count - 2);
result = attackee.MakeChoice(choice);
attackee.Discard(DeckLocation.Hand, result.Cards);
attackee.Draw(DeckLocation.Hand);
}
player.AddCardsToHand(DeckLocation.Revealed);
}
}
}
}
public class OpulentCastle : Castle
{
public OpulentCastle()
: base(Categories.Action, Traits.Discard | Traits.ConditionalBenefit | Traits.PlusCoin | Traits.Terminal)
{
BaseCost = new Cost(7);
VictoryPoints = 3;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var choice = new Choice("Discard any number of Victory cards. +2 per card discarded.", this, player.Hand[Categories.Victory], ChoiceOutcome.Discard, player, minimum: 0, maximum: player.Hand[Categories.Victory].Count);
var result = player.MakeChoice(choice);
player.Discard(DeckLocation.Hand, result.Cards);
var benefit = new CardBenefit();
benefit.Currency += new Coin(2 * result.Cards.Count);
player.ReceiveBenefit(this, benefit);
}
}
public class Overlord : Card
{
public Card ClonedCard { get; private set; }
private DeckLocation? _currentLocation;
public Overlord()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.ConditionalBenefit | Traits.Component | Traits.Debts)
{
BaseCost = new Cost(debtCost: 8);
}
public override bool Finalize(IGame game, ISupply supply)
{
Contract.Requires(game != null, "game cannot be null");
if (base.Finalize(game, supply))
return true;
foreach (var player in game.Players)
if (!player.TokenPiles.ContainsKey(TypeClass.DebtToken))
player.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
return false;
}
public override void AddedTo(DeckLocation location, IPlayer player)
{
base.AddedTo(location, player);
_currentLocation = location;
if (ClonedCard == null)
return;
switch (location)
{
case DeckLocation.InPlay:
case DeckLocation.SetAside:
ClonedCard.AddedTo(location, player);
break;
}
}
public override void RemovedFrom(DeckLocation location, IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.RemovedFrom(location, player);
_currentLocation = null;
if (ClonedCard == null)
return;
switch (location)
{
case DeckLocation.InPlay:
case DeckLocation.SetAside:
case DeckLocation.InPlayAndSetAside:
ClonedCard.PhysicalCard = null;
ClonedCard.RemovedFrom(location, player);
ClonedCard.TearDown(player._Game);
ClonedCard = null;
break;
}
}
public override Card LogicalCard => ClonedCard != null ? ClonedCard.LogicalCard : base.LogicalCard;
public override Categories Category => ClonedCard?.Category ?? base.Category;
public override Source Source => ClonedCard?.Source ?? base.Source;
public override Location Location => ClonedCard?.Location ?? base.Location;
public override Traits Traits => ClonedCard?.Traits ?? base.Traits;
public override Cost BaseCost => ClonedCard != null ? ClonedCard.BaseCost : base.BaseCost;
public override CardBenefit Benefit => ClonedCard != null ? ClonedCard.Benefit : base.Benefit;
public override bool SuppressBenefit => ClonedCard != null ? ClonedCard.SuppressBenefit : base.SuppressBenefit;
public override CardBenefit DurationBenefit => ClonedCard != null ? ClonedCard.DurationBenefit : base.DurationBenefit;
public override int VictoryPoints => ClonedCard?.VictoryPoints ?? base.VictoryPoints;
public override Card ModifiedBy => ClonedCard != null ? ClonedCard.ModifiedBy : base.ModifiedBy;
public override ItemCollection ChainedInto => ClonedCard != null ? ClonedCard.ChainedInto : base.ChainedInto;
public override bool CanCleanUp => ClonedCard?.CanCleanUp ?? base.CanCleanUp;
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
if (ClonedCard != null)
return ClonedCard.ComputeVictoryPoints(player, collection);
return base.ComputeVictoryPoints(player, collection);
}
public override IEnumerable LookThrough(Predicate predicate)
{
if (ClonedCard != null)
return ClonedCard.LookThrough(predicate);
return base.LookThrough(predicate);
}
public override bool IsStackable => ClonedCard == null;
public override DisplayableCollection Stack()
{
var cc = new DisplayableCollection { this };
if (ClonedCard != null)
cc.AddRange(ClonedCard.Stack());
return cc;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
if (ClonedCard == null)
{
base.FollowInstructions(player);
var choice = new Choice("Choose a card to clone this card as", this,
new SupplyCollection(player._Game.Table.TableEntities.FindAll(
supply =>
supply.Any()
&& supply.TopCard.Category.HasFlag(Categories.Action)
&& supply.CurrentCost <= new Cost(5))),
ChoiceOutcome.Select, player, false);
var result = player.MakeChoice(choice);
if (result.Supply != null)
{
player._Game.SendMessage(player, this, (Card)result.Supply.TopCard);
ClonedCard = CreateInstance(result.Supply.TopCard.Type);
ClonedCard.PhysicalCard = this;
// This is needed in order to set up player mats & any other acutrements
ClonedCard.ReceivedBy(player);
// Add the card to this card's area
ClonedCard.AddedTo(_currentLocation.GetValueOrDefault(DeckLocation.InPlay), player);
}
else
player._Game.SendMessage(player, this);
}
if (ClonedCard != null)
{
player.Actions++;
var previousPlayerMode = player.PutCardIntoPlay(ClonedCard);
player.PlayCard(ClonedCard, previousPlayerMode);
}
}
public override void ResolveDuration(IPlayer player)
{
ClonedCard?.ResolveDuration(player);
}
internal override XmlNode GenerateXml(XmlDocument doc, string nodeName)
{
var xn = base.GenerateXml(doc, nodeName);
if (ClonedCard != null)
xn.AppendChild(ClonedCard.GenerateXml(doc, "cloned_card"));
return xn;
}
internal override void LoadInstance(XmlNode xnCard)
{
base.LoadInstance(xnCard);
ClonedCard = (Card)Load(xnCard.SelectSingleNode("cloned_card"));
}
}
public class PatricianEmporium : Card
{
public PatricianEmporium()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.Randomizer | Traits.PlusCard | Traits.PlusAction | Traits.PlusCoin | Traits.Component | Traits.VPTokens | Traits.ConditionalBenefit | Traits.Cantrip | Traits.SplitPile)
{
BaseCost = new Cost(2);
}
public override void SetupSupply(IGame game, ISupply supply)
{
Contract.Requires(supply != null, "supply cannot be null");
base.SetupSupply(game, supply);
supply.Empty();
var cards = new CardCollection
{
new Emporium(),
new Emporium(),
new Emporium(),
new Emporium(),
new Emporium(),
new Patrician(),
new Patrician(),
new Patrician(),
new Patrician(),
new Patrician()
};
supply.AddTo(cards);
}
}
public class Patrician : Card
{
public Patrician()
: base(Categories.Action, Source.Empires, Location.Special, Traits.PlusCard | Traits.PlusAction | Traits.ConditionalBenefit | Traits.Cantrip)
{
BaseCost = new Cost(2);
Benefit.Cards = 1;
Benefit.Actions = 1;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var card = player.Draw(DeckLocation.Revealed);
if (card != null)
{
if (player._Game.ComputeCost(card) >= new Coin(5))
player.AddCardToHand(player.RetrieveCardFrom(DeckLocation.Revealed, card));
else
player.AddCardToDeck(player.RetrieveCardFrom(DeckLocation.Revealed, card), DeckPosition.Top);
}
}
}
public class Plunder : Card
{
public Plunder()
: base(Categories.Treasure, Source.Empires, Location.Special, Traits.PlusCoin | Traits.VPTokens | Traits.Component)
{
BaseCost = new Cost(5);
Benefit.Currency.Coin.Value = 2;
Benefit.VictoryPoints = 1;
}
}
public class Rocks : Card
{
public Rocks()
: base(Categories.Treasure, Source.Empires, Location.Special, Traits.PlusCoin | Traits.Gainer | Traits.ReactToGain | Traits.ReactToTrashing)
{
BaseCost = new Cost(4);
Benefit.Currency.Coin.Value = 1;
}
public override void SetupCard(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.SetupCard(game);
foreach (var player in game.Players)
{
player.CardGained += Player_CardGained;
player.Trashed += Player_Trashed;
}
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
{
player.CardGained -= Player_CardGained;
player.Trashed -= Player_Trashed;
}
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key = TypeClass.Rocks.ToString();
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(key) || e.HandledBy.Contains(this))
return;
e.Resolvers[key] = new CardGainResolver(player, this, "GainSilver", "Gain Silver", Player_GainRocks, true);
}
internal void Player_GainRocks(IPlayer player, ref Players.CardGainEventArgs e)
{
HandleRocks(player);
e.HandledBy.Add(this);
}
private void Player_Trashed(object sender, TrashEventArgs e)
{
// Already being processed or been handled -- don't need to process this one
if (e.Resolvers.ContainsKey(TypeClass.Rocks) || e.HandledBy.Contains(this))
return;
if (e.TrashedCards.Contains(PhysicalCard))
e.Resolvers[TypeClass.Rocks] = new TrashResolver(e.CurrentPlayer, this, "Gain Silver", Player_PlusCard, true);
}
internal void Player_PlusCard(IPlayer player, ref TrashEventArgs e)
{
HandleRocks(player);
e.HandledBy.Add(this);
}
private void HandleRocks(IPlayer player)
{
if (player.Phase == PhaseEnum.Buy || player.Phase == PhaseEnum.BuyTreasure)
player.Gain(player._Game.Table.Silver, this, DeckLocation.Deck, DeckPosition.Top);
else
player.Gain(player._Game.Table.Silver, this, DeckLocation.Hand);
}
}
public class RoyalBlacksmith : Card
{
public RoyalBlacksmith()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.Component | Traits.PlusCard | Traits.Discard | Traits.Debts | Traits.Terminal | Traits.NetCardDraw)
{
BaseCost = new Cost(debtCost: 8);
Benefit.Cards = 5;
}
public override bool Finalize(IGame game, ISupply supply)
{
Contract.Requires(game != null, "game cannot be null");
if (base.Finalize(game, supply))
return true;
foreach (var player in game.Players)
if (!player.TokenPiles.ContainsKey(TypeClass.DebtToken))
player.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
return false;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
player.RevealHand();
player.Discard(DeckLocation.Revealed, c => c is Universal.Copper);
player.AddCardsToHand(DeckLocation.Revealed);
}
}
public class Sacrifice : Card
{
public Sacrifice()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.Trasher | Traits.DeckReduction | Traits.ConditionalBenefit | Traits.RemoveCurses | Traits.VPTokens | Traits.Component | Traits.RemoveFromHand)
{
BaseCost = new Cost(4);
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var choiceTrash = new Choice(Resource.ChooseACardToTrash, this, player.Hand, ChoiceOutcome.Trash, player);
var resultTrash = player.MakeChoice(choiceTrash);
if (resultTrash.Cards.Any())
{
var trashedCard = resultTrash.Cards[0];
player.Trash(this, player.RetrieveCardFrom(DeckLocation.Hand, trashedCard));
var benefit = new CardBenefit();
if (trashedCard.Category.HasFlag(Categories.Action))
{
benefit.Actions = 2;
benefit.Cards = 2;
}
if (trashedCard.Category.HasFlag(Categories.Treasure))
benefit.Currency += new Coin(2);
if (trashedCard.Category.HasFlag(Categories.Victory))
benefit.VictoryPoints = 2;
player.ReceiveBenefit(this, benefit);
}
}
}
public class SettlersBustlingVillage : Card
{
public SettlersBustlingVillage()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.Randomizer | Traits.PlusCard | Traits.PlusAction | Traits.PlusMultipleActions | Traits.Cantrip | Traits.SplitPile)
{
BaseCost = new Cost(2);
}
public override void SetupSupply(IGame game, ISupply supply)
{
Contract.Requires(supply != null, "supply cannot be null");
base.SetupSupply(game, supply);
supply.Empty();
var cards = new CardCollection
{
new BustlingVillage(),
new BustlingVillage(),
new BustlingVillage(),
new BustlingVillage(),
new BustlingVillage(),
new Settlers(),
new Settlers(),
new Settlers(),
new Settlers(),
new Settlers()
};
supply.AddTo(cards);
}
}
public class Settlers : Card
{
public Settlers()
: base(Categories.Action, Source.Empires, Location.Special, Traits.PlusCard | Traits.PlusAction | Traits.Cantrip)
{
BaseCost = new Cost(2);
Benefit.Cards = 1;
Benefit.Actions = 1;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var discardPileCards = player.DiscardPile.LookThrough(c => true);
player._Game.SendMessage(player, this, "LookThroughDiscard", discardPileCards.ToArray());
var copperCount = discardPileCards.Count(c => c is Universal.Copper);
if (copperCount > 0)
{
var choice = Choice.CreateYesNoChoice("Do you want to reveal a Copper and put it into your hand?", this, player);
var result = player.MakeChoice(choice);
if (result.Options[0] == Resource.Yes)
{
player.AddCardsInto(DeckLocation.Revealed, player.RetrieveCardsFrom(DeckLocation.Discard, Universal.TypeClass.Copper, 1));
player.AddCardsToHand(DeckLocation.Revealed);
}
}
}
}
public class SmallCastle : Castle
{
public SmallCastle()
: base(Categories.Action, Traits.Trasher | Traits.Gainer | Traits.Terminal | Traits.RemoveFromHand)
{
BaseCost = new Cost(5);
VictoryPoints = 2;
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
Card trashedCard = null;
var choiceTrash = new Choice("You may trash a Castle from your hand (otherwise, trash this)", this, player.Hand[Categories.Castle], ChoiceOutcome.Trash, player, minimum: 0);
var resultTrash = player.MakeChoice(choiceTrash);
if (resultTrash.Cards.Any())
{
trashedCard = resultTrash.Cards[0];
player.Trash(this, player.RetrieveCardsFrom(DeckLocation.Hand, resultTrash.Cards));
}
else if (player.InPlay.Contains(PhysicalCard))
{
trashedCard = PhysicalCard;
player.Trash(this, player.RetrieveCardFrom(DeckLocation.InPlay, PhysicalCard));
}
if (trashedCard != null && ((ISupply)player._Game.Table[TypeClass.Castles]).CanGain())
player.Gain((ISupply)player._Game.Table[TypeClass.Castles], this);
}
}
public class SprawlingCastle : Castle
{
public SprawlingCastle()
: base(Categories.Unknown, Traits.ReactToGain | Traits.Gainer)
{
BaseCost = new Cost(8);
VictoryPoints = 4;
}
public override void SetupCard(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.SetupCard(game);
foreach (var player in game.Players)
player.CardGained += Player_CardGained;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
player.CardGained -= Player_CardGained;
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key = TypeClass.SprawlingCastle.ToString();
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(key) || e.HandledBy.Contains(this))
return;
e.Resolvers[key] = new CardGainResolver(player, this, "GainVPCards", "Gain Duchy/Estates", Player_GainGrandCastle, true);
}
internal void Player_GainGrandCastle(IPlayer player, ref Players.CardGainEventArgs e)
{
var choice = new Choice(Resource.ChooseOne, this, this, new List { "Gain a Duchy", "Gain 3 Estates" }, player);
var result = player.MakeChoice(choice);
if (result.Options.Contains("Gain a Duchy"))
player.Gain(player._Game.Table.Duchy, this);
else
player.Gain(player._Game.Table.Estate, this, 3);
e.HandledBy.Add(this);
}
}
public class Temple : Card
{
public Temple()
: base(Categories.Action | Categories.Gathering, Source.Empires, Location.Kingdom, Traits.VPTokens | Traits.Trasher | Traits.RemoveCurses | Traits.DeckReduction | Traits.ReactToGain | Traits.Terminal | Traits.Component | Traits.RemoveFromHand)
{
BaseCost = new Cost(4);
Benefit.VictoryPoints = 1;
}
public override void SetupCard(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.SetupCard(game);
foreach (var player in game.Players)
player.CardGained += Player_CardGained;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
player.CardGained -= Player_CardGained;
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var key = TypeClass.Temple.ToString();
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(key) || e.HandledBy.Contains(this))
return;
var player = sender as IPlayer;
var tSupply = (ISupply)player._Game.Table.TableEntities[TypeClass.Temple];
var victoryTokens = tSupply.Tokens.OfType();
e.Resolvers[key] = new CardGainResolver(player, this, "GainVPs",
$"Gain {victoryTokens.Count()} off Temple Supply pile", Player_GainTemple, true);
}
internal void Player_GainTemple(IPlayer player, ref Players.CardGainEventArgs e)
{
var tSupply = (ISupply)player._Game.Table.TableEntities[TypeClass.Temple];
var victoryTokens = tSupply.Tokens.OfType().ToList();
foreach (var vpToken in victoryTokens)
victoryTokens.ForEach(t => tSupply.RemoveToken(t));
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = victoryTokens.Count });
e.HandledBy.Add(this);
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var choiceTrash = new Choice("Trash 1 to 3 differently named cards", this, player.Hand.DistinctBy(c => c.Name), ChoiceOutcome.Trash, player, maximum: 3);
var resultTrash = player.MakeChoice(choiceTrash);
player.Trash(this, player.RetrieveCardsFrom(DeckLocation.Hand, resultTrash.Cards));
var tSupply = (ISupply)player._Game.Table.TableEntities[TypeClass.Temple];
tSupply.AddToken(new Prosperity.VictoryToken());
}
}
public class Villa : Card
{
public Villa()
: base(Categories.Action, Source.Empires, Location.Kingdom, Traits.PlusCoin | Traits.PlusAction | Traits.PlusMultipleActions | Traits.PlusBuy | Traits.ReactToGain)
{
BaseCost = new Cost(4);
Benefit.Actions = 2;
Benefit.Buys = 1;
Benefit.Currency.Coin.Value = 1;
}
public override void SetupCard(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.SetupCard(game);
foreach (var player in game.Players)
player.CardGained += Player_CardGained;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
player.CardGained -= Player_CardGained;
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key = TypeClass.Villa.ToString();
// This is not the card you are looking for
if (e.Card != this || e.Resolvers.ContainsKey(key) || e.HandledBy.Contains(this) || e.IsLostTrackOf)
return;
e.Resolvers[key] = new CardGainResolver(player, this, "PutCardIntoHand", $"Put {Name} into your hand", Player_GainVilla, true);
}
internal void Player_GainVilla(IPlayer player, ref Players.CardGainEventArgs e)
{
e.Cancelled = true;
e.IsLostTrackOf = true;
e.Location = DeckLocation.Hand;
player.ReceiveBenefit(this, new CardBenefit { Actions = 1 });
if (player.Phase == PhaseEnum.Buy || player.Phase == PhaseEnum.BuyTreasure)
player.GoToActionPhase();
e.HandledBy.Add(this);
}
}
public class WildHunt : Card
{
public WildHunt()
: base(Categories.Action | Categories.Gathering, Source.Empires, Location.Kingdom, Traits.VPTokens | Traits.Component | Traits.ConditionalBenefit | Traits.Terminal | Traits.Gainer | Traits.NetCardDraw)
{
BaseCost = new Cost(5);
}
public override void FollowInstructions(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.FollowInstructions(player);
var choice = new Choice(Resource.ChooseOne, this, this, new List { "+3 Cards and +1 to the Wild Hunt Supply pile", "Gain an Estate and on the Wild Hunt Supply pile" }, player);
var result = player.MakeChoice(choice);
var whSupply = (ISupply)player._Game.Table.TableEntities[TypeClass.WildHunt];
if (result.Options.Contains("+3 Cards and +1 to the Wild Hunt Supply pile"))
{
player.ReceiveBenefit(this, new CardBenefit { Cards = 3 });
whSupply.AddToken(new Prosperity.VictoryToken());
}
else if (result.Options.Contains("Gain an Estate and on the Wild Hunt Supply pile"))
{
if (player.Gain(player._Game.Table.Estate, this))
{
var victoryTokens = whSupply.Tokens.OfType().ToList();
foreach (var vpToken in victoryTokens)
victoryTokens.ForEach(t => whSupply.RemoveToken(t));
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = victoryTokens.Count });
}
}
}
}
public class Advance : Event
{
public Advance()
: base(Source.Empires, 0, Traits.Trasher | Traits.Gainer | Traits.RemoveFromHand)
{
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
var choiceTrash = new Choice(Resource.ChooseToTrashOptional, this, player.Hand[Categories.Action], ChoiceOutcome.Trash, player, minimum: 0);
var resultTrash = player.MakeChoice(choiceTrash);
if (resultTrash.Cards.Any())
{
player.Trash(this, player.RetrieveCardsFrom(DeckLocation.Hand, resultTrash.Cards));
var gainableSupplies = new SupplyCollection(player._Game.Table.TableEntities.FindAll(
supply =>
supply.CanGain()
&& supply.TopCard.Category.HasFlag(Categories.Action)
&& supply.CurrentCost <= new Coin(6)
));
var choice = new Choice(Resource.GainUpTo6, this, gainableSupplies, ChoiceOutcome.Gain, player, false);
var result = player.MakeChoice(choice);
if (result.Supply != null)
player.Gain(result.Supply, this);
}
}
}
public class Annex : Event
{
public Annex()
: base(Source.Empires, new Cost(debtCost: 8), Traits.CardOrdering | Traits.Gainer | Traits.Component | Traits.Debts)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
if (!player.TokenPiles.ContainsKey(TypeClass.DebtToken))
player.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
var discardPileCards = player.DiscardPile.LookThrough(c => true);
player._Game.SendMessage(player, this, "LookThroughDiscard", discardPileCards.ToArray());
var choice = new Choice("Choose up to 5 cards to leave in your discard pile (shuffling the rest into your deck)", this, discardPileCards, ChoiceOutcome.Select, player, minimum: 0, maximum: 5);
var result = player.MakeChoice(choice);
player.AddCardsToDeck(player.DiscardPile.Retrieve(player, c => !result.Cards.Contains(c)), DeckPosition.Top);
player.ShuffleDrawPile();
player.Gain(player._Game.Table.Duchy, this);
}
}
public class Banquet : Event
{
public Banquet()
: base(Source.Empires, 3, Traits.Gainer)
{
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
player.Gain(player._Game.Table.Copper, this);
player.Gain(player._Game.Table.Copper, this);
var gainableSupplies = new SupplyCollection(player._Game.Table.TableEntities.FindAll(
supply =>
supply.CanGain()
&& !supply.TopCard.Category.HasFlag(Categories.Victory)
&& supply.CurrentCost <= new Coin(5)
));
var choice = new Choice("Gain a non-Victory card costing up to 5", this, gainableSupplies, ChoiceOutcome.Gain, player, false);
var result = player.MakeChoice(choice);
if (result.Supply != null)
player.Gain(result.Supply, this);
}
}
public class Conquest : Event
{
public Conquest()
: base(Source.Empires, 6, Traits.Gainer | Traits.Component | Traits.VPTokens)
{
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
player.Gain(player._Game.Table.Silver, this);
player.Gain(player._Game.Table.Silver, this);
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = player.CurrentTurn.CardsGained.Count(c => c is Universal.Silver) });
}
}
public class Delve : Event
{
public Delve()
: base(Source.Empires, 2, Traits.PlusBuy | Traits.Gainer)
{
Benefit.Buys = 1;
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
player.Gain(player._Game.Table.Silver, this);
}
}
public class Dominate : Event
{
public Dominate()
: base(Source.Empires, 14, Traits.Gainer | Traits.Component | Traits.VPTokens)
{
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
if (player.Gain(player._Game.Table.Province, this))
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = 9 });
}
}
public class Donate : Event
{
public Donate()
: base(Source.Empires, new Cost(debtCost: 8), Traits.Trasher | Traits.DeckReduction | Traits.RemoveCurses | Traits.Component | Traits.Debts | Traits.RemoveFromHand)
{
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
foreach (var player in game.Players)
player.PhaseChanged -= Player_PhaseChanged;
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
player.PhaseChanged += Player_PhaseChanged;
}
private void Player_PhaseChanged(object sender, PhaseChangedEventArgs pcea)
{
if (pcea.NewPhase == PhaseEnum.Waiting && pcea.OldPhase == PhaseEnum.Cleanup)
{
if (!pcea.HandledBy.Contains(TypeClass.Donate))
pcea.Resolvers[TypeClass.Donate] = new PhaseChangedResolver(
this,
"Resolve Donate",
(IPlayer player, ref PhaseChangedEventArgs e) =>
{
// put all cards from your deck and discard pile into your hand
player.AddCardsInto(DeckLocation.Hand, player.RetrieveCardsFrom(DeckLocation.Deck));
player.AddCardsInto(DeckLocation.Hand, player.RetrieveCardsFrom(DeckLocation.Discard));
// trash any number
var choiceTrash = new Choice("Choose any number of cards to trash", this, player.Hand, ChoiceOutcome.Trash, player, minimum: 0, maximum: player.Hand.Count);
var resultTrash = player.MakeChoice(choiceTrash);
player.Trash(this, player.RetrieveCardsFrom(DeckLocation.Hand, resultTrash.Cards));
// shuffle your hand into your deck
player.AddCardsInto(DeckLocation.Deck, player.RetrieveCardsFrom(DeckLocation.Hand));
player.ShuffleDrawPile();
// then draw 5 cards
player.DrawHand(5);
player.PhaseChanged -= Player_PhaseChanged;
e.HandledBy.Add(TypeClass.Donate);
},
true
);
}
}
}
public class Ritual : Event
{
public Ritual()
: base(Source.Empires, 4, Traits.PlusCurses | Traits.Trasher | Traits.Component | Traits.VPTokens | Traits.RemoveFromHand)
{
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
if (player.Gain(player._Game.Table.Curse, this))
{
var choice = new Choice(Resource.ChooseACardToTrash, this, player.Hand, ChoiceOutcome.Trash, player);
var result = player.MakeChoice(choice);
if (result.Cards.Any())
{
player.Trash(this, player.RetrieveCardFrom(DeckLocation.Hand, result.Cards[0]));
var cardCost = player._Game.ComputeCost(result.Cards[0]);
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = cardCost.Coin.Value });
}
}
}
}
public class SaltTheEarth : Event
{
public SaltTheEarth()
: base(Source.Empires, 4, Traits.Trasher | Traits.Component | Traits.VPTokens)
{
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = 1 });
var trashableSupplies = new SupplyCollection(player._Game.Table.TableEntities.FindAll(
supply =>
supply.TopCard != null
&& supply.TopCard.Category.HasFlag(Categories.Victory)
));
var choice = new Choice("Trash a Victory card from the Supply", this, trashableSupplies, ChoiceOutcome.Trash, player, false);
var result = player.MakeChoice(choice);
if (result.Supply != null)
player.Trash(this, result.Supply);
}
}
public class Tax : Event
{
public Tax()
: base(Source.Empires, 2, Traits.Component | Traits.Debts)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
foreach (var supply in game.Table.TableEntities.Values.OfType())
supply.AddToken(new DebtToken());
EndChanges();
foreach (var player in game.Players)
player.CardBought += Player_CardBought;
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
foreach (var player in game.Players)
player.CardBought -= Player_CardBought;
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
var supplies = new SupplyCollection(player._Game.Table.TableEntities.FindAll(s => true));
var choice = new Choice("Add 2 to a Supply pile", this, supplies, ChoiceOutcome.Select, player, false);
var result = player.MakeChoice(choice);
if (result.Supply != null)
{
player._Game.SendMessage(player, this, result.Supply);
result.Supply.AddToken(new DebtToken());
result.Supply.AddToken(new DebtToken());
}
}
private void Player_CardBought(object sender, CardBuyEventArgs e)
{
if (e.From is ISupply supply)
{
supply.BeginChanges();
var tokens = supply.RemoveTokens(TypeClass.DebtToken);
supply.EndChanges();
if (tokens.Any())
((IPlayer)sender).AddTokens(tokens);
}
}
}
public class Triumph : Event
{
public Triumph()
: base(Source.Empires, new Cost(debtCost: 5), Traits.Gainer | Traits.Component | Traits.VPTokens | Traits.Debts)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
if (!player.TokenPiles.ContainsKey(TypeClass.DebtToken))
player.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
if (player.Gain(player._Game.Table.Estate, this))
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = player.CurrentTurn.CardsGained.Count });
}
}
public class Wedding : Event
{
public Wedding()
: base(Source.Empires, new Cost(coinCost: 4, debtCost: 3), Traits.Gainer | Traits.Component | Traits.VPTokens | Traits.Debts)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
if (!player.TokenPiles.ContainsKey(TypeClass.DebtToken))
player.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = 1 });
player.Gain(player._Game.Table.Gold, this);
}
}
public class Windfall : Event
{
public Windfall()
: base(Source.Empires, 5, Traits.Gainer)
{
}
public override void Bought(IPlayer player)
{
Contract.Requires(player != null, "player cannot be null");
base.Bought(player);
if (player.DrawPile.Count == 0 && player.DiscardPile.Count == 0)
{
player.Gain(player._Game.Table.Gold, this, count: 3);
}
}
}
public class Aqueduct : Landmark
{
private readonly Dictionary _cardGainedHandlers = new Dictionary();
public Aqueduct()
: base(Source.Empires, Traits.Component | Traits.VPTokens | Traits.ReactToGain)
{
}
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _cardGainedHandlers.Keys)
playerLoop.CardGained -= _cardGainedHandlers[playerLoop];
_cardGainedHandlers.Clear();
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
for (var i = 0; i < 8; i++)
{
game.Table.Silver.AddToken(new Prosperity.VictoryToken());
game.Table.Gold.AddToken(new Prosperity.VictoryToken());
}
EndChanges();
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_cardGainedHandlers[enumPlayers.Current] = new CardGainedEventHandler(Player_CardGained);
enumPlayers.Current.CardGained += _cardGainedHandlers[enumPlayers.Current];
}
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key1 = typeof(AqueductTreasure).ToString();
var key2 = typeof(AqueductVictory).ToString();
if (e.Card.Category.HasFlag(Categories.Treasure) && !e.HandledBy.Contains(typeof(AqueductTreasure)) && e.Game.Table[e.Card.Type].Tokens.Any(t => t is Prosperity.VictoryToken))
e.Resolvers[key1] = new CardGainResolver(player, this, "MoveVPs", $"Move token to {this}", Player_GainTreasure, true);
if (e.Card.Category.HasFlag(Categories.Victory) && !e.HandledBy.Contains(typeof(AqueductVictory)) && e.Game.Table[TypeClass.Aqueduct].Tokens.Any(t => t is Prosperity.VictoryToken))
e.Resolvers[key2] = new CardGainResolver(player, this, "GainVPs", $"Gain tokens off {this}", Player_GainVictory, true);
}
internal void Player_GainTreasure(IPlayer player, ref Players.CardGainEventArgs e)
{
var token = e.Game.Table[e.Card.Type].RemoveToken(Prosperity.TypeClass.VictoryToken);
e.Game.Table[TypeClass.Aqueduct].AddToken(token);
e.HandledBy.Add(typeof(AqueductTreasure));
}
internal void Player_GainVictory(IPlayer player, ref Players.CardGainEventArgs e)
{
BeginChanges();
var tokens = new TokenCollection();
do
{
tokens.Add(RemoveToken(Prosperity.TypeClass.VictoryToken));
} while (Tokens.Any(t => t is Prosperity.VictoryToken));
EndChanges();
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = tokens.Count });
e.HandledBy.Add(typeof(AqueductVictory));
}
}
// Dummy classes used by Action resolution, since Aqueduct has 2 triggers for the same event
internal class AqueductTreasure { }
internal class AqueductVictory { }
public class Arena : Landmark
{
private readonly Dictionary _phaseChangingEventHandlers = new Dictionary();
public Arena()
: base(Source.Empires, Traits.Component | Traits.VPTokens | Traits.ReactToGain)
{
}
public override bool CanUndo => false;
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _phaseChangingEventHandlers.Keys)
playerLoop.PhaseChanging -= _phaseChangingEventHandlers[playerLoop];
_phaseChangingEventHandlers.Clear();
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
for (var i = 0; i < game.Players.Count * 6; i++)
AddToken(new Prosperity.VictoryToken());
EndChanges();
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_phaseChangingEventHandlers[enumPlayers.Current] = new PhaseChangingEventHandler(Player_PhaseChanging);
enumPlayers.Current.PhaseChanging += _phaseChangingEventHandlers[enumPlayers.Current];
}
}
private void Player_PhaseChanging(object sender, PhaseChangingEventArgs e)
{
var player = sender as IPlayer;
if (!e.HandledBy.Contains(TypeClass.Arena) && Tokens.Any(t => t is Prosperity.VictoryToken) &&
e.NewPhase == PhaseEnum.BuyTreasure && player.Hand[Categories.Action].Any())
e.Resolvers[TypeClass.Arena] = new PhaseChangingResolver(this, Resource.DiscardActionCard, Player_DiscardAction, false);
}
internal void Player_DiscardAction(IPlayer player, ref PhaseChangingEventArgs e)
{
var choice = new Choice(Resource.DiscardActionCard, this, player.Hand[Categories.Action], ChoiceOutcome.Discard, player, minimum: 0);
var result = player.MakeChoice(choice);
if (result.Cards.Any())
{
player.Discard(DeckLocation.Hand, result.Cards);
BeginChanges();
var tokens = new TokenCollection { RemoveToken(Prosperity.TypeClass.VictoryToken), RemoveToken(Prosperity.TypeClass.VictoryToken) };
EndChanges();
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = tokens.Count });
}
e.HandledBy.Add(TypeClass.Arena);
}
}
public class BanditFort : Landmark
{
public BanditFort()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new BanditFort());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
return base.ComputeVictoryPoints(player, collection) - 2 * collection.Count(c => c is Universal.Silver || c is Universal.Gold);
}
}
public class Basilica : Landmark
{
private readonly Dictionary _CardBoughtEventHandlers = new Dictionary();
public Basilica()
: base(Source.Empires, Traits.Component | Traits.VPTokens | Traits.ReactToBuy)
{
}
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _CardBoughtEventHandlers.Keys)
playerLoop.CardBought -= _CardBoughtEventHandlers[playerLoop];
_CardBoughtEventHandlers.Clear();
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
for (var i = 0; i < game.Players.Count * 6; i++)
AddToken(new Prosperity.VictoryToken());
EndChanges();
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_CardBoughtEventHandlers[enumPlayers.Current] = new CardBoughtEventHandler(Player_CardBought);
enumPlayers.Current.CardBought += _CardBoughtEventHandlers[enumPlayers.Current];
}
}
private void Player_CardBought(object sender, CardBuyEventArgs e)
{
var player = sender as IPlayer;
if (!e.HandledBy.Contains(TypeClass.Basilica) && Tokens.Any(t => t is Prosperity.VictoryToken) &&
player.Currency.Coin >= 2 && e.Card.Type.IsSubclassOf(typeof(Card)))
e.Resolvers[TypeClass.Basilica] = new CardBuyResolver(player, e.Card, "Gain 2", Player_Action, true);
}
internal void Player_Action(IPlayer player, ref CardBuyEventArgs e)
{
BeginChanges();
var tokens = new TokenCollection { RemoveToken(Prosperity.TypeClass.VictoryToken), RemoveToken(Prosperity.TypeClass.VictoryToken) };
EndChanges();
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = tokens.Count });
e.HandledBy.Add(TypeClass.Basilica);
}
}
public class Baths : Landmark
{
private readonly Dictionary _phaseChangingEventHandlers = new Dictionary();
public Baths()
: base(Source.Empires, Traits.Component | Traits.VPTokens)
{
}
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _phaseChangingEventHandlers.Keys)
playerLoop.PhaseChanging -= _phaseChangingEventHandlers[playerLoop];
_phaseChangingEventHandlers.Clear();
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
for (var i = 0; i < game.Players.Count * 6; i++)
AddToken(new Prosperity.VictoryToken());
EndChanges();
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_phaseChangingEventHandlers[enumPlayers.Current] = new PhaseChangingEventHandler(Player_PhaseChanging);
enumPlayers.Current.PhaseChanging += _phaseChangingEventHandlers[enumPlayers.Current];
}
}
private void Player_PhaseChanging(object sender, PhaseChangingEventArgs e)
{
if (!e.HandledBy.Contains(TypeClass.Baths) && Tokens.Any(t => t is Prosperity.VictoryToken) &&
e.CurrentPhase == PhaseEnum.Cleanup && e.NewPhase == PhaseEnum.Waiting && e.CurrentPlayer.CurrentTurn.CardsGained.Count == 0)
e.Resolvers[TypeClass.Baths] = new PhaseChangingResolver(this, "Gain 2", Player_Action, true);
}
internal void Player_Action(IPlayer player, ref PhaseChangingEventArgs e)
{
BeginChanges();
var tokens = new TokenCollection { RemoveToken(Prosperity.TypeClass.VictoryToken), RemoveToken(Prosperity.TypeClass.VictoryToken) };
EndChanges();
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = tokens.Count });
e.HandledBy.Add(TypeClass.Baths);
}
}
public class Battlefield : Landmark
{
private readonly Dictionary _cardGainedHandlers = new Dictionary();
public Battlefield()
: base(Source.Empires, Traits.ReactToGain | Traits.Component | Traits.VPTokens)
{
}
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _cardGainedHandlers.Keys)
playerLoop.CardGained -= _cardGainedHandlers[playerLoop];
_cardGainedHandlers.Clear();
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
for (var i = 0; i < game.Players.Count * 6; i++)
AddToken(new Prosperity.VictoryToken());
EndChanges();
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_cardGainedHandlers[enumPlayers.Current] = new CardGainedEventHandler(Player_CardGained);
enumPlayers.Current.CardGained += _cardGainedHandlers[enumPlayers.Current];
}
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key = TypeClass.Battlefield.ToString();
if (e.Card.Category.HasFlag(Categories.Victory) && !e.HandledBy.Contains(TypeClass.Battlefield) &&
e.Game.Table[TypeClass.Battlefield].Tokens.Any(t => t is Prosperity.VictoryToken))
e.Resolvers[key] = new CardGainResolver(player, this, "GainVPs", "Gain 2 tokens off Battlefield", Player_GainVictory, true);
}
internal void Player_GainVictory(IPlayer player, ref Players.CardGainEventArgs e)
{
BeginChanges();
var tokens = new TokenCollection { RemoveToken(Prosperity.TypeClass.VictoryToken), RemoveToken(Prosperity.TypeClass.VictoryToken) };
EndChanges();
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = tokens.Count });
e.HandledBy.Add(TypeClass.Battlefield);
}
}
public class Colonnade : Landmark
{
private readonly Dictionary _cardBoughtEventHandlers = new Dictionary();
public Colonnade()
: base(Source.Empires, Traits.Component | Traits.VPTokens | Traits.ReactToBuy)
{
}
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _cardBoughtEventHandlers.Keys)
playerLoop.CardBought -= _cardBoughtEventHandlers[playerLoop];
_cardBoughtEventHandlers.Clear();
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
for (var i = 0; i < game.Players.Count * 6; i++)
AddToken(new Prosperity.VictoryToken());
EndChanges();
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_cardBoughtEventHandlers[enumPlayers.Current] = new CardBoughtEventHandler(Player_CardBought);
enumPlayers.Current.CardBought += _cardBoughtEventHandlers[enumPlayers.Current];
}
}
private void Player_CardBought(object sender, CardBuyEventArgs e)
{
var player = sender as IPlayer;
if (!e.HandledBy.Contains(TypeClass.Colonnade) && Tokens.Any(t => t is Prosperity.VictoryToken) &&
e.Card.Category.HasFlag(Categories.Action) && player.InPlayAndSetAside[c => c.LogicalCard.Type == e.Card.Type].Any() && e.Card.Type.IsSubclassOf(typeof(Card)))
e.Resolvers[TypeClass.Colonnade] = new CardBuyResolver(player, e.Card, "Gain 2", Player_Action, true);
}
internal void Player_Action(IPlayer player, ref CardBuyEventArgs e)
{
BeginChanges();
var tokens = new TokenCollection { RemoveToken(Prosperity.TypeClass.VictoryToken), RemoveToken(Prosperity.TypeClass.VictoryToken) };
EndChanges();
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = tokens.Count });
e.HandledBy.Add(TypeClass.Colonnade);
}
}
public class DefiledShrine : Landmark
{
private readonly Dictionary _cardBoughtEventHandlers = new Dictionary();
private readonly Dictionary _cardGainedHandlers = new Dictionary();
public DefiledShrine()
: base(Source.Empires, Traits.Component | Traits.VPTokens | Traits.ReactToGain | Traits.PlusCurses)
{
}
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _cardBoughtEventHandlers.Keys)
playerLoop.CardBought -= _cardBoughtEventHandlers[playerLoop];
_cardBoughtEventHandlers.Clear();
foreach (var playerLoop in _cardGainedHandlers.Keys)
playerLoop.CardGained -= _cardGainedHandlers[playerLoop];
_cardGainedHandlers.Clear();
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
foreach (var sAction in game.Table.TableEntities.Values.OfType())
{
if (sAction.Randomizer.Category.HasFlag(Categories.Action) && !sAction.Randomizer.Category.HasFlag(Categories.Gathering))
{
sAction.AddToken(new Prosperity.VictoryToken());
sAction.AddToken(new Prosperity.VictoryToken());
}
}
EndChanges();
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_cardBoughtEventHandlers[enumPlayers.Current] = new CardBoughtEventHandler(Player_CardBought);
enumPlayers.Current.CardBought += _cardBoughtEventHandlers[enumPlayers.Current];
_cardGainedHandlers[enumPlayers.Current] = new CardGainedEventHandler(Player_CardGained);
enumPlayers.Current.CardGained += _cardGainedHandlers[enumPlayers.Current];
}
}
private void Player_CardBought(object sender, CardBuyEventArgs e)
{
var player = sender as IPlayer;
if (!e.HandledBy.Contains(TypeClass.DefiledShrine) && Tokens.Any(t => t is Prosperity.VictoryToken) &&
e.Card.Type == Universal.TypeClass.Curse)
e.Resolvers[TypeClass.DefiledShrine] = new CardBuyResolver(player, e.Card, $"Take from {this}", Player_TakeVP, true);
}
internal void Player_TakeVP(IPlayer player, ref CardBuyEventArgs e)
{
BeginChanges();
var tokens = new TokenCollection();
do
{
tokens.Add(RemoveToken(Prosperity.TypeClass.VictoryToken));
} while (Tokens.Any(t => t is Prosperity.VictoryToken));
EndChanges();
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = tokens.Count });
e.HandledBy.Add(TypeClass.DefiledShrine);
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
var key = TypeClass.DefiledShrine.ToString();
if (!e.HandledBy.Contains(TypeClass.DefiledShrine) && e.Game.Table[e.Card.Type].Tokens.Any(t => t is Prosperity.VictoryToken) &&
e.Card.Category.HasFlag(Categories.Action))
e.Resolvers[key] = new CardGainResolver(player, this, "MoveVPs", $"Move token to {this}", Player_GainAction, true);
}
internal void Player_GainAction(IPlayer player, ref Players.CardGainEventArgs e)
{
var token = e.Game.Table[e.Card.Type].RemoveToken(Prosperity.TypeClass.VictoryToken);
e.Game.Table[TypeClass.DefiledShrine].AddToken(token);
e.HandledBy.Add(TypeClass.DefiledShrine);
}
}
public class Fountain : Landmark
{
public Fountain()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new Fountain());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
return base.ComputeVictoryPoints(player, collection) +
collection.Count(c => c.Type == Universal.TypeClass.Copper) >= 10 ? 15 : 0;
}
}
public class Keep : Landmark
{
public Keep()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new Keep());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
var iPointsList = collection as IList ?? collection.ToList();
var vps = base.ComputeVictoryPoints(player, iPointsList);
if (player == null)
return vps;
foreach (var treasureGroup in iPointsList.Where(c => c.Category.HasFlag(Categories.Treasure)).GroupBy(c => c.Type))
{
var most = true;
var enumerator = player._Game.GetPlayersStartingWithEnumerator(player);
enumerator.MoveNext();
while (enumerator.MoveNext())
{
if (treasureGroup.Count() < enumerator.Current.CountAll(player, c => c.Type == treasureGroup.Key, false))
{
most = false;
break;
}
}
if (most)
vps += 5;
}
return vps;
}
}
public class Labyrinth : Landmark
{
private readonly Dictionary _cardGainedHandlers = new Dictionary();
public Labyrinth()
: base(Source.Empires, Traits.Component | Traits.VPTokens | Traits.ReactToGain)
{
}
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _cardGainedHandlers.Keys)
playerLoop.CardGained -= _cardGainedHandlers[playerLoop];
_cardGainedHandlers.Clear();
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
for (var i = 0; i < game.Players.Count * 6; i++)
AddToken(new Prosperity.VictoryToken());
EndChanges();
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_cardGainedHandlers[enumPlayers.Current] = new CardGainedEventHandler(Player_CardGained);
enumPlayers.Current.CardGained += _cardGainedHandlers[enumPlayers.Current];
}
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
if (!(sender is IPlayer player))
return;
var key = TypeClass.Labyrinth.ToString();
if (!e.HandledBy.Contains(TypeClass.Labyrinth) && e.Game.Table[TypeClass.Labyrinth].Tokens.Any(t => t is Prosperity.VictoryToken) &&
player._Game.ActivePlayer == player && player.CurrentTurn.CardsGained.Count == 2 && player.CurrentTurn.CardsGained[1] == e.Card)
e.Resolvers[key] = new CardGainResolver(player, this, "GainVPs", "Gain 2 tokens off Labyrinth", Player_Gain2ndCard, true);
}
internal void Player_Gain2ndCard(IPlayer player, ref Players.CardGainEventArgs e)
{
BeginChanges();
var tokens = new TokenCollection { RemoveToken(Prosperity.TypeClass.VictoryToken), RemoveToken(Prosperity.TypeClass.VictoryToken) };
EndChanges();
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = tokens.Count });
e.HandledBy.Add(TypeClass.Labyrinth);
}
}
public class MountainPass : Landmark
{
public MountainPass()
: base(Source.Empires, Traits.Component | Traits.VPTokens | Traits.ReactToGain | Traits.Debts | Traits.AffectOthers)
{
}
public override void TearDown(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.TearDown(game);
ClearTriggers(game);
}
private void ClearTriggers(IGame game)
{
foreach (var player in game.Players)
{
player.CardGained -= Player_CardGained;
player.PhaseChanged -= Player_PhaseChanged;
}
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
BeginChanges();
for (var i = 0; i < 8; i++)
AddToken(new Prosperity.VictoryToken());
EndChanges();
foreach (var player in game.Players)
player.CardGained += Player_CardGained;
}
private void Player_CardGained(object sender, Players.CardGainEventArgs e)
{
var player = sender as IPlayer;
if (!e.HandledBy.Contains(TypeClass.MountainPass)
&& e.Card is Universal.Province
&& player.CurrentTurn.CardsGained.Count(c => c is Universal.Province) == 1)
player.PhaseChanged += Player_PhaseChanged;
}
private void Player_PhaseChanged(object sender, PhaseChangedEventArgs e)
{
if (e.NewPhase == PhaseEnum.Waiting && e.OldPhase == PhaseEnum.Cleanup)
{
if (!e.HandledBy.Contains(TypeClass.MountainPass))
e.Resolvers[TypeClass.MountainPass] = new PhaseChangedResolver(this, "Bid on 8", Player_TurnEnded, true);
}
}
private void Player_TurnEnded(object sender, ref PhaseChangedEventArgs e)
{
var player = sender as IPlayer;
Debt maxBid = null;
IPlayer maxBidder = null;
var enumPlayers = player._Game.GetPlayersStartingWithEnumerator(player._Game.GetPlayerFromIndex(player, 1));
while (enumPlayers.MoveNext())
{
var options = new List { "Pass" };
for (var i = (maxBid == null ? 0 : maxBid.Value) + 1; i <= 40; i++)
options.Add($"{i}");
var choice = new Choice("Bid on 8", this, this, options, enumPlayers.Current);
var result = enumPlayers.Current.MakeChoice(choice);
if (result.Options[0] == "Pass")
player._Game.SendMessage(enumPlayers.Current, this);
else
{
var currency = new Currency(result.Options[0]);
player._Game.SendMessage(enumPlayers.Current, this, currency);
maxBid = currency.Debt;
maxBidder = enumPlayers.Current;
// We can't get higher than 40
if (maxBid.Value == 40)
break;
}
}
if (maxBidder != null)
{
BeginChanges();
var vpTokens = new TokenCollection();
do
{
vpTokens.Add(RemoveToken(Prosperity.TypeClass.VictoryToken));
} while (Tokens.Any(t => t is Prosperity.VictoryToken));
EndChanges();
maxBidder.ReceiveBenefit(this, new CardBenefit { VictoryPoints = vpTokens.Count });
if (!maxBidder.TokenPiles.ContainsKey(TypeClass.DebtToken))
maxBidder.TokenPiles[TypeClass.DebtToken] = new TokenCollection();
var tokens = new TokenCollection();
for (var i = 0; i < maxBid.Value; i++)
tokens.Add(new DebtToken());
maxBidder.AddTokens(tokens);
}
ClearTriggers(player._Game);
e.HandledBy.Add(TypeClass.MountainPass);
}
}
public class Museum : Landmark
{
public Museum()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new Museum());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
var iPointsList = collection as IList ?? collection.ToList();
return base.ComputeVictoryPoints(player, iPointsList) + 2 * iPointsList.OfType().DistinctBy(c => c.Name).Count();
}
}
public class ObeliskMarker : Token
{
public ObeliskMarker()
: base("O", "Obelisk marker")
{
}
public override string Title => "This marks the Supply pile randomly chosen by Obelisk";
}
public class Obelisk : Landmark
{
public Obelisk()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Setup()
{
base.Setup();
var actionPiles = _Game.Table.TableEntities.Where(te => te.Value.Category.HasFlag(Categories.Action)).ToList();
var chosenPile = actionPiles.Choose();
_Game.Table.TableEntities[chosenPile.Key].AddToken(new ObeliskMarker());
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new Obelisk());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
var iPointsList = collection as IList ?? collection.ToList();
var vps = base.ComputeVictoryPoints(player, iPointsList);
if (player == null)
return vps;
var obeliskPile = player._Game.Table.TableEntities.Values.FirstOrDefault(te => te.Tokens.Any(t => t is ObeliskMarker));
return vps + 2 * iPointsList.OfType().Count(c => obeliskPile != null && obeliskPile.Types.Contains(c.Type));
}
}
public class Orchard : Landmark
{
public Orchard()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new Orchard());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
var iPointsList = collection as IList ?? collection.ToList();
return base.ComputeVictoryPoints(player, iPointsList) +
4 * iPointsList.Where(c => c.Category.HasFlag(Categories.Action)).GroupBy(c => c.Type).Count(k => k.Count() >= 3);
}
}
public class Palace : Landmark
{
public Palace()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new Palace());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
var iPointsList = collection as IList ?? collection.ToList();
return base.ComputeVictoryPoints(player, iPointsList) +
3 * Math.Min(iPointsList.Count(c => c is Universal.Copper),
Math.Min(iPointsList.Count(c => c is Universal.Silver),
iPointsList.Count(c => c is Universal.Gold)));
}
}
public class Tomb : Landmark
{
private readonly Dictionary _trashedEventHandlers = new Dictionary();
public Tomb()
: base(Source.Empires, Traits.ReactToTrashing | Traits.Component | Traits.VPTokens)
{
}
public override void TearDown(IGame game)
{
base.TearDown(game);
foreach (var playerLoop in _trashedEventHandlers.Keys)
playerLoop.Trashed -= _trashedEventHandlers[playerLoop];
_trashedEventHandlers.Clear();
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
var enumPlayers = game.GetPlayersStartingWithActiveEnumerator();
while (enumPlayers.MoveNext())
{
_trashedEventHandlers[enumPlayers.Current] = new TrashedEventHandler(Player_Trashed);
enumPlayers.Current.Trashed += _trashedEventHandlers[enumPlayers.Current];
}
}
private void Player_Trashed(object sender, TrashEventArgs e)
{
var player = sender as IPlayer;
if (!e.HandledBy.Contains(TypeClass.Tomb))
e.Resolvers[TypeClass.Tomb] = new TrashResolver(player, this, "Gain 1", Player_Trashed, true);
}
internal void Player_Trashed(IPlayer player, ref TrashEventArgs e)
{
player.ReceiveBenefit(this, new CardBenefit { VictoryPoints = e.TrashedCards.Count(c => c.Category.HasFlag(Categories.Card)) });
e.HandledBy.Add(TypeClass.Tomb);
}
}
public class Tower : Landmark
{
public Tower()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new Tower());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
if (player == null)
return 0;
var emptyPiles = player._Game.Table.TableEntities.Values.Where(t => !t.Category.HasFlag(Categories.Shelter) && t.Count == 0);
var iPointsList = collection as IList ?? collection.ToList();
return base.ComputeVictoryPoints(player, iPointsList) +
iPointsList.OfType()
.Count(
c =>
!c.Category.HasFlag(Categories.Victory) && !c.Category.HasFlag(Categories.Shelter) &&
emptyPiles.Any(e => e.Types.Contains(c.Type)));
}
}
public class TriumphalArch : Landmark
{
public TriumphalArch()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new TriumphalArch());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
var iPointsList = collection as IList ?? collection.ToList();
var typeGroups = iPointsList.Where(c => c.Category.HasFlag(Categories.Action)).GroupBy(c => c.Type);
var actionType = typeGroups.OrderByDescending(g => g.Count()).Skip(1).FirstOrDefault();
return base.ComputeVictoryPoints(player, iPointsList) + 3 * (actionType?.Count() ?? 0);
}
}
public class Wall : Landmark
{
public Wall()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new Wall());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
var iPointsList = collection as IList ?? collection.ToList();
return base.ComputeVictoryPoints(player, iPointsList) - Math.Max(0, iPointsList.OfType().Count() - 15);
}
}
public class WolfDen : Landmark
{
public WolfDen()
: base(Source.Empires, Traits.VariableVPs)
{
}
public override void Finalize(IGame game)
{
Contract.Requires(game != null, "game cannot be null");
base.Finalize(game);
foreach (var player in game.Players)
player.EndgamePile.Add(new WolfDen());
}
public override int ComputeVictoryPoints(IPlayer player, IEnumerable collection)
{
var iPointsList = collection as IList ?? collection.ToList();
return base.ComputeVictoryPoints(player, iPointsList) - 3 * iPointsList.OfType().GroupBy(c => c.Type).Count(g => g.Count() == 1);
}
}
}