瀏覽代碼

Dispatch phase reworked

Tigran 4 年之前
父節點
當前提交
59b581d869

+ 8 - 8
MafiaTelegramBot.sln.DotSettings.user

@@ -1,13 +1,13 @@
 <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
-	<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=0ea868fa_002D7cf8_002D466b_002Dbf25_002Dc540fc960a6b/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="Killing_Player_In_Summing_Up_Phase #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
-  &lt;TestAncestor&gt;&#xD;
-    &lt;TestId&gt;xUnit::BB1857E4-418E-4AE5-8437-43EBD1D903FB::net5.0::MafiaTelegramBotTests.Game.Tests.GameRooms.Tests.GameRoomTests&lt;/TestId&gt;&#xD;
-  &lt;/TestAncestor&gt;&#xD;
+	<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=0ea868fa_002D7cf8_002D466b_002Dbf25_002Dc540fc960a6b/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="Killing_Player_In_Summing_Up_Phase #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;
+  &lt;TestAncestor&gt;
+    &lt;TestId&gt;xUnit::BB1857E4-418E-4AE5-8437-43EBD1D903FB::net5.0::MafiaTelegramBotTests.Game.Tests.GameRooms.Tests.GameRoomTests&lt;/TestId&gt;
+  &lt;/TestAncestor&gt;
 &lt;/SessionState&gt;</s:String>
-	<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=fbf9be8e_002Dabe2_002D485d_002Dbae0_002D5fd4fbd8c901/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" Name="Killing_Player_In_Summing_Up_Phase" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
-  &lt;TestAncestor&gt;&#xD;
-    &lt;TestId&gt;xUnit::BB1857E4-418E-4AE5-8437-43EBD1D903FB::net5.0::MafiaTelegramBotTests.Game.Tests.GameRooms.Tests.GameRoomTests.Killing_Player_In_Summing_Up_Phase&lt;/TestId&gt;&#xD;
-  &lt;/TestAncestor&gt;&#xD;
+	<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=fbf9be8e_002Dabe2_002D485d_002Dbae0_002D5fd4fbd8c901/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" Name="Killing_Player_In_Summing_Up_Phase" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;
+  &lt;TestAncestor&gt;
+    &lt;TestId&gt;xUnit::BB1857E4-418E-4AE5-8437-43EBD1D903FB::net5.0::MafiaTelegramBotTests.Game.Tests.GameRooms.Tests.GameRoomTests.Killing_Player_In_Summing_Up_Phase&lt;/TestId&gt;
+  &lt;/TestAncestor&gt;
 &lt;/SessionState&gt;</s:String>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=MafiaTelegramBot_002Fappsettings/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=MafiaTelegramBot_002FResources_002Fkeyboard/@EntryIndexedValue">True</s:Boolean>

+ 0 - 2
MafiaTelegramBot/Game/GameRoles/BodyguardRole.cs

@@ -32,8 +32,6 @@ namespace MafiaTelegramBot.Game.GameRoles
                 {
                     target.IsAlive = true;
                     Player.IsAlive = false;
-                    await Bot.SendWithMarkdown2(Player.ChatId, $"{strings.you_save_player} {target.NickName}");
-                    await Bot.SendWithMarkdown2(target.ChatId, strings.bodyguard_save_you);
                 }
             }
             

+ 2 - 2
MafiaTelegramBot/Game/GameRoles/MafiaRole.cs

@@ -14,7 +14,7 @@ namespace MafiaTelegramBot.Game.GameRoles
         {
             Player.IsSpeaker = true;
             var targets = Room.Players.Values.Where(p => p.IsAlive).ToList();
-            var message = await Bot.SendWithMarkdown2(Player.ChatId, strings.choose_player_to_kill, 
+            var message = await Room.PlayersCh.SendTo(Player.ChatId, strings.choose_player_to_kill, 
                 Keyboard.NightChooseTargetKeyboard(targets, Player.Id, true));
             MessageId = message.MessageId;
         }
@@ -29,7 +29,7 @@ namespace MafiaTelegramBot.Game.GameRoles
         public override async Task SetNightTarget(long userId)
         {
             NightTargetId = userId;
-            await Bot.EditMessageAsync(Player.ChatId, MessageId, $"{strings.you_choose_target} {Room.Players[userId].NickName}");
+            await Room.PlayersCh.EditTo(Player.Id, MessageId, $"{strings.you_choose_target} {Room.Players[userId].NickName}");
         }
 
         public MafiaRole(GameRoom room, Player player) : base(room, player) { }

+ 86 - 46
MafiaTelegramBot/Game/GameRooms/GameRoom.GameProcess.cs

@@ -39,7 +39,7 @@ namespace MafiaTelegramBot.Game.GameRooms
                     var player = _turnOrder.Dequeue();
                     if(!player.IsPlaying) continue;
                     await PlayersCh.Send($"{strings.now_turn} \\({player.TurnOrder}\\) {player.NickName}");
-                    await Bot.SendWithMarkdown2(player.ChatId, strings.you_turn_say);
+                    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;
@@ -116,7 +116,7 @@ namespace MafiaTelegramBot.Game.GameRooms
                 if(!player.IsPlaying || !player.IsAlive) continue;
                 if (firstPlayer == null) player.IsFirst = true;
                 await PlayersCh.Send($"{strings.now_turn} \\({player.TurnOrder}\\) {player.NickName}");
-                await Bot.SendWithMarkdown2(player.ChatId, strings.you_turn_say);
+                if (TimerEnabled) await Bot.SendWithMarkdown2(player.ChatId, strings.you_turn_say);
                 await player.CurrentRole.SpeakAction();
                 await player.CurrentRole.VotingAction();
                 if (player.IsFirst) firstPlayer = player;
@@ -155,24 +155,21 @@ namespace MafiaTelegramBot.Game.GameRooms
                 {
                     if (role is Roles.Mafia)
                     {
-                        var votes = players.Where(p=> p.IsAlive)
-                            .GroupBy(p => p.CurrentRole.GetNightTarget())
+                        var votes = players.Where(p=> p.IsAlive && p.CurrentRole.NightTargetId != -1)
+                            .GroupBy(p => p.CurrentRole.NightTargetId)
                             .Select(item => new {id = item.Key, count = item.Count()})
                             .ToList();
                         if (votes.Count > 0)
                         {
                             var max = votes.Max(item => item.count);
                             var maxCount = votes.Count(item => item.count == max);
-                            if (maxCount != 1)
-                            {
-                                mafiaNotAgree = $"\n{strings.mafia_not_agree}";
-                                continue;
-                            }
-
-                            var selected = votes.First(item => item.count == max);
-                            if (selected.id == -1) continue;
-                            await Players[selected.id].CurrentRole.Kill();
+                            if (maxCount > 1) continue;
+                            var selected = votes.First(item => item.count == max).id;
+                            if (selected == -1) continue;
+                            await Players[selected].CurrentRole.Kill();
                         }
+                        else
+                            mafiaNotAgree = strings.mafia_not_kill_message;
                         foreach (var mafia in players) await mafia.CurrentRole.ApplyNightActionResult();
                     }
                     else if (players.Count == 1)
@@ -186,41 +183,38 @@ namespace MafiaTelegramBot.Game.GameRooms
                 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) message += alive
-                        ? $"{Players[id].NickName} {strings.will_be_ressurected}"
-                        : $"{Players[id].NickName} {strings.will_be_killed}";
+                        if(beforeKill[id] != alive) {
+                            message += alive
+                            ? $"{Players[id].NickName} {strings.will_be_ressurected}"
+                            : $"{Players[id].NickName} {strings.will_be_killed}";
+                            await PlayersCh.SendTo(id, alive ? strings.you_will_be_ressurected : strings.you_will_be_killed);
+                        }
                 message += mafiaNotAgree;
                 await PlayersCh.SendSticker(Stickers.Sticker["Day"]);
                 await PlayersCh.Send(message);
             });
         }
 
-        private async Task<List<Player>> DefencePhase()
+        private async Task DefencePhase(List<Player> contenders)
         {
-            return await Task.Run( async () =>
+            await Task.Run( async () =>
             {
-                var contendersForDispatch = _voteUpList.Where(p => p.VotedCount == _largeVote).ToList();
-                foreach (var defender in _voteUpList) defender.VotedCount = 0;
-                _voteUpList.Clear();
-                _largeVote = 0;
-                if (contendersForDispatch.Count == 1) return contendersForDispatch;
-                foreach (var defender in contendersForDispatch) await defender.CurrentRole.DefenceAction();
-                var votersPlayers = Players.Values
-                    .Where(p => p.IsAlive)
-                    .Except(contendersForDispatch)
-                    .ToList();
+                VoteUpList.Clear();
+                VoteKillList.Clear();
+                foreach (var contender in contenders) await contender.CurrentRole.DefenceAction();
+                var votersPlayers = Players.Values.Where(p => p.IsAlive).ToArray();
                 Timer votingTimer = new() {Interval = 10 * 1000, AutoReset = false};
                 var resetEvent = new ManualResetEvent(false);
                 votingTimer.Elapsed += (_, _) => resetEvent.Set();
                 List<Message> messagesToDelete = new();
                 foreach (var player in votersPlayers)
-                    messagesToDelete.Add(await player.CurrentRole.SendVotingList(contendersForDispatch));
+                    messagesToDelete.Add(await PlayersCh.SendTo(player.ChatId, strings.you_have_ten_seconds,  
+                        Keyboard.VoteKeyboard(contenders, player.Id, vote: Callback.VoteToKill)));
                 votingTimer.Start();
                 resetEvent.WaitOne();
                 votingTimer.Stop();
                 foreach (var message in messagesToDelete)
-                    await Bot.EditMessageAsync(message.Chat.Id, message.MessageId, strings.time_out);
-                return _voteUpList.Where(p => p.VotedCount == _largeVote).ToList();
+                    await PlayersCh.EditTo(message.Chat.Id, message.MessageId, strings.time_out);
             });
         }
 
@@ -228,28 +222,74 @@ namespace MafiaTelegramBot.Game.GameRooms
         {
             await Task.Run(async () =>
             {
-                var contenders = await DefencePhase();
-                if (contenders.Count > 1) contenders = await DefencePhase();
-                if (contenders.Count is > 1 or 0 ) await PlayersCh.Send(strings.villagers_could_not_decide);
-                else await contenders[0].CurrentRole.Dispatch();
+                if (VoteUpList.Count == 1) await VoteUpList[0].CurrentRole.Dispatch();
+                else
+                {
+                    await DefencePhase(VoteUpList);
+                    var contenders = await CalculateCandidates();
+                    switch (contenders.Count)
+                    {
+                        case 1:
+                            await VoteUpList[0].CurrentRole.Dispatch();
+                            break;
+                        case > 1:
+                            await DefencePhase(contenders);
+                            contenders = await CalculateCandidates();
+                            if (contenders.Count == 1) await VoteUpList[0].CurrentRole.Dispatch();
+                            else await PlayersCh.Send(strings.villagers_could_not_decide);
+                            break;
+                        default:
+                            await PlayersCh.Send(strings.villagers_could_not_decide);
+                            break;
+                    }
+                }
             });
         }
 
-        private async Task<Message> PutUpVote(long playerId, long targetId)
+        private async Task<List<Player>> CalculateCandidates()
+        {
+            var message = strings.results_of_voting;
+            foreach (var (playerId, targetId) in VoteKillList)
+                message += $"\n{Players[playerId]} {strings.vote_to} {Players[targetId]}";
+            if (message == strings.results_of_voting) message = strings.no_one_voted;
+            await PlayersCh.Send(message);
+            
+            var votes = VoteUpList.Where(p=> p.IsAlive && p.IsPlaying)
+                .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(maxObjects.Select(obj => Players[obj.id]));
+            return result.ToList();
+        }
+
+        private async Task PutUpVote(long playerId, long targetId, int messageId, bool toKill = false)
         {
             var player = Players[playerId];
-            var target = Players[targetId];
-            _voteUpList.AddUnique(target);
-            target.VotedCount++;
-            if (target.VotedCount > _largeVote) _largeVote = target.VotedCount; 
-            if (playerId != targetId)
+            if (targetId == 0)
             {
-                
-                await PlayersCh.SendExcept(player.ChatId, $"{player.NickName} {strings.vote_to} {target.NickName}");
-                return await Bot.SendWithMarkdown2(player.ChatId, $"{strings.you_vote_player} {target.NickName}");
+                if (!toKill) await PlayersCh.SendExcept(player.ChatId, $"{player.NickName} {strings.skip_vote}");
+                await PlayersCh.EditTo(player.ChatId, messageId, strings.you_skip_vote);
+            }
+            else
+            {
+                var target = Players[targetId];
+                VoteUpList.Add(target);
+                if (toKill) VoteKillList.Add(playerId, targetId);
+                if (playerId != targetId)
+                {
+                    if (!toKill) await PlayersCh.SendExcept(player.ChatId, $"{player.NickName} {strings.put_up_vote_to} {target.NickName}");
+                    await PlayersCh.EditTo(player.ChatId, messageId, $"{strings.you_vote_player} {target.NickName}");
+                }
+                else
+                {
+                    if (!toKill) await PlayersCh.SendExcept(player.ChatId, $"{player.NickName} {strings.vote_to_self}");
+                    await PlayersCh.EditTo(player.ChatId, messageId, strings.you_vote_to_self);
+                } 
             }
-            await PlayersCh.SendExcept(player.ChatId, $"{player.NickName} {strings.vote_to_self}");
-            return await Bot.SendWithMarkdown2(player.ChatId, strings.you_vote_to_self);
         }
 
         private async Task EndOfGame()

+ 12 - 0
MafiaTelegramBot/Game/GameRooms/GameRoom.MessageChannels.cs

@@ -2,6 +2,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using MafiaTelegramBot.Models;
 using MafiaTelegramBot.Resources;
+using Telegram.Bot.Types;
 using Telegram.Bot.Types.ReplyMarkups;
 
 namespace MafiaTelegramBot.Game.GameRooms
@@ -26,6 +27,17 @@ namespace MafiaTelegramBot.Game.GameRooms
 
         public class PlayersChannel : Channel
         {
+            public async Task<Message> SendTo(long userId, string message, IReplyMarkup replyMarkup = null)
+            {
+                if(Room.Players.ContainsKey(userId)) return await Bot.SendWithMarkdown2(Room.Players[userId].ChatId, message, replyMarkup);
+                return new Message();
+            }
+            
+            public async Task EditTo(long userId, int messageId, string message, InlineKeyboardMarkup replyMarkup = null)
+            {
+                if(Room.Players.ContainsKey(userId)) await Bot.EditMessageAsync(Room.Players[userId].ChatId, messageId, message, replyMarkup);
+            }
+            
             public override async Task Send(string message, IReplyMarkup replyMarkup = null,
                 bool exceptDied = false)
             {

+ 7 - 16
MafiaTelegramBot/Game/GameRooms/GameRoom.QueryHandler.cs

@@ -23,36 +23,27 @@ namespace MafiaTelegramBot.Game.GameRooms
                 var data = update.CallbackQuery.Data.Split('|');
                 var userId = long.Parse(data[1]);
                 var player = await UserDao.GetPlayerById(userId);
+                var messageId = update.CallbackQuery.Message.MessageId;
                 var chatId = player.ChatId;
                 var command = Enum.Parse<Callback>(data[0]);
                 switch (command)
                 {
                     case Callback.Vote:
-                        await VoteAsync(player, long.Parse(data[2]));
+                        await _room.PutUpVote(player.Id, long.Parse(data[2]), messageId);
+                        player.CurrentRole.VoteActionComplete.Set();
                         break;
-                    case Callback.Skip:
-                        await SkipAsync(player);
+                    case Callback.VoteToKill:
+                        await _room.PutUpVote(player.Id, long.Parse(data[2]), messageId, true);
+                        player.CurrentRole.VoteActionComplete.Set();
                         break;
                     case Callback.Target:
                         await player.CurrentRole.SetNightTarget(long.Parse(data[2]));
                         break;
                     default:
-                        await Bot.SendWithMarkdown2(chatId, strings.not_supported_in_game);
+                        await _room.PlayersCh.SendTo(chatId, strings.not_supported_in_game);
                         break;
                 }
             }
-
-            private async Task<Message> VoteAsync(Player player, long targetId)
-            {
-                var message = await _room.PutUpVote(player.Id, targetId);
-                player.CurrentRole.VoteActionComplete.Set();
-                return message;
-            }
-
-            private async Task<Message> SkipAsync(Player player)
-            {
-                return await Bot.SendWithMarkdown2(player.ChatId, strings.you_skip_vote);
-            }
         }
     }
 }

+ 17 - 32
MafiaTelegramBot/Game/GameRooms/GameRoom.Role.cs

@@ -18,7 +18,7 @@ namespace MafiaTelegramBot.Game.GameRooms
             protected readonly GameRoom Room;
             protected readonly Player Player;
             public readonly List<Player> KnownRoles = new();
-            protected long NightTargetId = -1;
+            public long NightTargetId = -1;
             public readonly ManualResetEvent TalkingActionComplete = new(false);
             public readonly ManualResetEvent VoteActionComplete = new(false);
             protected int MessageId = -1;
@@ -32,7 +32,7 @@ namespace MafiaTelegramBot.Game.GameRooms
                 NightTargetId = -1;
                 MessageId = -1;
                 Player.IsSpeaker = false;
-                await Bot.SendWithMarkdown2(Player.ChatId, message);
+                await Room.PlayersCh.SendTo(Player.ChatId, message);
             }
             public virtual Task SetNightTarget(long userId) { return Task.CompletedTask; }
             
@@ -47,7 +47,7 @@ namespace MafiaTelegramBot.Game.GameRooms
                 else
                 {
                     await Room.PlayersCh.SendExcept(Player.Id, $"{strings.to_player} {Player.NickName} {strings.issued_posthumous_minute}");
-                    await Bot.SendWithMarkdown2(Player.ChatId, strings.you_turn_say);
+                    await Room.PlayersCh.SendTo(Player.ChatId, strings.you_turn_say);
                     await SpeakAction(enableTimer: true);
                     await Kill();
                 }
@@ -58,12 +58,9 @@ namespace MafiaTelegramBot.Game.GameRooms
                 if (Room.PlayersRole.ContainsKey(Roles.Hooker)
                     && Room.PlayersRole[Roles.Hooker].Count == 1
                     && Room.PlayersRole[Roles.Hooker][0].CurrentRole.NightTargetId == Player.Id
-                    && !Room.IsDay) await Room.PlayersRole[Roles.Hooker][0].CurrentRole.Kill();
-                else
-                {
-                    Player.IsAlive = false;
-                    await Bot.SendWithMarkdown2(Player.ChatId, strings.you_died);
-                }
+                    && !Room.IsDay)
+                    await Room.PlayersRole[Roles.Hooker][0].CurrentRole.Kill();
+                else Player.IsAlive = false;
             }
 
             protected Role(GameRoom room, Player player)
@@ -72,57 +69,45 @@ namespace MafiaTelegramBot.Game.GameRooms
                 Player = player;
             }
 
-            public long GetNightTarget()
-            {
-                return NightTargetId;
-            }
-
             public async Task SpeakAction(int seconds = 60, bool enableTimer = false)
             {
                 TalkingActionComplete.Reset();
-                await Bot.SendWithMarkdown2(Player.ChatId, $"{strings.your_turn}", Keyboard.InGamePlayerMenuWithEndTurn);
+                await Room.PlayersCh.SendTo(Player.ChatId, $"{strings.your_turn}", Keyboard.InGamePlayerMenuWithEndTurn);
                 Player.IsSpeaker = true;
                 var timer = new Timer(seconds * 1000) {AutoReset = false};
                 timer.Elapsed += (_, _) => TalkingActionComplete.Set();
                 if (Room.TimerEnabled || enableTimer) timer.Start();
                 TalkingActionComplete.WaitOne();
                 if (Room.TimerEnabled || enableTimer) timer.Stop();
-                await Bot.SendWithMarkdown2(Player.ChatId, strings.your_turn_ended, Keyboard.InGamePlayerMenu);
+                await Room.PlayersCh.SendTo(Player.ChatId, strings.your_turn_ended, Keyboard.InGamePlayerMenu);
                 Player.IsSpeaker = false;
             }
 
             public async Task VotingAction()
             {
                 var voteTimer = new Timer(10 * 1000) {AutoReset = false};
-                var alivePlayers = Room.Players.Values.Where(p => p.IsAlive).ToList();
-                var message = await SendVotingList(alivePlayers, !Player.IsFirst);
+                var alivePlayers = Room.Players.Values.Where(p => p.IsAlive).Except(Room.VoteUpList).ToList();
+                var message = await Room.PlayersCh.SendTo(Player.ChatId,
+                    $"{strings.put_up_vote}\n{strings.you_have_ten_seconds}",  
+                    Keyboard.VoteKeyboard(alivePlayers, Player.Id, !Player.IsFirst));
                 voteTimer.Elapsed += async (_, _) =>
                 {
-                    await Bot.EditMessageAsync(Player.ChatId, message.MessageId, strings.time_out);
-                    if (Player.IsFirst)
-                        await Room.PutUpVote(Player.Id, Player.Id);
-                    else
-                        await Room.PlayersCh.SendExcept(Player.Id, $"{Player.NickName} {strings.didnt_put_anyone}");
+                    if (Player.IsFirst) await Room.PutUpVote(Player.Id, Player.Id, message.MessageId);
+                    else await Room.PutUpVote(Player.Id, 0, message.MessageId);
                     VoteActionComplete.Set();
                 };
-                if(Player.IsFirst) await Bot.SendWithMarkdown2(Player.Id, strings.user_not_choose);
+                if(Player.IsFirst) await Room.PlayersCh.SendTo(Player.Id, strings.user_not_choose);
                 voteTimer.Start();
                 VoteActionComplete.WaitOne();
+                VoteActionComplete.Reset();
                 voteTimer.Stop();
                 if(message!=null) await Bot.Get().DeleteMessageAsync(Player.ChatId, message.MessageId);
             }
-
-            public async Task<Message> SendVotingList(List<Player> targets, bool skipButton = true)
-            {
-                VoteActionComplete.Reset();
-                return await Bot.SendWithMarkdown2(Player.ChatId, $"{strings.put_up_vote}\n{strings.you_have_ten_seconds}", 
-                    Keyboard.VoteKeyboard(targets, Player.Id, skipButton));
-            }
             
             public async Task DefenceAction()
             {
                 await Room.PlayersCh.SendExcept(Player.Id, $"{strings.now_defence} \\({Player.TurnOrder}\\) {Player.NickName}");
-                await Bot.SendWithMarkdown2(Player.ChatId, strings.you_have_eigty_seconds_to_defence);
+                await Room.PlayersCh.SendTo(Player.ChatId, strings.you_have_eigty_seconds_to_defence);
                 await SpeakAction(90);
             }
             public static Role GetNewRoleInstance(Roles roleKey, GameRoom room, Player player)

+ 2 - 1
MafiaTelegramBot/Game/GameRooms/GameRoom.Structure.cs

@@ -19,7 +19,8 @@ namespace MafiaTelegramBot.Game.GameRooms
 
         private int _largeVote; //большее из количеств голосов за игроков 
 
-        private readonly List<Player> _voteUpList = new();
+        public readonly List<Player> VoteUpList = new();
+        public readonly Dictionary<long, long> VoteKillList = new();
 
         public readonly MessageHandler MHandler;
         public readonly QueryHandler QHandler;

+ 1 - 3
MafiaTelegramBot/Game/Player.cs

@@ -20,17 +20,15 @@ namespace MafiaTelegramBot.Game
         public int TurnOrder = -1;
         private string _roomName = "";
 
-        private static System.Timers.Timer ActiveTime;
+        private static System.Timers.Timer ActiveTime = new();
         public bool IsSpeaker;
         public bool IsPlaying;
         public bool IsFirst;
-        
         public bool IsAlive = true;
         public bool CanBeHealed = true;
         public bool IsBlocked = false;
         public bool CanBeBlockedNight = true;
         public bool CanBeBlockedDay = true;
-        public int VotedCount = 0; //количество голосов за игрока
 
         public readonly StatisticsList Statistics = new();
         public OpenedRolesEntity OpenedRoles = new();

+ 1 - 0
MafiaTelegramBot/Resources/Callback.cs

@@ -21,6 +21,7 @@ namespace MafiaTelegramBot.Resources
         ChangeRoles,
         ApplyRolesChange,
         Vote,
+        VoteToKill,
         Skip,
         Target
     }

+ 3 - 3
MafiaTelegramBot/Resources/Keyboard.cs

@@ -137,7 +137,7 @@ namespace MafiaTelegramBot.Resources
             return new InlineKeyboardMarkup(inlineButtons);
         }
 
-        public static InlineKeyboardMarkup VoteKeyboard(List<Player> players, long userId, bool withSkipButton)
+        public static InlineKeyboardMarkup VoteKeyboard(List<Player> players, long userId, bool withSkipButton = true, Callback vote = Callback.Vote)
         {
             players.Sort((x, y) => x.TurnOrder - y.TurnOrder);
             var inlineButtons = new InlineKeyboardButton[withSkipButton ?players.Count+1 :players.Count][];
@@ -145,11 +145,11 @@ namespace MafiaTelegramBot.Resources
             {
                 inlineButtons[i] = new[]
                 {
-                    InlineKeyboardButton.WithCallbackData($"({players[i].TurnOrder}) {players[i].NickName}", $"{Callback.Vote}|{userId}|{players[i].Id}")
+                    InlineKeyboardButton.WithCallbackData($"({players[i].TurnOrder}) {players[i].NickName}", $"{vote}|{userId}|{players[i].Id}")
                 };
             }
             if (withSkipButton) inlineButtons[players.Count] = new[]
-                    {InlineKeyboardButton.WithCallbackData(strings.skip, $"{Callback.Skip}|{userId}")};
+                    {InlineKeyboardButton.WithCallbackData(strings.skip, $"{vote}|{userId}|0")};
             return inlineButtons;
         }
 

+ 40 - 76
MafiaTelegramBot/Resources/strings.Designer.cs

@@ -351,18 +351,6 @@ namespace MafiaTelegramBot {
             }
         }
         
-        internal static string enable {
-            get {
-                return ResourceManager.GetString("enable", resourceCulture);
-            }
-        }
-        
-        internal static string disable {
-            get {
-                return ResourceManager.GetString("disable", resourceCulture);
-            }
-        }
-        
         internal static string _public {
             get {
                 return ResourceManager.GetString("public", resourceCulture);
@@ -477,18 +465,6 @@ namespace MafiaTelegramBot {
             }
         }
         
-        internal static string villagers_are_sleep {
-            get {
-                return ResourceManager.GetString("villagers_are_sleep", resourceCulture);
-            }
-        }
-        
-        internal static string mafia_get_mail {
-            get {
-                return ResourceManager.GetString("mafia_get_mail", resourceCulture);
-            }
-        }
-        
         internal static string alive {
             get {
                 return ResourceManager.GetString("alive", resourceCulture);
@@ -513,9 +489,9 @@ namespace MafiaTelegramBot {
             }
         }
         
-        internal static string didnt_put_anyone {
+        internal static string skip_vote {
             get {
-                return ResourceManager.GetString("didnt_put_anyone", resourceCulture);
+                return ResourceManager.GetString("skip_vote", resourceCulture);
             }
         }
         
@@ -549,9 +525,9 @@ namespace MafiaTelegramBot {
             }
         }
         
-        internal static string vote_to {
+        internal static string put_up_vote_to {
             get {
-                return ResourceManager.GetString("vote_to", resourceCulture);
+                return ResourceManager.GetString("put_up_vote_to", resourceCulture);
             }
         }
         
@@ -603,12 +579,6 @@ namespace MafiaTelegramBot {
             }
         }
         
-        internal static string doctor_heals_you {
-            get {
-                return ResourceManager.GetString("doctor_heals_you", resourceCulture);
-            }
-        }
-        
         internal static string dame_block_you {
             get {
                 return ResourceManager.GetString("dame_block_you", resourceCulture);
@@ -621,24 +591,12 @@ namespace MafiaTelegramBot {
             }
         }
         
-        internal static string you_save_player {
-            get {
-                return ResourceManager.GetString("you_save_player", resourceCulture);
-            }
-        }
-        
         internal static string nothing_to_heal {
             get {
                 return ResourceManager.GetString("nothing_to_heal", resourceCulture);
             }
         }
         
-        internal static string doctor_save_you {
-            get {
-                return ResourceManager.GetString("doctor_save_you", resourceCulture);
-            }
-        }
-        
         internal static string at_this_night {
             get {
                 return ResourceManager.GetString("at_this_night", resourceCulture);
@@ -717,12 +675,6 @@ namespace MafiaTelegramBot {
             }
         }
         
-        internal static string you_died {
-            get {
-                return ResourceManager.GetString("you_died", resourceCulture);
-            }
-        }
-        
         internal static string villagers_want_dispatch {
             get {
                 return ResourceManager.GetString("villagers_want_dispatch", resourceCulture);
@@ -741,24 +693,12 @@ namespace MafiaTelegramBot {
             }
         }
         
-        internal static string mafia_not_agree {
-            get {
-                return ResourceManager.GetString("mafia_not_agree", resourceCulture);
-            }
-        }
-        
         internal static string you_heal_yourself {
             get {
                 return ResourceManager.GetString("you_heal_yourself", resourceCulture);
             }
         }
         
-        internal static string you_save_yourself {
-            get {
-                return ResourceManager.GetString("you_save_yourself", resourceCulture);
-            }
-        }
-        
         internal static string change_roles {
             get {
                 return ResourceManager.GetString("change_roles", resourceCulture);
@@ -777,18 +717,6 @@ namespace MafiaTelegramBot {
             }
         }
         
-        internal static string minus {
-            get {
-                return ResourceManager.GetString("minus", resourceCulture);
-            }
-        }
-        
-        internal static string plus {
-            get {
-                return ResourceManager.GetString("plus", resourceCulture);
-            }
-        }
-        
         internal static string current_enabled_roles {
             get {
                 return ResourceManager.GetString("current_enabled_roles", resourceCulture);
@@ -866,5 +794,41 @@ namespace MafiaTelegramBot {
                 return ResourceManager.GetString("in_this_game_we_have", resourceCulture);
             }
         }
+        
+        internal static string mafia_not_kill_message {
+            get {
+                return ResourceManager.GetString("mafia_not_kill_message", resourceCulture);
+            }
+        }
+        
+        internal static string you_will_be_ressurected {
+            get {
+                return ResourceManager.GetString("you_will_be_ressurected", resourceCulture);
+            }
+        }
+        
+        internal static string you_will_be_killed {
+            get {
+                return ResourceManager.GetString("you_will_be_killed", resourceCulture);
+            }
+        }
+        
+        internal static string vote_to {
+            get {
+                return ResourceManager.GetString("vote_to", resourceCulture);
+            }
+        }
+        
+        internal static string results_of_voting {
+            get {
+                return ResourceManager.GetString("results_of_voting", resourceCulture);
+            }
+        }
+        
+        internal static string no_one_voted {
+            get {
+                return ResourceManager.GetString("no_one_voted", resourceCulture);
+            }
+        }
     }
 }

+ 22 - 40
MafiaTelegramBot/Resources/strings.resx

@@ -171,12 +171,6 @@
     <data name="disabled" xml:space="preserve">
         <value>отключен</value>
     </data>
-    <data name="enable" xml:space="preserve">
-        <value>Включить</value>
-    </data>
-    <data name="disable" xml:space="preserve">
-        <value>Отключить</value>
-    </data>
     <data name="public" xml:space="preserve">
         <value>Публичная</value>
     </data>
@@ -234,12 +228,6 @@
     <data name="you_now_died" xml:space="preserve">
         <value>Вы мертвы и не можете говорить!</value>
     </data>
-    <data name="villagers_are_sleep" xml:space="preserve">
-        <value>Жители ночью спят, а не разговаривают!</value>
-    </data>
-    <data name="mafia_get_mail" xml:space="preserve">
-        <value>В первую ночь мафия получает список соучастников.</value>
-    </data>
     <data name="alive" xml:space="preserve">
         <value>живой</value>
     </data>
@@ -252,8 +240,8 @@
     <data name="put_up_vote" xml:space="preserve">
         <value>Кого вы хотите выставить на голосование?</value>
     </data>
-    <data name="didnt_put_anyone" xml:space="preserve">
-        <value>никого не выставил на голосование</value>
+    <data name="skip_vote" xml:space="preserve">
+        <value>пропустил голосование</value>
     </data>
     <data name="skip" xml:space="preserve">
         <value>Пропустить</value>
@@ -270,8 +258,8 @@
     <data name="you_vote_player" xml:space="preserve">
         <value>Вы выбрали игрока</value>
     </data>
-    <data name="vote_to" xml:space="preserve">
-        <value>проголосовал против</value>
+    <data name="put_up_vote_to" xml:space="preserve">
+        <value>выставил на голосование игрока</value>
     </data>
     <data name="vote_to_self" xml:space="preserve">
         <value>выставил самого себя.</value>
@@ -297,24 +285,15 @@
     <data name="you_turn_say" xml:space="preserve">
         <value>У Вас есть минута, чтобы что-то сказать</value>
     </data>
-    <data name="doctor_heals_you" xml:space="preserve">
-        <value>Доктор вылечил вас</value>
-    </data>
     <data name="dame_block_you" xml:space="preserve">
         <value>Дама заблокировала вас. Вы не сможете разговаривать и голосовать в этот день.</value>
     </data>
     <data name="hooker_block_you" xml:space="preserve">
         <value>Проститутка переспала с вами. Ваши действия этой ночью ограничены.</value>
     </data>
-    <data name="you_save_player" xml:space="preserve">
-        <value>Вы спасли игрока</value>
-    </data>
     <data name="nothing_to_heal" xml:space="preserve">
         <value>Вам некого лечить этой ночью</value>
     </data>
-    <data name="doctor_save_you" xml:space="preserve">
-        <value>Доктор спас вас от смерти</value>
-    </data>
     <data name="at_this_night" xml:space="preserve">
         <value>Этой ночью был убит</value>
     </data>
@@ -354,9 +333,6 @@
     <data name="issued_posthumous_minute" xml:space="preserve">
         <value>выдается посмертная минута.</value>
     </data>
-    <data name="you_died" xml:space="preserve">
-        <value>Вы убиты</value>
-    </data>
     <data name="villagers_want_dispatch" xml:space="preserve">
         <value>Жители хотели повесить игрока</value>
     </data>
@@ -366,15 +342,9 @@
     <data name="activity_blocked" xml:space="preserve">
         <value>Действие заблокировано</value>
     </data>
-    <data name="mafia_not_agree" xml:space="preserve">
-        <value>Мафия не смогла договориться.</value>
-    </data>
     <data name="you_heal_yourself" xml:space="preserve">
         <value>Вы вылечили сами себя</value>
     </data>
-    <data name="you_save_yourself" xml:space="preserve">
-        <value>Вы спасли себя</value>
-    </data>
     <data name="change_roles" xml:space="preserve">
         <value>Изменить рассадку</value>
     </data>
@@ -384,12 +354,6 @@
     <data name="what_settings" xml:space="preserve">
         <value>Что вы хотите настроить?</value>
     </data>
-    <data name="minus" xml:space="preserve">
-        <value>-</value>
-    </data>
-    <data name="plus" xml:space="preserve">
-        <value>+</value>
-    </data>
     <data name="current_enabled_roles" xml:space="preserve">
         <value>Текущая рассадка в комнате:</value>
     </data>
@@ -429,4 +393,22 @@
     <data name="in_this_game_we_have" xml:space="preserve">
         <value>В этой игре имеются следующие роли:</value>
     </data>
+    <data name="mafia_not_kill_message" xml:space="preserve">
+        <value>Мафия не успела выбрать жертву</value>
+    </data>
+    <data name="you_will_be_ressurected" xml:space="preserve">
+        <value>Вы были воскрешены</value>
+    </data>
+    <data name="you_will_be_killed" xml:space="preserve">
+        <value>Вы были убиты</value>
+    </data>
+    <data name="vote_to" xml:space="preserve">
+        <value>проголосовал против</value>
+    </data>
+    <data name="results_of_voting" xml:space="preserve">
+        <value>Итоги голосования:</value>
+    </data>
+    <data name="no_one_voted" xml:space="preserve">
+        <value>Ни кто ни за кого не проголосовал</value>
+    </data>
 </root>