diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7409cd7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.atom-build.yml
\ No newline at end of file
diff --git a/Graveyard.csproj b/Graveyard.csproj
new file mode 100644
index 0000000..f338ab9
--- /dev/null
+++ b/Graveyard.csproj
@@ -0,0 +1,85 @@
+
+
+
+ Debug
+ x86
+ {59c8cca4-7f9b-4592-87a8-a23aded7759e}
+ Library
+ Graveyard
+ Graveyard
+ v4.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ lib\HearthDb.dll
+
+
+ lib\HearthstoneDeckTracker.exe
+
+
+ False
+ lib\MahApps.Metro.dll
+
+
+
+
+
+
+
+
+
+
+
+ SettingsView.xaml
+
+
+ Designer
+ MSBuild:Compile
+
+
+ True
+ True
+ Settings.settings
+
+
+ PublicSettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ec7561d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+# Graveyard
+Graveyard is a plugin for the [Hearthstone Deck Tracker](https://github.com/HearthSim/Hearthstone-Deck-Tracker).
+
+Graveyard displays minions that have died this game. In addition Graveyard will display specialized views for decks containing certain cards.
+
+* **Anyfin Can Happen**
+Displays Murlocs killed by both the player and opponent as well as a damage calculator (thanks to [AnyfinCalculator](https://github.com/ericBG/AnyfinCalculator)).
+
+* **N'Zoth the Corruptor**
+Displays deathrattle minions.
+
+* **Resurect** or **Onix Bishop**
+Displays resurect chance next to each minion that has died.
+
+## Installing
+Extract `graveyard.dll` from the archive and move it to `%AppData%\HearthstoneDeckTracker\Plugins`. You will need to enable Graveyard in the Hearthstone Deck Tracker client under `Options > Tracker > Plugins > Graveyard`.
diff --git a/lib/HearthDb.dll b/lib/HearthDb.dll
new file mode 100644
index 0000000..7a7a423
Binary files /dev/null and b/lib/HearthDb.dll differ
diff --git a/lib/HearthstoneDeckTracker.exe b/lib/HearthstoneDeckTracker.exe
new file mode 100644
index 0000000..da11318
Binary files /dev/null and b/lib/HearthstoneDeckTracker.exe differ
diff --git a/lib/MahApps.Metro.dll b/lib/MahApps.Metro.dll
new file mode 100644
index 0000000..c71adf9
Binary files /dev/null and b/lib/MahApps.Metro.dll differ
diff --git a/src/AnyfinCalculator.cs b/src/AnyfinCalculator.cs
new file mode 100644
index 0000000..b7a12aa
--- /dev/null
+++ b/src/AnyfinCalculator.cs
@@ -0,0 +1,246 @@
+/**
+ * Code originally from https://github.com/ericBG/AnyfinCalculator.
+ * Thank you ericBG for all the hard work!
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 ericBG
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+using System.Runtime.CompilerServices;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System;
+using HearthDb.Enums;
+using Hearthstone_Deck_Tracker;
+using Hearthstone_Deck_Tracker.Hearthstone;
+using Hearthstone_Deck_Tracker.Hearthstone.Entities;
+using Hearthstone_Deck_Tracker.Utility.Logging;
+
+namespace HDT.Plugins.Graveyard
+{
+ public class MurlocInfo
+ {
+ public enum State
+ {
+ Dead,
+ OnBoard,
+ OnOpponentsBoard
+ }
+
+ public State BoardState { get; set; }
+ public bool CanAttack { get; set; }
+ public bool IsSilenced { get; set; }
+ public bool AreBuffsApplied { get; set; }
+ public int Attack { get; set; }
+ public Card Murloc { get; set; }
+ }
+
+ public class Range where T : IComparable
+ {
+ public T Minimum { get; set; }
+ public T Maximum { get; set; }
+ }
+
+ public class AnyfinCalculator
+ {
+ public static Range CalculateDamageDealt(List Graveyard)
+ {
+ var DeadMurlocs = new List();
+ foreach (var card in Graveyard) {
+ for (var i = 0; i < card.Count; i++) {
+ var c = card.Clone() as Card;
+ c.Count = 1;
+ DeadMurlocs.Add(c);
+ }
+ }
+
+ if (Core.Game.PlayerMinionCount >= 7) return new Range() {Maximum = 0, Minimum = 0};
+ if (DeadMurlocs.Count() + Core.Game.PlayerMinionCount <= 7)
+ {
+ var damage = CalculateDamageInternal(DeadMurlocs, Core.Game.Player.Board,
+ Core.Game.Opponent.Board);
+ return new Range {Maximum = damage, Minimum = damage};
+ }
+ var sw = Stopwatch.StartNew();
+ var board = Core.Game.Player.Board.ToList();
+ var opponent = Core.Game.Opponent.Board.ToList();
+ int? min = null, max = null;
+ foreach (var combination in Combinations(DeadMurlocs, 7 - Core.Game.PlayerMinionCount))
+ {
+ var damage = CalculateDamageInternal(combination, board, opponent);
+ if (damage > max || !max.HasValue) max = damage;
+ if (damage < min || !min.HasValue) min = damage;
+ }
+ sw.Stop();
+ Log.Debug($"Time to calculate the possibilities: {sw.Elapsed.ToString("ss\\:fff")}");
+ return new Range {Maximum = max.Value, Minimum = min.Value};
+ }
+
+ private static int CalculateDamageInternal(IEnumerable graveyard, IEnumerable friendlyBoard,
+ IEnumerable opponentBoard)
+ {
+ var deadMurlocs = graveyard.ToList();
+ var aliveMurlocs = friendlyBoard.Where(c => c.Card.IsMurloc()).ToList();
+ var opponentMurlocs = opponentBoard.Where(c => c.Card.IsMurloc()).ToList();
+ //compiles together into one big freaking list
+ var murlocs =
+ deadMurlocs.Select(
+ c =>
+ new MurlocInfo
+ {
+ AreBuffsApplied = false,
+ Attack = c.Attack,
+ BoardState = MurlocInfo.State.Dead,
+ CanAttack = c.IsChargeMurloc(),
+ IsSilenced = false,
+ Murloc = c
+ })
+ .Concat(
+ aliveMurlocs.Select(
+ ent =>
+ new MurlocInfo
+ {
+ AreBuffsApplied = true,
+ Attack = ent.GetTag(GameTag.ATK),
+ BoardState = MurlocInfo.State.OnBoard,
+ CanAttack = CanAttack(ent),
+ IsSilenced = IsSilenced(ent),
+ Murloc = ent.Card
+ }))
+ .Concat(
+ opponentMurlocs.Select(
+ ent =>
+ new MurlocInfo
+ {
+ AreBuffsApplied = false,
+ Attack = ent.Card.Attack,
+ BoardState = MurlocInfo.State.OnOpponentsBoard,
+ CanAttack = false,
+ IsSilenced = IsSilenced(ent),
+ Murloc = ent.Card
+ })).ToList();
+ var nonSilencedWarleaders =
+ murlocs.Count(m => m.BoardState != MurlocInfo.State.Dead && m.Murloc.IsWarleader() && !m.IsSilenced);
+ var nonSilencedGrimscales =
+ murlocs.Count(m => m.BoardState != MurlocInfo.State.Dead && m.Murloc.IsGrimscale() && !m.IsSilenced);
+ var murlocsToBeSummoned = murlocs.Count(m => m.BoardState == MurlocInfo.State.Dead);
+ foreach (var murloc in murlocs.Where(t => t.AreBuffsApplied))
+ {
+ murloc.AreBuffsApplied = false;
+ murloc.Attack -= nonSilencedGrimscales + (nonSilencedWarleaders*2);
+ if (murloc.IsSilenced) continue;
+ if (murloc.Murloc.IsGrimscale()) murloc.Attack += 1;
+ if (murloc.Murloc.IsWarleader()) murloc.Attack += 2;
+ if (murloc.Murloc.IsMurkEye()) murloc.Attack -= (murlocs.Count(m => m.BoardState != MurlocInfo.State.Dead) - 1);
+ }
+ nonSilencedWarleaders += murlocs.Count(m => m.BoardState == MurlocInfo.State.Dead && m.Murloc.IsWarleader());
+ nonSilencedGrimscales += murlocs.Count(m => m.BoardState == MurlocInfo.State.Dead && m.Murloc.IsGrimscale());
+ foreach (var murloc in murlocs)
+ {
+ murloc.AreBuffsApplied = true;
+ murloc.Attack += nonSilencedGrimscales + (nonSilencedWarleaders*2);
+ if (murloc.IsSilenced) continue;
+ if (murloc.Murloc.IsWarleader()) murloc.Attack -= 2;
+ if (murloc.Murloc.IsGrimscale()) murloc.Attack -= 1;
+ if (murloc.Murloc.IsMurkEye()) murloc.Attack += (murlocs.Count - 1);
+ if (murloc.Murloc.IsTidecaller()) murloc.Attack += murlocsToBeSummoned;
+ }
+ Log.Debug(murlocs.Aggregate("",
+ (s, m) =>
+ s + $"{m.Murloc.Name}{(m.IsSilenced ? " (Silenced)" : "")}: {m.Attack} {(!m.CanAttack ? "(Can't Attack)" : "")}\n"));
+ return murlocs.Sum(m => m.CanAttack ? m.Attack : 0);
+ }
+
+ private static bool IsSilenced(Entity entity) => entity.GetTag(GameTag.SILENCED) == 1;
+
+ private static bool CanAttack(Entity entity)
+ {
+ if (entity.GetTag(GameTag.CANT_ATTACK) == 1 || entity.GetTag(GameTag.FROZEN) == 1)
+ return false;
+ if (entity.GetTag(GameTag.EXHAUSTED) == 1)
+ //from reading the HDT source, it seems like internally Charge minions still have summoning sickness
+ return entity.GetTag(GameTag.CHARGE) == 1 &&
+ entity.GetTag(GameTag.NUM_ATTACKS_THIS_TURN) < MaxAttacks(entity);
+ return entity.GetTag(GameTag.NUM_ATTACKS_THIS_TURN) < MaxAttacks(entity);
+ }
+
+ private static int MaxAttacks(Entity entity)
+ {
+ // GVG_111t == V-07-TR-0N (MegaWindfury, 4x attack)
+ if (entity.CardId == "GVG_111t") return 4;
+ // if it has windfury it can attack twice, else it can only attack once
+ return entity.GetTag(GameTag.WINDFURY) == 1 ? 2 : 1;
+ }
+
+ public static IEnumerable> Combinations(IEnumerable elements, int k)
+ {
+ return k == 0
+ ? new[] {new T[0]}
+ : elements.SelectMany((e, i) =>
+ Combinations(elements.Skip(i + 1), k - 1).Select(c => (new[] {e}).Concat(c)));
+ }
+ }
+
+ public static class Murlocs
+ {
+ static Murlocs()
+ {
+ BluegillWarrior = Database.GetCardFromId("CS2_173");
+ GrimscaleOracle = Database.GetCardFromId("EX1_508");
+ MurlocWarleader = Database.GetCardFromId("EX1_507");
+ OldMurkEye = Database.GetCardFromId("EX1_062");
+ MurlocTidecaller = Database.GetCardFromId("EX1_509");
+ AnyfinCanHappen = Database.GetCardFromId("LOE_026");
+ }
+
+ public static Card BluegillWarrior { get; }
+ public static Card GrimscaleOracle { get; }
+ public static Card MurlocWarleader { get; }
+ public static Card OldMurkEye { get; }
+ public static Card MurlocTidecaller { get; }
+ public static Card AnyfinCanHappen { get; }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsMurloc(this Card card) => card.Race == "Murloc";
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsChargeMurloc(this Card card) => card.Id == OldMurkEye.Id || card.Id == BluegillWarrior.Id;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsBluegill(this Card card) => card.Id == BluegillWarrior.Id;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsGrimscale(this Card card) => card.Id == GrimscaleOracle.Id;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsWarleader(this Card card) => card.Id == MurlocWarleader.Id;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsMurkEye(this Card card) => card.Id == OldMurkEye.Id;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsTidecaller(this Card card) => card.Id == MurlocTidecaller.Id;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsAnyfin(this Card card) => card.Id == AnyfinCanHappen.Id;
+ }
+}
diff --git a/src/AnyfinView.cs b/src/AnyfinView.cs
new file mode 100644
index 0000000..e5c4f3c
--- /dev/null
+++ b/src/AnyfinView.cs
@@ -0,0 +1,44 @@
+using System.Linq;
+using System.Windows;
+using Hearthstone_Deck_Tracker;
+using Hearthstone_Deck_Tracker.Hearthstone;
+
+namespace HDT.Plugins.Graveyard
+{
+ public class AnyfinView : NormalView
+ {
+ private HearthstoneTextBlock _dmg;
+
+ public static bool isValid () {
+ return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == "LOE_026") > -1;
+ }
+
+ public AnyfinView () {
+ // Section Label
+ Label.Text = "Anyfin Can Happen";
+
+ // Damage Label
+ _dmg = new HearthstoneTextBlock();
+ _dmg.FontSize = 24;
+ _dmg.TextAlignment = TextAlignment.Center;
+ _dmg.Text = "0";
+ Children.Add(_dmg);
+ _dmg.Visibility = Visibility.Hidden;
+ }
+
+ new public bool Update (Card card) {
+ if (card.Race != "Murloc" || !base.Update(card)) return false;
+ UpdateDamage();
+ return true;
+ }
+
+ public void UpdateDamage () {
+ // Update damage counter
+ Range damage = AnyfinCalculator.CalculateDamageDealt(Cards);
+
+ _dmg.Text = damage.Minimum == damage.Maximum ?
+ damage.Maximum.ToString() : $"{damage.Minimum} - {damage.Maximum}";
+ _dmg.Visibility = Cards.Count > 0 ? Visibility.Visible : Visibility.Hidden;
+ }
+ }
+}
diff --git a/src/Graveyard.cs b/src/Graveyard.cs
new file mode 100644
index 0000000..357eb57
--- /dev/null
+++ b/src/Graveyard.cs
@@ -0,0 +1,107 @@
+using System;
+using System.ComponentModel;
+using System.Windows.Controls;
+using System.Windows.Media;
+using Hearthstone_Deck_Tracker;
+using Hearthstone_Deck_Tracker.Utility.Logging;
+using Hearthstone_Deck_Tracker.API;
+using Core = Hearthstone_Deck_Tracker.API.Core;
+using Card = Hearthstone_Deck_Tracker.Hearthstone.Card;
+
+namespace HDT.Plugins.Graveyard
+{
+ public class Graveyard
+ {
+ // The views
+ public NormalView Normal;
+ public ResurrectView Resurrect;
+ public AnyfinView Anyfin;
+ public NZothView NZoth;
+
+ private StackPanel _vertical;
+
+ public Graveyard () {
+ // Create container
+ _vertical = new StackPanel();
+ _vertical.Orientation = Orientation.Vertical;
+ _vertical.RenderTransform = new ScaleTransform(
+ Config.Instance.OverlayPlayerScaling / 100,
+ Config.Instance.OverlayPlayerScaling / 100);
+ Core.OverlayCanvas.Children.Add(_vertical);
+
+ // Stick to the left of the player panal
+ var border = Core.OverlayCanvas.FindName("BorderStackPanelPlayer") as Border;
+ DependencyPropertyDescriptor.FromProperty(Canvas.LeftProperty, typeof(Border))
+ .AddValueChanged(border, Layout);
+ DependencyPropertyDescriptor.FromProperty(Canvas.TopProperty, typeof(Border))
+ .AddValueChanged(border, Layout);
+ DependencyPropertyDescriptor.FromProperty(StackPanel.ActualWidthProperty, typeof(StackPanel))
+ .AddValueChanged(_vertical, Layout);
+
+ // Connect events
+ GameEvents.OnGameStart.Add(Reset);
+ DeckManagerEvents.OnDeckSelected.Add(d => Reset());
+ GameEvents.OnPlayerPlayToGraveyard.Add(Update);
+
+ GameEvents.OnOpponentPlayToGraveyard.Add(c => Anyfin?.Update(c));
+ GameEvents.OnPlayerPlay.Add(c => Anyfin?.UpdateDamage());
+ GameEvents.OnOpponentPlay.Add(c => Anyfin?.UpdateDamage());
+ }
+
+ public void Dispose () {
+ Core.OverlayCanvas.Children.Remove(_vertical);
+ var border = Core.OverlayCanvas.FindName("BorderStackPanelPlayer") as Border;
+ DependencyPropertyDescriptor.FromProperty(Canvas.LeftProperty, typeof(Border))
+ .RemoveValueChanged(border, Layout);
+ DependencyPropertyDescriptor.FromProperty(Canvas.TopProperty, typeof(Border))
+ .RemoveValueChanged(border, Layout);
+ DependencyPropertyDescriptor.FromProperty(StackPanel.ActualWidthProperty, typeof(StackPanel))
+ .RemoveValueChanged(_vertical, Layout);
+ }
+
+ private void Layout (object obj, EventArgs e) {
+ var border = Core.OverlayCanvas.FindName("BorderStackPanelPlayer") as Border;
+ Canvas.SetLeft(_vertical, Canvas.GetLeft(border) - _vertical.ActualWidth * Config.Instance.OverlayPlayerScaling / 100 - 10);
+ Canvas.SetTop(_vertical, Canvas.GetTop(border));
+ }
+
+ /**
+ * Clear then recreate all Views.
+ */
+ public void Reset () {
+ _vertical.Children.Clear();
+
+ if (Settings.Default.ResurrectEnabled && ResurrectView.isValid()) {
+ Resurrect = new ResurrectView();
+ _vertical.Children.Add(Resurrect);
+ Normal = null;
+ } else if (Settings.Default.NormalEnabled) {
+ Normal = new NormalView();
+ _vertical.Children.Add(Normal);
+ Resurrect = null;
+ }
+
+ if (Settings.Default.AnyfinEnabled && AnyfinView.isValid()) {
+ Anyfin = new AnyfinView();
+ _vertical.Children.Add(Anyfin);
+ } else {
+ Anyfin = null;
+ }
+
+ if (Settings.Default.NZothEnabled && NZothView.isValid()) {
+ NZoth = new NZothView();
+ _vertical.Children.Add(NZoth);
+ } else {
+ NZoth = null;
+ }
+ }
+
+ public void Update (Card card) {
+ var a = Anyfin?.Update(card) ?? false;
+ var b = NZoth?.Update(card) ?? false;
+ var c = Resurrect?.Update(card) ?? false;
+ if (!(a || b || c))
+ Normal?.Update(card);
+ }
+ }
+}
diff --git a/src/GraveyardPlugin.cs b/src/GraveyardPlugin.cs
new file mode 100644
index 0000000..e51d6ef
--- /dev/null
+++ b/src/GraveyardPlugin.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Windows.Controls;
+using Hearthstone_Deck_Tracker.Plugins;
+
+namespace HDT.Plugins.Graveyard
+{
+ public class GraveyardPlugin : IPlugin
+ {
+ public Graveyard GraveyardInstance;
+
+ public string Author
+ {
+ get { return "RedHatter"; }
+ }
+
+ public string ButtonText
+ {
+ get { return "Settings"; }
+ }
+
+ public string Description
+ {
+ get { return "Displays minions that have died this game. Includes specialized displays: deathrattle minions for N'Zoth decks, resurect chance for Resurect Priest, and Murloc minions with a damage calculator for Anyfin Can Happen."; }
+ }
+
+ public MenuItem MenuItem
+ {
+ get { return null; }
+ }
+
+ public string Name
+ {
+ get { return "Graveyard"; }
+ }
+
+ public void OnButtonPress()
+ {
+ SettingsView.Flyout.IsOpen = true;
+ }
+
+ public void OnLoad()
+ {
+ GraveyardInstance = new Graveyard ();
+ }
+
+ public void OnUnload()
+ {
+ GraveyardInstance.Dispose();
+ GraveyardInstance = null;
+ }
+
+ public void OnUpdate() {}
+
+ public Version Version
+ {
+ get { return new Version(1, 0, 0); }
+ }
+ }
+}
diff --git a/src/NZothView.cs b/src/NZothView.cs
new file mode 100644
index 0000000..ff509ac
--- /dev/null
+++ b/src/NZothView.cs
@@ -0,0 +1,24 @@
+using System.Linq;
+using System.Windows;
+using Hearthstone_Deck_Tracker;
+using Hearthstone_Deck_Tracker.Utility.Logging;
+using Hearthstone_Deck_Tracker.Hearthstone;
+
+namespace HDT.Plugins.Graveyard
+{
+ public class NZothView : NormalView
+ {
+ public static bool isValid () {
+ return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == "OG_133") > -1;
+ }
+
+ public NZothView () {
+ // Section Label
+ Label.Text = "N'Zoth, the Corruptor";
+ }
+
+ new public bool Update (Card card) {
+ return card.Mechanics.Contains("Deathrattle") && base.Update(card);
+ }
+ }
+}
diff --git a/src/NormalView.cs b/src/NormalView.cs
new file mode 100644
index 0000000..b1422ab
--- /dev/null
+++ b/src/NormalView.cs
@@ -0,0 +1,59 @@
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Collections.Generic;
+using Hearthstone_Deck_Tracker;
+using Hearthstone_Deck_Tracker.Utility.Logging;
+using Hearthstone_Deck_Tracker.Controls;
+using Card = Hearthstone_Deck_Tracker.Hearthstone.Card;
+
+namespace HDT.Plugins.Graveyard
+{
+ public class NormalView : StackPanel
+ {
+ public List Cards;
+ public HearthstoneTextBlock Label;
+ public AnimatedCardList View;
+
+ public NormalView () {
+ Orientation = Orientation.Vertical;
+
+ // Section Label
+ Label = new HearthstoneTextBlock();
+ Label.FontSize = 16;
+ Label.TextAlignment = TextAlignment.Center;
+ Label.Text = "Graveyard";
+ var margin = Label.Margin;
+ margin.Top = 20;
+ Label.Margin = margin;
+ Children.Add(Label);
+ Label.Visibility = Visibility.Hidden;
+
+ // Card View
+ View = new AnimatedCardList();
+ Children.Add(View);
+ Cards = new List();
+ }
+
+ public bool Update (Card card) {
+ if (card.Type != "Minion")
+ return false;
+
+ // Increment
+ var match = Cards.FirstOrDefault(c => c.Name == card.Name);
+ if (match != null) {
+ Cards.Remove(match);
+ card = match.Clone() as Card;
+ card.Count++;
+ }
+
+ // Update View
+ Cards.Add(card);
+ View.Update(Cards, false);
+
+ Label.Visibility = Visibility.Visible;
+
+ return true;
+ }
+ }
+}
diff --git a/src/ResurrectView.cs b/src/ResurrectView.cs
new file mode 100644
index 0000000..8362717
--- /dev/null
+++ b/src/ResurrectView.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Shapes;
+using System.Windows.Controls;
+using System.Collections.Generic;
+using Hearthstone_Deck_Tracker;
+using Hearthstone_Deck_Tracker.Hearthstone;
+
+namespace HDT.Plugins.Graveyard
+{
+ public class ResurrectView : NormalView
+ {
+ private Dictionary _chances;
+
+ public static bool isValid () {
+ return Core.Game.Player.PlayerCardList.FindIndex(card =>
+ card.Id == "BRM_017" || card.Id == "KAR_204") > -1;
+ }
+
+ public ResurrectView () {
+ // Section Label
+ Label.Text = "Resurrect";
+
+ _chances = new Dictionary();
+ }
+
+ new public bool Update (Card card) {
+ if (!base.Update(card)) return false;
+
+ var count = (double) Cards.Aggregate(0, (total, c) => total + c.Count);
+ for (var i = 0; i < Cards.Count(); i++) {
+ if (!_chances.ContainsKey(Cards[i])) {
+ var chance = new HearthstoneTextBlock();
+ chance.FontSize = 18;
+ chance.TextAlignment = TextAlignment.Left;
+ var grid = (View.Items.GetItemAt(i) as UserControl).Content as Grid;
+ grid.Width = 260;
+ (grid.Children[0] as Rectangle).HorizontalAlignment = HorizontalAlignment.Right;
+ (grid.Children[1] as Rectangle).Width = 260;
+ grid.Children.Add(chance);
+ _chances.Add(Cards[i], chance);
+ }
+
+ _chances[Cards[i]].Text = $"{Math.Round(Cards[i].Count / count * 100)}%";
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/Settings.Designer.cs b/src/Settings.Designer.cs
new file mode 100644
index 0000000..6cb9be7
--- /dev/null
+++ b/src/Settings.Designer.cs
@@ -0,0 +1,65 @@
+namespace HDT.Plugins.Graveyard {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
+ public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool NormalEnabled {
+ get {
+ return ((bool)(this["NormalEnabled"]));
+ }
+ set {
+ this["NormalEnabled"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ResurrectEnabled {
+ get {
+ return ((bool)(this["ResurrectEnabled"]));
+ }
+ set {
+ this["ResurrectEnabled"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool AnyfinEnabled {
+ get {
+ return ((bool)(this["AnyfinEnabled"]));
+ }
+ set {
+ this["AnyfinEnabled"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool NZothEnabled {
+ get {
+ return ((bool)(this["NZothEnabled"]));
+ }
+ set {
+ this["NZothEnabled"] = value;
+ }
+ }
+
+ }
+}
diff --git a/src/Settings.settings b/src/Settings.settings
new file mode 100644
index 0000000..a2fbdab
--- /dev/null
+++ b/src/Settings.settings
@@ -0,0 +1,18 @@
+
+
+
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+
diff --git a/src/SettingsView.xaml b/src/SettingsView.xaml
new file mode 100644
index 0000000..4717f5e
--- /dev/null
+++ b/src/SettingsView.xaml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SettingsView.xaml.cs b/src/SettingsView.xaml.cs
new file mode 100644
index 0000000..ea4a4ec
--- /dev/null
+++ b/src/SettingsView.xaml.cs
@@ -0,0 +1,35 @@
+using System.Windows.Controls;
+using MahApps.Metro.Controls;
+using Hearthstone_Deck_Tracker;
+
+namespace HDT.Plugins.Graveyard
+{
+ public partial class SettingsView : StackPanel
+ {
+ private static Flyout _flyout;
+ public static Flyout Flyout {
+ get {
+ if (_flyout == null)
+ _flyout = CreateSettingsFlyout();
+
+ return _flyout;
+ }
+ }
+
+ private static Flyout CreateSettingsFlyout()
+ {
+ var settings = new Flyout();
+ settings.Position = Position.Left;
+ Panel.SetZIndex(settings, 100);
+ settings.Header = "Graveyard Settings";
+ settings.Content = new SettingsView();
+ Core.MainWindow.Flyouts.Items.Add(settings);
+ return settings;
+ }
+
+ public SettingsView() {
+ InitializeComponent();
+ Settings.Default.PropertyChanged += (sender, e) => Settings.Default.Save();
+ }
+ }
+}