using System; using System.Collections.Generic; using System.Linq; using System.Text; using DominionBase.Piles; using DominionBase.Players; namespace DominionBase.Cards { public class OwnerChangedEventArgs : EventArgs { public Player OldOwner; public Player NewOwner; public OwnerChangedEventArgs(Player oldOwner, Player newOwner) { this.OldOwner = oldOwner; this.NewOwner = newOwner; } } public enum CardBack { Standard, Red } /// /// This is the standard abstract class for defining a card and declaring what it is, what happens when it is played, and what benefits it may provide. /// See the standard constructor for more detailed information about basic setup. /// public abstract class Card : IComparable, ICard, IDisposable { public delegate void OwnerChangedEventHandler(object sender, OwnerChangedEventArgs e); public event OwnerChangedEventHandler OwnerChanged = null; private String _Name = String.Empty; private String _ActionText = ""; private String _ExtraText = String.Empty; private Category _Category = Category.Unknown; private Source _Source = Source.All; private Location _Location = Location.General; private Group _GroupMembership = Group.Basic; private Cost _BaseCost = new Cost(0); private CardBack _CardBack = CardBack.Standard; private CardBenefit _Benefit; private CardBenefit _DurationBenefit; private int _VictoryPoints = 0; private Card _ModifiedBy = null; private Dictionary _IsAttackBlocked = new Dictionary(); private Guid _UniqueId = Guid.NewGuid(); private Player _Owner = null; private Token.TokenActionEventHandler _TokenEventHandler = null; internal Card(String name, Category category, Source source, Location location) { _Name = name; _Category = category; _Source = source; _Location = location; Boolean isTreasure = (_Category & Category.Treasure) == Category.Treasure; _Benefit = new CardBenefit(isTreasure); _DurationBenefit = new CardBenefit(isTreasure); if ((category & Cards.Category.Attack) == Cards.Category.Attack) _GroupMembership |= Group.AffectOthers; Boolean isAction = (category & Cards.Category.Action) == Cards.Category.Action; Boolean isVictory = (category & Cards.Category.Victory) == Cards.Category.Victory; Boolean isReaction = (category & Cards.Category.Reaction) == Cards.Category.Reaction; Boolean isShelter = (category & Cards.Category.Shelter) == Cards.Category.Shelter; if (((isAction && isTreasure) || (isAction && isVictory) || (isAction && isShelter)) || ((isTreasure && isVictory) || (isTreasure && isReaction) || (isTreasure && isShelter)) || ((isVictory && isReaction) || (isVictory && isShelter)) || (isReaction && isShelter)) _GroupMembership |= Group.MultiType; } internal Card(String name, Category category, Source source, Location location, Group group) : this(name, category, source, location) { if (group == Group.None) _GroupMembership = group; else _GroupMembership |= group; } internal Card(String name, Category category, Source source, Location location, CardBack cardBack) : this(name, category, source, location) { _CardBack = cardBack; } internal Card(String name, Category category, Source source, Location location, Group group, CardBack cardBack) : this(name, category, source, location, group) { _CardBack = cardBack; } public static Card CreateInstance(Type type) { return (Card)type.GetConstructor(Type.EmptyTypes).Invoke(null); } public virtual List GetSerializingTypes() { return new List(); } public virtual void CheckSetup(Preset preset, Table table) { return; } public virtual void CheckSetup(Preset preset, Card card) { return; } public virtual CardSettingCollection GenerateSettings() { return new CardSettingCollection(); } public virtual void FinalizeSettings(CardSettingCollection settings) { return; } public void TestFireAllEvents() { if (OwnerChanged != null) OwnerChanged(this, new OwnerChangedEventArgs(null, null)); } internal virtual void TearDown() { this.Owner = null; } #region IDisposable variables, properties, & methods // Track whether Dispose has been called. private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. _BaseCost = null; _Benefit = null; _DurationBenefit = null; _ModifiedBy = null; _Owner = null; } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. // Note disposing has been done. disposed = true; } } ~Card() { Dispose(false); } #endregion public Guid UniqueId { get { return _UniqueId; } } public Type CardType { get { return this.GetType(); } } public virtual Type BaseType { get { return this.CardType; } } public virtual Boolean IsStackable { get { return true; } } public virtual String SpecialPresetKey { get { return null; } } public String Name { get { return _Name; } protected set { _Name = value; } } public String Text { get { StringBuilder sb = new StringBuilder(); StringBuilder sbBenefitText = new StringBuilder(); if (_Benefit.Any && _Benefit.Equals(_DurationBenefit)) { sbBenefitText.AppendLine("Now and at the start of your next turn:"); sbBenefitText.Append(_Benefit.Text); } else { if (_Benefit.Any || (_Category & Category.Treasure) == Category.Treasure) { sbBenefitText.Append(_Benefit.Text); } if (_DurationBenefit.Any) { if (sbBenefitText.Length > 0) { sbBenefitText.AppendLine(); sbBenefitText.AppendLine(); } sbBenefitText.AppendLine("At the start of your next turn:"); sbBenefitText.Append(_DurationBenefit.Text); } } if (!String.IsNullOrEmpty(_ActionText)) { if (sb.Length > 0 && !_ActionText.StartsWith("
")) { sb.AppendLine(); sb.AppendLine(); } sb.Append(_ActionText.Replace("", sbBenefitText.ToString())); } if (_VictoryPoints != 0 || ((this.Category & Cards.Category.Victory) == Cards.Category.Victory && (this.GroupMembership & Group.VariableVPs) != Group.VariableVPs)) { if (sb.Length > 0) { if ((this.Category & Cards.Category.Treasure) != Cards.Category.Treasure) sb.Append("
"); else { sb.AppendLine(); sb.AppendLine(); } } sb.Append(String.Format("{0}", _VictoryPoints)); } if (!String.IsNullOrEmpty(_ExtraText)) { sb.Append("
"); sb.AppendLine(); sb.Append(_ExtraText); } return sb.ToString(); } protected set { string[] strings = value.Split(new string[] { "
" }, StringSplitOptions.None); if (strings.Length > 0) { _ActionText = strings[0].Replace("", System.Environment.NewLine); if (strings.Length > 1) _ExtraText = strings[1].Replace("", System.Environment.NewLine); } if (!_ActionText.Contains("")) _ActionText = String.Format("{1}{0}", _ActionText, (String.IsNullOrEmpty(_ActionText) ? "" : System.Environment.NewLine)); } } public Category Category { get { return _Category; } } public Source Source { get { return _Source; } } public Location Location { get { return _Location; } } public Group GroupMembership { get { return _GroupMembership; } } public Cost BaseCost { get { return _BaseCost; } protected set { _BaseCost = value; } } public CardBack CardBack { get { return _CardBack; } } public CardBenefit Benefit { get { return _Benefit; } } public CardBenefit DurationBenefit { get { return _DurationBenefit; } } public int VictoryPoints { get { return _VictoryPoints; } protected set { _VictoryPoints = value; } } public Card ModifiedBy { get { return _ModifiedBy; } internal set { _ModifiedBy = value; } } protected Dictionary IsAttackBlocked { get { return _IsAttackBlocked; } } public Player Owner { get { return _Owner; } set { Player oldOwner = _Owner; _Owner = value; if (oldOwner != _Owner && OwnerChanged != null) { OwnerChangedEventArgs ocea = new OwnerChangedEventArgs(oldOwner, _Owner); OwnerChanged(this, ocea); } } } public virtual Boolean IsEndgameTriggered(Supply supply) { return false; } public virtual int GetVictoryPoints(IEnumerable cards) { return _VictoryPoints; } public virtual void ObtainedBy(Player player) { this.Owner = player; } public virtual void LostBy(Player player) { this.Owner = null; } public virtual void AddedTo(DeckLocation location, Player player) { if (location == DeckLocation.Hand) { if (player._Game.Table.Supplies.ContainsKey(this)) { if (player._Game.Table.Supplies[this].Tokens.Any(token => token.ActDefined)) { _TokenEventHandler = new Token.TokenActionEventHandler(token_TokenAction); player.TokenActedOn += _TokenEventHandler; } } } } private void token_TokenAction(object sender, TokenActionEventArgs e) { e.Actor._Game.Table.Supplies[this].Tokens.ForEach(delegate(Token t) { t.Act(this, e); }); } public virtual void AddedTo(Type deckType, Player player) { } public virtual void RemovedFrom(DeckLocation location, Player player) { if (_TokenEventHandler != null) player.TokenActedOn -= _TokenEventHandler; _TokenEventHandler = null; } public virtual void RemovedFrom(Type deckType, Player player) { } // Stub for Attacks, so they can get called during an existing attack internal virtual void player_Attacked(object sender, AttackedEventArgs e) { } public virtual void Setup(Game game, Supply supply) { } public virtual void AddedToSupply(Game game, Supply supply) { } /// /// Very basic card playing -- anything special needs to happen in the override for the specific card class /// /// Game this card is associated with public virtual void Play(Player player) { PlaySetup(player); PlayRest(player); } protected virtual void PlaySetup(Player player) { if (player.Phase == PhaseEnum.Action && (this.Category & Category.Action) != Category.Action) throw new Exception("Cannot play this card right now!"); if (player.Phase == PhaseEnum.Treasure && (this.Category & Category.Treasure) != Category.Treasure) throw new Exception("Cannot play this card right now!"); if (player.Phase == PhaseEnum.Buy || player.Phase == PhaseEnum.Waiting || player.Phase == PhaseEnum.Cleanup || player.Phase == PhaseEnum.Choosing || player.Phase == PhaseEnum.Endgame || player.Phase == PhaseEnum.Starting) throw new Exception("Cannot play cards right now!"); // This is an Attack card, so React-to-Attack cards must trigger before anything else if ((this.Category & Cards.Category.Attack) == Cards.Category.Attack) { // Check Attack play reactions IEnumerator enumerator = player._Game.GetPlayersStartingWithEnumerator(player); enumerator.MoveNext(); this.IsAttackBlocked[enumerator.Current] = false; while (enumerator.MoveNext()) this.IsAttackBlocked[enumerator.Current] = !enumerator.Current.AttackedBy(player, this); } } protected virtual void PlayRest(Player player) { player.ReceiveBenefit(this, this.Benefit, true); if ((this.Category & Category.Action) == Category.Action) player.ActionPlayed(); } public virtual void PlayFinished(Player player) { this.IsAttackBlocked.Clear(); } public virtual Boolean CanCleanUp { get { return true; } } public virtual void PlayDuration(Player player) { if (this.ModifiedBy != null) this.ModifiedBy.Modify(player, this); else GainDurationBenefits(player); } protected virtual void GainDurationBenefits(Player player) { player.ReceiveBenefit(this, this.DurationBenefit, true); } protected virtual void Modify(Player player, Card card) { card.GainDurationBenefits(player); } public override string ToString() { return Name; } /// /// Returns true if the card can be bought /// /// /// internal virtual Boolean CanBuy(Player player) { return CanGain(); } internal virtual Boolean CanGain() { return true; } internal virtual CardCollection Gaining(Game game, Supply supplyPile) { return new CardCollection(); } internal virtual CardCollection Buying(Game game, Supply supplyPile) { return new CardCollection(); } internal virtual void PhaseChanged(object sender, PhaseChangedEventArgs e) { } public int CompareTo(Card card) { if (ReferenceEquals(this, card)) return 0; else if (card.CardType == Cards.Prosperity.TypeClass.Colony) return 1; else if (this.CardType == Cards.Prosperity.TypeClass.Colony) return -1; else if (card.CardType == Cards.Universal.TypeClass.Province) return 1; else if (this.CardType == Cards.Universal.TypeClass.Province) return -1; else if (card.CardType == Cards.Universal.TypeClass.Duchy) return 1; else if (this.CardType == Cards.Universal.TypeClass.Duchy) return -1; else if (card.CardType == Cards.Universal.TypeClass.Estate) return 1; else if (this.CardType == Cards.Universal.TypeClass.Estate) return -1; else if (card.CardType == Cards.Universal.TypeClass.Curse) return 1; else if (this.CardType == Cards.Universal.TypeClass.Curse) return -1; else if (card.CardType == Cards.Prosperity.TypeClass.Platinum) return 1; else if (this.CardType == Cards.Prosperity.TypeClass.Platinum) return -1; else if (card.CardType == Cards.Universal.TypeClass.Gold) return 1; else if (this.CardType == Cards.Universal.TypeClass.Gold) return -1; else if (card.CardType == Cards.Alchemy.TypeClass.Potion) return 1; else if (this.CardType == Cards.Alchemy.TypeClass.Potion) return -1; else if (card.CardType == Cards.Universal.TypeClass.Silver) return 1; else if (this.CardType == Cards.Universal.TypeClass.Silver) return -1; else if (card.CardType == Cards.Universal.TypeClass.Copper) return 1; else if (this.CardType == Cards.Universal.TypeClass.Copper) return -1; else if (card.BaseCost.Coin.Value < this.BaseCost.Coin.Value) return -1; else if (card.BaseCost.Coin.Value > this.BaseCost.Coin.Value) return 1; else if (card.BaseCost.Potion.Value < this.BaseCost.Potion.Value) return -1; else if (card.BaseCost.Potion.Value > this.BaseCost.Potion.Value) return 1; else return this.Name.CompareTo(card.Name); } internal virtual void End(Player player, Deck deck) { } internal virtual void Bought(Player player) { } internal virtual void Gaining(Player player, ref DeckLocation location, ref DeckPosition position) { Receiving(player, ref location, ref position); } internal virtual void Gained(Player player) { ReceivedBy(player); } internal virtual void Receiving(Player player, ref DeckLocation location, ref DeckPosition position) { } internal virtual void ReceivedBy(Player player) { this.ObtainedBy(player); } internal virtual void TrashedBy(Player player) { this.LostBy(player); } public virtual CardCollection CardStack() { return new CardCollection() { this }; } } }