public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			if(HsLogReaderConstants.CardAlreadyInCacheRegex.IsMatch(logLine))
			{
				var id = HsLogReaderConstants.CardAlreadyInCacheRegex.Match(logLine).Groups["id"].Value;
				if(game.CurrentGameMode == GameMode.Arena)
					gameState.GameHandler.HandlePossibleArenaCard(id);
				else
					gameState.GameHandler.HandlePossibleConstructedCard(id, false);
			}
			else if(HsLogReaderConstants.GoldProgressRegex.IsMatch(logLine) && (DateTime.Now - gameState.LastGameStart) > TimeSpan.FromSeconds(10)
			        && game.CurrentGameMode != GameMode.Spectator)
			{
				int wins;
				var rawWins = HsLogReaderConstants.GoldProgressRegex.Match(logLine).Groups["wins"].Value;
				if(int.TryParse(rawWins, out wins))
				{
					var timeZone = GetTimeZoneInfo(game.CurrentRegion);
					if(timeZone != null)
						UpdateGoldProgress(wins, game, timeZone);
				}
			}
			else if(HsLogReaderConstants.DustRewardRegex.IsMatch(logLine))
			{
				int amount;
				if(int.TryParse(HsLogReaderConstants.DustRewardRegex.Match(logLine).Groups["amount"].Value, out amount))
					gameState.GameHandler.HandleDustReward(amount);
			}
			else if(HsLogReaderConstants.GoldRewardRegex.IsMatch(logLine))
			{
				int amount;
				if(int.TryParse(HsLogReaderConstants.GoldRewardRegex.Match(logLine).Groups["amount"].Value, out amount))
					gameState.GameHandler.HandleGoldReward(amount);
			}
		}
 public void Handle(string logLine, IHsGameState gameState, IGame game)
 {
     if (HsLogReaderConstants.CardAlreadyInCacheRegex.IsMatch(logLine))
     {
         var id = HsLogReaderConstants.CardAlreadyInCacheRegex.Match(logLine).Groups["id"].Value;
         if (game.CurrentGameMode == GameMode.Arena)
             gameState.GameHandler.HandlePossibleArenaCard(id);
         else
             gameState.GameHandler.HandlePossibleConstructedCard(id, false);
     }
     else if (HsLogReaderConstants.GoldProgressRegex.IsMatch(logLine)
              && (DateTime.Now - gameState.LastGameStart) > TimeSpan.FromSeconds(10)
              && game.CurrentGameMode != GameMode.Spectator)
     {
         int wins;
         var rawWins = HsLogReaderConstants.GoldProgressRegex.Match(logLine).Groups["wins"].Value;
         if (int.TryParse(rawWins, out wins))
         {
             TimeZoneInfo timeZone = null;
             switch (game.CurrentRegion)
             {
                 case Region.EU:
                     timeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
                     break;
                 case Region.US:
                     timeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
                     break;
                 case Region.ASIA:
                     timeZone = TimeZoneInfo.FindSystemTimeZoneById("Korea Standard Time");
                     break;
             }
             if (timeZone != null)
             {
                 var region = (int)game.CurrentRegion - 1;
                 var date = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, timeZone).Date;
                 if (Config.Instance.GoldProgressLastReset[region].Date != date)
                 {
                     Config.Instance.GoldProgressTotal[region] = 0;
                     Config.Instance.GoldProgressLastReset[region] = date;
                 }
                 Config.Instance.GoldProgress[region] = wins == 3 ? 0 : wins;
                 if (wins == 3)
                     Config.Instance.GoldProgressTotal[region] += 10;
                 Config.Save();
             }
         }
     }
     else if (HsLogReaderConstants.DustRewardRegex.IsMatch(logLine))
     {
         int amount;
         if (int.TryParse(HsLogReaderConstants.DustRewardRegex.Match(logLine).Groups["amount"].Value, out amount))
             gameState.GameHandler.HandleDustReward(amount);
     }
     else if (HsLogReaderConstants.GoldRewardRegex.IsMatch(logLine))
     {
         int amount;
         if (int.TryParse(HsLogReaderConstants.GoldRewardRegex.Match(logLine).Groups["amount"].Value, out amount))
             gameState.GameHandler.HandleGoldReward(amount);
     }
 }
 public void Handle(string logLine, IHsGameState gameState, IGame game)
 {
     if (gameState.AwaitingRankedDetection)
     {
         gameState.LastAssetUnload = DateTime.Now;
         gameState.WaitingForFirstAssetUnload = false;
     }
     if (logLine.Contains("Medal_Ranked_"))
     {
         var match = Regex.Match(logLine, "Medal_Ranked_(?<rank>(\\d+))");
         if (match.Success)
         {
             int rank;
             if (int.TryParse(match.Groups["rank"].Value, out rank))
                 gameState.GameHandler.SetRank(rank);
         }
     }
     else if (logLine.Contains("rank_window"))
     {
         gameState.FoundRanked = true;
         gameState.GameHandler.SetGameMode(GameMode.Ranked);
     }
     else if (HsLogReaderConstants.UnloadCardRegex.IsMatch(logLine))
     {
         var id = HsLogReaderConstants.UnloadCardRegex.Match(logLine).Groups["id"].Value;
         if (game.CurrentGameMode == GameMode.Arena)
             gameState.GameHandler.HandlePossibleArenaCard(id);
         else
             gameState.GameHandler.HandlePossibleConstructedCard(id, true);
     }
 }
		public void TagChange(IHsGameState gameState, GameTag tag, int id, int value, IGame game, bool isCreationTag = false)
		{
			if(gameState.LastId != id)
			{
				if(gameState.ProposedKeyPoint != null)
				{
					ReplayMaker.Generate(gameState.ProposedKeyPoint.Type, gameState.ProposedKeyPoint.Id, gameState.ProposedKeyPoint.Player, game);
					gameState.ProposedKeyPoint = null;
				}
			}
			gameState.LastId = id;
			if(id > gameState.MaxId)
				gameState.MaxId = id;
			if(!game.Entities.ContainsKey(id))
				game.Entities.Add(id, new Entity(id));

			if(!gameState.DeterminedPlayers)
			{
				var entity = game.Entities[id];
				if(tag == CONTROLLER && entity.IsInHand && string.IsNullOrEmpty(entity.CardId))
					DeterminePlayers(gameState, game, value);
			}

			var prevValue = game.Entities[id].GetTag(tag);
			game.Entities[id].SetTag(tag, value);

			if(isCreationTag)
			{
				var action = _tagChangeActions.FindAction(tag, game, gameState, id, value, prevValue);
				if(action != null)
					_creationTagActionQueue.Enqueue(new Tuple<int, Action>(id, action));
			}
			else
				_tagChangeActions.FindAction(tag, game, gameState, id, value, prevValue)?.Invoke();
		}
	    public void Handle(string logLine, IHsGameState gameState, IGame game)
	    {
		    if(!logLine.Contains("---Register"))
			    return;

		    if(logLine.Contains("---RegisterScreenBox---"))
		    {
			    if(game.CurrentGameMode == GameMode.Spectator)
				    gameState.GameEnd();
		    }
		    else if(logLine.Contains("---RegisterScreenForge---"))
		    {
			    gameState.GameHandler.SetGameMode(GameMode.Arena);
			    game.ResetArenaCards();
		    }
		    else if(logLine.Contains("---RegisterScreenPractice---"))
			    gameState.GameHandler.SetGameMode(GameMode.Practice);
		    else if(logLine.Contains("---RegisterScreenTourneys---"))
			    gameState.GameHandler.SetGameMode(GameMode.Casual);
		    else if(logLine.Contains("---RegisterScreenFriendly---"))
			    gameState.GameHandler.SetGameMode(GameMode.Friendly);
		    else if(logLine.Contains("---RegisterProfileNotices---"))
			    gameState.GameLoaded = true;
		    else if(logLine.Contains("---RegisterFriendChallenge---"))
		    {
			    gameState.GameHandler.HandleInMenu();
		    }
		    else if(logLine.Contains("---RegisterScreenCollectionManager---"))
			    gameState.GameHandler.ResetConstructedImporting();
	    }
 public void Handle(string logLine, IHsGameState gameState, IGame game)
 {
     if (HsLogReaderConstants.CardAlreadyInCacheRegex.IsMatch(logLine))
     {
         var id = HsLogReaderConstants.CardAlreadyInCacheRegex.Match(logLine).Groups["id"].Value;
         if (game.CurrentGameMode == GameMode.Arena)
             gameState.GameHandler.HandlePossibleArenaCard(id);
         else
             gameState.GameHandler.HandlePossibleConstructedCard(id, false);
     }
     else if ((DateTime.Now - gameState.LastGameStart) > TimeSpan.FromSeconds(10)
              && game.CurrentGameMode != GameMode.Spectator)
     {
         GoldTracking(logLine, game);
     }
     else if (HsLogReaderConstants.DustRewardRegex.IsMatch(logLine))
     {
         int amount;
         if (int.TryParse(HsLogReaderConstants.DustRewardRegex.Match(logLine).Groups["amount"].Value,
             out amount))
             gameState.GameHandler.HandleDustReward(amount);
     }
     else if (HsLogReaderConstants.GoldRewardRegex.IsMatch(logLine))
     {
         int amount;
         if (int.TryParse(HsLogReaderConstants.GoldRewardRegex.Match(logLine).Groups["amount"].Value,
             out amount))
             gameState.GameHandler.HandleGoldReward(amount);
     }
 }
        public void Handle(string logLine, IHsGameState gameState, IGame game)
        {
            if (logLine[9] != 'R' && logLine[16] != 'r') // [Bob] ---Register
                return;

            if (logLine.Length == 29 && logLine[23] == 'B' && logLine[25] == 'x') // ---RegisterScreenBox---
            {
                if (game.CurrentGameMode == GameMode.Spectator)
                    gameState.GameEnd();
            }
            else if (logLine.Length == 31 && logLine[23] == 'F' && logLine[27] == 'e') // ---RegisterScreenForge---
            {
                gameState.GameHandler.SetGameMode(GameMode.Arena);
                game.ResetArenaCards();
            }
            else if (logLine.Length == 34)
            {
                if (logLine[23] == 'P' && logLine[30] == 'e') // ---RegisterScreenPractice---
                    gameState.GameHandler.SetGameMode(GameMode.Practice);
                else if (logLine[23] == 'T' && logLine[30] == 's') // ---RegisterScreenTourneys---
                    gameState.GameHandler.SetGameMode(GameMode.Casual);
                else if (logLine[23] == 'F' && logLine[30] == 'y') // ---RegisterScreenFriendly---
                    gameState.GameHandler.SetGameMode(GameMode.Friendly);
                else if (logLine[23] == 'e' && logLine[24] == 'N' && logLine[30] == 's') // RegisterProfileNotices
                    gameState.GameLoaded = true;
            }
            else if (logLine.Length == 35 && logLine[17] == 'F' && logLine[22] == 'd' && logLine[23] == 'C') // RegisterFriendChallenge
            {
                gameState.GameHandler.HandleInMenu();
            }
            else if (logLine.Length == 43 && logLine[23] == 'C' && logLine[32] == 'n' && logLine[33] == 'M' && logLine[39] == 'r')// ---RegisterScreenCollectionManager---
                gameState.GameHandler.ResetConstructedImporting();
        }
		private void StateChange(int value, IHsGameState gameState)
		{
			if(value != (int)State.COMPLETE)
				return;
			gameState.GameHandler.HandleGameEnd();
			gameState.GameEnded = true;
		}
		internal void DeterminePlayers(IHsGameState gameState, IGame game, int playerId, bool isOpponentId = true)
		{
			if(isOpponentId)
			{
				game.Entities.FirstOrDefault(e => e.Value.GetTag(PLAYER_ID) == 1).Value?.SetPlayer(playerId != 1);
				game.Entities.FirstOrDefault(e => e.Value.GetTag(PLAYER_ID) == 2).Value?.SetPlayer(playerId == 1);
				game.Player.Id = playerId % 2 + 1;
				game.Opponent.Id = playerId;
			}
			else
			{
				game.Entities.FirstOrDefault(e => e.Value.GetTag(PLAYER_ID) == 1).Value?.SetPlayer(playerId == 1);
				game.Entities.FirstOrDefault(e => e.Value.GetTag(PLAYER_ID) == 2).Value?.SetPlayer(playerId != 1);
				game.Player.Id = playerId;
				game.Opponent.Id = playerId % 2 + 1;
			}
			if(gameState.WasInProgress)
			{
				var playerName = game.GetStoredPlayerName(game.Player.Id);
				if(!string.IsNullOrEmpty(playerName))
					game.Player.Name = playerName;
				var opponentName = game.GetStoredPlayerName(game.Opponent.Id);
				if(!string.IsNullOrEmpty(opponentName))
					game.Opponent.Name = opponentName;
			}
			gameState.DeterminedPlayers = game.PlayerEntity != null;
		}
Beispiel #10
0
		public void Handle(LogLineItem logLine, IHsGameState gameState, IGame game)
		{
			var match = HsLogReaderConstants.GameModeRegex.Match(logLine.Line);
			if(match.Success)
			{
				game.CurrentMode = GetMode(match.Groups["curr"].Value);
				game.PreviousMode = GetMode(match.Groups["prev"].Value);

				if((DateTime.Now - logLine.Time).TotalSeconds < 5 && _lastAutoImport < logLine.Time
					&& game.CurrentMode == Mode.TOURNAMENT)
				{
					_lastAutoImport = logLine.Time;
					var decks = DeckImporter.FromConstructed();
					if(decks.Any() && (Config.Instance.ConstructedAutoImportNew || Config.Instance.ConstructedAutoUpdate))
						DeckManager.ImportDecks(decks, false, Config.Instance.ConstructedAutoImportNew,
							Config.Instance.ConstructedAutoUpdate);
				}

				if(game.PreviousMode == Mode.GAMEPLAY && game.CurrentMode != Mode.GAMEPLAY)
					gameState.GameHandler.HandleInMenu();

				if(game.CurrentMode == Mode.HUB && !_checkedMirrorStatus && (DateTime.Now - logLine.Time).TotalSeconds < 5)
					CheckMirrorStatus();
			}
			else if(logLine.Line.Contains("Gameplay.Start"))
			{
				gameState.Reset();
				gameState.GameHandler.HandleGameStart(logLine.Time);
			}
		}
 public void SetUp()
 {
     _game = MockRepository.GenerateMock<IGame>();
     _gameHandler = MockRepository.GenerateMock<IGameHandler>();
     _gameState = MockRepository.GenerateMock<IHsGameState>();
     _gameState.Stub(x => x.GameHandler).Return(_gameHandler);
     _arenaHandler = new ArenaHandler();
 }
		public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			var match = HsLogReaderConstants.GameModeRegex.Match(logLine);
			if(match.Success)
			{
				var newMode = GetGameMode(match.Groups["curr"].Value) ?? GetGameMode(match.Groups["prev"].Value);
				if(newMode.HasValue && !(game.CurrentGameMode == GameMode.Ranked && newMode.Value == GameMode.Casual))
					game.CurrentGameMode = newMode.Value;
			}
		}
		public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			if(!UnloadCardRegex.IsMatch(logLine))
				return;
			var id = UnloadCardRegex.Match(logLine).Groups["id"].Value;
			if(game.CurrentMode == Mode.DRAFT && game.PreviousMode == Mode.HUB)
				gameState.GameHandler.HandlePossibleArenaCard(id);
			else if((game.CurrentMode == Mode.COLLECTIONMANAGER || game.CurrentMode == Mode.TAVERN_BRAWL) && game.PreviousMode == Mode.HUB)
				gameState.GameHandler.HandlePossibleConstructedCard(id, true);
		}
		public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			if(!UnloadCardRegex.IsMatch(logLine))
				return;
			var id = UnloadCardRegex.Match(logLine).Groups["id"].Value;
			if(game.CurrentGameMode == GameMode.Arena)
				gameState.GameHandler.HandlePossibleArenaCard(id);
			else
				gameState.GameHandler.HandlePossibleConstructedCard(id, true);
		}
		private void TurnChange(IHsGameState gameState, IGame game)
		{
			if(!gameState.SetupDone || game.PlayerEntity == null)
				return;
			var activePlayer = game.PlayerEntity.HasTag(CURRENT_PLAYER) ? ActivePlayer.Player : ActivePlayer.Opponent;
			if(activePlayer == ActivePlayer.Player)
				gameState.PlayerUsedHeroPower = false;
			else
				gameState.OpponentUsedHeroPower = false;
		}
		public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			var match = HsLogReaderConstants.GameModeRegex.Match(logLine);
			if(!match.Success)
				return;
			var prev = match.Groups["prev"].Value;
			var newMode = GetGameMode(match.Groups["curr"].Value) ?? GetGameMode(prev);
			if(newMode.HasValue && !(game.CurrentGameMode == Ranked && newMode.Value == Casual))
				game.CurrentGameMode = newMode.Value;
			if(prev == "GAMEPLAY")
				gameState.GameHandler.HandleInMenu();
		}
		public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			var match = HsLogReaderConstants.ExistingHeroRegex.Match(logLine);
			if(match.Success)
				game.NewArenaDeck(match.Groups["id"].Value);
			else
			{
				match = HsLogReaderConstants.ExistingCardRegex.Match(logLine);
				if(match.Success)
				{
					try
					{
						game.NewArenaCard(match.Groups["id"].Value);
					}
					catch(Exception ex)
					{
						Logger.WriteLine("Error adding arena card: " + ex, "ArenaHandler");
					}
				}
				else
				{
					match = HsLogReaderConstants.NewChoiceRegex.Match(logLine);
					if(match.Success)
					{
						if(Database.GetHeroNameFromId(match.Groups["id"].Value, false) != null)
							game.NewArenaDeck(match.Groups["id"].Value);
						else
						{
							var cardId = match.Groups["id"].Value;
							var timeSinceLastChoice = DateTime.Now.Subtract(_lastChoice).Milliseconds;

							if(_lastChoiceId == cardId && timeSinceLastChoice < 1000)
							{
								Logger.WriteLine(string.Format("Card with the same ID ({0}) was chosen less {1} ms ago. Ignoring.", cardId, timeSinceLastChoice));
								return;
							}

							try
							{
								game.NewArenaCard(cardId);
							}
							catch(Exception ex)
							{
								Logger.WriteLine("Error adding arena card: " + ex, "ArenaHandler");
							}

							_lastChoice = DateTime.Now;
							_lastChoiceId = cardId;
						}
					}
				}
			}
		}
		public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			var match = ExistingHeroRegex.Match(logLine);
			if(match.Success)
				game.NewArenaDeck(match.Groups["id"].Value);
			else
			{
				match = ExistingCardRegex.Match(logLine);
				if(match.Success)
				{
					try
					{
						game.NewArenaCard(match.Groups["id"].Value);
					}
					catch(Exception ex)
					{
						Log.Error("Error adding arena card: " + ex);
					}
				}
				else
				{
					match = NewChoiceRegex.Match(logLine);
					if(!match.Success)
						return;
					if(Database.GetHeroNameFromId(match.Groups["id"].Value, false) != null)
						game.NewArenaDeck(match.Groups["id"].Value);
					else
					{
						var cardId = match.Groups["id"].Value;
							var timeSinceLastChoice = DateTime.Now.Subtract(_lastChoice).TotalMilliseconds;

						if(_lastChoiceId == cardId && timeSinceLastChoice < 1000)
						{
							Log.Warn($"Card with the same ID ({cardId}) was chosen less {timeSinceLastChoice} ms ago. Ignoring.");
							return;
						}

						try
						{
							game.NewArenaCard(cardId);
						}
						catch(Exception ex)
						{
							Log.Error("Error adding arena card: " + ex);
						}

						_lastChoice = DateTime.Now;
						_lastChoiceId = cardId;
					}
				}
			}
		}
 public void Handle(string logLine, IHsGameState gameState, IGame game)
 {
     if ((logLine.Contains("Begin Spectating") || logLine.Contains("Start Spectator") || gameState.FoundSpectatorStart) && game.IsInMenu)
     {
         gameState.GameHandler.SetGameMode(GameMode.Spectator);
         gameState.FoundSpectatorStart = false;
     }
     else if (logLine.Contains("End Spectator"))
     {
         gameState.GameHandler.SetGameMode(GameMode.Spectator);
         gameState.GameHandler.HandleGameEnd();
     }
 }
		public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			var match = HsLogReaderConstants.ConnectionRegex.Match(logLine);
			if(match.Success)
			{
				game.MetaData.ServerAddress = match.Groups["address"].Value.Trim();
				game.MetaData.ClientId = match.Groups["client"].Value.Trim();
				game.MetaData.GameId = match.Groups["game"].Value.Trim();
				game.MetaData.SpectateKey = match.Groups["spectateKey"].Value.Trim();

				gameState.Reset();
				gameState.GameHandler.HandleGameStart();
			}
		}
		public Action FindAction(GameTag tag, IGame game, IHsGameState gameState, int id, int value, int prevValue)
		{
			switch(tag)
			{
				case ZONE:
					return () => ZoneChange(gameState, id, game, value, prevValue);
				case PLAYSTATE:
					return () => PlaystateChange(gameState, id, game, value);
				case CARDTYPE:
					return () => CardTypeChange(gameState, id, game, value);
				case LAST_CARD_PLAYED:
					return () => LastCardPlayedChange(gameState, value);
				case DEFENDING:
					return () => DefendingChange(gameState, id, game, value);
				case ATTACKING:
					return () => AttackingChange(gameState, id, game, value);
				case PROPOSED_DEFENDER:
					return () => ProposedDefenderChange(game, value);
				case PROPOSED_ATTACKER:
					return () => ProposedAttackerChange(game, value);
				case NUM_MINIONS_PLAYED_THIS_TURN:
					return () => NumMinionsPlayedThisTurnChange(gameState, game, value);
				case PREDAMAGE:
					return () => PredamageChange(gameState, id, game, value);
				case NUM_TURNS_IN_PLAY:
					return () => NumTurnsInPlayChange(gameState, id, game, value);
				case NUM_ATTACKS_THIS_TURN:
					return () => NumAttacksThisTurnChange(gameState, id, game, value);
				case ZONE_POSITION:
					return () => ZonePositionChange(gameState, id, game);
				case CARD_TARGET:
					return () => CardTargetChange(gameState, id, game, value);
				case WEAPON:
					return () => EquippedWeaponChange(gameState, id, game, value);
				case EXHAUSTED:
					return () => ExhaustedChange(gameState, id, game, value);
				case CONTROLLER:
					return () => ControllerChange(gameState, id, game, prevValue, value);
				case FATIGUE:
					return () => FatigueChange(gameState, value, game, id);
				case STEP:
					return () => StepChange(gameState, game);
				case TURN:
					return () => TurnChange(gameState, game);
				case STATE:
					return () => StateChange(value, gameState);
			}
			return null;
		}
		public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			if(GoldProgressRegex.IsMatch(logLine) && (DateTime.Now - gameState.LastGameStart) > TimeSpan.FromSeconds(10)
			        && game.CurrentGameMode != GameMode.Spectator)
			{
				int wins;
				var rawWins = GoldProgressRegex.Match(logLine).Groups["wins"].Value;
				if(int.TryParse(rawWins, out wins))
				{
					var timeZone = GetTimeZoneInfo(game.CurrentRegion);
					if(timeZone != null)
						UpdateGoldProgress(wins, game, timeZone);
				}
			}
		}
        public void Handle(string logLine, IHsGameState gameState)
        {
            if (HsLogReaderConstants.CardMovementRegex.IsMatch(logLine))
            {
                var match = HsLogReaderConstants.CardMovementRegex.Match(logLine);

                var id = match.Groups["Id"].Value.Trim();
                var from = match.Groups["from"].Value.Trim();
                var to = match.Groups["to"].Value.Trim();

                if (id.Contains("HERO") || (id.Contains("NAX") && id.Contains("_01")) || id.StartsWith("BRMA"))
                {
                    if (!from.Contains("PLAY"))
                    {
                        if (to.Contains("FRIENDLY"))
                            gameState.GameHandler.SetPlayerHero(Database.GetHeroNameFromId(id, false));
                        else if (to.Contains("OPPOSING"))
                            gameState.GameHandler.SetOpponentHero(Database.GetHeroNameFromId(id, false));
                    }
                }
            }
        }
        public void Handle(string logLine, IHsGameState gameState)
        {
            if (HsLogReaderConstants.CardMovementRegex.IsMatch(logLine))
            {
                var match = HsLogReaderConstants.CardMovementRegex.Match(logLine);

                var id = match.Groups["Id"].Value.Trim();
                var from = match.Groups["from"].Value.Trim();
                var to = match.Groups["to"].Value.Trim();

	            var card = Database.GetCardFromId(id);
                if(card != null && card.Type == "Hero")
                {
                    if (!from.Contains("PLAY"))
                    {
                        if (to.Contains("FRIENDLY"))
                            gameState.GameHandler.SetPlayerHero(Database.GetHeroNameFromId(id, false));
                        else if (to.Contains("OPPOSING"))
                            gameState.GameHandler.SetOpponentHero(Database.GetHeroNameFromId(id, false));
                    }
                }
            }
        }
		public void Handle(string logLine, IHsGameState gameState, IGame game)
		{
			var match = HsLogReaderConstants.GameModeRegex.Match(logLine);
			if(!match.Success)
				return;
			game.CurrentMode = GetMode(match.Groups["curr"].Value);
			game.PreviousMode = GetMode(match.Groups["prev"].Value);

			var newMode = GetGameMode(game.CurrentMode) ?? GetGameMode(game.PreviousMode);
			if(newMode.HasValue && !(game.CurrentGameMode == Ranked && newMode.Value == Casual))
				game.CurrentGameMode = newMode.Value;
			if(game.PreviousMode == Mode.GAMEPLAY)
				gameState.GameHandler.HandleInMenu();
			switch(game.CurrentMode)
			{
				case Mode.COLLECTIONMANAGER:
				case Mode.TAVERN_BRAWL:
					gameState.GameHandler.ResetConstructedImporting();
					break;
				case Mode.DRAFT:
					game.ResetArenaCards();
					break;
			}
		}
		public void Handle(LogLineItem logLine, IHsGameState gameState, IGame game)
		{
			var match = HsLogReaderConstants.ConnectionRegex.Match(logLine.Line);
			if(match.Success)
			{
				game.MetaData.ServerAddress = match.Groups["address"].Value.Trim();
				game.MetaData.ClientId = match.Groups["client"].Value.Trim();
				game.MetaData.GameId = match.Groups["game"].Value.Trim();
				game.MetaData.SpectateKey = match.Groups["spectateKey"].Value.Trim();

				var region = Helper.GetRegionByServerIp(game.MetaData.ServerAddress);
				if(game.CurrentRegion == Region.UNKNOWN || region == Region.CHINA)
				{
					game.CurrentRegion = region;
					Log.Info("Set current region to" + region);
				}

				//just to make sure this still works in case the typo gets fixed
				if(logLine.Line.ToLower().Contains("reconncting=true") || logLine.Line.ToLower().Contains("reconnecting=true"))
					game.StoreGameState();
				gameState.Reset();
				gameState.GameHandler.HandleGameStart();
			}
		}
		private void StepChange(IHsGameState gameState, IGame game)
		{
			if(gameState.SetupDone || game.Entities.FirstOrDefault().Value?.Name != "GameEntity")
				return;
			Log.Info("Game was already in progress.");
			gameState.WasInProgress = true;
		}
		private async void SetHeroAsync(int id, IGame game, IHsGameState gameState)
		{
			Log.Info("Found hero with id=" + id);
			if(game.PlayerEntity == null)
			{
				Log.Info("Waiting for PlayerEntity to exist");
				while(game.PlayerEntity == null)
					await Task.Delay(100);
				Log.Info("Found PlayerEntity");
			}
			if(string.IsNullOrEmpty(game.Player.Class) && id == game.PlayerEntity.GetTag(HERO_ENTITY))
			{
				Entity entity;
				if(!game.Entities.TryGetValue(id, out entity))
					return;
				gameState.GameHandler.SetPlayerHero(Database.GetHeroNameFromId(entity.CardId));
				return;
			}
			if(game.OpponentEntity == null)
			{
				Log.Info("Waiting for OpponentEntity to exist");
				while(game.OpponentEntity == null)
					await Task.Delay(100);
				Log.Info("Found OpponentEntity");
			}
			if(string.IsNullOrEmpty(game.Opponent.Class) && id == game.OpponentEntity.GetTag(HERO_ENTITY))
			{
				Entity entity;
				if(!game.Entities.TryGetValue(id, out entity))
					return;
				gameState.GameHandler.SetOpponentHero(Database.GetHeroNameFromId(entity.CardId));
			}
		}
		private void LastCardPlayedChange(IHsGameState gameState, int value) => gameState.LastCardPlayed = value;
		private void DefendingChange(IHsGameState gameState, int id, IGame game, int value)
		{
			Entity entity;
			if(!game.Entities.TryGetValue(id, out entity))
				return;
			gameState.GameHandler.HandleDefendingEntity(value == 1 ? entity : null);
		}
        private void ZoneChange(IHsGameState gameState, int id, IGame game, int value, int prevValue)
        {
            if (id <= 3)
            {
                return;
            }
            if (!game.Entities.TryGetValue(id, out var entity))
            {
                return;
            }
            if (!entity.Info.OriginalZone.HasValue)
            {
                if (prevValue != (int)Zone.INVALID && prevValue != (int)Zone.SETASIDE)
                {
                    entity.Info.OriginalZone = (Zone)prevValue;
                }
                else if (value != (int)Zone.INVALID && value != (int)Zone.SETASIDE)
                {
                    entity.Info.OriginalZone = (Zone)value;
                }
            }
            var controller = entity.GetTag(GameTag.CONTROLLER);

            switch ((Zone)prevValue)
            {
            case Zone.DECK:
                ZoneChangeFromDeck(gameState, id, game, value, prevValue, controller, entity.CardId);
                break;

            case Zone.HAND:
                ZoneChangeFromHand(gameState, id, game, value, prevValue, controller, entity.CardId);
                break;

            case Zone.PLAY:
                ZoneChangeFromPlay(gameState, id, game, value, prevValue, controller, entity.CardId);
                break;

            case Zone.SECRET:
                ZoneChangeFromSecret(gameState, id, game, value, prevValue, controller, entity.CardId);
                break;

            case Zone.INVALID:
                var maxId = GetMaxHeroPowerId(game);
                if (!gameState.SetupDone && (id <= maxId || game.GameEntity?.GetTag(GameTag.STEP) == (int)Step.INVALID && entity.GetTag(GameTag.ZONE_POSITION) < 5))
                {
                    entity.Info.OriginalZone = Zone.DECK;
                    SimulateZoneChangesFromDeck(gameState, id, game, value, entity.CardId, maxId);
                }
                else
                {
                    ZoneChangeFromOther(gameState, id, game, value, prevValue, controller, entity.CardId);
                }
                break;

            case Zone.GRAVEYARD:
            case Zone.SETASIDE:
            case Zone.REMOVEDFROMGAME:
                ZoneChangeFromOther(gameState, id, game, value, prevValue, controller, entity.CardId);
                break;

            default:
                Log.Warn($"unhandled zone change (id={id}): {prevValue} -> {value}");
                break;
            }
        }
 private void LastCardPlayedChange(IHsGameState gameState, int value) => gameState.LastCardPlayed = value;
        private void ZoneChangeFromOther(IHsGameState gameState, int id, IGame game, int value, int prevValue, int controller, string cardId)
        {
            if (!game.Entities.TryGetValue(id, out var entity))
            {
                return;
            }
            if (entity.Info.OriginalZone == Zone.DECK && value != (int)Zone.DECK)
            {
                //This entity was moved from DECK to SETASIDE to HAND, e.g. by Tracking
                entity.Info.Discarded = false;
                ZoneChangeFromDeck(gameState, id, game, value, prevValue, controller, cardId);
                return;
            }
            entity.Info.Created = true;
            switch ((Zone)value)
            {
            case Zone.PLAY:
                if (controller == game.Player.Id)
                {
                    gameState.GameHandler.HandlePlayerCreateInPlay(entity, cardId, gameState.GetTurnNumber());
                }
                if (controller == game.Opponent.Id)
                {
                    gameState.GameHandler.HandleOpponentCreateInPlay(entity, cardId, gameState.GetTurnNumber());
                }
                break;

            case Zone.DECK:
                if (controller == game.Player.Id)
                {
                    if (gameState.JoustReveals > 0)
                    {
                        break;
                    }
                    gameState.GameHandler.HandlePlayerGetToDeck(entity, cardId, gameState.GetTurnNumber());
                }
                if (controller == game.Opponent.Id)
                {
                    if (gameState.JoustReveals > 0)
                    {
                        break;
                    }
                    gameState.GameHandler.HandleOpponentGetToDeck(entity, gameState.GetTurnNumber());
                }
                break;

            case Zone.HAND:
                if (controller == game.Player.Id)
                {
                    gameState.GameHandler.HandlePlayerGet(entity, cardId, gameState.GetTurnNumber());
                }
                else if (controller == game.Opponent.Id)
                {
                    gameState.GameHandler.HandleOpponentGet(entity, gameState.GetTurnNumber(), id);
                }
                break;

            case Zone.SECRET:
                if (controller == game.Player.Id)
                {
                    gameState.GameHandler.HandlePlayerSecretPlayed(entity, cardId, gameState.GetTurnNumber(), (Zone)prevValue);
                }
                else if (controller == game.Opponent.Id)
                {
                    gameState.GameHandler.HandleOpponentSecretPlayed(entity, cardId, -1, gameState.GetTurnNumber(), (Zone)prevValue, id);
                }
                break;

            case Zone.SETASIDE:
                if (controller == game.Player.Id)
                {
                    gameState.GameHandler.HandlePlayerCreateInSetAside(entity, gameState.GetTurnNumber());
                }
                if (controller == game.Opponent.Id)
                {
                    gameState.GameHandler.HandleOpponentCreateInSetAside(entity, gameState.GetTurnNumber());
                }
                break;

            default:
                Log.Warn($"unhandled zone change (id={id}): {prevValue} -> {value}");
                break;
            }
        }