using DominionBase.Cards; using DominionBase.Enums; using DominionBase.Piles; using DominionBase.Players; using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Xml; namespace DominionBase { public class Table : IDisposable, ITable { private IGame _game; private List _tableEntitiesKeys; private readonly int _baseVictoryCards = 12; public Table(IGame game) { _game = game; } public Table(IGame game, int numPlayers) : this(game) { NumPlayers = numPlayers; var multiplier = NumPlayers >= 5 ? 2 : 1; Copper = new Supply(game, null, Cards.Universal.TypeClass.Copper, 60 * multiplier); Silver = new Supply(game, null, Cards.Universal.TypeClass.Silver, 40 * multiplier); Gold = new Supply(game, null, Cards.Universal.TypeClass.Gold, 30 * multiplier); var extraProvinceCards = 0; switch (NumPlayers) { case 1: case 2: _baseVictoryCards = 8; break; case 5: extraProvinceCards = 3; break; case 6: extraProvinceCards = 6; break; } Estate = new Supply(game, null, Cards.Universal.TypeClass.Estate, 3 * NumPlayers + _baseVictoryCards); Duchy = new Supply(game, null, Cards.Universal.TypeClass.Duchy, _baseVictoryCards); Province = new Supply(game, null, Cards.Universal.TypeClass.Province, _baseVictoryCards + extraProvinceCards); Curse = new Supply(game, null, Cards.Universal.TypeClass.Curse, 10 * Math.Max(NumPlayers - 1, 1)); } public void Clear() { foreach (var supply in TableEntities.Values.OfType()) supply.Clear(); TableEntities.Clear(); foreach (var supply in SpecialPiles.Values) supply.Clear(); SpecialPiles.Clear(); TokenPiles.Clear(); Trash.Clear(); _tableEntitiesKeys = null; #if DEBUG Trash.TestFireAllEvents(); #endif } #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. _game = null; TableEntities = null; SpecialPiles = null; TokenPiles = null; _tableEntitiesKeys = null; Trash = 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; } } ~Table() { Dispose(false); } #endregion public int NumPlayers { get; } public TokenCollections TokenPiles { get; private set; } = new TokenCollections(); public ISupply Copper { get { return (ISupply)TableEntities[Cards.Universal.TypeClass.Copper]; } private set { TableEntities[Cards.Universal.TypeClass.Copper] = value; } } public ISupply Silver { get { return (ISupply)TableEntities[Cards.Universal.TypeClass.Silver]; } private set { TableEntities[Cards.Universal.TypeClass.Silver] = value; } } public ISupply Gold { get { return (ISupply)TableEntities[Cards.Universal.TypeClass.Gold]; } private set { TableEntities[Cards.Universal.TypeClass.Gold] = value; } } public ISupply Potion { get { return (ISupply)TableEntities[Cards.Alchemy.TypeClass.Potion]; } private set { TableEntities[Cards.Alchemy.TypeClass.Potion] = value; } } public ISupply Estate { get { return (ISupply)TableEntities[Cards.Universal.TypeClass.Estate]; } private set { TableEntities[Cards.Universal.TypeClass.Estate] = value; } } public ISupply Duchy { get { return (ISupply)TableEntities[Cards.Universal.TypeClass.Duchy]; } private set { TableEntities[Cards.Universal.TypeClass.Duchy] = value; } } public ISupply Province { get { return (ISupply)TableEntities[Cards.Universal.TypeClass.Province]; } private set { TableEntities[Cards.Universal.TypeClass.Province] = value; } } public ISupply Curse { get { return (ISupply)TableEntities[Cards.Universal.TypeClass.Curse]; } private set { TableEntities[Cards.Universal.TypeClass.Curse] = value; } } public ISupply FindSupplyPileByType(Type type, bool includeSpecialPiles) { if (TableEntities.ContainsKey(type)) return (ISupply)TableEntities[type]; var sc = TableEntities.Values.OfType().Where(s => s.TopCard != null && s.TopCard.Type == type).ToList(); if (sc.Count == 1) return sc[0]; if (includeSpecialPiles) { if (SpecialPiles.ContainsKey(type)) return (ISupply)SpecialPiles[type]; sc = SpecialPiles.OfType().Where(s => s.TopCard != null && s.TopCard.Type == type).ToList(); if (sc.Count == 1) return sc[0]; } return null; } public ISupply FindSupplyPileByCard(ICardBase card) { if (TableEntities.ContainsKey(card)) return (ISupply)TableEntities[card]; var sc = TableEntities.Values.OfType().Where(s => s.TopCard != null && s.TopCard.Name == card.Name).ToList(); return sc.Count == 1 ? sc[0] : null; } public ITableable this[Type type] { get { if (TableEntities.ContainsKey(type)) return TableEntities[type]; foreach (var entity in TableEntities.Values) if (entity.Types.Contains(type)) return entity; if (SpecialPiles.ContainsKey(type)) return SpecialPiles[type]; foreach (var entity in SpecialPiles.Values) if (entity.Types.Contains(type)) return entity; return TableEntities[type]; } } public ITableable this[ICardBase card] => TableEntities[card]; public TableEntityCollection TableEntities { get; private set; } = new TableEntityCollection(); public List TableEntityKeysOrdered { get { if (_tableEntitiesKeys == null) { _tableEntitiesKeys = new List(TableEntities.Keys); _tableEntitiesKeys.Sort((t1, t2) => TableEntities[t1].Randomizer.CompareTo(TableEntities[t2].Randomizer)); } return _tableEntitiesKeys; } } public TableEntityCollection SpecialPiles { get; private set; } = new TableEntityCollection(); public Trash Trash { get; private set; } = new Trash(); public int EmptySupplyPiles { get { return TableEntities.Values.OfType().Count(s => s.Count == 0 && (s.Randomizer.Location == Location.General || s.Randomizer.Location == Location.Kingdom)); } } public ITableable GetOrAdd(IGame game, Type type) { Contract.Requires(game != null, "game cannot be null"); if (!TableEntities.ContainsKey(type)) AddTableItem(game.Players, type); return TableEntities[type]; } public void AddTableItem(PlayerCollection players, Type type) { Contract.Requires(type != null, "type cannot be null"); ITableable tableItem; if (type.IsSubclassOf(typeof(Card))) { // Minimum required -- Need to do this first to figure out what kind of card it is (silly, I know) tableItem = new Supply(_game, players, type, 8); if (tableItem.Category.HasFlag(Categories.Victory)) { // Victory supply piles should be 12 in 3+ player games if (NumPlayers > 2) ((ISupply)tableItem).AddTo(_baseVictoryCards - 8); } else // Not a Victory card, so there should be 10 { ((ISupply)tableItem).AddTo(2); } } else if (type.IsSubclassOf(typeof(Event))) { tableItem = Event.CreateInstance(type); ((Event)tableItem).Init(_game, players); } else if (type.IsSubclassOf(typeof(Landmark))) { tableItem = Landmark.CreateInstance(type); ((Landmark)tableItem).Init(_game, players); } else if (type.IsSubclassOf(typeof(Boon))) { tableItem = Boon.CreateInstance(type); ((Boon)tableItem).Init(_game, players); } else if (type.IsSubclassOf(typeof(Hex))) { tableItem = Hex.CreateInstance(type); ((Hex)tableItem).Init(_game, players); } else if (type.IsSubclassOf(typeof(State))) { tableItem = State.CreateInstance(type); ((State)tableItem).Init(_game, players); } else if (type.IsSubclassOf(typeof(Artifact))) { tableItem = Artifact.CreateInstance(type); ((Artifact)tableItem).Init(_game, players); } else if (type.IsSubclassOf(typeof(Project))) { tableItem = Project.CreateInstance(type); ((Project)tableItem).Init(_game, players); } else if (type.IsSubclassOf(typeof(Way))) { tableItem = Way.CreateInstance(type); ((Way)tableItem).Init(_game, players); } else throw new ArgumentException("Type must be a subclass of Card, Event, Landmark, Boon, Hex, State, Artifact, Project, or Way classes"); TableEntities[type] = tableItem; _tableEntitiesKeys = null; } public void Reset() { TableEntities.Reset(); } public void AddPlayer(Player player) { TableEntities.AddPlayer(player); SpecialPiles.AddPlayer(player); } internal void RemovePlayer(Player player) { TableEntities.RemovePlayer(player); SpecialPiles.RemovePlayer(player); } public void SetupSupplies(IGame game) { Contract.Requires(game != null, "game cannot be null"); if (game.Settings.ColonyPlatinumSelected == ColonyPlatinumSelected.Yes) { TableEntities[Cards.Prosperity.TypeClass.Platinum] = new Supply(game, null, Cards.Prosperity.TypeClass.Platinum, 12); TableEntities[Cards.Prosperity.TypeClass.Colony] = new Supply(game, null, Cards.Prosperity.TypeClass.Colony, _baseVictoryCards); } if (game.Settings.ShelterSelected == ShelterSelected.Yes) { Estate.Take(3 * game.Players.Count); TableEntities[Cards.DarkAges.TypeClass.Shelters] = new Supply(game, null, Cards.DarkAges.TypeClass.Shelters, 0); } TableEntities.Setup(); foreach (var supply in TableEntities.Values.Concat(SpecialPiles.Values).OfType()) { if (supply.CurrentCost.Potion.Value > 0 && !TableEntities.ContainsKey(Cards.Alchemy.TypeClass.Potion)) { Potion = new Supply(game, game.Players, Cards.Alchemy.TypeClass.Potion, 16); break; } if (supply.Types.Count() > 1) { foreach (var type in supply.Types) { var card = Card.CreateInstance(type); if (game.ComputeCost(card).Potion.Value > 0 && !TableEntities.ContainsKey(Cards.Alchemy.TypeClass.Potion)) { Potion = new Supply(game, game.Players, Cards.Alchemy.TypeClass.Potion, 16); break; } } } if (TableEntities.ContainsKey(Cards.Alchemy.TypeClass.Potion)) break; } } public void FinalizeSupplies(IGame game) { TableEntities.FinalizeSetup(game); } public void TearDown(IGame game) { TableEntities.TearDown(game); SpecialPiles.TearDown(game); TokenPiles.TearDown(game); Trash.TearDown(game); } public XmlNode GenerateXml(XmlDocument doc, string nodeName) { Contract.Requires(doc != null, "doc cannot be null"); var xeTable = doc.CreateElement(nodeName); xeTable.AppendChild(TableEntities.GenerateXml(doc, "supplies")); xeTable.AppendChild(SpecialPiles.GenerateXml(doc, "specials")); xeTable.AppendChild(TokenPiles.GenerateXml(doc, "tokenpiles")); xeTable.AppendChild(Trash.GenerateXml(doc)); return xeTable; } public void Load(XmlNode xnTable) { Contract.Requires(xnTable != null, "xnTable cannot be null"); TableEntities.Load(_game, xnTable.SelectSingleNode("supplies")); SpecialPiles.Load(_game, xnTable.SelectSingleNode("specials")); TokenPiles.Load(xnTable.SelectSingleNode("tokenpiles")); Trash.Load(xnTable); } } }