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);
}
}
}