What's new
  • Visit Rebornbuddy
  • Visit Panda Profiles
  • Visit LLamamMagic
  • Visit Resources
  • Visit Downloads
  • Visit Portal

API starting guide - Custom Deck

Status
Not open for further replies.

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

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:
I have no idea of programin but... now people will share decks AND a special logic for that deck?
 
I have no idea of programin but... now people will share decks AND a special logic for that deck?

ye i think so people that know c++ etc programming wil make custom classes for community and then share it and you just put the file in the decks folder.
 
I'm gonna need some import/export decks, this 9 decks limitation is killing me
 
I have no idea of programin but... now people will share decks AND a special logic for that deck?

There are so many improvements to be made over the base bot logic I'm sure there's going to be a solid foundation made before we start really customizing every deck (by that I mean I assume someone out there will fix the logic vs taunt minions, how to value certain minions properly, etc. and then everyone will include that code in their custom decks).
 
There are so many improvements to be made over the base bot logic I'm sure there's going to be a solid foundation made before we start really customizing every deck (by that I mean I assume someone out there will fix the logic vs taunt minions, how to value certain minions properly, etc. and then everyone will include that code in their custom decks).

its easy to kill tauns the bot is already doing it i see that alot of times with my priest when there is a taunt he wil use that spell that kills instantly a minion taunt and the mage wil use poly on a big minion or taunt:)
 
So i just read threw that wall of text Im no programmer so im confused as fuck. But thanks for this cant wait to see whats comes next
But when i got to the bottom an read
API released

Good luck everyone ;)

I really Read
Good luck Every Buddy else

[video=youtube;xMQIECfKQgw]https://www.youtube.com/watch?v=xMQIECfKQgw[/video]
 
Last edited:
seems like a pretty basic setup with more function calls than script writing.

Are these function calls working?

What I mean is, if we do a function call to attack taunt, what cards will the bot select? can we do a function call to attack with specific cards first? (by hp, by attack, by "will survive") or is that just the randomness of the bot we will have to accept?
 
seems like a pretty basic setup with more function calls than script writing.

Are these function calls working?

What I mean is, if we do a function call to attack taunt, what cards will the bot select? can we do a function call to attack with specific cards first? (by hp, by attack, by "will survive") or is that just the randomness of the bot we will have to accept?

what? no, I would create a priority HSCard, loop through all cards in hand then choose the card that best gets scenario, then play that card. An example

you have a 2-2, and a 2-1 minion, and they have a 1-2, you could iterate through your hand, adding first card as HSCard, then if something is better than that, swap that HSCard to the new card, then play it. If you would like a code sample I would be happy to show you. For that specific example, you would want something that would live if you attacked it, so attacking with the 2-2 would (usually) be better.

If you want to attack with a specific card if it's up and they have a taunt, you could do something like

Code:
if (TritonHS.GetCards(CardZone.Battlefield, false).Find(s => s.HasTaunt) != null) {
      HSCard card = TritonHS.GetCards(CardZone.Hand, true).Find(s => s.Id == "GAME_010");
      if(card != null) card.DoAttack(TritonHS.GetCards(CardZone.Battlefield, false).Find(s => s.HasTaunt && s.CanBeTargetedByOpponents));
}


which will attack the first minion in the enemy battlefield with taunt, that has the ID of GAME_010 (i'm assuming they'll release the wiki with the card ID's, if not, I could go through and find them I guess.)

Question to HSB team, are there plans to add a regular plugin system outside of custom decks? Or no. Will there be a settings button? For instance will we be able to make gui's for user friendlieness?

(to the first part. If not, could we at least have the current logic file so that we could add plugin functionality to it as a community?)
 
Last edited:
which will attack the first minion in the enemy battlefield with taunt, that has the ID of GAME_010 (i'm assuming they'll release the wiki with the card ID's, if not, I could go through and find them I guess.)

I forgot it. Just added a json file containing card definitions (including Ids) ;)
 
Done in the main post.

thnx hey would it be possible to fix faerie dragon spells like holy fire and other spells cant target faerie dragong you can only target faerie dragon with consecration etc and other aoe spells but not with holy fire but it keeps looping it:) also the card file i put in the hearthbuddy folder right?
 
nevermind i think it wil work now with the cardtxt

"Health": 2,
"CardtextInhand": "Can't be targeted by Spells or Hero Powers.",
"321": 1,
"EnchantmentBirthVisual": 0,
 
First of all sorry for bad English.. I cant understand how can i use my own deck at HeathBuddy can u explain with a video or something ?
 
Excuse me...How do you fix that fairy dragon by those code...the Guide is too long I am lost? What did you do with those code?
 
Excuse me...How do you fix that fairy dragon by those code...the Guide is too long I am lost? What did you do with those code?

this is only for developers, not for "normal" users.
 
Status
Not open for further replies.
Back
Top