using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Text; using System.Xml; using DominionBase.Enums; using DominionBase.Piles; using DominionBase.Players; namespace DominionBase.Cards { /// /// This is the standard abstract class for defining an event 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 Event : IComparable, IBuyable, IRandomizable, IDisposable { public virtual event PileChangedEventHandler PileChanged; public virtual event BuyCheckEventHandler BuyCheck; public virtual event TokensChangedEventHandler TokensChanged; private IGame _Game; private string _ActionText = ""; private string _ExtraText = string.Empty; private readonly Traits _Traits = Traits.Basic; private Cost _Cost = new Cost(0); private Cost _LastComputedCost; protected bool _AsynchronousChanging { get; private set; } protected PileChangedEventArgs _AsynchronousPileChangedEventArgs { get; private set; } private CardBenefit _Benefit; internal Event(Source source, int coinCost, Traits traits, Edition edition = Edition.All) : this(source, new Cost(coinCost), traits, edition) { } internal Event(Source source, Cost cost, Traits traits, Edition edition = Edition.All) { Name = ResourcesHelper.Get($"{source}_{Type.Name}_Name"); Source = source; BaseCost = cost; _Benefit = new CardBenefit(); if (traits == Traits.None) _Traits = traits; else _Traits |= traits; Edition = edition; var text = ResourcesHelper.Get($"{source}_{Type.Name}_{Edition}_Text"); if (string.IsNullOrEmpty(text)) text = ResourcesHelper.Get($"{source}_{Type.Name}_Text"); Text = text; SetupText = ResourcesHelper.Get($"{source}_{Type.Name}_Setup"); } public void Init(IGame game, PlayerCollection players) { _Game = game; } public static Event CreateInstance(Type type) { return (Event)Activator.CreateInstance(type); } #region ICardBase variables, properties, & methods public virtual Cost BaseCost { get { return _Cost; } protected set { _Cost = value; } } public Type BaseType => GetType(); public virtual Categories Category { get; } = Categories.Event; public virtual bool HasCost => true; public virtual bool HasVPs => false; public virtual string SetupText { get; } = string.Empty; public virtual Source Source { get; } = Source.All; public string Text { get { var sb = new StringBuilder(); var sbBenefitText = new StringBuilder(); if (_Benefit.Any) { sbBenefitText.Append(_Benefit.Text); } if (!string.IsNullOrEmpty(_ActionText)) { if (sb.Length > 0 && !_ActionText.StartsWith("
")) { sb.AppendLine(); sb.AppendLine(); } sb.Append(_ActionText.Replace("", sbBenefitText.ToString())); } if (!string.IsNullOrEmpty(_ExtraText)) { sb.Append("
"); sb.AppendLine(); sb.Append(_ExtraText); } return sb.ToString(); } protected set { if (value == null) value = string.Empty; var strings = value.Split(new[] { "
" }, StringSplitOptions.None); if (strings.Length > 0) { _ActionText = strings[0].Replace("", Environment.NewLine); if (strings.Length > 1) _ExtraText = strings[1].Replace("", Environment.NewLine); } if (!_ActionText.Contains("")) _ActionText = $"{(string.IsNullOrEmpty(_ActionText) ? "" : Environment.NewLine)}{_ActionText}"; } } public virtual List GetSerializingTypes() { return new List(); } #endregion #region IDisposable variables, properties, & methods // Track whether Dispose has been called. private bool disposed; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. _Cost = null; _Benefit = 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; } } ~Event() { Dispose(false); } #endregion public virtual void CheckSetup(Preset preset, ITable table) { } public virtual void CheckSetup(Preset preset, string cardName, IRandomizable card) { } public virtual CardSettingCollection GenerateSettings() { return new CardSettingCollection(); } public virtual void FinalizeSettings(CardSettingCollection settings) { } public Guid UniqueId { get; } = Guid.NewGuid(); #region ITableable Properties public virtual bool CanUndo => true; public int Count => 1; public Orientation Orientation { get; } = Orientation.Landscape; public IDisplayable Randomizer => this; public Type TableableType => Type; public IEnumerable Types => new List { Type }; #endregion public Type Type => GetType(); public string Name { get; protected set; } public string ImageName => Type.Name; public IDisplayable RootCard => this; public IDisplayable DisplayCard => this; public ICost TopCard => this; public virtual Location Location { get; } = Location.LandscapeCard; public virtual Traits Traits => _Traits; public virtual string SpecialPresetKey => null; public virtual bool IsStackable => false; public virtual DisplayableCollection Stack() { return new DisplayableCollection { this }; } public CardBack CardBack { get; } = CardBack.Standard; public virtual CardBenefit Benefit => _Benefit; public Edition Edition { get; internal set; } #region IDisplayable public virtual Facing Facing { get; private set; } = Facing.FaceUp; #endregion public virtual void Clear() { } public virtual void End(IPlayer player, DisplayableCollection collection) { } public void AddPlayer(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); player.PhaseChanged += Player_PhaseChangedEvent; player.PlayerModeChanged += Player_PlayerModeChangedEvent; } public void RemovePlayer(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); player.PhaseChanged -= Player_PhaseChangedEvent; player.PlayerModeChanged -= Player_PlayerModeChangedEvent; } void Player_PhaseChangedEvent(object sender, PhaseChangedEventArgs e) { PhaseChanged(sender, e); } void Player_PlayerModeChangedEvent(object sender, PlayerModeChangedEventArgs e) { PlayerModeChanged(sender, e); } internal virtual void PhaseChanged(object sender, PhaseChangedEventArgs e) { } internal virtual void PlayerModeChanged(object sender, PlayerModeChangedEventArgs e) { } public TokenCollection Tokens { get; } = new TokenCollection(); public void AddToken(Token token) { Tokens.Add(token); if (TokensChanged != null) { var etcea = new TokensChangedEventArgs(token); TokensChanged(this, etcea); } var pcea = new PileChangedEventArgs(Operation.Refresh); if (_AsynchronousChanging) _AsynchronousPileChangedEventArgs = pcea; else PileChanged?.Invoke(this, pcea); } public Token RemoveToken(Type tokenType) { var foundToken = Tokens.FirstOrDefault(t => t.GetType() == tokenType); return foundToken != null ? RemoveToken(foundToken) : null; } public Token RemoveToken(Token token) { Tokens.Remove(token); if (TokensChanged != null) { var etcea = new TokensChangedEventArgs(token); TokensChanged(this, etcea); } var pcea = new PileChangedEventArgs(Operation.Refresh); if (_AsynchronousChanging) _AsynchronousPileChangedEventArgs = pcea; else PileChanged?.Invoke(this, pcea); return token; } public int CompareTo(Event eventCard) { Contract.Requires(eventCard != default(Event), "eventCard cannot be null"); if (ReferenceEquals(this, eventCard)) return 0; var nc = -BaseCost.CompareTo(eventCard.BaseCost); if (nc == 0) nc = string.Compare(Name, eventCard.Name, StringComparison.CurrentCulture); if (nc == 0) nc = UniqueId.CompareTo(eventCard.UniqueId); return nc; } public int CompareTo(IDisplayable obj) { Contract.Requires(obj != default(ITableable), "obj cannot be null"); if (ReferenceEquals(this, obj)) return 0; if (obj is Event oEvt) return CompareTo(oEvt); if (obj is Card || obj is ISupply) return 1; if (obj is Project || obj is Landmark || obj is State || obj is Boon || obj is Hex || obj is Artifact || obj is Way) return -1; var nc = string.Compare(Name, obj.Name, StringComparison.CurrentCulture); if (nc == 0) nc = UniqueId.CompareTo(obj.UniqueId); return nc; } public int CompareTo(ITableable obj) { Contract.Requires(obj != default(ITableable), "obj cannot be null"); if (obj is IDisplayable oDisp) return CompareTo(oDisp); return string.Compare(Name, obj.Name, StringComparison.CurrentCulture); } public override string ToString() { return Name; } public Cost CurrentCost { get { var currentCost = _Game.ComputeCost(this); if (_LastComputedCost == (Cost)null) _LastComputedCost = currentCost; if (_LastComputedCost != currentCost) { var pcea = new PileChangedEventArgs(Operation.Refresh); if (_AsynchronousChanging) _AsynchronousPileChangedEventArgs = pcea; else PileChanged?.Invoke(this, pcea); } _LastComputedCost = currentCost; return currentCost; } } public bool CanBuy(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); return CanBuy(player, player.Currency); } public virtual bool CanBuy(IPlayer player, Currency currency) { Contract.Requires(player != null, "player cannot be null"); Contract.Requires(currency != default(Currency), "currency cannot be null"); if (currency.Coin >= CurrentCost.Coin && currency.Potion >= CurrentCost.Potion && !player.TokenPiles[Empires.TypeClass.DebtToken].Any()) { if (BuyCheck != null) { var bcea = new BuyCheckEventArgs(player); BuyCheck(this, bcea); if (bcea.Cancelled) return false; } return true; } return false; } public virtual void Bought(IPlayer player) { Contract.Requires(player != null, "player cannot be null"); player.ReceiveBenefit(this, Benefit, true); } public virtual void TearDown(IGame game) { } public virtual void BeginChanges() { _AsynchronousChanging = true; _AsynchronousPileChangedEventArgs = null; } public virtual void EndChanges() { _AsynchronousChanging = false; if (_AsynchronousPileChangedEventArgs != null) PileChanged?.Invoke(this, _AsynchronousPileChangedEventArgs); _AsynchronousPileChangedEventArgs = null; } public virtual void Reset() { } public void FullSetup() { Setup(); SnapshotSetup(); FinalizeSetup(); } public virtual void Setup() { } public virtual void SnapshotSetup() { } public virtual void FinalizeSetup() { Finalize(_Game); } public virtual void Finalize(IGame game) { } public XmlNode GenerateXml(XmlDocument doc) { Contract.Requires(doc != null, "doc cannot be null"); var xeEvent = doc.CreateElement("event"); return xeEvent; } public static Event Load(IGame game, XmlNode xnEntity) { Contract.Requires(xnEntity != null, "xnEntity cannot be null"); var xnType = xnEntity.SelectSingleNode("type"); if (xnType == null) return null; var type = Type.GetType(xnType.InnerText); var @event = CreateInstance(type); @event.Load(xnEntity); return @event; } public void Load(XmlNode xnEntity) { if (xnEntity == null) return; var eventType = Type.GetType(xnEntity.Attributes["type"].Value); var @event = CreateInstance(eventType); //@event.LoadInstance(xnEntity); } } }