using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Text; using System.Xml; using DominionBase.Piles; using DominionBase.Players; using DominionBase.Enums; using DominionBase.Properties; namespace DominionBase.Cards { /// /// This is the standard abstract class for defining a way 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 Way : IComparable, IRandomizable, ITableable, IDisposable { public virtual event PileChangedEventHandler PileChanged; public virtual event TokensChangedEventHandler TokensChanged; internal IGame _Game; private string _ActionText = ""; private string _ExtraText = string.Empty; private readonly Traits _Traits = Traits.Basic; private CardBenefit _Benefit; protected bool _AsynchronousChanging { get; private set; } protected PileChangedEventArgs _AsynchronousPileChangedEventArgs { get; private set; } internal Way(Source source, Traits traits, Edition edition = Edition.All) { Name = ResourcesHelper.Get($"{source}_{Type.Name}_Name"); Source = source; _Benefit = new CardBenefit(); if (traits == Traits.None) _Traits = traits; else _Traits |= traits; Edition = edition; Text = ResourcesHelper.Get($"{source}_{Type.Name}_Text"); SetupText = ResourcesHelper.Get($"{source}_{Type.Name}_Setup"); } internal void Init(IGame game, PlayerCollection players) { _Game = game; } public static Way CreateInstance(Type type) { return (Way)Activator.CreateInstance(type); } #region ICardBase variables, properties, & methods public virtual Cost BaseCost { get; } = new Cost(); public Type BaseType => GetType(); public virtual Categories Category { get; } = Categories.Way; public virtual bool HasCost => false; 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. } // 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; } } ~Way() { 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 => false; 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 virtual CardBenefit Benefit => _Benefit; public IDisplayable RootCard => this; public IDisplayable DisplayCard => this; public virtual int VictoryPoints => 0; public virtual Location Location { get; } = Location.LandscapeCard; public virtual Traits Traits => _Traits; public virtual string SpecialPresetKey => null; public CardBack CardBack { get; } = CardBack.Standard; public virtual bool IsStackable => false; public virtual DisplayableCollection Stack() { return new DisplayableCollection { this }; } public virtual Facing Facing { get; private set; } = Facing.FaceUp; public virtual void Clear() { } public virtual void End(IPlayer player, DisplayableCollection collection) { } public virtual int ComputeVictoryPoints(IPlayer player, IEnumerable collection) { return 0; } public Edition Edition { get; } public virtual void PerformEndgameCalculations(IPlayer player, PointsCollection 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(Way wayCard) { Contract.Requires(wayCard != default(Way), "wayCard cannot be null"); if (ReferenceEquals(this, wayCard)) return 0; var nc = string.Compare(Name, wayCard.Name, StringComparison.CurrentCulture); if (nc == 0) nc = UniqueId.CompareTo(wayCard.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 Way oWay) return CompareTo(oWay); if (obj is Card || obj is ISupply || obj is Event || obj is Project || obj is State || obj is Boon || obj is Hex || obj is Artifact) 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 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) { Contract.Requires(game != null, "game cannot be null"); var enumPlayers = game.GetPlayersStartingWithActiveEnumerator(); while (enumPlayers.MoveNext()) { enumPlayers.Current.CardFollowingInstructions += Player_CardFollowingInstructions; } } protected virtual bool CardFollowingAllowed(CardFollowingInstructionsEventArgs e) { return true; } private void Player_CardFollowingInstructions(object sender, CardFollowingInstructionsEventArgs e) { var player = (IPlayer)sender; if (!CardFollowingAllowed(e) || e.Cancelled || e.HandledBy.Contains(this) || !e.Card.Category.HasFlag(Categories.Action) ) return; e.Resolvers[GetType()] = new CardFollowingInstructionsResolver( this, Resource.ResolveViaCard.Replace("{card}", Name), (IPlayer playerAction, ref CardFollowingInstructionsEventArgs eAction) => { playerAction._Game.SendMessage(playerAction, this, "PlayAs", this); eAction.Cancelled = true; FollowAlternateInstructions(playerAction, eAction.Card); eAction.HandledBy.Add(this); }, false); } public virtual void FollowAlternateInstructions(IPlayer player, Card playedCard) { Contract.Requires(player != default(IPlayer), "player cannot be null"); player.ReceiveBenefit(this, Benefit, true); } public XmlNode GenerateXml(XmlDocument doc) { Contract.Requires(doc != null, "doc cannot be null"); var xeWay = doc.CreateElement("way"); return xeWay; } public static Way 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 way = CreateInstance(type); way.Load(xnEntity); return way; } public void Load(XmlNode xnEntity) { if (xnEntity == null) return; var wayType = Type.GetType(xnEntity.Attributes["type"].Value); var way = CreateInstance(wayType); //way.LoadInstance(xnEntity); } } }