hankerspace
New Member
- Joined
- Apr 4, 2014
- Messages
- 164
- Reaction score
- 3
API released
Here is a little guide to explain how to make you custom deck.
First of all, current API is considered as a "custom deck". User custom deck will be used only once INGAME and during player turn.
In other terms, the entry point of a custom deck (SelectCard) is called once our turn.
You custom deck will be developped in c#. I advise you to use Visual Studio IDE or Mono IDE to help you during developpement. Custom decks sourcefiles must be added in "CustomDecks" folder (near hearthbuddy.exe). Create a subfolder for each different custom deck.
You custom class must inherit from ICustomDeck interface. You must implement "IEnumerator SelectCard()" function.
I strongly advise you to script a custom deck for only 30 card. Describe behaviors for these 30 cards and provide cards needed for your custom deck.
You can, ofcourse, split you custom deck between multiple .cs files. Since your files are in the same namespace, you can access to your namespace members. I Strongly advise you to split your helpers function, custom card behavior, and main function is different files.
Cards definitions (Ids) : View attachment CardDefs.txt
Errors
Scripts compilation is done during Bot startup. Compilation errors are displayed in log window. If your custom deck is avaible, it will be avaible in 'custom deck" list. Else you deck contains errors. Check log window.
Custom deck called "." is the actual deck of Hearthbuddy's team.
If your custom deck throw an exception (trying to access to a null member, dividing by 0, etc...), a log message will be displayed begining with "Exception occured in coroutine:" statement, followed by exception description and line(s) involved in exception.
Documentation
First of all : never cache members! Never store a card in a variable for X loops. Always retrieve cards from TritonHS functions in real-time. If you want to store a card, store its CardID and find a Card matching ID to retrieve it.
Basically, in a first time you should retrieve cards definitions (in your hand, on your battlefield...) and act : should i drop this card? Should i attack with this one?
We are awaiting for an Hearthbuddy wiki. Meanwhile i'll post the API documentation in this thread :
Triton.Game.TritonHS members
Triton.Game.HSCard members
Triton.Game.Mapping.TAG_RACE enum
Triton.Game.Mapping.TAG_RARITY enum
Triton.Game.TAG_CLASS enum
Triton.Game.CardZone enum
Samples :
Endturn :
Here is a simple sample :
This custom deck is implemented in file "MyHearthbuddyPath/CustomDecks/EndturnDeck/EndturnDeck.cs"
This custom deck will end turn on each turn (pretty useless but thats an example).
OnlyMinions :
Second sample : basic bot designed to drop minions without battlecries ASAP.
Comments are quite exhaustive, i think you can get the workflow by yourself
This custom deck is implemented in file "MyHearthbuddyPath/CustomDecks/OnlyMinions/OnlyMinionsDeck.cs"
Good luck everyone
Here is a little guide to explain how to make you custom deck.
First of all, current API is considered as a "custom deck". User custom deck will be used only once INGAME and during player turn.
In other terms, the entry point of a custom deck (SelectCard) is called once our turn.
You custom deck will be developped in c#. I advise you to use Visual Studio IDE or Mono IDE to help you during developpement. Custom decks sourcefiles must be added in "CustomDecks" folder (near hearthbuddy.exe). Create a subfolder for each different custom deck.
You custom class must inherit from ICustomDeck interface. You must implement "IEnumerator SelectCard()" function.
I strongly advise you to script a custom deck for only 30 card. Describe behaviors for these 30 cards and provide cards needed for your custom deck.
You can, ofcourse, split you custom deck between multiple .cs files. Since your files are in the same namespace, you can access to your namespace members. I Strongly advise you to split your helpers function, custom card behavior, and main function is different files.
Cards definitions (Ids) : View attachment CardDefs.txt
Errors
Scripts compilation is done during Bot startup. Compilation errors are displayed in log window. If your custom deck is avaible, it will be avaible in 'custom deck" list. Else you deck contains errors. Check log window.
Custom deck called "." is the actual deck of Hearthbuddy's team.
If your custom deck throw an exception (trying to access to a null member, dividing by 0, etc...), a log message will be displayed begining with "Exception occured in coroutine:" statement, followed by exception description and line(s) involved in exception.
Documentation
First of all : never cache members! Never store a card in a variable for X loops. Always retrieve cards from TritonHS functions in real-time. If you want to store a card, store its CardID and find a Card matching ID to retrieve it.
Basically, in a first time you should retrieve cards definitions (in your hand, on your battlefield...) and act : should i drop this card? Should i attack with this one?
We are awaiting for an Hearthbuddy wiki. Meanwhile i'll post the API documentation in this thread :
Triton.Game.TritonHS members
Code:
/// <summary>
/// Retrieves the card in the specified zone, and player.
/// </summary>
/// <param name="zone">Zone to retrieve cards from</param>
/// <param name="localPlayer">Retrieve our cards?</param>
/// <returns>List of cards</returns>
public static List<HSCard> GetCards(CardZone zone, bool localPlayer = true)
/// <summary>
/// End current turn
/// </summary>
public static void EndTurn()
/// <summary>
/// Is our turn?
/// </summary>
/// <returns></returns>
public static bool IsOurTurn()
public static bool IsInGame()
/// <summary>
/// Is the game currently in target mode? => Should we have to choose a target?
/// </summary>
/// <returns></returns>
public static bool IsInTargetMode()
/// <summary>
/// Cancel current targeting mode
/// </summary>
public static void CancelTargetingMode()
/// <summary>
/// Concede
/// </summary>
public static void Concede()
/// <summary>
/// Retrieve current mana crystals avaible
/// </summary>
public static int CurrentMana
public static int Fatigue
public static int NumCardsPlayedThisTurn
public static int NumMinionsPlayedThisTurn
public static int Resources
public static int RecallOwed
#region Heros
/// <summary>
/// Retrieve our hero total health (health + armor)
/// </summary>
public static int OurHeroHealthAndArmor
/// <summary>
/// Retrieve our hero armor
/// </summary>
public static int OurHeroArmor
/// <summary>
/// Retrieve enemy hero total health (health + armor)
/// </summary>
public static int EnemyHeroHealthAndArmor
/// <summary>
/// Retrieve enemy hero amor
/// </summary>
public static int EnemyHeroArmor
/// <summary>
/// Retrieve our hero attack dmgs
/// </summary>
public static int OurHeroAttack
/// <summary>
/// Retrieve enemy hero attack dmgs
/// </summary>
public static int EnemyHeroAttack
/// <summary>
/// Retrieve our hero power card (hero spell)
/// </summary>
public static HSCard OurHeroPowerCard
/// <summary>
/// Retrieve our hero card
/// </summary>
public static HSCard OurHero
/// <summary>
/// Retrieve enemy hero card
/// </summary>
public static HSCard EnemyHero
/// <summary>
/// Do we have a weapon?
/// </summary>
public static bool DoWeHaveWeapon
/// <summary>
/// Does enemy hhas weapon?
/// </summary>
public static bool DoesEnemyHasWeapon
/// <summary>
/// Retrieve our hero card (retrieve null if no weapon)
/// </summary>
public static HSCard OurWeaponCard
/// <summary>
/// Retrieve enemy hero card (retrieve null if no weapon)
/// </summary>
public static HSCard EnemyWeaponCard
/// <summary>
/// Retrieve current spell power (additional spell power)
/// </summary>
public static int CurrentSpellPower
#endregion
#region Choice mode
/// <summary>
/// Is the game in multi-choice mode? (drood cards)
/// </summary>
/// <returns></returns>
public static bool IsInChoiceMode()
/// <summary>
/// If we are in multiple cards choice, choose left card
/// </summary>
public static void ChooseOneClickLeft()
/// <summary>
/// If we are in multiple cards choice, choose right card
/// </summary>
public static void ChooseOneClickRight()
Triton.Game.HSCard members
Code:
public int Health
public int Attack
public int Cost
public string Id
public TAG_RACE Race
public string Name
public TAG_RARITY Rarity
public int Durability
public TAG_CLASS Class
// Has, Is
public bool HasTaunt
public bool HasCharge
public bool HasBattlecry
public bool CanBeTargetedByAbilities
public bool CanBeTargetedByHeroPowers
public bool IsImmune
public bool IsPoisonous
public bool IsEnraged
public bool IsFreeze
public bool IsFrozen
public bool IsAsleep
public bool IsStealthed
public bool HasDivineShield
public bool IsHero
public bool IsHeroPower
public bool IsMinion
public bool IsSpell
public bool IsAbility)
public bool IsWeapon
public bool IsElite
public bool IsEnchantment
public bool IsExhausted
public bool IsAttached
public bool IsRecentlyArrived
public bool IsSecret
public bool CanAttack
public bool CanBeAttacked
/// <summary>
/// Can this card be targetted?
/// </summary>
/// <returns></returns>
public bool CanBeTargeted
/// <summary>
/// Can this card be used?
/// </summary>
/// <returns></returns>
public bool CanBeUsed
public bool CanBeTargetedByOpponents
public bool IsMagnet
public bool HasSpellPower
public bool IsAffectedBySpellPower
public bool IsDamaged
public bool HasWindfury
public bool HasCombo
public bool HasRecall
public bool HasDeathrattle
public bool IsSilenced
public bool CanBeDamaged
public int EntityId
public int ControllerId
public int CreatorId
public int NumAttackThisTurn
public int ZonePosition
// Actions
/// <summary>
/// Grab this card (from hand)
/// </summary>
public void DoGrab()
/// <summary>
/// Drop this card (once grabbed)
/// </summary>
public void DoDrop()
/// <summary>
/// Select a target for this card (ensure we are in target mode before)
/// </summary>
/// <param name="target"></param>
public void DoTarget(HSCard target)
/// <summary>
/// Cancel current targeting mode
/// </summary>
public void CancelTarget()
/// <summary>
/// Do attack with this card
/// </summary>
/// <param name="attackee">target card</param>
public void DoAttack(HSCard attackee)
/// <summary>
/// Toggle this card (if we are during mulligan)
/// </summary>
Triton.Game.Mapping.TAG_RACE enum
Code:
INVALID = 0,
BLOODELF = 1,
DRAENEI = 2,
DWARF = 3,
GNOME = 4,
GOBLIN = 5,
HUMAN = 6,
NIGHTELF = 7,
ORC = 8,
TAUREN = 9,
TROLL = 10,
UNDEAD = 11,
WORGEN = 12,
GOBLIN2 = 13,
MURLOC = 14,
DEMON = 15,
SCOURGE = 16,
MECHANICAL = 17,
ELEMENTAL = 18,
OGRE = 19,
PET = 20,
TOTEM = 21,
NERUBIAN = 22,
PIRATE = 23,
DRAGON = 24,
Triton.Game.Mapping.TAG_RARITY enum
Code:
INVALID = 0,
COMMON = 1,
FREE = 2,
RARE = 3,
EPIC = 4,
LEGENDARY = 5,
Triton.Game.TAG_CLASS enum
Code:
INVALID = 0,
DEATHKNIGHT = 1,
DRUID = 2,
HUNTER = 3,
MAGE = 4,
PALADIN = 5,
PRIEST = 6,
ROGUE = 7,
SHAMAN = 8,
WARLOCK = 9,
WARRIOR = 10,
DREAM = 11,
Triton.Game.CardZone enum
Code:
None,
Hand,
Battlefield,
Graveyard
Samples :
Endturn :
Here is a simple sample :
This custom deck is implemented in file "MyHearthbuddyPath/CustomDecks/EndturnDeck/EndturnDeck.cs"
Code:
using System.Collections;
using Triton.Bot;
using Triton.Common;
using Triton.Game;
namespace Hearthbuddy.Samples
{
public class EndTurnDeck : ICustomDeck
{
public IEnumerator SelectCard()
{
TritonHS.EndTurn();
yield break;
}
}
}
This custom deck will end turn on each turn (pretty useless but thats an example).
OnlyMinions :
Second sample : basic bot designed to drop minions without battlecries ASAP.
Comments are quite exhaustive, i think you can get the workflow by yourself

This custom deck is implemented in file "MyHearthbuddyPath/CustomDecks/OnlyMinions/OnlyMinionsDeck.cs"
Code:
using System.Collections;
using System.Linq;
using Triton.Bot;
using Triton.Common;
using Triton.Game;
using Triton.Game.Mapping;
namespace Hearthbuddy.Samples
{
class OnlyMinions : ICustomDeck
{
private int _loopCount = 1;
public IEnumerator SelectCard()
{
// Some verbose
Logging.Write("------- Turn " + TritonHS.CurrentTurn + " Loop " + _loopCount + " -------");
_loopCount++;
// ----- First : drops
// Try to play coin :
yield return TryToPlayCoin();
// Retrieve our cards in hand which can be used
// Can be used do a lot of check for us : enough mana, battlefield is not full to drop a minion, this spell can be used, etc...
// Use this function to determine if a card can be played from you hand
foreach (HSCard card in TritonHS.GetCards(CardZone.Hand).Where(s =>
s.CanBeUsed &&
s.IsMinion && // Manage only minions (s.IsSecret, s.IsWeapon, etc... to manage other kind of cards)
!s.HasBattlecry //without battlecries in this custom deck
))
{
Logging.Write("Lets play " + card.Name);
// This card is a minion : drop it
card.DoGrab(); // First : grab card
yield return Coroutine.Sleep(500);
// Then : little sleep (ensure card is considered "grabbed" by the game
card.DoDrop(); // Finally : drop
yield return Coroutine.Sleep(500);
// Check if we are in target mode (how to manage a battlecry).
if (TritonHS.IsInTargetMode())
{
// We are in target mode : this minion has a battlecry
// card.DoTarget(TritonHS.EnemyHero); // Sample : our minion battlecry do X dmg => focus enemy hero
// We dont manage Battlecries currently : cancel the target mode -> retrieve this card in our hands
card.CancelTarget(); // TritonHS.CancelTargetingMode() works too
}
yield break;
// Get out of this loop => return at the start of the function => check if we can use new cards (maybe we have drawn a card?)
}
// ----- Second : attacks
// Retrive cards on our battlefield which can be used
// CanbeUsed function is checking if we can attack with this minion (it is not frozen, not exhausted, has atk > 0, etc...)
foreach (HSCard card in TritonHS.GetCards(CardZone.Battlefield).Where(s => s.CanBeUsed))
{
// Check if enemy has a taunter
if (DoTheEnemyHasATaunter())
{
// Do our attack on enemy taunter
Logging.Write("Do attack : " + card.Name + " -> " + RetrieveEnemyTaunter().Name);
card.DoAttack(RetrieveEnemyTaunter());
yield return Coroutine.Sleep(1000); // Little sleep after an attack
yield break;
// Get out of this loop => return at the start of the function => check if we can use new cards (maybe we have drawn a card?)
}
// Enemy has NO taunter and we can target him => go for the face
if (TritonHS.EnemyHero.CanBeTargetedByOpponents)
{
// Do our attack
Logging.Write("Do attack : " + card.Name + " -> " + TritonHS.EnemyHero.Name);
card.DoAttack(TritonHS.EnemyHero);
yield return Coroutine.Sleep(1000); // Little sleep after an attack
yield break;
// Get out of this loop => return at the start of the function => check if we can use new cards (maybe we have drawn a card?)
}
}
// ----- Third : check hero power
if (TritonHS.OurHeroPowerCard.CanBeUsed)
{
// We can use our hero power
if (TritonHS.OurHero.Class == TAG_CLASS.WARLOCK && TritonHS.OurHeroHealthAndArmor < 10)
// Be careful with warlock power
{
Logging.Write("We are a warlock and we are low life : ignore our hero power.");
}
else
{
Logging.Write("Lets use our hero power.");
// Grab card : only a grab is needed for spells
TritonHS.OurHeroPowerCard.DoGrab();
yield return Coroutine.Sleep(1000); // Game need to load some stuff
// Is a target needed for hero power?
// Check if target retrieved is not null (maybe we did a misstake in our RetrieveTargetForHeroPower function)
if (TritonHS.IsInTargetMode() && RetrieveTargetForHeroPower() != null)
{
// So, find a target
TritonHS.OurHeroPowerCard.DoTarget(RetrieveTargetForHeroPower());
yield return Coroutine.Sleep(1000); // Game need to load some stuff
}
yield break;
// Get out of this loop => return at the start of the function => check if we can use new cards (maybe we have drawn a card?)
}
}
// ------ Fourth : use our Hero
// Our hero can be used : our hero ATK is > 0 && we are not frozen
// Dont focus taunters with our hero -> Focus enemy hero
// Enemy hero can be attacked
if (TritonHS.OurHero.CanBeUsed && !DoTheEnemyHasATaunter() &&
TritonHS.EnemyHero.CanBeTargetedByOpponents)
{
TritonHS.OurHero.DoAttack(TritonHS.EnemyHero);
}
// ----- Finally : end our turn
// If we reach this point : all our hand cards avaible are dropped & all our usable cards on battlefield are exhausted.
// Ensure we are not in target mode (maybe we failed somewhere)
// If game is still in target mode, endturn will fail
if (TritonHS.IsInTargetMode())
{
TritonHS.CancelTargetingMode();
}
// End our turn
_loopCount = 1;
Logging.Write("End turn.");
TritonHS.EndTurn();
}
/// <summary>
/// check if we have coin in hand, and play it if needed
/// </summary>
private IEnumerator TryToPlayCoin()
{
// Coin card ID
const string coinId = "GAME_005";
// Should we play coin?
if (TritonHS.GetCards(CardZone.Hand).Any(s => s.Id == coinId) // Do we have coin in hand?
&&
!TritonHS.GetCards(CardZone.Hand)
.Any(s => s.CanBeUsed // Ensure we cant play cards with current mana avaible
&&
TritonHS.GetCards(CardZone.Hand)
.Any(c => c.Cost == TritonHS.CurrentMana + 1 && c.IsMinion && !c.HasBattlecry)))
// A card is awaible with currentmana + 1 (more checks are needed!!)
{
HSCard coin = TritonHS.GetCards(CardZone.Hand).FirstOrDefault(s => s.Id == coinId);
if (coin != null)
{
Logging.Write("Lets play coin!");
coin.DoGrab();
yield return Coroutine.Sleep(500);
coin.DoDrop();
yield return Coroutine.Sleep(500);
}
}
}
/// <summary>
/// Determine if enemy player has a taunter on the field
/// </summary>
/// <returns></returns>
private bool DoTheEnemyHasATaunter()
{
return TritonHS.GetCards(CardZone.Battlefield, false).Any(s => s.HasTaunt);
}
/// <summary>
/// Retrieve first enemy taunter card which can be attacked
/// </summary>
/// <returns></returns>
private HSCard RetrieveEnemyTaunter()
{
return
TritonHS.GetCards(CardZone.Battlefield, false)
.FirstOrDefault(s => s.HasTaunt && s.CanBeTargetedByOpponents);
}
/// <summary>
/// Retrieve a target for hero power (depending on hero class)
/// </summary>
/// <returns>null if no target</returns>
private HSCard RetrieveTargetForHeroPower()
{
switch (TritonHS.OurHero.Class)
{
// These 6 class dont need a target => return null
case TAG_CLASS.DRUID:
case TAG_CLASS.HUNTER:
case TAG_CLASS.PALADIN:
case TAG_CLASS.ROGUE:
case TAG_CLASS.SHAMAN:
case TAG_CLASS.WARRIOR:
case TAG_CLASS.WARLOCK:
return null;
case TAG_CLASS.MAGE:
// Target enemy hero
return TritonHS.EnemyHero;
case TAG_CLASS.PRIEST:
// Target our hero
return TritonHS.OurHero;
default:
return null;
}
}
}
}
Good luck everyone

Last edited: