]> git.deb.at Git - rhonda/impostor.git/commitdiff
Refresh Murder Event API
authorminiduikboot <mini@duikbo.at>
Sat, 28 Oct 2023 22:37:30 +0000 (00:37 +0200)
committerminiduikboot <mini@duikbo.at>
Sat, 28 Oct 2023 22:37:30 +0000 (00:37 +0200)
- IPlayerCheckMurderEvent now exists
- You can see if a kill was successful with IPlayerMurderEvent
- You can now see if host authority was requested with Game.IsHostAuthoritive

src/Impostor.Api/Events/Game/Player/IPlayerCheckMurderEvent.cs [new file with mode: 0644]
src/Impostor.Api/Events/Game/Player/IPlayerMurderEvent.cs
src/Impostor.Api/Games/IGame.cs
src/Impostor.Server/Events/Game/Player/PlayerCheckMurderEvent.cs [new file with mode: 0644]
src/Impostor.Server/Events/Game/Player/PlayerMurderEvent.cs
src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.Api.cs
src/Impostor.Server/Net/Inner/Objects/InnerPlayerControl.cs
src/Impostor.Server/Net/State/Game.cs

diff --git a/src/Impostor.Api/Events/Game/Player/IPlayerCheckMurderEvent.cs b/src/Impostor.Api/Events/Game/Player/IPlayerCheckMurderEvent.cs
new file mode 100644 (file)
index 0000000..878d5a5
--- /dev/null
@@ -0,0 +1,27 @@
+using Impostor.Api.Innersloth;
+using Impostor.Api.Net.Inner.Objects;
+
+namespace Impostor.Api.Events.Player
+{
+    /// <summary>
+    ///     Event that allows changing or canceling an upcoming player murder.
+    /// </summary>
+    /// Note that this event only triggers if the Host of the Game did not disable server authority.
+    ///
+    /// If you want to get an event after the murder took place, listen to <see cref="IPlayerMurderEvent"/>.
+    public interface IPlayerCheckMurderEvent : IPlayerEvent, IEventCancelable
+    {
+        /// <summary>
+        ///     Gets the player who got murdered.
+        /// </summary>
+        IInnerPlayerControl Victim { get; }
+
+        /// <summary>
+        ///     Gets or sets the result of this event.
+        /// </summary>
+        /// Its initial value is what would have happened in the normal game flow.
+        /// - If set to Succeeded, the Victim will die.
+        /// - If set to FailedProtected, the Victim will be protected with a shield animation and the attacker will have a cooldown set.
+        MurderResultFlags Result { get; set; }
+    }
+}
index c47c00b4e80968df2d169b55e3abaf4915375d79..b90ea3eeb6d728c965a2e2a42c385c7098b4569b 100644 (file)
@@ -1,12 +1,26 @@
+using Impostor.Api.Innersloth;
 using Impostor.Api.Net.Inner.Objects;
 
 namespace Impostor.Api.Events.Player
 {
+    /// <summary>
+    ///     Event that is called when a player is killed by another player.
+    /// </summary>
+    /// This event works regardless of server authority is enabled or not.
+    ///
+    /// If you want to cancel this kill, listen to <see cref="IPlayerCheckMurderEvent"/>
+    /// If you want to know about players that were voted out, listed to <see cref="IPlayerExileEvent"/>
     public interface IPlayerMurderEvent : IPlayerEvent
     {
         /// <summary>
         ///     Gets the player who got murdered.
         /// </summary>
         IInnerPlayerControl Victim { get; }
+
+        /// <summary>
+        ///     Gets the result of the event.
+        /// </summary>
+        /// Note that if FailedError or FailedProtected is set, the kill did not take place
+        MurderResultFlags Result { get; }
     }
 }
index 636b016e374ba4812c6c723ddf1bffdf948e3236..8f5d29b63349aa09d16245a5e864367433616119 100644 (file)
@@ -39,6 +39,12 @@ namespace Impostor.Api.Games
 
         int HostId { get; }
 
+        /// <summary>
+        ///     Gets a value indicating whether the Host of the game has requested host authority.
+        /// </summary>
+        /// Vanilla Among Us does not request this, but certain client-side mods will.
+        bool IsHostAuthoritive { get; }
+
         IClientPlayer? GetClientPlayer(int clientId);
 
         T? FindObjectByNetId<T>(uint netId)
diff --git a/src/Impostor.Server/Events/Game/Player/PlayerCheckMurderEvent.cs b/src/Impostor.Server/Events/Game/Player/PlayerCheckMurderEvent.cs
new file mode 100644 (file)
index 0000000..fb0a1f0
--- /dev/null
@@ -0,0 +1,32 @@
+using Impostor.Api.Events.Player;
+using Impostor.Api.Games;
+using Impostor.Api.Innersloth;
+using Impostor.Api.Net;
+using Impostor.Api.Net.Inner.Objects;
+
+namespace Impostor.Server.Events.Player
+{
+    public class PlayerCheckMurderEvent : IPlayerCheckMurderEvent
+    {
+        public PlayerCheckMurderEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl, IInnerPlayerControl victim, MurderResultFlags result)
+        {
+            Game = game;
+            ClientPlayer = clientPlayer;
+            PlayerControl = playerControl;
+            Victim = victim;
+            Result = result;
+        }
+
+        public IGame Game { get; }
+
+        public IClientPlayer ClientPlayer { get; }
+
+        public IInnerPlayerControl PlayerControl { get; }
+
+        public IInnerPlayerControl Victim { get; }
+
+        public MurderResultFlags Result { get; set; }
+
+        public bool IsCancelled { get; set; }
+    }
+}
index ca64c35dacfb754d7bc16cc92b0e52a64ca6110a..d157fd4d3d2f8ebacefb7c2e327390da9d9888a2 100644 (file)
@@ -1,5 +1,6 @@
 using Impostor.Api.Events.Player;
 using Impostor.Api.Games;
+using Impostor.Api.Innersloth;
 using Impostor.Api.Net;
 using Impostor.Api.Net.Inner.Objects;
 
@@ -7,12 +8,13 @@ namespace Impostor.Server.Events.Player
 {
     public class PlayerMurderEvent : IPlayerMurderEvent
     {
-        public PlayerMurderEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl, IInnerPlayerControl victim)
+        public PlayerMurderEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl, IInnerPlayerControl victim, MurderResultFlags result)
         {
             Game = game;
             ClientPlayer = clientPlayer;
             PlayerControl = playerControl;
             Victim = victim;
+            Result = result;
         }
 
         public IGame Game { get; }
@@ -22,5 +24,7 @@ namespace Impostor.Server.Events.Player
         public IInnerPlayerControl PlayerControl { get; }
 
         public IInnerPlayerControl Victim { get; }
+
+        public MurderResultFlags Result { get; }
     }
 }
index eeb4095a594c1ee9301dfe4ce307b62429a3c543..0718400fb35e04b050e5c2a3532e0bf141b405c4 100644 (file)
@@ -82,7 +82,7 @@ namespace Impostor.Server.Net.Inner.Objects
             await Game.FinishRpcAsync(writer, player.OwnerId);
         }
 
-        public async ValueTask MurderPlayerAsync(IInnerPlayerControl target, MurderResultFlags result = MurderResultFlags.Succeeded)
+        public async ValueTask MurderPlayerAsync(IInnerPlayerControl target, MurderResultFlags result)
         {
             if (!PlayerInfo.IsImpostor)
             {
@@ -99,7 +99,7 @@ namespace Impostor.Server.Net.Inner.Objects
                 throw new ImpostorProtocolException("Tried to murder a player, but target was not alive.");
             }
 
-            if (result == MurderResultFlags.Succeeded)
+            if ((result & (MurderResultFlags.FailedError | MurderResultFlags.FailedProtected)) == 0)
             {
                 ((InnerPlayerControl)target).Die(DeathReason.Kill);
             }
@@ -108,7 +108,7 @@ namespace Impostor.Server.Net.Inner.Objects
             Rpc12MurderPlayer.Serialize(writer, target, result);
             await Game.FinishRpcAsync(writer);
 
-            await _eventManager.CallAsync(new PlayerMurderEvent(Game, Game.GetClientPlayer(OwnerId)!, this, target));
+            await _eventManager.CallAsync(new PlayerMurderEvent(Game, Game.GetClientPlayer(OwnerId)!, this, target, result));
         }
 
         public async ValueTask MurderPlayerAsync(IInnerPlayerControl target)
index a964c264d56a8788903dcfd14140667f1a80d3f7..01da1f6257eeec75a542609f1e91f2b646fd77a1 100644 (file)
@@ -696,7 +696,7 @@ namespace Impostor.Server.Net.Inner.Objects
             IsMurdering = target;
 
             // Check if host authority mode is on
-            if (_game.IsHostAuthoritive())
+            if (_game.IsHostAuthoritive)
             {
                 // Pass the RPC on unharmed, the client will handle it
                 return true;
@@ -706,8 +706,15 @@ namespace Impostor.Server.Net.Inner.Objects
             {
                 var tgt = (InnerPlayerControl)target;
                 var result = tgt.IsProtected() ? MurderResultFlags.FailedProtected : MurderResultFlags.Succeeded;
-                tgt.ProtectedOn = null; // Clear GA protection in all cases
-                await MurderPlayerAsync(target, result);
+
+                var evt = new PlayerCheckMurderEvent(Game, sender, this, target, result);
+                await _eventManager.CallAsync(evt);
+
+                if (!evt.IsCancelled)
+                {
+                    tgt.ProtectedOn = null; // Clear GA protection in all cases
+                    await MurderPlayerAsync(target, evt.Result);
+                }
             }
 
             return false;
@@ -715,7 +722,7 @@ namespace Impostor.Server.Net.Inner.Objects
 
         private async ValueTask<bool> HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target, MurderResultFlags result)
         {
-            if (!_game.IsHostAuthoritive())
+            if (!_game.IsHostAuthoritive)
             {
                 if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Client tried to murder directly"))
                 {
@@ -742,8 +749,12 @@ namespace Impostor.Server.Net.Inner.Objects
 
             if (target != null && !target.PlayerInfo.IsDead)
             {
-                ((InnerPlayerControl)target).Die(DeathReason.Kill);
-                await _eventManager.CallAsync(new PlayerMurderEvent(Game, sender, this, target));
+                if ((result & (MurderResultFlags.FailedError | MurderResultFlags.FailedProtected)) == 0)
+                {
+                    ((InnerPlayerControl)target).Die(DeathReason.Kill);
+                }
+
+                await _eventManager.CallAsync(new PlayerMurderEvent(Game, sender, this, target, result));
             }
 
             IsMurdering = null;
@@ -771,7 +782,7 @@ namespace Impostor.Server.Net.Inner.Objects
                 }
             }
 
-            if (_game.IsHostAuthoritive())
+            if (_game.IsHostAuthoritive)
             {
                 return true;
             }
index 8b7219f71ae300fdff17e961c36cf40a11b3afa0..6bb6fe52b7bdc05e7a9f5e664a0678c9633a4ab0 100644 (file)
@@ -92,6 +92,21 @@ namespace Impostor.Server.Net.State
 
         public IEnumerable<IClientPlayer> Players => _players.Select(p => p.Value);
 
+        public bool IsHostAuthoritive
+        {
+            get
+            {
+                if (Host == null)
+                {
+                    return false;
+                }
+                else
+                {
+                    return Host.Client.GameVersion.HasDisableServerAuthorityFlag;
+                }
+            }
+        }
+
         internal GameNet GameNet { get; }
 
         public bool TryGetPlayer(int id, [MaybeNullWhen(false)] out ClientPlayer player)
@@ -145,17 +160,5 @@ namespace Impostor.Server.Net.State
                 .Select(p => p.Client.Connection)
                 .Where(c => c != null && c.IsConnected)!;
         }
-
-        internal bool IsHostAuthoritive()
-        {
-            if (Host == null)
-            {
-                return false;
-            }
-            else
-            {
-                return Host.Client.GameVersion.HasDisableServerAuthorityFlag;
-            }
-        }
     }
 }