using Dominion.NET_WPF.Controls;
using Dominion.NET_WPF.Models;
using Dominion.NET_WPF.ViewModel;
using DominionBase;
using DominionBase.Cards;
using DominionBase.Enums;
using DominionBase.Players;
using DominionBase.Players.PlayerMessages;
using DominionBase.Utilities;
using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using CtrlOrientation = System.Windows.Controls.Orientation;
using WinApplication = System.Windows.Application;
namespace Dominion.NET_WPF
{
internal delegate void player_ChooseDelegate(DominionBase.Players.Player player, Choice choice);
///
/// Interaction logic for wMain.xaml
///
public partial class WMain : Window
{
private static Settings _settings;
public static Settings Settings { get { return _settings; } private set { _settings = value; } }
public static IGame Game;
private DominionBase.Players.Player _player;
private VersionInfo _latestVersionInfo;
private readonly Dictionary _matEventHandlers = new Dictionary();
private Thread _gameThread;
public AutoResetEvent WaitEvent = new AutoResetEvent(false);
private Label _tradeRouteLabel;
private int _currentPlayDepth;
private bool _startingNewGame;
private Statistics _statistics;
private wCardPreview _wcp;
private MainViewModel Context => (MainViewModel)DataContext;
private int CurrentPlayDepth
{
get { return _currentPlayDepth; }
set
{
_currentPlayDepth = Math.Max(0, value);
}
}
private readonly Stack _playStack = new Stack();
public WMain()
{
//Thread.CurrentThread.CurrentUICulture = new CultureInfo("de");
//Thread.CurrentThread.CurrentCulture = new CultureInfo("de");
InitializeComponent();
if (!Directory.Exists(DominionBase.Utilities.Application.ApplicationPath))
Directory.CreateDirectory(DominionBase.Utilities.Application.ApplicationPath);
tiGamePoints.Visibility = Visibility.Collapsed;
InitPointsChart();
bTurnDone.IsEnabled = false;
bPlayTreasures.Text = "Play _Treasures";
bPlayTreasures.IsEnabled = false;
bDonePlayingTreasures.IsEnabled = false;
bSpendVillager.IsEnabled = false;
bSpendCoffers.IsEnabled = false;
bPayOffDebtTokens.IsEnabled = false;
bBuyPhase.IsEnabled = false;
bNightPhase.IsEnabled = false;
bUndo.IsEnabled = false;
SetGameBackground();
_statistics = Statistics.Load();
}
internal Control FindGameObject(IGameObject gameObject)
{
//stackPanelSupplyPiles.Children.OfType().SelectMany(sp => sp.Children.OfType()).Select(sc => sc.FindGameObject(gameObject)).FirstOrDefault(c => c != null);
return stackPanelSupplyPiles.Children.OfType().SelectMany(sp => sp.Children.OfType()).Select(sc => sc.FindGameObject(gameObject)).FirstOrDefault(c => c != null)
?? (from TabItem ti in tcAreas.Items select ti.Content as UcPlayerDisplay into display select display?.FindGameObject(gameObject)).FirstOrDefault(fe => fe != null);
}
private async void Window_Initialized(object sender, EventArgs e)
{
Settings = Settings.Load();
Context.Settings = Settings;
glMain.LogFile = Path.Combine(DominionBase.Utilities.Application.ApplicationPath, "game.log");
if (_settings.WindowSize.Width > 0)
Width = _settings.WindowSize.Width;
if (_settings.WindowSize.Height > 0)
Height = _settings.WindowSize.Height;
WindowState = _settings.WindowState;
#if DEBUG
await Task.Run(() => { });
#else
miCheckForUpdates.IsEnabled = false;
await Task.Run(() =>
{
try { CheckForUpdates(false); }
catch { }
});
miCheckForUpdates.IsEnabled = true;
#endif
}
private void SetGameBackground()
{
// Don't load brushes while updating
//if (WinApplication.Current.Properties["Update"] != null)
// return;
var bg = Settings.BackgroundBrush;
if (Settings.BackgroundBrush is ImageBrush image)
{
image.Stretch = Stretch.Fill;
image.TileMode = TileMode.Tile;
image.ViewportUnits = BrushMappingMode.Absolute;
image.Viewport = new Rect(0, 0, image.ImageSource.Height / 2, image.ImageSource.Width / 2);
}
gGame.Fill = bg;
foreach (var wp in dpMatsandPiles.Children.OfType())
{
foreach (var ccc in wp.Children.OfType())
{
ccc.Foreground = Settings.ForegroundBrush;
}
}
}
private void EnqueueGameMessageAndWait(GameMessage message)
{
var lockWasTaken = false;
var temp = Game.MessageRequestQueue;
try { Monitor.Enter(temp, ref lockWasTaken); { Game.MessageRequestQueue.Enqueue(message); } }
finally { if (lockWasTaken) Monitor.Exit(temp); }
Game.WaitEvent.Set();
// So... it turns out that this will completely hang the application if allowed to actually wait for the MessageResponseQueue to empty out.
// It's not great that this isn't working the way it's *supposed to* work, but that's an investigation for a different day. For now, it works
// as it works and it's unclear what the ramifications of that are.
//while (game.MessageResponseQueue.Count == 0)
//Thread.Sleep(100);
}
private void ReleaseEvents()
{
if (Game == null)
return;
Game.GameEndedEvent -= Game_GameEndedEvent;
Game.GameMessage -= Game_GameMessage;
if (Game.Table != null)
{
if (Game.Table.TokenPiles != null)
Game.Table.TokenPiles.TokenCollectionsChanged -= TokenPiles_TokenCollectionsChanged;
if (Game.Table.Trash != null)
Game.Table.Trash.PileChanged -= Trash_PileChanged;
foreach (var pileKey in Game.Table.SpecialPiles.Keys)
{
if (_matEventHandlers.ContainsKey(pileKey))
Game.Table.SpecialPiles[pileKey].PileChanged -= _matEventHandlers[pileKey];
if (pileKey == DominionBase.Cards.Nocturne.TypeClass.Boons)
((IBoonSupply)Game.Table.SpecialPiles[pileKey]).Shuffled += SpecialPile_Shuffle;
if (pileKey == DominionBase.Cards.Nocturne.TypeClass.Hexes)
((IHexSupply)Game.Table.SpecialPiles[pileKey]).Shuffled += SpecialPile_Shuffle;
}
}
if (Game.Players != null)
{
foreach (var player in Game.Players)
{
player.Choose = null;
player.Revealed.PileChanged -= Revealed_PileChanged;
player.SetAside.PileChanged -= SetAside_PileChanged;
player.BenefitReceivingInitiated -= Player_BenefitReceiving;
player.CardPlaying -= Player_CardPlaying;
player.CardPlayFinished -= Player_CardPlayed;
player.CardUndoPlaying -= Player_CardUndoPlaying;
player.CardUndoPlayed -= Player_CardUndoPlayed;
player.CardBuying -= Player_CardBuying;
player.CardBought -= Player_CardBought;
player.CardBuyFinished -= Player_CardBuyFinished;
player.CardGaining -= Player_CardGaining;
player.CardGainedInto -= Player_CardGainedInto;
player.CardGainFinished -= Player_CardGainFinished;
player.TokenPlaying -= Player_TokenPlaying;
player.TokenPlayed -= Player_TokenPlayed;
player.Trashing -= Player_Trashing;
player.TrashedFinished -= Player_Trashed;
player.Calling -= Player_Calling;
player.CalledFinished -= Player_Called;
player.PhaseChangedFinished -= Player_PhaseChangedEvent;
player.PlayerModeChanged -= Player_PlayerModeChangedEvent;
player.CardsDrawn -= Player_CardsDrawn;
player.TurnStarting -= Player_TurnStarting;
player.TurnEnded -= Player_TurnEnded;
player.ShufflingStart -= Player_ShufflingStart;
player.CardsAddedToDeck -= Player_CardsAddedToDeck;
player.CardsAddedToHand -= Player_CardsAddedToHand;
player.CardsDiscarded -= Player_CardsDiscarded;
player.PlayerMats.CardMatsChanged -= PlayerMats_DecksChanged;
player.TokenPiles.TokenCollectionsChanged -= PlayerTokenPiles_TokenCollectionsChanged;
player.BenefitsChanged -= Player_BenefitsChanged;
player.CardFlippedOver -= Player_CardFlippedOver;
player.TakeablesChanged -= Player_TakeablesChanged;
if (player == _player)
{
player.CardReceived -= Player_CardReceived;
}
}
}
}
private void ReleaseGame()
{
_player = null;
uccChooser.IsReady = false;
uccChooser.ClearAutoYields(AutoYieldDuration.Game);
cardTrash.Pile = new DisplayableCollection();
foreach (TabItem ti in tcAreas.Items)
{
if (!(ti.Content is UcPlayerDisplay display)) continue;
display.TearDown();
(((ti.Header as DockPanel)?.ToolTip as ToolTip)?.Content as UcPlayerOverview)?.TearDown();
}
foreach (var control in stackPanelSupplyPiles.Children.OfType().SelectMany(sp => sp.Children.OfType()))
control.Supply = null;
if (Game == null)
return;
ReleaseEvents();
Game.Clear();
Game = null;
GC.Collect();
GC.Collect(1);
GC.Collect(2);
GC.Collect(3);
GC.WaitForPendingFinalizers();
GC.Collect();
}
private void StartGame(GameSettings settings)
{
_startingNewGame = false;
ReleaseGame();
Settings = Settings.Load();
SetGameBackground();
// Clean out the Image Repository before starting a new game --
// so we don't allocate too much memory for cards we're not even using
Caching.ImageRepository.Reset();
tiGamePoints.Visibility = Visibility.Collapsed;
dpGameInfo.Visibility = Visibility.Visible;
glMain.TearDown();
glMain.Clear();
LayoutSupplyPiles();
while (tcAreas.Items.Count > 3)
tcAreas.Items.RemoveAt(tcAreas.Items.Count - 1);
dpMatsandPiles.Children.Clear();
dpGameStuff.Children.Clear();
_tradeRouteLabel = null;
// Try to force garbage collection to save some memory
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
try
{
Game = new Game(
_settings.NumberOfHumanPlayers,
_settings.PlayerSettings.Take(_settings.NumberOfPlayers).Select(ps => ps.Name),
_settings.PlayerSettings.Take(_settings.NumberOfPlayers).Select(ps => ps.AIClassType),
settings);
Game.SelectCards();
if (Game.Settings.Preset != null || Settings.AutomaticallyAcceptKingdomCards)
AcceptGame();
else
{
var selector = new WCardSelection { Owner = this };
if (selector.ShowDialog() == true)
{
Settings.AutomaticallyAcceptKingdomCards = selector.cbAutoAccept.IsChecked == true;
Settings.Save();
AcceptGame();
}
else
Game = null;
}
}
catch (DominionBase.Cards.ConstraintException ce)
{
wMessageBox.Show(ce.Message, "Constraint exception!", MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
catch (GameCreationException gce)
{
wMessageBox.Show(gce.Message, "Game creation exception!", MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
private void AcceptGame()
{
Game.AcceptCards();
Game.GameEndedEvent += Game_GameEndedEvent;
Settings.PlayerSettings.ForEach(ps => glMain.AddPlayerColor(ps.Name, ps.UIColor));
glMain.NewSection($"Game started with {Game.Players.Count} players");
Game.Table.TokenPiles.TokenCollectionsChanged += TokenPiles_TokenCollectionsChanged;
Game.Table.Trash.PileChanged += Trash_PileChanged;
Trash_PileChanged(Game.Table.Trash, new DominionBase.Piles.PileChangedEventArgs(Operation.Refresh));
Game.GameMessage += Game_GameMessage;
foreach (var player in Game.Players.FindAll(p => p.PlayerType == PlayerType.Human))
player.Choose = Player_Choose;
_player = Game.Players.Any(player => player.PlayerType == PlayerType.Human) ? Game.Players.OfType().First() : null;
var message = "Using the following cards";
if (Game.Settings.Preset != null)
message = $"{message} from the preset \"{Game.Settings.Preset.Name}\"";
glMain.Log($"{message}:");
var kingdomSupplies = Game.Table.TableEntities.Values.Where(s => s.Randomizer.Location == Location.Kingdom).OrderBy(s => s.Name);
glMain.Log(" ", kingdomSupplies);
var sidewaysSupplies = Game.Table.TableEntities.Values.Where(s => s.Randomizer.Location == Location.LandscapeCard).OrderBy(s => s.Name);
if (sidewaysSupplies.Any())
glMain.Log(" ", sidewaysSupplies);
var prosperityPiles = Game.Table.TableEntities.Count(kvp => kvp.Value.Location == Location.Kingdom && kvp.Value.Source == Source.Prosperity);
var darkAgesPiles = Game.Table.TableEntities.Count(kvp => kvp.Value.Location == Location.Kingdom && kvp.Value.Source == Source.DarkAges);
var kingdomPiles = Game.Table.TableEntities.Count(kvp => kvp.Value.Location == Location.Kingdom && kvp.Value.Tokens.Count(t => t is DominionBase.Cards.Cornucopia.BaneMarker) == 0);
glMain.Log(
$"Prosperity Kingdom card ratio is {prosperityPiles}/{kingdomPiles} = {(float)prosperityPiles / kingdomPiles:P0}");
glMain.Log(
$" Colony / Platinum {(Game.Settings.ColonyPlatinumSelected == ColonyPlatinumSelected.Yes ? "" : "not ")}selected");
glMain.Log(
$"Dark Ages Kingdom card ratio is {darkAgesPiles}/{kingdomPiles} = {(float)darkAgesPiles / kingdomPiles:P0}");
glMain.Log(
$" Shelters {(Game.Settings.ShelterSelected == ShelterSelected.Yes ? "" : "not ")}selected");
glMain.Log("Turn order is: ", string.Join(", ", Game.Players.Select(p => p == _player ? $"{p.Name} (You)" : p.Name)));
Type[] specialTypes = {
DominionBase.Cards.Promotional.TypeClass.BlackMarketSupply,
DominionBase.Cards.Cornucopia.TypeClass.PrizeSupply,
DominionBase.Cards.Cornucopia2ndEdition.TypeClass.PrizeSupply,
DominionBase.Cards.DarkAges.TypeClass.Madman,
DominionBase.Cards.DarkAges.TypeClass.Mercenary,
DominionBase.Cards.DarkAges.TypeClass.Spoils,
DominionBase.Cards.DarkAges2ndEdition.TypeClass.Madman,
DominionBase.Cards.DarkAges2ndEdition.TypeClass.Mercenary,
DominionBase.Cards.Adventures.TypeClass.TreasureHunter,
DominionBase.Cards.Adventures.TypeClass.Warrior,
DominionBase.Cards.Adventures.TypeClass.Hero,
DominionBase.Cards.Adventures.TypeClass.Champion,
DominionBase.Cards.Adventures.TypeClass.Soldier,
DominionBase.Cards.Adventures.TypeClass.Fugitive,
DominionBase.Cards.Adventures.TypeClass.Disciple,
DominionBase.Cards.Adventures.TypeClass.Teacher,
DominionBase.Cards.Adventures2ndEdition.TypeClass.TreasureHunter,
DominionBase.Cards.Adventures2ndEdition.TypeClass.Warrior,
DominionBase.Cards.Adventures2ndEdition.TypeClass.Hero,
DominionBase.Cards.Adventures2ndEdition.TypeClass.Champion,
DominionBase.Cards.Nocturne.TypeClass.Boons,
DominionBase.Cards.Nocturne.TypeClass.DruidBoons,
DominionBase.Cards.Nocturne.TypeClass.Hexes,
DominionBase.Cards.Nocturne.TypeClass.Bat,
DominionBase.Cards.Nocturne.TypeClass.Ghost,
DominionBase.Cards.Nocturne.TypeClass.Imp,
DominionBase.Cards.Nocturne.TypeClass.WillOWisp,
DominionBase.Cards.Nocturne.TypeClass.Wish,
DominionBase.Cards.Menagerie.TypeClass.Horse
};
foreach (var specialType in specialTypes)
{
if (!Game.Table.SpecialPiles.ContainsKey(specialType)) continue;
_matEventHandlers[specialType] = GamePile_PileChanged;
Game.Table.SpecialPiles[specialType].PileChanged += _matEventHandlers[specialType];
GamePile_PileChanged(Game.Table.SpecialPiles[specialType], new DominionBase.Piles.PileChangedEventArgs(Operation.Refresh));
}
if (Game.Table.SpecialPiles.ContainsKey(DominionBase.Cards.Nocturne.TypeClass.Boons))
((IBoonSupply)Game.Table.SpecialPiles[DominionBase.Cards.Nocturne.TypeClass.Boons]).Shuffled += SpecialPile_Shuffle;
if (Game.Table.SpecialPiles.ContainsKey(DominionBase.Cards.Nocturne.TypeClass.Hexes))
((IHexSupply)Game.Table.SpecialPiles[DominionBase.Cards.Nocturne.TypeClass.Hexes]).Shuffled += SpecialPile_Shuffle;
if (Game.Table.TableEntities.ContainsKey(DominionBase.Cards.Prosperity.TypeClass.TradeRoute)
|| Game.Table.TableEntities.ContainsKey(DominionBase.Cards.Prosperity2ndEdition.TypeClass.TradeRoute))
{
if (dpGameStuff.Children.Count > 0)
{
var bDiv = new Border
{
BorderThickness = new Thickness(2),
BorderBrush = Brushes.Black
};
Panel.SetZIndex(bDiv, 1);
DockPanel.SetDock(bDiv, Dock.Left);
dpGameStuff.Children.Add(bDiv);
}
var lTradeRoute = new Label
{
Content = "Trade Route Tokens:",
FontSize = 16d,
FontWeight = FontWeights.Bold,
HorizontalContentAlignment = HorizontalAlignment.Right,
Background = Caching.BrushRepository.GetBackgroundBrush(Categories.Treasure)
};
DockPanel.SetDock(lTradeRoute, Dock.Left);
dpGameStuff.Children.Add(lTradeRoute);
_tradeRouteLabel = new Label
{
Content = "0",
FontWeight = FontWeights.Bold,
VerticalAlignment = VerticalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Center,
Background = Caching.BrushRepository.GetBackgroundBrush(Categories.Treasure),
Padding = new Thickness(0, 0, 5, 0),
BorderThickness = new Thickness(0, 0, 1, 0)
};
DockPanel.SetDock(_tradeRouteLabel, Dock.Left);
dpGameStuff.Children.Add(_tradeRouteLabel);
}
bStuffDivider.Visibility = dpGameStuff.Children.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
foreach (var player in Game.Players)
{
var tiPlayer = new TabItem();
var dpHeader = new DockPanel();
var iHeader = new Image
{
Stretch = Stretch.None,
Margin = new Thickness(0, 0, 5, 0)
};
DockPanel.SetDock(iHeader, Dock.Left);
switch (player.PlayerType)
{
case PlayerType.Human:
iHeader.Source = (BitmapImage)Resources["imHuman"];
break;
case PlayerType.Computer:
iHeader.Source = (BitmapImage)Resources["imComputer"];
break;
}
dpHeader.Children.Add(iHeader);
var tbHeader = new TextBlock { Text = player.Name };
dpHeader.Children.Add(tbHeader);
var iFooter = new Image
{
Visibility = Visibility.Hidden,
Stretch = Stretch.None,
Margin = new Thickness(5, 0, 0, 0)
};
DockPanel.SetDock(iFooter, Dock.Right);
iFooter.Source = (BitmapImage)Resources["imEffects"];
iFooter.ToolTip = $"{player} has an Attack card in play";
dpHeader.Children.Add(iFooter);
tiPlayer.Header = dpHeader;
tcAreas.Items.Add(tiPlayer);
var ucpdPlayer = new UcPlayerDisplay();
tiPlayer.Content = ucpdPlayer;
ucpdPlayer.IsUIPlayer = player == _player;
ucpdPlayer.Player = player;
var playerSettings = _settings.PlayerSettings.FirstOrDefault(ps => ps.Name == player.Name);
if (playerSettings != null)
{
var hlsValue = HlsColor.RgbToHls(playerSettings.UIColor);
var cPlayer = HlsColor.HlsToRgb(hlsValue.H, hlsValue.L * 1.125, hlsValue.S * 0.95, hlsValue.A);
var gsc = new GradientStopCollection
{
new GradientStop(cPlayer, 0),
new GradientStop(playerSettings.UIColor, 0.25),
new GradientStop(playerSettings.UIColor, 0.75),
new GradientStop(cPlayer, 1)
};
gsc.Freeze();
tiPlayer.Background = new LinearGradientBrush(gsc, 0);
//tiPlayer.Background = new SolidColorBrush(playerSettings.UIColor);
ucpdPlayer.ColorFocus = playerSettings.UIColor;
ucpdPlayer.DisplayVictoryPoints = _settings.DisplayVictoryPoints;
//if (WinApplication.Current.Properties["Update"] == null)
ucpdPlayer.BackgroundFocus = playerSettings.BackgroundBrush as ImageBrush;
ucpdPlayer.ForegroundFocus = playerSettings.ForegroundBrush;
}
var tt = new ToolTip();
var ucpo = new UcPlayerOverview { Player = player };
tt.Content = ucpo;
ToolTipService.SetToolTip(dpHeader, tt);
if (Settings.ToolTipShowDuration == ToolTipShowDuration.Off)
ToolTipService.SetIsEnabled(dpHeader, false);
else
{
ToolTipService.SetIsEnabled(dpHeader, true);
ToolTipService.SetShowDuration(dpHeader, (int)Settings.ToolTipShowDuration);
}
dpHeader.MouseDown += TiPlayer_MouseDown;
dpHeader.MouseUp += TiPlayer_MouseUp;
player.Revealed.PileChanged += Revealed_PileChanged;
player.SetAside.PileChanged += SetAside_PileChanged;
player.BenefitReceivingInitiated += Player_BenefitReceiving;
//player.DiscardPile.PileChanged += DiscardPile_PileChanged;
player.CardPlaying += Player_CardPlaying;
player.CardPlayFinished += Player_CardPlayed;
player.CardUndoPlaying += Player_CardUndoPlaying;
player.CardUndoPlayed += Player_CardUndoPlayed;
player.CardBuying += Player_CardBuying;
player.CardBought += Player_CardBought;
player.CardBuyFinished += Player_CardBuyFinished;
player.CardGaining += Player_CardGaining;
player.CardGainedInto += Player_CardGainedInto;
player.CardGainFinished += Player_CardGainFinished;
player.TokenPlaying += Player_TokenPlaying;
player.TokenPlayed += Player_TokenPlayed;
player.Trashing += Player_Trashing;
player.TrashedFinished += Player_Trashed;
player.Calling += Player_Calling;
player.CalledFinished += Player_Called;
player.PhaseChangedFinished += Player_PhaseChangedEvent;
player.PlayerModeChanged += Player_PlayerModeChangedEvent;
player.CardsDrawn += Player_CardsDrawn;
player.TurnStarting += Player_TurnStarting;
player.TurnEnded += Player_TurnEnded;
player.ShufflingStart += Player_ShufflingStart;
player.CardsAddedToDeck += Player_CardsAddedToDeck;
player.CardsAddedToHand += Player_CardsAddedToHand;
player.CardsDiscarded += Player_CardsDiscarded;
player.PlayerMats.CardMatsChanged += PlayerMats_DecksChanged;
player.TokenPiles.TokenCollectionsChanged += PlayerTokenPiles_TokenCollectionsChanged;
player.BenefitsChanged += Player_BenefitsChanged;
player.CardFlippedOver += Player_CardFlippedOver;
player.TakeablesChanged += Player_TakeablesChanged;
if (player == _player)
{
tcAreas.SelectedItem = tiPlayer;
player.CardReceived += Player_CardReceived;
}
}
Game.FinalizeSetup();
LayoutSupplyPiles();
miNewGame.IsEnabled = false;
miLoadGame.IsEnabled = false;
miEndGame.IsEnabled = true;
miSaveGame.IsEnabled = false;
_gameThread = new Thread(Game.StartAsync);
_gameThread.Start();
UpdateDisplay();
}
private void LayoutSupplyPiles()
{
stackPanelSupplyPiles.Children.Clear();
ucGameLog gameLog = null;
if (tiGameLog.Visibility == Visibility.Visible)
{
gameLog = tiGameLog.Content as ucGameLog;
tiGameLog.Content = null;
tiGameLog.Visibility = Visibility.Collapsed;
}
else if (dpGameInfo.Children.OfType().FirstOrDefault() != null)
{
gameLog = dpGameInfo.Children.OfType().FirstOrDefault();
dpGameInfo.Children.Remove(gameLog);
}
switch (_settings.GameLogLocation)
{
case GameLogLocation.InCommonArea:
dpGameInfo.Children.Add(gameLog);
break;
case GameLogLocation.InGameTabArea:
tiGameLog.Visibility = Visibility.Visible;
tiGameLog.Content = gameLog;
break;
}
if (Game?.Table == null)
return;
var pilesPerColumn = 0;
switch (_settings.LayoutStyle)
{
case LayoutStyle.Supply2Columns:
pilesPerColumn = Math.Max(
Game.Table.TableEntities.Count(skv => skv.Value.Location == Location.Kingdom),
Game.Table.TableEntities.Count(skv => skv.Value.Location == Location.General || skv.Value.Location == Location.LandscapeCard));
break;
case LayoutStyle.Supply4Columns:
pilesPerColumn = Math.Max(
(Game.Table.TableEntities.Count(skv => skv.Value.Location == Location.Kingdom) + 1) / 2,
(Game.Table.TableEntities.Count(skv => skv.Value.Location == Location.General || skv.Value.Location == Location.LandscapeCard) + 1) / 2);
break;
}
var spFirstAction = new StackPanel
{
FlowDirection = FlowDirection.LeftToRight,
HorizontalAlignment = HorizontalAlignment.Stretch,
Margin = new Thickness(0, 0, 4, 0)
};
stackPanelSupplyPiles.Children.Add(spFirstAction);
var borderSupply = new Border
{
BorderThickness = new Thickness(1),
BorderBrush = Brushes.DarkSlateBlue
};
stackPanelSupplyPiles.Children.Add(borderSupply);
var spFirstGeneral = new StackPanel
{
FlowDirection = FlowDirection.LeftToRight,
HorizontalAlignment = HorizontalAlignment.Stretch,
Margin = new Thickness(0, 0, 4, 0)
};
stackPanelSupplyPiles.Children.Add(spFirstGeneral);
var spCurrentAction = spFirstAction;
var spCurrentGeneral = spFirstGeneral;
foreach (var supplyType in Game.Table.TableEntityKeysOrdered)
{
StackPanel sp;
switch (Game.Table.TableEntities[supplyType].Location)
{
case Location.General:
case Location.LandscapeCard:
sp = spCurrentGeneral;
if (_settings.LayoutStyle == LayoutStyle.Supply4Columns &&
sp.Children.Count > 0 &&
sp.Children.OfType().Last().Supply.Category.HasFlag(Categories.Curse))
{
sp = new StackPanel
{
FlowDirection = FlowDirection.LeftToRight,
HorizontalAlignment = HorizontalAlignment.Stretch,
Margin = new Thickness(0, 0, 4, 0)
};
borderSupply = new Border
{
BorderThickness = new Thickness(1),
BorderBrush = Brushes.DarkSlateBlue
};
stackPanelSupplyPiles.Children.Add(borderSupply);
stackPanelSupplyPiles.Children.Add(sp);
spCurrentGeneral = sp;
}
break;
case Location.Kingdom:
sp = spCurrentAction;
if (sp.Children.OfType().Count() >= pilesPerColumn)
{
sp = new StackPanel
{
FlowDirection = FlowDirection.LeftToRight,
HorizontalAlignment = HorizontalAlignment.Stretch,
Margin = new Thickness(0, 0, 4, 0)
};
borderSupply = new Border
{
BorderThickness = new Thickness(1),
BorderBrush = Brushes.DarkSlateBlue
};
stackPanelSupplyPiles.Children.Insert(stackPanelSupplyPiles.Children.IndexOf(spFirstGeneral), sp);
stackPanelSupplyPiles.Children.Insert(stackPanelSupplyPiles.Children.IndexOf(spFirstGeneral), borderSupply);
spCurrentAction = sp;
}
break;
default:
continue;
}
var newSC = new SupplyControl();
sp.Children.Add(newSC);
var previousSCIndex = sp.Children.Count - 1;
while (previousSCIndex >= 0 && !(sp.Children[previousSCIndex--] is SupplyControl)) ;
newSC.HorizontalAlignment = HorizontalAlignment.Stretch;
newSC.Width = sp.Width;
newSC.Supply = Game.Table.TableEntities[supplyType];
if (previousSCIndex >= 0)
{
if ((Game.Table.TableEntities[supplyType].Location == Location.General && ((SupplyControl)sp.Children[previousSCIndex]).Supply.Category != newSC.Supply.Category) ||
(Game.Table.TableEntities[supplyType].Location == Location.Kingdom && ((DominionBase.Piles.Supply)((SupplyControl)sp.Children[previousSCIndex]).Supply).Randomizer.BaseCost != ((DominionBase.Piles.Supply)newSC.Supply).Randomizer.BaseCost) ||
(Game.Table.TableEntities[supplyType].Location == Location.LandscapeCard && ((SupplyControl)sp.Children[previousSCIndex]).Supply.Location != newSC.Supply.Location)
)
{
borderSupply = new Border
{
Margin = new Thickness(15, 0, 15, 0),
BorderThickness = new Thickness(1),
BorderBrush = Brushes.LightSkyBlue
};
sp.Children.Insert(sp.Children.Count - 1, borderSupply);
}
}
}
CheckBuyable(Game.ActivePlayer);
stackPanelSupplyPiles.InvalidateVisual();
rdGrid0.Height = new GridLength(stackPanelSupplyPiles.ActualHeight + 5);
rdGrid0.Height = GridLength.Auto;
}
private void TiPlayer_MouseDown(object sender, MouseButtonEventArgs e)
{
if (Settings != null && Settings.ShowToolTipOnRightClick && e.ChangedButton == MouseButton.Right && e.ButtonState == MouseButtonState.Pressed)
{
(sender as UIElement)?.CaptureMouse();
var element = sender as FrameworkElement;
var tt = element?.ToolTip as ToolTip;
if (tt?.Content is UcPlayerOverview ucpo)
ucpo.Turn = Game.TurnsTaken.LastOrDefault(t => t.Player == ((element.Parent as ContentControl)?.Content as UcPlayerDisplay)?.Player && t != Game.CurrentTurn);
if (tt != null) tt.IsOpen = true;
}
}
private static void TiPlayer_MouseUp(object sender, MouseButtonEventArgs e)
{
if (Settings != null && Settings.ShowToolTipOnRightClick && e.ChangedButton == MouseButton.Right && e.ButtonState == MouseButtonState.Released)
{
(sender as UIElement)?.ReleaseMouseCapture();
if ((sender as FrameworkElement)?.ToolTip is ToolTip tt) tt.IsOpen = false;
}
}
private void Game_GameMessage(object sender, GameMessageEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (e.SourceCard != null)
{
var type = e.SourceCard.Type;
if ((type == DominionBase.Cards.Base.TypeClass.Chancellor
|| type == DominionBase.Cards.Cornucopia.TypeClass.TrustySteed
|| type == DominionBase.Cards.Cornucopia2ndEdition.TypeClass.TrustySteed
|| type == DominionBase.Cards.DarkAges.TypeClass.Scavenger
|| type == DominionBase.Cards.DarkAges2ndEdition.TypeClass.Scavenger
|| type == DominionBase.Cards.Adventures.TypeClass.Messenger
|| (type == DominionBase.Cards.Nocturne.TypeClass.BadOmens && e.MessageType == "Discard"))
&& !e.Cards.Any())
{
if (e.Player == _player)
{
glMain.Log(e.Player, "You put your deck into your discard pile");
}
else
{
glMain.Log(e.Player, e.Player, " puts deck into discard pile");
}
}
else if (type == DominionBase.Cards.Intrigue.TypeClass.Bridge
|| type == DominionBase.Cards.Intrigue2ndEdition.TypeClass.Bridge
|| type == DominionBase.Cards.Prosperity.TypeClass.Quarry
|| type == DominionBase.Cards.Cornucopia.TypeClass.Princess
|| type == DominionBase.Cards.Hinterlands.TypeClass.Highway
|| type == DominionBase.Cards.Adventures.TypeClass.BridgeTroll
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.BridgeTroll
|| type == DominionBase.Cards.Renaissance.TypeClass.Inventor
|| type == DominionBase.Cards.Renaissance.TypeClass.Canal
)
{
var types = string.Empty;
if (type == DominionBase.Cards.Prosperity.TypeClass.Quarry)
types = "Action ";
glMain.Log(e.Player, e.SourceCard, " reduces the cost of all ", types, "cards by ", new DominionBase.Currencies.Coin(e.Count));
}
else if (type == DominionBase.Cards.Intrigue.TypeClass.Masquerade ||
type == DominionBase.Cards.Intrigue2ndEdition.TypeClass.Masquerade)
{
string postText = $" to the left ({e.AffectedPlayer})";
if (e.Player == _player)
glMain.Log(e.Player, "You pass ", e.Cards[0], postText);
else
glMain.Log(e.Player, e.Player, " passes a card", postText);
}
else if ((type == DominionBase.Cards.Intrigue.TypeClass.WishingWell
|| type == DominionBase.Cards.Intrigue2ndEdition.TypeClass.WishingWell
|| type == DominionBase.Cards.DarkAges.TypeClass.Mystic
|| type == DominionBase.Cards.DarkAges.TypeClass.Rebuild
|| type == DominionBase.Cards.DarkAges2ndEdition.TypeClass.Mystic
|| type == DominionBase.Cards.DarkAges2ndEdition.TypeClass.Rebuild
|| type == DominionBase.Cards.Guilds.TypeClass.Doctor
|| type == DominionBase.Cards.Guilds.TypeClass.Journeyman
|| type == DominionBase.Cards.Guilds2ndEdition.TypeClass.Doctor
|| type == DominionBase.Cards.Guilds2ndEdition.TypeClass.Journeyman
) && e.Cards.Any()
)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" name{(e.Player == _player ? "" : "s")} ",
e.Cards[0]);
}
else if (type == DominionBase.Cards.Seaside.TypeClass.Ambassador
|| type == DominionBase.Cards.Seaside2ndEdition.TypeClass.Ambassador
)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" return{(e.Player == _player ? "" : "s")} {StringUtility.Plural("card", e.Cards.Count)} to the ",
e.Cards.FirstOrDefault(),
" supply pile");
}
else if (type == DominionBase.Cards.Seaside.TypeClass.Embargo
|| type == DominionBase.Cards.Seaside2ndEdition.TypeClass.Embargo
//|| type == DominionBase.Cards.Seaside2019Errata.TypeClass.Embargo
)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" put{(e.Player == _player ? "" : "s")} {e.SourceCard.Name} token on ",
e.Cards.FirstOrDefault());
}
else if (type == DominionBase.Cards.Empires.TypeClass.Tax)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" put{(e.Player == _player ? "" : "s")} 2 Debt tokens on ",
e.Cards.FirstOrDefault());
}
else if (type == DominionBase.Cards.Seaside.TypeClass.Haven
|| type == DominionBase.Cards.Seaside2ndEdition.TypeClass.Haven
|| type == DominionBase.Cards.Adventures.TypeClass.Gear
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Gear
|| type == DominionBase.Cards.Empires.TypeClass.Archive
|| type == DominionBase.Cards.Nocturne.TypeClass.Crypt
|| type == DominionBase.Cards.Renaissance.TypeClass.Research
)
{
if (e.Player == _player)
glMain.Log(e.Player, "You set aside ", e.Cards.Any() ? e.Cards : (object)"nothing");
else
glMain.Log(e.Player, e.Player, " sets aside ", StringUtility.Plural("card", e.Cards.Count, true));
}
else if (type == DominionBase.Cards.Promotional.TypeClass.Prince
|| type == DominionBase.Cards.Nocturne.TypeClass.Ghost
|| type == DominionBase.Cards.Renaissance.TypeClass.CargoShip
)
{
if (e.MessageType == "SetAside")
{
if (e.Player == _player)
glMain.Log(e.Player, "You set aside ", e.Cards.FirstOrDefault(), " on ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, " sets aside ", e.Cards, " on ", e.SourceCard);
}
else if (e.MessageType == "Retrieve")
{
if (e.Player == _player)
glMain.Log(e.Player, "You retrieve ", e.Cards.FirstOrDefault(), " from ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, " retrieves ", e.Cards, " from ", e.SourceCard);
}
}
else if (type == DominionBase.Cards.Adventures.TypeClass.Save
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Save
)
{
if (e.MessageType == "SetAside")
{
if (e.Player == _player)
glMain.Log(e.Player, "You set aside ", e.Cards.FirstOrDefault(), " on ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, " sets aside ", StringUtility.Plural("card", e.Cards.Count), " on ", e.SourceCard);
}
else if (e.MessageType == "Retrieve")
{
if (e.Player == _player)
glMain.Log(e.Player, "You retrieve ", e.Cards.FirstOrDefault(), " from ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, " retrieves ", StringUtility.Plural("card", e.Cards.Count), " from ", e.SourceCard);
}
}
//else if (type == DominionBase.Cards.Adventures.TypeClass.Inheritance)
//{
// if (e.Count == 1)
// {
// if (e.Player == _Player)
// glMain.Log(e.Player, "You set aside ", e.Cards.FirstOrDefault());
// else
// glMain.Log(e.Player, e.Player, " sets aside ", e.Cards.FirstOrDefault());
// }
// else
// {
// if (e.Player == _Player)
// glMain.Log(e.Player, "You retrieve ", e.Cards.FirstOrDefault());
// else
// glMain.Log(e.Player, e.Player, " retrieves ", e.Cards.FirstOrDefault());
// }
//}
else if (type == DominionBase.Cards.Empires.TypeClass.Encampment
|| type == DominionBase.Cards.Nocturne.TypeClass.FaithfulHound)
{
var targetCard = e.Cards.FirstOrDefault() ?? e.SourceCard;
switch (e.MessageType)
{
case "SetAside":
if (e.Player == _player)
glMain.Log(e.Player, "You set aside ", targetCard);
else
glMain.Log(e.Player, e.Player, " sets aside ", targetCard);
break;
case "Return":
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" return{(e.Player == _player ? "" : "s")} {StringUtility.Plural("card", e.Cards.Count)} to the ",
targetCard,
" supply pile");
break;
case "PutIntoHand":
if (e.Player == _player)
glMain.Log(e.Player, "You put ", targetCard, " into your hand");
else
glMain.Log(e.Player, e.Player, " puts ", targetCard, " into hand");
break;
}
}
else if (type == DominionBase.Cards.Seaside.TypeClass.Lighthouse
|| type == DominionBase.Cards.Seaside2ndEdition.TypeClass.Lighthouse
|| type == DominionBase.Cards.Adventures.TypeClass.Champion
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Champion
|| type == DominionBase.Cards.Nocturne.TypeClass.Guardian
)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"Your" : (object)e.Player,
e.Player == _player ? " " : "'s ",
((Card)e.SourceCard).PhysicalCard,
" provides immunity to the attack.");
}
else if (type == DominionBase.Cards.Prosperity.TypeClass.Contraband
|| type == DominionBase.Cards.DarkAges.TypeClass.BandOfMisfits
|| type == DominionBase.Cards.DarkAges2ndEdition.TypeClass.BandOfMisfits
//|| type == DominionBase.Cards.DarkAges2019Errata.TypeClass.BandOfMisfits
|| type == DominionBase.Cards.Empires.TypeClass.Overlord
//|| type == DominionBase.Cards.Empires2019Errata.TypeClass.Overlord
)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" name{(e.Player == _player ? "" : "s")} ",
e.Cards.Any() ? (object)e.Cards[0] : (object)"nothing");
}
else if (type == DominionBase.Cards.Hinterlands.TypeClass.Trader
|| type == DominionBase.Cards.Hinterlands2ndEdition.TypeClass.Trader
)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" gain{(e.Player == _player ? "" : "s")} ",
e.Cards[1],
" instead of ",
e.Cards[0]);
}
else if (type == DominionBase.Cards.Guilds.TypeClass.Butcher
|| type == DominionBase.Cards.Guilds2ndEdition.TypeClass.Butcher
)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" spend{(e.Player == _player ? "" : "s")} {StringUtility.Plural("Coffer", e.Count)}");
}
else if (type == DominionBase.Cards.Adventures.TypeClass.Page
|| type == DominionBase.Cards.Adventures.TypeClass.TreasureHunter
|| type == DominionBase.Cards.Adventures.TypeClass.Warrior
|| type == DominionBase.Cards.Adventures.TypeClass.Hero
|| type == DominionBase.Cards.Adventures.TypeClass.Peasant
|| type == DominionBase.Cards.Adventures.TypeClass.Soldier
|| type == DominionBase.Cards.Adventures.TypeClass.Fugitive
|| type == DominionBase.Cards.Adventures.TypeClass.Disciple
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Page
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.TreasureHunter
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Warrior
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Hero
|| type == DominionBase.Cards.Nocturne.TypeClass.Bat
|| type == DominionBase.Cards.Nocturne.TypeClass.Changeling
|| type == DominionBase.Cards.Nocturne.TypeClass.Vampire)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
" exchange",
e.Player == _player ? " " : "s ",
e.Cards[0],
" for ",
e.Cards[1]);
}
else if (type == DominionBase.Cards.Adventures.TypeClass.Teacher
|| type == DominionBase.Cards.Adventures.TypeClass.Ferry
|| type == DominionBase.Cards.Adventures.TypeClass.LostArts
|| type == DominionBase.Cards.Adventures.TypeClass.Pathfinding
|| type == DominionBase.Cards.Adventures.TypeClass.Plan
|| type == DominionBase.Cards.Adventures.TypeClass.Seaway
|| type == DominionBase.Cards.Adventures.TypeClass.Training
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Ferry
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.LostArts
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Pathfinding
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Plan
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Seaway
|| type == DominionBase.Cards.Adventures2ndEdition.TypeClass.Training
)
{
var token = e.Tokens.FirstOrDefault();
if (e.Player == _player)
glMain.Log(e.Player, "You move your ", token, " to ", e.Cards[0]);
else
glMain.Log(e.Player, e.Player, " moves their ", token, " to ", e.Cards[0]);
}
else if (type == DominionBase.Cards.Empires.TypeClass.MountainPass)
{
if (e.Currency == (Currency)null)
{
if (e.Player == _player)
glMain.Log(e.Player, "You pass on ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, " passes on ", e.SourceCard);
}
else
{
if (e.Player == _player)
glMain.Log(e.Player, "You bid ", e.Currency, " on ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, " bids ", e.Currency, " on ", e.SourceCard);
}
}
else if (type == DominionBase.Cards.Seaside.TypeClass.Outpost
|| type == DominionBase.Cards.Seaside2ndEdition.TypeClass.Outpost
|| type == DominionBase.Cards.Adventures.TypeClass.Expedition
|| type == DominionBase.Cards.Renaissance.TypeClass.Flag
)
{
if (e.MessageType != "Taken" && e.MessageType != "Returned")
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" will draw {StringUtility.Plural("card", e.Count)} for next turn from ",
e.SourceCard
);
}
else if (type == DominionBase.Cards.Intrigue2ndEdition.TypeClass.SecretPassage)
{
if (e.Player == _player)
glMain.Log(e.Player, "You place ", e.Cards[0], " in your deck");
else
glMain.Log(e.Player, e.Player, " places a card in their deck");
}
else if (
//type == DominionBase.Cards.Nocturne.TypeClass.BadOmens
//|| type == DominionBase.Cards.Nocturne.TypeClass.Delusion
//|| type == DominionBase.Cards.Nocturne.TypeClass.Envy
//|| type == DominionBase.Cards.Nocturne.TypeClass.Famine
//|| type == DominionBase.Cards.Nocturne.TypeClass.Fear
//|| type == DominionBase.Cards.Nocturne.TypeClass.Greed
//|| type == DominionBase.Cards.Nocturne.TypeClass.Haunting
//|| type == DominionBase.Cards.Nocturne.TypeClass.Locusts
//|| type == DominionBase.Cards.Nocturne.TypeClass.Misery
//|| type == DominionBase.Cards.Nocturne.TypeClass.Plague
//|| type == DominionBase.Cards.Nocturne.TypeClass.Poverty
//||
type == DominionBase.Cards.Nocturne.TypeClass.TheEarthsGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheFieldsGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheFlamesGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheForestsGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheMoonsGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheMountainsGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheRiversGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheSeasGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheSkysGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheSunsGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheSwampsGift
|| type == DominionBase.Cards.Nocturne.TypeClass.TheWindsGift
//|| type == DominionBase.Cards.Nocturne.TypeClass.War
)
{
var verb = "receive";
if (e.MessageType == "Return")
verb = "return";
if (e.Player == _player)
glMain.Log(e.Player, $"You {verb} ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, $" {verb}s ", e.SourceCard);
}
//else if (type == DominionBase.Cards.Nocturne.TypeClass.Deluded
// || type == DominionBase.Cards.Nocturne.TypeClass.Envious
// || type == DominionBase.Cards.Nocturne.TypeClass.LostInTheWoods)
//{
// var verb = "take";
// if ((string)e.Data == "Returned")
// verb = "return";
// if (e.Player == _player)
// glMain.Log(e.Player, $"You {verb} ", e.SourceCard);
// else
// glMain.Log(e.Player, e.Player, $" {verb}s ", e.SourceCard);
//}
else
{
switch (e.MessageType)
{
case "AddToken":
if (e.Player == _player)
glMain.Log(e.Player, $"You add a token to {e.SourceCard}");
else
glMain.Log(e.Player, e.Player, $" adds a token to {e.SourceCard}");
break;
case "RemoveAllTokens":
if (e.Player == _player)
glMain.Log(e.Player, $"You remove all tokens from {e.SourceCard}");
else
glMain.Log(e.Player, e.Player, $" removes all tokens from {e.SourceCard}");
break;
case "Retrieve":
if (e.Player == _player)
glMain.Log(e.Player, "You retrieve ", e.Cards.FirstOrDefault(), " from ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, " retrieves ", e.Cards, " from ", e.SourceCard);
break;
case "Receive":
if (e.Player == _player)
glMain.Log(e.Player, "You receive ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, " receives ", e.SourceCard);
break;
case "Return":
if (e.Player == _player)
glMain.Log(e.Player, "You return ", e.Cards.FirstOrDefault(), " to its pile");
else
glMain.Log(e.Player, e.Player, " returns ", e.Cards.FirstOrDefault(), " to its pile");
break;
case "SetAside":
if (e.Player == _player)
glMain.Log(e.Player, "You set aside ", e.Cards.FirstOrDefault(), " on ", e.SourceCard);
else
glMain.Log(e.Player, e.Player, " sets aside ", e.Cards, " on ", e.SourceCard);
break;
case "ActionGainChanged":
glMain.Log(e.Player, "Actions gained changed to ", e.Count.ToString(), " from ", e.SourceCard);
break;
case "Overpay":
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" overpay{(e.Player == _player ? "" : "s")} by ",
e.Currency);
break;
case "LookThroughDiscard":
if (e.Player == _player)
glMain.Log(e.Player, "You look through your discard pile, containing ", e.Cards.Any() ? e.Cards : (object)"nothing");
else
glMain.Log(e.Player, e.Player, " looks through their discard pile");
break;
case "Reveal":
if (e.Player == _player)
glMain.Log(e.Player, "You reveal, containing ", e.Cards.Any() ? e.Cards : (object)"nothing");
else
glMain.Log(e.Player, e.Player, " reveals, containing ", e.Cards.Any() ? e.Cards : (object)"nothing");
break;
case "Exile":
if (e.Player == _player)
glMain.Log(e.Player, "You exile ", e.Cards.Any() ? e.Cards : (object)"nothing");
else
glMain.Log(e.Player, e.Player, " exiles ", e.Cards.Any() ? e.Cards : (object)"nothing");
break;
case "PlayAs":
glMain.Log(e.Player, " as ", e.Cards.FirstOrDefault());
break;
case "ConvertCardsToCoins":
glMain.Log(e.Player, "Converted ", $"+{e.Count}{(e.Count == 1 ? ResourcesHelper.Get("Card") : ResourcesHelper.Get("Cards"))}", " to ", $"{e.Count}");
break;
case "ConvertCoinsToCards":
glMain.Log(e.Player, "Converted ", $"{e.Count}", " to ", $"+{e.Count}{(e.Count == 1 ? ResourcesHelper.Get("Card") : ResourcesHelper.Get("Cards"))}");
break;
}
}
}
else if (e.SourceToken != null)
{
if (e.SourceToken is DominionBase.Cards.Adventures.JourneyToken jToken)
{
if (e.Player == _player)
glMain.Log(e.Player, "You flip over your ", jToken, " to ", jToken.FaceShowing);
else
glMain.Log(e.Player, e.Player, " flips over ", e.Player, "'s ", jToken, " to ", jToken.FaceShowing);
}
}
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Game_GameMessage), DispatcherPriority.Normal, sender, e);
}
}
internal string DepthPrefix()
{
var sb = new StringBuilder();
for (var c = 0; c < CurrentPlayDepth; c++)
sb.Append("... ");
return sb.ToString();
}
private void PlayerTokenPiles_TokenCollectionsChanged(object sender, TokenCollectionsChangedEventArgs e)
{
if (Dispatcher.CheckAccess())
{
switch (e.OperationPerformed)
{
case TokenCollectionsChangedEventArgs.Operation.Added:
if (e.AddedTokens[0] is DominionBase.Cards.Seaside.PirateShipToken)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" gain{(e.Player == _player ? "" : "s")} a Pirate Ship token"
);
}
else if (e.AddedTokens[0] is DominionBase.Cards.Guilds.Coffer)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" gain{(e.Player == _player ? "" : "s")} a Coffers"
);
}
else if (e.AddedTokens[0] is DominionBase.Cards.Adventures.MinusOneCardToken ||
e.AddedTokens[0] is DominionBase.Cards.Adventures.MinusOneCoinToken)
{
if (e.Player == _player)
{
glMain.Log(
e.Player,
"You take your ",
e.AddedTokens[0].Name
);
}
else
{
glMain.Log(
e.Player,
e.Player,
" takes ",
e.Player,
"'s ",
e.AddedTokens[0].Name
);
}
}
else if (e.AddedTokens[0] is DominionBase.Cards.Empires.DebtToken debtToken)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" gain{(e.Player == _player ? "" : "s")} {StringUtility.Plural(debtToken.LongDisplayString, e.AddedTokens.Count)}"
);
}
else if (e.AddedTokens[0] is DominionBase.Cards.Renaissance.Villager)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" gain{(e.Player == _player ? "" : "s")} a Villager"
);
}
break;
case TokenCollectionsChangedEventArgs.Operation.Removed:
if (e.RemovedTokens[0] is DominionBase.Cards.Adventures.MinusOneCardToken ||
e.RemovedTokens[0] is DominionBase.Cards.Adventures.MinusOneCoinToken)
{
if (e.Player == _player)
{
glMain.Log(
e.Player,
"You discard your ",
e.RemovedTokens[0].Name
);
}
else
{
glMain.Log(
e.Player,
e.Player,
" discards ",
e.RemovedTokens[0].Name
);
}
}
break;
}
}
else
{
Dispatcher.BeginInvoke(new EventHandler(PlayerTokenPiles_TokenCollectionsChanged), DispatcherPriority.Normal, sender, e);
}
}
private void PlayerMats_DecksChanged(object sender, DominionBase.Piles.CardMatsChangedEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (e.CardMat is DominionBase.Cards.Seaside.IslandMat)
{
if (e.OperationPerformed == DominionBase.Piles.CardMatsChangedEventArgs.Operation.Added)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" set{(e.Player == _player ? "" : "s")} aside ",
e.AddedCards.Select(c => c.PhysicalCard),
" on Island Mat");
}
}
else if (e.CardMat is DominionBase.Cards.Seaside.NativeVillageMat)
{
if (e.OperationPerformed == DominionBase.Piles.CardMatsChangedEventArgs.Operation.Added)
{
if (e.Player == _player)
glMain.Log(e.Player, "You put ", e.AddedCards.Select(c => c.PhysicalCard), " on Native Village Mat");
else
glMain.Log(
e.Player,
e.Player,
$" puts {StringUtility.Plural("card", e.AddedCards.Count)} on Native Village Mat");
}
else if (e.OperationPerformed == DominionBase.Piles.CardMatsChangedEventArgs.Operation.Removed && e.Player.Phase != PhaseEnum.Endgame)
{
if (e.Player == _player)
{
var nvTakenCards = e.RemovedCards.Select(c => c.PhysicalCard);
glMain.Log(e.Player, "You take ", !nvTakenCards.Any() ? (object)"nothing" : (object)nvTakenCards, " from Native Village Mat");
}
else
glMain.Log(
e.Player,
e.Player,
$" takes {StringUtility.Plural("card", e.RemovedCards.Count)} from Native Village Mat");
}
}
else if (
e.CardMat is DominionBase.Cards.Promotional.PrinceSetAside
|| e.CardMat is DominionBase.Cards.Promotional.SummonSetAside
)
{
if (e.OperationPerformed == DominionBase.Piles.CardMatsChangedEventArgs.Operation.Added && e.AddedCards.Count > 0)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" set{(e.Player == _player ? "" : "s")} aside ",
e.AddedCards.Select(c => c.PhysicalCard));
}
if (e.OperationPerformed == DominionBase.Piles.CardMatsChangedEventArgs.Operation.Removed && e.RemovedCards.Count > 0)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" retrieve{(e.Player == _player ? "" : "s")} ",
e.RemovedCards.Select(c => c.PhysicalCard));
}
}
else if (e.CardMat is DominionBase.Cards.Adventures.TavernMat)
{
if (e.OperationPerformed == DominionBase.Piles.CardMatsChangedEventArgs.Operation.Added)
{
glMain.Log(
e.Player,
e.Player == _player ? (object)"You" : (object)e.Player,
$" put{(e.Player == _player ? "" : "s")} ",
e.AddedCards.Select(c => c.PhysicalCard),
" on Tavern Mat");
}
}
}
else
{
Dispatcher.BeginInvoke(new EventHandler(PlayerMats_DecksChanged), DispatcherPriority.Normal, sender, e);
}
}
private void Player_CardsAddedToDeck(object sender, DominionBase.Players.CardsAddedToDeckEventArgs e)
{
if (Dispatcher.CheckAccess())
{
var locationMod = string.Empty;
if (e.DeckPosition == DominionBase.Piles.DeckPosition.Bottom)
locationMod = "the ";
if (e.Cards.Count == 0)
return;
if (sender == _player)
glMain.Log(
sender as DominionBase.Players.Player,
"You put ",
e.Cards.Select(c => c.PhysicalCard),
$" on {locationMod}{e.DeckPosition.ToString().ToLower()} of your deck"
);
else
glMain.Log(
sender as DominionBase.Players.Player,
sender,
string.Format(" puts {0} on {2}{1} of their deck",
StringUtility.Plural("card", e.Cards.Count),
e.DeckPosition.ToString().ToLower(),
locationMod
));
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_CardsAddedToDeck), DispatcherPriority.Normal, sender, e);
}
}
private void Player_CardsAddedToHand(object sender, DominionBase.Players.CardsAddedToHandEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (e.Cards.Count == 0)
return;
if (sender == _player)
glMain.Log(
sender as DominionBase.Players.Player,
"You put ",
e.Cards.Select(c => c.PhysicalCard),
" into your hand");
else
glMain.Log(sender as DominionBase.Players.Player, sender,
$" puts {StringUtility.Plural("card", e.Cards.Count)} into their hand");
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_CardsAddedToHand), DispatcherPriority.Normal, sender, e);
}
}
private void Player_CardsDiscarded(object sender, DominionBase.Players.CardsDiscardEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (e.Cards.Count == 0 || e.HandledBy.Contains(this))
return;
e.HandledBy.Add(this);
var location = string.Empty;
switch (e.FromLocation)
{
case DeckLocation.InPlay:
case DeckLocation.SetAside:
case DeckLocation.InPlayAndSetAside:
return;
case DeckLocation.Hand:
case DeckLocation.Deck:
location = $" from {(sender == _player ? "your" : "their")} {e.FromLocation.ToString().ToLower()}";
break;
case DeckLocation.PlayerMat:
location = $" from {((IPlayer)sender).PlayerMats[e.FromMatType].Name}";
break;
}
var name = sender == _player ? (object)"You" : (object)sender;
string verb = $" discard{(sender == _player ? "" : "s")} ";
if (e.Cards.Count == 1)
glMain.Log(sender as DominionBase.Players.Player, name, verb, e.Cards.Select(c => c.PhysicalCard), location);
else
glMain.Log(sender as DominionBase.Players.Player, name, verb,
$"{StringUtility.Plural("card", e.Cards.Count)}{location}");
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_CardsDiscarded), DispatcherPriority.Normal, sender, e);
}
}
private void Player_ShufflingStart(object sender, DominionBase.Players.ShuffleEventArgs e)
{
if (Dispatcher.CheckAccess())
{
glMain.Log(
e.Player,
"(",
e.Player == _player ? (object)"You" : (object)e.Player,
$" shuffle{(e.Player == _player ? "" : "s")}...)"
);
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_ShufflingStart), DispatcherPriority.Normal, sender, e);
}
}
private void SpecialPile_Shuffle(object sender, DominionBase.Players.ShuffleEventArgs e)
{
if (Dispatcher.CheckAccess())
{
glMain.Log(
e.Player,
"(",
e.Player == _player ? (object)"You" : (object)e.Player,
$" shuffle{(e.Player == _player ? "" : "s")} {sender}...)"
);
}
else
{
Dispatcher.BeginInvoke(new EventHandler(SpecialPile_Shuffle), DispatcherPriority.Normal, sender, e);
}
}
private void Player_BenefitReceiving(object sender, DominionBase.Players.BenefitReceiveVisualEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (!e.Benefit.Any && !string.IsNullOrWhiteSpace(e.Benefit.FlavorText))
return;
var sb = new StringBuilder();
sb.AppendFormat(" get{0}", _player != null && e.Player.PlayerUniqueId == _player.UniqueId ? "" : "s");
if (e.Benefit.Cards > 0)
sb.AppendFormat(" +{0} card{1}", e.Benefit.Cards, e.Benefit.Cards == 1 ? "" : "s");
else if (e.Benefit.Cards < 0 && e.Benefit.Actions <= 0 && e.Benefit.Buys <= 0 && e.Benefit.Currency <= new Currency() && e.Benefit.VictoryPoints <= 0)
return;
if (e.Benefit.Actions > 0)
sb.AppendFormat(" +{0}", StringUtility.Plural("action", e.Benefit.Actions));
else if (e.Benefit.Actions < 0)
sb.AppendFormat(" {0}", StringUtility.Plural("action", e.Benefit.Actions));
if (e.Benefit.Buys > 0)
sb.AppendFormat(" +{0}", StringUtility.Plural("buy", e.Benefit.Buys));
else if (e.Benefit.Buys < 0)
sb.AppendFormat(" {0}", StringUtility.Plural("buy", e.Benefit.Buys));
if (e.Benefit.Currency > new Currency())
sb.AppendFormat(" +{0}", Utilities.RenderText(e.Benefit.Currency.ToString()));
else if (e.Benefit.Currency < new Currency())
sb.AppendFormat(" {0}", Utilities.RenderText(e.Benefit.Currency.ToString()));
if (e.Benefit.Coffers > 0)
sb.AppendFormat(" +{0}", StringUtility.Plural("coffer", e.Benefit.Coffers));
else if (e.Benefit.Coffers < 0)
sb.AppendFormat(" {0}", StringUtility.Plural("coffer", e.Benefit.Coffers));
if (e.Benefit.Villagers > 0)
sb.AppendFormat(" +{0}", StringUtility.Plural("villager", e.Benefit.Villagers));
else if (e.Benefit.Villagers < 0)
sb.AppendFormat(" {0}", StringUtility.Plural("villager", e.Benefit.Villagers));
if (e.Benefit.VictoryPoints > 0)
sb.Append(Utilities.RenderText($" +{e.Benefit.VictoryPoints}"));
else if (e.Benefit.VictoryPoints < 0)
sb.Append(Utilities.RenderText($" -{-e.Benefit.VictoryPoints}"));
sb.Append(e.Benefit.FlavorText);
if (e.Phase == PhaseEnum.Starting
|| !_playStack.Any()
|| (sender is ICardBase sCardBase && _playStack.Peek().Type != sCardBase.Type)
|| sender is Token)
{
sb.Append(" from ");
var from = sender;
if (sender is ICardBase cardBase && cardBase.Type.IsSubclassOf(typeof(Card)))
from = ((Card)sender).PhysicalCard;
glMain.Log(
e.Player,
_player != null && e.Player.PlayerUniqueId == _player.UniqueId ? (object)"You" : (object)e.Player,
sb.ToString(),
from);
}
else
glMain.Log(
e.Player,
_player != null && e.Player.PlayerUniqueId == _player.UniqueId ? (object)"You" : (object)e.Player,
sb.ToString());
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_BenefitReceiving), DispatcherPriority.Normal, sender, e);
}
}
private void Player_TurnStarting(object sender, DominionBase.Players.TurnStartingEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (e.Player == _player)
{
dpStuff.IsEnabled = true;
miSaveGame.IsEnabled = true;
}
else
{
dpStuff.IsEnabled = false;
miSaveGame.IsEnabled = false;
}
// Just in case
CurrentPlayDepth = 0;
_playStack.Clear();
if (Game.Players[0] == e.Player && e.GrantedBy == null)
glMain.NewTurn(Game.TurnsTaken.TurnNumber(e.Player));
glMain.NewTurn(e.Player, e.GrantedBy);
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_TurnStarting), DispatcherPriority.Normal, sender, e);
}
}
private void Player_TurnEnded(object sender, DominionBase.Players.TurnEndedEventArgs e)
{
if (Dispatcher.CheckAccess())
{
uccChooser.ClearAutoYields(AutoYieldDuration.Turn);
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_TurnEnded), DispatcherPriority.Normal, sender, e);
}
}
private void Player_CardsDrawn(object sender, DominionBase.Players.CardsDrawnEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (e.Cards.Any())
{
var from = string.Empty;
if (e.FromDeckPosition == DominionBase.Piles.DeckPosition.Bottom)
from = $" from the bottom of {(sender == _player ? "your" : "their")} deck";
if (sender == _player)
glMain.Log(
sender as DominionBase.Players.Player,
"You draw ",
e.Cards.Select(c => c.PhysicalCard),
from);
else
glMain.Log(
sender as DominionBase.Players.Player,
sender,
$" draws {StringUtility.Plural("card", e.Cards.Count)}",
from);
}
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_CardsDrawn), DispatcherPriority.Normal, sender, e);
}
}
private void Game_GameEndedEvent(object sender, GameEndedEventArgs e)
{
if (Dispatcher.CheckAccess())
{
//_Statistics.Add(game, _Player);
//_Statistics.Save();
miNewGame.IsEnabled = true;
miLoadGame.IsEnabled = true;
miEndGame.IsEnabled = false;
miSaveGame.IsEnabled = false;
miReplay.IsEnabled = true;
if (_startingNewGame)
{
glMain.TearDown();
glMain.Clear();
}
glMain.NewSection("Game ended");
foreach (var player in Game.Players)
{
var playerType = string.Empty;
switch (player.PlayerType)
{
case PlayerType.Human:
playerType = "Human";
break;
case PlayerType.Computer:
playerType = (string)player.GetType().GetProperty("AIType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.FlattenHierarchy).GetValue(player, null);
break;
}
glMain.Log(string.Empty, player, $" ({playerType}): ", Colors.Crimson, player.VictoryPoints, Colors.Transparent,
$" {StringUtility.Plural("point", player.VictoryPoints, false)} in ", Colors.DodgerBlue, Game.UnmodifiedTurnsTaken.Count(t => t.Player == player && t.IsTurnFinished), Colors.Transparent, " turns");
}
if (Game.Winners.Any())
glMain.Log(StringUtility.Plural("Winner", Game.Winners.Count, false), ": ", Game.Winners, " with ", Game.Winners[0].VictoryPoints, StringUtility.Plural(" point", Game.Winners[0].VictoryPoints, false));
miReplay.IsEnabled = Game.State == GameState.Ended || Game.State == GameState.Aborted;
tbActions.Text = string.Empty;
tbBuys.Text = string.Empty;
tbCurrency.Text = string.Empty;
bPlayTreasures.Text = "Play _Treasures";
bPlayTreasures.IsEnabled = false;
bDonePlayingTreasures.IsEnabled = false;
bSpendVillager.IsEnabled = false;
bSpendCoffers.IsEnabled = false;
bPayOffDebtTokens.IsEnabled = false;
bBuyPhase.IsEnabled = false;
bNightPhase.IsEnabled = false;
bUndo.IsEnabled = false;
bTurnDone.IsEnabled = false;
foreach (var control in stackPanelSupplyPiles.Children.OfType().SelectMany(sp => sp.Children.OfType()))
control.Clickability = SupplyVisibility.Plain;
ReleaseEvents();
if (_startingNewGame)
Game_NewGame_Click(null, null);
ChartGame(Game);
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Game_GameEndedEvent), DispatcherPriority.Normal, sender, e);
}
}
private void InitPointsChart()
{
Context.GameEndModel.IsLegendVisible = true;
Context.GameEndModel.LegendPosition = OxyPlot.LegendPosition.TopLeft;
Context.GameEndModel.Title = "Player points per turn of the game";
Context.GameEndModel.Axes.Add(new OxyPlot.Axes.LinearAxis
{
Position = OxyPlot.Axes.AxisPosition.Bottom,
Title = "Turn"
});
Context.GameEndModel.Axes.Add(new OxyPlot.Axes.LinearAxis
{
Position = OxyPlot.Axes.AxisPosition.Left,
Title = "Points"
});
}
private void ChartGame(IGame game)
{
Context.GameEndModel.Series.Clear();
var minPoints = 0;
var maxPoints = 0;
var playersSeries = new Dictionary>();
foreach (var turn in game.TurnsTaken)
{
if (!playersSeries.ContainsKey(turn.Player))
playersSeries[turn.Player] = new List();
double index = playersSeries[turn.Player].Count(dp => dp.X == Math.Floor(dp.X)) + 1;
// Extra turns are 1/2 index off
if (turn.GrantedBy != null)
index -= 0.5;
if (turn.Points == null)
continue;
minPoints = minPoints == 0 ? turn.Points.Value : Math.Min(minPoints, turn.Points.Value);
maxPoints = maxPoints == 0 ? turn.Points.Value : Math.Max(maxPoints, turn.Points.Value);
playersSeries[turn.Player].Add(new OxyPlot.DataPoint(index, turn.Points.Value));
}
var keys = playersSeries.Keys.ToList();
if (!keys.Any() || !playersSeries[keys[0]].Any())
return;
foreach (var player in playersSeries)
{
// If there weren't an equal number of turns, then fill out the turns to account for it
if (player.Value.Count == 0 || Math.Floor(player.Value[player.Value.Count - 1].X) < Math.Floor(playersSeries[keys[0]][playersSeries[keys[0]].Count - 1].X))
player.Value.Add(new OxyPlot.DataPoint(playersSeries[keys[0]][playersSeries[keys[0]].Count - 1].X, player.Key.VictoryPoints));
var playerSettings = Context.Settings.PlayerSettings.FirstOrDefault(ps => ps.Name == player.Key.Name);
var hlsValue = HlsColor.RgbToHls(playerSettings.UIColor);
var seriesColor = Math.Abs(hlsValue.A) >= 0.001
? HlsColor.HlsToRgb(hlsValue.H, hlsValue.L * 0.65, hlsValue.S * 1.05, hlsValue.A)
: HlsColor.HlsToRgb(0d, hlsValue.L * 0.65, 0d, 1d);
var markerColor = Math.Abs(hlsValue.A) >= 0.001
? HlsColor.HlsToRgb(hlsValue.H, hlsValue.L * 0.90, hlsValue.S * 1.05, hlsValue.A)
: HlsColor.HlsToRgb(0d, hlsValue.L * 0.90, 0d, 1d);
var series = new OxyPlot.Series.LineSeries
{
CanTrackerInterpolatePoints = false,
Color = OxyPlot.OxyColor.FromRgb(seriesColor.R, seriesColor.G, seriesColor.B),
MarkerFill = OxyPlot.OxyColor.FromRgb(markerColor.R, markerColor.G, markerColor.B),
MarkerType = (OxyPlot.MarkerType)(Context.GameEndModel.Series.Count + 1),
Title = player.Key.Name,
};
series.Points.AddRange(player.Value);
Context.GameEndModel.Series.Add(series);
}
tiGamePoints.Visibility = Visibility.Visible;
}
private void Player_BenefitsChanged(object sender, DominionBase.Players.BenefitsChangedEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (e.Player == Game.ActivePlayer)
{
tbActions.Inlines.Clear();
tbActions.Inlines.Add(((TextBlock)Utilities.RenderText(
$"{(e.Actions > 0 ? "" : "")}{e.Actions}{(e.Actions > 0 ? "" : "")}", NET_WPF.RenderSize.Tiny, true)[0]).Inlines.ElementAt(0));
tbBuys.Inlines.Clear();
tbBuys.Inlines.Add(((TextBlock)Utilities.RenderText(
$"{(e.Buys > 0 ? "" : "")}{e.Buys}{(e.Buys > 0 ? "" : "")}", NET_WPF.RenderSize.Tiny, true)[0]).Inlines.ElementAt(0));
tbCurrency.Inlines.Clear();
var tbTemp = (TextBlock)Utilities.RenderText(e.Player.Currency.ToString(), NET_WPF.RenderSize.Tiny, false)[0];
while (tbTemp.Inlines.Any())
{
var container = tbTemp.Inlines.ElementAt(0) as InlineUIContainer;
if (container?.Child is Canvas canvas)
canvas.Margin = new Thickness(2, 0, 2, 0);
tbCurrency.Inlines.Add(tbTemp.Inlines.ElementAt(0));
}
UpdateDisplay();
}
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_BenefitsChanged), DispatcherPriority.Normal, sender, e);
}
}
private void Player_CardFlippedOver(object sender, DominionBase.Players.CardFlippedOverEventArgs e)
{
if (Dispatcher.CheckAccess())
{
var player = (IPlayer)sender;
glMain.Log(
player,
player == _player ? (object)"You" : (object)player,
$" flip{(player == _player ? "" : "s")} over ",
e.Card,
" ",
e.NewFacing
);
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_CardFlippedOver), DispatcherPriority.Normal, sender, e);
}
}
private void Player_TakeablesChanged(object sender, DominionBase.Players.TakeableEventArgs e)
{
if (Dispatcher.CheckAccess())
{
var player = (IPlayer)sender;
var verb = "unknown";
switch (e.TakeableAction)
{
case TakeableAction.Taken:
verb = "take";
break;
case TakeableAction.Returned:
verb = "return";
break;
}
glMain.Log(
player,
player == _player ? (object)"You" : (object)player,
$" {verb}{(player == _player ? "" : "s")} ",
e.Takeables
);
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_TakeablesChanged), DispatcherPriority.Normal, sender, e);
}
}
private void Player_PhaseChangedEvent(object sender, DominionBase.Players.PhaseChangedEventArgs e)
{
if (Dispatcher.CheckAccess())
{
phaseDisplay.Phase = e.NewPhase;
if (e.CurrentPlayer == Game.ActivePlayer)
{
//System.Diagnostics.Trace.WriteLine(String.Format("{2} Phase changed: {0} to {1}", e.OldPhase, e.NewPhase, DateTime.Now.ToString("o")));
if (e.NewPhase == PhaseEnum.Starting ||
e.NewPhase == PhaseEnum.Buy ||
e.CurrentPlayer.PlayerMode == PlayerMode.Waiting)
CheckBuyable(e.CurrentPlayer);
else
ClearBuyable();
}
if (e.CurrentPlayer != _player)
return;
if (e.NewPhase == PhaseEnum.Setup || e.NewPhase == PhaseEnum.Starting || e.NewPhase == PhaseEnum.Endgame)
miSettings.IsEnabled = miCurrentGame.IsEnabled = true;
else if (e.CurrentPlayer.PlayerMode == PlayerMode.Waiting)
miSettings.IsEnabled = miCurrentGame.IsEnabled = false;
UpdateDisplay();
if (_settings.AutoPlayTreasures && e.NewPhase == PhaseEnum.BuyTreasure)
{
// Ugly hack, but it mostly works -- just a slight delay between the end of the PhaseChangedEvent and the AutoPlay
Task.Run(() =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(50));
AutoPlayTreasures();
});
//var autoplayInvoker = new BackgroundWorker();
//autoplayInvoker.DoWork += delegate
//{
// Thread.Sleep(TimeSpan.FromMilliseconds(50));
// AutoPlayTreasures();
//};
//autoplayInvoker.RunWorkerAsync();
}
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_PhaseChangedEvent), DispatcherPriority.Normal, sender, e);
}
}
private void Player_PlayerModeChangedEvent(object sender, DominionBase.Players.PlayerModeChangedEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (e.CurrentPlayer == Game.ActivePlayer)
{
//System.Diagnostics.Trace.WriteLine(String.Format("{2} Phase changed: {0} to {1}", e.OldPhase, e.NewPhase, DateTime.Now.ToString("o")));
if (e.CurrentPlayer.Phase == PhaseEnum.Starting ||
e.CurrentPlayer.Phase == PhaseEnum.Buy ||
e.NewPlayerMode == PlayerMode.Waiting)
CheckBuyable(e.CurrentPlayer);
else
ClearBuyable();
}
if (e.CurrentPlayer != _player)
return;
if (e.CurrentPlayer.Phase == PhaseEnum.Setup || e.CurrentPlayer.Phase == PhaseEnum.Starting || e.CurrentPlayer.Phase == PhaseEnum.Endgame)
miSettings.IsEnabled = miCurrentGame.IsEnabled = true;
else if (e.CurrentPlayer.PlayerMode == PlayerMode.Waiting)
miSettings.IsEnabled = miCurrentGame.IsEnabled = false;
UpdateDisplay();
if (_settings.AutoPlayTreasures
&& e.NewPlayerMode == PlayerMode.Normal
&& e.CurrentPlayer.Phase == PhaseEnum.BuyTreasure)
{
// Ugly hack, but it mostly works -- just a slight delay between the end of the PhaseChangedEvent and the AutoPlay
Task.Run(() =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(50));
AutoPlayTreasures();
});
//var autoplayInvoker = new BackgroundWorker();
//autoplayInvoker.DoWork += delegate
//{
// Thread.Sleep(TimeSpan.FromMilliseconds(50));
// AutoPlayTreasures();
//};
//autoplayInvoker.RunWorkerAsync();
}
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_PlayerModeChangedEvent), DispatcherPriority.Normal, sender, e);
}
}
private void AutoPlayTreasures()
{
WaitCallback wcb = UpdateDisplayTarget;
GamePlayMessage gpm;
// Always play Contraband first
var contrabandTreasures = _player.Hand[DominionBase.Cards.Prosperity.TypeClass.Contraband];
foreach (var card in contrabandTreasures)
{
if (_player.Phase != PhaseEnum.ActionTreasure && _player.Phase != PhaseEnum.BuyTreasure)
break;
while (Game.MessageResponseQueue.TryDequeue(out GameMessage result)) ;
gpm = new GamePlayMessage(wcb, _player, card) { Message = $"{_player} playing {card}" };
EnqueueGameMessageAndWait(gpm);
}
// Play "normal" Treasure cards next
var tNormal = _player.Hand[c =>
c.Category.HasFlag(Categories.Treasure)
&& c.Type != DominionBase.Cards.Prosperity.TypeClass.Bank
&& c.Type != DominionBase.Cards.Prosperity.TypeClass.Contraband
&& c.Type != DominionBase.Cards.Prosperity.TypeClass.Loan
&& c.Type != DominionBase.Cards.Prosperity.TypeClass.Venture
&& c.Type != DominionBase.Cards.Prosperity2ndEdition.TypeClass.Loan
&& c.Type != DominionBase.Cards.Cornucopia.TypeClass.HornOfPlenty
&& c.Type != DominionBase.Cards.Cornucopia2ndEdition.TypeClass.HornOfPlenty
];
if (tNormal.Any())
_player.PlayCards(tNormal);
// Only play Loan & Venture after cards like Philosopher's Stone that work better with more cards
// There are some very specific situations where playing Horn Of Plenty before Philospher's Stone
// or Venture is the right way to play things, but that's so incredibly rare.
var tLoanVenture = _player.Hand[DominionBase.Cards.Prosperity.TypeClass.Venture];
if (_settings.AutoPlayTreasuresIncludingLoan)
{
if (_settings.AutoPlayTreasuresLoanFirst)
tLoanVenture.InsertRange(0, _player.Hand[c => c is DominionBase.Cards.Prosperity.Loan || c is DominionBase.Cards.Prosperity2ndEdition.Loan]);
else
tLoanVenture.AddRange(_player.Hand[c => c is DominionBase.Cards.Prosperity.Loan || c is DominionBase.Cards.Prosperity2ndEdition.Loan]);
}
foreach (var card in tLoanVenture)
{
if (_player.Phase != PhaseEnum.ActionTreasure && _player.Phase != PhaseEnum.BuyTreasure)
break;
gpm = new GamePlayMessage(wcb, _player, card) { Message = $"{_player} playing {card}" };
EnqueueGameMessageAndWait(gpm);
return;
}
// Always play Bank & Horn of Plenty last
var tBankHornofPlenty = _player.Hand[DominionBase.Cards.Prosperity.TypeClass.Bank];
if (_settings.AutoPlayTreasuresIncludingHornOfPlenty)
{
// If Horn Of Plenty is to be played first, play ALL Horn Of Plenty cards first
if (_settings.AutoPlayTreasuresHornOfPlentyFirst)
tBankHornofPlenty.InsertRange(0, _player.Hand[c => c is DominionBase.Cards.Cornucopia.HornOfPlenty || c is DominionBase.Cards.Cornucopia2ndEdition.HornOfPlenty]);
// Otherwise, play a SINGLE Bank card, then ALL Horn of Plenty cards, then all remaining Bank cards
else
tBankHornofPlenty.InsertRange(tBankHornofPlenty.Count == 0 ? 0 : 1, _player.Hand[c => c is DominionBase.Cards.Cornucopia.HornOfPlenty || c is DominionBase.Cards.Cornucopia2ndEdition.HornOfPlenty]);
}
foreach (var card in tBankHornofPlenty)
{
if (_player.Phase != PhaseEnum.ActionTreasure && _player.Phase != PhaseEnum.BuyTreasure)
break;
gpm = new GamePlayMessage(wcb, _player, card) { Message = $"{_player} playing {card}" };
EnqueueGameMessageAndWait(gpm);
}
}
private void ClearBuyable()
{
foreach (var control in stackPanelSupplyPiles.Children.OfType().SelectMany(sp => sp.Children.OfType()))
{
control.SupplyClick -= SupplyControl_SupplyClick;
control.Clickability = control.Clickability;
}
}
private void CheckBuyable(IPlayer player)
{
var buyablePhase = player != null && player.Phase == PhaseEnum.Buy;
foreach (var control in stackPanelSupplyPiles.Children.OfType().SelectMany(sp => sp.Children.OfType()))
{
control.SupplyClick -= SupplyControl_SupplyClick;
if (_player != player)
control.Clickability = SupplyVisibility.Plain;
else if (buyablePhase && player.Buys > 0 && _player == player && control.Supply is IBuyable sBuyable && sBuyable.CanBuy(player))
{
control.Clickability = SupplyVisibility.Gainable;
// Only attach the SupplyClick event if we're actually buying a card.
// Otherwise, we'll double-trigger on certain events (like Border Village's Gain ability)
if (player.PlayerMode == PlayerMode.Normal)
control.SupplyClick += SupplyControl_SupplyClick;
}
else
control.Clickability = SupplyVisibility.NotClickable;
}
}
private void Player_Trashing(object sender, DominionBase.Players.TrashEventArgs e)
{
if (Dispatcher.CheckAccess())
{
e.TrashedCards.Sort();
glMain.Log(
sender as DominionBase.Players.Player,
sender == _player ? (object)"You" : (object)sender,
$" trash{(sender == _player ? "" : "es")} ",
e.TrashedCards.Select(c => c.PhysicalCard));
glMain.Push();
CurrentPlayDepth++;
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_Trashing), DispatcherPriority.Normal, sender, e);
}
}
private void Player_Trashed(object sender, DominionBase.Players.TrashEventArgs e)
{
if (Dispatcher.CheckAccess())
{
glMain.Pop();
CurrentPlayDepth--;
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_Trashed), DispatcherPriority.Normal, sender, e);
}
}
private void Player_Calling(object sender, DominionBase.Players.CallEventArgs e)
{
if (Dispatcher.CheckAccess())
{
glMain.Log(
sender as DominionBase.Players.Player,
sender == _player ? (object)"You" : (object)sender,
$" call{(sender == _player ? "" : "s")} ",
e.CalledCard.PhysicalCard);
glMain.Push();
CurrentPlayDepth++;
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_Calling), DispatcherPriority.Normal, sender, e);
}
}
private void Player_Called(object sender, DominionBase.Players.CallEventArgs e)
{
if (Dispatcher.CheckAccess())
{
glMain.Pop();
CurrentPlayDepth--;
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_Called), DispatcherPriority.Normal, sender, e);
}
}
private void Player_CardReceived(object sender, DominionBase.Players.CardReceivedEventArgs e)
{
if (Dispatcher.CheckAccess())
{
var player = sender as DominionBase.Players.Player;
var extra = new StringBuilder();
switch (e.Location)
{
case DeckLocation.Deck:
var locationMod = string.Empty;
if (player != null)
{
var dp = player.ResolveDeckPosition(e.Location, e.Position);
if (dp == DominionBase.Piles.DeckPosition.Bottom)
locationMod = "the ";
extra.AppendFormat(", putting it on {1}{0} of your {2}", dp.ToString().ToLower(), locationMod, e.Location.ToString().ToLower());
}
break;
case DeckLocation.Hand:
case DeckLocation.InPlay:
case DeckLocation.SetAside:
extra.AppendFormat(", putting it into your {0}", e.Location.ToString().ToLower());
break;
}
glMain.Log(
player,
player == _player ? (object)"You" : (object)player,
$" receive{(player == _player ? "" : "s")} ",
e.Card.PhysicalCard,
" from ",
e.FromPlayer,
extra);
UpdateDisplay();
}
else
{
Dispatcher.BeginInvoke(new EventHandler(Player_CardReceived), DispatcherPriority.Normal, sender, e);
}
}
private void Player_CardGaining(object sender, DominionBase.Players.CardGainEventArgs e)
{
if (Dispatcher.CheckAccess())
{
if (!e.Bought)
{
List