public async Task <ActionResult> AnswerGameQuestions([FromRoute] long gameId, [FromBody] AnswerGameQuestionsInputModel inputModel, CancellationToken cancellationToken) { using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { var game = await _dataContext.Games .Include(q => q.GameQuestions) .ThenInclude(q => q.Question) .ThenInclude(q => q.QuestionCategories) .FirstOrDefaultAsync(q => q.Id == gameId, cancellationToken); #region Game Validation if (game == null) { return(NotFound()); } if (game.GameState == GameStateIds.Completed || game.GameState == GameStateIds.SelectCategory) { return(BadRequest("invalid_gameState", "invalid_gameState")); } if (game.CurrentTurnPlayerId != AccountId) { return(BadRequest("invalid_turn", "invalid_turn")); } var playerStat = await _dataContext.AccountStatsSummaries.FirstOrDefaultAsync(q => q.AccountId == game.CurrentTurnPlayerId, cancellationToken); if (playerStat == null) { playerStat = new AccountStatsSummary { AccountId = game.CurrentTurnPlayerId.Value }; _dataContext.AccountStatsSummaries.Add(playerStat); } if (inputModel.Answers.Count() != game.GameQuestions.Count) { return(BadRequest("invalid_answersCount", "invalid_answersCount")); } #endregion var changedCategoryStats = new List <AccountCategoryStat>(); foreach (var answer in inputModel.Answers) { var gq = game.GameQuestions.FirstOrDefault(q => q.QuestionId == answer.QuestionId); if (gq == null) { return(BadRequest("invalid_answers", "no such question in this game.")); } var selectedAnswer = answer.SelectedAnswer; if (game.GameState == GameStateIds.Player1AnswerQuestions) { if (gq.Player1SelectedAnswer.HasValue) { return(BadRequest("invalid_answers", "question already answered.")); } gq.Player1SelectedAnswer = selectedAnswer; } else { if (gq.Player2SelectedAnswer.HasValue) { return(BadRequest("invalid_answers", "question already answered.")); } gq.Player2SelectedAnswer = selectedAnswer; } var score = gq.Question.CorrectAnswerNumber == selectedAnswer ? 1 : 0; switch (selectedAnswer) { case 1: gq.Question.Answer1ChooseHistory++; break; case 2: gq.Question.Answer2ChooseHistory++; break; case 3: gq.Question.Answer3ChooseHistory++; break; case 4: gq.Question.Answer4ChooseHistory++; break; } // Process Category stats for player foreach (var category in gq.Question.QuestionCategories) { var playerCategoryStat = await _dataContext.AccountCategoryStats.FirstOrDefaultAsync(q => q.AccountId == game.CurrentTurnPlayerId && q.CategoryId == category.Id, cancellationToken); if (playerCategoryStat == null) { playerCategoryStat = new AccountCategoryStat { AccountId = game.CurrentTurnPlayerId.Value, CategoryId = category.CategoryId }; _dataContext.AccountCategoryStats.Add(playerCategoryStat); changedCategoryStats.Add(playerCategoryStat); } playerCategoryStat.TotalQuestionsCount += 1; playerCategoryStat.CorrectAnswersCount += score; } if (answer.UsedAnswersHistoryHelper) { var accountCoin = await _dataContext.AccountItems.FirstOrDefaultAsync(q => q.AccountId == game.CurrentTurnPlayerId && q.ShopItemId == ShopItemTypeIds.Coin, cancellationToken); if (accountCoin == null || accountCoin.Quantity < _gameSettingsOptions.AnswersHistoryHelperPrice) { // TODO: cheat detected return(BadRequest("insufficient_funds", "insufficient funds to use this helper")); } accountCoin.Quantity -= _gameSettingsOptions.AnswersHistoryHelperPrice; } if (answer.UsedRemoveTwoAnswersHelper) { var accountCoin = await _dataContext.AccountItems.FirstOrDefaultAsync(q => q.AccountId == game.CurrentTurnPlayerId && q.ItemTypeId == ShopItemTypeIds.Coin, cancellationToken); if (accountCoin == null || accountCoin.Quantity < _gameSettingsOptions.RemoveTwoAnswersHelperPrice) { // TODO: cheat detected return(BadRequest("insufficient_funds", "insufficient funds to use this helper")); } accountCoin.Quantity -= _gameSettingsOptions.RemoveTwoAnswersHelperPrice; playerStat.RemoveTwoAnswersHelperUsageCount += 1; } if (answer.UsedAskMergenHelper) { var accountCoin = await _dataContext.AccountItems.FirstOrDefaultAsync(q => q.AccountId == game.CurrentTurnPlayerId && q.ItemTypeId == ShopItemTypeIds.Coin, cancellationToken); if (accountCoin == null || accountCoin.Quantity < _gameSettingsOptions.AskMergenHelperPrice) { // TODO: cheat detected return(BadRequest("insufficient_funds", "insufficient funds to use this helper")); } accountCoin.Quantity -= _gameSettingsOptions.AskMergenHelperPrice; playerStat.AskMergenHelperUsageCount += 1; } if (answer.UsedDoubleChanceHelper) { var accountCoin = await _dataContext.AccountItems.FirstOrDefaultAsync(q => q.AccountId == game.CurrentTurnPlayerId && q.ItemTypeId == ShopItemTypeIds.Coin, cancellationToken); if (accountCoin == null || accountCoin.Quantity < _gameSettingsOptions.DoubleChanceHelperPrice) { // TODO: cheat detected return(BadRequest("insufficient_funds", "insufficient funds to use this helper")); } accountCoin.Quantity -= _gameSettingsOptions.DoubleChanceHelperPrice; playerStat.DoubleChanceHelperUsageCount += 1; } if (answer.TimeExtenderHelperUsageCount > 0) { var accountCoin = await _dataContext.AccountItems.FirstOrDefaultAsync(q => q.AccountId == game.CurrentTurnPlayerId && q.ItemTypeId == ShopItemTypeIds.Coin, cancellationToken); if (accountCoin == null || accountCoin.Quantity < _gameSettingsOptions.TimeExtenderHelperPrice * answer.TimeExtenderHelperUsageCount) { // TODO: cheat detected return(BadRequest("insufficient_funds", "insufficient funds to use this helper")); } accountCoin.Quantity -= _gameSettingsOptions.TimeExtenderHelperPrice * answer.TimeExtenderHelperUsageCount; playerStat.TimeExtenderHelperUsageCount += 1; } } await _achievementService.ProcessAnswerTimeAchievementsAsync(playerStat, changedCategoryStats, cancellationToken); var gameBattleId = game.BattleId; if (game.GameQuestions.All(q => q.Player1SelectedAnswer.HasValue && q.Player2SelectedAnswer.HasValue)) { game.GameState = GameStateIds.Completed; var battle = await _dataContext.OneToOneBattles .Include(q => q.Games).ThenInclude(q => q.GameQuestions).ThenInclude(q => q.Question) .Include(q => q.Player1) .Include(q => q.Player2) .FirstOrDefaultAsync(q => q.Id == gameBattleId, cancellationToken); if (battle.Games.Count != 6) { battle.Round += 1; } if (battle.Games.Count == 6 && battle.Games.All(q => q.GameState == GameStateIds.Completed)) { // Battle Completed battle.BattleStateId = BattleStateIds.Completed; int player1CorrectAnswersCount = 0; int player2CorrectAnswersCount = 0; foreach (var battleGame in battle.Games) { player1CorrectAnswersCount += battleGame.GameQuestions.Sum(q => q.Player1SelectedAnswer == q.Question.CorrectAnswerNumber ? 1 : 0); player2CorrectAnswersCount += battleGame.GameQuestions.Sum(q => q.Player2SelectedAnswer == q.Question.CorrectAnswerNumber ? 1 : 0); } battle.Player1CorrectAnswersCount = player1CorrectAnswersCount; battle.Player2CorrectAnswersCount = player2CorrectAnswersCount; if (player1CorrectAnswersCount > player2CorrectAnswersCount) { battle.WinnerPlayerId = battle.Player1Id; } else if (player2CorrectAnswersCount > player1CorrectAnswersCount) { battle.WinnerPlayerId = battle.Player2Id; } var playersStats = await _dataContext.AccountStatsSummaries .Where(q => q.AccountId == battle.Player1Id || q.AccountId == battle.Player2Id) .ToDictionaryAsync(q => q.AccountId, q => q, cancellationToken); var player1Stats = playersStats[battle.Player1Id]; var player2Stats = playersStats[battle.Player2Id.Value]; player1Stats.TotalBattlesPlayed += 1; player2Stats.TotalBattlesPlayed += 1; if (battle.WinnerPlayerId == battle.Player1Id) { player1Stats.WinCount += 1; player2Stats.LoseCount += 1; player1Stats.LastPlayDateTime = DateTime.Now; player2Stats.LastPlayDateTime = DateTime.Now; if (await _battleManager.HasPlayedYesterday(player1Stats.AccountId, cancellationToken)) { player1Stats.ContinuousActiveDaysCount += 1; } if (await _battleManager.HasPlayedYesterday(player2Stats.AccountId, cancellationToken)) { player2Stats.ContinuousActiveDaysCount += 1; } if (player1CorrectAnswersCount == 18) { player1Stats.AceWinCount += 1; } // Experience for win player1Stats.Score += player1CorrectAnswersCount + ExperienceBase * WinExperienceMultiplier; player1Stats.Coins += player1CorrectAnswersCount + CoinBase * WinCoinMultiplier; // Experience for lose player2Stats.Score += player2CorrectAnswersCount + ExperienceBase * LoseExperienceMultiplier; player2Stats.Coins += player2CorrectAnswersCount + CoinBase * LoseCoinMultiplier; } else if (battle.WinnerPlayerId == battle.Player2Id) { player1Stats.LoseCount += 1; player2Stats.WinCount += 1; if (player2CorrectAnswersCount == 18) { player2Stats.AceWinCount += 1; } // Experience for win player2Stats.Score += player2CorrectAnswersCount + ExperienceBase * WinExperienceMultiplier; player2Stats.Coins += player2CorrectAnswersCount + CoinBase * WinCoinMultiplier; // Experience for lose player1Stats.Score += player1CorrectAnswersCount + ExperienceBase * LoseExperienceMultiplier; player1Stats.Coins += player1CorrectAnswersCount + CoinBase * LoseCoinMultiplier; } else { // Exprience for draw (drop) player1Stats.Score += player1CorrectAnswersCount + ExperienceBase * DrawExperienceMultiplier; player2Stats.Score += player2CorrectAnswersCount + ExperienceBase * DrawExperienceMultiplier; } player1Stats.Level = _levelManager.GetLevel(player1Stats.Score)?.LevelNumber ?? 0; player2Stats.Level = _levelManager.GetLevel(player2Stats.Score)?.LevelNumber ?? 0; player1Stats.WinRatio = player1Stats.WinCount / (float)player1Stats.TotalBattlesPlayed; player2Stats.WinRatio = player2Stats.WinCount / (float)player2Stats.TotalBattlesPlayed; player1Stats.LoseRatio = player1Stats.LoseCount / (float)player1Stats.TotalBattlesPlayed; player2Stats.LoseRatio = player2Stats.LoseCount / (float)player2Stats.TotalBattlesPlayed; await _achievementService.ProcessBattleAchievementsAsync(battle, player1Stats, player2Stats, cancellationToken); var account = await _accountManager.GetAsync(game.CurrentTurnPlayerId.Value, cancellationToken); var accountNotifId = game.CurrentTurnPlayerId == player1Stats.AccountId ? player2Stats.AccountId : player1Stats.AccountId; await _notificationManager.SaveAsync( new Core.Entities.Notification { AccountId = accountNotifId, Body = $"Your battle Finish with{account.Email}", NotificationTypeId = NotificationTypeIds.BattleCompleted, Title = "Battle Completed" }, cancellationToken); await _dataContext.SaveChangesAsync(cancellationToken); // Calculate Sky player1Stats.Sky = await CalculatePlayerSkyAsync(battle.Player1Id, cancellationToken); player2Stats.Sky = await CalculatePlayerSkyAsync(battle.Player2Id.Value, cancellationToken); } else { var nextPlayer = game.CurrentTurnPlayerId == battle.Player1Id ? battle.Player1 : battle.Player2; var newGame = await _gamingService.CreateGameAsync(battle, nextPlayer.Id, cancellationToken); battle.LastGame = newGame; } } else { var battle = await _dataContext.OneToOneBattles .Include(q => q.Player1) .Include(q => q.Player2) .FirstOrDefaultAsync(q => q.Id == gameBattleId, cancellationToken); var nextPlayer = game.CurrentTurnPlayerId == battle.Player1Id ? battle.Player2 : battle.Player1; game.CurrentTurnPlayerId = nextPlayer?.Id; game.GameState = game.CurrentTurnPlayerId == battle.Player1Id ? GameStateIds.Player1AnswerQuestions : GameStateIds.Player2AnswerQuestions; } await _dataContext.SaveChangesAsync(cancellationToken); transaction.Complete(); } return(Ok()); }