HearthBuddy卡牌无法识别 最新的处理方案是,直接引用第三方的类库来做卡牌的数据解析 分析(淘汰) 偶数萨电鳗狼王添加方法  实际操作

界面上无法识别,提示是

[Unidentified card ID :DAL_010]
[Unidentified card ID :DAL_415]

Unidentified card ID :HERO_02c

首先使用卡牌工具,查询卡牌id对应的卡牌名字

https://github.com/ChuckHearthstone/CardQuery

HearthBuddy卡牌无法识别
最新的处理方案是,直接引用第三方的类库来做卡牌的数据解析
分析(淘汰)
偶数萨电鳗狼王添加方法
 实际操作

 1.添加卡牌id的枚举

https://github.com/ChuckHearthstone/SilverFish/blob/89ca9e06327f1bd4271c5ebb2ece268530fc194e/DefaultRoutine/Chuck.SilverFish/Enums/CardIdEnum.cs#L1184

2.添加卡牌的Sim

https://www.nuget.org/packages/Chuck-HearthDb/   HearthBuddy的exe文件,引用这个类库

然后修改卡牌代码的解析 https://github.com/ChuckHearthstone/SilverFish/blob/master/DefaultRoutine/Chuck.SilverFish/ai/CardDB.cs#L137

  private CardDB()
        {
            CardList.Clear();
            cardidToCardList.Clear();

            //placeholdercard
            Card plchldr = new Card {name = CardName.unknown, cost = 1000};
            namelist.Add("unknown");
            CardList.Add(plchldr);
            unknownCard = CardList[0];

            string name = "";
            var cards = Cards.All;
            foreach (var item in cards.Keys)
            {
                var card = new Card();
                allCardIDS.Add(item);
                card.cardIDenum = ConvertHelper.cardIdstringToEnum(item);
                var dbCard = cards[item];
                card.EnglishName = dbCard.GetLocName(Locale.enUS);
                card.ChineseName = dbCard.GetLocName(Locale.zhCN);
                card.Health = dbCard.Health;
                card.Class = (int) dbCard.Class;
                card.Attack.Value = dbCard.Attack;
                card.race = (int) dbCard.Race;
                card.rarity = (int) dbCard.Rarity;
                card.cost = dbCard.Cost;
                card.type = (CardType) dbCard.Type;
                if (card.type == CardType.Token)
                {
                    card.isToken = true;
                }
                if (card.type == CardType.ENCHANTMENT)
                {
                    continue;
                }

                var trimmedCardName = TrimHelper.TrimEnglishName(dbCard.Name);
                if (!string.IsNullOrWhiteSpace(name))
                {
                    namelist.Add(trimmedCardName);
                }
                card.name = ConvertHelper.cardNamestringToEnum(trimmedCardName);

                card.poisonous = dbCard.Entity.GetTag(GameTag.POISONOUS) == 1;
                card.Enrage = dbCard.Entity.GetTag(GameTag.ENRAGED) == 1;
                card.Aura = dbCard.Entity.GetTag(GameTag.AURA) == 1;
                card.tank = dbCard.Entity.GetTag(GameTag.TAUNT) == 1;
                card.battlecry= dbCard.Entity.GetTag(GameTag.BATTLECRY) == 1;
                card.discover = dbCard.Entity.GetTag(GameTag.DISCOVER) == 1;
                card.windfury = dbCard.Entity.GetTag(GameTag.WINDFURY) == 1;
                card.deathrattle = dbCard.Entity.GetTag(GameTag.DEATHRATTLE) == 1;
                card.Reborn = dbCard.Entity.GetTag(GameTag.REBORN) == 1;
                card.Inspire = dbCard.Entity.GetTag(GameTag.INSPIRE) == 1;
                card.Durability = dbCard.Entity.GetTag(GameTag.DURABILITY);
                card.Elite = dbCard.Entity.GetTag(GameTag.ELITE) == 1;
                card.Combo = dbCard.Entity.GetTag(GameTag.COMBO) == 1;
                card.oneTurnEffect = dbCard.Entity.GetTag(GameTag.TAG_ONE_TURN_EFFECT) == 1;
                card.overload = dbCard.Entity.GetTag(GameTag.OVERLOAD);
                card.lifesteal = dbCard.Entity.GetTag(GameTag.LIFESTEAL) == 1;
                card.untouchable = dbCard.Entity.GetTag(GameTag.UNTOUCHABLE) == 1;
                card.Stealth = dbCard.Entity.GetTag(GameTag.STEALTH)==1;
                card.Secret = dbCard.Entity.GetTag(GameTag.SECRET) == 1;
                card.Quest = dbCard.Entity.GetTag(GameTag.QUEST) == 1;
                card.Freeze = dbCard.Entity.GetTag(GameTag.FREEZE) == 1;
                card.AdjacentBuff = dbCard.Entity.GetTag(GameTag.ADJACENT_BUFF) == 1;
                card.DivineShield = dbCard.Entity.GetTag(GameTag.DIVINE_SHIELD) == 1;
                card.Charge = dbCard.Entity.GetTag(GameTag.CHARGE) == 1;
                card.Rush.Value = dbCard.Entity.GetTag(GameTag.RUSH) == 1;
                card.Silence = dbCard.Entity.GetTag(GameTag.SILENCE) == 1;
                card.Morph = dbCard.Entity.GetTag(GameTag.MORPH) == 1;
                card.Spellpower = dbCard.Entity.GetTag(GameTag.SPELLPOWER) > 0;
                card.spellpowervalue = dbCard.Entity.GetTag(GameTag.SPELLPOWER);
                if (!string.IsNullOrEmpty(dbCard.Text))
                {
                    if (dbCard.Text.ToLower().Contains("choose one"))
                    {
                        card.choice = true;
                    }
                }

                var playRequirements = dbCard.Entity.GetPlayRequirements();
                if (playRequirements != null)
                {
                    foreach (var playRequirement in playRequirements)
                    {
                        var reqId = Convert.ToInt32(playRequirement.ReqId);
                        var errorType = (ErrorType) Enum.ToObject(typeof(ErrorType), reqId);
                        card.playrequires.Add(errorType);
                    }
                }

                if (card.name != CardName.unknown)
                {
                    CardList.Add(card);
                    if (!cardidToCardList.ContainsKey(card.cardIDenum))
                    {
                        cardidToCardList.Add(card.cardIDenum, card);
                    }
                    else
                    {
                        Logger.GetLoggerInstanceForType()
                            .ErrorFormat("[c.cardIDenum:" + card.cardIDenum + "] already exists in cardidToCardList");
                    }
                }
            }

            teacherminion = getCardDataFromID(CardIdEnum.NEW1_026t);
            illidanminion = getCardDataFromID(CardIdEnum.EX1_614t);
            lepergnome = getCardDataFromID(CardIdEnum.EX1_029);
            burlyrockjaw = getCardDataFromID(CardIdEnum.GVG_068);

            Helpfunctions.Instance.InfoLog("CardList:" + cardidToCardList.Count);

        }

分析(淘汰)

搜索getcardid,在所有的相关函数处设置断点,看会进入哪里的断点

发现是进入了namespace Triton.Game.Mapping

[Attribute38("EntityBase")]
public class EntityBase : MonoClass

这个类

public string GetCardId()
{
    return base.method_13("GetCardId", Array.Empty<object>());
}

namespace Triton.Game

public class HSCard

public string Id
        {
            get
            {
                return this.Entity_0.GetCardId();
            }
        }

namespace HREngine.Bots

public class Silverfish

// HREngine.Bots.Silverfish
// Token: 0x06000048 RID: 72 RVA: 0x0000A16C File Offset: 0x0000836C
private void getDecks()
{
    Dictionary<string, int> tmpDeck = new Dictionary<string, int>(this.startDeck);
    List<GraveYardItem> graveYard = new List<GraveYardItem>();
    Dictionary<CardDB.cardIDEnum, int> og = new Dictionary<CardDB.cardIDEnum, int>();
    Dictionary<CardDB.cardIDEnum, int> eg = new Dictionary<CardDB.cardIDEnum, int>();
    int owncontroler = TritonHs.OurHero.GetTag(GAME_TAG.CONTROLLER);
    int enemycontroler = TritonHs.EnemyHero.GetTag(GAME_TAG.CONTROLLER);
    this.turnDeck.Clear();
    this.noDuplicates = false;
    List<HSCard> allcards = TritonHs.GetAllCards();
    int allcardscount = allcards.Count;
    for (int i = 0; i < allcardscount; i++)
    {
        HSCard entity = allcards[i];
        if (entity.Id != null && !(entity.Id == ""))
        {
            if (CardDB.Instance.cardIdstringToEnum(entity.Id) == CardDB.cardIDEnum.UNG_116t)
            {
                this.ownMinionsCost0 = true;
            }
            if (entity.GetZone() == TAG_ZONE.GRAVEYARD)
            {
                CardDB.cardIDEnum cide = CardDB.Instance.cardIdstringToEnum(entity.Id);
                GraveYardItem gyi = new GraveYardItem(cide, entity.EntityId, entity.GetTag(GAME_TAG.CONTROLLER) == owncontroler);
                graveYard.Add(gyi);
                if (entity.GetTag(GAME_TAG.CONTROLLER) == owncontroler)
                {
                    if (og.ContainsKey(cide))
                    {
                        Dictionary<CardDB.cardIDEnum, int> dictionary;
                        CardDB.cardIDEnum key;
                        (dictionary = og)[key = cide] = dictionary[key] + 1;
                    }
                    else
                    {
                        og.Add(cide, 1);
                    }
                }
                else if (entity.GetTag(GAME_TAG.CONTROLLER) == enemycontroler)
                {
                    if (eg.ContainsKey(cide))
                    {
                        Dictionary<CardDB.cardIDEnum, int> dictionary;
                        CardDB.cardIDEnum key;
                        (dictionary = eg)[key = cide] = dictionary[key] + 1;
                    }
                    else
                    {
                        eg.Add(cide, 1);
                    }
                }
                if (cide == CardDB.cardIDEnum.UNG_067t1)
                {
                    this.ownCrystalCore = 5;
                }
            }
            string entityId = entity.Id;
            TAG_ZONE entZone = entity.GetZone();
            if (i < 30)
            {
                if (entityId != "")
                {
                    if (entZone != TAG_ZONE.DECK)
                    {
                        if (tmpDeck.ContainsKey(entityId))
                        {
                            Dictionary<string, int> dictionary2;
                            string key2;
                            (dictionary2 = tmpDeck)[key2 = entityId] = dictionary2[key2] - 1;
                        }
                    }
                }
            }
            else if (i >= 60 && entity.ControllerId == owncontroler)
            {
                if (this.extraDeck.ContainsKey(i))
                {
                    if (entityId != "" && entityId != this.extraDeck[i].id)
                    {
                        this.extraDeck[i].setId(entityId);
                    }
                    if (entZone == TAG_ZONE.DECK != this.extraDeck[i].isindeck)
                    {
                        this.extraDeck[i].setisindeck(entZone == TAG_ZONE.DECK);
                    }
                }
                else if (entZone == TAG_ZONE.DECK)
                {
                    this.extraDeck.Add(i, new Silverfish.extraCard(entityId, true));
                }
            }
        }
    }
    Action a = Ai.Instance.bestmove;
    foreach (KeyValuePair<int, Silverfish.extraCard> c in this.extraDeck)
    {
        if (c.Value.isindeck)
        {
            string entityId = c.Value.id;
            if (entityId == "")
            {
                if (a != null)
                {
                    actionEnum actionType = a.actionType;
                    if (actionType == actionEnum.playcard)
                    {
                        CardDB.cardIDEnum cardIDEnum = a.card.card.cardIDenum;
                        if (cardIDEnum <= CardDB.cardIDEnum.LOE_019t)
                        {
                            if (cardIDEnum == CardDB.cardIDEnum.BRM_007)
                            {
                                goto IL_42B;
                            }
                            if (cardIDEnum != CardDB.cardIDEnum.LOE_002)
                            {
                                if (cardIDEnum == CardDB.cardIDEnum.LOE_019t)
                                {
                                    entityId = "LOE_019t2";
                                }
                            }
                            else
                            {
                                entityId = "LOE_002t";
                            }
                        }
                        else if (cardIDEnum != CardDB.cardIDEnum.LOE_079)
                        {
                            if (cardIDEnum == CardDB.cardIDEnum.LOE_104)
                            {
                                goto IL_42B;
                            }
                            if (cardIDEnum == CardDB.cardIDEnum.LOE_110)
                            {
                                entityId = "LOE_110t";
                            }
                        }
                        else
                        {
                            entityId = "LOE_019t";
                        }
                        goto IL_485;
                        IL_42B:
                        if (a.target != null)
                        {
                            entityId = a.target.handcard.card.cardIDenum.ToString();
                        }
                    }
                    IL_485:;
                }
                if (entityId == "")
                {
                    Dictionary<CardDB.cardIDEnum, int> oldCardsOut = Probabilitymaker.Instance.enemyCardsOut;
                    foreach (KeyValuePair<CardDB.cardIDEnum, int> tmp in eg)
                    {
                        if (!oldCardsOut.ContainsKey(tmp.Key) || tmp.Value != oldCardsOut[tmp.Key])
                        {
                            CardDB.cardIDEnum cardIDEnum = tmp.Key;
                            if (cardIDEnum != CardDB.cardIDEnum.AT_035)
                            {
                                if (cardIDEnum != CardDB.cardIDEnum.GVG_031)
                                {
                                    if (cardIDEnum == CardDB.cardIDEnum.LOE_111)
                                    {
                                        entityId = "LOE_111";
                                    }
                                }
                                else
                                {
                                    entityId = "aiextra1";
                                }
                            }
                            else
                            {
                                entityId = "AT_035t";
                            }
                        }
                    }
                    if (entityId == "" && this.lastpf != null)
                    {
                        int num = 0;
                        foreach (Minion j in this.enemyMinions)
                        {
                            if (j.handcard.card.cardIDenum == CardDB.cardIDEnum.GVG_056)
                            {
                                num++;
                            }
                        }
                        if (num > 0)
                        {
                            foreach (Minion j in this.lastpf.enemyMinions)
                            {
                                if (j.handcard.card.cardIDenum == CardDB.cardIDEnum.GVG_056)
                                {
                                    num--;
                                }
                            }
                        }
                        if (num > 0)
                        {
                            entityId = "GVG_056t";
                        }
                        else
                        {
                            num = 0;
                            foreach (Minion j in this.lastpf.ownMinions)
                            {
                                if (j.handcard.card.cardIDenum == CardDB.cardIDEnum.GVG_035)
                                {
                                    num++;
                                }
                            }
                            if (num > 0)
                            {
                                foreach (Minion j in this.ownMinions)
                                {
                                    if (j.handcard.card.cardIDenum == CardDB.cardIDEnum.GVG_035)
                                    {
                                        num--;
                                    }
                                }
                            }
                            if (num > 0)
                            {
                                entityId = "GVG_035";
                            }
                        }
                    }
                }
                if (entityId == "")
                {
                    entityId = "aiextra1";
                }
            }
            c.Value.setId(entityId);
            CardDB.cardIDEnum ce = CardDB.Instance.cardIdstringToEnum(entityId);
            if (this.turnDeck.ContainsKey(ce))
            {
                Dictionary<CardDB.cardIDEnum, int> dictionary;
                CardDB.cardIDEnum key;
                (dictionary = this.turnDeck)[key = ce] = dictionary[key] + 1;
            }
            else
            {
                this.turnDeck.Add(ce, 1);
            }
        }
    }
    foreach (KeyValuePair<string, int> c2 in tmpDeck)
    {
        if (c2.Value >= 1)
        {
            CardDB.cardIDEnum ce = CardDB.Instance.cardIdstringToEnum(c2.Key);
            if (ce != CardDB.cardIDEnum.None)
            {
                if (this.turnDeck.ContainsKey(ce))
                {
                    Dictionary<CardDB.cardIDEnum, int> dictionary;
                    CardDB.cardIDEnum key;
                    (dictionary = this.turnDeck)[key = ce] = dictionary[key] + c2.Value;
                }
                else
                {
                    this.turnDeck.Add(ce, c2.Value);
                }
            }
        }
    }
    Probabilitymaker.Instance.setOwnCardsOut(og);
    Probabilitymaker.Instance.setEnemyCardsOut(eg);
    bool isTurnStart = false;
    if (Ai.Instance.nextMoveGuess.mana == -100)
    {
        isTurnStart = true;
        Ai.Instance.updateTwoTurnSim();
    }
    Probabilitymaker.Instance.setGraveYard(graveYard, isTurnStart);
    if (this.startDeck.Count != 0)
    {
        this.noDuplicates = true;
        foreach (int i in this.turnDeck.Values)
        {
            if (i > 1)
            {
                this.noDuplicates = false;
                break;
            }
        }
    }
}

在updateEverything函数里面的this.getDecks();的下一行设置断点,发现这个执行完,就看到卡牌错误提示

// HREngine.Bots.Silverfish
// Token: 0x06000044 RID: 68 RVA: 0x00008154 File Offset: 0x00006354
public bool updateEverything(Behavior botbase, int numcal, out bool sleepRetry)
{
    this.needSleep = false;
    this.updateBehaveString(botbase);
    this.ownPlayerController = TritonHs.OurHero.ControllerId;
    Hrtprozis.Instance.clearAllRecalc();
    Handmanager.Instance.clearAllRecalc();
    this.getHerostuff();
    this.getMinions();
    this.getHandcards();
    this.getDecks();
    Hrtprozis.Instance.updateTurnDeck(this.turnDeck, this.noDuplicates);
    Hrtprozis.Instance.setOwnPlayer(this.ownPlayerController);
    Handmanager.Instance.setOwnPlayer(this.ownPlayerController);
    this.numOptionPlayedThisTurn = 0;
    this.numOptionPlayedThisTurn += this.cardsPlayedThisTurn + this.ownHero.numAttacksThisTurn;
    foreach (Minion i in this.ownMinions)
    {
        if (i.Hp >= 1)
        {
            this.numOptionPlayedThisTurn += i.numAttacksThisTurn;
        }
    }
    List<HSCard> list = TritonHs.GetCards(CardZone.Graveyard, true);
    foreach (GraveYardItem gi in Probabilitymaker.Instance.turngraveyard)
    {
        if (gi.own)
        {
            foreach (HSCard entiti in list)
            {
                if (gi.entity == entiti.EntityId)
                {
                    this.numOptionPlayedThisTurn += entiti.NumAttackThisTurn;
                    break;
                }
            }
        }
    }
    Hrtprozis.Instance.updatePlayer(this.ownMaxMana, this.currentMana, this.cardsPlayedThisTurn, this.numMinionsPlayedThisTurn, this.numOptionPlayedThisTurn, this.ueberladung, this.lockedMana, TritonHs.OurHero.EntityId, TritonHs.EnemyHero.EntityId);
    Hrtprozis.Instance.updateSecretStuff(this.ownSecretList, this.enemySecretList.Count);
    Hrtprozis.Instance.updateHero(this.ownWeapon, this.heroname, this.heroAbility, this.ownAbilityisReady, this.ownHeroPowerCost, this.ownHero, 10);
    Hrtprozis.Instance.updateHero(this.enemyWeapon, this.enemyHeroname, this.enemyAbility, false, this.enemyHeroPowerCost, this.enemyHero, this.enemyMaxMana);
    Questmanager.Instance.updatePlayedMobs(this.gTurnStep);
    Hrtprozis.Instance.updateMinions(this.ownMinions, this.enemyMinions);
    Hrtprozis.Instance.updateLurkersDB(this.LurkersDB);
    Handmanager.Instance.setHandcards(this.handCards, this.anzcards, this.enemyAnzCards);
    Hrtprozis.Instance.updateFatigueStats(this.ownDecksize, this.ownHeroFatigue, this.enemyDecksize, this.enemyHeroFatigue);
    Hrtprozis.Instance.updateJadeGolemsInfo(GameState.Get().GetFriendlySidePlayer().GetTag(GAME_TAG.JADE_GOLEM), GameState.Get().GetOpposingSidePlayer().GetTag(GAME_TAG.JADE_GOLEM));
    Hrtprozis.Instance.updateTurnInfo(this.gTurn, this.gTurnStep);
    this.updateCThunInfobyCThun();
    Hrtprozis.Instance.updateCThunInfo(this.anzOgOwnCThunAngrBonus, this.anzOgOwnCThunHpBonus, this.anzOgOwnCThunTaunt);
    Hrtprozis.Instance.updateCrystalCore(this.ownCrystalCore);
    Hrtprozis.Instance.updateOwnMinionsInDeckCost0(this.ownMinionsCost0);
    Probabilitymaker.Instance.setEnemySecretGuesses(this.enemySecretList);
    sleepRetry = this.needSleep;
    bool result;
    if (sleepRetry && numcal == 0)
    {
        result = false;
    }
    else
    {
        if (!Hrtprozis.Instance.setGameRule)
        {
            RulesEngine.Instance.setCardIdRulesGame(this.ownHero.cardClass, this.enemyHero.cardClass);
            Hrtprozis.Instance.setGameRule = true;
        }
        Playfield p = new Playfield();
        p.enemyCardsOut = new Dictionary<CardDB.cardIDEnum, int>(Probabilitymaker.Instance.enemyCardsOut);
        if (this.lastpf != null)
        {
            if (this.lastpf.isEqualf(p))
            {
                return false;
            }
            if (p.gTurnStep > 0 && Ai.Instance.nextMoveGuess.ownMinions.Count == p.ownMinions.Count)
            {
                if (Ai.Instance.nextMoveGuess.ownHero.Ready != p.ownHero.Ready && !p.ownHero.Ready)
                {
                    sleepRetry = true;
                    Helpfunctions.Instance.ErrorLog("[AI] Hero ready = " + p.ownHero.Ready + ". Attempting recover....");
                    Ai.Instance.nextMoveGuess = new Playfield
                    {
                        mana = -100
                    };
                    return false;
                }
                for (int j = 0; j < p.ownMinions.Count; j++)
                {
                    if (Ai.Instance.nextMoveGuess.ownMinions[j].Ready != p.ownMinions[j].Ready && !p.ownMinions[j].Ready)
                    {
                        sleepRetry = true;
                        Helpfunctions.Instance.ErrorLog(string.Concat(new object[]
                        {
                            "[AI] Minion ready = ",
                            p.ownMinions[j].Ready,
                            " (",
                            p.ownMinions[j].entitiyID,
                            " ",
                            p.ownMinions[j].handcard.card.cardIDenum,
                            " ",
                            p.ownMinions[j].name,
                            "). Attempting recover...."
                        }));
                        Ai.Instance.nextMoveGuess = new Playfield
                        {
                            mana = -100
                        };
                        return false;
                    }
                }
            }
            Probabilitymaker.Instance.updateSecretList(p, this.lastpf);
            this.lastpf = p;
        }
        else
        {
            this.lastpf = p;
        }
        p = new Playfield();
        Helpfunctions.Instance.ErrorLog("calculating stuff... " + DateTime.Now.ToString("HH:mm:ss.ffff"));
        using (TritonHs.Memory.ReleaseFrame(true))
        {
            this.printstuff();
            Ai.Instance.dosomethingclever(botbase);
        }
        Helpfunctions.Instance.ErrorLog("calculating ended! " + DateTime.Now.ToString("HH:mm:ss.ffff"));
        if (this.sttngs.printRules > 0)
        {
            string[] rulesStr = Ai.Instance.bestplay.rulesUsed.Split(new char[]
            {
                '@'
            });
            if (rulesStr.Count<string>() > 0 && rulesStr[0] != "")
            {
                Helpfunctions.Instance.ErrorLog("ruleWeight " + Ai.Instance.bestplay.ruleWeight * -1);
                foreach (string rs in rulesStr)
                {
                    if (!(rs == ""))
                    {
                        Helpfunctions.Instance.ErrorLog("rule: " + rs);
                    }
                }
            }
        }
        result = true;
    }
    return result;
}

namespace HREngine.Bots

public class DefaultRoutine : IRoutine, IRunnable, IAuthored, IBase, IConfigurable

在update everything函数调用的地方设置断点,发现执行pdate everything函数,就有错误提示。

所以卡牌的错误,是在update everything里面出来的。

// HREngine.Bots.DefaultRoutine
// Token: 0x06000017 RID: 23 RVA: 0x00005A54 File Offset: 0x00003C54
public async Task OurTurnLogic()
{
    if (this.behave.BehaviorName() != DefaultRoutineSettings.Instance.DefaultBehavior)
    {
        this.behave = this.sf.getBehaviorByName(DefaultRoutineSettings.Instance.DefaultBehavior);
        Silverfish.Instance.lastpf = null;
    }
    if (this.learnmode && (TritonHs.IsInTargetMode() || TritonHs.IsInChoiceMode()))
    {
        await Coroutine.Sleep(50);
    }
    else if (TritonHs.IsInTargetMode())
    {
        if (this.dirtytarget >= 0)
        {
            DefaultRoutine.Log.Info("targeting...");
            HSCard source = null;
            if (this.dirtyTargetSource == 9000)
            {
                source = TritonHs.OurHeroPowerCard;
            }
            else
            {
                source = this.getEntityWithNumber(this.dirtyTargetSource);
            }
            HSCard target = this.getEntityWithNumber(this.dirtytarget);
            if (target == null)
            {
                DefaultRoutine.Log.Error("target is null...");
                TritonHs.CancelTargetingMode();
            }
            else
            {
                this.dirtytarget = -1;
                this.dirtyTargetSource = -1;
                if (source == null)
                {
                    await TritonHs.DoTarget(target);
                }
                else
                {
                    await source.DoTarget(target);
                }
                await Coroutine.Sleep(555);
            }
        }
        else
        {
            DefaultRoutine.Log.Error("target failure...");
            TritonHs.CancelTargetingMode();
        }
    }
    else if (TritonHs.IsInChoiceMode())
    {
        await Coroutine.Sleep(555 + this.makeChoice());
        switch (this.dirtychoice)
        {
        case 0:
            TritonHs.ChooseOneClickMiddle();
            break;
        case 1:
            TritonHs.ChooseOneClickLeft();
            break;
        case 2:
            TritonHs.ChooseOneClickRight();
            break;
        }
        this.dirtychoice = -1;
        await Coroutine.Sleep(555);
    }
    else
    {
        bool sleepRetry = false;
        bool templearn = Silverfish.Instance.updateEverything(this.behave, 0, out sleepRetry);
        if (sleepRetry)
        {
            DefaultRoutine.Log.Error("[AI] Readiness error. Attempting recover...");
            await Coroutine.Sleep(500);
            templearn = Silverfish.Instance.updateEverything(this.behave, 1, out sleepRetry);
        }
        if (templearn)
        {
            this.printlearnmode = true;
        }
        if (this.learnmode)
        {
            if (this.printlearnmode)
            {
                Ai.Instance.simmulateWholeTurnandPrint();
            }
            this.printlearnmode = false;
            await Coroutine.Sleep(50);
        }
        else
        {
            Action moveTodo = Ai.Instance.bestmove;
            if (moveTodo == null || moveTodo.actionType == actionEnum.endturn || Ai.Instance.bestmoveValue < -9999f)
            {
                bool doEndTurn = false;
                bool doConcede = false;
                if (Ai.Instance.bestmoveValue > -10000f)
                {
                    doEndTurn = true;
                }
                else if (Settings.Instance.concedeMode != 0)
                {
                    doConcede = true;
                }
                else if (new Playfield().ownHeroHasDirectLethal())
                {
                    Playfield lastChancePl = Ai.Instance.bestplay;
                    bool lastChance = false;
                    if (lastChancePl.owncarddraw > 0)
                    {
                        foreach (Handmanager.Handcard hc in lastChancePl.owncards)
                        {
                            if (hc.card.name == CardDB.cardName.unknown)
                            {
                                lastChance = true;
                            }
                        }
                        if (!lastChance)
                        {
                            doConcede = true;
                        }
                    }
                    else
                    {
                        doConcede = true;
                    }
                    if (doConcede)
                    {
                        foreach (Minion i in lastChancePl.ownMinions)
                        {
                            if (i.playedThisTurn)
                            {
                                CardDB.cardName name = i.handcard.card.name;
                                if (name <= CardDB.cardName.nzoththecorruptor)
                                {
                                    if (name != CardDB.cardName.barongeddon)
                                    {
                                        if (name != CardDB.cardName.cthun)
                                        {
                                            if (name == CardDB.cardName.nzoththecorruptor)
                                            {
                                                lastChance = true;
                                            }
                                        }
                                        else
                                        {
                                            lastChance = true;
                                        }
                                    }
                                    else if (lastChancePl.enemyHero.Hp < 3)
                                    {
                                        lastChance = true;
                                    }
                                }
                                else if (name != CardDB.cardName.ragnarosthefirelord)
                                {
                                    if (name != CardDB.cardName.sirfinleymrrgglton)
                                    {
                                        if (name == CardDB.cardName.yoggsaronhopesend)
                                        {
                                            lastChance = true;
                                        }
                                    }
                                    else
                                    {
                                        lastChance = true;
                                    }
                                }
                                else if (lastChancePl.enemyHero.Hp < 9)
                                {
                                    lastChance = true;
                                }
                            }
                        }
                    }
                    if (lastChance)
                    {
                        doConcede = false;
                    }
                }
                else if (moveTodo == null || moveTodo.actionType == actionEnum.endturn)
                {
                    doEndTurn = true;
                }
                if (doEndTurn)
                {
                    Helpfunctions.Instance.ErrorLog("end turn");
                    await TritonHs.EndTurn();
                    return;
                }
                if (doConcede)
                {
                    Helpfunctions.Instance.ErrorLog("Lethal detected. Concede...");
                    Helpfunctions.Instance.logg("Concede... Lethal detected###############################################");
                    TritonHs.Concede(true);
                    return;
                }
            }
            Helpfunctions.Instance.ErrorLog("play action");
            if (moveTodo == null)
            {
                Helpfunctions.Instance.ErrorLog("moveTodo == null. EndTurn");
                await TritonHs.EndTurn();
            }
            else
            {
                moveTodo.print(false);
                if (moveTodo.actionType == actionEnum.playcard)
                {
                    Questmanager.Instance.updatePlayedCardFromHand(moveTodo.card);
                    HSCard cardtoplay = this.getCardWithNumber(moveTodo.card.entity);
                    if (cardtoplay == null)
                    {
                        Helpfunctions.Instance.ErrorLog("[Tick] cardtoplay == null");
                    }
                    else if (moveTodo.target != null)
                    {
                        HSCard target2 = this.getEntityWithNumber(moveTodo.target.entitiyID);
                        if (target2 != null)
                        {
                            Helpfunctions.Instance.ErrorLog(string.Concat(new object[]
                            {
                                "play: ",
                                cardtoplay.Name,
                                " (",
                                cardtoplay.EntityId,
                                ") target: ",
                                target2.Name,
                                " (",
                                target2.EntityId,
                                ")"
                            }));
                            Helpfunctions.Instance.logg(string.Concat(new object[]
                            {
                                "play: ",
                                cardtoplay.Name,
                                " (",
                                cardtoplay.EntityId,
                                ") target: ",
                                target2.Name,
                                " (",
                                target2.EntityId,
                                ") choice: ",
                                moveTodo.druidchoice
                            }));
                            if (moveTodo.druidchoice >= 1)
                            {
                                this.dirtytarget = moveTodo.target.entitiyID;
                                this.dirtychoice = moveTodo.druidchoice;
                                this.choiceCardId = moveTodo.card.card.cardIDenum.ToString();
                            }
                            this.dirtyTargetSource = moveTodo.card.entity;
                            this.dirtytarget = moveTodo.target.entitiyID;
                            await cardtoplay.Pickup(500);
                            if (moveTodo.card.card.type == CardDB.cardtype.MOB)
                            {
                                await cardtoplay.UseAt(moveTodo.place);
                            }
                            else if (moveTodo.card.card.type == CardDB.cardtype.WEAPON)
                            {
                                await cardtoplay.UseOn(target2.Card);
                            }
                            else if (moveTodo.card.card.type == CardDB.cardtype.SPELL)
                            {
                                await cardtoplay.UseOn(target2.Card);
                            }
                            else
                            {
                                await cardtoplay.UseOn(target2.Card);
                            }
                        }
                        else
                        {
                            Helpfunctions.Instance.ErrorLog("[AI] Target is missing. Attempting recover...");
                            Helpfunctions.Instance.logg("[AI] Target " + moveTodo.target.entitiyID + "is missing. Attempting recover...");
                        }
                        await Coroutine.Sleep(500);
                    }
                    else
                    {
                        Helpfunctions.Instance.ErrorLog(string.Concat(new object[]
                        {
                            "play: ",
                            cardtoplay.Name,
                            " (",
                            cardtoplay.EntityId,
                            ") target nothing"
                        }));
                        Helpfunctions.Instance.logg(string.Concat(new object[]
                        {
                            "play: ",
                            cardtoplay.Name,
                            " (",
                            cardtoplay.EntityId,
                            ") choice: ",
                            moveTodo.druidchoice
                        }));
                        if (moveTodo.druidchoice >= 1)
                        {
                            this.dirtychoice = moveTodo.druidchoice;
                            this.choiceCardId = moveTodo.card.card.cardIDenum.ToString();
                        }
                        this.dirtyTargetSource = -1;
                        this.dirtytarget = -1;
                        await cardtoplay.Pickup(500);
                        if (moveTodo.card.card.type == CardDB.cardtype.MOB)
                        {
                            await cardtoplay.UseAt(moveTodo.place);
                        }
                        else
                        {
                            await cardtoplay.Use();
                        }
                        await Coroutine.Sleep(500);
                    }
                }
                else if (moveTodo.actionType == actionEnum.attackWithMinion)
                {
                    HSCard attacker = this.getEntityWithNumber(moveTodo.own.entitiyID);
                    HSCard target3 = this.getEntityWithNumber(moveTodo.target.entitiyID);
                    if (attacker != null)
                    {
                        if (target3 != null)
                        {
                            Helpfunctions.Instance.ErrorLog("minion attack: " + attacker.Name + " target: " + target3.Name);
                            Helpfunctions.Instance.logg("minion attack: " + attacker.Name + " target: " + target3.Name);
                            await attacker.DoAttack(target3);
                        }
                        else
                        {
                            Helpfunctions.Instance.ErrorLog("[AI] Target is missing. Attempting recover...");
                            Helpfunctions.Instance.logg("[AI] Target " + moveTodo.target.entitiyID + "is missing. Attempting recover...");
                        }
                    }
                    else
                    {
                        Helpfunctions.Instance.ErrorLog("[AI] Attacker is missing. Attempting recover...");
                        Helpfunctions.Instance.logg("[AI] Attacker " + moveTodo.own.entitiyID + " is missing. Attempting recover...");
                    }
                    await Coroutine.Sleep(250);
                }
                else if (moveTodo.actionType == actionEnum.attackWithHero)
                {
                    HSCard attacker2 = this.getEntityWithNumber(moveTodo.own.entitiyID);
                    HSCard target4 = this.getEntityWithNumber(moveTodo.target.entitiyID);
                    if (attacker2 != null)
                    {
                        if (target4 != null)
                        {
                            this.dirtytarget = moveTodo.target.entitiyID;
                            Helpfunctions.Instance.ErrorLog("heroattack: " + attacker2.Name + " target: " + target4.Name);
                            Helpfunctions.Instance.logg("heroattack: " + attacker2.Name + " target: " + target4.Name);
                            this.dirtyTargetSource = moveTodo.own.entitiyID;
                            this.dirtytarget = moveTodo.target.entitiyID;
                            await attacker2.DoAttack(target4);
                        }
                        else
                        {
                            Helpfunctions.Instance.ErrorLog("[AI] Target is missing. Attempting recover...");
                            Helpfunctions.Instance.logg("[AI] Target " + moveTodo.target.entitiyID + "is missing (H). Attempting recover...");
                        }
                    }
                    else
                    {
                        Helpfunctions.Instance.ErrorLog("[AI] Attacker is missing. Attempting recover...");
                        Helpfunctions.Instance.logg("[AI] Attacker " + moveTodo.own.entitiyID + " is missing (H). Attempting recover...");
                    }
                    await Coroutine.Sleep(250);
                }
                else if (moveTodo.actionType == actionEnum.useHeroPower)
                {
                    HSCard cardtoplay2 = TritonHs.OurHeroPowerCard;
                    if (moveTodo.target != null)
                    {
                        HSCard target5 = this.getEntityWithNumber(moveTodo.target.entitiyID);
                        if (target5 != null)
                        {
                            Helpfunctions.Instance.ErrorLog("use ablitiy: " + cardtoplay2.Name + " target " + target5.Name);
                            Helpfunctions.Instance.logg(string.Concat(new string[]
                            {
                                "use ablitiy: ",
                                cardtoplay2.Name,
                                " target ",
                                target5.Name,
                                (moveTodo.druidchoice > 0) ? (" choice: " + moveTodo.druidchoice) : ""
                            }));
                            if (moveTodo.druidchoice > 0)
                            {
                                this.dirtytarget = moveTodo.target.entitiyID;
                                this.dirtychoice = moveTodo.druidchoice;
                                this.choiceCardId = moveTodo.card.card.cardIDenum.ToString();
                            }
                            this.dirtyTargetSource = 9000;
                            this.dirtytarget = moveTodo.target.entitiyID;
                            await cardtoplay2.Pickup(500);
                            await cardtoplay2.UseOn(target5.Card);
                        }
                        else
                        {
                            Helpfunctions.Instance.ErrorLog("[AI] Target is missing. Attempting recover...");
                            Helpfunctions.Instance.logg("[AI] Target " + moveTodo.target.entitiyID + "is missing. Attempting recover...");
                        }
                        await Coroutine.Sleep(500);
                    }
                    else
                    {
                        Helpfunctions.Instance.ErrorLog("use ablitiy: " + cardtoplay2.Name + " target nothing");
                        Helpfunctions.Instance.logg("use ablitiy: " + cardtoplay2.Name + " target nothing" + ((moveTodo.druidchoice > 0) ? (" choice: " + moveTodo.druidchoice) : ""));
                        if (moveTodo.druidchoice >= 1)
                        {
                            this.dirtychoice = moveTodo.druidchoice;
                            this.choiceCardId = moveTodo.card.card.cardIDenum.ToString();
                        }
                        this.dirtyTargetSource = -1;
                        this.dirtytarget = -1;
                        await cardtoplay2.Pickup(500);
                    }
                }
                else
                {
                    await TritonHs.EndTurn();
                }
            }
        }
    }
}

最后发现错误提示,是从

namespace HREngine.Bots

public class CardDB里面报错的

把cardid的字符串转换为cardIDEnum的时候出错

public CardDB.cardIDEnum cardIdstringToEnum(string s)
        {
            CardDB.cardIDEnum CardEnum;
            CardDB.cardIDEnum result;
            if (Enum.TryParse<CardDB.cardIDEnum>(s, false, out CardEnum))
            {
                result = CardEnum;
            }
            else
            {
                Logger.GetLoggerInstanceForType().ErrorFormat("[Unidentified card ID :" + s + "]", new object[0]);
                result = CardDB.cardIDEnum.None;
            }
            return result;
        }

根据我的印象,https://github.com/HearthSim/Hearthdb 这个项目就是用来生成卡牌的。

1.生成data

clone项目,编译之后,得到能识别的的CardDefs.xml。然后重命名成_carddb.txt,然后替换掉HearthBuddy里面的

2.自己实现卡牌

另外,这个文件不在HearthBuddy的exe里面,是外部加载进来的。HearthbuddyReleaseRoutinesDefaultRoutineSilverfishaiCardDB.cs

卡牌的实现需要自己写在Silverfishcards文件夹下,并且卡牌在实现的时候,根据需要从Silverfishai的某一个类中继承,实现对应的方法

 github上面尝试找了一下实现https://github.com/search?l=C%23&o=desc&q=getBattlecryEffect&s=indexed&type=Code

<Entity CardID="DAL_010" ID="51375" version="2">
<MasterPower>773590e3-24fa-4564-994a-94049b32d3f3</MasterPower>
<Tag enumID="185" name="CARDNAME" type="LocString">
<deDE>Togwaggels Plan</deDE>
<enUS>Togwaggle's Scheme</enUS>
<esES>Plan de Togafloja</esES>
<esMX>Complot de Togwaggle</esMX>
<frFR>Manœuvre de Cire-Pilleur</frFR>
<itIT>Piano di Cobaldo</itIT>
<jaJP>トグワグルの計略</jaJP>
<koKR>토그왜글의 계략</koKR>
<plPL>Intryga Trzęsibrzucha</plPL>
<ptBR>Estratagema de Fubalumba</ptBR>
<ruRU>Козни Вихлепыха</ruRU>
<thTH>แผนการของท็อกแว็กเกิล</thTH>
<zhCN>托瓦格尔的阴谋</zhCN>
<zhTW>托戈瓦哥的陰謀</zhTW>
</Tag>

<Entity CardID="DAL_415" ID="52111" version="2">
<MasterPower>2ef4d4c2-dbdf-4ae4-9f14-079984458f86</MasterPower>
<Tag enumID="185" name="CARDNAME" type="LocString">
<deDE>Ü.B.E.L.-Täter</deDE>
<enUS>EVIL Miscreant</enUS>
<esES>Malhechor del MAL</esES>
<esMX>Bellaco del MAL</esMX>
<frFR>Voyou du M.A.L.</frFR>
<itIT>Furfante del M.A.L.E.</itIT>
<jaJP>悪党同盟の悪漢</jaJP>
<koKR>잔.악.무.도. 악당</koKR>
<plPL>Szubrawiec Ligi Z.Ł.A.</plPL>
<ptBR>Mandrião da MAL</ptBR>
<ruRU>Негодяй ЗЛА</ruRU>
<thTH>จอมโฉด EVIL</thTH>
<zhCN>怪盗恶霸</zhCN>
<zhTW>邪惡陣線無賴</zhTW>
</Tag>

对了,貌似贴吧已经有部分教程了。https://tieba.baidu.com/p/5663869359

偶数萨电鳗狼王添加方法

==================================
1.编写新卡SIM,
在HearthbuddyRoutinesDefaultRoutineSilverfishcards,
在目录下新建一个Sim_扩展包缩写+卡牌编号.cs文件,
编写SIM后保存。
==================================
2.编写并增加新卡识别信息,
HearthbuddyRoutinesDefaultRoutineSilverfishdata 下的 _carddb.txt 文件,
其中存储所有卡牌信息。
含:185、184等等。
注意特效卡牌后面添加playRequirement,
每个节点后退两个字符。
(上述是新卡描述)
==================================
3.增加新卡编号及英文名称
在RoutinesDefaultRoutineSilverfishai的CardDB.cs中的3550行后增加新卡
扩展包简称_卡牌编号,
如:
LOOT_149,
LOOT_149e,
扩展包简称均为大写,可以联想GVG,
==================================
在RoutinesDefaultRoutineSilverfishai的CardDB.cs中的6303行后,增加新增卡牌英文名称。
注意卡牌英文名称没有空格且奇数偶数卡牌需添加两个名字,编写两个卡牌信息。
2费电鳗murksparkeel
6费偶数狼王geengreymane
战斗号角calltoarms
5费吵吵gigglingInventor
5费菌菇fungalmancer
3费荆棘Hench-ClanThug
刺杀花vilespineslayer
9费奇数bakuthemooneater

 实际操作

发现阴燃电鳗无法识别

https://github.com/chucklu/HearthBuddyAnalyze/tree/master/CardQuery

HearthBuddy卡牌无法识别
最新的处理方案是,直接引用第三方的类库来做卡牌的数据解析
分析(淘汰)
偶数萨电鳗狼王添加方法
 实际操作

使用工具,查询

阴燃电鳗的战吼效果和火元素类似,查找火元素的编号CS2_042,

HearthBuddy卡牌无法识别
最新的处理方案是,直接引用第三方的类库来做卡牌的数据解析
分析(淘汰)
偶数萨电鳗狼王添加方法
 实际操作

然后在solution explorer搜索这个编号,找到

HearthBuddy卡牌无法识别
最新的处理方案是,直接引用第三方的类库来做卡牌的数据解析
分析(淘汰)
偶数萨电鳗狼王添加方法
 实际操作

 注释居然是法语

    class Sim_CS2_042 : SimTemplate //fireelemental
    {

//    kampfschrei:/ verursacht 3 schaden.
        public override void getBattlecryEffect(Playfield p, Minion own, Minion target, int choice)
        {
            int dmg = 3;
            p.minionGetDamageOrHeal(target, dmg);
           
        }

    }

 public enum cardIDEnum 里面需要新加一个卡牌枚举

可能还需要加一个类似Pen_CS2_042.cs的 

在整个解决方案搜火元素的名字fireelemental

HearthBuddy卡牌无法识别
最新的处理方案是,直接引用第三方的类库来做卡牌的数据解析
分析(淘汰)
偶数萨电鳗狼王添加方法
 实际操作

 public enum cardName需要添加一个枚举,这里的cardName就是在下面记录伤害和记录血量的地方使用

public Dictionary<CardDB.cardName, int> DamageTargetDatabase = new Dictionary<CardDB.cardName, int>();  记录伤害的

Dictionary<CardDB.cardName, int> GangUpDatabase = new Dictionary<CardDB.cardName, int>(); 这个应该是卡牌的评分

 

GangUpDatabase.Add(CardDB.cardName.emperorthaurissan, 5);

{
"Id": "BRM_028",
"DbfId": 2262,
"Name": "索瑞森大帝",
"Text": "在你的回合结束时,你所有手牌的法力值消耗减少(1)点。",
"FlavorText": "把一个邪恶的炎魔之王召唤到这个世界上,然后看着这个家伙奴役了他所有的子民并非是他最为后悔的事情。",
"Class": "NEUTRAL",
"Rarity": "LEGENDARY",
"Type": "MINION",
"Race": "INVALID",
"Set": "BRM",
"Faction": "INVALID",
"Cost": 6,
"Attack": 5,
"Health": 5,
"Durability": 0,
"Armor": 0,
"Mechanics": [],
"ArtistName": "Wayne Reynolds",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": true
}


GangUpDatabase.Add(CardDB.cardName.etherealpeddler, 3);

{
"Id": "KAR_070",
"DbfId": 39700,
"Name": "虚灵商人",
"Text": "<b>战吼:</b>如果你的手牌中有其他职业的卡牌,则其法力值消耗减少(2)点。",
"FlavorText": "虚灵以热衷收集和买卖各种魔法物品和神器而著称,比那些贪婪的地精靠谱多了。",
"Class": "ROGUE",
"Rarity": "RARE",
"Type": "MINION",
"Race": "INVALID",
"Set": "KARA",
"Faction": "INVALID",
"Cost": 5,
"Attack": 5,
"Health": 6,
"Durability": 0,
"Armor": 0,
"Mechanics": [
"Battlecry"
],
"ArtistName": "Alex Horley Orlandelli",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": true
}


GangUpDatabase.Add(CardDB.cardName.felcannon, 0);

{
"Id": "GVG_020",
"DbfId": 1997,
"Name": "邪能火炮",
"Text": "在你的回合结束时,对一个非机械随从造成2点伤害。",
"FlavorText": "包装盒上写着:全新改良,比旧款更邪门!",
"Class": "WARLOCK",
"Rarity": "RARE",
"Type": "MINION",
"Race": "MECHANICAL",
"Set": "GVG",
"Faction": "INVALID",
"Cost": 4,
"Attack": 3,
"Health": 5,
"Durability": 0,
"Armor": 0,
"Mechanics": [],
"ArtistName": "Matt Gaser",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": true
}


GangUpDatabase.Add(CardDB.cardName.fireelemental, 5);

{
"Id": "CS2_042",
"DbfId": 189,
"Name": "火元素",
"Text": "<b>战吼:</b>造成3点伤害。",
"FlavorText": "他从来不洗澡。嗯...",
"Class": "SHAMAN",
"Rarity": "FREE",
"Type": "MINION",
"Race": "ELEMENTAL",
"Set": "CORE",
"Faction": "NEUTRAL",
"Cost": 6,
"Attack": 6,
"Health": 5,
"Durability": 0,
"Armor": 0,
"Mechanics": [
"Battlecry"
],
"ArtistName": "Ralph Horsley",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": false
}


GangUpDatabase.Add(CardDB.cardName.fireguarddestroyer, 4);

{
"Id": "BRM_012",
"DbfId": 2290,
"Name": "火焰驱逐者",
"Text": "<b>战吼:</b>获得1-4点攻击力。<b>过载:</b>(1)",
"FlavorText": "很多火元素竞相面试“火焰驱逐者”的职位,但拉格纳罗斯认为只有极少数火元素能够胜任。",
"Class": "SHAMAN",
"Rarity": "COMMON",
"Type": "MINION",
"Race": "ELEMENTAL",
"Set": "BRM",
"Faction": "INVALID",
"Cost": 4,
"Attack": 3,
"Health": 6,
"Durability": 0,
"Armor": 0,
"Mechanics": [
"Battlecry"
],
"ArtistName": "Paul Mafayon",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": true
}


GangUpDatabase.Add(CardDB.cardName.flametonguetotem, 4);

{
"Id": "EX1_565",
"DbfId": 1008,
"Name": "火舌图腾",
"Text": "相邻的随从获得+2攻击力。",
"FlavorText": "图腾制造师喜欢用最稀有的木材来打造图腾。甚至有传言说,这些图腾是由埃隆巴克保护者身上的树皮做的。",
"Class": "SHAMAN",
"Rarity": "FREE",
"Type": "MINION",
"Race": "TOTEM",
"Set": "CORE",
"Faction": "NEUTRAL",
"Cost": 3,
"Attack": 0,
"Health": 3,
"Durability": 0,
"Armor": 0,
"Mechanics": [],
"ArtistName": "Jonathan Ryder",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": false
}


GangUpDatabase.Add(CardDB.cardName.flamewaker, 4);

{
"Id": "BRM_002",
"DbfId": 2275,
"Name": "火妖",
"Text": "在你施放一个法术后,造成2点伤害,随机分配到所有敌人身上。",
"FlavorText": "他们深居于炎热的洞窟之中,听候管理者的命令:消灭一切胆敢打扰炎魔之王的敌人。",
"Class": "MAGE",
"Rarity": "RARE",
"Type": "MINION",
"Race": "INVALID",
"Set": "BRM",
"Faction": "INVALID",
"Cost": 3,
"Attack": 2,
"Health": 4,
"Durability": 0,
"Armor": 0,
"Mechanics": [],
"ArtistName": "Alex Horley Orlandelli",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": true
}


GangUpDatabase.Add(CardDB.cardName.flesheatingghoul, 0);

{
"Id": "tt_004",
"DbfId": 397,
"Name": "腐肉食尸鬼",
"Text": "每当一个随从死亡,便获得+1攻击力。",
"FlavorText": "诟病食尸鬼吃“腐肉”其实对它们并不公平,它们只是没有别的可吃了而已。",
"Class": "NEUTRAL",
"Rarity": "COMMON",
"Type": "MINION",
"Race": "INVALID",
"Set": "EXPERT1",
"Faction": "NEUTRAL",
"Cost": 3,
"Attack": 2,
"Health": 3,
"Durability": 0,
"Armor": 0,
"Mechanics": [],
"ArtistName": "Alex Horley Orlandelli",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": false
}


GangUpDatabase.Add(CardDB.cardName.floatingwatcher, 0);  漂浮观察者,术士卡,544

{
"Id": "GVG_100",
"DbfId": 2068,
"Name": "漂浮观察者",
"Text": "每当你的英雄在你的回合受到伤害,便获得+2/+2。",
"FlavorText": "和他对话时,你很难与之进行眼神交流。",
"Class": "WARLOCK",
"Rarity": "COMMON",
"Type": "MINION",
"Race": "DEMON",
"Set": "GVG",
"Faction": "INVALID",
"Cost": 5,
"Attack": 4,
"Health": 4,
"Durability": 0,
"Armor": 0,
"Mechanics": [],
"ArtistName": "Todd Lockwood",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": true
}


GangUpDatabase.Add(CardDB.cardName.foereaper4000, 1);   死神4000型,中立卡

{
"Id": "GVG_113",
"DbfId": 2081,
"Name": "死神4000型",
"Text": "同时对其攻击目标相邻的随从造成伤害。",
"FlavorText": "对于田里的庄稼来说,所有的收割机都是死神。",
"Class": "NEUTRAL",
"Rarity": "LEGENDARY",
"Type": "MINION",
"Race": "MECHANICAL",
"Set": "GVG",
"Faction": "INVALID",
"Cost": 8,
"Attack": 6,
"Health": 9,
"Durability": 0,
"Armor": 0,
"Mechanics": [],
"ArtistName": "James Ryman",
"EntourageCardIds": [],
"DefaultLanguage": "zhCN",
"Collectible": true,
"IsWild": true
}