123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using MafiaTelegramBot.Controllers;
- using MafiaTelegramBot.CustomCollections.Extensions;
- using MafiaTelegramBot.DataBase;
- using MafiaTelegramBot.DataBase.EntityDao;
- using MafiaTelegramBot.Game.GameRoles;
- using MafiaTelegramBot.Models;
- using MafiaTelegramBot.Resources;
- using Microsoft.EntityFrameworkCore;
- using Timer = System.Timers.Timer;
- namespace MafiaTelegramBot.Game.GameRooms
- {
- public partial class GameRoom
- {
- public void Start()
- {
- new Task(async () =>
- {
- IsRunning = true;
- await FirstNight();
- await FirstDay();
- await GameCycle();
- await EndOfGame();
- }).Start();
- }
- private async Task FirstDay()
- {
- IsDay = true;
- await PlayersCh.SendSticker(Stickers.Sticker["Day"]);
- await PlayersCh.Send(strings.first_day_message);
- await Task.Run(async() =>
- {
- var turnsCount = _turnOrder.Count;
- Player firstPlayer = null;
- for (var i = 0; i < turnsCount; ++i)
- {
- var player = _turnOrder.Dequeue();
- if(!player.IsPlaying) continue;
- await PlayersCh.Send($"{strings.now_turn} ({player.TurnOrder}) {player.NickName}");
- if (TimerEnabled) await Bot.SendWithMarkdown2(player.ChatId, strings.you_turn_say);
- await player.CurrentRole.SpeakAction();
- if (firstPlayer != null && player.IsPlaying) _turnOrder.Enqueue(player);
- else firstPlayer = player;
- }
- _turnOrder.Enqueue(firstPlayer);
- });
- }
- private async Task FirstNight()
- {
- IsDay = false;
- await Task.Run(async() =>
- {
- //Start updating games stats
- var statsQuery = "SELECT * FROM mafia.statistics WHERE";
- foreach (var player in Players.Values)
- {
- statsQuery += $" id = {player.Id} AND role = '{player.GetRole().ToString()}' OR role = 'All' OR";
- }
- statsQuery = statsQuery.Substring(0, statsQuery.Length - 2);
- try
- {
- var statsList = UserDao.DataBase.Statistics.FromSqlRaw(statsQuery).ToArrayAsync().Result;
- foreach (var player in Players.Values)
- {
- var userProfile = UserDao.GetPlayerById(player.Id).Result;
- foreach (var row in statsList.Where(s => s.UserId == player.Id))
- row.Games++;
- if (userProfile.Statistics.Contains(Roles.All))
- {
- userProfile.Statistics[player.CurrentRole.RoleKey].Games++;
- userProfile.Statistics[Roles.All].Games++;
- }
- }
- UserDao.DataBase.Statistics.UpdateRange(statsList);
- }
- catch (Exception e)
- {
- await Console.Out.WriteLineAsync(e.Message);
- }
- //Stop updating games stats
-
- await PlayersCh.SendSticker(Stickers.Sticker["Night"]);
- await PlayersCh.Send(strings.city_falls_asleep);
- var mafia = Players.Values.Where(player => player.GetRole() is Roles.Mafia).ToArray();
- var don = Players.Values.FirstOrDefault(player => player.GetRole() is Roles.Don);
- var dame = Players.Values.FirstOrDefault(player => player.GetRole() is Roles.Dame);
- var message = strings.your_teammates;
- if (don != null) message += $"\n({don.TurnOrder}) {don.NickName} - {roles.Don}";
- if (dame != null) message += $"\n({dame.TurnOrder}) {dame.NickName} - {roles.Dame}";
- message = mafia.Aggregate(message, (current, player) => current + $"\n({player.TurnOrder}) {player.NickName}");
- await _mafiaCh.Send(message);
- var resetEvent = new ManualResetEvent(false);
- var timer = new Timer
- {
- AutoReset = false,
- Interval = Constants.FIRST_NIGHT_INTERVAL
- };
- timer.Elapsed += (_, _) => resetEvent.Set();
- timer.Start();
-
- try
- {
- await UserDao.DataBase.SaveChangesAsync();
- }
- catch (Exception e)
- {
- await Console.Out.WriteLineAsync(e.Message);
- }
- foreach (var player in Players.Values)
- {
- if (player.GetRole() is not (Roles.Don or Roles.Mafia or Roles.Dame))
- player.CurrentRole.KnownRoles.Add(player.Id, player.GetRoleName());
- else
- foreach (var p in Players.Values
- .Where(p => p.GetRole() is Roles.Don or Roles.Mafia or Roles.Dame))
- player.CurrentRole.KnownRoles.Add(p.Id, p.GetRoleName());
- }
- resetEvent.WaitOne();
- });
- }
- private async Task GameCycle()
- {
- await Task.Run(async () =>
- {
- while (GameNotEnded())
- {
- await NightPhase();
- await SummingUpPhase();
- if (!GameNotEnded()) break;
- await DayPhase();
- await DispatchPhase();
- if (!GameNotEnded()) break;
- }
- });
- }
- private bool GameNotEnded()
- {
- var playersCount = Players.Values.Count(p => p.IsAlive);
- var mafiaCount = Players.Values.Count(p => p.IsAlive && p.GetRole() is Roles.Don or Roles.Mafia or Roles.Dame);
- if (PlayersRole.ContainsKey(Roles.Werewolf)
- && PlayersRole[Roles.Werewolf].Count == 1 && PlayersRole[Roles.Werewolf][0].IsAlive
- && ((WerewolfRole) PlayersRole[Roles.Werewolf][0].CurrentRole).IsMafia) mafiaCount++;
- var villagersCount = playersCount - mafiaCount;
- return villagersCount > mafiaCount && mafiaCount > 0;
- }
- private async Task DayPhase()
- {
- IsDay = true;
-
- var discussionTimer = new Timer {AutoReset = false, Interval = Constants.DISCUSSION_INTERVAL};
- var discussionEnded = new ManualResetEvent(false);
- foreach (var player in Players.Values.Where(player => !player.IsBlocked)) player.IsSpeaker = true;
- discussionTimer.Elapsed += async (_, _) =>
- {
- foreach (var player in Players.Values) player.IsSpeaker = false;
- await PlayersCh.Send(strings.day_discussion_ended);
- discussionEnded.Set();
- };
- await PlayersCh.Send(strings.disscution_time);
- discussionTimer.Start();
- discussionEnded.WaitOne();
-
- var turnsCount = _turnOrder.Count;
- Player toEndQueue = null;
- for (var i = 0; i < turnsCount; ++i)
- {
- var player = _turnOrder.Dequeue();
- if(!Players.ContainsKey(player.Id)) continue;
- if (!player.IsBlocked && player.IsAlive)
- {
- await PlayersCh.Send($"{strings.now_turn} ({player.TurnOrder}) {player.NickName}");
- if (TimerEnabled) await Bot.SendWithMarkdown2(player.ChatId, strings.you_turn_say);
- await player.CurrentRole.SpeakAction();
- if (Players.ContainsKey(player.Id)) await player.CurrentRole.VotingAction(VoteUpList.Count == 0);
- }
- if (toEndQueue != null) _turnOrder.Enqueue(player);
- else toEndQueue = player;
- }
- _turnOrder.Enqueue(toEndQueue);
- }
- private async Task NightPhase()
- {
- IsDay = false;
- await PlayersCh.SendSticker(Stickers.Sticker["Night"]);
- await PlayersCh.Send(strings.city_falls_asleep);
- await Task.Run(async () =>
- {
- var resetEvent = new ManualResetEvent(false);
- var timer = new Timer{ Interval = Constants.NIGHT_ACTION_INTERVAL, AutoReset = false };
- foreach (var player in Players.Values)
- await player.CurrentRole.NightAction();
- timer.Elapsed += (_, _) => resetEvent.Set();
- var doctorId = PlayersRole[Roles.Doctor].Count == 1 ? PlayersRole[Roles.Doctor][0].Id : -1;
- foreach (var player in Players.Values)
- {
- if(doctorId != player.Id) player.CanBeHealed = true;
- player.CanBeBlockedNight = true;
- player.CanBeBlockedDay = true;
- player.IsBlocked = false;
- }
- timer.Start();
- resetEvent.WaitOne();
- });
- }
- public async Task SummingUpPhase()
- {
- await Task.Run(async () =>
- {
- var beforeKill = Players.Values.ToDictionary(p=>p.Id,p=>p.IsAlive);
- var mafiaNotAgree = "";
- var votes = Players.Values.Where(p=> p.IsAlive && p.CurrentRole.MafiaTargetId != -1 && p.CurrentRole.MafiaTargetId != -2 && p.CurrentRole.MafiaTargetId != -4)
- .GroupBy(p => p.CurrentRole.MafiaTargetId)
- .Select(item => new {id = item.Key, count = item.Count()})
- .ToList();
- if (votes.Count == 0)
- {
- var allMafiaVotes = Players.Values.Where(p => p.CurrentRole.MafiaTargetId is -1 or -2 or -3).Distinct().ToList();
- if (allMafiaVotes.Count == 1)
- {
- mafiaNotAgree = allMafiaVotes[0].CurrentRole.MafiaTargetId switch
- {
- -1 => strings.mafia_not_kill_message,
- -3 => strings.mafia_decided_not_kill,
- _ => "",
- };
- }
- }
- else {
- var max = votes.Max(item => item.count);
- var maxObjects = votes.Where(item => item.count == max).ToList();
- if(maxObjects.Count == 1)
- {
- if (Players.ContainsKey(maxObjects[0].id))
- await Players[maxObjects[0].id].CurrentRole.Kill();
- }
- }
- foreach (var (role, players) in PlayersRole)
- {
- if (role is Roles.Mafia)
- foreach (var mafia in players) await mafia.CurrentRole.ApplyNightActionResult();
- else if (players.Count == 1) await players[0].CurrentRole.ApplyNightActionResult();
- }
- var afterKill = Players.Values.ToDictionary(p=>p.Id,p=>p.IsAlive);
- var message = strings.city_wakes_up;
- if(afterKill.IsEquals(beforeKill)) message += strings.everyone_survived;
- else foreach (var (id, alive) in afterKill)
- if(beforeKill[id] != alive && Players.ContainsKey(id))
- {
- message += alive
- ? $"\n{Players[id].NickName} {strings.will_be_ressurected}"
- : $"\n{Players[id].NickName} {strings.will_be_killed}";
- if(!alive && Players[id].GetRole() is Roles.Don or Roles.Dame or Roles.Mafia
- && PlayersRole.ContainsKey(Roles.Werewolf) && PlayersRole[Roles.Werewolf].Count == 1)
- await ((WerewolfRole) PlayersRole[Roles.Werewolf][0].CurrentRole).TransformToMafia();
- await PlayersCh.SendTo(id, alive ? strings.you_will_be_ressurected : strings.you_will_be_killed);
- }
- message += '\n' + mafiaNotAgree;
- await PlayersCh.SendSticker(Stickers.Sticker["Day"]);
- await PlayersCh.Send(message);
- });
- }
- private async Task DefencePhase(List<Player> contenders)
- {
- await Task.Run( async () =>
- {
- var contendersCopy = contenders.ToList();
- foreach (var contender in contendersCopy.Where(p => !p.IsBlocked))
- if (Players.ContainsKey(contender.Id)) await contender.CurrentRole.DefenceAction();
- var votersPlayers = Players.Values.Where(p => p.IsAlive && !p.IsBlocked).ToArray();
- foreach (var voter in votersPlayers)
- await voter.CurrentRole.VotingAction(contendersCopy.Where(p => Players.ContainsKey(p.Id)).ToList());
- var timer = new Timer {Interval = Constants.VOTE_KILL_INTERVAL, AutoReset = false};
- var resetEvent = new ManualResetEvent(false);
- timer.Elapsed += async(_, _) =>
- {
- foreach (var voter in votersPlayers.Where(p => !VoteKillList.ContainsKey(p.Id) && Players.ContainsKey(p.Id)))
- await voter.CurrentRole.RandomVoting();
- resetEvent.Set();
- };
- timer.Start();
- resetEvent.WaitOne();
- });
- }
- private async Task DispatchPhase()
- {
- await Task.Run(async () =>
- {
- switch (VoteUpList.Count)
- {
- case 0:
- await PlayersCh.Send(strings.nothing_up_to_vote);
- break;
- case 1:
- {
- await VoteUpList[0].CurrentRole.Dispatch();
- break;
- }
- default:
- {
- await DefencePhase(VoteUpList);
- VoteUpList = await CalculateCandidates();
- switch (VoteUpList.Count)
- {
- case 0:
- await PlayersCh.Send(strings.nothing_up_to_dispatch);
- break;
- case 1:
- await VoteUpList[0].CurrentRole.Dispatch();
- break;
- case > 1:
- var copyVoteUpList = VoteUpList.ToList();
- await DefencePhase(VoteUpList);
- VoteUpList = await CalculateCandidates();
- var alivers = copyVoteUpList.Except(VoteUpList).ToList();
- foreach (var alive in alivers)
- alive.ElderRoleAchievementEvent();
-
- switch (VoteUpList.Count)
- {
- case 0:
- await PlayersCh.Send(strings.nothing_up_to_dispatch);
- break;
- case 1:
- await VoteUpList[0].CurrentRole.Dispatch();
- break;
- default:
- await PlayersCh.Send(strings.villagers_could_not_decide);
- foreach (var alive in VoteUpList)
- alive.ElderRoleAchievementEvent();
- break;
- }
- break;
- }
- break;
- }
- }
- VoteUpList.Clear();
- VoteKillList.Clear();
- });
- }
- private async Task<List<Player>> CalculateCandidates()
- {
- var message = strings.results_of_voting;
- foreach (var (playerId, targetId) in VoteKillList)
- {
- message += Players.ContainsKey(playerId) && Players.ContainsKey(targetId)
- ? $"\n({Players[playerId].TurnOrder}) {Players[playerId].NickName} {strings.vote_to} " +
- $"({Players[targetId].TurnOrder}) {Players[targetId].NickName}" : "";
- }
- if (message == strings.results_of_voting) message = strings.no_one_voted;
- await PlayersCh.Send(message);
-
- var votes = VoteUpList.Where(p=>Players.ContainsKey(p.Id))
- .GroupBy(p => p.Id)
- .Select(item => new {id = item.Key, count = item.Count()})
- .ToList();
- List<Player> result = new();
- if (votes.Count == 0) return result;
- var max = votes.Max(item => item.count);
- var maxObjects = votes.Where(item => item.count == max);
- result.AddRange(from obj in maxObjects select Players[obj.id]);
- VoteKillList.Clear();
- VoteUpList.Clear();
- return result;
- }
- private async Task PutUpVote(long playerId, long targetId, int messageId = -1, bool toKill = false)
- {
- if (Players.ContainsKey(playerId))
- {
- if (targetId == 0)
- {
- if (!toKill) await PlayersCh.SendExcept(playerId, $"{Players[playerId].NickName} {strings.skip_vote}");
- await PlayersCh.EditTo(playerId, messageId, strings.you_skip_vote);
- }
- else
- {
- if (Players.ContainsKey(targetId))
- {
- if (!toKill)
- {
- if (VoteUpList.AddUnique(Players[targetId]))
- {
- if (playerId != targetId)
- {
- await PlayersCh.SendExcept(playerId, $"{Players[playerId].NickName} {strings.put_up_vote_to} {Players[targetId].NickName}");
- await PlayersCh.EditTo(playerId, messageId, $"{strings.you_vote_player} {Players[targetId].NickName}");
- }
- else
- {
- await PlayersCh.SendExcept(playerId, $"{Players[playerId].NickName} {strings.vote_to_self}");
- await PlayersCh.EditTo(playerId, messageId, strings.you_vote_to_self);
- }
- }
- }
- else if (VoteKillList.AddUniqueByKey(playerId, targetId))
- {
- VoteUpList.Add(Players[targetId]);
- if (messageId != -1)
- {
- if (playerId != targetId)
- await PlayersCh.EditTo(playerId, messageId, $"{strings.you_vote_to_kill} {Players[targetId].NickName}");
- else
- await PlayersCh.EditTo(playerId, messageId, strings.you_vote_to_kill_self);
- }
- }
- }
- }
- }
- else await PlayersCh.SendTo(playerId, strings.this_player_left_from_game);
- }
- private async Task EndOfGame()
- {
- await Task.Run(async () =>
- {
- var aliveMafia = Players.Values.Count(p => p.GetRole() is Roles.Don or Roles.Dame or Roles.Mafia && p.IsAlive);
- if (PlayersRole.ContainsKey(Roles.Werewolf)
- && PlayersRole[Roles.Werewolf].Count == 1 && PlayersRole[Roles.Werewolf][0].IsAlive
- && ((WerewolfRole) PlayersRole[Roles.Werewolf][0].CurrentRole).IsMafia) aliveMafia++;
- var additionalResult = "";
- foreach (var (_, value) in PlayersRole)
- {
- if (value.Count != 1) continue;
- var yellowResult = await value[0].CurrentRole.IsWon();
- if (yellowResult != "") additionalResult += "\n" + yellowResult;
- }
-
- if (aliveMafia == 0)
- {
- await PlayersCh.Send(strings.villagers_won + additionalResult, exceptDied: false);
- await PlayersCh.SendSticker(Stickers.Sticker["VillagerWins"]);
- }
- else
- {
- if (aliveMafia == 1)
- {
- var player = Players.Values.FirstOrDefault(p => p.IsAlive && p.GetRole() is Roles.Don or Roles.Dame or Roles.Mafia) ??
- PlayersRole[Roles.Werewolf][0];
- player.LawyerRoleAchievementEvent();
- }
- await PlayersCh.Send(strings.mafia_won + additionalResult, exceptDied: false);
- await PlayersCh.SendSticker(Stickers.Sticker["MafiaWins"]);
- }
- if (PlayersRole.ContainsKey(Roles.Fool) && PlayersRole[Roles.Fool].Count == 1)
- await PlayersRole[Roles.Fool][0].CurrentRole.IsWon();
- var rolesMessage = strings.in_this_game_roles;
- var players = Players.Values.ToList();
-
- var statsQueryStats = "SELECT * FROM mafia.statistics WHERE";
- var statsQueryOpenRoles = "SELECT * FROM mafia.opened_roles WHERE";
-
- foreach (var player in players)
- {
- rolesMessage += $"\n({player.TurnOrder}) {player.NickName} - {player.GetRoleName()}";
- statsQueryStats += $" id = {player.Id} AND role = '{player.GetRole().ToString()}' OR role = 'All' OR";
- statsQueryOpenRoles += $" id = {player.Id} OR";
- }
-
- await PlayersCh.Send(rolesMessage);
-
- //var updatingNotifications = await PlayersCh.SendWithReturn("Update data! Wait until it disappears");
-
- statsQueryStats = statsQueryStats.Substring(0, statsQueryStats.Length - 2);
- statsQueryOpenRoles = statsQueryOpenRoles.Substring(0, statsQueryOpenRoles.Length - 2);
- try
- {
- var statsList = UserDao.DataBase.Statistics.FromSqlRaw(statsQueryStats).ToArrayAsync().Result;
- var openRolesList = UserDao.DataBase.OpenedRoles.FromSqlRaw(statsQueryOpenRoles).ToArrayAsync().Result;
-
- void UpdateWins(Player player)
- {
- var userProfile = UserDao.GetPlayerById(player.Id).Result;
- foreach (var row in statsList.Where(s => s.UserId == player.Id))
- {
- row.Wins++;
- }
- var roles = openRolesList.Where(o => o.Id == player.Id).First();
- if (roles.Hooker == false)
- roles.Hooker = true;
- if (userProfile.Statistics.Contains(Roles.All))
- {
- userProfile.Statistics[player.CurrentRole.RoleKey].Wins++;
- userProfile.Statistics[Roles.All].Wins++;
- }
- }
-
- var villagerTeam = players.Where(p => p.CurrentRole.ColorRole == 1 && p.GetRole() != Roles.Lawyer).Select(p => p.Id);
- var mafiaTeam = players.Where(p => p.CurrentRole.ColorRole == 2 || p.GetRole() == Roles.Lawyer).Select(p => p.Id);
-
- foreach (var player in players)
- {
- if (aliveMafia == 0)
- {
- if(player.CurrentRole.ColorRole == 1 && player.CurrentRole.RoleKey != Roles.Lawyer)
- {
- UpdateWins(player);
- if (player.CurrentRole.RoleKey == Roles.Werewolf)
- {
- if (player.CurrentRole.ColorRole == 1)
- player.WerewolfRoleAchievementEvent(1);
- else player.WerewolfRoleAchievementEvent(2, true);
- }
- player.ParasiteRoleAchievementEvent(villagerTeam.ToList());
- }
- else player.ResetParasiteProgress();
- }
- else
- {
- if(player.CurrentRole.ColorRole == 2 || player.CurrentRole.RoleKey == Roles.Lawyer)
- {
- UpdateWins(player);
- player.ParasiteRoleAchievementEvent(mafiaTeam.ToList());
- }
- else player.ResetParasiteProgress();
- }
- if (player.CurrentRole.ColorRole == 3)
- {
- if (player.CurrentRole.IsWon().Result != "") UpdateWins(player);
- else player.ResetParasiteProgress();
- }
-
- if (player.CurrentRole.RoleKey == Roles.Cop)
- {
- if (((CopRole) PlayersRole[Roles.Cop][0].CurrentRole).CountRed ==
- ((CopRole) PlayersRole[Roles.Cop][0].CurrentRole).CountBlack) player.JournalistAchievementEvent();
- if (((CopRole) PlayersRole[Roles.Cop][0].CurrentRole).CountRed == 0 &&
- ((CopRole) PlayersRole[Roles.Cop][0].CurrentRole).CountBlack != 0) player.DetectiveAchievementEvent();
- }
- player.ResetState();
- }
- UserDao.DataBase.Statistics.UpdateRange(statsList);
- UserDao.DataBase.OpenedRoles.UpdateRange(openRolesList);
- await UserDao.DataBase.SaveChangesAsync();
- }
- catch (Exception e)
- {
- await Console.Out.WriteLineAsync(e.Message);
- }
-
- IsRunning = false;
- IsDay = false;
- _turnOrder.Clear();
- Settings.Clear();
- foreach (var (_, list) in PlayersRole) list.Clear();
- if (!Players.ContainsKey(Owner.Id) || Players.Count == 0) new Task(async() =>
- await RoomController.DissolveRoom(RoomEncrypter.GetCode(RoomName))).Start();
- else
- {
- await Bot.SendWithMarkdown2(Owner.ChatId, strings.thanks_for_game, Keyboard.OwnerGameMenu);
- await PlayersCh.SendExcept(Owner.Id, strings.thanks_for_game, Keyboard.PlayerGameMenu);
- }
- if (Players.Count <= Constants.PLAYER_DISABLE_TIMER) StartTimer();
- await MafiaDataBase.GetInstance().SaveChangesAsync();
- });
- }
- }
- }
|