using DominionBase.Currencies; using DominionBase.Enums; using DominionBase.Piles; using DominionBase.Players; using DominionBase.Properties; using DominionBase.Utilities; using System; using System.Diagnostics.Contracts; using System.Linq; namespace DominionBase.Cards.DarkAges2ndEdition { public static class TypeClass { public static readonly Type Armory = typeof(Armory); public static readonly Type BandOfMisfits = typeof(BandOfMisfits); public static readonly Type Beggar = typeof(Beggar); public static readonly Type Catacombs = typeof(Catacombs); public static readonly Type Count = typeof(Count); public static readonly Type Cultist = typeof(Cultist); public static readonly Type DameAnna = typeof(DameAnna); public static readonly Type DameJosephine = typeof(DameJosephine); public static readonly Type DameMolly = typeof(DameMolly); public static readonly Type DameNatalie = typeof(DameNatalie); public static readonly Type DameSylvia = typeof(DameSylvia); public static readonly Type Feodum = typeof(Feodum); public static readonly Type Forager = typeof(Forager); public static readonly Type Graverobber = typeof(Graverobber); public static readonly Type Hermit = typeof(Hermit); public static readonly Type Ironmonger = typeof(Ironmonger); public static readonly Type Knights = typeof(Knights); public static readonly Type Madman = typeof(Madman); public static readonly Type MarketSquare = typeof(MarketSquare); public static readonly Type Mercenary = typeof(Mercenary); public static readonly Type Mystic = typeof(Mystic); public static readonly Type OvergrownEstate = typeof(OvergrownEstate); public static readonly Type Pillage = typeof(Pillage); public static readonly Type PoorHouse = typeof(PoorHouse); public static readonly Type Rats = typeof(Rats); public static readonly Type Rebuild = typeof(Rebuild); public static readonly Type Rogue = typeof(Rogue); public static readonly Type Scavenger = typeof(Scavenger); public static readonly Type Shelters = typeof(Shelters); public static readonly Type SirBailey = typeof(SirBailey); public static readonly Type SirDestry = typeof(SirDestry); public static readonly Type SirMartin = typeof(SirMartin); public static readonly Type SirMichael = typeof(SirMichael); public static readonly Type SirVander = typeof(SirVander); public static readonly Type Squire = typeof(Squire); public static readonly Type Storeroom = typeof(Storeroom); public static readonly Type Urchin = typeof(Urchin); public static readonly Type WanderingMinstrel = typeof(WanderingMinstrel); } public class Armory : DarkAges.Armory { public Armory() : base(Edition.Second) { } } public class BandOfMisfits : DarkAges.BandOfMisfits { public BandOfMisfits() : base(Edition.Second) { } } public class Beggar : DarkAges.Beggar { public Beggar() : base(Edition.Second) { } } public class Catacombs : DarkAges.Catacombs { public Catacombs() : base(Edition.Second) { } } public class Count : DarkAges.Count { public Count() : base(Edition.Second) { } } public class Cultist : DarkAges.Cultist { public Cultist() : base(Edition.Second) { } } public class DameAnna : DarkAges.DameAnna { public DameAnna() : base(Edition.Second) { } } public class DameJosephine : DarkAges.DameJosephine { public DameJosephine() : base(Edition.Second) { } } public class DameMolly : DarkAges.DameMolly { public DameMolly() : base(Edition.Second) { } } public class DameNatalie : DarkAges.DameNatalie { public DameNatalie() : base(Edition.Second) { } } public class DameSylvia : DarkAges.DameSylvia { public DameSylvia() : base(Edition.Second) { } } public class Feodum : DarkAges.Feodum { public Feodum() : base(Edition.Second) { } } public class Forager : DarkAges.Forager { public Forager() : base(Edition.Second) { } } public class Graverobber : DarkAges.Graverobber { public Graverobber() : base(Edition.Second) { } } public class Hermit : Card { private CardsDiscardingEventHandler _cardsDiscardingEventHandler; private bool _shouldBeTrashed; public Hermit() : base(Categories.Action, Source.DarkAges, Location.Kingdom, Traits.Gainer | Traits.IncludesExtraPiles | Traits.Terminal | Traits.Trasher | Traits.RemoveFromHand, edition: Edition.Second) { BaseCost = new Cost(3); OwnerChanged += new OwnerChangedEventHandler(Hermit_OwnerChanged); } public override void SetupSupply(IGame game, ISupply supply) { Contract.Requires(game != null, "game cannot be null"); base.SetupSupply(game, supply); Madman.SetupSupply(game); } public override void TearDown(IGame game) { Hermit_OwnerChanged(this, new OwnerChangedEventArgs(Owner, null)); base.TearDown(game); OwnerChanged -= new OwnerChangedEventHandler(Hermit_OwnerChanged); } private void Hermit_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) { var player = (Player)sender; // Only allow this if no cards were bought this turn // We set this up right now because this action happens regardless of whether or not the card is "lost track of" if (e.Cards.Contains(PhysicalCard) && player.CurrentTurn.CardsBought.Count == 0 && (e.FromLocation == DeckLocation.InPlay || e.FromLocation == DeckLocation.SetAside || e.FromLocation == DeckLocation.InPlayAndSetAside)) _shouldBeTrashed = true; if (e.GetResolver(Type) != null || !_shouldBeTrashed) return; e.AddResolver(Type, new CardsDiscardResolver(sender as IPlayer, this, $"Trash {PhysicalCard}", Player_Action, true)); } internal void Player_Action(IPlayer player, ref CardsDiscardEventArgs e) { e.Cards.Remove(PhysicalCard); if (player.InPlay.Contains(PhysicalCard)) player.Trash(this, player.RetrieveCardFrom(DeckLocation.InPlay, PhysicalCard)); else if (player.SetAside.Contains(PhysicalCard)) player.Trash(this, player.RetrieveCardFrom(DeckLocation.SetAside, PhysicalCard)); player.Gain((ISupply)player._Game.Table.SpecialPiles[TypeClass.Madman], this); e.HandledBy.Add(this); _shouldBeTrashed = false; } public override void FollowInstructions(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); _shouldBeTrashed = false; base.FollowInstructions(player); var discardPileCards = player.DiscardPile.LookThrough(c => true); player._Game.SendMessage(player, this, discardPileCards.ToArray()); var nonTreasures = player.DiscardPile.LookThrough(c => !c.Category.HasFlag(Categories.Treasure)); nonTreasures.Add(new Universal.Blank()); nonTreasures.AddRange(player.Hand[c => !c.Category.HasFlag(Categories.Treasure)]); if (nonTreasures.Count > 1) { var choiceTrash = new Choice("You may choose a non-Treasure card to trash", this, nonTreasures, ChoiceOutcome.Trash, player, minimum: 0); var resultTrash = player.MakeChoice(choiceTrash); if (resultTrash.Cards.Any()) { var cardToTrash = player.RetrieveCardFrom(player.Hand.Contains(resultTrash.Cards[0]) ? DeckLocation.Hand : DeckLocation.Discard, resultTrash.Cards[0]); player.Trash(this, cardToTrash); } } var gainableSupplies = new SupplyCollection(player._Game.Table.TableEntities.FindAll(supply => supply.CanGain() && supply.CurrentCost <= new Coin(3))); var choice = new Choice("Gain a card costing up to 3", this, gainableSupplies, ChoiceOutcome.Gain, player, false); var result = player.MakeChoice(choice); if (result.Supply != null) player.Gain(result.Supply, this); } } public class Ironmonger : DarkAges.Ironmonger { public Ironmonger() : base(Edition.Second) { } } public class Knights : Card { public Knights() : base(Categories.Action | Categories.Attack | Categories.Knight, Source.DarkAges, Location.Kingdom, Traits.Randomizer, edition: Edition.Second) { BaseCost = new Cost(5); } public override Visibility SupplyVisibility => Visibility.Top; 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 DameAnna(), new DameJosephine(), new DameMolly(), new DameNatalie(), new DameSylvia(), new SirBailey(), new SirDestry(), new SirMartin(), new SirMichael(), new SirVander() }; cards.Shuffle(); supply.AddTo(cards); } } public class Madman : DarkAges.Madman { public Madman() : base(Edition.Second) { } public static new void SetupSupply(IGame game) { Contract.Requires(game != null, "game cannot be null"); if (!game.Table.SpecialPiles.ContainsKey(TypeClass.Madman)) { var supply = new Supply(game, game.Players, TypeClass.Madman, BaseCount); supply.FullSetup(); game.Table.SpecialPiles.Add(TypeClass.Madman, supply); } } } public class MarketSquare : DarkAges.MarketSquare { public MarketSquare() : base(Edition.Second) { } } public class Mercenary : DarkAges.Mercenary { public Mercenary() : base(Edition.Second) { } public static new void SetupSupply(IGame game) { Contract.Requires(game != null, "game cannot be null"); if (!game.Table.SpecialPiles.ContainsKey(TypeClass.Mercenary)) { var supply = new Supply(game, game.Players, TypeClass.Mercenary, BaseCount); supply.FullSetup(); game.Table.SpecialPiles.Add(TypeClass.Mercenary, supply); } } } public class Mystic : DarkAges.Mystic { public Mystic() : base(Edition.Second) { } } public class OvergrownEstate : DarkAges.OvergrownEstate { public OvergrownEstate() : base(Edition.Second) { } } public class Pillage : DarkAges.Pillage { public Pillage() : this(Edition.Second) { } public Pillage(Edition edition) : base(edition) { } } public class PoorHouse : DarkAges.PoorHouse { public PoorHouse() : base(Edition.Second) { } } public class Rats : DarkAges.Rats { public Rats() : base(Edition.Second) { } } public class Rebuild : DarkAges.Rebuild { public Rebuild() : base(Edition.Second) { } } public class Rogue : DarkAges.Rogue { public Rogue() : base(Edition.Second) { } } public class Scavenger : DarkAges.Scavenger { public Scavenger() : base(Edition.Second) { } } public class Shelters : Card { public Shelters() : base(Categories.Shelter, Source.DarkAges, Location.Invisible, edition: Edition.Second) { } public override void SetupSupply(IGame game, ISupply supply) { Contract.Requires(game != null, "game cannot be null"); base.SetupSupply(game, supply); for (var i = 0; i < game.Players.Count; i++) { var card = new DarkAges.Hovel(); game.Table.Estate.AddTo(card); card.SetupCard(game); } for (var i = 0; i < game.Players.Count; i++) { var card = new DarkAges.Necropolis(); game.Table.Estate.AddTo(card); card.SetupCard(game); } for (var i = 0; i < game.Players.Count; i++) { var card = new OvergrownEstate(); game.Table.Estate.AddTo(card); card.SetupCard(game); } } } public class SirBailey : DarkAges.SirBailey { public SirBailey() : base(Edition.Second) { } } public class SirDestry : DarkAges.SirDestry { public SirDestry() : base(Edition.Second) { } } public class SirMartin : DarkAges.SirMartin { public SirMartin() : base(Edition.Second) { } } public class SirMichael : DarkAges.SirMichael { public SirMichael() : base(Edition.Second) { } } public class SirVander : DarkAges.SirVander { public SirVander() : base(Edition.Second) { } } public class Squire : DarkAges.Squire { public Squire() : base(Edition.Second) { } } public class Storeroom : DarkAges.Storeroom { public Storeroom() : base(Edition.Second) { } public override void FollowInstructions(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); base.FollowInstructions(player); var choiceDiscardTheFirst = new Choice("Discard any number of cards. Draw per card discarded", this, player.Hand, ChoiceOutcome.Discard, player, minimum: 0, maximum: player.Hand.Count); var resultDiscardTheFirst = player.MakeChoice(choiceDiscardTheFirst); player.Discard(DeckLocation.Hand, resultDiscardTheFirst.Cards); player.DrawHand(resultDiscardTheFirst.Cards.Count); var choiceDiscardTheSecond = new Choice("Discard any number of cards. +1 per card discarded.", this, player.Hand, ChoiceOutcome.Discard, player, minimum: 0, maximum: player.Hand.Count); var resultDiscardTheSecond = player.MakeChoice(choiceDiscardTheSecond); player.Discard(DeckLocation.Hand, resultDiscardTheSecond.Cards); player.ReceiveBenefit(this, new CardBenefit { Currency = new Currency(resultDiscardTheSecond.Cards.Count) }); } } public class Urchin : Card { private CardPutIntoPlayEventHandler _CardPutIntoPlayEventHandler; public Urchin() : base(Categories.Action | Categories.Attack, Source.DarkAges, Location.Kingdom, Traits.IncludesExtraPiles | Traits.PlusCard | Traits.PlusAction | Traits.Discard | Traits.Trasher | Traits.Gainer | Traits.Cantrip, edition: Edition.Second) { BaseCost = new Cost(3); Benefit.Cards = 1; Benefit.Actions = 1; } public override void SetupSupply(IGame game, ISupply supply) { Contract.Requires(game != null, "game cannot be null"); base.SetupSupply(game, supply); Mercenary.SetupSupply(game); } public override void AddedTo(DeckLocation location, IPlayer player) { Contract.Requires(player != null, "player cannot be null"); base.AddedTo(location, player); switch (location) { case DeckLocation.InPlay: case DeckLocation.SetAside: if (_CardPutIntoPlayEventHandler != null) player.CardPutIntoPlay -= _CardPutIntoPlayEventHandler; _CardPutIntoPlayEventHandler = new CardPutIntoPlayEventHandler(ActivePlayer_CardPutIntoPlay); player.CardPutIntoPlay += _CardPutIntoPlayEventHandler; break; } } 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; var choice = new Choice(Resource.DiscardDownTo4Cards, this, attackee.Hand, ChoiceOutcome.Discard, attackee, minimum: attackee.Hand.Count - 4, maximum: attackee.Hand.Count - 4); var result = attackee.MakeChoice(choice); attackee.Discard(DeckLocation.Hand, result.Cards); } } private void ActivePlayer_CardPutIntoPlay(object sender, CardPutIntoPlayEventArgs e) { if (e.Card != this && e.Card.Category.HasFlag(Categories.Attack)) { var choicePlayer = Choice.CreateYesNoChoice($"Do you want to trash {PhysicalCard}?", this, e.Player); var resultPlayer = e.Player.MakeChoice(choicePlayer); if (resultPlayer.Options[0] == Resource.Yes) { if (e.Player.InPlay.Contains(PhysicalCard)) e.Player.Trash(this, e.Player.RetrieveCardFrom(DeckLocation.InPlay, PhysicalCard)); else if (e.Player.SetAside.Contains(PhysicalCard)) e.Player.Trash(this, e.Player.RetrieveCardFrom(DeckLocation.SetAside, PhysicalCard)); else return; e.Player.Gain((ISupply)e.Player._Game.Table.SpecialPiles[TypeClass.Mercenary], this); } } } public override void RemovedFrom(DeckLocation location, IPlayer player) { Contract.Requires(player != null, "player cannot be null"); base.RemovedFrom(location, player); if (_CardPutIntoPlayEventHandler != null) player.CardPutIntoPlay -= _CardPutIntoPlayEventHandler; _CardPutIntoPlayEventHandler = null; } } public class WanderingMinstrel : DarkAges.WanderingMinstrel { public WanderingMinstrel() : base(Edition.Second) { } } }