Skip to content

Commit

Permalink
feat: Show related cards (#41)
Browse files Browse the repository at this point in the history
* wip: fizzle snapshot tracking

* wip: add link between copied card and the original

* wip: work for multiple snapshots

* wip: player's tidepool pupil tracking

* wip: display related cards on overlay and hydration station

* wip: show related cards grid

* wip: show related cards when hovering in hand

* wip: scale grid depending on maxHeight

- useful because when hovering cards in hand we need to make the grid smaller so the highlighted card doesnt cover it

* wip: hover on hand work for deck on the left

* fix: tidepool pupil considering non spell cards

* fix: display card grid position when scaling window

* fix: clear related cards when card is shuffled into the deck

* feat: show related cards on hover in hand

* wip: add related cards label

* wip: add cards implementations

* fix: add zindex to cardgridtooltip

* fix: grid positioning

* feat: add pet parrot

* chore: add related cards tooltip to the configs

* refactor: create functions to set related cards tooltip

Also:
- adjusts indentation
- adds comment for fizzle snapshot

* fix: update VMActionTests

* feat: update tyr to only show unique cards

* feat: localize tooltip label

* refactor: move GetRelatedCards to a new class

* refactor: create RelatedCardsSystem

* refactor: create CardUtils

* feat: implement cards with related cards

* refactor: delete old related cards handler

* fix: stop showing card on related cards box when it is already on player decklist

* fix: tooltip on deckLens when the deck is big

* fix: update MockGame

* fix: use localized string

* fix: Tyrs Tears normal and forged cardIds

* fix: hover on hand for entity related cards

* feat: add Product9

* feat: add Lady Liadrin

* feat: add Shudderwock

* fix: multiple entity related cards in hand
  • Loading branch information
mateuscechetto authored Oct 23, 2024
1 parent c11570e commit ebec50c
Show file tree
Hide file tree
Showing 49 changed files with 948 additions and 67 deletions.
2 changes: 2 additions & 0 deletions HDTTests/Hearthstone/Secrets/MockGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Hearthstone_Deck_Tracker.Hearthstone;
using Hearthstone_Deck_Tracker.Hearthstone.CounterSystem;
using Hearthstone_Deck_Tracker.Hearthstone.Entities;
using Hearthstone_Deck_Tracker.Hearthstone.RelatedCardsSystem;
using Hearthstone_Deck_Tracker.Hearthstone.Secrets;
using Hearthstone_Deck_Tracker.Stats;
using Card = Hearthstone_Deck_Tracker.Hearthstone.Card;
Expand All @@ -28,6 +29,7 @@ public MockGame()
public Entity PlayerEntity { get; set; }
public Entity OpponentEntity { get; set; }
public CounterManager CounterManager { get; set; }
public RelatedCardsManager RelatedCardsManager { get; set; }
public bool IsMulliganDone { get; set; }
public bool IsInMenu { get; set; }
public bool IsUsingPremade { get; set; }
Expand Down
4 changes: 3 additions & 1 deletion HDTTests/Utility/ValueMoments/Actions/VMActionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ public void VMAction_MixpanelPayloadReturnsCorrect()
"player_wotog_counters",
"opponent_wotog_counters",
"player_counters",
"opponent_counters"
"opponent_counters",
"player_related_cards",
"opponent_related_cards"
}},
{ "hdt_general_settings_disabled", new []{
"overlay_hide_completely",
Expand Down
6 changes: 6 additions & 0 deletions Hearthstone Deck Tracker/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,9 @@ public class Config
[DefaultValue(false)]
public bool HideOpponentCounters = false;

[DefaultValue(false)]
public bool HideOpponentRelatedCards = false;

[DefaultValue(DisplayMode.Auto)]
public DisplayMode OpponentCthunCounter = DisplayMode.Auto;

Expand Down Expand Up @@ -467,6 +470,9 @@ public class Config
[DefaultValue(false)]
public bool HidePlayerCounters = false;

[DefaultValue(false)]
public bool HidePlayerRelatedCards = false;

[DefaultValue(true)]
public bool DisablePlayerWotogs = true;

Expand Down
27 changes: 23 additions & 4 deletions Hearthstone Deck Tracker/Controls/GridCardImages.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,20 @@ public GridCardImages()
}
private IEnumerable<Hearthstone.Card>? _previousCards;
Storyboard? ExpandAnimation => FindResource("AnimateGrid") as Storyboard;
public async void SetCardIdsFromCards(IEnumerable<Hearthstone.Card>? cards)
public async void SetCardIdsFromCards(IEnumerable<Hearthstone.Card>? cards, int? maxGridHeight = null)
{
if (cards == null || (_previousCards != null && _previousCards.SequenceEqual(cards)))
if(cards == null || (_previousCards != null && _previousCards.SequenceEqual(cards)))
{
if((maxGridHeight.HasValue && maxGridHeight != _maxGridHeight)
|| (!maxGridHeight.HasValue && GridHeight != _maxGridHeight))
{
_maxGridHeight = maxGridHeight ?? GridHeight;
CardsCollectionChanged(_maxGridHeight);
ExpandAnimation?.Begin();
}

return;
}

_previousCards = cards;

Expand Down Expand Up @@ -122,7 +132,7 @@ public async void SetCardIdsFromCards(IEnumerable<Hearthstone.Card>? cards)
Cards.Add(cardWithImage);
}

CardsCollectionChanged();
CardsCollectionChanged(maxGridHeight);

// Clear the loading image source after all cards are processed
LoadingImageSource = null;
Expand All @@ -132,7 +142,7 @@ public async void SetCardIdsFromCards(IEnumerable<Hearthstone.Card>? cards)
ExpandAnimation?.Begin();
}

private void CardsCollectionChanged()
private void CardsCollectionChanged(int? maxGridHeight = null)
{
var cardCount = Cards.Count;
if (cardCount == 0)
Expand All @@ -150,6 +160,13 @@ private void CardsCollectionChanged()
else
cardHeight = (int)(cardWidth / cardRatio);

if (maxGridHeight.HasValue && cardHeight * rows > maxGridHeight.Value)
{
var scaleFactor = (double)maxGridHeight.Value / (cardHeight * rows);
cardWidth = (int)(cardWidth * scaleFactor);
cardHeight = (int)(cardHeight * scaleFactor);
}

CardWidth = Math.Min(cardWidth, (int)MaxCardWidth);
CardHeight = Math.Min(cardHeight, (int)MaxCardHeight);
}
Expand All @@ -165,6 +182,8 @@ public void SetTitle(string title)

private const double MaxCardWidth = 256 * 0.75;
private const double MaxCardHeight = 388 * 0.75;

private int _maxGridHeight = GridHeight;
public Thickness CardMargin => CalculateCardMargin();

private Thickness CalculateCardMargin()
Expand Down
3 changes: 2 additions & 1 deletion Hearthstone Deck Tracker/Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,8 @@ internal static async void UpdateOpponentCards(bool reset = false)
_updateRequestsOpponent--;
if(_updateRequestsOpponent > 0)
return;
Overlay.UpdateOpponentCards(new List<Card>(Game.Opponent.OpponentCardList), reset);
var cardWithRelatedCards = Game.RelatedCardsManager.GetCardsOpponentMayHave(Game.Opponent).ToList();
Overlay.UpdateOpponentCards(new List<Card>(Game.Opponent.OpponentCardList), cardWithRelatedCards, reset);
if(Windows.OpponentWindow.IsVisible)
Windows.OpponentWindow.UpdateOpponentCards(new List<Card>(Game.Opponent.OpponentCardList), reset);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@
Margin="10,5,0,0" VerticalAlignment="Top"
Checked="CheckBoxCounters_Checked"
Unchecked="CheckBoxCounters_Unchecked" />
<CheckBox x:Name="CheckBoxRelatedCards"
Content="{lex:LocText Options_Overlay_Player_CheckBox_Related_Cards}" HorizontalAlignment="Left"
Margin="10,5,0,0" VerticalAlignment="Top"
Checked="CheckBoxRelatedCards_Checked"
Unchecked="CheckBoxRelatedCards_Unchecked" />
<CheckBox x:Name="CheckBoxAttack"
Content="{lex:LocText Options_Overlay_Opponent_CheckBox_Attack}" HorizontalAlignment="Left"
Margin="10,5,0,0" VerticalAlignment="Top"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public void Load()
CheckBoxAttack.IsChecked = !Config.Instance.HideOpponentAttackIcon;
CheckBoxActiveEffects.IsChecked = !Config.Instance.HideOpponentActiveEffects;
CheckBoxCounters.IsChecked = !Config.Instance.HideOpponentCounters;
CheckBoxRelatedCards.IsChecked = !Config.Instance.HideOpponentRelatedCards;
CheckboxEnableWotogs.IsChecked = !Config.Instance.DisableOpponentWotogs;
ComboBoxCthun.ItemsSource = Enum.GetValues(typeof(DisplayMode)).Cast<DisplayMode>();
ComboBoxCthun.SelectedItem = Config.Instance.OpponentCthunCounter;
Expand Down Expand Up @@ -344,6 +345,22 @@ private void CheckBoxCounters_Unchecked(object sender, RoutedEventArgs e)
Config.Save();
}

private void CheckBoxRelatedCards_Checked(object sender, RoutedEventArgs e)
{
if(!_initialized)
return;
Config.Instance.HideOpponentRelatedCards = false;
Config.Save();
}

private void CheckBoxRelatedCards_Unchecked(object sender, RoutedEventArgs e)
{
if(!_initialized)
return;
Config.Instance.HideOpponentRelatedCards = true;
Config.Save();
}

private void CheckBoxWotogs_Checked(object sender, RoutedEventArgs e)
{
if(!_initialized)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@
Margin="10,5,0,0" VerticalAlignment="Top"
Checked="CheckBoxCounters_Checked"
Unchecked="CheckBoxCounters_Unchecked" />
<CheckBox x:Name="CheckBoxRelatedCards"
Content="{lex:LocText Options_Overlay_Player_CheckBox_Related_Cards}" HorizontalAlignment="Left"
Margin="10,5,0,0" VerticalAlignment="Top"
Checked="CheckBoxRelatedCards_Checked"
Unchecked="CheckBoxRelatedCards_Unchecked" />
<CheckBox x:Name="CheckBoxAttack"
Content="{lex:LocText Options_Overlay_Player_CheckBox_Attack}" HorizontalAlignment="Left"
Margin="10,5,0,0" VerticalAlignment="Top"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public void Load(GameV2 game)
CheckBoxCenterDeckVertically.IsChecked = Config.Instance.OverlayCenterPlayerStackPanel;
CheckBoxActiveEffects.IsChecked = !Config.Instance.HidePlayerActiveEffects;
CheckBoxCounters.IsChecked = !Config.Instance.HidePlayerCounters;
CheckBoxRelatedCards.IsChecked = !Config.Instance.HidePlayerRelatedCards;
CheckboxEnableWotogs.IsChecked = !Config.Instance.DisablePlayerWotogs;
CheckBoxAttack.IsChecked = !Config.Instance.HidePlayerAttackIcon;
ComboBoxCthun.ItemsSource = Enum.GetValues(typeof(DisplayMode)).Cast<DisplayMode>();
Expand Down Expand Up @@ -359,6 +360,22 @@ private void CheckBoxCounters_Unchecked(object sender, RoutedEventArgs e)
Config.Save();
}

private void CheckBoxRelatedCards_Checked(object sender, RoutedEventArgs e)
{
if(!_initialized)
return;
Config.Instance.HidePlayerRelatedCards = false;
Config.Save();
}

private void CheckBoxRelatedCards_Unchecked(object sender, RoutedEventArgs e)
{
if(!_initialized)
return;
Config.Instance.HidePlayerRelatedCards = true;
Config.Save();
}

private void CheckBoxWotogs_Checked(object sender, RoutedEventArgs e)
{
if(!_initialized)
Expand Down
10 changes: 9 additions & 1 deletion Hearthstone Deck Tracker/GameEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1747,6 +1747,13 @@ public void HandlePlayerSecretPlayed(Entity entity, string cardId, int turn, Zon
}
}

public void HandlePlayerSecretTrigger(Entity entity, string? cardId, int turn, int otherId)
{
if (!entity.IsSecret)
return;
_game.Player.SecretTriggered(entity, turn);
}

public void HandlePlayerHandDiscard(Entity entity, string cardId, int turn)
{
if(string.IsNullOrEmpty(cardId))
Expand Down Expand Up @@ -2196,7 +2203,7 @@ public void HandleOpponentSecretTrigger(Entity entity, string? cardId, int turn,
{
if (!entity.IsSecret)
return;
_game.Opponent.SecretTriggered(entity, turn);
_game.Opponent.OpponentSecretTriggered(entity, turn);
_game.SecretsManager.RemoveSecret(entity);
Core.UpdateOpponentCards();
var card = Database.GetCardFromId(cardId);
Expand Down Expand Up @@ -2254,6 +2261,7 @@ public void HandleQuestRewardDatabaseId(int id, int value)
void IGameHandler.HandlePlayerDraw(Entity entity, string cardId, int turn) => HandlePlayerDraw(entity, cardId, turn);
void IGameHandler.HandlePlayerMulligan(Entity entity, string cardId) => HandlePlayerMulligan(entity, cardId);
void IGameHandler.HandlePlayerSecretPlayed(Entity entity, string cardId, int turn, Zone fromZone, string parentBlockCardId) => HandlePlayerSecretPlayed(entity, cardId, turn, fromZone, parentBlockCardId);
void IGameHandler.HandlePlayerSecretTrigger(Entity entity, string? cardId, int turn, int otherId) => HandlePlayerSecretTrigger(entity, cardId, turn, otherId);
void IGameHandler.HandlePlayerHandDiscard(Entity entity, string cardId, int turn) => HandlePlayerHandDiscard(entity, cardId, turn);
void IGameHandler.HandlePlayerPlay(Entity entity, string cardId, int turn, string parentBlockCardId) => HandlePlayerPlay(entity, cardId, turn, parentBlockCardId);
void IGameHandler.HandlePlayerDeckDiscard(Entity entity, string cardId, int turn) => HandlePlayerDeckDiscard(entity, cardId, turn);
Expand Down
44 changes: 44 additions & 0 deletions Hearthstone Deck Tracker/Hearthstone/CardUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Linq;
using HearthDb.Enums;
using Hearthstone_Deck_Tracker.Enums;

namespace Hearthstone_Deck_Tracker.Hearthstone;

public static class CardUtils
{
public static IEnumerable<Card?> FilterCardsByFormat(this IEnumerable<Card?> cards, Format? format)
{
return cards.Where(card => IsCardFromFormat(card, format));
}

public static bool IsCardFromFormat(Card? card, Format? format)
{
return format switch
{
Format.Classic => card != null && Helper.ClassicOnlySets.Contains(card.Set),
Format.Wild => card != null && !Helper.ClassicOnlySets.Contains(card.Set),
Format.Standard => card != null && !Helper.WildOnlySets.Contains(card.Set) && !Helper.ClassicOnlySets.Contains(card.Set),
Format.Twist => card != null && Helper.TwistSets.Contains(card.Set),
_ => true
};
}

public static IEnumerable<Card?> FilterCardsByPlayerClass(this IEnumerable<Card?> cards, string? playerClass, bool ignoreNeutral = false)
{
return cards.Where(card => IsCardFromPlayerClass(card, playerClass, ignoreNeutral));
}

public static bool IsCardFromPlayerClass(Card? card, string? playerClass, bool ignoreNeutral = false)
{
return card != null &&
(card.PlayerClass == playerClass || card.GetTouristVisitClass() == playerClass ||
(!ignoreNeutral && card.CardClass == CardClass.NEUTRAL));
}

public static bool MayCardBeRelevant(Card? card, Format? format, string? playerClass,
bool ignoreNeutral = false)
{
return IsCardFromFormat(card, format) && IsCardFromPlayerClass(card, playerClass, ignoreNeutral);
}
}
43 changes: 5 additions & 38 deletions Hearthstone Deck Tracker/Hearthstone/CounterSystem/BaseCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,47 +71,14 @@ protected List<string> GetCardsInDeckOrKnown(string[] cardIds)

protected string[] FilterCardsByClassAndFormat(string[] cardIds, string? playerClass, bool ignoreNeutral = false)
{
var filteredByFormat = FilterCardsByFormat(cardIds);

var cardsToDisplay = filteredByFormat
return cardIds
.Select(Database.GetCardFromId)
.Where(card =>
card != null &&
(card.PlayerClass == playerClass || (card.GetTouristVisitClass() == playerClass) ||
!ignoreNeutral && card.CardClass == CardClass.NEUTRAL))
.Select(card => card!.Id).ToArray();

return cardsToDisplay;
.FilterCardsByFormat(Game.CurrentFormat)!
.FilterCardsByPlayerClass(playerClass, ignoreNeutral)
.Select(card => card!.Id)
.ToArray();
}

private string[] FilterCardsByFormat(string[] cardIds)
{
switch(Game.CurrentFormat)
{
case Format.Classic:
return cardIds
.Select(Database.GetCardFromId)
.Where(card => card != null && Helper.ClassicOnlySets.Contains(card.Set))
.Select(card => card!.Id).ToArray();
case Format.Wild:
return cardIds
.Select(Database.GetCardFromId)
.Where(card => card != null && !Helper.ClassicOnlySets.Contains(card.Set))
.Select(card => card!.Id).ToArray();
case Format.Standard:
return cardIds
.Select(Database.GetCardFromId)
.Where(card => card != null && !Helper.WildOnlySets.Contains(card.Set) && !Helper.ClassicOnlySets.Contains(card.Set))
.Select(card => card!.Id).ToArray();
case Format.Twist:
return cardIds
.Select(Database.GetCardFromId)
.Where(card => card != null && Helper.TwistSets.Contains(card.Set))
.Select(card => card!.Id).ToArray();
default:
return cardIds;
}
}

public event EventHandler? CounterChanged;

Expand Down
6 changes: 5 additions & 1 deletion Hearthstone Deck Tracker/Hearthstone/Entities/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ public EntityInfo CloneWithNewEntity(Entity entity)
GuessedCardState = GuessedCardState,
LatestCardId = LatestCardId,
StoredCardIds = StoredCardIds,
DeckIndex = DeckIndex
DeckIndex = DeckIndex,
CopyOfCardId = CopyOfCardId
};
}

Expand Down Expand Up @@ -355,6 +356,7 @@ public int GetCreatorId()
public bool? OriginalEntityWasCreated { get; internal set; }
public GuessedCardState GuessedCardState { get; set; } = GuessedCardState.None;
public List<string> StoredCardIds { get; set; } = new List<string>();
public string? CopyOfCardId { get; set; }
public int DeckIndex { get; set; }
public bool InGraveardAtStartOfGame { get; set; }

Expand Down Expand Up @@ -400,6 +402,8 @@ public override string ToString()
sb.Append(", deckIndex=" + DeckIndex);
if(Forged)
sb.Append(", forged=true");
if(CopyOfCardId != null)
sb.Append(", copyOf=" + CopyOfCardId);
return sb.ToString();
}
}
Expand Down
4 changes: 4 additions & 0 deletions Hearthstone Deck Tracker/Hearthstone/GameV2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Hearthstone_Deck_Tracker.Hearthstone.CounterSystem;
using Hearthstone_Deck_Tracker.Hearthstone.EffectSystem;
using Hearthstone_Deck_Tracker.Hearthstone.Entities;
using Hearthstone_Deck_Tracker.Hearthstone.RelatedCardsSystem;
using Hearthstone_Deck_Tracker.Hearthstone.Secrets;
using Hearthstone_Deck_Tracker.HsReplay;
using Hearthstone_Deck_Tracker.Live;
Expand Down Expand Up @@ -56,6 +57,7 @@ public class GameV2 : IGame
public GameMetrics Metrics { get; private set; } = new();
public ActiveEffects ActiveEffects { get; }
public CounterManager CounterManager { get; }
public RelatedCardsManager RelatedCardsManager { get; }
public GameV2()
{
Player = new Player(this, true);
Expand All @@ -64,6 +66,7 @@ public GameV2()
SecretsManager = new SecretsManager(this, new RemoteArenaSettings());
ActiveEffects = new ActiveEffects();
CounterManager = new CounterManager(this);
RelatedCardsManager = new RelatedCardsManager();
_battlegroundsBoardState = new BattlegroundsBoardState(this);
_battlegroundsHeroLatestTavernUpTurn = new Dictionary<int, Dictionary<int, int>>();
_battlegroundsHeroTriplesByTier = new Dictionary<int, Dictionary<int, int>>();
Expand Down Expand Up @@ -367,6 +370,7 @@ public void Reset(bool resetStats = true)
Player.Reset();
Opponent.Reset();
ActiveEffects.Reset();
RelatedCardsManager.Reset();
if(!_matchInfoCacheInvalid && MatchInfo?.LocalPlayer != null && MatchInfo.OpposingPlayer != null)
UpdatePlayers(MatchInfo);
ProposedAttacker = 0;
Expand Down
Loading

0 comments on commit ebec50c

Please sign in to comment.