Esempio n. 1
0
        public void Handle(string logLine, IHsGameState gameState, IGame game)
        {
            if (logLine.Contains("CREATE_GAME"))
            {
                gameState.GameHandler.HandleGameStart();
                gameState.GameEnded     = false;
                gameState.AddToTurn     = -1;
                gameState.GameLoaded    = true;
                gameState.LastGameStart = DateTime.Now;
            }
            else if (HsLogReaderConstants.GameEntityRegex.IsMatch(logLine))
            {
                gameState.GameHandler.HandleGameStart();
                gameState.GameEnded = false;
                gameState.AddToTurn = -1;
                var match = HsLogReaderConstants.GameEntityRegex.Match(logLine);
                var id    = int.Parse(match.Groups["id"].Value);
                if (!game.Entities.ContainsKey(id))
                {
                    game.Entities.Add(id, new Entity(id));
                }
                gameState.CurrentEntityId = id;
            }
            else if (HsLogReaderConstants.PlayerEntityRegex.IsMatch(logLine))
            {
                var match = HsLogReaderConstants.PlayerEntityRegex.Match(logLine);
                var id    = int.Parse(match.Groups["id"].Value);
                if (!game.Entities.ContainsKey(id))
                {
                    game.Entities.Add(id, new Entity(id));
                }
                gameState.CurrentEntityId = id;
            }
            else if (HsLogReaderConstants.TagChangeRegex.IsMatch(logLine))
            {
                var match     = HsLogReaderConstants.TagChangeRegex.Match(logLine);
                var rawEntity = match.Groups["entity"].Value.Replace("UNKNOWN ENTITY ", "");
                int entityId;
                if (rawEntity.StartsWith("[") && HsLogReaderConstants.EntityRegex.IsMatch(rawEntity))
                {
                    var entity = HsLogReaderConstants.EntityRegex.Match(rawEntity);
                    var id     = int.Parse(entity.Groups["id"].Value);
                    _tagChangeHandler.TagChange(gameState, match.Groups["tag"].Value, id, match.Groups["value"].Value, game);
                }
                else if (int.TryParse(rawEntity, out entityId))
                {
                    _tagChangeHandler.TagChange(gameState, match.Groups["tag"].Value, entityId, match.Groups["value"].Value, game);
                }
                else
                {
                    var entity = game.Entities.FirstOrDefault(x => x.Value.Name == rawEntity);
                    if (entity.Value == null)
                    {
                        //while the id is unknown, store in tmp entities
                        var tmpEntity = _tmpEntities.FirstOrDefault(x => x.Name == rawEntity);
                        if (tmpEntity == null)
                        {
                            tmpEntity      = new Entity(_tmpEntities.Count + 1);
                            tmpEntity.Name = rawEntity;
                            _tmpEntities.Add(tmpEntity);
                        }
                        GAME_TAG tag;
                        Enum.TryParse(match.Groups["tag"].Value, out tag);
                        var value = HsLogReaderV2.ParseTagValue(tag, match.Groups["value"].Value);
                        tmpEntity.SetTag(tag, value);
                        if (tmpEntity.HasTag(GAME_TAG.ENTITY_ID))
                        {
                            var id = tmpEntity.GetTag(GAME_TAG.ENTITY_ID);
                            if (game.Entities.ContainsKey(id))
                            {
                                game.Entities[id].Name = tmpEntity.Name;
                                foreach (var t in tmpEntity.Tags)
                                {
                                    game.Entities[id].SetTag(t.Key, t.Value);
                                }
                                _tmpEntities.Remove(tmpEntity);
                                //Logger.WriteLine("COPIED TMP ENTITY (" + rawEntity + ")");
                            }
                            else
                            {
                                Logger.WriteLine(
                                    "TMP ENTITY (" + rawEntity + ") NOW HAS A KEY, BUT GAME.ENTITIES DOES NOT CONTAIN THIS KEY",
                                    "LogReader");
                            }
                        }
                    }
                    else
                    {
                        _tagChangeHandler.TagChange(gameState, match.Groups["tag"].Value, entity.Key, match.Groups["value"].Value, game);
                    }
                }

                if (HsLogReaderConstants.EntityNameRegex.IsMatch(logLine))
                {
                    match = HsLogReaderConstants.EntityNameRegex.Match(logLine);
                    var name   = match.Groups["name"].Value;
                    var player = int.Parse(match.Groups["value"].Value);
                    if (player == 1)
                    {
                        gameState.GameHandler.HandlePlayerName(name);
                    }
                    else if (player == 2)
                    {
                        gameState.GameHandler.HandleOpponentName(name);
                    }
                }
            }
            else if (HsLogReaderConstants.CreationRegex.IsMatch(logLine))
            {
                var match  = HsLogReaderConstants.CreationRegex.Match(logLine);
                var id     = int.Parse(match.Groups["id"].Value);
                var cardId = match.Groups["cardId"].Value;
                if (!game.Entities.ContainsKey(id))
                {
                    if (string.IsNullOrEmpty(cardId))
                    {
                        if (gameState.KnownCardIds.TryGetValue(id, out cardId))
                        {
                            Logger.WriteLine(string.Format("Found known cardId for entity {0}: {1}", id, cardId));
                            gameState.KnownCardIds.Remove(id);
                        }
                    }
                    game.Entities.Add(id, new Entity(id)
                    {
                        CardId = cardId
                    });
                }
                gameState.CurrentEntityId        = id;
                gameState.CurrentEntityHasCardId = !string.IsNullOrEmpty(cardId);
            }
            else if (HsLogReaderConstants.UpdatingEntityRegex.IsMatch(logLine))
            {
                var match     = HsLogReaderConstants.UpdatingEntityRegex.Match(logLine);
                var cardId    = match.Groups["cardId"].Value;
                var rawEntity = match.Groups["entity"].Value;
                int entityId;
                if (rawEntity.StartsWith("[") && HsLogReaderConstants.EntityRegex.IsMatch(rawEntity))
                {
                    var entity = HsLogReaderConstants.EntityRegex.Match(rawEntity);
                    entityId = int.Parse(entity.Groups["id"].Value);
                }
                else if (!int.TryParse(rawEntity, out entityId))
                {
                    entityId = -1;
                }
                if (entityId != -1)
                {
                    gameState.CurrentEntityId = entityId;
                    if (!game.Entities.ContainsKey(entityId))
                    {
                        game.Entities.Add(entityId, new Entity(entityId));
                    }
                    game.Entities[entityId].CardId = cardId;
                }
                if (gameState.JoustReveals > 0)
                {
                    Entity currentEntity;
                    if (game.Entities.TryGetValue(entityId, out currentEntity))
                    {
                        if (currentEntity.IsControlledBy(game.Opponent.Id))
                        {
                            gameState.GameHandler.HandleOpponentJoust(currentEntity, cardId, gameState.GetTurnNumber());
                        }
                        else if (currentEntity.IsControlledBy(game.Player.Id))
                        {
                            gameState.GameHandler.HandlePlayerJoust(currentEntity, cardId, gameState.GetTurnNumber());
                        }
                    }
                    //gameState.JoustReveals--;
                }
            }
            else if (HsLogReaderConstants.CreationTagRegex.IsMatch(logLine) && !logLine.Contains("HIDE_ENTITY"))
            {
                var match = HsLogReaderConstants.CreationTagRegex.Match(logLine);
                _tagChangeHandler.TagChange(gameState, match.Groups["tag"].Value, gameState.CurrentEntityId, match.Groups["value"].Value, game);
            }
            else if ((logLine.Contains("Begin Spectating") || logLine.Contains("Start Spectator")) && game.IsInMenu)
            {
                gameState.GameHandler.SetGameMode(GameMode.Spectator);
            }
            else if (logLine.Contains("End Spectator"))
            {
                gameState.GameHandler.SetGameMode(GameMode.Spectator);
                gameState.GameHandler.HandleGameEnd();
            }
            else if (HsLogReaderConstants.ActionStartRegex.IsMatch(logLine))
            {
                var playerEntity =
                    game.Entities.FirstOrDefault(
                        e => e.Value.HasTag(GAME_TAG.PLAYER_ID) && e.Value.GetTag(GAME_TAG.PLAYER_ID) == game.Player.Id);
                var opponentEntity =
                    game.Entities.FirstOrDefault(
                        e => e.Value.HasTag(GAME_TAG.PLAYER_ID) && e.Value.GetTag(GAME_TAG.PLAYER_ID) == game.Opponent.Id);

                var match = HsLogReaderConstants.ActionStartRegex.Match(logLine);
                var actionStartingCardId   = match.Groups["cardId"].Value.Trim();
                var actionStartingEntityId = int.Parse(match.Groups["id"].Value);

                if (string.IsNullOrEmpty(actionStartingCardId))
                {
                    Entity tmpEntity;
                    if (game.Entities.TryGetValue(actionStartingEntityId, out tmpEntity))
                    {
                        actionStartingCardId = tmpEntity.CardId;
                    }
                }
                if (!string.IsNullOrEmpty(actionStartingCardId))
                {
                    if (actionStartingCardId == "BRM_007") //Gang Up
                    {
                        //if (playerEntity.Value != null && playerEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) == 1)
                        //{
                        var target = match.Groups["target"].Value.Trim();
                        if (target.StartsWith("[") && HsLogReaderConstants.EntityRegex.IsMatch(target))
                        {
                            var cardIdMatch = HsLogReaderConstants.CardIdRegex.Match(target);
                            if (cardIdMatch.Success)
                            {
                                var targetCardId = cardIdMatch.Groups["cardId"].Value.Trim();

                                for (int i = 0; i < 3; i++)
                                {
                                    var id = game.Entities.Count + i + 1;
                                    if (!gameState.KnownCardIds.ContainsKey(id))
                                    {
                                        gameState.KnownCardIds.Add(id, targetCardId);
                                    }
                                }
                            }
                        }
                        //}
                    }
                    else if (actionStartingCardId == "GVG_056") //Iron Juggernaut
                    {
                        // burrowing mine will be the entity created next
                        int id = game.Entities.Count + 1;
                        //if(playerEntity.Value == null || playerEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) != 1)
                        //{
                        if (!gameState.KnownCardIds.ContainsKey(id))
                        {
                            gameState.KnownCardIds.Add(id, "GVG_056t");
                        }
                        //}
                    }
                    else if (actionStartingCardId == "GVG_031") //Recycle
                    {
                        // Recycled card will be the entity created next
                        int id = game.Entities.Count + 1;
                        //if(playerEntity.Value == null || playerEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) != 1)
                        //{
                        gameState.ProposeKeyPoint(KeyPointType.CreateToDeck, id, ActivePlayer.Player);
                        var target = match.Groups["target"].Value.Trim();
                        if (target.StartsWith("[") && HsLogReaderConstants.EntityRegex.IsMatch(target))
                        {
                            var cardIdMatch = HsLogReaderConstants.CardIdRegex.Match(target);
                            if (cardIdMatch.Success)
                            {
                                var targetCardId = cardIdMatch.Groups["cardId"].Value.Trim();
                                if (!gameState.KnownCardIds.ContainsKey(id))
                                {
                                    gameState.KnownCardIds.Add(id, targetCardId);
                                }
                            }
                        }
                        //}
                    }
                    else if (actionStartingCardId == "GVG_035") //Malorne
                    {
                        // Malorne will be the entity created next
                        int id = game.Entities.Count + 1;
                        //if(playerEntity.Value == null || playerEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) != 1)
                        //{
                        if (!gameState.KnownCardIds.ContainsKey(id))
                        {
                            gameState.KnownCardIds.Add(id, "GVG_035");
                        }
                        // }
                    }


                    else
                    {
                        if (playerEntity.Value != null && playerEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) == 1 &&
                            !gameState.PlayerUsedHeroPower ||
                            opponentEntity.Value != null && opponentEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) == 1 &&
                            !gameState.OpponentUsedHeroPower)
                        {
                            var card = Database.GetCardFromId(actionStartingCardId);
                            if (card.Type == "Hero Power")
                            {
                                if (playerEntity.Value != null && playerEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) == 1)
                                {
                                    gameState.GameHandler.HandlePlayerHeroPower(actionStartingCardId, gameState.GetTurnNumber());
                                    gameState.PlayerUsedHeroPower = true;
                                }
                                else if (opponentEntity.Value != null)
                                {
                                    gameState.GameHandler.HandleOpponentHeroPower(actionStartingCardId, gameState.GetTurnNumber());
                                    gameState.OpponentUsedHeroPower = true;
                                }
                            }
                        }
                    }
                }
            }
            else if (logLine.Contains("BlockType=JOUST"))
            {
                gameState.JoustReveals = 2;
            }
        }
Esempio n. 2
0
        public void TagChange(IHsGameState gameState, string rawTag, int id, string rawValue, IGame game, bool isRecursive = false)
        {
            if (gameState.LastId != id)
            {
                //game.SecondToLastUsedId = gameState.LastId;
                if (gameState.ProposedKeyPoint != null)
                {
                    ReplayMaker.Generate(gameState.ProposedKeyPoint.Type, gameState.ProposedKeyPoint.Id, gameState.ProposedKeyPoint.Player, game);
                    gameState.ProposedKeyPoint = null;
                }
            }
            gameState.LastId = id;
            if (!game.Entities.ContainsKey(id))
            {
                game.Entities.Add(id, new Entity(id));
            }
            GAME_TAG tag;

            if (!Enum.TryParse(rawTag, out tag))
            {
                int tmp;
                if (int.TryParse(rawTag, out tmp) && Enum.IsDefined(typeof(GAME_TAG), tmp))
                {
                    tag = (GAME_TAG)tmp;
                }
            }
            var value     = HsLogReaderV2.ParseTagValue(tag, rawValue);
            var prevValue = game.Entities[id].GetTag(tag);

            game.Entities[id].SetTag(tag, value);

            if (tag == GAME_TAG.CONTROLLER && gameState.WaitForController != null && game.Player.Id == -1)
            {
                var p1 = game.Entities.FirstOrDefault(e => e.Value.GetTag(GAME_TAG.PLAYER_ID) == 1).Value;
                var p2 = game.Entities.FirstOrDefault(e => e.Value.GetTag(GAME_TAG.PLAYER_ID) == 2).Value;
                if (gameState.CurrentEntityHasCardId)
                {
                    if (p1 != null)
                    {
                        p1.IsPlayer = value == 1;
                    }
                    if (p2 != null)
                    {
                        p2.IsPlayer = value != 1;
                    }
                    game.Player.Id   = value;
                    game.Opponent.Id = value % 2 + 1;
                }
                else
                {
                    if (p1 != null)
                    {
                        p1.IsPlayer = value != 1;
                    }
                    if (p2 != null)
                    {
                        p2.IsPlayer = value == 1;
                    }
                    game.Player.Id   = value % 2 + 1;
                    game.Opponent.Id = value;
                }
            }
            var controller = game.Entities[id].GetTag(GAME_TAG.CONTROLLER);
            var player     = game.Entities[id].HasTag(GAME_TAG.CONTROLLER) ? (controller == game.Player.Id ? "FRIENDLY" : "OPPOSING") : "";
            var cardId     = game.Entities[id].CardId;

            if (tag == GAME_TAG.ZONE)
            {
                if (((TAG_ZONE)value == TAG_ZONE.HAND || ((TAG_ZONE)value == TAG_ZONE.PLAY || (TAG_ZONE)value == TAG_ZONE.DECK) && game.IsMulliganDone) && gameState.WaitForController == null)
                {
                    if (!game.IsMulliganDone)
                    {
                        prevValue = (int)TAG_ZONE.DECK;
                    }
                    if (controller == 0)
                    {
                        game.Entities[id].SetTag(GAME_TAG.ZONE, prevValue);
                        gameState.WaitForController = new { Tag = rawTag, Id = id, Value = rawValue };
                        return;
                    }
                }
                switch ((TAG_ZONE)prevValue)
                {
                case TAG_ZONE.DECK:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.HAND:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerDraw(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Draw, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            if (!string.IsNullOrEmpty(game.Entities[id].CardId))
                            {
#if DEBUG
                                Logger.WriteLine(string.Format("Opponent Draw (EntityID={0}) already has a CardID. Removing. Blizzard Pls.", id), "TagChange");
#endif
                                game.Entities[id].CardId = string.Empty;
                            }
                            gameState.GameHandler.HandleOpponentDraw(game.Entities[id], gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Draw, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.SETASIDE:
                    case TAG_ZONE.REMOVEDFROMGAME:
                        if (controller == game.Player.Id)
                        {
                            if (gameState.JoustReveals > 0)
                            {
                                gameState.JoustReveals--;
                                break;
                            }
                            gameState.GameHandler.HandlePlayerRemoveFromDeck(game.Entities[id], gameState.GetTurnNumber());
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            if (gameState.JoustReveals > 0)
                            {
                                gameState.JoustReveals--;
                                break;
                            }
                            gameState.GameHandler.HandleOpponentRemoveFromDeck(game.Entities[id], gameState.GetTurnNumber());
                        }
                        break;

                    case TAG_ZONE.GRAVEYARD:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerDeckDiscard(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.DeckDiscard, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentDeckDiscard(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.DeckDiscard, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.PLAY:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerDeckToPlay(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.DeckDiscard, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentDeckToPlay(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.DeckDiscard, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.SECRET:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerSecretPlayed(game.Entities[id], cardId, gameState.GetTurnNumber(), true);
                            gameState.ProposeKeyPoint(KeyPointType.SecretPlayed, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentSecretPlayed(game.Entities[id], cardId, -1, gameState.GetTurnNumber(), true, id);
                            gameState.ProposeKeyPoint(KeyPointType.SecretPlayed, id, ActivePlayer.Player);
                        }
                        break;

                    default:
                        Logger.WriteLine(string.Format("WARNING - unhandled zone change (id={0}): {1} -> {2}", id, (TAG_ZONE)prevValue, (TAG_ZONE)value), "TagChange");
                        break;
                    }
                    break;

                case TAG_ZONE.HAND:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.PLAY:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerPlay(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Play, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentPlay(game.Entities[id], cardId, game.Entities[id].GetTag(GAME_TAG.ZONE_POSITION), gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Play, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.REMOVEDFROMGAME:
                    case TAG_ZONE.GRAVEYARD:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerHandDiscard(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.HandDiscard, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentHandDiscard(game.Entities[id], cardId, game.Entities[id].GetTag(GAME_TAG.ZONE_POSITION), gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.HandDiscard, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.SECRET:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerSecretPlayed(game.Entities[id], cardId, gameState.GetTurnNumber(), false);
                            gameState.ProposeKeyPoint(KeyPointType.SecretPlayed, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentSecretPlayed(game.Entities[id], cardId, game.Entities[id].GetTag(GAME_TAG.ZONE_POSITION), gameState.GetTurnNumber(), false, id);
                            gameState.ProposeKeyPoint(KeyPointType.SecretPlayed, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.DECK:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerMulligan(game.Entities[id], cardId);
                            gameState.ProposeKeyPoint(KeyPointType.Mulligan, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentMulligan(game.Entities[id], game.Entities[id].GetTag(GAME_TAG.ZONE_POSITION));
                            gameState.ProposeKeyPoint(KeyPointType.Mulligan, id, ActivePlayer.Opponent);
                        }
                        break;

                    default:
                        Logger.WriteLine(string.Format("WARNING - unhandled zone change (id={0}): {1} -> {2}", id, (TAG_ZONE)prevValue, (TAG_ZONE)value), "TagChange");
                        break;
                    }
                    break;

                case TAG_ZONE.PLAY:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.HAND:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerBackToHand(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.PlayToHand, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentPlayToHand(game.Entities[id], cardId, gameState.GetTurnNumber(), id);
                            gameState.ProposeKeyPoint(KeyPointType.PlayToHand, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.DECK:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerPlayToDeck(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.PlayToDeck, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentPlayToDeck(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.PlayToDeck, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.GRAVEYARD:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerPlayToGraveyard(game.Entities[id], cardId,
                                                                              gameState.GetTurnNumber());
                            if (game.Entities[id].HasTag(GAME_TAG.HEALTH))
                            {
                                gameState.ProposeKeyPoint(KeyPointType.Death, id, ActivePlayer.Player);
                            }
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentPlayToGraveyard(game.Entities[id], cardId,
                                                                                gameState.GetTurnNumber(), gameState.PlayersTurn());
                            if (game.Entities[id].HasTag(GAME_TAG.HEALTH))
                            {
                                gameState.ProposeKeyPoint(KeyPointType.Death, id, ActivePlayer.Opponent);
                            }
                        }
                        break;

                    default:
                        Logger.WriteLine(string.Format("WARNING - unhandled zone change (id={0}): {1} -> {2}", id, (TAG_ZONE)prevValue, (TAG_ZONE)value), "TagChange");
                        break;
                    }
                    break;

                case TAG_ZONE.SECRET:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.SECRET:
                    case TAG_ZONE.GRAVEYARD:
                        if (controller == game.Player.Id)
                        {
                            gameState.ProposeKeyPoint(KeyPointType.SecretTriggered, id, ActivePlayer.Player);
                        }
                        if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentSecretTrigger(game.Entities[id], cardId, gameState.GetTurnNumber(), id);
                            gameState.ProposeKeyPoint(KeyPointType.SecretTriggered, id, ActivePlayer.Opponent);
                        }
                        break;

                    default:
                        Logger.WriteLine(string.Format("WARNING - unhandled zone change (id={0}): {1} -> {2}", id, (TAG_ZONE)prevValue, (TAG_ZONE)value), "TagChange");
                        break;
                    }
                    break;

                case TAG_ZONE.GRAVEYARD:
                case TAG_ZONE.SETASIDE:
                case TAG_ZONE.CREATED:
                case TAG_ZONE.INVALID:
                case TAG_ZONE.REMOVEDFROMGAME:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.PLAY:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerCreateInPlay(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Summon, id, ActivePlayer.Player);
                        }
                        if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentCreateInPlay(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Summon, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.DECK:
                        if (controller == game.Player.Id)
                        {
                            if (gameState.JoustReveals > 0)
                            {
                                break;
                            }
                            gameState.GameHandler.HandlePlayerGetToDeck(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.CreateToDeck, id, ActivePlayer.Player);
                        }
                        if (controller == game.Opponent.Id)
                        {
                            if (gameState.JoustReveals > 0)
                            {
                                break;
                            }
                            gameState.GameHandler.HandleOpponentGetToDeck(game.Entities[id], gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.CreateToDeck, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.HAND:
                        if (controller == game.Player.Id)
                        {
                            gameState.GameHandler.HandlePlayerGet(game.Entities[id], cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Obtain, id, ActivePlayer.Player);
                        }
                        else if (controller == game.Opponent.Id)
                        {
                            gameState.GameHandler.HandleOpponentGet(game.Entities[id], gameState.GetTurnNumber(), id);
                            gameState.ProposeKeyPoint(KeyPointType.Obtain, id, ActivePlayer.Opponent);
                        }
                        break;

                    default:
                        Logger.WriteLine(string.Format("WARNING - unhandled zone change (id={0}): {1} -> {2}", id, (TAG_ZONE)prevValue, (TAG_ZONE)value), "TagChange");
                        break;
                    }
                    break;

                default:
                    Logger.WriteLine(string.Format("WARNING - unhandled zone change (id={0}): {1} -> {2}", id, (TAG_ZONE)prevValue, (TAG_ZONE)value), "TagChange");
                    break;
                }
            }
            else if (tag == GAME_TAG.PLAYSTATE)
            {
                if (value == (int)TAG_PLAYSTATE.CONCEDED)
                {
                    gameState.GameHandler.HandleConcede();
                }
                if (!gameState.GameEnded)
                {
                    if (game.Entities[id].IsPlayer)
                    {
                        if (value == (int)TAG_PLAYSTATE.WON)
                        {
                            gameState.GameEndKeyPoint(true, id);
                            gameState.GameHandler.HandleWin();
                            gameState.GameHandler.HandleGameEnd();
                            gameState.GameEnded = true;
                        }
                        else if (value == (int)TAG_PLAYSTATE.LOST)
                        {
                            gameState.GameEndKeyPoint(false, id);
                            gameState.GameHandler.HandleLoss();
                            gameState.GameHandler.HandleGameEnd();
                            gameState.GameEnded = true;
                        }
                        else if (value == (int)TAG_PLAYSTATE.TIED)
                        {
                            gameState.GameEndKeyPoint(false, id);
                            gameState.GameHandler.HandleTied();
                            gameState.GameHandler.HandleGameEnd();
                        }
                    }
                }
            }
            else if (tag == GAME_TAG.CURRENT_PLAYER && value == 1)
            {
                var activePlayer = game.Entities[id].IsPlayer ? ActivePlayer.Player : ActivePlayer.Opponent;
                gameState.GameHandler.TurnStart(activePlayer, gameState.GetTurnNumber());
                if (activePlayer == ActivePlayer.Player)
                {
                    gameState.PlayerUsedHeroPower = false;
                }
                else
                {
                    gameState.OpponentUsedHeroPower = false;
                }
            }
            else if (tag == GAME_TAG.LAST_CARD_PLAYED)
            {
                gameState.LastCardPlayed = value;
            }
            else if (tag == GAME_TAG.DEFENDING)
            {
                if (player == "OPPOSING")
                {
                    gameState.GameHandler.HandleDefendingEntity(value == 1 ? game.Entities[id] : null);
                }
            }
            else if (tag == GAME_TAG.ATTACKING)
            {
                if (player == "FRIENDLY")
                {
                    gameState.GameHandler.HandleAttackingEntity(value == 1 ? game.Entities[id] : null);
                }
            }
            else if (tag == GAME_TAG.PROPOSED_DEFENDER)
            {
                game.OpponentSecrets.ProposedDefenderEntityId = value;
            }
            else if (tag == GAME_TAG.PROPOSED_ATTACKER)
            {
                game.OpponentSecrets.ProposedAttackerEntityId = value;
            }
            else if (tag == GAME_TAG.NUM_MINIONS_PLAYED_THIS_TURN && value > 0)
            {
                if (gameState.PlayersTurn())
                {
                    gameState.GameHandler.HandlePlayerMinionPlayed();
                }
            }
            else if (tag == GAME_TAG.PREDAMAGE && value > 0)
            {
                if (gameState.PlayersTurn())
                {
                    gameState.GameHandler.HandleOpponentDamage(game.Entities[id]);
                }
            }
            else if (tag == GAME_TAG.NUM_TURNS_IN_PLAY && value > 0)
            {
                if (!gameState.PlayersTurn())
                {
                    gameState.GameHandler.HandleOpponentTurnStart(game.Entities[id]);
                }
            }
            else if (tag == GAME_TAG.NUM_ATTACKS_THIS_TURN && value > 0)
            {
                if (controller == game.Player.Id)
                {
                    gameState.ProposeKeyPoint(KeyPointType.Attack, id, ActivePlayer.Player);
                }
                else if (controller == game.Opponent.Id)
                {
                    gameState.ProposeKeyPoint(KeyPointType.Attack, id, ActivePlayer.Opponent);
                }
            }
            else if (tag == GAME_TAG.ZONE_POSITION)
            {
                var zone = game.Entities[id].GetTag(GAME_TAG.ZONE);
                if (zone == (int)TAG_ZONE.HAND)
                {
                    if (controller == game.Player.Id)
                    {
                        ReplayMaker.Generate(KeyPointType.HandPos, id, ActivePlayer.Player, game);
                        gameState.GameHandler.HandleZonePositionUpdate(ActivePlayer.Player, TAG_ZONE.HAND, gameState.GetTurnNumber());
                    }
                    else if (controller == game.Opponent.Id)
                    {
                        ReplayMaker.Generate(KeyPointType.HandPos, id, ActivePlayer.Opponent, game);
                        gameState.GameHandler.HandleZonePositionUpdate(ActivePlayer.Opponent, TAG_ZONE.HAND, gameState.GetTurnNumber());
                    }
                }
                else if (zone == (int)TAG_ZONE.PLAY)
                {
                    if (controller == game.Player.Id)
                    {
                        ReplayMaker.Generate(KeyPointType.BoardPos, id, ActivePlayer.Player, game);
                        gameState.GameHandler.HandleZonePositionUpdate(ActivePlayer.Player, TAG_ZONE.PLAY, gameState.GetTurnNumber());
                    }
                    else if (controller == game.Opponent.Id)
                    {
                        ReplayMaker.Generate(KeyPointType.BoardPos, id, ActivePlayer.Opponent, game);
                        gameState.GameHandler.HandleZonePositionUpdate(ActivePlayer.Opponent, TAG_ZONE.PLAY, gameState.GetTurnNumber());
                    }
                }
            }
            else if (tag == GAME_TAG.CARD_TARGET && value > 0)
            {
                if (controller == game.Player.Id)
                {
                    gameState.ProposeKeyPoint(KeyPointType.PlaySpell, id, ActivePlayer.Player);
                }
                else if (controller == game.Opponent.Id)
                {
                    gameState.ProposeKeyPoint(KeyPointType.PlaySpell, id, ActivePlayer.Opponent);
                }
            }
            else if (tag == GAME_TAG.EQUIPPED_WEAPON && value == 0)
            {
                if (controller == game.Player.Id)
                {
                    gameState.ProposeKeyPoint(KeyPointType.WeaponDestroyed, id, ActivePlayer.Player);
                }
                else if (controller == game.Opponent.Id)
                {
                    gameState.ProposeKeyPoint(KeyPointType.WeaponDestroyed, id, ActivePlayer.Opponent);
                }
            }
            else if (tag == GAME_TAG.EXHAUSTED && value > 0)
            {
                if (game.Entities[id].GetTag(GAME_TAG.CARDTYPE) == (int)TAG_CARDTYPE.HERO_POWER)
                {
                    if (controller == game.Player.Id)
                    {
                        gameState.ProposeKeyPoint(KeyPointType.HeroPower, id, ActivePlayer.Player);
                    }
                    else if (controller == game.Opponent.Id)
                    {
                        gameState.ProposeKeyPoint(KeyPointType.HeroPower, id, ActivePlayer.Opponent);
                    }
                }
            }
            else if (tag == GAME_TAG.CONTROLLER && prevValue > 0)
            {
                if (value == game.Player.Id)
                {
                    if (game.Entities[id].IsInZone(TAG_ZONE.SECRET))
                    {
                        gameState.GameHandler.HandleOpponentStolen(game.Entities[id], cardId, gameState.GetTurnNumber());
                        gameState.ProposeKeyPoint(KeyPointType.SecretStolen, id, ActivePlayer.Player);
                    }
                    else if (game.Entities[id].IsInZone(TAG_ZONE.PLAY))
                    {
                        gameState.GameHandler.HandleOpponentStolen(game.Entities[id], cardId, gameState.GetTurnNumber());
                    }
                }
                else if (value == game.Opponent.Id)
                {
                    if (game.Entities[id].IsInZone(TAG_ZONE.SECRET))
                    {
                        gameState.GameHandler.HandleOpponentStolen(game.Entities[id], cardId, gameState.GetTurnNumber());
                        gameState.ProposeKeyPoint(KeyPointType.SecretStolen, id, ActivePlayer.Player);
                    }
                    else if (game.Entities[id].IsInZone(TAG_ZONE.PLAY))
                    {
                        gameState.GameHandler.HandlePlayerStolen(game.Entities[id], cardId, gameState.GetTurnNumber());
                    }
                }
            }
            else if (tag == GAME_TAG.FATIGUE)
            {
                if (controller == game.Player.Id)
                {
                    gameState.GameHandler.HandlePlayerFatigue(Convert.ToInt32(rawValue));
                }
                else if (controller == game.Opponent.Id)
                {
                    gameState.GameHandler.HandleOpponentFatigue(Convert.ToInt32(rawValue));
                }
            }
            if (gameState.WaitForController != null)
            {
                if (!isRecursive)
                {
                    TagChange(gameState, (string)gameState.WaitForController.Tag, (int)gameState.WaitForController.Id, (string)gameState.WaitForController.Value, game, true);
                    gameState.WaitForController = null;
                }
            }
        }
        public void Handle(string logLine, IHsGameState gameState, IGame game)
        {
            if (logLine.Contains("CREATE_GAME"))
            {
                gameState.GameHandler.HandleGameStart();
                gameState.GameEnded     = false;
                gameState.AddToTurn     = -1;
                gameState.GameLoaded    = true;
                gameState.LastGameStart = DateTime.Now;
            }
            else if (HsLogReaderConstants.PowerTaskList.GameEntityRegex.IsMatch(logLine))
            {
                gameState.GameHandler.HandleGameStart();
                gameState.GameEnded = false;
                gameState.AddToTurn = -1;
                var match = HsLogReaderConstants.PowerTaskList.GameEntityRegex.Match(logLine);
                var id    = int.Parse(match.Groups["id"].Value);
                if (!game.Entities.ContainsKey(id))
                {
                    game.Entities.Add(id, new Entity(id));
                }
                gameState.CurrentEntityId = id;
            }
            else if (HsLogReaderConstants.PowerTaskList.PlayerEntityRegex.IsMatch(logLine))
            {
                var match = HsLogReaderConstants.PowerTaskList.PlayerEntityRegex.Match(logLine);
                var id    = int.Parse(match.Groups["id"].Value);
                if (!game.Entities.ContainsKey(id))
                {
                    game.Entities.Add(id, new Entity(id));
                }
                gameState.CurrentEntityId = id;
            }
            else if (HsLogReaderConstants.PowerTaskList.TagChangeRegex.IsMatch(logLine))
            {
                var match     = HsLogReaderConstants.PowerTaskList.TagChangeRegex.Match(logLine);
                var rawEntity = match.Groups["entity"].Value.Replace("UNKNOWN ENTITY ", "");
                int entityId;
                if (rawEntity.StartsWith("[") && HsLogReaderConstants.PowerTaskList.EntityRegex.IsMatch(rawEntity))
                {
                    var entity = HsLogReaderConstants.PowerTaskList.EntityRegex.Match(rawEntity);
                    var id     = int.Parse(entity.Groups["id"].Value);
                    _tagChangeHandler.TagChange(gameState, match.Groups["tag"].Value, id, match.Groups["value"].Value, game);
                }
                else if (int.TryParse(rawEntity, out entityId))
                {
                    _tagChangeHandler.TagChange(gameState, match.Groups["tag"].Value, entityId, match.Groups["value"].Value, game);
                }
                else
                {
                    var entity = game.Entities.FirstOrDefault(x => x.Value.Name == rawEntity);

                    if (entity.Value == null)
                    {
                        entity = game.Entities.FirstOrDefault(x => x.Value.Name == "UNKNOWN HUMAN PLAYER");
                        if (entity.Value != null)
                        {
                            entity.Value.Name = rawEntity;
                        }
                    }

                    if (entity.Value == null)
                    {
                        //while the id is unknown, store in tmp entities
                        var tmpEntity = _tmpEntities.FirstOrDefault(x => x.Name == rawEntity);
                        if (tmpEntity == null)
                        {
                            tmpEntity      = new Entity(_tmpEntities.Count + 1);
                            tmpEntity.Name = rawEntity;
                            _tmpEntities.Add(tmpEntity);
                        }
                        GAME_TAG tag;
                        Enum.TryParse(match.Groups["tag"].Value, out tag);
                        var value = HsLogReaderV2.ParseTagValue(tag, match.Groups["value"].Value);
                        tmpEntity.SetTag(tag, value);
                        if (tmpEntity.HasTag(GAME_TAG.ENTITY_ID))
                        {
                            var id = tmpEntity.GetTag(GAME_TAG.ENTITY_ID);
                            if (game.Entities.ContainsKey(id))
                            {
                                game.Entities[id].Name = tmpEntity.Name;
                                foreach (var t in tmpEntity.Tags)
                                {
                                    game.Entities[id].SetTag(t.Key, t.Value);
                                }
                                _tmpEntities.Remove(tmpEntity);
                                //Logger.WriteLine("COPIED TMP ENTITY (" + rawEntity + ")");
                            }
                            else
                            {
                                Logger.WriteLine(
                                    "TMP ENTITY (" + rawEntity + ") NOW HAS A KEY, BUT GAME.ENTITIES DOES NOT CONTAIN THIS KEY",
                                    "LogReader");
                            }
                        }
                    }
                    else
                    {
                        _tagChangeHandler.TagChange(gameState, match.Groups["tag"].Value, entity.Key, match.Groups["value"].Value, game);
                    }
                }

                if (HsLogReaderConstants.PowerTaskList.EntityNameRegex.IsMatch(logLine))
                {
                    match = HsLogReaderConstants.PowerTaskList.EntityNameRegex.Match(logLine);
                    var name   = match.Groups["name"].Value;
                    var player = int.Parse(match.Groups["value"].Value);
                    if (player == game.Player.Id)
                    {
                        game.Player.Name = name;
                    }
                    else if (player == game.Opponent.Id)
                    {
                        game.Opponent.Name = name;
                    }
                }
            }
            else if (HsLogReaderConstants.PowerTaskList.CreationRegex.IsMatch(logLine))
            {
                var match  = HsLogReaderConstants.PowerTaskList.CreationRegex.Match(logLine);
                var id     = int.Parse(match.Groups["id"].Value);
                var cardId = match.Groups["cardId"].Value;
                if (!game.Entities.ContainsKey(id))
                {
                    if (string.IsNullOrEmpty(cardId))
                    {
                        if (gameState.KnownCardIds.TryGetValue(id, out cardId))
                        {
                            Logger.WriteLine(string.Format("Found known cardId for entity {0}: {1}", id, cardId));
                            gameState.KnownCardIds.Remove(id);
                        }
                    }
                    game.Entities.Add(id, new Entity(id)
                    {
                        CardId = cardId
                    });
                }
                gameState.CurrentEntityId        = id;
                gameState.CurrentEntityHasCardId = !string.IsNullOrEmpty(cardId);
            }
            else if (HsLogReaderConstants.PowerTaskList.UpdatingEntityRegex.IsMatch(logLine))
            {
                var match     = HsLogReaderConstants.PowerTaskList.UpdatingEntityRegex.Match(logLine);
                var cardId    = match.Groups["cardId"].Value;
                var rawEntity = match.Groups["entity"].Value;
                int entityId;
                if (rawEntity.StartsWith("[") && HsLogReaderConstants.PowerTaskList.EntityRegex.IsMatch(rawEntity))
                {
                    var entity = HsLogReaderConstants.PowerTaskList.EntityRegex.Match(rawEntity);
                    entityId = int.Parse(entity.Groups["id"].Value);
                }
                else if (!int.TryParse(rawEntity, out entityId))
                {
                    entityId = -1;
                }
                if (entityId != -1)
                {
                    gameState.CurrentEntityId = entityId;
                    if (!game.Entities.ContainsKey(entityId))
                    {
                        game.Entities.Add(entityId, new Entity(entityId));
                    }
                    game.Entities[entityId].CardId = cardId;
                }
                if (gameState.JoustReveals > 0)
                {
                    Entity currentEntity;
                    if (game.Entities.TryGetValue(entityId, out currentEntity))
                    {
                        if (currentEntity.IsControlledBy(game.Opponent.Id))
                        {
                            gameState.GameHandler.HandleOpponentJoust(currentEntity, cardId, gameState.GetTurnNumber());
                        }
                        else if (currentEntity.IsControlledBy(game.Player.Id))
                        {
                            gameState.GameHandler.HandlePlayerJoust(currentEntity, cardId, gameState.GetTurnNumber());
                        }
                    }
                    //gameState.JoustReveals--;
                }
            }
            else if (HsLogReaderConstants.PowerTaskList.CreationTagRegex.IsMatch(logLine) && !logLine.Contains("HIDE_ENTITY"))
            {
                var match = HsLogReaderConstants.PowerTaskList.CreationTagRegex.Match(logLine);
                _tagChangeHandler.TagChange(gameState, match.Groups["tag"].Value, gameState.CurrentEntityId, match.Groups["value"].Value, game);
            }
            else if ((logLine.Contains("Begin Spectating") || logLine.Contains("Start Spectator")) && game.IsInMenu)
            {
                gameState.GameHandler.SetGameMode(GameMode.Spectator);
            }
            else if (logLine.Contains("End Spectator"))
            {
                gameState.GameHandler.SetGameMode(GameMode.Spectator);
                gameState.GameHandler.HandleGameEnd();
            }
            else if (HsLogReaderConstants.PowerTaskList.ActionStartRegex.IsMatch(logLine))
            {
                Entity actionEntity;
                var    playerEntity =
                    game.Entities.FirstOrDefault(
                        e => e.Value.HasTag(GAME_TAG.PLAYER_ID) && e.Value.GetTag(GAME_TAG.PLAYER_ID) == game.Player.Id);
                var opponentEntity =
                    game.Entities.FirstOrDefault(
                        e => e.Value.HasTag(GAME_TAG.PLAYER_ID) && e.Value.GetTag(GAME_TAG.PLAYER_ID) == game.Opponent.Id);

                var match = HsLogReaderConstants.PowerTaskList.ActionStartRegex.Match(logLine);
                var actionStartingCardId   = match.Groups["cardId"].Value.Trim();
                var actionStartingEntityId = int.Parse(match.Groups["id"].Value);

                if (string.IsNullOrEmpty(actionStartingCardId))
                {
                    if (game.Entities.TryGetValue(actionStartingEntityId, out actionEntity))
                    {
                        actionStartingCardId = actionEntity.CardId;
                    }
                }
                if (game.Entities.TryGetValue(actionStartingEntityId, out actionEntity))
                {
                    // spell owned by the player
                    if (actionEntity.HasTag(GAME_TAG.CONTROLLER) && actionEntity.GetTag(GAME_TAG.CONTROLLER) == game.Player.Id &&
                        actionEntity.GetTag(GAME_TAG.CARDTYPE) == (int)TAG_CARDTYPE.SPELL)
                    {
                        int    targetEntityId = actionEntity.GetTag(GAME_TAG.CARD_TARGET);
                        Entity targetEntity;
                        var    targetsMinion = game.Entities.TryGetValue(targetEntityId, out targetEntity);
                        gameState.GameHandler.HandlePlayerSpellPlayed(targetsMinion);
                    }
                }
                if (!string.IsNullOrEmpty(actionStartingCardId))
                {
                    if (match.Groups["type"].Value == "TRIGGER")
                    {
                        switch (actionStartingCardId)
                        {
                        case CardIds.Collectible.Rogue.TradePrinceGallywix:
                            AddKnownCardId(gameState, game, game.Entities[gameState.LastCardPlayed].CardId);
                            AddKnownCardId(gameState, game, CardIds.NonCollectible.Neutral.GallywixsCoinToken);
                            break;

                        case CardIds.Collectible.Druid.Malorne:
                            AddKnownCardId(gameState, game, CardIds.Collectible.Druid.Malorne);
                            break;
                        }
                    }
                    else         //POWER
                    {
                        switch (actionStartingCardId)
                        {
                        case CardIds.Collectible.Rogue.GangUp:
                            AddTargetAsKnownCardId(gameState, game, match, 3);
                            break;

                        case CardIds.Collectible.Rogue.BeneathTheGrounds:
                            AddKnownCardId(gameState, game, CardIds.NonCollectible.Rogue.AmbushToken, 3);
                            break;

                        case CardIds.Collectible.Warrior.IronJuggernaut:
                            AddKnownCardId(gameState, game, CardIds.NonCollectible.Warrior.BurrowingMineToken);
                            break;

                        case CardIds.Collectible.Druid.Recycle:
                            AddTargetAsKnownCardId(gameState, game, match);
                            break;

                        case CardIds.Collectible.Mage.ForgottenTorch:
                            AddKnownCardId(gameState, game, CardIds.NonCollectible.Mage.RoaringTorchToken);
                            break;

                        case CardIds.Collectible.Warlock.CurseOfRafaam:
                            AddKnownCardId(gameState, game, CardIds.NonCollectible.Warlock.CursedToken);
                            break;

                        case CardIds.Collectible.Neutral.AncientShade:
                            AddKnownCardId(gameState, game, CardIds.NonCollectible.Neutral.AncientCurseToken);
                            break;

                        case CardIds.Collectible.Priest.Entomb:
                            AddTargetAsKnownCardId(gameState, game, match);
                            break;

                        case CardIds.Collectible.Priest.ExcavatedEvil:
                            AddKnownCardId(gameState, game, CardIds.Collectible.Priest.ExcavatedEvil);
                            break;

                        case CardIds.Collectible.Neutral.EliseStarseeker:
                            AddKnownCardId(gameState, game, CardIds.NonCollectible.Neutral.MapToTheGoldenMonkeyToken);
                            break;

                        case CardIds.NonCollectible.Neutral.MapToTheGoldenMonkeyToken:
                            AddKnownCardId(gameState, game, CardIds.NonCollectible.Neutral.GoldenMonkeyToken);
                            break;

                        default:
                            if (playerEntity.Value != null && playerEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) == 1 &&
                                !gameState.PlayerUsedHeroPower ||
                                opponentEntity.Value != null && opponentEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) == 1 &&
                                !gameState.OpponentUsedHeroPower)
                            {
                                var card = Database.GetCardFromId(actionStartingCardId);
                                if (card.Type == "Hero Power")
                                {
                                    if (playerEntity.Value != null && playerEntity.Value.GetTag(GAME_TAG.CURRENT_PLAYER) == 1)
                                    {
                                        gameState.GameHandler.HandlePlayerHeroPower(actionStartingCardId, gameState.GetTurnNumber());
                                        gameState.PlayerUsedHeroPower = true;
                                    }
                                    else if (opponentEntity.Value != null)
                                    {
                                        gameState.GameHandler.HandleOpponentHeroPower(actionStartingCardId, gameState.GetTurnNumber());
                                        gameState.OpponentUsedHeroPower = true;
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
            }
            else if (logLine.Contains("BlockType=JOUST"))
            {
                gameState.JoustReveals = 2;
            }
        }
        public void TagChange(IHsGameState gameState, string rawTag, int id, string rawValue, IGame game, bool isRecursive = false)
        {
            if (gameState.LastId != id)
            {
                game.SecondToLastUsedId = gameState.LastId;
                if (gameState.ProposedKeyPoint != null)
                {
                    ReplayMaker.Generate(gameState.ProposedKeyPoint.Type, gameState.ProposedKeyPoint.Id, gameState.ProposedKeyPoint.Player, game);
                    gameState.ProposedKeyPoint = null;
                }
            }
            gameState.LastId = id;
            if (!game.Entities.ContainsKey(id))
            {
                game.Entities.Add(id, new Entity(id));
            }
            GAME_TAG tag;

            if (!Enum.TryParse(rawTag, out tag))
            {
                int tmp;
                if (int.TryParse(rawTag, out tmp) && Enum.IsDefined(typeof(GAME_TAG), tmp))
                {
                    tag = (GAME_TAG)tmp;
                }
            }
            var value    = HsLogReaderV2.ParseTagValue(tag, rawValue);
            var prevZone = game.Entities[id].GetTag(GAME_TAG.ZONE);

            game.Entities[id].SetTag(tag, value);

            if (tag == GAME_TAG.CONTROLLER && gameState.WaitForController != null && game.PlayerId == -1)
            {
                var p1 = game.Entities.FirstOrDefault(e => e.Value.GetTag(GAME_TAG.PLAYER_ID) == 1).Value;
                var p2 = game.Entities.FirstOrDefault(e => e.Value.GetTag(GAME_TAG.PLAYER_ID) == 2).Value;
                if (gameState.CurrentEntityHasCardId)
                {
                    if (p1 != null)
                    {
                        p1.IsPlayer = value == 1;
                    }
                    if (p2 != null)
                    {
                        p2.IsPlayer = value != 1;
                    }
                    game.PlayerId   = value;
                    game.OpponentId = value == 1 ? 2 : 1;
                }
                else
                {
                    if (p1 != null)
                    {
                        p1.IsPlayer = value != 1;
                    }
                    if (p2 != null)
                    {
                        p2.IsPlayer = value == 1;
                    }
                    game.PlayerId   = value == 1 ? 2 : 1;
                    game.OpponentId = value;
                }
            }
            var controller = game.Entities[id].GetTag(GAME_TAG.CONTROLLER);
            var player     = game.Entities[id].HasTag(GAME_TAG.CONTROLLER) ? (controller == game.PlayerId ? "FRIENDLY" : "OPPOSING") : "";
            var cardId     = game.Entities[id].CardId;

            if (tag == GAME_TAG.ZONE)
            {
                //Logger.WriteLine("--------" + player + " " + game.Entities[id].CardId + " " + (TAG_ZONE)prevZone + " -> " +
                //                 (TAG_ZONE)value);

                if (((TAG_ZONE)value == TAG_ZONE.HAND || ((TAG_ZONE)value == TAG_ZONE.PLAY) && game.IsMulliganDone) && gameState.WaitForController == null)
                {
                    if (!game.IsMulliganDone)
                    {
                        prevZone = (int)TAG_ZONE.DECK;
                    }
                    if (controller == 0)
                    {
                        game.Entities[id].SetTag(GAME_TAG.ZONE, prevZone);
                        gameState.WaitForController = new { Tag = rawTag, Id = id, Value = rawValue };
                        //Logger.WriteLine("CURRENTLY NO CONTROLLER SET FOR CARD, WAITING...");
                        return;
                    }
                }
                switch ((TAG_ZONE)prevZone)
                {
                case TAG_ZONE.DECK:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.HAND:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerDraw(cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Draw, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentDraw(gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Draw, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.REMOVEDFROMGAME:
                    case TAG_ZONE.GRAVEYARD:
                    case TAG_ZONE.SETASIDE:
                    case TAG_ZONE.PLAY:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerDeckDiscard(cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.DeckDiscard, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentDeckDiscard(cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.DeckDiscard, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.SECRET:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerSecretPlayed(cardId, gameState.GetTurnNumber(), true);
                            gameState.ProposeKeyPoint(KeyPointType.SecretPlayed, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentSecretPlayed(cardId, -1, gameState.GetTurnNumber(), true, id);
                            gameState.ProposeKeyPoint(KeyPointType.SecretPlayed, id, ActivePlayer.Player);
                        }
                        break;
                    }
                    break;

                case TAG_ZONE.HAND:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.PLAY:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerPlay(cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Play, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentPlay(cardId, game.Entities[id].GetTag(GAME_TAG.ZONE_POSITION), gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Play, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.REMOVEDFROMGAME:
                    case TAG_ZONE.GRAVEYARD:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerHandDiscard(cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.HandDiscard, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentHandDiscard(cardId, game.Entities[id].GetTag(GAME_TAG.ZONE_POSITION), gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.HandDiscard, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.SECRET:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerSecretPlayed(cardId, gameState.GetTurnNumber(), false);
                            gameState.ProposeKeyPoint(KeyPointType.SecretPlayed, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentSecretPlayed(cardId, game.Entities[id].GetTag(GAME_TAG.ZONE_POSITION), gameState.GetTurnNumber(), false, id);
                            gameState.ProposeKeyPoint(KeyPointType.SecretPlayed, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.DECK:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerMulligan(cardId);
                            gameState.ProposeKeyPoint(KeyPointType.Mulligan, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentMulligan(game.Entities[id].GetTag(GAME_TAG.ZONE_POSITION));
                            gameState.ProposeKeyPoint(KeyPointType.Mulligan, id, ActivePlayer.Opponent);
                        }
                        break;
                    }
                    break;

                case TAG_ZONE.PLAY:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.HAND:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerBackToHand(cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.PlayToHand, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentPlayToHand(cardId, gameState.GetTurnNumber(), id);
                            gameState.ProposeKeyPoint(KeyPointType.PlayToHand, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.DECK:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerPlayToDeck(cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.PlayToDeck, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentPlayToDeck(cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.PlayToDeck, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.GRAVEYARD:
                        if (game.Entities[id].HasTag(GAME_TAG.HEALTH))
                        {
                            if (controller == game.PlayerId)
                            {
                                gameState.ProposeKeyPoint(KeyPointType.Death, id, ActivePlayer.Player);
                            }
                            else if (controller == game.OpponentId)
                            {
                                gameState.ProposeKeyPoint(KeyPointType.Death, id, ActivePlayer.Opponent);
                            }
                        }
                        break;
                    }
                    break;

                case TAG_ZONE.SECRET:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.SECRET:
                    case TAG_ZONE.GRAVEYARD:
                        if (controller == game.PlayerId)
                        {
                            gameState.ProposeKeyPoint(KeyPointType.SecretTriggered, id, ActivePlayer.Player);
                        }
                        if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentSecretTrigger(cardId, gameState.GetTurnNumber(), id);
                            gameState.ProposeKeyPoint(KeyPointType.SecretTriggered, id, ActivePlayer.Opponent);
                        }
                        break;
                    }
                    break;

                case TAG_ZONE.GRAVEYARD:
                case TAG_ZONE.SETASIDE:
                case TAG_ZONE.CREATED:
                case TAG_ZONE.INVALID:
                case TAG_ZONE.REMOVEDFROMGAME:
                    switch ((TAG_ZONE)value)
                    {
                    case TAG_ZONE.PLAY:
                        if (controller == game.PlayerId)
                        {
                            gameState.ProposeKeyPoint(KeyPointType.Summon, id, ActivePlayer.Player);
                        }
                        if (controller == game.OpponentId)
                        {
                            gameState.ProposeKeyPoint(KeyPointType.Summon, id, ActivePlayer.Opponent);
                        }
                        break;

                    case TAG_ZONE.HAND:
                        if (controller == game.PlayerId)
                        {
                            gameState.GameHandler.HandlePlayerGet(cardId, gameState.GetTurnNumber());
                            gameState.ProposeKeyPoint(KeyPointType.Obtain, id, ActivePlayer.Player);
                        }
                        else if (controller == game.OpponentId)
                        {
                            gameState.GameHandler.HandleOpponentGet(gameState.GetTurnNumber(), id);
                            gameState.ProposeKeyPoint(KeyPointType.Obtain, id, ActivePlayer.Opponent);
                        }
                        break;
                    }
                    break;
                }
            }
            else if (tag == GAME_TAG.PLAYSTATE)
            {
                if (value == (int)TAG_PLAYSTATE.QUIT)
                {
                    gameState.GameHandler.HandleConcede();
                }
                if (!gameState.GameEnded)
                {
                    if (game.Entities[id].IsPlayer)
                    {
                        if (value == (int)TAG_PLAYSTATE.WON)
                        {
                            gameState.GameEndKeyPoint(true, id);
                            gameState.GameHandler.HandleWin();
                            gameState.GameHandler.HandleGameEnd();
                            gameState.GameEnded = true;
                        }
                        else if (value == (int)TAG_PLAYSTATE.LOST)
                        {
                            gameState.GameEndKeyPoint(false, id);
                            gameState.GameHandler.HandleLoss();
                            gameState.GameHandler.HandleGameEnd();
                            gameState.GameEnded = true;
                        }
                        else if (value == (int)TAG_PLAYSTATE.TIED)
                        {
                            gameState.GameEndKeyPoint(false, id);
                            gameState.GameHandler.HandleTied();
                            gameState.GameHandler.HandleGameEnd();
                        }
                    }
                }
            }
            else if (tag == GAME_TAG.CURRENT_PLAYER && value == 1)
            {
                var activePlayer = game.Entities[id].IsPlayer ? ActivePlayer.Player : ActivePlayer.Opponent;
                gameState.GameHandler.TurnStart(activePlayer, gameState.GetTurnNumber());
                if (activePlayer == ActivePlayer.Player)
                {
                    gameState.PlayerUsedHeroPower = false;
                }
                else
                {
                    gameState.OpponentUsedHeroPower = false;
                }
            }
            else if (tag == GAME_TAG.NUM_ATTACKS_THIS_TURN && value > 0)
            {
                if (controller == game.PlayerId)
                {
                    gameState.ProposeKeyPoint(KeyPointType.Attack, id, ActivePlayer.Player);
                }
                else if (controller == game.OpponentId)
                {
                    gameState.ProposeKeyPoint(KeyPointType.Attack, id, ActivePlayer.Opponent);
                }
            }
            else if (tag == GAME_TAG.ZONE_POSITION)
            {
                var zone = game.Entities[id].GetTag(GAME_TAG.ZONE);
                if (zone == (int)TAG_ZONE.HAND)
                {
                    if (controller == game.PlayerId)
                    {
                        ReplayMaker.Generate(KeyPointType.HandPos, id, ActivePlayer.Player, game);
                    }
                    else if (controller == game.OpponentId)
                    {
                        ReplayMaker.Generate(KeyPointType.HandPos, id, ActivePlayer.Opponent, game);
                    }
                }
                else if (zone == (int)TAG_ZONE.PLAY)
                {
                    if (controller == game.PlayerId)
                    {
                        ReplayMaker.Generate(KeyPointType.BoardPos, id, ActivePlayer.Player, game);
                    }
                    else if (controller == game.OpponentId)
                    {
                        ReplayMaker.Generate(KeyPointType.BoardPos, id, ActivePlayer.Opponent, game);
                    }
                }
            }
            else if (tag == GAME_TAG.CARD_TARGET && value > 0)
            {
                if (controller == game.PlayerId)
                {
                    gameState.ProposeKeyPoint(KeyPointType.PlaySpell, id, ActivePlayer.Player);
                }
                else if (controller == game.OpponentId)
                {
                    gameState.ProposeKeyPoint(KeyPointType.PlaySpell, id, ActivePlayer.Opponent);
                }
            }
            else if (tag == GAME_TAG.EQUIPPED_WEAPON && value == 0)
            {
                if (controller == game.PlayerId)
                {
                    gameState.ProposeKeyPoint(KeyPointType.WeaponDestroyed, id, ActivePlayer.Player);
                }
                else if (controller == game.OpponentId)
                {
                    gameState.ProposeKeyPoint(KeyPointType.WeaponDestroyed, id, ActivePlayer.Opponent);
                }
            }
            else if (tag == GAME_TAG.EXHAUSTED && value > 0)
            {
                if (game.Entities[id].GetTag(GAME_TAG.CARDTYPE) == (int)TAG_CARDTYPE.HERO_POWER)
                {
                    if (controller == game.PlayerId)
                    {
                        gameState.ProposeKeyPoint(KeyPointType.HeroPower, id, ActivePlayer.Player);
                    }
                    else if (controller == game.OpponentId)
                    {
                        gameState.ProposeKeyPoint(KeyPointType.HeroPower, id, ActivePlayer.Opponent);
                    }
                }
            }
            else if (tag == GAME_TAG.CONTROLLER && game.Entities[id].IsInZone(TAG_ZONE.SECRET))
            {
                if (value == game.PlayerId)
                {
                    gameState.GameHandler.HandleOpponentSecretTrigger(cardId, gameState.GetTurnNumber(), id);
                    gameState.ProposeKeyPoint(KeyPointType.SecretStolen, id, ActivePlayer.Player);
                }
                else if (value == game.OpponentId)
                {
                    gameState.ProposeKeyPoint(KeyPointType.SecretStolen, id, ActivePlayer.Player);
                }
            }
            else if (tag == GAME_TAG.FATIGUE)
            {
                if (controller == game.PlayerId)
                {
                    gameState.GameHandler.HandlePlayerFatigue(Convert.ToInt32(rawValue));
                }
                else if (controller == game.OpponentId)
                {
                    gameState.GameHandler.HandleOpponentFatigue(Convert.ToInt32(rawValue));
                }
            }
            if (gameState.WaitForController != null)
            {
                if (!isRecursive)
                {
                    TagChange(gameState, (string)gameState.WaitForController.Tag, (int)gameState.WaitForController.Id, (string)gameState.WaitForController.Value, game, true);
                    gameState.WaitForController = null;
                }
            }
        }