diff --git a/.github/workflows/build+upload.yml b/.github/workflows/build+upload.yml new file mode 100644 index 0000000..3df0349 --- /dev/null +++ b/.github/workflows/build+upload.yml @@ -0,0 +1,45 @@ +name: Build+Upload +on: + pull_request: + branches: [ master ] + workflow_dispatch: + +env: + PROJECT: ${{ 'Graveyard' }} + +jobs: + build-and-upload: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3.5.2 # https://github.com/marketplace/actions/checkout + - name: Get Head Commit Message + id: get_head_commit + shell: pwsh + run: | + $Message = git log --format=%B -n 1 HEAD + Write-Output "title=$($Message | Select -First 1)" >> $Env:GITHUB_OUTPUT + $Body = $Message | Select -Skip 1 + Write-Output "body=$Body" >> $Env:GITHUB_OUTPUT + $Body | Out-File Release.md + - uses: microsoft/setup-msbuild@v1.3.1 # https://github.com/marketplace/actions/setup-msbuild + - uses: NuGet/setup-nuget@v1.2.0 # https://github.com/marketplace/actions/setup-nuget-exe-for-use-with-actions + - run: .\scripts\update-libraries.ps1 + - run: nuget restore "${{ env.PROJECT }}.sln" + - id: set_version + run: | + $build = 2000 + $env:GITHUB_RUN_NUMBER + $buildVersion = .\scripts\assembly-version.ps1 -project "${{ env.PROJECT }}" -build $build -overrideVersion ${{ github.ref }} + echo "buildVersion=$buildVersion" >> $env:GITHUB_OUTPUT + # https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ + # https://stackoverflow.com/a/74377542/146360 + - run: msbuild.exe "${{ env.PROJECT }}.sln" /p:platform="x86" /p:configuration="Release" + - id: name_zip + run: | + $zipName = "${{ env.PROJECT }}-v${{ steps.set_version.outputs.buildVersion }}" + echo "zipName=$zipName" >> $env:GITHUB_OUTPUT + - uses: actions/upload-artifact@v4.6.2 # https://github.com/marketplace/actions/upload-a-build-artifact + with: + name: "${{ steps.name_zip.outputs.zipName }}" + path: | + .\${{ env.PROJECT }}\bin\x86\Release\${{ env.PROJECT }}.dll + .\${{ env.PROJECT }}\bin\x86\Release\*-* diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..9057607 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,52 @@ +name: Build+Release +on: + push: + branches: [ master ] + workflow_dispatch: + +env: + PROJECT: ${{ 'Graveyard' }} + +jobs: + build-and-release: + runs-on: windows-latest + steps: + - uses: actions/checkout@v3.5.2 # https://github.com/marketplace/actions/checkout + - name: Get Head Commit Message + id: get_head_commit + shell: pwsh + run: | + $Message = git log --format=%B -n 1 HEAD + Write-Output "title=$($Message | Select -First 1)" >> $Env:GITHUB_OUTPUT + $Body = $Message | Select -Skip 1 + Write-Output "body=$Body" >> $Env:GITHUB_OUTPUT + $Body | Out-File Release.md + - uses: microsoft/setup-msbuild@v1.3.1 # https://github.com/marketplace/actions/setup-msbuild + - uses: NuGet/setup-nuget@v1.2.0 # https://github.com/marketplace/actions/setup-nuget-exe-for-use-with-actions + - run: .\scripts\update-libraries.ps1 + - run: nuget restore "${{ env.PROJECT }}.sln" + - id: set_version + run: | + $build = 4000 + $env:GITHUB_RUN_NUMBER + $buildVersion = .\scripts\assembly-version.ps1 -project "${{ env.PROJECT }}" -build $build -overrideVersion ${{ github.ref }} + echo "buildVersion=$buildVersion" >> $env:GITHUB_OUTPUT + # https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ + # https://stackoverflow.com/a/74377542/146360 + - run: msbuild.exe "${{ env.PROJECT }}.sln" /p:platform="x86" /p:configuration="Release" + - id: create_zip + run: | + $zipName = "${{ env.PROJECT }}-v${{ steps.set_version.outputs.buildVersion }}.zip" + 7z a $zipName .\${{ env.PROJECT }}\bin\x86\Release\${{ env.PROJECT }}.dll .\${{ env.PROJECT }}\bin\x86\Release\*-* + echo "zipName=$zipName" >> $env:GITHUB_OUTPUT + - uses: ncipollo/release-action@v1.12.0 # https://github.com/marketplace/actions/create-release + id: create_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: "v${{ steps.set_version.outputs.buildVersion }}: ${{ steps.get_head_commit.outputs.title }}" + bodyFile: Release.md + tag: v${{ steps.set_version.outputs.buildVersion }} + draft: false + prerelease: true + artifacts: "${{ steps.create_zip.outputs.zipName }}" + artifactContentType: "application/zip" diff --git a/Graveyard.sln b/Graveyard.sln index 01f6a03..84a9b10 100644 --- a/Graveyard.sln +++ b/Graveyard.sln @@ -6,8 +6,11 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Graveyard", "Graveyard\Graveyard.csproj", "{59C8CCA4-7F9B-4592-87A8-A23ADED7759E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{53E4F774-8043-46B4-B786-BB32DEDA2CF1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{B860F31B-7A79-4229-864D-E02D5D1BBD76}" ProjectSection(SolutionItems) = preProject - update-libraries.ps1 = update-libraries.ps1 + scripts\assembly-version.ps1 = scripts\assembly-version.ps1 + scripts\update-libraries.ps1 = scripts\update-libraries.ps1 EndProjectSection EndProject Global @@ -30,6 +33,9 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B860F31B-7A79-4229-864D-E02D5D1BBD76} = {53E4F774-8043-46B4-B786-BB32DEDA2CF1} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A5703983-F932-4DBC-94F0-0FE31B4E6A90} EndGlobalSection diff --git a/Graveyard/AnimateDeadView.cs b/Graveyard/AnimateDeadView.cs new file mode 100644 index 0000000..eccd1e6 --- /dev/null +++ b/Graveyard/AnimateDeadView.cs @@ -0,0 +1,20 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class AnimateDeadView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Priest.AnimateDead) + { + Name = "AnimateDead", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Type == "Minion" && card.Cost <= 3, + }); + } + } +} diff --git a/Graveyard/AntonidasView.cs b/Graveyard/AntonidasView.cs new file mode 100644 index 0000000..f9fcd01 --- /dev/null +++ b/Graveyard/AntonidasView.cs @@ -0,0 +1,21 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + public class AntonidasView + { + private static MultiTurnView.ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new MultiTurnView.ViewConfig(Mage.GrandMagusAntonidas) + { + Name = "Antonidas", + Enabled = "AntonidasEnabled", + CreateView = () => new MultiTurnView(3), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.GetSchool() == School.Fire, + }); + } + } +} diff --git a/Graveyard/AnyfinCalculator.cs b/Graveyard/AnyfinCalculator.cs deleted file mode 100644 index b1d2964..0000000 --- a/Graveyard/AnyfinCalculator.cs +++ /dev/null @@ -1,297 +0,0 @@ -/** - * 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(); - - // Calculate which of the murlocs give buffs (now only your own murlocs) - var nonSilencedWarleaders = murlocs.Count(m => m.BoardState == MurlocInfo.State.OnBoard && m.Murloc.IsWarleader() && !m.IsSilenced); - var nonSilencedGrimscales = murlocs.Count(m => m.BoardState == MurlocInfo.State.OnBoard && m.Murloc.IsGrimscale() && !m.IsSilenced); - - // Get the murlocs that will be summoned - var murlocsToBeSummoned = murlocs.Count(m => m.BoardState == MurlocInfo.State.Dead); - - // Go through each currently buffed murloc and remove the buffs - 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); - } - } - - // Add the now summoned buffers to the pool - nonSilencedWarleaders += murlocs.Count(m => m.BoardState == MurlocInfo.State.Dead && m.Murloc.IsWarleader()); - nonSilencedGrimscales += murlocs.Count(m => m.BoardState == MurlocInfo.State.Dead && m.Murloc.IsGrimscale()); - - // Go through the murlocs on the board and apply all of the final buffs - 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/Graveyard/AnyfinView.cs b/Graveyard/AnyfinView.cs index caa9c20..f409c64 100644 --- a/Graveyard/AnyfinView.cs +++ b/Graveyard/AnyfinView.cs @@ -1,65 +1,40 @@ using Hearthstone_Deck_Tracker; +using Hearthstone_Deck_Tracker.API; using Hearthstone_Deck_Tracker.Hearthstone; using System.Collections.Generic; using System.Linq; using System.Windows; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { public class AnyfinView : NormalView { - private HearthstoneTextBlock _dmg; - private HearthstoneTextBlock _secondDmg; - - public static bool isValid() - { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Paladin.AnyfinCanHappen) > -1; - } - - public AnyfinView() + private static AnyfinViewConfig _Config; + internal static ViewConfig Config { - // Section Label - Label.Text = Strings.GetLocalized("Anyfin"); - - // Damage Label - _dmg = new HearthstoneTextBlock(); - _dmg.FontSize = 24; - _dmg.TextAlignment = TextAlignment.Center; - _dmg.Text = "0"; - Children.Add(_dmg); - _dmg.Visibility = Visibility.Hidden; - - _secondDmg = new HearthstoneTextBlock(); - _secondDmg.FontSize = 24; - _secondDmg.TextAlignment = TextAlignment.Center; - _secondDmg.Text = "0"; - Children.Add(_secondDmg); - _secondDmg.Visibility = Visibility.Hidden; - } - - public bool Update(Card card) - { - if (card.Race != "Murloc" || !base.Update(card)) + get => _Config ?? (_Config = new AnyfinViewConfig() { - return false; + Name = "Anyfin", + Enabled = "AnyfinEnabled", + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + CreateView = () => new AnyfinView(), + Condition = card => card.Race == "Murloc" || card.Race == "All", + }); + } + + internal class AnyfinViewConfig : ViewConfig + { + public AnyfinViewConfig() : base(Paladin.AnyfinCanHappen) + { + + } + + public override void RegisterView(ViewBase view, bool isDefault = false) + { + base.RegisterView(view, isDefault); + RegisterForCardEvent(GameEvents.OnOpponentPlayToGraveyard, view.Update, isDefault); } - - 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; - - List moreCards = Cards.Select(c => c.Clone() as Card).ToList(); - moreCards.AddRange(Cards); - Range secondDamage = AnyfinCalculator.CalculateDamageDealt(moreCards); - _secondDmg.Text = damage.Minimum == damage.Maximum ? secondDamage.Maximum.ToString() : $"{secondDamage.Minimum} - {secondDamage.Maximum}"; - _secondDmg.Visibility = Cards.Count > 0 ? Visibility.Visible : Visibility.Hidden; - } + } } } diff --git a/Graveyard/AzeriteRatView.cs b/Graveyard/AzeriteRatView.cs new file mode 100644 index 0000000..2850223 --- /dev/null +++ b/Graveyard/AzeriteRatView.cs @@ -0,0 +1,39 @@ +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Hearthstone; +using System.Windows; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class AzeriteRatView : NormalView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig( + Deathknight.ReapWhatYouSow, + Deathknight.SkeletonCrew) + { + Name = Database.GetCardFromId(HearthDb.CardIds.NonCollectible.Deathknight.KoboldMiner_TheAzeriteRatToken)?.LocalizedName ?? "AzeriteRat", + CreateView = () => new AzeriteRatView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Type == "Minion", + }); + } + + public override bool Update(Card card) + { + if (!Condition(card)) return false; + + var removed = Cards.RemoveAll(c => c.Cost < card.Cost); + if (removed > 0 || Cards.Count == 0) + { + Cards.Add(card.Clone() as Card); + View.Update(Cards, true); + Visibility = Visibility.Visible; + } +#warning Always return false to pass card onto GraveyardView, other unwanted side effects? + return false; + } + } +} diff --git a/Graveyard/CardExtensions.cs b/Graveyard/CardExtensions.cs new file mode 100644 index 0000000..22335b7 --- /dev/null +++ b/Graveyard/CardExtensions.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Card = Hearthstone_Deck_Tracker.Hearthstone.Card; +using DbCard = HearthDb.Card; + +namespace HDT.Plugins.Graveyard +{ + public enum School + { + NotASpell = -1, + General = 0, + Arcane = 1, + Fire = 2, + Frost = 3, + Nature = 4, + Holy = 5, + Shadow = 6, + Fel = 7, + } + + internal static class CardExtensions + { + private static readonly Dictionary SchoolCache = new Dictionary(); + + public static School GetSchool(this Card card) + { + if (card.Type == "Spell") + { + if (SchoolCache.ContainsKey(card.Id)) + { + return SchoolCache[card.Id]; + } + + HearthDb.Cards.All.TryGetValue(card.Id, out DbCard dbCard); + + if (dbCard != null) + { + var school = School.General; + var tryGetSchool = (School?)dbCard.SpellSchool; + if (tryGetSchool.HasValue) + { + school = tryGetSchool.Value; + } + SchoolCache.Add(card.Id, school); + return school; + }; + } + return School.NotASpell; + } + } +} diff --git a/Graveyard/CardUpdatePoller.cs b/Graveyard/CardUpdatePoller.cs new file mode 100644 index 0000000..cd5b6af --- /dev/null +++ b/Graveyard/CardUpdatePoller.cs @@ -0,0 +1,95 @@ +using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.Utility.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HDT.Plugins.Graveyard +{ + internal enum DefaultMode + { + None, + First, + Last, + Custom, + } + + internal class CardUpdatePoller + { + private readonly List> Registered = new List>(); + + public DefaultMode DefaultMode { get; private set; } + + public Func Default { get; private set; } + + public CardUpdatePoller(DefaultMode mode = DefaultMode.None) + { + DefaultMode = mode == DefaultMode.Custom ? DefaultMode.None : mode; + } + + public CardUpdatePoller(Func update) + { + SetDefault(update); + } + + private void SetDefault(Func update) + { + Default = update; + DefaultMode = DefaultMode.Custom; + } + + public void Register(Func update, bool isDefault = false) + { + if (isDefault) + { + SetDefault(update); + } + else + { + Registered.Add(update); + } + } + + public void Poll(Card card) + { + var result = false; + int startFor = 0, endFor = Registered.Count; + if (DefaultMode == DefaultMode.First) + { + startFor += 1; + } + else if (DefaultMode == DefaultMode.Last) + { + endFor -= 1; + } + for (int i = startFor; i < endFor; i++) + { + try + { + var update = Registered[i]; + result |= update.Invoke(card); + } + catch (Exception ex) + { + Log.Error(ex); + } + } + if (!result && DefaultMode != DefaultMode.None) + { + result |= Default.Invoke(card); + } + _LastResult = result; + } + + public bool? LastResult => _LastResult; + private bool? _LastResult; + + + public void Clear() + { + Registered.Clear(); + } + } +} diff --git a/Graveyard/CavernsView.cs b/Graveyard/CavernsView.cs index 294515d..f9cbbdb 100644 --- a/Graveyard/CavernsView.cs +++ b/Graveyard/CavernsView.cs @@ -1,24 +1,21 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class CavernsView : NormalView + public class CavernsView { - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Rogue.TheCavernsBelow) > -1; - } - - public CavernsView() - { - // Section Label - Label.Text = Strings.GetLocalized("Caverns"); - } - - public bool Update(Card card) - { - return base.Update(card); + get => _Config ?? (_Config = new ViewConfig(Rogue.TheCavernsBelow) + { + Name = "Caverns", + Enabled = "CavernsEnabled", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Type == "Minion", + }); } } } diff --git a/Graveyard/ChancesTracker.cs b/Graveyard/ChancesTracker.cs index 9f8c59e..a287250 100644 --- a/Graveyard/ChancesTracker.cs +++ b/Graveyard/ChancesTracker.cs @@ -7,32 +7,47 @@ using Hearthstone_Deck_Tracker; using Hearthstone_Deck_Tracker.Controls; using Card = Hearthstone_Deck_Tracker.Hearthstone.Card; +using Hearthstone_Deck_Tracker.Utility.Logging; namespace HDT.Plugins.Graveyard { public class ChancesTracker { - private Dictionary _chances = new Dictionary(); + private readonly Dictionary Chances = new Dictionary(); public void Update(Card card, List Cards, AnimatedCardList View) { var count = (double)Cards.Aggregate(0, (total, c) => total + c.Count); for (var i = 0; i < Cards.Count(); i++) { - if (!_chances.ContainsKey(Cards[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 Hearthstone_Deck_Tracker.Controls.Card).HorizontalAlignment = HorizontalAlignment.Right; - (grid.Children[1] as Rectangle).Width = 260; - grid.Children.Add(chance); - _chances.Add(Cards[i], chance); + var chance = new HearthstoneTextBlock + { + FontSize = 18, + TextAlignment = TextAlignment.Left + }; + if (View.AnimatedCards[i] is UserControl control && control.Content is Grid grid) + { + grid.Width = 260; + if (grid.Children[0] is Hearthstone_Deck_Tracker.Controls.Card cardControl) + cardControl.HorizontalAlignment = HorizontalAlignment.Right; + else + Log.Warn("Expected Hearthstone_Deck_Tracker.Controls.Card, check AnimatedCard for layout changes"); + if (grid.Children[2] is Rectangle rectangle) + rectangle.Width = 260; + else + Log.Warn("Expected Rectangle, check AnimatedCard for layout changes"); + grid.Children.Add(chance); + } + else + { + Log.Warn("Expected UserControl and Grid, check AnimatedCard for layout changes"); + } + Chances.Add(Cards[i], chance); } - _chances[Cards[i]].Text = $"{Math.Round(Cards[i].Count / count * 100)}%"; + Chances[Cards[i]].Text = $"{Math.Round(Cards[i].Count / count * 100)}%"; } } } diff --git a/Graveyard/ChancesView.cs b/Graveyard/ChancesView.cs new file mode 100644 index 0000000..9111ac4 --- /dev/null +++ b/Graveyard/ChancesView.cs @@ -0,0 +1,33 @@ +using Hearthstone_Deck_Tracker; +using Hearthstone_Deck_Tracker.Hearthstone; +using System.Windows; + +namespace HDT.Plugins.Graveyard +{ + public class ChancesView : NormalView + { + private readonly ChancesTracker Chances = new ChancesTracker(); + + public ChancesView() : base() + { + Margin = new Thickness(Margin.Left + Settings.Default.ChancesViewLeft, Margin.Top, Margin.Right, Margin.Right); + } + + public override HearthstoneTextBlock AddTitle(string text = "") + { + var label = base.AddTitle(text); + label.Margin = new Thickness(label.Margin.Left - Settings.Default.ChancesViewLeft, label.Margin.Top, label.Margin.Right, label.Margin.Right); + return label; + } + + public override bool Update(Card card) + { + if (base.Update(card)) + { + Chances.Update(card, Cards, View); + return true; + } + return false; + } + } +} diff --git a/Graveyard/CultivationView.cs b/Graveyard/CultivationView.cs new file mode 100644 index 0000000..36ac15f --- /dev/null +++ b/Graveyard/CultivationView.cs @@ -0,0 +1,30 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + public class CultivationView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Druid.Cultivation) + { + ShowFirst = () => true, + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Name.Contains("Treant"), + }); + } + + internal class ViewConfig : Plugins.Graveyard.ViewConfig + { + public ViewConfig(params string[] showOn) : base(showOn) { } + public override void RegisterView(ViewBase view, bool isDefault = false) + { + base.RegisterView(view, isDefault); + Plugin.Events.OnPlayerCreateInPlay.Register(view.Update); + } + } + } +} diff --git a/Graveyard/DeathrattleView.cs b/Graveyard/DeathrattleView.cs index 01b26ab..e026253 100644 --- a/Graveyard/DeathrattleView.cs +++ b/Graveyard/DeathrattleView.cs @@ -1,31 +1,52 @@ -using Hearthstone_Deck_Tracker; +using Hearthstone_Deck_Tracker.API; using Hearthstone_Deck_Tracker.Hearthstone; +using System; using System.Linq; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class DeathrattleView : NormalView + public class DeathrattleView { - public static bool isValid() - { - return Core.Game.Player.PlayerCardList.FindIndex(card => - card.Id == HearthDb.CardIds.Collectible.Hunter.JewelOfNzoth || - card.Id == HearthDb.CardIds.Collectible.Neutral.Vectus || - card.Id == HearthDb.CardIds.Collectible.Hunter.NineLives || - card.Id == HearthDb.CardIds.Collectible.Neutral.DaUndatakah || - card.Id == HearthDb.CardIds.Collectible.Priest.TwilightsCall - ) > -1; - } + private static readonly string SharedName = "Deathrattle"; + private static readonly Func SharedCreateView = () => new NormalView(); + private static readonly Predicate SharedCondition = card => card.Mechanics.Contains("Deathrattle") && card.Id != Rogue.UnearthedRaptor; - public DeathrattleView() + private static ViewConfig _PlayerConfig; + internal static ViewConfig PlayerConfig { - // Section Label - Label.Text = Strings.GetLocalized("Deathrattle"); + get => _PlayerConfig ?? (_PlayerConfig = new ViewConfig( + Hunter.DefenseAttorneyNathanos, + Priest.XyrellaTheDevout, + Warlock.TamsinsPhylactery, + Priest.AmuletOfUndying, + Rogue.CounterfeitBlade, + Hunter.JewelOfNzoth, + Neutral.Vectus, + Hunter.NineLives, + Neutral.DaUndatakah, + Priest.TwilightsCall, + Deathknight.Boneshredder) + { + Name = SharedName, + Enabled = "DeathrattleEnabled", + Condition = SharedCondition, + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + CreateView = SharedCreateView, + }); } - public bool Update(Card card) + private static ViewConfig _OpponentConfig; + internal static ViewConfig OpponentConfig { - return card.Mechanics.Contains("Deathrattle") && card.Id != HearthDb.CardIds.Collectible.Rogue.UnearthedRaptor && base.Update(card); + get => _OpponentConfig ?? (_OpponentConfig = new ViewConfig() + { + Name = "Deathrattle", + Enabled = "OpponentDeathrattleEnabled", + Condition = SharedCondition, + UpdateOn = GameEvents.OnOpponentPlayToGraveyard, + CreateView = SharedCreateView, + }); } } } diff --git a/Graveyard/DiscardView.cs b/Graveyard/DiscardView.cs index f2ff11d..1b54109 100644 --- a/Graveyard/DiscardView.cs +++ b/Graveyard/DiscardView.cs @@ -1,34 +1,21 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class DiscardView : NormalView + public class DiscardView { - private ChancesTracker _chances = new ChancesTracker(); - - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Warlock.CruelDinomancer) > -1; - } - - public DiscardView() - { - // Section Label - Label.Text = Strings.GetLocalized("DiscardTitle"); - } - - public bool Update(Card card) - { - if (!base.Update(card)) + get => _Config ?? (_Config = new ViewConfig(Warlock.CruelDinomancer) { - return false; - } - - // Silverware Golem and Clutchmother Zaras are still counted as discarded, even when their effects trigger - _chances.Update(card, Cards, View); - - return true; + Name = "DiscardTitle", + Enabled = "DiscardEnabled", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerHandDiscard, + Condition = card => card.Type == "Minion", + }); } } } diff --git a/Graveyard/DragoncallerAlannaView.cs b/Graveyard/DragoncallerAlannaView.cs index 0d0e629..e478401 100644 --- a/Graveyard/DragoncallerAlannaView.cs +++ b/Graveyard/DragoncallerAlannaView.cs @@ -1,23 +1,21 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class DragoncallerAlannaView : NormalView + public class DragoncallerAlannaView { - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Mage.DragoncallerAlanna) > -1; - } - - public DragoncallerAlannaView() - { - Label.Text = Strings.GetLocalized("Alanna"); - } - - public bool Update(Card card) - { - return card.Type == "Spell" && card.Cost >= 5 && base.Update(card, true); + get => _Config ?? (_Config = new ViewConfig(Mage.DragoncallerAlanna) + { + Name = "Alanna", + Enabled = "DragoncallerAlannaEnabled", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Type == "Spell" && card.Cost >= 5, + }); } } } diff --git a/Graveyard/ElwynnBoarView.cs b/Graveyard/ElwynnBoarView.cs new file mode 100644 index 0000000..095cb0f --- /dev/null +++ b/Graveyard/ElwynnBoarView.cs @@ -0,0 +1,24 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + public class ElwynnBoarView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Neutral.ElwynnBoar) + { + Name = "ElwynnBoar", + Enabled = "ElwynnBoarEnabled", + ShowFirst = () => true, + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Id == Neutral.ElwynnBoar, + }); + } + + public static bool IsAlwaysSeparate => Settings.Default.ElwynnBoarEnabled && Settings.Default.AlwaysBoarSeparately; + } +} diff --git a/Graveyard/EventManager.cs b/Graveyard/EventManager.cs new file mode 100644 index 0000000..fa839f8 --- /dev/null +++ b/Graveyard/EventManager.cs @@ -0,0 +1,100 @@ +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Enums; +using Hearthstone_Deck_Tracker.Hearthstone; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HDT.Plugins.Graveyard +{ + internal class EventManager + { + // Remember to add a GameEvents.[GameEvent].Add(EventManager.[GameEvent]) + // call to GraveyardPlugin.OnLoad for new pollers + public TurnUpdatePoller OnOpponentTurnStart { get; } = new TurnUpdatePoller(ActivePlayer.Opponent); + public CardUpdatePoller OnPlayerPlayToGraveyard { get; } = new CardUpdatePoller(); + public CardUpdatePoller OnOpponentPlayToGraveyard { get; } = new CardUpdatePoller(); + public CardUpdatePoller OnPlayerPlay { get; } = new CardUpdatePoller(); + public CardUpdatePoller OnOpponentPlay { get; } = new CardUpdatePoller(); + public CardUpdatePoller OnPlayerCreateInPlay { get; } = new CardUpdatePoller(); + public CardUpdatePoller OnOpponentCreateInPlay { get; } = new CardUpdatePoller(); + public CardUpdatePoller OnPlayerHandDiscard { get; } = new CardUpdatePoller(); + public CardUpdatePoller OnPlayerDraw { get; } = new CardUpdatePoller(); + public CardUpdatePoller OnPlayerMulligan { get; } = new CardUpdatePoller(); + public CardUpdatePoller OnPlayerGet { get; } = new CardUpdatePoller(); + + public void Clear() + { + OnPlayerPlayToGraveyard.Clear(); + OnOpponentPlayToGraveyard.Clear(); + + OnPlayerPlay.Clear(); + OnOpponentPlay.Clear(); + + OnPlayerCreateInPlay.Clear(); + OnOpponentCreateInPlay.Clear(); + + OnPlayerHandDiscard.Clear(); + + OnPlayerDraw.Clear(); + + OnPlayerMulligan.Clear(); + + OnPlayerGet.Clear(); + + OnOpponentTurnStart.Clear(); + } + + public CardUpdatePoller MapCardEvent(ActionList actionList) + { + CardUpdatePoller cardUpdatePoller; + if (actionList == GameEvents.OnPlayerPlayToGraveyard) + { + cardUpdatePoller = OnPlayerPlayToGraveyard; + } + else if (actionList == GameEvents.OnOpponentPlayToGraveyard) + { + cardUpdatePoller = OnOpponentPlayToGraveyard; + } + else if (actionList == GameEvents.OnPlayerPlay) + { + cardUpdatePoller = OnPlayerPlay; + } + else if (actionList == GameEvents.OnOpponentPlay) + { + cardUpdatePoller = OnOpponentPlay; + } + else if (actionList == GameEvents.OnPlayerCreateInPlay) + { + cardUpdatePoller = OnPlayerCreateInPlay; + } + else if (actionList == GameEvents.OnOpponentCreateInPlay) + { + cardUpdatePoller = OnOpponentCreateInPlay; + } + else if (actionList == GameEvents.OnPlayerHandDiscard) + { + cardUpdatePoller = OnPlayerHandDiscard; + } + else if (actionList == GameEvents.OnPlayerDraw) + { + cardUpdatePoller = OnPlayerDraw; + } + else if (actionList == GameEvents.OnPlayerMulligan) + { + cardUpdatePoller = OnPlayerMulligan; + } + else if (actionList == GameEvents.OnPlayerGet) + { + cardUpdatePoller = OnPlayerGet; + } + else + { + throw new ArgumentException($"Requested game event mapping not supported by Graveyard"); + } + return cardUpdatePoller; + } + } +} diff --git a/Graveyard/FizzleView.cs b/Graveyard/FizzleView.cs new file mode 100644 index 0000000..0ec8881 --- /dev/null +++ b/Graveyard/FizzleView.cs @@ -0,0 +1,48 @@ +using HearthDb.Enums; +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Hearthstone; +using System.Linq; +using System.Windows; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class FizzleView : NormalView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(FizzleId) + { + CreateView = () => new FizzleView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Id == FizzleId || card.Id == SnapshotId, + }); + } + + private static readonly string FizzleId = Neutral.PhotographerFizzle; + private static readonly string SnapshotId = HearthDb.CardIds.NonCollectible.Neutral.PhotographerFizzle_FizzlesSnapshotToken; + + public override bool Update(Card card) + { + if (!(card.Id == FizzleId || card.Id == SnapshotId)) return false; + + if (card.Id == FizzleId) + { + Cards.AddRange(Core.Game.Player.Hand.OrderBy(e => e.ZonePosition).Select(e => Database.GetCardFromId(e.CardId))); + } + else if (card.Id == SnapshotId + && Core.Game.Player.PlayerEntities + .Where(x => x.CardId == SnapshotId && (x.IsInHand || x.IsInDeck)) + .Count() == 0) + { + Cards.Clear(); + } + View.Update(Cards, false); + + Visibility = Visibility.Visible; + + return true; + } + } +} diff --git a/Graveyard/FourHorsemenView.cs b/Graveyard/FourHorsemenView.cs new file mode 100644 index 0000000..15e60a0 --- /dev/null +++ b/Graveyard/FourHorsemenView.cs @@ -0,0 +1,31 @@ +using Hearthstone_Deck_Tracker.API; +using System.Collections.Generic; +using Collectible = HearthDb.CardIds.Collectible.Neutral; +using NonCollectible = HearthDb.CardIds.NonCollectible.Neutral; + +namespace HDT.Plugins.Graveyard +{ + internal class FourHorsemenView + { + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Collectible.RivendareWarrider) + { + Name = "FourHorsemen", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => Horsemen.Contains(card.Id), + UseSoloCardNameAsTitle = false, + }); + } + private static ViewConfig _Config; + + internal static readonly List Horsemen = new List + { + Collectible.RivendareWarrider, + NonCollectible.RivendareWarrider_BlaumeuxFamineriderToken, + NonCollectible.RivendareWarrider_KorthazzDeathriderToken, + NonCollectible.RivendareWarrider_ZeliekConquestriderToken, + }; + } +} diff --git a/Graveyard/GrandFinaleView.cs b/Graveyard/GrandFinaleView.cs new file mode 100644 index 0000000..794be50 --- /dev/null +++ b/Graveyard/GrandFinaleView.cs @@ -0,0 +1,23 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + public class GrandFinaleView + { + private static MultiTurnView.ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new MultiTurnView.ViewConfig( + Mage.GrandFinale, + Mage.UnchainedGladiator) + { + Name = "GrandFinale", + Enabled = "GrandFinaleEnabled", + CreateView = () => new MultiTurnView(1), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Race == "Elemental" || card.Race == "All", + }); + } + } +} diff --git a/Graveyard/Graveyard.cs b/Graveyard/Graveyard.cs index aa04c2b..da593ec 100644 --- a/Graveyard/Graveyard.cs +++ b/Graveyard/Graveyard.cs @@ -1,365 +1,269 @@ -using System; -using System.ComponentModel; +using System.Collections.Generic; +using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; -using System.Collections.Generic; -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.API; -using Core = Hearthstone_Deck_Tracker.API.Core; +using static HearthDb.CardIds.Collectible; using Card = Hearthstone_Deck_Tracker.Hearthstone.Card; -using GameMode = Hearthstone_Deck_Tracker.Enums.GameMode; - +using Core = Hearthstone_Deck_Tracker.API.Core; namespace HDT.Plugins.Graveyard { - public class Graveyard + public class Graveyard { // The views - public NormalView Normal; - public NormalView Enemy; - - public ResurrectView Resurrect; - public AnyfinView Anyfin; - public DeathrattleView Deathrattle; - public NZothView NZoth; - public HadronoxView Hadronox; - public DiscardView Discard; - public GuldanView Guldan; - public ShudderwockView Shudderwock; - public DragoncallerAlannaView DragoncallerAlanna; - public CavernsView Caverns; - public MulchmuncherView Mulchmuncher; - public KangorView Kangor; - public WitchingHourView WitchingHour; - public TessGreymaneView TessGreymane; - public SoulwardenView Soulwarden; - public ZuljinView Zuljin; - public HoardPillagerView HoardPillager; - public LadyLiadrinView LadyLiadrin; - public NZothGotDView NZothGotD; - public RallyView Rally; - public SaurfangView Saurfang; - - private StackPanel _friendlyPanel; - private StackPanel _enemyPanel; + internal List ConfigList { get; } = new List + { + QuestlineView.FriendlyConfig, + ResurrectView.Config, + AnyfinView.Config, + DeathrattleView.PlayerConfig, + NZothView.Config, + HadronoxView.Config, + DiscardView.Config, + GuldanView.Config, + ShudderwockView.Config, + DragoncallerAlannaView.Config, + CavernsView.Config, + MulchmuncherView.Config, + KangorView.Config, + WitchingHourView.Config, + TessGreymaneView.Config, + SoulwardenView.Config, + ZuljinView.Config, + HoardPillagerView.Config, + LadyLiadrinView.Config, + NZothGotDView.Config, + RallyView.Config, + SaurfangView.Config, + YShaarjView.Config, + ElwynnBoarView.Config, + KargalView.Config, + AntonidasView.Config, + GrandFinaleView.Config, + LastPlayedView.BrilliantMacawConfig, + LastPlayedView.GreySageParrotConfig, + LastPlayedView.MonstrousParrotConfig, + LastPlayedView.SunwingSquawkerConfig, + LastPlayedView.VanessaVanCleefConfig, + LastPlayedView.LadyDarkveinConfig, + MulticasterView.Config, + ShirvallahView.Config, + JaceDarkweaverView.Config, + SI7View.Config, + HedraView.Config, + ImpKingView.Config, + KelthuzadTheInevitableView.Config, + ReanimateView.Config, + AnimateDeadView.Config, + UnendingSwarmView.Config, + HighCultistBasalephView.Config, + SivaraView.Config, + FourHorsemenView.Config, + RelicView.Config, + LastPlayedView.AsvedonConfig, + StranglethornHeartView.Config, + MixtapeView.Config, + LastPlayedView.LastRiffConfig, + SpellsCreatedView.Config, + MenagerieView.Config, + FizzleView.Config, + TyrView.Config, + TyrsTearsView.Config, + MinionsCreatedView.Config, + CultivationView.Config, + OgreGangView.Config, + AzeriteRatView.Config, + LastPlayedView.EndgameConfig, + JepettoView.Config, + InventorBoomView.Config, + }; + + private readonly FrameworkElement FriendlyView; + private readonly FrameworkElement EnemyView; + + private readonly StackPanel FriendlyPanel; + private readonly StackPanel EnemyPanel; + + private StackPanel FirstPanel; public static InputManager Input; public Graveyard() { - // Create container - _enemyPanel = new StackPanel(); - _enemyPanel.Orientation = Orientation.Vertical; - Core.OverlayCanvas.Children.Add(_enemyPanel); - Canvas.SetTop(_enemyPanel, Settings.Default.EnemyTop); - Canvas.SetLeft(_enemyPanel, Settings.Default.EnemyLeft); - - // Create container - _friendlyPanel = new StackPanel(); - _friendlyPanel.Orientation = Settings.Default.FriendlyOrientation; - Core.OverlayCanvas.Children.Add(_friendlyPanel); - Canvas.SetTop(_friendlyPanel, Settings.Default.PlayerTop); - Canvas.SetLeft(_friendlyPanel, Settings.Default.PlayerLeft); + // Create container + EnemyPanel = new StackPanel + { + Orientation = Orientation.Vertical + }; +#if DEBUGXAML + EnemyView = new Border + { + BorderBrush = Brushes.Green, + BorderThickness = new Thickness(1), + Child = EnemyPanel, + }; +#else + EnemyView = EnemyPanel; +#endif + Core.OverlayCanvas.Children.Add(EnemyView); + + // Create container + FriendlyPanel = new StackPanel + { + Orientation = Settings.Default.FriendlyOrientation + }; +#if DEBUGXAML + FriendlyView = new Border + { + BorderBrush = Brushes.Green, + BorderThickness = new Thickness(1), + Child = FriendlyPanel, + }; +#else + FriendlyView = FriendlyPanel; +#endif + Core.OverlayCanvas.Children.Add(FriendlyView); - Input = new InputManager(_friendlyPanel, _enemyPanel); + Input = new InputManager(FriendlyPanel, EnemyPanel); Settings.Default.PropertyChanged += SettingsChanged; SettingsChanged(null, null); - - // Connect events - GameEvents.OnGameStart.Add(Reset); - GameEvents.OnGameEnd.Add(Reset); - DeckManagerEvents.OnDeckSelected.Add(d => Reset()); - - GameEvents.OnPlayerPlayToGraveyard.Add(PlayerGraveyardUpdate); - GameEvents.OnOpponentPlayToGraveyard.Add(EnemyGraveyardUpdate); - - GameEvents.OnPlayerPlay.Add(c => Anyfin?.UpdateDamage()); - GameEvents.OnOpponentPlay.Add(c => Anyfin?.UpdateDamage()); - - GameEvents.OnPlayerHandDiscard.Add(PlayerDiscardUpdate); - GameEvents.OnPlayerPlay.Add(PlayerPlayUpdate); } - //on year change clear out the grid and update the data - private void SettingsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + //on year change clear out the grid and update the data + private void SettingsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - _friendlyPanel.Orientation = Settings.Default.FriendlyOrientation; - _friendlyPanel.RenderTransform = new ScaleTransform(Settings.Default.FriendlyScale / 100, Settings.Default.FriendlyScale / 100); - _friendlyPanel.Opacity = Settings.Default.FriendlyOpacity / 100; - _enemyPanel.RenderTransform = new ScaleTransform(Settings.Default.EnemyScale / 100, Settings.Default.EnemyScale / 100); - _enemyPanel.Opacity = Settings.Default.EnemyOpacity / 100; + FriendlyPanel.Orientation = Settings.Default.FriendlyOrientation; + FriendlyPanel.RenderTransform = new ScaleTransform(Settings.Default.FriendlyScale / 100, Settings.Default.FriendlyScale / 100); + FriendlyPanel.Opacity = Settings.Default.FriendlyOpacity / 100; + EnemyPanel.RenderTransform = new ScaleTransform(Settings.Default.EnemyScale / 100, Settings.Default.EnemyScale / 100); + EnemyPanel.Opacity = Settings.Default.EnemyOpacity / 100; } public void Dispose() { - Core.OverlayCanvas.Children.Remove(_friendlyPanel); - Core.OverlayCanvas.Children.Remove(_enemyPanel); + Core.OverlayCanvas.Children.Remove(FriendlyPanel); + Core.OverlayCanvas.Children.Remove(EnemyPanel); Input.Dispose(); } - /** - * Clear then recreate all Views. - */ - public void Reset() - { - _friendlyPanel.Children.Clear(); - _enemyPanel.Children.Clear(); + public void ClearUI() + { + FriendlyPanel.Children.Clear(); + EnemyPanel.Children.Clear(); - if (Core.Game.CurrentGameMode == GameMode.Battlegrounds) - { - // don't show graveyard for Battlegrounds - return; - } + Plugin.Events.Clear(); + } - if (Settings.Default.EnemyEnabled) - { - Enemy = new NormalView(); - _enemyPanel.Children.Add(Enemy); - } - else - { - Enemy = null; - } + public void Update() + { + var visibility = (Core.Game.IsInMenu && Hearthstone_Deck_Tracker.Config.Instance.HideInMenu) + || Core.Game.IsBattlegroundsMatch + || Core.Game.IsMercenariesMatch ? Visibility.Collapsed : Visibility.Visible; - if (Settings.Default.ResurrectEnabled && ResurrectView.isValid()) + FriendlyView.Visibility = visibility; + if (FriendlyView.Visibility == Visibility.Visible) { - Resurrect = new ResurrectView(); - _friendlyPanel.Children.Add(Resurrect); - Normal = null; - } - else if (Settings.Default.NormalEnabled) - { - Normal = new NormalView(); - _friendlyPanel.Children.Add(Normal); - Resurrect = null; - } - else - { - Normal = null; - Resurrect = null; - } + Canvas.SetTop(FriendlyView, Settings.Default.PlayerTop.PercentageToPixels(Core.OverlayWindow.Height)); + Canvas.SetLeft(FriendlyView, Settings.Default.PlayerLeft.PercentageToPixels(Core.OverlayWindow.Width)); + } - if (Settings.Default.AnyfinEnabled && AnyfinView.isValid()) - { - Anyfin = new AnyfinView(); - _friendlyPanel.Children.Add(Anyfin); - } - else + EnemyView.Visibility = visibility; + if (EnemyView.Visibility == Visibility.Visible) { - Anyfin = null; - } - - if (Settings.Default.DeathrattleEnabled && DeathrattleView.isValid()) - { - Deathrattle = new DeathrattleView(); - _friendlyPanel.Children.Add(Deathrattle); - } - else - { - Deathrattle = null; + Canvas.SetTop(EnemyView, Settings.Default.EnemyTop.PercentageToPixels(Core.OverlayWindow.Height)); + Canvas.SetLeft(EnemyView, Settings.Default.EnemyLeft.PercentageToPixels(Core.OverlayWindow.Width)); } + } - if (Settings.Default.NZothEnabled && NZothView.isValid()) - { - NZoth = new NZothView(); - _friendlyPanel.Children.Add(NZoth); - } - else - { - NZoth = null; - } + private IEnumerable _PlayerCardList = null; + private IEnumerable PlayerCardList => _PlayerCardList ?? (_PlayerCardList = Core.Game.Player.PlayerCardList.Concat(PlayerETCBand)); + private IEnumerable PlayerETCBand => Core.Game.Player.PlayerSideboardsDict? + .Where(s => s.OwnerCardId == Neutral.ETCBandManager) + .FirstOrDefault()?.Cards ?? Enumerable.Empty(); - if (Settings.Default.HadronoxEnabled && HadronoxView.isValid()) - { - Hadronox = new HadronoxView(); - _friendlyPanel.Children.Add(Hadronox); - } - else - { - Hadronox = null; - } + /** + * Clear then recreate all Views. + */ + public void Reset() + { + ClearUI(); - if (Settings.Default.DiscardEnabled && DiscardView.isValid()) - { - Discard = new DiscardView(); - _friendlyPanel.Children.Add(Discard); - } - else - { - Discard = null; - } + _PlayerCardList = null; - if (Settings.Default.ShudderwockEnabled && ShudderwockView.isValid()) + if ((Core.Game.IsInMenu && Hearthstone_Deck_Tracker.Config.Instance.HideInMenu) || Core.Game.IsBattlegroundsMatch || Core.Game.IsMercenariesMatch) { - Shudderwock = new ShudderwockView(); - _friendlyPanel.Children.Add(Shudderwock); - } - else - { - Shudderwock = null; + // don't initialize in menu unless the overlay is visible + // don't show graveyard for Battlegrounds or Mercenaries + // this should include spectating + return; } + + InitializeView(EnemyPanel, QuestlineView.EnemyConfig); + InitializeView(EnemyPanel, GraveyardView.EnemyConfig, true); + InitializeView(EnemyPanel, DeathrattleView.OpponentConfig); - if (Settings.Default.GuldanEnabled && GuldanView.isValid()) - { - Guldan = new GuldanView(); - _friendlyPanel.Children.Add(Guldan); - } - else - { - Guldan = null; - } - if (Settings.Default.DragoncallerAlannaEnabled && DragoncallerAlannaView.isValid()) - { - DragoncallerAlanna = new DragoncallerAlannaView(); - _friendlyPanel.Children.Add(DragoncallerAlanna); - } - else - { - DragoncallerAlanna = null; - } - if (Settings.Default.MulchmuncherEnabled && MulchmuncherView.isValid()) - { - Mulchmuncher = new MulchmuncherView(); - _friendlyPanel.Children.Add(Mulchmuncher); - } - else - { - Mulchmuncher = null; - } + FirstPanel = new StackPanel(); + FriendlyPanel.Children.Add(FirstPanel); + + InitializeView(FriendlyPanel, GraveyardView.FriendlyConfig, true); - if (Settings.Default.CavernsEnabled && CavernsView.isValid()) - { - Caverns = new CavernsView(); - _friendlyPanel.Children.Add(Caverns); - } - else - { - Caverns = null; - } - if (Settings.Default.KangorEnabled && KangorView.isValid()) - { - Kangor = new KangorView(); - _friendlyPanel.Children.Add(Kangor); - } - else - { - Kangor = null; - } - if (Settings.Default.WitchingHourEnabled && WitchingHourView.isValid()) - { - WitchingHour = new WitchingHourView(); - _friendlyPanel.Children.Add(WitchingHour); - } - else - { - WitchingHour = null; - } - if (Settings.Default.SoulwardenEnabled && SoulwardenView.isValid()) - { - Soulwarden = new SoulwardenView(); - _friendlyPanel.Children.Add(Soulwarden); - } - else - { - Soulwarden = null; - } - if (Settings.Default.TessGreymaneEnabled && TessGreymaneView.isValid()) - { - TessGreymane = new TessGreymaneView(); - _friendlyPanel.Children.Add(TessGreymane); - } - else - { - TessGreymane = null; - } - if (Settings.Default.ZuljinEnabled && ZuljinView.isValid()) + if (Settings.Default.CardsEnabled) { - Zuljin = new ZuljinView(); - _friendlyPanel.Children.Add(Zuljin); + foreach (var config in ConfigList) + { + if (config.ShowFirst()) + { + InitializeView(FirstPanel, config); + } + else + { + InitializeView(FriendlyPanel, config); + } + } } - else + else { - Zuljin = null; - } - if (Settings.Default.HoardPillagerEnabled && HoardPillagerView.isValid()) - { - HoardPillager = new HoardPillagerView(); - _friendlyPanel.Children.Add(HoardPillager); - } - else - { - HoardPillager = null; - } - if (Settings.Default.LadyLiadrinEnabled && LadyLiadrinView.isValid()) - { - LadyLiadrin = new LadyLiadrinView(); - _friendlyPanel.Children.Add(LadyLiadrin); - } - else - { - LadyLiadrin = null; - } - if (Settings.Default.NZothGotDEnabled && NZothGotDView.isValid()) - { - NZothGotD = new NZothGotDView(); - _friendlyPanel.Children.Add(NZothGotD); + InitializeView(FriendlyPanel, ResurrectView.Config); + InitializeView(FriendlyPanel, DeathrattleView.PlayerConfig); } - if (Settings.Default.RallyEnabled && RallyView.isValid()) - { - Rally = new RallyView(); - _friendlyPanel.Children.Add(Rally); - } - if (Settings.Default.SaurfangEnabled && SaurfangView.isValid()) + + // Show "demo mode" when overlay is visible in menu + if (Core.Game.IsInMenu) { - Saurfang = new SaurfangView(); - _friendlyPanel.Children.Add(Saurfang); + foreach (var card in PlayerCardList) + { + if (card != null) + { + Plugin.Events.OnOpponentPlay.Poll(card); + Plugin.Events.OnOpponentPlayToGraveyard.Poll(card); + Plugin.Events.OnPlayerHandDiscard.Poll(card); + Plugin.Events.OnPlayerPlay.Poll(card); + Plugin.Events.OnPlayerPlayToGraveyard.Poll(card); + } + } } } - public void PlayerGraveyardUpdate(Card card) - { - var any = Anyfin?.Update(card) ?? false; - var deathrattle = Deathrattle?.Update(card) ?? false; - var nzoth = NZoth?.Update(card) ?? false; - var hadr = Hadronox?.Update(card) ?? false; - var guldan = Guldan?.Update(card) ?? false; - var rez = Resurrect?.Update(card) ?? false; - var mulch = Mulchmuncher?.Update(card) ?? false; - var kangor = Kangor?.Update(card) ?? false; - var witching = WitchingHour?.Update(card) ?? false; - var hoardpillager = HoardPillager?.Update(card) ?? false; - var nzothgotd = NZothGotD?.Update(card) ?? false; - var rally = Rally?.Update(card) ?? false; - var saurfang = Saurfang?.Update(card) ?? false; - if (!(any || deathrattle || nzoth || hadr || guldan || rez || mulch || kangor || witching || hoardpillager || nzothgotd || rally || saurfang)) - { - Normal?.Update(card); - } - } + private ViewBase InitializeView(Panel parent, ViewConfig config, bool isDefault = false) + { + var view = new ViewBuilder(config, PlayerCardList).BuildView(!Settings.Default.CardsEnabled); + if (view == null) return null; - public void EnemyGraveyardUpdate(Card card) - { - Anyfin?.Update(card); - Enemy?.Update(card); + config.RegisterView(view, isDefault); + ShowView(parent, view); + return view; } - public void PlayerDiscardUpdate(Card card) + private bool ShowView(Panel parent, ViewBase view) { - Discard?.Update(card); - Soulwarden?.Update(card); - } + if (view == null) return false; + + parent.Children.Add(view); - public void PlayerPlayUpdate(Card card) - { - Shudderwock?.Update(card); - DragoncallerAlanna?.Update(card); - Caverns?.Update(card); - TessGreymane?.Update(card); - Zuljin?.Update(card); - LadyLiadrin?.Update(card); - } + return true; + } } -} \ No newline at end of file +} diff --git a/Graveyard/Graveyard.csproj b/Graveyard/Graveyard.csproj index 580de1e..b15753e 100644 --- a/Graveyard/Graveyard.csproj +++ b/Graveyard/Graveyard.csproj @@ -71,24 +71,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + True @@ -96,6 +148,7 @@ Strings.resx + @@ -108,7 +161,10 @@ + + + Designer @@ -120,6 +176,7 @@ Settings.settings + PublicSettingsSingleFileGenerator Settings.Designer.cs diff --git a/Graveyard/GraveyardPlugin.cs b/Graveyard/GraveyardPlugin.cs index c56ff2d..3c956f4 100644 --- a/Graveyard/GraveyardPlugin.cs +++ b/Graveyard/GraveyardPlugin.cs @@ -1,3 +1,7 @@ +using HDT.Plugins.Graveyard.Properties; +using Hearthstone_Deck_Tracker; +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Hearthstone; using Hearthstone_Deck_Tracker.Plugins; using System; using System.Reflection; @@ -5,30 +9,89 @@ namespace HDT.Plugins.Graveyard { - public class GraveyardPlugin : IPlugin + public class Plugin : IPlugin { - public Graveyard GraveyardInstance; + private Settings Settings; + internal static EventManager Events { get; private set; } + internal static Graveyard Graveyard { get; private set; } public string Author => "RedHatter"; - public string ButtonText => Strings.GetLocalized("Settings"); + public string ButtonText => Strings.GetLocalized(nameof(Resources.Strings.Settings)); - public string Description => Strings.GetLocalized("GraveyardDescription"); + public string Description => Strings.GetLocalized(nameof(Resources.Strings.GraveyardDescription)); - public MenuItem MenuItem => null; - public string Name => "Graveyard"; + public MenuItem MenuItem { get; set; } + public string Name => LibraryInfo.Name; public void OnButtonPress() => SettingsView.Flyout.IsOpen = true; - public void OnLoad() => GraveyardInstance = new Graveyard(); + public void OnLoad() + { + Settings = Settings.Default; + + MenuItem = new MenuItem { Header = Strings.GetLocalized(nameof(Resources.Strings.Graveyard)) }; + MenuItem.Click += (sender, args) => OnButtonPress(); + + Events = new EventManager(); + + ViewConfigCards.Instance = new ViewConfigCards(Settings); + + Graveyard = new Graveyard(); + + Settings.Upgrade(); + + GameEvents.OnGameStart.Add(Graveyard.Reset); + DeckManagerEvents.OnDeckSelected.Add(UpdateSelectedDeck); + + GameEvents.OnPlayerPlayToGraveyard.Add(Events.OnPlayerPlayToGraveyard.Poll); + GameEvents.OnOpponentPlayToGraveyard.Add(Events.OnOpponentPlayToGraveyard.Poll); + + GameEvents.OnPlayerHandDiscard.Add(Events.OnPlayerHandDiscard.Poll); + + GameEvents.OnPlayerPlay.Add(Events.OnPlayerPlay.Poll); + GameEvents.OnOpponentPlay.Add(Events.OnOpponentPlay.Poll); + + GameEvents.OnPlayerCreateInPlay.Add(Events.OnPlayerCreateInPlay.Poll); + GameEvents.OnOpponentCreateInPlay.Add(Events.OnOpponentCreateInPlay.Poll); + + GameEvents.OnPlayerDraw.Add(Events.OnPlayerDraw.Poll); + + GameEvents.OnPlayerMulligan.Add(Events.OnPlayerMulligan.Poll); + + GameEvents.OnPlayerGet.Add(Events.OnPlayerGet.Poll); + + GameEvents.OnTurnStart.Add(Events.OnOpponentTurnStart.Poll); + + UpdateSelectedDeck(DeckList.Instance.ActiveDeck); + } + + private Deck SelectedDeck; + private void UpdateSelectedDeck(Deck deck) + { + if (deck == DeckList.Instance.ActiveDeck && deck != SelectedDeck) + { + SelectedDeck = deck; + Graveyard.Reset(); + } + } + public void OnUnload() { - Settings.Default.Save(); + if (Settings?.HasChanges ?? false) Settings?.Save(); + Settings = null; + + Graveyard?.Dispose(); + Graveyard = null; + + ViewConfigCards.Instance = null; - GraveyardInstance?.Dispose(); - GraveyardInstance = null; + Events?.Clear(); + Events = null; + } + + public void OnUpdate() + { + Graveyard?.Update(); } - public void OnUpdate() { } - public static readonly Version AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version; - public static readonly Version PluginVersion = new Version(AssemblyVersion.Major, AssemblyVersion.Minor, AssemblyVersion.Build); - public Version Version => PluginVersion; + public Version Version => LibraryInfo.Version; } } diff --git a/Graveyard/GraveyardView.cs b/Graveyard/GraveyardView.cs new file mode 100644 index 0000000..895b92c --- /dev/null +++ b/Graveyard/GraveyardView.cs @@ -0,0 +1,43 @@ +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Hearthstone; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HDT.Plugins.Graveyard +{ + internal class GraveyardView + { + private static readonly string SharedName = "Graveyard"; + private static readonly Func SharedCreateView = () => new NormalView(); + private static readonly Predicate SharedCondition = card => card.Type == "Minion"; + + internal static ViewConfig FriendlyConfig + { + get => _FriendlyConfig ?? (_FriendlyConfig = new ViewConfig() + { + Name = SharedName, + Enabled = "NormalEnabled", + CreateView = SharedCreateView, + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = SharedCondition, + }); + } + private static ViewConfig _FriendlyConfig; + + internal static ViewConfig EnemyConfig + { + get => _EnemyConfig ?? (_EnemyConfig = new ViewConfig() + { + Name = SharedName, + Enabled = "EnemyEnabled", + CreateView = SharedCreateView, + UpdateOn = GameEvents.OnOpponentPlayToGraveyard, + Condition = SharedCondition, + }); + } + private static ViewConfig _EnemyConfig; + } +} diff --git a/Graveyard/GuldanView.cs b/Graveyard/GuldanView.cs index e373746..07a0eb9 100644 --- a/Graveyard/GuldanView.cs +++ b/Graveyard/GuldanView.cs @@ -1,32 +1,24 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class GuldanView : NormalView + public class GuldanView { - private ChancesTracker _chances = new ChancesTracker(); - - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == Warlock.BloodreaverGuldan || card.Id == Warlock.KanrethadEbonlocke) > -1; - } - - public GuldanView() - { - // Section Label - Label.Text = Strings.GetLocalized("Guldan"); - } - - public bool Update(Card card) - { - var update = card.Race == "Demon" && base.Update(card); - - if (update) - _chances.Update(card, Cards, View); - - return update; + get => _Config ?? (_Config = new ViewConfig( + Demonhunter.AllFelBreaksLoose, + Warlock.BloodreaverGuldan, + Warlock.KanrethadEbonlocke) + { + Name = "Guldan", + Enabled = "GuldanEnabled", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Race == "Demon", + }); } } } diff --git a/Graveyard/HadronoxView.cs b/Graveyard/HadronoxView.cs index f8c10b9..312063e 100644 --- a/Graveyard/HadronoxView.cs +++ b/Graveyard/HadronoxView.cs @@ -1,25 +1,23 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; using System.Linq; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class HadronoxView : NormalView + public class HadronoxView { - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Druid.Hadronox) > -1; - } - - public HadronoxView() - { - // Section Label - Label.Text = Strings.GetLocalized("Hadronox"); - } - - public bool Update(Card card) - { - return card.Mechanics.Contains("Taunt") && base.Update(card); + get => _Config ?? (_Config = new ViewConfig(Druid.Hadronox) + { + Name = "Hadronox", + Enabled = "HadronoxEnabled", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Mechanics.Contains("Taunt"), + }); } + } } diff --git a/Graveyard/HedraView.cs b/Graveyard/HedraView.cs new file mode 100644 index 0000000..9f941ff --- /dev/null +++ b/Graveyard/HedraView.cs @@ -0,0 +1,18 @@ +using static HDT.Plugins.Graveyard.HoldingEffectView; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class HedraView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new HoldingEffectConfig(Druid.HedraTheHeretic) + { + Name = "Hedra", + Condition = card => card.Type == "Spell", + }); + } + } +} diff --git a/Graveyard/HighCultistBasalephView.cs b/Graveyard/HighCultistBasalephView.cs new file mode 100644 index 0000000..3caf5eb --- /dev/null +++ b/Graveyard/HighCultistBasalephView.cs @@ -0,0 +1,44 @@ +using Hearthstone_Deck_Tracker.API; +using System.Threading.Tasks; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + public class HighCultistBasalephView : NormalView + { + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Priest.HighCultistBasaleph) + { + Name = "HighCultistBasaleph", + CreateView = () => new HighCultistBasalephView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Race == "Undead" || card.Race == "All", + }); + } + private static ViewConfig _Config; + + internal class ViewConfig : Plugins.Graveyard.ViewConfig + { + public ViewConfig(params string[] showOn) : base(showOn) + { + + } + + public override void RegisterView(ViewBase view, bool isDefault = false) + { + base.RegisterView(view, isDefault); + if (view is HighCultistBasalephView basalephView) + { + Plugin.Events.OnOpponentTurnStart.Register(basalephView.PlayerTurnEnded); + } + } + } + + private async Task PlayerTurnEnded() + { + Cards.Clear(); + await View.Update(Cards, true); + } + } +} diff --git a/Graveyard/HoardPillagerView.cs b/Graveyard/HoardPillagerView.cs index 843f6ff..ec251fa 100644 --- a/Graveyard/HoardPillagerView.cs +++ b/Graveyard/HoardPillagerView.cs @@ -1,31 +1,21 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class HoardPillagerView : NormalView + public class HoardPillagerView { - private ChancesTracker _chances = new ChancesTracker(); - - public static bool isValid() - { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Neutral.HoardPillager - || card.Id == HearthDb.CardIds.Collectible.Neutral.RummagingKobold) > -1; - } - - public HoardPillagerView() + private static ViewConfig _Config; + internal static ViewConfig Config { - Label.Text = Strings.GetLocalized("HoardPillager"); - } - - public bool Update(Card card) - { - var update = card.Type == "Weapon" && base.Update(card, true); - - if (update) - _chances.Update(card, Cards, View); - - return update; - } + get => _Config ?? (_Config = new ViewConfig(Neutral.HoardPillager, Neutral.RummagingKobold) + { + Name = "HoardPillager", + Enabled = "HoardPillagerEnabled", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Type == "Weapon" + }); + } } } diff --git a/Graveyard/HoldingEffectView.cs b/Graveyard/HoldingEffectView.cs new file mode 100644 index 0000000..643af12 --- /dev/null +++ b/Graveyard/HoldingEffectView.cs @@ -0,0 +1,101 @@ +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Hearthstone; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace HDT.Plugins.Graveyard +{ + // For cards with holding (i.e. in hand) effects + // such as Commander Sivara and Hedra the Heretic + // Single card per instance + internal class HoldingEffectView : NormalView + { + public string CardId { get; set; } + public int? CardLimit { get; set; } + + private bool IsCard(Card card) => card.Id == CardId; + private bool CardInHand = false; + + private bool CardLimitReached => CardLimit.HasValue && + Cards.Sum(c => c.Count) >= CardLimit.Value; + + private bool ClearOnNextTurn = false; + + public HoldingEffectView(string cardId) + { + CardId = cardId; + } + + private void CardOutbound(Card card, Action outboundAction = null) + { + if (CardInHand && IsCard(card)) + { + CardInHand = false; + outboundAction?.Invoke(); + } + } + + private void CardInbound(Card card, Action inboundAction = null) + { + if (!CardInHand && IsCard(card)) + { + CardInHand = true; + inboundAction?.Invoke(); + } + } + + public override bool Update(Card card) + { + CardOutbound(card, () => ClearOnNextTurn = true); + return CardInHand && !CardLimitReached && base.Update(card); + } + + private bool PlayerDraw(Card card) + { + CardInbound(card); + return CardInHand; + } + + private bool PlayerMulligan(Card card) + { + CardOutbound(card); + return CardInHand; + } + + private async Task TurnEnded() + { + if (ClearOnNextTurn) + { + Cards.Clear(); + await View.Update(Cards, true); + ClearOnNextTurn = false; + } + } + + internal class HoldingEffectConfig : ViewConfig + { + public int? CardLimit { get; set; } + + public HoldingEffectConfig(string cardId) : base(cardId) + { + CreateView = () => new HoldingEffectView(cardId) + { + CardLimit = CardLimit, + }; + UpdateOn = GameEvents.OnPlayerPlay; + } + + public override void RegisterView(ViewBase view, bool isDefault = false) + { + base.RegisterView(view, isDefault); + if (view is HoldingEffectView holdingEffectView) + { + RegisterForCardEvent(GameEvents.OnPlayerDraw, holdingEffectView.PlayerDraw); + RegisterForCardEvent(GameEvents.OnPlayerMulligan, holdingEffectView.PlayerMulligan); + Plugin.Events.OnOpponentTurnStart.Register(holdingEffectView.TurnEnded); + } + } + } + } +} diff --git a/Graveyard/ImpKingView.cs b/Graveyard/ImpKingView.cs new file mode 100644 index 0000000..07a8c2e --- /dev/null +++ b/Graveyard/ImpKingView.cs @@ -0,0 +1,43 @@ +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Hearthstone; +using System.Collections.Generic; +using static HearthDb.CardIds; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class ImpKingView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Warlock.ImpKingRafaam) + { + Name = "ImpKing", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => Imps.Contains(card.Id) || IsEponymousImp(card), + }); + } + + internal static bool IsEponymousImp(Card card) + { + return card.Race == "Demon" && card.Name.Contains("Imp") && card.Id != Neutral.ImprisonedVilefiend; + } + + internal static readonly List Imps = new List + { + NonCollectible.Warlock.DarkAlleyPact_FiendToken, + NonCollectible.Neutral.RustswornCultist_RustedDevilToken, + NonCollectible.Warlock.NofinsImpossible_ImplocToken, + Warlock.ImprisonedScrapImp, + Warlock.TinyKnightOfEvilTGT, + Warlock.VulgarHomunculus, + Neutral.StreetTricksterGANGS, + Warlock.UnlicensedApothecary, + Neutral.SneakyDevil, + Warlock.EnvoyRustwix, + Warlock.RingMatron, + }; + } +} diff --git a/Graveyard/InputManager.cs b/Graveyard/InputManager.cs index aa95756..0661851 100644 --- a/Graveyard/InputManager.cs +++ b/Graveyard/InputManager.cs @@ -1,17 +1,16 @@ +using Hearthstone_Deck_Tracker; using System; using System.Windows; using System.Windows.Controls; -using System.Collections.Generic; -using Hearthstone_Deck_Tracker; using Core = Hearthstone_Deck_Tracker.API.Core; namespace HDT.Plugins.Graveyard { - public class InputManager + public class InputManager { private User32.MouseInput _mouseInput; - private StackPanel _player; - private StackPanel _enemy; + private readonly StackPanel _player; + private readonly StackPanel _enemy; private String _selected; @@ -58,12 +57,12 @@ private void MouseInputOnLmbUp(object sender, EventArgs eventArgs) var p = Core.OverlayCanvas.PointFromScreen(new Point(pos.X, pos.Y)); if (_selected == "player") { - Settings.Default.PlayerTop = p.Y; - Settings.Default.PlayerLeft = p.X; + Settings.Default.PlayerTop = p.Y.PixelsToPercentage(Core.OverlayWindow.Height); + Settings.Default.PlayerLeft = p.X.PixelsToPercentage(Core.OverlayWindow.Width); } else if (_selected == "enemy") { - Settings.Default.EnemyTop = p.Y; - Settings.Default.EnemyLeft = p.X; + Settings.Default.EnemyTop = p.Y.PixelsToPercentage(Core.OverlayWindow.Height); + Settings.Default.EnemyLeft = p.X.PixelsToPercentage(Core.OverlayWindow.Width); } _selected = null; diff --git a/Graveyard/InventorBoomView.cs b/Graveyard/InventorBoomView.cs new file mode 100644 index 0000000..9ddf7e9 --- /dev/null +++ b/Graveyard/InventorBoomView.cs @@ -0,0 +1,27 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class InventorBoomView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Warrior.InventorBoom) + { + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => { + + if (card.Name == "Zilliax Deluxe 3000") + return true; + if (card.Race == "Mech" || card.Race == "All") + if (card.Cost >= 5) + return true; + return false; + }, + }); + } + } +} diff --git a/Graveyard/JaceDarkweaverView.cs b/Graveyard/JaceDarkweaverView.cs new file mode 100644 index 0000000..f79e4d3 --- /dev/null +++ b/Graveyard/JaceDarkweaverView.cs @@ -0,0 +1,20 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class JaceDarkweaverView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Demonhunter.JaceDarkweaver) + { + Name = "Jace", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.GetSchool() == School.Fel, + }); + } + } +} diff --git a/Graveyard/JepettoView.cs b/Graveyard/JepettoView.cs new file mode 100644 index 0000000..261ad49 --- /dev/null +++ b/Graveyard/JepettoView.cs @@ -0,0 +1,21 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class JepettoView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Neutral.JoymancerJepetto) + { + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Type == "Minion" + && (card.Health == 1 + || card.Attack == 1) + }); + } + } +} diff --git a/Graveyard/KangorView.cs b/Graveyard/KangorView.cs index 27d7d86..9958665 100644 --- a/Graveyard/KangorView.cs +++ b/Graveyard/KangorView.cs @@ -1,24 +1,21 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class KangorView : NormalView + public class KangorView { - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Paladin.KangorsEndlessArmy) > -1; - } - - public KangorView() - { - // Section Label - Label.Text = Strings.GetLocalized("Kangor"); - } - - public bool Update(Card card) - { - return (card.Race == "Mech" || card.Race == "All") && base.Update(card); - } + get => _Config ?? (_Config = new ViewConfig(Paladin.KangorsEndlessArmy) + { + Name = "Kangor", + Enabled = "KangorEnabled", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Race == "Mech" || card.Race == "All", + }); + } } } diff --git a/Graveyard/KargalView.cs b/Graveyard/KargalView.cs new file mode 100644 index 0000000..627a986 --- /dev/null +++ b/Graveyard/KargalView.cs @@ -0,0 +1,29 @@ +using Hearthstone_Deck_Tracker.API; +using System.Collections.Generic; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + public class KargalView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Neutral.KargalBattlescar) + { + Name = "Kargal", + Enabled = "KargalEnabled", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => Posts.Contains(card.Id), + }); + } + + internal static readonly List Posts = new List + { + Neutral.CrossroadsWatchPost, + Neutral.FarWatchPost, + Neutral.MorshanWatchPost, + }; + } +} diff --git a/Graveyard/KelthuzadTheInevitableView.cs b/Graveyard/KelthuzadTheInevitableView.cs new file mode 100644 index 0000000..9d0fa24 --- /dev/null +++ b/Graveyard/KelthuzadTheInevitableView.cs @@ -0,0 +1,21 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + public class KelthuzadTheInevitableView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Mage.KelthuzadTheInevitable) + { + Name = "KelthuzadTheInevitable", + ShowFirst = () => true, + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Id == Neutral.VolatileSkeleton, + }); + } + } +} \ No newline at end of file diff --git a/Graveyard/KeyToLocalizedConverter.cs b/Graveyard/KeyToLocalizedConverter.cs new file mode 100644 index 0000000..5fc5d39 --- /dev/null +++ b/Graveyard/KeyToLocalizedConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; + +namespace HDT.Plugins.Graveyard +{ + public class KeyToLocalizedConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return Strings.GetLocalized((string)value); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value; + } + } +} diff --git a/Graveyard/LadyLiadrinView.cs b/Graveyard/LadyLiadrinView.cs index 7572d73..dfc3fcf 100644 --- a/Graveyard/LadyLiadrinView.cs +++ b/Graveyard/LadyLiadrinView.cs @@ -1,12 +1,25 @@ -using Hearthstone_Deck_Tracker; +using Hearthstone_Deck_Tracker.API; using Hearthstone_Deck_Tracker.Hearthstone; using System.Collections.Generic; using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class LadyLiadrinView : NormalView + public class LadyLiadrinView { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Paladin.LadyLiadrin) + { + Name = "LadyLiadrin", + Enabled = "LadyLiadrinEnabled", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Type == "Spell" && SpellList.Contains(card.Id), + }); + } + public static readonly List SpellList = new List { // 0 @@ -17,27 +30,33 @@ public class LadyLiadrinView : NormalView Paladin.BlessingOfWisdom, Paladin.DivineStrength, Paladin.HandOfProtectionLegacy, + Paladin.HolyMakiRoll, //Paladin.Humility Paladin.SandBreath, Paladin.ShieldOfHonor, // 2 Paladin.DarkConviction, + Paladin.DesperateStand, + Paladin.ForQuelthalas, Paladin.FlashOfLight, - Paladin.HandOfAdal, - Paladin.HolyLightLegacy, + Paladin.HandOfAdalCore, Paladin.LibramOfWisdom, Paladin.LightforgedBlessing, + Paladin.NobleMount, Paladin.PotionOfHeroism, - Paladin.SealOfLight, + Paladin.RingOfCourage, Paladin.SoundTheBells, //Paladin.Subdue // 3 Paladin.GiftOfLuminance, + Paladin.HoldTheBridge, + //Paladin.RighteousDefense, + Paladin.SealOfBlood, Paladin.SealOfChampions, // 4 Paladin.BlessingOfKingsLegacy, //Paladin.HammerOfWrath, - Paladin.SilvermoonPortal, + Paladin.SilvermoonPortalKARA, // 5 Paladin.BlessedChampion, Paladin.BlessingOfAuthority, @@ -50,25 +69,11 @@ public class LadyLiadrinView : NormalView Paladin.SpikeridgedSteed, // 8 Paladin.Dinosize, - Paladin.LayOnHands, + Paladin.LayOnHandsExpert1, // 9 Paladin.LibramOfHope, + // 10 + Paladin.TheGardensGrace, }; - - public static bool isValid() - { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Paladin.LadyLiadrin) > -1; - } - - public LadyLiadrinView() - { - // Section Label - Label.Text = Strings.GetLocalized("LadyLiadrin"); - } - - public bool Update(Card card) - { - return card.Type == "Spell" && SpellList.Contains(card.Id) && base.Update(card, true); - } } } diff --git a/Graveyard/LastCardView.cs b/Graveyard/LastCardView.cs new file mode 100644 index 0000000..b025e36 --- /dev/null +++ b/Graveyard/LastCardView.cs @@ -0,0 +1,45 @@ +using Hearthstone_Deck_Tracker; +using Hearthstone_Deck_Tracker.Controls; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using Card = Hearthstone_Deck_Tracker.Hearthstone.Card; + +namespace HDT.Plugins.Graveyard +{ + public class LastCardView : ViewBase + { + internal class ViewConfig : Plugins.Graveyard.ViewConfig + { + public ViewConfig(params string[] showOn) : base(showOn) + { + Enabled = "LastPlayedEnabled"; + ShowFirst = () => true; + CreateView = () => new LastCardView(); + } + } + + public readonly HearthstoneTextBlock Label; + public readonly AnimatedCardList Cards; + + public LastCardView() + { + Visibility = Visibility.Collapsed; + Orientation = Orientation.Vertical; + + Cards = new AnimatedCardList(); + Children.Add(Cards); + } + + public override bool Update(Card card) + { + if (!Condition(card)) return false; + + Cards.Update(new List { card.Clone() as Card }, false); + + Visibility = Visibility.Visible; + + return true; + } + } +} diff --git a/Graveyard/LastPlayedView.cs b/Graveyard/LastPlayedView.cs new file mode 100644 index 0000000..134c9dc --- /dev/null +++ b/Graveyard/LastPlayedView.cs @@ -0,0 +1,117 @@ +using HearthDb.Enums; +using Hearthstone_Deck_Tracker.API; +using System.Collections.Generic; +using System.Linq; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + public class LastPlayedView + { + internal static ViewConfig GreySageParrotConfig + { + get => _GreySageParrotConfig ?? (_GreySageParrotConfig = new LastCardView.ViewConfig(Mage.GreySageParrot) + { + Name = "GreySageParrot", + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Type == "Spell" && card.Cost >= 6, + }); + } + private static ViewConfig _GreySageParrotConfig; + + internal static ViewConfig SunwingSquawkerConfig + { + get => _SunwingSquawkerConfig ?? (_SunwingSquawkerConfig = new LastCardView.ViewConfig(Paladin.SunwingSquawker) + { + Name = "SunwingSquawker", + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Type == "Spell" && LadyLiadrinView.SpellList.Contains(card.Id), + }); + } + private static ViewConfig _SunwingSquawkerConfig; + + internal static ViewConfig BrilliantMacawConfig + { + get => _BrilliantMacawConfig ?? (_BrilliantMacawConfig= new LastCardView.ViewConfig(Shaman.BrilliantMacaw) + { + Name = "BrilliantMacaw", + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Mechanics.Contains("Battlecry") && card.Id != Shaman.BrilliantMacaw, + }); + } + private static ViewConfig _BrilliantMacawConfig; + + internal static ViewConfig MonstrousParrotConfig + { + get => _MonstrousParrotConfig ?? (_MonstrousParrotConfig = new LastCardView.ViewConfig(Hunter.MonstrousParrot) + { + Name = "MonstrousParrot", + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Mechanics.Contains("Deathrattle") && card.Id != Rogue.UnearthedRaptor, + }); + } + private static ViewConfig _MonstrousParrotConfig; + + internal static ViewConfig VanessaVanCleefConfig + { + get => _VanessaVanCleefConfig ?? (_VanessaVanCleefConfig = new LastCardView.ViewConfig(Rogue.VanessaVancleefLegacy) + { + Name = "VanessaVanCleef", + UpdateOn = GameEvents.OnOpponentPlay, + Condition = card => true, + }); + } + private static ViewConfig _VanessaVanCleefConfig; + + internal static ViewConfig LadyDarkveinConfig + { + get => _LadyDarkveinConfig ?? (_LadyDarkveinConfig = new LastCardView.ViewConfig(Warlock.LadyDarkvein) + { + Name = "LadyDarkvein", + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => (card.GetSchool() == School.Shadow), + }); + } + private static ViewConfig _LadyDarkveinConfig; + + internal static ViewConfig AsvedonConfig + { + get => _AsvedonConfig ?? (_AsvedonConfig = new LastCardView.ViewConfig(Warrior.AsvedonTheGrandshield) + { + Name = "Asvedon", + UpdateOn = GameEvents.OnOpponentPlay, + Condition = card => card.Type == "Spell", + }); + } + private static ViewConfig _AsvedonConfig; + + internal static ViewConfig LastRiffConfig + { + get => _LastRiffConfig ?? (_LastRiffConfig = new LastCardView.ViewConfig(Riffs.ToArray()) + { + Name = "LastRiff", + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => Riffs.Contains(card.Id), + }); + } + private static ViewConfig _LastRiffConfig; + + internal static readonly List Riffs = new List + { + Warrior.VerseRiff, + Warrior.BridgeRiff, + Warrior.ChorusRiff, + }; + + internal static ViewConfig EndgameConfig + { + get => _EndgameConfig ?? (_EndgameConfig = new LastCardView.ViewConfig(Warlock.Endgame) + { + Name = "Endgame", + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Race == "Demon", + }); + } + private static ViewConfig _EndgameConfig; + } +} diff --git a/Graveyard/MenagerieView.cs b/Graveyard/MenagerieView.cs new file mode 100644 index 0000000..d7ccc69 --- /dev/null +++ b/Graveyard/MenagerieView.cs @@ -0,0 +1,69 @@ +using Hearthstone_Deck_Tracker; +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Controls; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using static HearthDb.CardIds.Collectible; +using Card = Hearthstone_Deck_Tracker.Hearthstone.Card; + +namespace HDT.Plugins.Graveyard +{ + internal class MenagerieView : ViewBase + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Warrior.PowerSlider, Neutral.TheOneAmalgamBand) + { + Name = "Menagerie", + CreateView = () => new MenagerieView(), + UpdateOn = GameEvents.OnPlayerPlay, + }); + } + + public readonly HearthstoneTextBlock Label; + public readonly AnimatedCardList Cards; + + public Dictionary RaceList = new Dictionary(); + public int AllCount = 0; + + public MenagerieView() + { + Visibility = Visibility.Collapsed; + Orientation = Orientation.Vertical; + + Cards = new AnimatedCardList(); + Children.Add(Cards); + } + + public override bool Update(Card card) + { + var race = card.Race; + + if (string.IsNullOrEmpty(race)) return false; + + if (race == "All") + { + AllCount += 1; + race = $"All{AllCount}"; + } + + if (RaceList.ContainsKey(race)) + { + RaceList[race] = card.Clone() as Card; + } + else + { + RaceList.Add(race, card.Clone() as Card); + } + + Cards.Update(RaceList.Values.ToList(), true); + + Visibility = Visibility.Visible; + + return true; + } + } +} diff --git a/Graveyard/MinionsCreatedView.cs b/Graveyard/MinionsCreatedView.cs new file mode 100644 index 0000000..baf2918 --- /dev/null +++ b/Graveyard/MinionsCreatedView.cs @@ -0,0 +1,19 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class MinionsCreatedView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Priest.RaDen) + { + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerGet, + Condition = card => card.Type == "Minion", + }); + } + } +} diff --git a/Graveyard/MixtapeView.cs b/Graveyard/MixtapeView.cs new file mode 100644 index 0000000..94b0d4f --- /dev/null +++ b/Graveyard/MixtapeView.cs @@ -0,0 +1,20 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class MixtapeView + { + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig( + Rogue.Mixtape) + { + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnOpponentPlay, + Condition = card => true, + }); + } + private static ViewConfig _Config; + } +} diff --git a/Graveyard/MulchmuncherView.cs b/Graveyard/MulchmuncherView.cs index ca5026a..07af75c 100644 --- a/Graveyard/MulchmuncherView.cs +++ b/Graveyard/MulchmuncherView.cs @@ -1,24 +1,22 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class MulchmuncherView : NormalView + public class MulchmuncherView { - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Druid.Mulchmuncher) > -1; - } - - public MulchmuncherView() - { - // Section Label - Label.Text = Strings.GetLocalized("Mulchmuncher"); - } - - public bool Update(Card card) - { - return card.Name == "Treant" && base.Update(card); - } + get => _Config ?? (_Config = new ViewConfig(Druid.Mulchmuncher) + { + Name = "Mulchmuncher", + Enabled = "MulchmuncherEnabled", + ShowFirst = () => true, + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Name.Contains("Treant"), + }); + } } } diff --git a/Graveyard/MultiTurnView.cs b/Graveyard/MultiTurnView.cs new file mode 100644 index 0000000..d3886d7 --- /dev/null +++ b/Graveyard/MultiTurnView.cs @@ -0,0 +1,115 @@ +using Hearthstone_Deck_Tracker; +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using Card = Hearthstone_Deck_Tracker.Hearthstone.Card; + +namespace HDT.Plugins.Graveyard +{ + public class MultiTurnView : ViewBase + { + internal class ViewConfig : Plugins.Graveyard.ViewConfig + { + public ViewConfig(params string[] showOn) : base(showOn) + { + + } + + public override void RegisterView(ViewBase view, bool isDefault = false) + { + base.RegisterView(view, isDefault); + if (view is MultiTurnView multiTurn) + { + Plugin.Events.OnOpponentTurnStart.Register(multiTurn.TurnEnded); + } + } + } + + public HearthstoneTextBlock Label; + + public List Views = new List(); + + public List> CardLists = new List>(); + + public readonly int Turns; + + public MultiTurnView(int turns) + { + Turns = turns; + + Visibility = Visibility.Collapsed; + Orientation = Orientation.Vertical; + MinWidth = 250; + + // Turn Card Lists + for (int i = 0; i < Turns + 1; i++) + { + Views.Add(new TurnView(i == 0 ? "#" : i.ToString())); + Children.Add(Views[i]); + CardLists.Add(new List()); + } + } + + public override bool Update(Card card) + { + if (!Condition(card)) return false; + + CardLists[0].Add(card.Clone() as Card); + Views[0].Cards.Update(CardLists[0], false); + + Visibility = Visibility.Visible; + + return true; + } + + public async Task TurnEnded() + { + for (int i = Turns; i > 0; i--) + { + CardLists[i] = CardLists[i - 1]; + + } + CardLists[0] = new List(); + + var tasks = new List(); + for (int i = 0; i < Turns + 1; i++) + { + tasks.Add(Views[i].Cards.Update(CardLists[i], true)); + } + + await Task.WhenAll(tasks); + } + } + + public class TurnView : StackPanel + { + public HearthstoneTextBlock Title { get; private set; } + public AnimatedCardList Cards { get; private set; } + + public TurnView(string name) + { + Orientation = Orientation.Horizontal; + + Title = new HearthstoneTextBlock + { + FontSize = 24, + TextAlignment = TextAlignment.Center, + VerticalAlignment = VerticalAlignment.Top, + MinHeight = 30, + MinWidth = 30, + Text = name, + }; + Children.Add(Title); + + Cards = new AnimatedCardList(); + Children.Add(Cards); + } + } + +} diff --git a/Graveyard/MulticasterView.cs b/Graveyard/MulticasterView.cs new file mode 100644 index 0000000..a19c4e1 --- /dev/null +++ b/Graveyard/MulticasterView.cs @@ -0,0 +1,72 @@ +using Hearthstone_Deck_Tracker; +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Controls; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using static HearthDb.CardIds.Collectible; +using Card = Hearthstone_Deck_Tracker.Hearthstone.Card; + +namespace HDT.Plugins.Graveyard +{ + public class MulticasterView : ViewBase + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig( + Mage.MagisterDawngrasp, + Neutral.Multicaster, + Shaman.CoralKeeper, + Mage.InquisitiveCreation, + Mage.WisdomOfNorgannon, + Mage.Sif, + Mage.ElementalInspiration) + { + Name = "Multicaster", + Enabled = "MulticasterEnabled", + CreateView = () => new MulticasterView(), + UpdateOn = GameEvents.OnPlayerPlay, + }); + } + + public readonly HearthstoneTextBlock Label; + public readonly AnimatedCardList Cards; + + public Dictionary SchoolList = new Dictionary(); + + public MulticasterView() + { + Visibility = Visibility.Collapsed; + Orientation = Orientation.Vertical; + + Cards = new AnimatedCardList(); + Children.Add(Cards); + } + + public override bool Update(Card card) + { + var school = card.GetSchool(); + + if (school > School.General) + { + if (SchoolList.ContainsKey(school)) + { + SchoolList[school] = card.Clone() as Card; + } + else + { + SchoolList.Add(school, card.Clone() as Card); + } + + Cards.Update(SchoolList.Values.ToList(), true); + + Visibility = Visibility.Visible; + + return true; + } + return false; + } + } +} diff --git a/Graveyard/NZothGotDView.cs b/Graveyard/NZothGotDView.cs index e24d006..8ddda85 100644 --- a/Graveyard/NZothGotDView.cs +++ b/Graveyard/NZothGotDView.cs @@ -1,33 +1,21 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; -using System.Linq; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class NZothGotDView : NormalView + public class NZothGotDView { - private ChancesTracker _chances = new ChancesTracker(); - - public static bool isValid() - { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Neutral.NzothGodOfTheDeep) > -1; - } - - public NZothGotDView() + private static ViewConfig _Config; + internal static ViewConfig Config { - // Section Label - Label.Text = Strings.GetLocalized("NZothGotD"); - } - - public bool Update(Card card) - { - if ((card.Race != null || card.Type == "Minion" && WitchingHourView.ChooseOne.Contains(card.Id)) && base.Update(card)) - { - _chances.Update(card, Cards, View); - - return true; - } - return false; - } + get => _Config ?? (_Config = new ViewConfig(Neutral.NzothGodOfTheDeep) + { + Name = "NZothGotD", + Enabled = "NZothGotDEnabled", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Type == "Minion" && card.Race != null, + }); + } } } diff --git a/Graveyard/NZothView.cs b/Graveyard/NZothView.cs index 64dd694..e4150ee 100644 --- a/Graveyard/NZothView.cs +++ b/Graveyard/NZothView.cs @@ -1,25 +1,23 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; using System.Linq; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class NZothView : NormalView + public class NZothView { - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Neutral.NzothTheCorruptor) > -1; - } - - public NZothView() - { - // Section Label - Label.Text = Strings.GetLocalized("NZoth"); - } - - public bool Update(Card card) - { - return card.Mechanics.Contains("Deathrattle") && card.Id != HearthDb.CardIds.Collectible.Rogue.UnearthedRaptor && base.Update(card); + get => _Config ?? (_Config = new ViewConfig(Neutral.NzothTheCorruptor) + { + Name = "NZoth", + Enabled = "NZothEnabled", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Mechanics.Contains("Deathrattle") && card.Id != Rogue.UnearthedRaptor, + }); } + } } diff --git a/Graveyard/NormalView.cs b/Graveyard/NormalView.cs index 5b39c8e..caa0368 100644 --- a/Graveyard/NormalView.cs +++ b/Graveyard/NormalView.cs @@ -1,5 +1,6 @@ using Hearthstone_Deck_Tracker; using Hearthstone_Deck_Tracker.Controls; +using System; using System.Collections.Generic; using System.Linq; using System.Windows; @@ -8,7 +9,7 @@ namespace HDT.Plugins.Graveyard { - public class NormalView : StackPanel + public class NormalView : ViewBase { public List Cards; public HearthstoneTextBlock Label; @@ -16,33 +17,19 @@ public class NormalView : StackPanel public NormalView() { + Visibility = Visibility.Collapsed; Orientation = Orientation.Vertical; - // Section Label - Label = new HearthstoneTextBlock(); - Label.FontSize = 16; - Label.TextAlignment = TextAlignment.Center; - Label.Text = Strings.GetLocalized("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, bool isSpell = false) - { - if (card.Type != "Minion" && !isSpell) - { - return false; - } + public override bool Update(Card card) + { + if (!Condition(card)) return false; - // Increment var match = Cards.FirstOrDefault(c => c.Name == card.Name); if (match != null) { @@ -54,9 +41,15 @@ public bool Update(Card card, bool isSpell = false) } View.Update(Cards, false); - Label.Visibility = Visibility.Visible; + Visibility = Visibility.Visible; return true; } + + [Obsolete("Use update without isSpell parameter, Condition predicate should be updated instead")] + public bool Update(Card card, bool isSpell) + { + return Update(card); + } } } diff --git a/Graveyard/OgreGangView.cs b/Graveyard/OgreGangView.cs new file mode 100644 index 0000000..85fb7fa --- /dev/null +++ b/Graveyard/OgreGangView.cs @@ -0,0 +1,27 @@ +using Hearthstone_Deck_Tracker.API; +using System.Collections.Generic; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class OgreGangView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(Neutral.KingpinPud) + { + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => GangMembers.Contains(card.Id), + }); + } + + internal static readonly List GangMembers = new List + { + Neutral.OgreGangAce, + Neutral.OgreGangOutlaw, + Neutral.OgreGangRider, + }; + } +} diff --git a/Graveyard/PositionHelpers.cs b/Graveyard/PositionHelpers.cs new file mode 100644 index 0000000..bea1a36 --- /dev/null +++ b/Graveyard/PositionHelpers.cs @@ -0,0 +1,8 @@ +namespace HDT.Plugins.Graveyard +{ + public static class PositionHelpers + { + public static double PixelsToPercentage(this double value, double size) => value * 100 / size; + public static double PercentageToPixels(this double value, double size) => size * value / 100; + } +} diff --git a/Graveyard/Properties/AssemblyInfo.cs b/Graveyard/Properties/AssemblyInfo.cs index 5f8d843..68c2c97 100644 --- a/Graveyard/Properties/AssemblyInfo.cs +++ b/Graveyard/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Graveyard")] -[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.8.11.0")] -[assembly: AssemblyFileVersion("1.8.11.0")] +[assembly: AssemblyVersion("1.19.0.0")] +[assembly: AssemblyFileVersion("1.19.0.0")] diff --git a/Graveyard/Properties/LibraryInfo.cs b/Graveyard/Properties/LibraryInfo.cs new file mode 100644 index 0000000..0c6ff2d --- /dev/null +++ b/Graveyard/Properties/LibraryInfo.cs @@ -0,0 +1,14 @@ +using System; +using System.Reflection; + +namespace HDT.Plugins.Graveyard.Properties +{ + public static class LibraryInfo + { + public static readonly AssemblyName AssemblyName = Assembly.GetExecutingAssembly().GetName(); + public static readonly Version AssemblyVersion = AssemblyName.Version; + + public static string Name = AssemblyName.Name; + public static readonly Version Version = new Version(AssemblyVersion.Major, AssemblyVersion.Minor, AssemblyVersion.Build); + } +} diff --git a/Graveyard/QuestlineView.cs b/Graveyard/QuestlineView.cs new file mode 100644 index 0000000..70c11ef --- /dev/null +++ b/Graveyard/QuestlineView.cs @@ -0,0 +1,130 @@ +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Hearthstone; +using System; +using Coll = HearthDb.CardIds.Collectible; +using Non = HearthDb.CardIds.NonCollectible; + +namespace HDT.Plugins.Graveyard +{ + public class QuestlineView : NormalView + { + private static readonly string SharedName = "Questline"; + private static readonly Func SharedCreateView = () => new QuestlineView(); + private static readonly Predicate SharedCondition = card => true; + + internal static ViewConfig FriendlyConfig + { + get => _FriendlyConfig ?? (_FriendlyConfig = new ViewConfig() + { + Name = SharedName, + Enabled = "FriendlyQuestlineEnabled", + ShowFirst = () => true, + CreateView = SharedCreateView, + UpdateOn = GameEvents.OnPlayerPlay, + Condition = SharedCondition, + }); + } + private static ViewConfig _FriendlyConfig; + + internal static ViewConfig EnemyConfig + { + get => _EnemyConfig ?? (_EnemyConfig = new ViewConfig() + { + Name = SharedName, + Enabled = "EnemyQuestlineEnabled", + CreateView = SharedCreateView, + UpdateOn = GameEvents.OnOpponentPlay, + Condition = SharedCondition, + }); + } + private static ViewConfig _EnemyConfig; + + internal class ViewConfig : Plugins.Graveyard.ViewConfig + { + public ViewConfig() : base() + { + + } + + public override void RegisterView(ViewBase view, bool isDefault = false) + { + base.RegisterView(view, isDefault); + if (view is QuestlineView questlineView) + { + if (UpdateOn == GameEvents.OnPlayerPlay) + { + Plugin.Events.OnPlayerCreateInPlay.Register(questlineView.HunterShortcut); + } + else if (UpdateOn == GameEvents.OnOpponentPlay) + { + Plugin.Events.OnOpponentCreateInPlay.Register(questlineView.HunterShortcut); + } + } + } + } + + private bool HunterShortcut(Card card) + { + if (card.Id == "SW_322e3") + { + return base.Update(Database.GetCardFromId(Non.Hunter.DefendtheDwarvenDistrict_TakeTheHighGroundToken)); + } + else if (card.Id == "SW_322e") + { + return base.Update(Database.GetCardFromId(Non.Hunter.DefendtheDwarvenDistrict_KnockEmDownToken)); + } + return true; + } + + public override bool Update(Card card) + { + switch (card.Id) + { + case Coll.Demonhunter.FinalShowdown: + case Non.Demonhunter.FinalShowdown_GainMomentumToken: + case Non.Demonhunter.FinalShowdown_CloseThePortalToken: + case Non.Demonhunter.FinalShowdown_DemonslayerKurtrusToken: + case Coll.Druid.LostInThePark: + case Non.Druid.LostinthePark_DefendTheSquirrelsToken: + case Non.Druid.LostinthePark_FeralFriendsyToken: + case Non.Druid.LostinthePark_GuffTheToughToken: + case Coll.Hunter.DefendTheDwarvenDistrict: + case Non.Hunter.DefendtheDwarvenDistrict_TakeTheHighGroundToken: + case Non.Hunter.DefendtheDwarvenDistrict_KnockEmDownToken: + case Non.Hunter.DefendtheDwarvenDistrict_TavishMasterMarksmanToken: + case Coll.Mage.SorcerersGambit: + case Non.Mage.SorcerersGambit_StallForTimeToken: + case Non.Mage.SorcerersGambit_ReachThePortalRoomToken: + case Non.Mage.SorcerersGambit_ArcanistDawngraspToken: + case Coll.Paladin.RiseToTheOccasion: + case Non.Paladin.RisetotheOccasion_PaveTheWayToken: + case Non.Paladin.RisetotheOccasion_AvengeTheFallenToken: + case Non.Paladin.RisetotheOccasion_LightbornCarielToken: + case Coll.Priest.SeekGuidance: + case Non.Priest.SeekGuidance_DiscoverTheVoidShardToken: + case Non.Priest.SeekGuidance_IlluminateTheVoidToken: + case Non.Priest.SeekGuidance_XyrellaTheSanctifiedToken: + case Coll.Rogue.FindTheImposter: + case Non.Rogue.FindtheImposter_LearnTheTruthToken: + case Non.Rogue.FindtheImposter_MarkedATraitorToken: + case Non.Rogue.FindtheImposter_SpymasterScabbsToken: + case Coll.Shaman.CommandTheElements: + case Non.Shaman.CommandtheElements_StirTheStonesToken: + case Non.Shaman.CommandtheElements_TameTheFlamesToken: + case Non.Shaman.CommandtheElements_StormcallerBrukanToken: + case Coll.Warlock.TheDemonSeed: + case Non.Warlock.TheDemonSeed_EstablishTheLinkToken: + case Non.Warlock.TheDemonSeed_CompleteTheRitualToken: + case Non.Warlock.TheDemonSeed_BlightbornTamsinToken: + case Coll.Warrior.RaidTheDocks: + case Non.Warrior.RaidtheDocks_CreateADistractionToken: + case Non.Warrior.RaidtheDocks_SecureTheSuppliesToken: + case Non.Warrior.RaidtheDocks_CapnRokaraToken: + return base.Update(card); + default: + return false; + } + + } + } +} diff --git a/Graveyard/RallyView.cs b/Graveyard/RallyView.cs index 112ecdf..e6e56c7 100644 --- a/Graveyard/RallyView.cs +++ b/Graveyard/RallyView.cs @@ -1,34 +1,23 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class RallyView : NormalView + public class RallyView { - private ChancesTracker _chances = new ChancesTracker(); - - public static bool isValid() - { - return AlwaysSeparate && Core.Game.Player.PlayerCardList.FindIndex(card => - card.Id == HearthDb.CardIds.Collectible.Neutral.Rally) > -1; - } - - public static bool AlwaysSeparate => Settings.Default.AlwaysRallySeparately || !Settings.Default.ResurrectEnabled; // This is iffy - - public RallyView() + private static ViewConfig _Config; + internal static ViewConfig Config { - // Section Label - Label.Text = Strings.GetLocalized("Rally"); - } - - public bool Update(Card card) - { - var update = card.Type == "Minion" && card.Cost >= 1 && card.Cost <= 3 && base.Update(card); - - if (update) - _chances.Update(card, Cards, View); - - return update; + get => _Config ?? (_Config = new ViewConfig(Neutral.Rally) + { + Name = "Rally", + Enabled = "RallyEnabled", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Type == "Minion" && card.Cost >= 1 && card.Cost <= 3, + }); } + + public static bool IsAlwaysSeparate => Settings.Default.RallyEnabled && Settings.Default.AlwaysRallySeparately; } } diff --git a/Graveyard/ReanimateView.cs b/Graveyard/ReanimateView.cs new file mode 100644 index 0000000..7deb1ad --- /dev/null +++ b/Graveyard/ReanimateView.cs @@ -0,0 +1,23 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + public class ReanimateView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig( + Priest.Bonecaller, + Priest.CatrinaMuerte, + Warlock.InfantryReanimator) + { + Name = "Reanimate", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Race == "Undead" || card.Race == "All", + }); + } + } +} diff --git a/Graveyard/RelicView.cs b/Graveyard/RelicView.cs new file mode 100644 index 0000000..7888e59 --- /dev/null +++ b/Graveyard/RelicView.cs @@ -0,0 +1,113 @@ +using Hearthstone_Deck_Tracker.API; +using Hearthstone_Deck_Tracker.Hearthstone; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class RelicView : NormalView + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig(ShowOn.ToArray()) + { + Name = "Relic", + CreateView = () => new RelicView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => Relics.Contains(card.Id), + }); + } + + private readonly string RelicsofOldId = "REV_942e2"; + private bool RelicsofOldWaiting = false; + private bool RelicsofOldCheck(Card card) + { + if (card.Id == RelicsofOldId) + { + RelicsofOldWaiting = true; + return true; + } + return false; + } + + private bool RelicsofOldUncheck(Card card) + { + if (card.Id == RelicsofOldId) + { + RelicsofOldWaiting = false; + return true; + } + return false; + } + + private bool RelicsCheck(Card card) + { + if (RelicsofOldCheck(card)) return true; + if (Config.Condition(card)) + { + UpdateList(card); + return true; + } + return false; + } + + public override bool Update(Card card) + { + if (!Condition(card)) return false; + + UpdateList(card, RelicsofOldWaiting ? 2 : 1); + + return true; + } + + private void UpdateList(Card card, int countToAdd = 1) + { + var match = Cards.FirstOrDefault(c => c.Name == card.Name); + if (match != null) + { + match.Count += countToAdd; + } + else + { + var clone = card.Clone() as Card; + clone.Count = countToAdd; + Cards.Add(clone); + } + View.Update(Cards, false); + + Visibility = Visibility.Visible; + } + + internal static readonly List Relics = new List + { + Demonhunter.RelicOfExtinction, + Demonhunter.RelicOfPhantasms, + Demonhunter.RelicOfDimensions, + }; + + internal static readonly List ShowOn = Relics.Append(Demonhunter.ArtificerXymox).ToList(); + + internal class ViewConfig : Plugins.Graveyard.ViewConfig + { + public ViewConfig(params string[] showOn) : base(showOn) + { + + } + + public override void RegisterView(ViewBase view, bool isDefault = false) + { + base.RegisterView(view, isDefault); + if (view is RelicView relicView) + { + Plugin.Events.OnPlayerCreateInPlay.Register(relicView.RelicsCheck); + Plugin.Events.OnPlayerPlayToGraveyard.Register(relicView.RelicsofOldUncheck); + } + } + } + + + } +} diff --git a/Graveyard/Resources/Strings.Designer.cs b/Graveyard/Resources/Strings.Designer.cs index bebf28d..f532467 100644 --- a/Graveyard/Resources/Strings.Designer.cs +++ b/Graveyard/Resources/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace HDT.Plugins.Graveyard.Resources { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Strings { @@ -61,74 +61,20 @@ internal Strings() { } /// - /// Looks up a localized string similar to Dragoncaller Alanna. + /// Looks up a localized string similar to Cards. /// - public static string Alanna { + public static string Cards { get { - return ResourceManager.GetString("Alanna", resourceCulture); + return ResourceManager.GetString("Cards", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'Dragoncaller Alanna', show spells that cost 5 or more that have been played.. + /// Looks up a localized string similar to Show player card lists based on player starting deck contents. /// - public static string AlannaDescription { + public static string CardsDescription { get { - return ResourceManager.GetString("AlannaDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Always Rally! separately. - /// - public static string AlwaysRallySeparately { - get { - return ResourceManager.GetString("AlwaysRallySeparately", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show a separate view for Rally! even when the Resurrect view is active. - /// - public static string AlwaysRallySeparatelyDescription { - get { - return ResourceManager.GetString("AlwaysRallySeparatelyDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Anyfin. - /// - public static string Anyfin { - get { - return ResourceManager.GetString("Anyfin", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to When the deck contains 'Anyfin Can Happen', show murlocs that have died and display minimum and maximum possible damage. - /// - public static string AnyfinDescription { - get { - return ResourceManager.GetString("AnyfinDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The Caverns Below. - /// - public static string Caverns { - get { - return ResourceManager.GetString("Caverns", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to When the deck contains 'The Caverns Below', count all minions played. - /// - public static string CavernsDescription { - get { - return ResourceManager.GetString("CavernsDescription", resourceCulture); + return ResourceManager.GetString("CardsDescription", resourceCulture); } } @@ -142,7 +88,7 @@ public static string Deathrattle { } /// - /// Looks up a localized string similar to When the deck contains 'Twilight's Call', show the deathrattle minions that have died. + /// Looks up a localized string similar to Show player deathrattle minions that have died. /// public static string DeathrattleDescription { get { @@ -151,34 +97,16 @@ public static string DeathrattleDescription { } /// - /// Looks up a localized string similar to Discard. + /// Looks up a localized string similar to Four Horsemen. /// - public static string Discard { + public static string FourHorsemen { get { - return ResourceManager.GetString("Discard", resourceCulture); + return ResourceManager.GetString("FourHorsemen", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'Cruel Dinomancer', show summon chance of friendly minions that have been discarded. - /// - public static string DiscardDescription { - get { - return ResourceManager.GetString("DiscardDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Discarded Minions. - /// - public static string DiscardTitle { - get { - return ResourceManager.GetString("DiscardTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Friendly. + /// Looks up a localized string similar to Player. /// public static string Friendly { get { @@ -187,7 +115,7 @@ public static string Friendly { } /// - /// Looks up a localized string similar to All friendly minions that have died.. + /// Looks up a localized string similar to Show all player minions that have died. /// public static string FriendlyDescription { get { @@ -206,15 +134,11 @@ public static string Graveyard { /// /// Looks up a localized string similar to Displays minions that have died this game. Includes specialized displays: - ///- Deathrattle minions (Nine Lives/Da Undatakah/Twilight's Call/N'Zoth) - ///- Taunt minions (Hadronox) - ///- Demon deaths (Bloodreaver Gul'dan/Kanrethad Ebonlocke) - ///- Resurrection chance (Catrina Muerte/Mass Resurrection/Wild Priest cards) - ///- Murloc minions with a damage calculator for Anyfin Can Happen - ///- Discard retrieve chance (Soulwarden/Cruel Dinomancer) - ///- Treant deaths (Mulchmuncher) - ///- Mech deaths (Kangor's Endless Army) - ///- Spells [rest of string was truncated]";. + /// + ///- Resurrection chance + ///- Deathrattle minions + /// + ///Go to Settings for the full list of cards supported. /// public static string GraveyardDescription { get { @@ -232,29 +156,11 @@ public static string Guldan { } /// - /// Looks up a localized string similar to When the deck contains 'Bloodreaver Gul'dan' or 'Kanrethad Ebonlocke', show demons that have died. - /// - public static string GuldanDescription { - get { - return ResourceManager.GetString("GuldanDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hadronox. - /// - public static string Hadronox { - get { - return ResourceManager.GetString("Hadronox", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to When the deck contains 'Hadronox', show the friendly taunt minions that have died. + /// Looks up a localized string similar to Help. /// - public static string HadronoxDescription { + public static string Help { get { - return ResourceManager.GetString("HadronoxDescription", resourceCulture); + return ResourceManager.GetString("Help", resourceCulture); } } @@ -267,15 +173,6 @@ public static string HoardPillager { } } - /// - /// Looks up a localized string similar to When the deck contains 'Hoard Pillager' or 'Rummaging Kobold', show equip/return chance of weapons used this game.. - /// - public static string HoardPillagerDescription { - get { - return ResourceManager.GetString("HoardPillagerDescription", resourceCulture); - } - } - /// /// Looks up a localized string similar to Horizontal. /// @@ -286,286 +183,286 @@ public static string Horizontal { } /// - /// Looks up a localized string similar to Kangor's Endless Army. + /// Looks up a localized string similar to Last Riff Played. /// - public static string Kangor { + public static string LastRiff { get { - return ResourceManager.GetString("Kangor", resourceCulture); + return ResourceManager.GetString("LastRiff", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'Kangor's Endless Army', show summon chance of friendly (base) Mechs that have died. + /// Looks up a localized string similar to Lock Graveyards. /// - public static string KangorDescription { + public static string Lock { get { - return ResourceManager.GetString("KangorDescription", resourceCulture); + return ResourceManager.GetString("Lock", resourceCulture); } } /// - /// Looks up a localized string similar to Kazakus. + /// Looks up a localized string similar to Last Spell by School. /// - public static string Kazakus { + public static string Multicaster { get { - return ResourceManager.GetString("Kazakus", resourceCulture); + return ResourceManager.GetString("Multicaster", resourceCulture); } } /// - /// Looks up a localized string similar to Also display for resurrect chances with Kazakus.. + /// Looks up a localized string similar to Opacity. /// - public static string KazakusDescription { + public static string Opacity { get { - return ResourceManager.GetString("KazakusDescription", resourceCulture); + return ResourceManager.GetString("Opacity", resourceCulture); } } /// - /// Looks up a localized string similar to Lady Liadrin. + /// Looks up a localized string similar to Opponent. /// - public static string LadyLiadrin { + public static string Opponent { get { - return ResourceManager.GetString("LadyLiadrin", resourceCulture); + return ResourceManager.GetString("Opponent", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'Lady Liadrin', show all spells cast on friendly characters this game.. + /// Looks up a localized string similar to Show opponent deathrattle minions that have died. /// - public static string LadyLiadrinDescription { + public static string OpponentDeathrattleDescription { get { - return ResourceManager.GetString("LadyLiadrinDescription", resourceCulture); + return ResourceManager.GetString("OpponentDeathrattleDescription", resourceCulture); } } /// - /// Looks up a localized string similar to Lock Graveyards. + /// Looks up a localized string similar to Show all opponent minions that have died. /// - public static string Lock { + public static string OpponentDescription { get { - return ResourceManager.GetString("Lock", resourceCulture); + return ResourceManager.GetString("OpponentDescription", resourceCulture); } } /// - /// Looks up a localized string similar to Mulchmuncher. + /// Looks up a localized string similar to Show opponent questline progress. /// - public static string Mulchmuncher { + public static string OpponentQuestlineDescription { get { - return ResourceManager.GetString("Mulchmuncher", resourceCulture); + return ResourceManager.GetString("OpponentQuestlineDescription", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains at least one 'Mulchmuncher', count Treants that have died. + /// Looks up a localized string similar to Orientation. /// - public static string MulchmuncherDescription { + public static string Orientation { get { - return ResourceManager.GetString("MulchmuncherDescription", resourceCulture); + return ResourceManager.GetString("Orientation", resourceCulture); } } /// - /// Looks up a localized string similar to N'Zoth, the Corruptor. + /// Looks up a localized string similar to Vertical or Horizontal view stacking. /// - public static string NZoth { + public static string OrientationDescription { get { - return ResourceManager.GetString("NZoth", resourceCulture); + return ResourceManager.GetString("OrientationDescription", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'N'Zoth, the Corruptor', show the deathrattle minions that have died. + /// Looks up a localized string similar to Report Issue. /// - public static string NZothDescription { + public static string PluginIssues { get { - return ResourceManager.GetString("NZothDescription", resourceCulture); + return ResourceManager.GetString("PluginIssues", resourceCulture); } } /// - /// Looks up a localized string similar to N'Zoth, God of the Deep. + /// Looks up a localized string similar to README. /// - public static string NZothGotD { + public static string PluginReadme { get { - return ResourceManager.GetString("NZothGotD", resourceCulture); + return ResourceManager.GetString("PluginReadme", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'N'Zoth, God of the Deep', show all minions, with a specific type, that have died. + /// Looks up a localized string similar to Latest Release. /// - public static string NZothGotDDescription { + public static string PluginReleases { get { - return ResourceManager.GetString("NZothGotDDescription", resourceCulture); + return ResourceManager.GetString("PluginReleases", resourceCulture); } } /// - /// Looks up a localized string similar to Opacity. + /// Looks up a localized string similar to Questline. /// - public static string Opacity { + public static string Questline { get { - return ResourceManager.GetString("Opacity", resourceCulture); + return ResourceManager.GetString("Questline", resourceCulture); } } /// - /// Looks up a localized string similar to Opponent. + /// Looks up a localized string similar to Show player questline progress. /// - public static string Opponent { + public static string QuestlineDescription { get { - return ResourceManager.GetString("Opponent", resourceCulture); + return ResourceManager.GetString("QuestlineDescription", resourceCulture); } } /// - /// Looks up a localized string similar to All enemy minions that have died. + /// Looks up a localized string similar to Undead. /// - public static string OpponentDescription { + public static string Reanimate { get { - return ResourceManager.GetString("OpponentDescription", resourceCulture); + return ResourceManager.GetString("Reanimate", resourceCulture); } } /// - /// Looks up a localized string similar to Orientation. + /// Looks up a localized string similar to Relics Cast. /// - public static string Orientation { + public static string Relic { get { - return ResourceManager.GetString("Orientation", resourceCulture); + return ResourceManager.GetString("Relic", resourceCulture); } } /// - /// Looks up a localized string similar to Vertical or Horizontal view stacking. + /// Looks up a localized string similar to Resurrect. /// - public static string OrientationDescription { + public static string Resurrect { get { - return ResourceManager.GetString("OrientationDescription", resourceCulture); + return ResourceManager.GetString("Resurrect", resourceCulture); } } /// - /// Looks up a localized string similar to Rally!. + /// Looks up a localized string similar to Show the resurrect chance of player minions that have died. /// - public static string Rally { + public static string ResurrectDescription { get { - return ResourceManager.GetString("Rally", resourceCulture); + return ResourceManager.GetString("ResurrectDescription", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'Rally!', show the resurrect chance of friendly 1, 2 and 3 cost minions that have died. + /// Looks up a localized string similar to Scale. /// - public static string RallyDescription { + public static string Scale { get { - return ResourceManager.GetString("RallyDescription", resourceCulture); + return ResourceManager.GetString("Scale", resourceCulture); } } /// - /// Looks up a localized string similar to Resurrect. + /// Looks up a localized string similar to Settings. /// - public static string Resurrect { + public static string Settings { get { - return ResourceManager.GetString("Resurrect", resourceCulture); + return ResourceManager.GetString("Settings", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'Resurrect', 'Onyx Bishop', 'Eternal Servitude', or 'Lesser Diamond Spellstone', show the resurrect chance of friendly minions that have died. + /// Looks up a localized string similar to Cards. /// - public static string ResurrectDescription { + public static string SettingsCardsTitle { get { - return ResourceManager.GetString("ResurrectDescription", resourceCulture); + return ResourceManager.GetString("SettingsCardsTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Overlord Saurfang. + /// Looks up a localized string similar to Display. /// - public static string Saurfang { + public static string SettingsDisplayTitle { get { - return ResourceManager.GetString("Saurfang", resourceCulture); + return ResourceManager.GetString("SettingsDisplayTitle", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'Overload Saurfang', show summon chance of Frenzy minions that have died.. + /// Looks up a localized string similar to Horizontal. /// - public static string SaurfangDescription { + public static string SettingsHorizontal { get { - return ResourceManager.GetString("SaurfangDescription", resourceCulture); + return ResourceManager.GetString("SettingsHorizontal", resourceCulture); } } /// - /// Looks up a localized string similar to Scale. + /// Looks up a localized string similar to % Screen Position (Left <-> Right). /// - public static string Scale { + public static string SettingsHorizontalTooltip { get { - return ResourceManager.GetString("Scale", resourceCulture); + return ResourceManager.GetString("SettingsHorizontalTooltip", resourceCulture); } } /// - /// Looks up a localized string similar to Settings. + /// Looks up a localized string similar to Position. /// - public static string Settings { + public static string SettingsPositionTitle { get { - return ResourceManager.GetString("Settings", resourceCulture); + return ResourceManager.GetString("SettingsPositionTitle", resourceCulture); } } /// - /// Looks up a localized string similar to Graveyard Settings. + /// Looks up a localized string similar to Reset to Default. /// - public static string SettingsTitle { + public static string SettingsResetButtonTooltip { get { - return ResourceManager.GetString("SettingsTitle", resourceCulture); + return ResourceManager.GetString("SettingsResetButtonTooltip", resourceCulture); } } /// - /// Looks up a localized string similar to Shudderwock. + /// Looks up a localized string similar to Graveyard Settings. /// - public static string Shudderwock { + public static string SettingsTitle { get { - return ResourceManager.GetString("Shudderwock", resourceCulture); + return ResourceManager.GetString("SettingsTitle", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'Shudderwock', show battlecry minions that have been played. + /// Looks up a localized string similar to Vertical. /// - public static string ShudderwockDescription { + public static string SettingsVertical { get { - return ResourceManager.GetString("ShudderwockDescription", resourceCulture); + return ResourceManager.GetString("SettingsVertical", resourceCulture); } } /// - /// Looks up a localized string similar to Soulwarden. + /// Looks up a localized string similar to % Screen Position (Top <-> Bottom). /// - public static string Soulwarden { + public static string SettingsVerticalTooltip { get { - return ResourceManager.GetString("Soulwarden", resourceCulture); + return ResourceManager.GetString("SettingsVerticalTooltip", resourceCulture); } } /// - /// Looks up a localized string similar to When the deck contains 'Soulwarden', show chance of retrieving friendly cards that have been discarded. + /// Looks up a localized string similar to SI:7. /// - public static string SoulwardenDescription { + public static string SI7 { get { - return ResourceManager.GetString("SoulwardenDescription", resourceCulture); + return ResourceManager.GetString("SI7", resourceCulture); } } /// - /// Looks up a localized string similar to Tess Greymane. + /// Looks up a localized string similar to Other Class Cards. /// public static string TessGreymane { get { @@ -573,15 +470,6 @@ public static string TessGreymane { } } - /// - /// Looks up a localized string similar to When the deck contains 'Tess Greymane', show all cards from other classes played this game.. - /// - public static string TessGreymaneDescription { - get { - return ResourceManager.GetString("TessGreymaneDescription", resourceCulture); - } - } - /// /// Looks up a localized string similar to Unlock Graveyards. /// @@ -601,39 +489,12 @@ public static string Vertical { } /// - /// Looks up a localized string similar to Witching Hour. + /// Looks up a localized string similar to Beasts. /// public static string WitchingHour { get { return ResourceManager.GetString("WitchingHour", resourceCulture); } } - - /// - /// Looks up a localized string similar to When the deck contains 'Witching Hour', show summon chance of friendly Beasts that have died. - /// - public static string WitchingHourDescription { - get { - return ResourceManager.GetString("WitchingHourDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Zul'jin. - /// - public static string Zuljin { - get { - return ResourceManager.GetString("Zuljin", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to When the deck contains 'Zul'jin', show all spells played this game.. - /// - public static string ZuljinDescription { - get { - return ResourceManager.GetString("ZuljinDescription", resourceCulture); - } - } } } diff --git a/Graveyard/Resources/Strings.resx b/Graveyard/Resources/Strings.resx index 5f9dc5a..a7dbc8a 100644 --- a/Graveyard/Resources/Strings.resx +++ b/Graveyard/Resources/Strings.resx @@ -117,129 +117,58 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Dragoncaller Alanna + + Cards - - When the deck contains 'Dragoncaller Alanna', show spells that cost 5 or more that have been played. - - - Always Rally! separately - - - Show a separate view for Rally! even when the Resurrect view is active - - - Anyfin - - - When the deck contains 'Anyfin Can Happen', show murlocs that have died and display minimum and maximum possible damage - - - The Caverns Below - - - When the deck contains 'The Caverns Below', count all minions played + + Show player card lists based on player starting deck contents Deathrattle - When the deck contains 'Twilight's Call', show the deathrattle minions that have died + Show player deathrattle minions that have died - - Discard - - - When the deck contains 'Cruel Dinomancer', show summon chance of friendly minions that have been discarded - - - Discarded Minions + + Four Horsemen - Friendly + Player - All friendly minions that have died. + Show all player minions that have died Graveyard Displays minions that have died this game. Includes specialized displays: -- Deathrattle minions (Nine Lives/Da Undatakah/Twilight's Call/N'Zoth) -- Taunt minions (Hadronox) -- Demon deaths (Bloodreaver Gul'dan/Kanrethad Ebonlocke) -- Resurrection chance (Catrina Muerte/Mass Resurrection/Wild Priest cards) -- Murloc minions with a damage calculator for Anyfin Can Happen -- Discard retrieve chance (Soulwarden/Cruel Dinomancer) -- Treant deaths (Mulchmuncher) -- Mech deaths (Kangor's Endless Army) -- Spells cast (Lady Lianna/Zul'jin/Dragoncaller Alanna) -- Beast minions resummon chance (Witching Hour) -- Minions played count (The Caverns Below) -- Other class cards played (Tess Greymane) -- Battlecry cards played (Shudderwock) + +- Resurrection chance +- Deathrattle minions + +Go to Settings for the full list of cards supported Demons - - When the deck contains 'Bloodreaver Gul'dan' or 'Kanrethad Ebonlocke', show demons that have died - - - Hadronox - - - When the deck contains 'Hadronox', show the friendly taunt minions that have died + + Help Weapons Used - - When the deck contains 'Hoard Pillager' or 'Rummaging Kobold', show equip/return chance of weapons used this game. - Horizontal - - Kangor's Endless Army - - - When the deck contains 'Kangor's Endless Army', show summon chance of friendly (base) Mechs that have died - - - Kazakus - - - Also display for resurrect chances with Kazakus. - - - Lady Liadrin - - - When the deck contains 'Lady Liadrin', show all spells cast on friendly characters this game. + + Last Riff Played Lock Graveyards - - Mulchmuncher - - - When the deck contains at least one 'Mulchmuncher', count Treants that have died - - - N'Zoth, the Corruptor - - - When the deck contains 'N'Zoth, the Corruptor', show the deathrattle minions that have died - - - N'Zoth, God of the Deep - - - When the deck contains 'N'Zoth, God of the Deep', show all minions, with a specific type, that have died + + Last Spell by School Opacity @@ -247,8 +176,14 @@ Opponent + + Show opponent deathrattle minions that have died + - All enemy minions that have died + Show all opponent minions that have died + + + Show opponent questline progress Orientation @@ -256,23 +191,32 @@ Vertical or Horizontal view stacking - - Rally! + + Report Issue + + + README + + + Latest Release + + + Questline + + + Show player questline progress - - When the deck contains 'Rally!', show the resurrect chance of friendly 1, 2 and 3 cost minions that have died + + Undead + + + Relics Cast Resurrect - When the deck contains 'Resurrect', 'Onyx Bishop', 'Eternal Servitude', or 'Lesser Diamond Spellstone', show the resurrect chance of friendly minions that have died - - - Overlord Saurfang - - - When the deck contains 'Overload Saurfang', show summon chance of Frenzy minions that have died. + Show the resurrect chance of player minions that have died Scale @@ -280,26 +224,38 @@ Settings + + Cards + + + Display + + + Horizontal + + + % Screen Position (Left <-> Right) + + + Position + + + Reset to Default + Graveyard Settings - - Shudderwock - - - When the deck contains 'Shudderwock', show battlecry minions that have been played + + Vertical - - Soulwarden + + % Screen Position (Top <-> Bottom) - - When the deck contains 'Soulwarden', show chance of retrieving friendly cards that have been discarded + + SI:7 - Tess Greymane - - - When the deck contains 'Tess Greymane', show all cards from other classes played this game. + Other Class Cards Unlock Graveyards @@ -308,15 +264,6 @@ Vertical - Witching Hour - - - When the deck contains 'Witching Hour', show summon chance of friendly Beasts that have died - - - Zul'jin - - - When the deck contains 'Zul'jin', show all spells played this game. + Beasts \ No newline at end of file diff --git a/Graveyard/Resources/Strings.zh-CN.resx b/Graveyard/Resources/Strings.zh-CN.resx index dd96a92..2ef47e3 100644 --- a/Graveyard/Resources/Strings.zh-CN.resx +++ b/Graveyard/Resources/Strings.zh-CN.resx @@ -117,23 +117,11 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 巨龙召唤者奥兰纳 + + - - 如果卡组中包含巨龙召唤者奥兰纳,显示所有打出过的5费或以上的法术牌 - - - 亡者归来 - - - 如果卡组中包含亡者归来,显示所有死亡的鱼人并显示最小和最大的伤害值 - - - 探索地下洞穴 - - - 如果卡组中包含探索地下洞穴,计算所有打出的随从数 + + 亡语 @@ -141,14 +129,8 @@ 如果卡组中包含暮光召唤,显示所有死亡的亡语随从 - - 弃牌 - - - 如果卡组中包含残暴的恐龙术士,显示所有被弃掉的友方随从及其复活概率 - - - 被弃掉的随从 + + 四骑士 友方墓地显示设置 @@ -160,73 +142,23 @@ 墓地 - 显示本局对战中死亡的随从。包含以下特殊计数器: -- 亡语随从(九命兽魂/送葬者安德提卡/暮光召唤/恩佐斯) -- 嘲讽随从(哈多诺克斯) -- 恶魔(鲜血掠夺者古尔丹) -- 复活几率(亡者卡特琳娜/群体复活/各类狂野牧师复活卡) -- 鱼人及一个亡者归来伤害计算器 -- 弃置的牌及其回归概率(护魂者/残暴的恐龙术士) -- 树人死亡数量(植被破碎机) -- 机械随从(坎格尔的无尽大军) -- 5费及以上的法术(巨龙召唤者奥兰纳) -- 野兽复活几率(巫术时刻) -- 随从打出数量(探索地下洞穴) + 显示在此游戏中死亡的仆从。 包含以下特殊计数器: + +- 复活机会 +- 亡语追随者 +- 任务线进度 + +转到设置以获取支持的卡的完整列表 恶魔 - - 如果卡组中包含鲜血掠夺者古尔丹或坎雷萨德·埃伯洛克,显示所有死亡的恶魔及其概率 - - - 哈多诺克斯 - - - 如果卡组中包含哈多诺克斯,显示所有死亡的嘲讽随从 - 使用过的武器 - - 如果卡组中包含藏宝匪贼或狗头人拾荒者,显示所有使用过的武器及其概率 - - - 坎格尔的无尽大军 - - - 如果卡组中包含坎格尔的无尽大军,显示所有死亡的机械随从及其复活概率 - - - 卡扎库斯 - - - 显示卡扎库斯药水的所有复活概率 - - - 女伯爵莉亚德琳 - - - 如果卡座中有伯爵夫人莉亚德琳伯爵夫人,出示所有您用友善角色打过的咒语牌 - 锁定位置 - - 植被破碎机 - - - 如果卡组中包含至少一张植被破碎机,计算所有死亡的树人数量 - - - 恩佐斯 - - - 如果卡组中包含恩佐斯,显示所有死亡的亡语随从 - - - 恩佐斯,深渊之神 - 不透明度 @@ -236,11 +168,14 @@ 所有死亡的敌方随从 + + 亡灵 + 复活池 - 如果卡组中包含复活术、玛瑙主教、永恒奴役、小型法术钻石或群体复活,显示所有死亡的友方随从及其复活概率 + 显示所有死亡的友方随从及其复活概率 大小 @@ -251,37 +186,13 @@ 墓地设置 - - 沙德沃克 - - - 如果卡组包含沙德沃克,显示所有打出过的战吼随从 - - - 护魂者 - - - 如果卡组中包含护魂者,显示所有被弃掉的牌及其重新置入手牌概率 - 苔丝·格雷迈恩 - - 如果卡组中包含苔丝·格雷迈恩,显示所有使用过的其他职业的卡牌 - 调整位置 巫术时刻 - - 如果卡组中包含巫术时刻,显示所有死亡的友方野兽及其复活概率 - - - 祖尔金 - - - 如果卡组中包含祖尔金,显示所有打出过的法术牌 - \ No newline at end of file diff --git a/Graveyard/ResurrectView.cs b/Graveyard/ResurrectView.cs index 4f60beb..cf51770 100644 --- a/Graveyard/ResurrectView.cs +++ b/Graveyard/ResurrectView.cs @@ -1,45 +1,31 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class ResurrectView : NormalView + public class ResurrectView { - private ChancesTracker _chances = new ChancesTracker(); - - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => - (card.Id == HearthDb.CardIds.Collectible.Neutral.Rally && !RallyView.AlwaysSeparate) || - card.Id == HearthDb.CardIds.Collectible.Priest.RaiseDead || - card.Id == HearthDb.CardIds.Collectible.Priest.Psychopomp || - card.Id == HearthDb.CardIds.Collectible.Neutral.BodyWrapper || - card.Id == HearthDb.CardIds.Collectible.Priest.MassResurrection || - card.Id == HearthDb.CardIds.Collectible.Priest.CatrinaMuerte || - card.Id == HearthDb.CardIds.Collectible.Priest.Resurrect || - card.Id == HearthDb.CardIds.Collectible.Priest.OnyxBishop || - card.Id == HearthDb.CardIds.Collectible.Priest.EternalServitude || - card.Id == HearthDb.CardIds.Collectible.Priest.LesserDiamondSpellstone || - (Settings.Default.ResurrectKazakus && card.Id == HearthDb.CardIds.Collectible.Neutral.Kazakus) - ) > -1; - } - - public ResurrectView() - { - // Section Label - Label.Text = Strings.GetLocalized("Resurrect"); - } - - public bool Update(Card card) - { - if (!base.Update(card)) - { - return false; - } - - _chances.Update(card, Cards, View); - - return true; + get => _Config ?? (_Config = new ViewConfig( + Warlock.HabeasCorpses, + Priest.RaiseDead, + Priest.Psychopomp, + Neutral.BodyWrapper, + Priest.MassResurrection, + Priest.Resurrect, + Priest.OnyxBishopKARA, + Priest.EternalServitude, + Priest.LesserDiamondSpellstone, + Neutral.Kazakus) + { + Name = "Resurrect", + Enabled = "ResurrectEnabled", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.Type == "Minion", + }); } } } diff --git a/Graveyard/SI7View.cs b/Graveyard/SI7View.cs new file mode 100644 index 0000000..a765fd1 --- /dev/null +++ b/Graveyard/SI7View.cs @@ -0,0 +1,23 @@ +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; + +namespace HDT.Plugins.Graveyard +{ + internal class SI7View + { + private static ViewConfig _Config; + internal static ViewConfig Config + { + get => _Config ?? (_Config = new ViewConfig( + Rogue.Si7Assassin, + Rogue.Si7Informant, + Rogue.Si7Smuggler) + { + Name = "SI7", + CreateView = () => new NormalView(), + UpdateOn = GameEvents.OnPlayerPlay, + Condition = card => card.Name.StartsWith("SI:7"), + }); + } + } +} diff --git a/Graveyard/SaurfangView.cs b/Graveyard/SaurfangView.cs index b5787fb..6a6d928 100644 --- a/Graveyard/SaurfangView.cs +++ b/Graveyard/SaurfangView.cs @@ -1,33 +1,21 @@ -using Hearthstone_Deck_Tracker; -using Hearthstone_Deck_Tracker.Hearthstone; -using System.Linq; +using Hearthstone_Deck_Tracker.API; +using static HearthDb.CardIds.Collectible; namespace HDT.Plugins.Graveyard { - public class SaurfangView : NormalView + public class SaurfangView { - private ChancesTracker _chances = new ChancesTracker(); - - public static bool isValid() + private static ViewConfig _Config; + internal static ViewConfig Config { - return Core.Game.Player.PlayerCardList.FindIndex(card => card.Id == HearthDb.CardIds.Collectible.Warrior.OverlordSaurfang) > -1; - } - - public SaurfangView() - { - Label.Text = Strings.GetLocalized("Saurfang"); - } - - public bool Update(Card card) - { - if (!((card.Text?.Contains("Frenzy:") ?? false) && base.Update(card))) - { - return false; - } - - _chances.Update(card, Cards, View); - - return true; + get => _Config ?? (_Config = new ViewConfig(Warrior.OverlordSaurfang) + { + Name = "Saurfang", + Enabled = "SaurfangEnabled", + CreateView = () => new ChancesView(), + UpdateOn = GameEvents.OnPlayerPlayToGraveyard, + Condition = card => card.EnglishText?.Contains("Frenzy:") ?? false + }); } } } diff --git a/Graveyard/Settings.Default.cs b/Graveyard/Settings.Default.cs new file mode 100644 index 0000000..6f7d798 --- /dev/null +++ b/Graveyard/Settings.Default.cs @@ -0,0 +1,42 @@ +using System; +using System.Windows.Controls; + +namespace HDT.Plugins.Graveyard +{ + public sealed partial class Settings + { + private int GetDefaultIntValue(string key) => int.Parse(Properties[key].DefaultValue.ToString()); + private double GetDefaultDblValue(string key) => double.Parse(Properties[key].DefaultValue.ToString()); + public double DefaultPlayerLeft => GetDefaultDblValue(nameof(PlayerLeft)); + public double DefaultPlayerTop => GetDefaultDblValue(nameof(PlayerTop)); + public double DefaultEnemyLeft => GetDefaultDblValue(nameof(EnemyLeft)); + public double DefaultEnemyTop => GetDefaultDblValue(nameof(EnemyTop)); + public double DefaultFriendlyOpacity => GetDefaultDblValue(nameof(FriendlyOpacity)); + public double DefaultFriendlyScale => GetDefaultDblValue(nameof(FriendlyScale)); + public Orientation DefaultFriendlyOrientation => (Orientation)Enum.Parse(typeof(Orientation), Properties[nameof(FriendlyOrientation)].DefaultValue.ToString()); + public double DefaultEnemyOpacity => GetDefaultDblValue(nameof(EnemyOpacity)); + public double DefaultEnemyScale => GetDefaultDblValue(nameof(EnemyScale)); + + public void ResetPlayerPosition() + { + PlayerLeft = DefaultPlayerLeft; + PlayerTop = DefaultPlayerTop; + } + public void ResetOpponentPosition() + { + EnemyLeft = DefaultEnemyLeft; + EnemyTop = DefaultEnemyTop; + } + public void ResetPlayerDisplay() + { + FriendlyOpacity = DefaultFriendlyOpacity; + FriendlyScale = DefaultFriendlyScale; + FriendlyOrientation = DefaultFriendlyOrientation; + } + public void ResetOpponentDisplay() + { + EnemyOpacity = DefaultEnemyOpacity; + EnemyScale = DefaultEnemyScale; + } + } +} diff --git a/Graveyard/Settings.Designer.cs b/Graveyard/Settings.Designer.cs index 3ad37ad..16b268d 100644 --- a/Graveyard/Settings.Designer.cs +++ b/Graveyard/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace HDT.Plugins.Graveyard { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -430,5 +430,257 @@ public bool SaurfangEnabled { this["SaurfangEnabled"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool YShaarjEnabled { + get { + return ((bool)(this["YShaarjEnabled"])); + } + set { + this["YShaarjEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool ElwynnBoarEnabled { + get { + return ((bool)(this["ElwynnBoarEnabled"])); + } + set { + this["ElwynnBoarEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool AlwaysBoarSeparately { + get { + return ((bool)(this["AlwaysBoarSeparately"])); + } + set { + this["AlwaysBoarSeparately"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool KargalEnabled { + get { + return ((bool)(this["KargalEnabled"])); + } + set { + this["KargalEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool AntonidasEnabled { + get { + return ((bool)(this["AntonidasEnabled"])); + } + set { + this["AntonidasEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool GrandFinaleEnabled { + get { + return ((bool)(this["GrandFinaleEnabled"])); + } + set { + this["GrandFinaleEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool LastPlayedEnabled { + get { + return ((bool)(this["LastPlayedEnabled"])); + } + set { + this["LastPlayedEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool FriendlyQuestlineEnabled { + get { + return ((bool)(this["FriendlyQuestlineEnabled"])); + } + set { + this["FriendlyQuestlineEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool EnemyQuestlineEnabled { + get { + return ((bool)(this["EnemyQuestlineEnabled"])); + } + set { + this["EnemyQuestlineEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool MulticasterEnabled { + get { + return ((bool)(this["MulticasterEnabled"])); + } + set { + this["MulticasterEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool ShirvallahEnabled { + get { + return ((bool)(this["ShirvallahEnabled"])); + } + set { + this["ShirvallahEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ExcludedCards { + get { + return ((string)(this["ExcludedCards"])); + } + set { + this["ExcludedCards"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1.9.5")] + public string Version { + get { + return ((string)(this["Version"])); + } + set { + this["Version"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("https://github.com/RedHatter/Graveyard")] + public string GitHubProjectUrl { + get { + return ((string)(this["GitHubProjectUrl"])); + } + set { + this["GitHubProjectUrl"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool OpponentDeathrattleEnabled { + get { + return ((bool)(this["OpponentDeathrattleEnabled"])); + } + set { + this["OpponentDeathrattleEnabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("95")] + public double MaxTop { + get { + return ((double)(this["MaxTop"])); + } + set { + this["MaxTop"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("95")] + public double MaxLeft { + get { + return ((double)(this["MaxLeft"])); + } + set { + this["MaxLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-14")] + public double TyrViewLeft { + get { + return ((double)(this["TyrViewLeft"])); + } + set { + this["TyrViewLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-42")] + public double ChancesViewLeft { + get { + return ((double)(this["ChancesViewLeft"])); + } + set { + this["ChancesViewLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Collapsed")] + public global::System.Windows.Visibility QuestlineVisibility { + get { + return ((global::System.Windows.Visibility)(this["QuestlineVisibility"])); + } + set { + this["QuestlineVisibility"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool CardsEnabled { + get { + return ((bool)(this["CardsEnabled"])); + } + set { + this["CardsEnabled"] = value; + } + } } } diff --git a/Graveyard/Settings.Upgrade.cs b/Graveyard/Settings.Upgrade.cs new file mode 100644 index 0000000..0e72215 --- /dev/null +++ b/Graveyard/Settings.Upgrade.cs @@ -0,0 +1,32 @@ +using HDT.Plugins.Graveyard.SettingsUpgrades; +using MahApps.Metro.Controls; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace HDT.Plugins.Graveyard +{ + public sealed partial class Settings + { + internal static IEnumerable Upgrades = new List() + { + new SettingsUpgradev0110(), + new SettingsUpgradev0111(), + new SettingsUpgradev0112(), + }; + + public override void Upgrade() + { + foreach (var upgrade in Upgrades.OrderBy(u => u.Version)) + { + if (upgrade.Version.CompareTo(new Version(Version)) > 0) + { + if (upgrade.Upgrade(this)) + Version = upgrade.Version.ToString(); + else + break; + } + } + } + } +} diff --git a/Graveyard/Settings.cs b/Graveyard/Settings.cs index fb02543..35bd291 100644 --- a/Graveyard/Settings.cs +++ b/Graveyard/Settings.cs @@ -1,12 +1,12 @@ -using Hearthstone_Deck_Tracker; +using HDT.Plugins.Graveyard.Properties; +using Hearthstone_Deck_Tracker; using Hearthstone_Deck_Tracker.Utility.Logging; using System; using System.Collections.Generic; using System.Configuration; +using System.Globalization; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace HDT.Plugins.Graveyard { @@ -24,55 +24,100 @@ public Setting(string name, string value) public sealed partial class Settings { - private const string Filename = "Graveyard.xml"; - internal static string DataDir => Config.Instance.DataDir; + private static class Old + { + internal static readonly string Filename = Path.ChangeExtension(LibraryInfo.Name, ".xml"); + internal static string DataDir => Config.Instance.DataDir; + internal static string SettingsPath => Path.Combine(DataDir, Filename); + } + + private static readonly string Filename = Path.ChangeExtension(nameof(Settings), ".xml"); + internal static string DataDir => Path.Combine(Config.Instance.DataDir, $"Plugin.{LibraryInfo.Name}"); private static string SettingsPath => Path.Combine(DataDir, Filename); + public bool HasChanges { get; private set; } = false; public Settings() { var provider = Providers; SettingsLoaded += SettingsLoadedEventHandler; SettingsSaving += SettingsSavingEventHandler; - } + } private void SettingsLoadedEventHandler(object sender, System.Configuration.SettingsLoadedEventArgs e) { try { + Log.Debug($"Loading {SettingsPath}"); + + if (!Directory.Exists(DataDir)) + Directory.CreateDirectory(DataDir); + + try + { + if (!File.Exists(SettingsPath) || File.Exists(Old.SettingsPath)) + { + File.Move(Old.SettingsPath, SettingsPath); + } + } + catch (Exception ex) { Log.Error(ex); } + if (File.Exists(SettingsPath)) { var actual = XmlManager>.Load(SettingsPath); foreach (var setting in actual) { - this[setting.Name] = Convert.ChangeType(setting.Value, this.Properties[setting.Name].PropertyType); + try + { + if (Properties[setting.Name].PropertyType.IsEnum) + this[setting.Name] = Enum.Parse(Properties[setting.Name].PropertyType, setting.Value); + else + this[setting.Name] = Convert.ChangeType(setting.Value, Properties[setting.Name].PropertyType, CultureInfo.InvariantCulture); + } + catch (Exception ex) + { + Log.Warn($"{setting.Name} loading error"); + Log.Error(ex); + } } + Log.Info($"{SettingsPath} loaded"); + } + else + { + Log.Warn($"{SettingsPath} does not exist"); } } catch (Exception ex) { Log.Error(ex); } + PropertyChanged += (s, a) => HasChanges = true; + Log.Info($"Watching {SettingsPath} changes"); } private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + Log.Debug($"Saving {SettingsPath}"); try { var saveFormat = PropertyValues.Cast() - .Where(p => p.SerializedValue.ToString() != p.Property.DefaultValue.ToString()) - .Select(p => new Setting(p.Name, p.SerializedValue.ToString())) + .Where(p => !p.SerializedValue.Equals(p.Property.DefaultValue)) + .Select(p => new Setting(p.Name, string.Format(CultureInfo.InvariantCulture, "{0}", p.SerializedValue))) .ToList(); XmlManager>.Save(SettingsPath, saveFormat); + HasChanges = false; + e.Cancel = true; + + Log.Info($"{SettingsPath} saved"); } catch (Exception ex) { Log.Error(ex); } - } + } } } diff --git a/Graveyard/Settings.settings b/Graveyard/Settings.settings index cb87235..996f049 100644 --- a/Graveyard/Settings.settings +++ b/Graveyard/Settings.settings @@ -104,5 +104,68 @@ True + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + True + + + True + + + + + + 1.9.5 + + + https://github.com/RedHatter/Graveyard + + + True + + + 95 + + + 95 + + + -14 + + + -42 + + + Collapsed + + + True + \ No newline at end of file diff --git a/Graveyard/SettingsCard.cs b/Graveyard/SettingsCard.cs new file mode 100644 index 0000000..2b589e6 --- /dev/null +++ b/Graveyard/SettingsCard.cs @@ -0,0 +1,56 @@ +using Hearthstone_Deck_Tracker.Hearthstone; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace HDT.Plugins.Graveyard +{ + internal class SettingsCard : INotifyPropertyChanged + { + public ViewConfigCard Config { get; private set; } + public Card Card => Config.Card; + public bool IsEnabled + { + get => _IsEnabled; + set => SetProperty(ref _IsEnabled, value, onChanged: () => + { + Config.IsEnabled = value; + ViewConfigCards.Instance.Toggle(Config); + }); + } + private bool _IsEnabled; + public string CardClass => Card == null ? string.Empty : Card.GetPlayerClass; + + public SettingsCard(ViewConfigCard config) + { + Config = config; + _IsEnabled = config.IsEnabled; + } + + #region INotifyPropertyChanged + protected bool SetProperty(ref T backingStore, T value, + [CallerMemberName] string propertyName = "", + Action onChanged = null) + { + if (EqualityComparer.Default.Equals(backingStore, value)) + return false; + + backingStore = value; + onChanged?.Invoke(); + OnPropertyChanged(propertyName); + return true; + } + + public event PropertyChangedEventHandler PropertyChanged; + protected void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + var changed = PropertyChanged; + if (changed == null) + return; + + changed.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + #endregion + } +} diff --git a/Graveyard/SettingsUpgrades/ISettingsUpgrade.cs b/Graveyard/SettingsUpgrades/ISettingsUpgrade.cs new file mode 100644 index 0000000..f0a5a76 --- /dev/null +++ b/Graveyard/SettingsUpgrades/ISettingsUpgrade.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HDT.Plugins.Graveyard.SettingsUpgrades +{ + internal interface ISettingsUpgrade + { + Version Version { get; } + + bool Upgrade(Settings settings); + } +} diff --git a/Graveyard/SettingsUpgrades/SettingsUpgradev0110.cs b/Graveyard/SettingsUpgrades/SettingsUpgradev0110.cs new file mode 100644 index 0000000..b8a5b64 --- /dev/null +++ b/Graveyard/SettingsUpgrades/SettingsUpgradev0110.cs @@ -0,0 +1,83 @@ +using Hearthstone_Deck_Tracker.Utility.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HDT.Plugins.Graveyard.SettingsUpgrades +{ + internal class SettingsUpgradev0110 : ISettingsUpgrade + { + private Settings Settings; + + public Version Version { get; } = new Version(1, 10); + + public bool Upgrade(Settings settings) + { + Settings = settings; + + if (Version.CompareTo(new Version(Settings.Version)) <= 0) return false; + + // Process config list for disabled settings and convert to excluded cards + // Exclude "Last Played" views to process separately + foreach (var config in Plugin.Graveyard.ConfigList + .Where(c => !string.IsNullOrEmpty(c.Enabled) + && !(bool)Settings[c.Enabled] + && c.Enabled != "LastPlayedEnabled" + && c.ShowOnCards != null + && c.ShowOnCards.Count() <= 2)) + { + TryUpdateConfig(config, true); + } + + // "Last Played" cards all use single LastPlayedEnabled setting + var lastPlayedConfigs = Plugin.Graveyard.ConfigList + .Where(c => c.Enabled == "LastPlayedEnabled" + && !(bool)Settings[c.Enabled]) + .ToList(); + for (int i = 0; i < lastPlayedConfigs.Count(); i++) + { + var config = lastPlayedConfigs[i]; + TryUpdateConfig(config, i == lastPlayedConfigs.Count() - 1); + } + + // Kazakus has his own ResurrectKazakus setting + if (!Settings.ResurrectKazakus) + { + var kazakusConfig = ResurrectView.Config.ShowOnCards + .Where(c => c.CardId == HearthDb.CardIds.Collectible.Neutral.Kazakus) + .FirstOrDefault(); + if (kazakusConfig != null) + { + kazakusConfig.IsEnabled = false; + ViewConfigCards.Instance.Toggle(kazakusConfig); + } + Settings.ResurrectKazakus = true; + } + + return true; + } + + bool TryUpdateConfig(ViewConfig config, bool updateSetting) + { + try + { + foreach (var configCard in config.ShowOnCards) + { + configCard.IsEnabled = false; + ViewConfigCards.Instance.Toggle(configCard); + } + if (updateSetting) Settings[config.Enabled] = true; + Log.Info($"Upgraded {config.Name} setting to {Version}"); + return true; + } + catch (Exception ex) + { + Log.Warn($"Upgrade {config.Name} setting to {Version} failed"); + Log.Error(ex); + return false; + } + } + } +} diff --git a/Graveyard/SettingsUpgrades/SettingsUpgradev0111.cs b/Graveyard/SettingsUpgrades/SettingsUpgradev0111.cs new file mode 100644 index 0000000..96f82f0 --- /dev/null +++ b/Graveyard/SettingsUpgrades/SettingsUpgradev0111.cs @@ -0,0 +1,44 @@ +using Hearthstone_Deck_Tracker.Utility.Logging; +using System; +using System.Windows; + +namespace HDT.Plugins.Graveyard.SettingsUpgrades +{ + internal class SettingsUpgradev0111 : ISettingsUpgrade + { + private Settings Settings; + + public Version Version { get; } = new Version(1, 11); + + public bool Upgrade(Settings settings) + { + Settings = settings; + + if (Version.CompareTo(new Version(Settings.Version)) <= 0) return false; + + return TryUpdateConfig(nameof(Settings.PlayerLeft), SystemParameters.PrimaryScreenWidth) + & TryUpdateConfig(nameof(Settings.PlayerTop), SystemParameters.PrimaryScreenHeight) + & TryUpdateConfig(nameof(Settings.EnemyLeft), SystemParameters.PrimaryScreenWidth) + & TryUpdateConfig(nameof(Settings.EnemyTop), SystemParameters.PrimaryScreenHeight); + } + + bool TryUpdateConfig(string setting, double size, bool updateSetting = true) + { + try + { + double position = (double)Settings[setting]; + double percentage = position.PixelsToPercentage(size); + if (updateSetting) Settings[setting] = percentage; + Log.Info($"Upgraded {setting} setting to {Version}"); + Log.Info($"value {position}px->{percentage:#,##0}% for size {size}"); + return updateSetting; + } + catch (Exception ex) + { + Log.Warn($"Upgrade {setting} setting to {Version} failed"); + Log.Error(ex); + return false; + } + } + } +} diff --git a/Graveyard/SettingsUpgrades/SettingsUpgradev0112.cs b/Graveyard/SettingsUpgrades/SettingsUpgradev0112.cs new file mode 100644 index 0000000..489e9bc --- /dev/null +++ b/Graveyard/SettingsUpgrades/SettingsUpgradev0112.cs @@ -0,0 +1,38 @@ +using Hearthstone_Deck_Tracker.Utility.Logging; +using System; + +namespace HDT.Plugins.Graveyard.SettingsUpgrades +{ + internal class SettingsUpgradev0112 : ISettingsUpgrade + { + private Settings Settings; + + public Version Version { get; } = new Version(1, 12); + + public bool Upgrade(Settings settings) + { + Settings = settings; + + if (Version.CompareTo(new Version(Settings.Version)) <= 0) return false; + + return TryUpdateConfig(nameof(Settings.FriendlyQuestlineEnabled)) + & TryUpdateConfig(nameof(Settings.EnemyQuestlineEnabled)); + } + + bool TryUpdateConfig(string setting, bool value = false, bool updateSetting = true) + { + try + { + if (updateSetting) Settings[setting] = value; + Log.Info($"Upgraded {setting} setting to {Version}"); + return updateSetting; + } + catch (Exception ex) + { + Log.Warn($"Upgrade {setting} setting to {Version} failed"); + Log.Error(ex); + return false; + } + } + } +} diff --git a/Graveyard/SettingsView.xaml b/Graveyard/SettingsView.xaml index 3680c06..929cf29 100644 --- a/Graveyard/SettingsView.xaml +++ b/Graveyard/SettingsView.xaml @@ -1,219 +1,456 @@ - - - + + + + + + + + + + - - - - - - + + + + + - -