From 0526561cc2a8ff52bde5baf8e361527f893816bd Mon Sep 17 00:00:00 2001 From: Gurge44 Date: Sun, 12 Jul 2026 12:29:57 +0200 Subject: [PATCH] Add Bouncer --- CustomRoles.cs | 1 + Main.cs | 1 + Modules/CustomRolesHelper.cs | 3 + Modules/ExtendedPlayerControl.cs | 16 +-- Modules/Utils.cs | 1 + Patches/ChatCommandPatch.cs | 4 +- Resources/Lang/en_US.json | 12 +- Roles/Standard/Coven/Dreamweaver.cs | 2 +- Roles/Standard/Coven/Enchanter.cs | 2 +- Roles/Standard/Crewmate/Chaos/Tree.cs | 3 +- .../Standard/Crewmate/Investigate/Analyst.cs | 2 +- .../Standard/Crewmate/Investigate/Catcher.cs | 2 +- .../Standard/Crewmate/Investigate/Coroner.cs | 2 +- Roles/Standard/Crewmate/Investigate/Druid.cs | 2 +- .../Standard/Crewmate/Investigate/Lighter.cs | 2 +- Roles/Standard/Crewmate/Investigate/Medium.cs | 2 +- .../Crewmate/Investigate/Perceiver.cs | 3 +- Roles/Standard/Crewmate/Investigate/Scout.cs | 2 +- Roles/Standard/Crewmate/Investigate/Sentry.cs | 6 +- Roles/Standard/Crewmate/Investigate/Spy.cs | 2 +- .../Crewmate/Investigate/Unshifter.cs | 2 +- .../Crewmate/Investigate/Whisperer.cs | 2 +- Roles/Standard/Crewmate/Killing/Veteran.cs | 2 +- .../Crewmate/Miscellaneous/CameraMan.cs | 4 +- .../Crewmate/Miscellaneous/Paranoid.cs | 2 +- .../Standard/Crewmate/Miscellaneous/Vacuum.cs | 2 +- Roles/Standard/Crewmate/Power/Adrenaline.cs | 2 +- Roles/Standard/Crewmate/Power/Astral.cs | 2 +- Roles/Standard/Crewmate/Power/Swapper.cs | 2 +- Roles/Standard/Crewmate/Support/Aid.cs | 2 +- Roles/Standard/Crewmate/Support/Benefactor.cs | 2 +- Roles/Standard/Crewmate/Support/Bouncer.cs | 114 ++++++++++++++++++ Roles/Standard/Crewmate/Support/Convener.cs | 2 +- Roles/Standard/Crewmate/Support/Deputy.cs | 2 +- .../Crewmate/Support/DonutDelivery.cs | 2 +- Roles/Standard/Crewmate/Support/Doorjammer.cs | 2 +- Roles/Standard/Crewmate/Support/Escort.cs | 2 +- Roles/Standard/Crewmate/Support/Gardener.cs | 2 +- Roles/Standard/Crewmate/Support/Grappler.cs | 2 +- Roles/Standard/Crewmate/Support/Grenadier.cs | 2 +- Roles/Standard/Crewmate/Support/Medic.cs | 2 +- Roles/Standard/Crewmate/Support/Monarch.cs | 2 +- Roles/Standard/Crewmate/Support/Pacifist.cs | 2 +- .../Crewmate/Support/SecurityGuard.cs | 2 +- Roles/Standard/Crewmate/Support/TimeMaster.cs | 4 +- Roles/Standard/Crewmate/Support/Ventguard.cs | 2 +- Roles/Standard/Crewmate/Support/Wizard.cs | 2 +- .../Standard/Impostor/Concealing/Anonymous.cs | 2 +- .../Impostor/Concealing/Fabricator.cs | 2 +- Roles/Standard/Impostor/Concealing/Swooper.cs | 4 +- .../Standard/Impostor/Killing/Cantankerous.cs | 2 +- Roles/Standard/Impostor/Killing/Kamikaze.cs | 2 - Roles/Standard/Impostor/Killing/Witch.cs | 2 +- .../Impostor/Miscellaneous/Consigliere.cs | 2 +- .../Impostor/Miscellaneous/Obstructer.cs | 6 +- .../Impostor/Miscellaneous/Visionary.cs | 2 +- .../Standard/Impostor/Support/Blackmailer.cs | 2 +- .../Standard/Impostor/Support/Camouflager.cs | 4 +- Roles/Standard/Impostor/Support/Consort.cs | 4 +- Roles/Standard/Impostor/Support/EvilEraser.cs | 2 +- Roles/Standard/Impostor/Support/Framer.cs | 2 +- Roles/Standard/Impostor/Support/Gangster.cs | 2 +- Roles/Standard/Impostor/Support/Gatekeeper.cs | 2 +- Roles/Standard/Impostor/Support/Hypnotist.cs | 2 +- Roles/Standard/Impostor/Support/Nullifier.cs | 2 +- Roles/Standard/Impostor/Support/Occultist.cs | 2 +- Roles/Standard/Neutral/Benign/Backstabber.cs | 2 +- Roles/Standard/Neutral/Benign/Investor.cs | 2 +- Roles/Standard/Neutral/Benign/Pursuer.cs | 2 +- Roles/Standard/Neutral/Evil/Gaslighter.cs | 8 +- Roles/Standard/Neutral/Evil/Vulture.cs | 2 +- Roles/Standard/Neutral/Killing/Blockade.cs | 2 +- Roles/Standard/Neutral/Killing/Cherokious.cs | 3 +- Roles/Standard/Neutral/Killing/Cultist.cs | 2 +- Roles/Standard/Neutral/Killing/Jackal.cs | 2 +- Roles/Standard/Neutral/Killing/Quarry.cs | 2 +- Roles/Standard/Neutral/Killing/Sprayer.cs | 2 +- Roles/Standard/Neutral/Killing/Virus.cs | 2 +- Roles/Standard/Neutral/Pariah/Spirit.cs | 2 +- Roles/Standard/Neutral/Pariah/Starspawn.cs | 2 +- 80 files changed, 226 insertions(+), 97 deletions(-) create mode 100644 Roles/Standard/Crewmate/Support/Bouncer.cs diff --git a/CustomRoles.cs b/CustomRoles.cs index f5b44014d..27da0e092 100644 --- a/CustomRoles.cs +++ b/CustomRoles.cs @@ -170,6 +170,7 @@ public enum CustomRoles Benefactor, Bestower, Bodyguard, + Bouncer, CameraMan, Captain, Car, diff --git a/Main.cs b/Main.cs index e7d6b37d4..575ec4be9 100644 --- a/Main.cs +++ b/Main.cs @@ -513,6 +513,7 @@ public class Main : BasePlugin { CustomRoles.Vacuum, "#E44CD6" }, { CustomRoles.Carrier, "#5DE2E7" }, { CustomRoles.Transmitter, "#c9a11e" }, + { CustomRoles.Bouncer, "#95C8E8" }, { CustomRoles.Operative, "#47f5d2" }, { CustomRoles.Tar, "#8C796B" }, { CustomRoles.Sensor, "#a3f7ff" }, diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 79509f674..875845050 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -41,6 +41,7 @@ internal static class CustomRolesHelper CustomRoles.Carrier, CustomRoles.Empress, CustomRoles.Tar, + CustomRoles.Bouncer, // HnS CustomRoles.Jet, @@ -252,6 +253,7 @@ internal static class CustomRolesHelper CustomRoles.Farmer => CustomRoles.Crewmate, CustomRoles.Captain => CustomRoles.Crewmate, CustomRoles.Transmitter => CustomRoles.Crewmate, + CustomRoles.Bouncer => CustomRoles.Crewmate, CustomRoles.Tar => CustomRoles.Crewmate, CustomRoles.Sensor => CustomRoles.Crewmate, CustomRoles.Doorjammer => CustomRoles.Crewmate, @@ -1433,6 +1435,7 @@ internal static class CustomRolesHelper CustomRoles.Benefactor => RoleOptionType.Crewmate_Support, CustomRoles.Bestower => RoleOptionType.Crewmate_Support, CustomRoles.Bodyguard => RoleOptionType.Crewmate_Support, + CustomRoles.Bouncer => RoleOptionType.Crewmate_Support, CustomRoles.Chef => RoleOptionType.Crewmate_Support, CustomRoles.Cleanser => RoleOptionType.Crewmate_Support, CustomRoles.Convener => RoleOptionType.Crewmate_Support, diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 4f82dd969..89f7d7d1f 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -869,25 +869,25 @@ internal static class ExtendedPlayerControl return Main.AbilityUseLimit.GetValueOrDefault(player.PlayerId, float.NaN); } - public void RpcRemoveAbilityUse(bool log = true) + public void RpcRemoveAbilityUse(bool log = true, bool notify = true) { float current = player.GetAbilityUseLimit(); if (float.IsNaN(current) || current <= 0f) return; - player.SetAbilityUseLimit(current - 1, log: log); + player.SetAbilityUseLimit(current - 1, log: log, notify: notify); } - public void RpcIncreaseAbilityUseLimitBy(float get, bool log = true) + public void RpcIncreaseAbilityUseLimitBy(float get, bool log = true, bool notify = true) { float current = player.GetAbilityUseLimit(); if (float.IsNaN(current)) return; - player.SetAbilityUseLimit(current + get, log: log); + player.SetAbilityUseLimit(current + get, log: log, notify: notify); } - public void SetAbilityUseLimit(float limit, bool rpc = true, bool log = true) + public void SetAbilityUseLimit(float limit, bool rpc = true, bool log = true, bool notify = true) { - player.PlayerId.SetAbilityUseLimit(limit, rpc, log); + player.PlayerId.SetAbilityUseLimit(limit, rpc, log, notify); } public void Suicide(PlayerState.DeathReason deathReason = PlayerState.DeathReason.Suicide, PlayerControl realKiller = null) @@ -2510,7 +2510,7 @@ internal static class ExtendedPlayerControl return Main.AbilityUseLimit.GetValueOrDefault(playerId, float.NaN); } - public void SetAbilityUseLimit(float limit, bool rpc = true, bool log = true) + public void SetAbilityUseLimit(float limit, bool rpc = true, bool log = true, bool notify = true) { limit = (float)Math.Round(limit, 2); @@ -2527,7 +2527,7 @@ internal static class ExtendedPlayerControl } PlayerControl pc = GetPlayerById(playerId); - if (Main.IntroDestroyed) NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc); + if (notify && Main.IntroDestroyed) NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc); if (log) Logger.Info($" {pc.GetNameWithRole()} => {Math.Round(limit, 2)}", "SetAbilityUseLimit"); } diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 614bccc59..f12ebacdc 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -4039,6 +4039,7 @@ public static class Utils int cd = role switch { CustomRoles.Mole => Mole.CD.GetInt(), + CustomRoles.Bouncer => Bouncer.AbilityCooldown.GetInt(), CustomRoles.Operative => Operative.AbilityCooldown.GetInt(), CustomRoles.PortalMaker => PortalMaker.AbilityCooldown.GetInt(), CustomRoles.Telecommunication => Telecommunication.VentCooldown.GetInt(), diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index 81b1d3ab2..3bcf93c52 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -1153,10 +1153,10 @@ internal static class ChatCommands private static void JailTalkCommand(PlayerControl player, string text, string[] args) { - if (args.Length < 2) return; + if (args.Length < 2 || !player.IsAlive()) return; Jailor jailor = Main.PlayerStates[player.PlayerId].Role as Jailor ?? Main.PlayerStates.Select(x => x.Value.Role as Jailor).FirstOrDefault(x => x != null); - if (jailor == null) return; + if (jailor == null || jailor.JailorTarget == byte.MaxValue) return; bool amJailor = Jailor.PlayerIdList.Contains(player.PlayerId); bool amJailed = player.PlayerId == jailor.JailorTarget; diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index b93253354..fa4eddc75 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -404,6 +404,16 @@ "BodyguardKillsKiller": "Bodyguard kills murderer", "BodyguardProtectRadius": "Protect Radius", "BodyguardProtected": "The Bodyguard protected your target and died for them", + "Bouncer": "Bouncer", + "BouncerInfo": "Guard Rooms", + "BouncerInfoLong": "(Crewmates):\\nAs the Bouncer, you may pet to mark the room you're currently in and bounce off players who try to enter it while your ability is active.", + "Bouncer.AbortBouncingIfLeftRoom": "Abort Bouncing When Leaving The Room", + "Bouncer.WhoGetsBounced": "Who Gets Bounced Off", + "Bouncer.WhoGetsBouncedOptions.Everyone": "Everyone", + "Bouncer.WhoGetsBouncedOptions.Impostors": "<#ff1919>Impostors", + "Bouncer.WhoGetsBouncedOptions.ImpNKNPNE": "<#ff1919>Imp/<#ff5500>NK/<#a10e49>NP/<#ffff00>NE", + "Bouncer.Suffix": "<#00ffa5>Guarding {0}", + "Bouncer.Suffix.RemainingSeconds": " <#00ffff>({0}s left)", "CameraMan": "Camera Man", "CameraManInfo": "View Cameras anytime", "CameraManInfoLong": "(Crewmates):\\nAs the Camera Man, you can vent/pet to instantly teleport to the security room.", @@ -1947,7 +1957,7 @@ "GangsterSuccessfullyRecruited": "You successfully recruited a player", "Gatekeeper": "Gatekeeper", "GatekeeperInfo": "Keep Track Of People In Rooms", - "GatekeeperInfoLong": "(Impostors):\\nAs the Gatekeeper, you may vanish/pet/shapeshift to mark the room you're in. From now, you'll now see how many living players are in that room, updated in real time. Dead bodies aren't counted. You can have multiple marked rooms at the same time depending on settings.", + "GatekeeperInfoLong": "(Impostors):\\nAs the Gatekeeper, you may vanish/pet/shapeshift to mark the room you're in. From now, you'll see how many living players are in that room, updated in real time. Dead bodies aren't counted. You can have multiple marked rooms at the same time depending on settings.", "Gatekeeper.MaxRoomsMarkedAtOnce": "Max Rooms Marked At Once", "Generator": "Generator", "GeneratorInfo": "Power up the ship", diff --git a/Roles/Standard/Coven/Dreamweaver.cs b/Roles/Standard/Coven/Dreamweaver.cs index 31cd31d42..523f115ad 100644 --- a/Roles/Standard/Coven/Dreamweaver.cs +++ b/Roles/Standard/Coven/Dreamweaver.cs @@ -63,10 +63,10 @@ public class Dreamweaver : CovenBase { if (Thanos.IsImmune(target)) return false; InsanePlayers.Add(target.PlayerId); + killer.RpcRemoveAbilityUse(notify: false); Utils.SendRPC(CustomRPC.SyncRoleData, DreamweaverId, 1, target.PlayerId); Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); killer.SetKillCooldown(AbilityCooldown.GetFloat()); - killer.RpcRemoveAbilityUse(); return false; } diff --git a/Roles/Standard/Coven/Enchanter.cs b/Roles/Standard/Coven/Enchanter.cs index 70b0b8831..d8265bbcf 100644 --- a/Roles/Standard/Coven/Enchanter.cs +++ b/Roles/Standard/Coven/Enchanter.cs @@ -61,10 +61,10 @@ public class Enchanter : CovenBase if (killer.GetAbilityUseLimit() < 1) return; EnchantedPlayers ??= []; EnchantedPlayers.Add(target.PlayerId); + killer.RpcRemoveAbilityUse(notify: false); Utils.SendRPC(CustomRPC.SyncRoleData, killer.PlayerId, 1, target.PlayerId); Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); killer.SetKillCooldown(AbilityCooldown.GetFloat()); - killer.RpcRemoveAbilityUse(); } } } diff --git a/Roles/Standard/Crewmate/Chaos/Tree.cs b/Roles/Standard/Crewmate/Chaos/Tree.cs index ad0de3d72..6a5292dbf 100644 --- a/Roles/Standard/Crewmate/Chaos/Tree.cs +++ b/Roles/Standard/Crewmate/Chaos/Tree.cs @@ -51,13 +51,14 @@ public class Tree : RoleBase public override void OnPet(PlayerControl pc) { if (pc.GetAbilityUseLimit() < 1) return; - pc.RpcRemoveAbilityUse(); Main.AllPlayerSpeed[pc.PlayerId] = Main.MinSpeed; pc.MarkDirtySettings(); bool treeSpriteVisible = TreeSpriteVisible.GetBool(); + pc.RpcRemoveAbilityUse(notify: !treeSpriteVisible); + if (treeSpriteVisible) { TreeSpriteActive = true; diff --git a/Roles/Standard/Crewmate/Investigate/Analyst.cs b/Roles/Standard/Crewmate/Investigate/Analyst.cs index 4bcfd2382..835d3a085 100644 --- a/Roles/Standard/Crewmate/Investigate/Analyst.cs +++ b/Roles/Standard/Crewmate/Investigate/Analyst.cs @@ -164,7 +164,7 @@ internal class Analyst : RoleBase if (CurrentTarget.TIME + Duration.GetInt() <= Utils.TimeStamp) { CurrentTarget.ID = byte.MaxValue; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); pc.Notify(GetAnalyzeResult(target), 10f); pc.SetKillCooldown(); } diff --git a/Roles/Standard/Crewmate/Investigate/Catcher.cs b/Roles/Standard/Crewmate/Investigate/Catcher.cs index b7b6b2201..85f7bd98c 100644 --- a/Roles/Standard/Crewmate/Investigate/Catcher.cs +++ b/Roles/Standard/Crewmate/Investigate/Catcher.cs @@ -93,7 +93,7 @@ public class Catcher : RoleBase { if (pc.GetAbilityUseLimit() < 1) return; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); Vector2 pos = pc.Pos(); Traps[pos] = new(pos, pc); diff --git a/Roles/Standard/Crewmate/Investigate/Coroner.cs b/Roles/Standard/Crewmate/Investigate/Coroner.cs index a10132d4e..bda93e171 100644 --- a/Roles/Standard/Crewmate/Investigate/Coroner.cs +++ b/Roles/Standard/Crewmate/Investigate/Coroner.cs @@ -108,8 +108,8 @@ public class Coroner : RoleBase CoronerTargets.Add(killer.PlayerId); TargetArrow.Add(pc.PlayerId, killer.PlayerId); + pc.RpcRemoveAbilityUse(notify: false); pc.Notify(GetString("CoronerTrackRecorded")); - pc.RpcRemoveAbilityUse(); if (LeaveDeadBodyUnreportable.GetBool()) { diff --git a/Roles/Standard/Crewmate/Investigate/Druid.cs b/Roles/Standard/Crewmate/Investigate/Druid.cs index ba151ceee..d1b95a1cf 100644 --- a/Roles/Standard/Crewmate/Investigate/Druid.cs +++ b/Roles/Standard/Crewmate/Investigate/Druid.cs @@ -156,7 +156,7 @@ public class Druid : RoleBase }, onTick: () => pc.Notify(string.Format(GetString("DruidTimeLeft"), (int)Math.Ceiling(DelayTimer.Remaining.TotalSeconds)), 2f, overrideAll: true), onCanceled: () => DelayTimer = null); } - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: isPet); } public override void OnCheckPlayerPosition(PlayerControl pc) diff --git a/Roles/Standard/Crewmate/Investigate/Lighter.cs b/Roles/Standard/Crewmate/Investigate/Lighter.cs index d81fe500b..478349422 100644 --- a/Roles/Standard/Crewmate/Investigate/Lighter.cs +++ b/Roles/Standard/Crewmate/Investigate/Lighter.cs @@ -116,8 +116,8 @@ internal class Lighter : RoleBase pc.MarkDirtySettings(); }, onCanceled: () => Timer = null); + pc.RpcRemoveAbilityUse(notify: false); pc.Notify(Translator.GetString("LighterSkillInUse"), skillDuration); - pc.RpcRemoveAbilityUse(); pc.MarkDirtySettings(); } else diff --git a/Roles/Standard/Crewmate/Investigate/Medium.cs b/Roles/Standard/Crewmate/Investigate/Medium.cs index 8dbb15c04..2583c7dd7 100644 --- a/Roles/Standard/Crewmate/Investigate/Medium.cs +++ b/Roles/Standard/Crewmate/Investigate/Medium.cs @@ -59,7 +59,7 @@ public class Medium : RoleBase { if (!pc.Is(CustomRoles.Medium) || pc.PlayerId == target.PlayerId || pc.GetAbilityUseLimit() < 1) continue; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); ContactPlayer.TryAdd(target.PlayerId, pc.PlayerId); Logger.Info($"Medium Connection: {pc.GetNameWithRole()} => {target.PlayerName}", "Medium"); } diff --git a/Roles/Standard/Crewmate/Investigate/Perceiver.cs b/Roles/Standard/Crewmate/Investigate/Perceiver.cs index cf2685a0b..b6a5fd2a0 100644 --- a/Roles/Standard/Crewmate/Investigate/Perceiver.cs +++ b/Roles/Standard/Crewmate/Investigate/Perceiver.cs @@ -74,12 +74,11 @@ internal class Perceiver : RoleBase private static void UseAbility(PlayerControl pc) { if (!pc || pc.GetAbilityUseLimit() < 1f) return; + pc.RpcRemoveAbilityUse(notify: false); PlayerControl[] killers = FastVector2.GetPlayersInRange(pc.Pos(), Radius.GetFloat(), x => !x.Is(Team.Crewmate) && x.HasKillButton()).ToArray(); pc.Notify(string.Format(Translator.GetString("PerceiverNotify"), killers.Length), 7f); - pc.RpcRemoveAbilityUse(); - if (pc.AmOwner) { HashSet allKillers = Main.CachedAlivePlayerControls().Where(x => !x.Is(Team.Crewmate) && x.HasKillButton()).Select(x => x.PlayerId).ToHashSet(); diff --git a/Roles/Standard/Crewmate/Investigate/Scout.cs b/Roles/Standard/Crewmate/Investigate/Scout.cs index 99f4c1157..c847a7a8a 100644 --- a/Roles/Standard/Crewmate/Investigate/Scout.cs +++ b/Roles/Standard/Crewmate/Investigate/Scout.cs @@ -87,7 +87,7 @@ public class Scout : RoleBase byte trackerId = reader.ReadByte(); byte targetId = reader.ReadByte(); - Utils.GetPlayerById(trackerId).RpcRemoveAbilityUse(); + Utils.GetPlayerById(trackerId)?.RpcRemoveAbilityUse(); TrackerTarget[trackerId].Add(targetId); } diff --git a/Roles/Standard/Crewmate/Investigate/Sentry.cs b/Roles/Standard/Crewmate/Investigate/Sentry.cs index 8d918a007..df052c660 100644 --- a/Roles/Standard/Crewmate/Investigate/Sentry.cs +++ b/Roles/Standard/Crewmate/Investigate/Sentry.cs @@ -149,7 +149,7 @@ internal class Sentry : RoleBase if (!fromDevice) { if (pc.GetAbilityUseLimit() < 1) return; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); } string roomName = Translator.GetString(MonitoredRoom.RoomId); @@ -168,7 +168,7 @@ internal class Sentry : RoleBase if (target.shapeshiftTargetPlayerId != byte.MaxValue) realTarget = Utils.GetPlayerById(target.shapeshiftTargetPlayerId); - if (realTarget == null) realTarget = target; + if (!realTarget) realTarget = target; string coloredName = Utils.ColorString(Main.PlayerColors[realTarget.PlayerId], realTarget.GetRealName()); @@ -182,7 +182,7 @@ internal class Sentry : RoleBase foreach (byte id in DeadBodiesInRoom) { PlayerControl bodyPc = Utils.GetPlayerById(id); - if (bodyPc == null) continue; + if (!bodyPc) continue; string coloredName = Utils.ColorString(Main.PlayerColors[id], bodyPc.GetRealName()); diff --git a/Roles/Standard/Crewmate/Investigate/Spy.cs b/Roles/Standard/Crewmate/Investigate/Spy.cs index 2e522bc80..505fb1dd7 100644 --- a/Roles/Standard/Crewmate/Investigate/Spy.cs +++ b/Roles/Standard/Crewmate/Investigate/Spy.cs @@ -85,7 +85,7 @@ public class Spy : RoleBase { if (!target.Is(CustomRoles.Spy) || killer.PlayerId == target.PlayerId || target.GetAbilityUseLimit() < 1) return true; - target.RpcRemoveAbilityUse(); + target.RpcRemoveAbilityUse(notify: false); SpyRedNameList ??= []; SpyRedNameList.Add(killer.PlayerId); SendRPC(1, killer.PlayerId); diff --git a/Roles/Standard/Crewmate/Investigate/Unshifter.cs b/Roles/Standard/Crewmate/Investigate/Unshifter.cs index e2742b301..f83677967 100644 --- a/Roles/Standard/Crewmate/Investigate/Unshifter.cs +++ b/Roles/Standard/Crewmate/Investigate/Unshifter.cs @@ -61,7 +61,7 @@ public class Unshifter : RoleBase target.RpcShapeshift(target, true); target.RpcResetAbilityCooldown(); - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); killer.SetKillCooldown(); killer.Notify(GetString("UnshifterSuccess")); diff --git a/Roles/Standard/Crewmate/Investigate/Whisperer.cs b/Roles/Standard/Crewmate/Investigate/Whisperer.cs index aff5aad24..6bdfd7827 100644 --- a/Roles/Standard/Crewmate/Investigate/Whisperer.cs +++ b/Roles/Standard/Crewmate/Investigate/Whisperer.cs @@ -165,7 +165,7 @@ public class Whisperer : RoleBase Info.Add(info); if (Info.Count > 5) Info.RemoveAt(0); - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); Utils.SendRPC(CustomRPC.SyncRoleData, WhispererId, 4, info); Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc); } diff --git a/Roles/Standard/Crewmate/Killing/Veteran.cs b/Roles/Standard/Crewmate/Killing/Veteran.cs index 535be3591..b875626e9 100644 --- a/Roles/Standard/Crewmate/Killing/Veteran.cs +++ b/Roles/Standard/Crewmate/Killing/Veteran.cs @@ -106,7 +106,7 @@ internal class Veteran : RoleBase pc.RpcResetAbilityCooldown(); pc.Notify(string.Format(Translator.GetString("VeteranOffGuard"), (int)pc.GetAbilityUseLimit())); }, onCanceled: () => VeteranInProtect.Remove(pc.PlayerId)); - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); pc.RPCPlayCustomSound("Gunload"); pc.Notify(Translator.GetString("VeteranOnGuard"), VeteranSkillDuration.GetFloat()); pc.MarkDirtySettings(); diff --git a/Roles/Standard/Crewmate/Miscellaneous/CameraMan.cs b/Roles/Standard/Crewmate/Miscellaneous/CameraMan.cs index 844c53f1e..956163a10 100644 --- a/Roles/Standard/Crewmate/Miscellaneous/CameraMan.cs +++ b/Roles/Standard/Crewmate/Miscellaneous/CameraMan.cs @@ -86,7 +86,7 @@ public class CameraMan : RoleBase public override void OnEnterVent(PlayerControl pc, Vent vent) { if (UsePets.GetBool()) return; - if (pc == null) return; + if (!pc) return; if (pc.GetAbilityUseLimit() >= 1) { @@ -99,7 +99,7 @@ public class CameraMan : RoleBase public override void OnPet(PlayerControl pc) { - if (pc == null) return; + if (!pc) return; if (pc.GetAbilityUseLimit() >= 1) { diff --git a/Roles/Standard/Crewmate/Miscellaneous/Paranoid.cs b/Roles/Standard/Crewmate/Miscellaneous/Paranoid.cs index 4958008a1..6ef3cc989 100644 --- a/Roles/Standard/Crewmate/Miscellaneous/Paranoid.cs +++ b/Roles/Standard/Crewmate/Miscellaneous/Paranoid.cs @@ -89,7 +89,7 @@ internal class Paranoid : RoleBase { if (pc.GetAbilityUseLimit() >= 1) { - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); if (AmongUsClient.Instance.AmHost) LateTask.New(() => Utils.SendMessage(Translator.GetString("SkillUsedLeft") + (ParanoidNumOfUseButton.GetInt() - ParaUsedButtonCount[pc.PlayerId]), pc.PlayerId), 4f, "Paranoid Skill Remain Message"); pc.NoCheckStartMeeting(pc.Data); } diff --git a/Roles/Standard/Crewmate/Miscellaneous/Vacuum.cs b/Roles/Standard/Crewmate/Miscellaneous/Vacuum.cs index 3e3d2c548..6747fa98a 100644 --- a/Roles/Standard/Crewmate/Miscellaneous/Vacuum.cs +++ b/Roles/Standard/Crewmate/Miscellaneous/Vacuum.cs @@ -71,7 +71,7 @@ public class Vacuum : RoleBase public override void OnPet(PlayerControl pc) { if (pc.GetAbilityUseLimit() < 1) return; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); Timer = new CountdownTimer(AbilityDuration.GetInt(), () => { Timer = null; diff --git a/Roles/Standard/Crewmate/Power/Adrenaline.cs b/Roles/Standard/Crewmate/Power/Adrenaline.cs index 363d2c49d..9fb63ccf9 100644 --- a/Roles/Standard/Crewmate/Power/Adrenaline.cs +++ b/Roles/Standard/Crewmate/Power/Adrenaline.cs @@ -70,7 +70,7 @@ public class Adrenaline : RoleBase { if (!base.OnCheckMurderAsTarget(killer, target) || target.GetAbilityUseLimit() < 1 || target.GetTaskState().CompletedTasksCount < MinTasksRequired.GetInt() || Timer != null) return true; - target.RpcRemoveAbilityUse(); + target.RpcRemoveAbilityUse(notify: false); Timer = new CountdownTimer(Time.GetInt(), () => { Timer = null; diff --git a/Roles/Standard/Crewmate/Power/Astral.cs b/Roles/Standard/Crewmate/Power/Astral.cs index 0f8860416..ed75929d1 100644 --- a/Roles/Standard/Crewmate/Power/Astral.cs +++ b/Roles/Standard/Crewmate/Power/Astral.cs @@ -68,7 +68,7 @@ public class Astral : RoleBase void BecomeGhostTemporarily(PlayerControl pc) { if (pc.GetAbilityUseLimit() < 1f || ReportDeadBodyPatch.MeetingStarted || GameStates.IsMeeting) return; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); Timer = new CountdownTimer(AbilityDuration.GetInt(), () => BecomeAliveAgain(pc), onTick: () => Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc), onCanceled: () => Timer = null); Utils.SendRPC(CustomRPC.SyncRoleData, AstralId); diff --git a/Roles/Standard/Crewmate/Power/Swapper.cs b/Roles/Standard/Crewmate/Power/Swapper.cs index 13c3bc5c1..1f392730f 100644 --- a/Roles/Standard/Crewmate/Power/Swapper.cs +++ b/Roles/Standard/Crewmate/Power/Swapper.cs @@ -184,7 +184,7 @@ public class Swapper : RoleBase { if (SwapTargets != (byte.MaxValue, byte.MaxValue)) { - Utils.GetPlayerById(SwapperId).RpcRemoveAbilityUse(); + Utils.GetPlayerById(SwapperId)?.RpcRemoveAbilityUse(notify: false); SwapTargets = (byte.MaxValue, byte.MaxValue); } } diff --git a/Roles/Standard/Crewmate/Support/Aid.cs b/Roles/Standard/Crewmate/Support/Aid.cs index 6078765c0..ee027aaad 100644 --- a/Roles/Standard/Crewmate/Support/Aid.cs +++ b/Roles/Standard/Crewmate/Support/Aid.cs @@ -91,7 +91,7 @@ public class Aid : RoleBase { if (pc.GetAbilityUseLimit() >= 1 && TargetId != byte.MaxValue) { - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); PlayerControl target = Utils.GetPlayerById(TargetId); ShieldedPlayers[TargetId] = new CountdownTimer(AidDur.GetInt(), () => { diff --git a/Roles/Standard/Crewmate/Support/Benefactor.cs b/Roles/Standard/Crewmate/Support/Benefactor.cs index 2bf76a622..b60f109a8 100644 --- a/Roles/Standard/Crewmate/Support/Benefactor.cs +++ b/Roles/Standard/Crewmate/Support/Benefactor.cs @@ -68,7 +68,7 @@ internal class Benefactor : RoleBase if (player.Is(CustomRoles.Benefactor)) { if (playerId.GetAbilityUseLimit() < 1) return; - player.RpcRemoveAbilityUse(); + player.RpcRemoveAbilityUse(notify: false); if (TaskIndex.TryGetValue(playerId, out var list)) list.Add(task.Index); else TaskIndex[playerId] = [task.Index]; diff --git a/Roles/Standard/Crewmate/Support/Bouncer.cs b/Roles/Standard/Crewmate/Support/Bouncer.cs new file mode 100644 index 000000000..447cfb971 --- /dev/null +++ b/Roles/Standard/Crewmate/Support/Bouncer.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using EHR.Modules.Extensions; + +namespace EHR.Roles; + +public class Bouncer : RoleBase +{ + public static bool On; + + public static OptionItem AbilityCooldown; + private static OptionItem AbilityDuration; + private static OptionItem AbortBouncingIfLeftRoom; + private static OptionItem WhoGetsBounced; + private static OptionItem AbilityUseLimit; + private static OptionItem AbilityUseGainWithEachTaskCompleted; + private static OptionItem AbilityChargesWhenFinishedTasks; + + private static readonly string[] WhoGetsBouncedOptions = + [ + "Bouncer.WhoGetsBouncedOptions.Everyone", + "Bouncer.WhoGetsBouncedOptions.Impostors", + "Bouncer.WhoGetsBouncedOptions.ImpNKNPNE" + ]; + + public override bool IsEnable => On; + + private Dictionary LastPosition = []; + private CountdownTimer Timer; + private PlainShipRoom MarkedRoom; + private byte BouncerId; + + public override void SetupCustomOption() + { + StartSetup(659300) + .AutoSetupOption(ref AbilityCooldown, 15, new IntegerValueRule(0, 120, 1), OptionFormat.Seconds) + .AutoSetupOption(ref AbilityDuration, 15, new IntegerValueRule(1, 60, 1), OptionFormat.Seconds) + .AutoSetupOption(ref AbortBouncingIfLeftRoom, true) + .AutoSetupOption(ref WhoGetsBounced, 0, WhoGetsBouncedOptions) + .AutoSetupOption(ref AbilityUseLimit, 1f, new FloatValueRule(0, 20, 0.05f), OptionFormat.Times) + .AutoSetupOption(ref AbilityUseGainWithEachTaskCompleted, 0.5f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times) + .AutoSetupOption(ref AbilityChargesWhenFinishedTasks, 0.2f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times); + } + + public override void Init() + { + On = false; + } + + public override void Add(byte playerId) + { + On = true; + BouncerId = playerId; + LastPosition = []; + Timer = null; + MarkedRoom = null; + playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat()); + } + + public override void OnPet(PlayerControl pc) + { + var room = pc.GetPlainShipRoom(); + if (!room || pc.GetAbilityUseLimit() < 1f) return; + + MarkedRoom = room; + Timer = new CountdownTimer(AbilityDuration.GetFloat(), () => + { + Timer = null; + if (!pc || !pc.IsAlive()) return; + Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc); + }, onTick: () => + { + if (Timer.Remaining.TotalSeconds >= 6 || !pc || !pc.IsAlive()) return; + Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc); + }, onCanceled: () => Timer = null); + pc.RpcRemoveAbilityUse(notify: false); + Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc); + } + + public override void OnCheckPlayerPosition(PlayerControl pc) + { + if (Timer == null) return; + + if (pc.PlayerId == BouncerId) + { + if (AbortBouncingIfLeftRoom.GetBool() && !pc.IsInRoom(MarkedRoom)) + { + Timer.Dispose(); + Timer = null; + } + + return; + } + + if (!LastPosition.TryGetValue(pc.PlayerId, out Vector2 lastPosition)) + LastPosition[pc.PlayerId] = pc.transform.position; + else if (pc.IsInRoom(MarkedRoom) && WhoGetsBounced.GetValue() switch + { + 0 => true, + 1 => pc.Is(Team.Impostor), + 2 => pc.Is(Team.Impostor) || pc.IsNeutralKiller() || pc.IsNeutralPariah() || pc.IsNeutralEvil(), + _ => false + }) + pc.TP(lastPosition); + } + + public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false) + { + if (seer.PlayerId != BouncerId || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || meeting || Timer == null) return string.Empty; + int remainingSeconds = (int)Math.Ceiling(Timer.Remaining.TotalSeconds); + string timerText = seer.IsModdedClient() || remainingSeconds <= 5 ? string.Format(Translator.GetString("Bouncer.Suffix.RemainingSeconds"), remainingSeconds) : string.Empty; + return string.Format(Translator.GetString("Bouncer.Suffix"), Translator.GetString(MarkedRoom.RoomId)) + timerText; + } +} \ No newline at end of file diff --git a/Roles/Standard/Crewmate/Support/Convener.cs b/Roles/Standard/Crewmate/Support/Convener.cs index 094c2ea0d..e4fa9f6d7 100644 --- a/Roles/Standard/Crewmate/Support/Convener.cs +++ b/Roles/Standard/Crewmate/Support/Convener.cs @@ -64,7 +64,7 @@ internal class Convener : RoleBase private static void PullEveryone(PlayerControl pc, bool isPet = false) { - if (pc == null || pc.GetAbilityUseLimit() < 1f) return; + if (!pc || pc.GetAbilityUseLimit() < 1f) return; pc.RPCPlayCustomSound("Teleport"); LateTask.New(() => Main.EnumerateAlivePlayerControls().MassTP(pc.Pos(), log: true), isPet ? 0.5f : 2f, "Convener TP"); diff --git a/Roles/Standard/Crewmate/Support/Deputy.cs b/Roles/Standard/Crewmate/Support/Deputy.cs index dc18e06ab..9924136f3 100644 --- a/Roles/Standard/Crewmate/Support/Deputy.cs +++ b/Roles/Standard/Crewmate/Support/Deputy.cs @@ -80,7 +80,7 @@ public class Deputy : RoleBase if (!target.Is(CustomRoles.Deputy)) { - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); killer.Notify(CustomRoles.Deputy.ColoredTextByRole(GetString("DeputyHandcuffedPlayer"))); diff --git a/Roles/Standard/Crewmate/Support/DonutDelivery.cs b/Roles/Standard/Crewmate/Support/DonutDelivery.cs index e8ca67216..8384231d0 100644 --- a/Roles/Standard/Crewmate/Support/DonutDelivery.cs +++ b/Roles/Standard/Crewmate/Support/DonutDelivery.cs @@ -98,7 +98,7 @@ public class DonutDelivery : RoleBase { if (!IsEnable) return false; - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); int num1 = IRandom.Instance.Next(0, 19); killer.Notify(GetString($"DonutDelivered-{num1}")); diff --git a/Roles/Standard/Crewmate/Support/Doorjammer.cs b/Roles/Standard/Crewmate/Support/Doorjammer.cs index e9cced17e..0faf6c400 100644 --- a/Roles/Standard/Crewmate/Support/Doorjammer.cs +++ b/Roles/Standard/Crewmate/Support/Doorjammer.cs @@ -65,7 +65,7 @@ public class Doorjammer : RoleBase Utils.SendRPC(CustomRPC.SyncRoleData, DoorjammerId, 2); } - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); JammedRooms.Add(room); Utils.SendRPC(CustomRPC.SyncRoleData, DoorjammerId, 1, (byte)room); Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc); diff --git a/Roles/Standard/Crewmate/Support/Escort.cs b/Roles/Standard/Crewmate/Support/Escort.cs index d3fc7144a..48436e36b 100644 --- a/Roles/Standard/Crewmate/Support/Escort.cs +++ b/Roles/Standard/Crewmate/Support/Escort.cs @@ -70,7 +70,7 @@ public class Escort : RoleBase { if (!IsEnable || killer == null || target == null || killer.GetAbilityUseLimit() <= 0) return false; - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); killer.SetKillCooldown(); target.BlockRole(Duration.GetFloat()); killer.Notify(GetString("EscortTargetHacked")); diff --git a/Roles/Standard/Crewmate/Support/Gardener.cs b/Roles/Standard/Crewmate/Support/Gardener.cs index 63c7efea8..881991c36 100644 --- a/Roles/Standard/Crewmate/Support/Gardener.cs +++ b/Roles/Standard/Crewmate/Support/Gardener.cs @@ -52,8 +52,8 @@ public class Gardener : RoleBase } Plants.Add(new Plant(pos)); + pc.RpcRemoveAbilityUse(notify: false); pc.Notify(Translator.GetString("Gardener.PlantCreated")); - pc.RpcRemoveAbilityUse(); } public override void AfterMeetingTasks() diff --git a/Roles/Standard/Crewmate/Support/Grappler.cs b/Roles/Standard/Crewmate/Support/Grappler.cs index c7f0661c2..e6950ad04 100644 --- a/Roles/Standard/Crewmate/Support/Grappler.cs +++ b/Roles/Standard/Crewmate/Support/Grappler.cs @@ -53,7 +53,7 @@ public class Grappler : RoleBase if (GrapplerId.GetAbilityUseLimit() >= 1f) { InUse = true; - GrapplerId.GetPlayer().RpcRemoveAbilityUse(); + GrapplerId.GetPlayer()?.RpcRemoveAbilityUse(notify: false); } else InUse = false; diff --git a/Roles/Standard/Crewmate/Support/Grenadier.cs b/Roles/Standard/Crewmate/Support/Grenadier.cs index 9357cc76f..ae619569a 100644 --- a/Roles/Standard/Crewmate/Support/Grenadier.cs +++ b/Roles/Standard/Crewmate/Support/Grenadier.cs @@ -123,8 +123,8 @@ internal class Grenadier : RoleBase } pc.RPCPlayCustomSound("FlashBang"); + pc.RpcRemoveAbilityUse(notify: false); pc.Notify(Translator.GetString("GrenadierSkillInUse"), GrenadierSkillDuration.GetFloat()); - pc.RpcRemoveAbilityUse(); Utils.MarkEveryoneDirtySettingsV3(); } else diff --git a/Roles/Standard/Crewmate/Support/Medic.cs b/Roles/Standard/Crewmate/Support/Medic.cs index cf02724ab..e70c80e25 100644 --- a/Roles/Standard/Crewmate/Support/Medic.cs +++ b/Roles/Standard/Crewmate/Support/Medic.cs @@ -170,7 +170,7 @@ public class Medic : RoleBase { if (ProtectList.Contains(target.PlayerId)) return false; - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); ProtectList.Add(target.PlayerId); TempMarkProtectedList.Add(target.PlayerId); diff --git a/Roles/Standard/Crewmate/Support/Monarch.cs b/Roles/Standard/Crewmate/Support/Monarch.cs index 3340f7552..441754adf 100644 --- a/Roles/Standard/Crewmate/Support/Monarch.cs +++ b/Roles/Standard/Crewmate/Support/Monarch.cs @@ -74,7 +74,7 @@ public class Monarch : RoleBase if (target != null && !target.GetCustomRole().IsNotKnightable() && !target.Is(CustomRoles.Knighted) && !target.Is(CustomRoles.Stealer)) { - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); target.RpcSetCustomRole(CustomRoles.Knighted); var sender = CustomRpcSender.Create("Monarch.OnCheckMurder", SendOption.Reliable); diff --git a/Roles/Standard/Crewmate/Support/Pacifist.cs b/Roles/Standard/Crewmate/Support/Pacifist.cs index 3f57efbcb..d5d2cbf3f 100644 --- a/Roles/Standard/Crewmate/Support/Pacifist.cs +++ b/Roles/Standard/Crewmate/Support/Pacifist.cs @@ -78,7 +78,7 @@ internal class Pacifist : RoleBase return; } - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); bool isMadMate = pc.Is(CustomRoles.Madmate); Main.EnumerateAlivePlayerControls() diff --git a/Roles/Standard/Crewmate/Support/SecurityGuard.cs b/Roles/Standard/Crewmate/Support/SecurityGuard.cs index e822916dd..ae8ceec57 100644 --- a/Roles/Standard/Crewmate/Support/SecurityGuard.cs +++ b/Roles/Standard/Crewmate/Support/SecurityGuard.cs @@ -95,8 +95,8 @@ internal class SecurityGuard : RoleBase pc.RpcResetAbilityCooldown(); pc.Notify(Translator.GetString("SecurityGuardSkillStop")); }, onCanceled: () => BlockSabo.Remove(pc.PlayerId)); + pc.RpcRemoveAbilityUse(notify: false); pc.Notify(Translator.GetString("SecurityGuardSkillInUse"), SecurityGuardSkillDuration.GetFloat()); - pc.RpcRemoveAbilityUse(); } else pc.Notify(Translator.GetString("OutOfAbilityUsesDoMoreTasks")); diff --git a/Roles/Standard/Crewmate/Support/TimeMaster.cs b/Roles/Standard/Crewmate/Support/TimeMaster.cs index e6751f96a..c80336a2b 100644 --- a/Roles/Standard/Crewmate/Support/TimeMaster.cs +++ b/Roles/Standard/Crewmate/Support/TimeMaster.cs @@ -109,7 +109,7 @@ internal class TimeMaster : RoleBase public override void OnPet(PlayerControl pc) { if (pc.GetAbilityUseLimit() < 1) return; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); Main.Instance.StartCoroutine(Rewind()); @@ -121,7 +121,7 @@ internal class TimeMaster : RoleBase { if (UsePets.GetBool()) return; if (pc.GetAbilityUseLimit() < 1) return; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); Main.Instance.StartCoroutine(Rewind()); diff --git a/Roles/Standard/Crewmate/Support/Ventguard.cs b/Roles/Standard/Crewmate/Support/Ventguard.cs index d590841da..d510cffd7 100644 --- a/Roles/Standard/Crewmate/Support/Ventguard.cs +++ b/Roles/Standard/Crewmate/Support/Ventguard.cs @@ -70,7 +70,7 @@ internal class Ventguard : RoleBase { if (pc.GetAbilityUseLimit() >= 1) { - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); if (!BlockedVents.Contains(vent.Id)) BlockedVents.Add(vent.Id); pc.Notify(Translator.GetString("VentBlockSuccess")); diff --git a/Roles/Standard/Crewmate/Support/Wizard.cs b/Roles/Standard/Crewmate/Support/Wizard.cs index 643750da4..f7c1fd117 100644 --- a/Roles/Standard/Crewmate/Support/Wizard.cs +++ b/Roles/Standard/Crewmate/Support/Wizard.cs @@ -217,7 +217,7 @@ public class Wizard : RoleBase hasValue |= sender.SyncSettings(target); hasValue |= sender.SetKillCooldown(killer); - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); hasValue |= CustomRpcSenderExtensions.Notify(ref sender, killer, string.Format(Translator.GetString("Wizard.BuffGivenNotify"), target.PlayerId.ColoredPlayerName(), Translator.GetString($"Wizard.Buff.{SelectedBuff}"), Math.Round(value, 1))); Utils.SendRPC(CustomRPC.SyncRoleData, 3, target.PlayerId); diff --git a/Roles/Standard/Impostor/Concealing/Anonymous.cs b/Roles/Standard/Impostor/Concealing/Anonymous.cs index 72dda8b9d..7cde900af 100644 --- a/Roles/Standard/Impostor/Concealing/Anonymous.cs +++ b/Roles/Standard/Impostor/Concealing/Anonymous.cs @@ -83,7 +83,7 @@ public class Anonymous : RoleBase { if (!shapeshifting || pc.GetAbilityUseLimit() < 1 || ssTarget == null || ssTarget.Is(CustomRoles.LazyGuy) || ssTarget.Is(CustomRoles.Lazy) || Thanos.IsImmune(ssTarget)) return false; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); var targetId = byte.MaxValue; diff --git a/Roles/Standard/Impostor/Concealing/Fabricator.cs b/Roles/Standard/Impostor/Concealing/Fabricator.cs index 32a6350e0..bbe484870 100644 --- a/Roles/Standard/Impostor/Concealing/Fabricator.cs +++ b/Roles/Standard/Impostor/Concealing/Fabricator.cs @@ -37,6 +37,6 @@ public class Fabricator : RoleBase if (NextDeathReason == PlayerState.DeathReason.Kill || killer.GetAbilityUseLimit() < 1 || !Main.PlayerStates.TryGetValue(target.PlayerId, out var state)) return; state.deathReason = NextDeathReason; RPC.SendDeathReason(target.PlayerId, state.deathReason, state.IsDead); - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); } } \ No newline at end of file diff --git a/Roles/Standard/Impostor/Concealing/Swooper.cs b/Roles/Standard/Impostor/Concealing/Swooper.cs index 45f94b760..e152f0df9 100644 --- a/Roles/Standard/Impostor/Concealing/Swooper.cs +++ b/Roles/Standard/Impostor/Concealing/Swooper.cs @@ -210,7 +210,7 @@ public class Swooper : RoleBase StartInvisTimer(pc, ventId); - if (!wraith) pc.RpcRemoveAbilityUse(); + if (!wraith) pc.RpcRemoveAbilityUse(notify: false); pc.Notify(GetString("SwooperInvisState"), Duration); return true; @@ -253,7 +253,7 @@ public class Swooper : RoleBase StartInvisTimer(pc); - if (!wraith) pc.RpcRemoveAbilityUse(); + if (!wraith) pc.RpcRemoveAbilityUse(notify: false); pc.Notify(GetString("SwooperInvisState"), Duration); } diff --git a/Roles/Standard/Impostor/Killing/Cantankerous.cs b/Roles/Standard/Impostor/Killing/Cantankerous.cs index 10e6838eb..127ba57a0 100644 --- a/Roles/Standard/Impostor/Killing/Cantankerous.cs +++ b/Roles/Standard/Impostor/Killing/Cantankerous.cs @@ -75,6 +75,6 @@ public class Cantankerous : RoleBase public override void OnMurder(PlayerControl killer, PlayerControl target) { - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); } } \ No newline at end of file diff --git a/Roles/Standard/Impostor/Killing/Kamikaze.cs b/Roles/Standard/Impostor/Killing/Kamikaze.cs index 2d6d3cd60..193963520 100644 --- a/Roles/Standard/Impostor/Killing/Kamikaze.cs +++ b/Roles/Standard/Impostor/Killing/Kamikaze.cs @@ -61,8 +61,6 @@ internal class Kamikaze : RoleBase public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) { - if (killer == null || target == null) return false; - if (killer.GetAbilityUseLimit() < 1) return true; return killer.CheckDoubleTrigger(target, () => diff --git a/Roles/Standard/Impostor/Killing/Witch.cs b/Roles/Standard/Impostor/Killing/Witch.cs index 4d0751130..69e858433 100644 --- a/Roles/Standard/Impostor/Killing/Witch.cs +++ b/Roles/Standard/Impostor/Killing/Witch.cs @@ -181,7 +181,7 @@ public class Witch : RoleBase SendRPC(true, killer.PlayerId, target.PlayerId); killer.SetKillCooldown(SpellCooldown.GetFloat()); killer.RPCPlayCustomSound("Curse"); - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target, ForceLoop: true); } } diff --git a/Roles/Standard/Impostor/Miscellaneous/Consigliere.cs b/Roles/Standard/Impostor/Miscellaneous/Consigliere.cs index 94b578d58..27cd6cd11 100644 --- a/Roles/Standard/Impostor/Miscellaneous/Consigliere.cs +++ b/Roles/Standard/Impostor/Miscellaneous/Consigliere.cs @@ -118,7 +118,7 @@ public class Consigliere : RoleBase { if (!IsReveal(target.PlayerId)) { - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); RevealTarget.Add(target.PlayerId); Logger.Info($"{killer.GetNameWithRole()}: Reveal target → {target.GetNameWithRole()} || Remaining: {killer.GetAbilityUseLimit()} uses", "Consigliere"); Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); diff --git a/Roles/Standard/Impostor/Miscellaneous/Obstructer.cs b/Roles/Standard/Impostor/Miscellaneous/Obstructer.cs index 25859cc36..f2eb013aa 100644 --- a/Roles/Standard/Impostor/Miscellaneous/Obstructer.cs +++ b/Roles/Standard/Impostor/Miscellaneous/Obstructer.cs @@ -59,8 +59,10 @@ public class Obstructer : RoleBase foreach (PlayerControl pc in Main.EnumerateAlivePlayerControls()) { - if (!Main.AllPlayerSpeed.ContainsKey(pc.PlayerId)) continue; - Main.AllPlayerSpeed[pc.PlayerId] -= SpeedReductionPerSecond.GetFloat(); + if (!Main.AllPlayerSpeed.TryGetValue(pc.PlayerId, out float speed)) continue; + float reduction = SpeedReductionPerSecond.GetFloat(); + if (speed - reduction <= Main.MinSpeed) continue; + Main.AllPlayerSpeed[pc.PlayerId] -= reduction; pc.MarkDirtySettings(); } } while (Utils.IsAnySabotageActive()); diff --git a/Roles/Standard/Impostor/Miscellaneous/Visionary.cs b/Roles/Standard/Impostor/Miscellaneous/Visionary.cs index dde3f11b0..8affc556d 100644 --- a/Roles/Standard/Impostor/Miscellaneous/Visionary.cs +++ b/Roles/Standard/Impostor/Miscellaneous/Visionary.cs @@ -72,7 +72,7 @@ internal class Visionary : RoleBase RevealedPlayerIds.Add(target.PlayerId); Utils.SendRPC(CustomRPC.SyncRoleData, shapeshifter.PlayerId, target.PlayerId); - shapeshifter.RpcRemoveAbilityUse(); + shapeshifter.RpcRemoveAbilityUse(notify: false); Utils.NotifyRoles(SpecifySeer: shapeshifter, SpecifyTarget: target); return false; diff --git a/Roles/Standard/Impostor/Support/Blackmailer.cs b/Roles/Standard/Impostor/Support/Blackmailer.cs index 9d8cd798f..90fb302f1 100644 --- a/Roles/Standard/Impostor/Support/Blackmailer.cs +++ b/Roles/Standard/Impostor/Support/Blackmailer.cs @@ -54,9 +54,9 @@ internal class Blackmailer : RoleBase return killer.CheckDoubleTrigger(target, () => { BlackmailedPlayerIds.Add(target.PlayerId); + killer.RpcRemoveAbilityUse(notify: false); Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); killer.SetKillCooldown(3f); - killer.RpcRemoveAbilityUse(); NumBlackmailedThisRound++; }); } diff --git a/Roles/Standard/Impostor/Support/Camouflager.cs b/Roles/Standard/Impostor/Support/Camouflager.cs index f7cba6f65..40e217a02 100644 --- a/Roles/Standard/Impostor/Support/Camouflager.cs +++ b/Roles/Standard/Impostor/Support/Camouflager.cs @@ -84,7 +84,7 @@ public class Camouflager : RoleBase return true; } - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); IsActive = true; Camouflage.CheckCamouflage(); @@ -94,7 +94,7 @@ public class Camouflager : RoleBase public override bool OnVanish(PlayerControl pc) { if (pc.GetAbilityUseLimit() < 1) return false; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); IsActive = true; Camouflage.CheckCamouflage(); diff --git a/Roles/Standard/Impostor/Support/Consort.cs b/Roles/Standard/Impostor/Support/Consort.cs index 02430d20f..2ba97a1a6 100644 --- a/Roles/Standard/Impostor/Support/Consort.cs +++ b/Roles/Standard/Impostor/Support/Consort.cs @@ -49,13 +49,11 @@ public class Consort : RoleBase public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) { - if (!IsEnable || killer == null || target == null) return false; - if (killer.GetAbilityUseLimit() <= 0 || !killer.Is(CustomRoles.Consort)) return true; return killer.CheckDoubleTrigger(target, () => { - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); target.BlockRole(Duration.GetFloat()); killer.Notify(GetString("EscortTargetHacked")); killer.SetKillCooldown(CD.GetFloat()); diff --git a/Roles/Standard/Impostor/Support/EvilEraser.cs b/Roles/Standard/Impostor/Support/EvilEraser.cs index 46f95582e..eacd698da 100644 --- a/Roles/Standard/Impostor/Support/EvilEraser.cs +++ b/Roles/Standard/Impostor/Support/EvilEraser.cs @@ -88,7 +88,7 @@ internal class EvilEraser : RoleBase { return killer.CheckDoubleTrigger(target, () => { - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); killer.SetKillCooldown(); killer.Notify(GetString("TargetErasedInRound")); if (!PlayerToErase.Contains(target.PlayerId)) PlayerToErase.Add(target.PlayerId); diff --git a/Roles/Standard/Impostor/Support/Framer.cs b/Roles/Standard/Impostor/Support/Framer.cs index 3798bb494..efedd6c61 100644 --- a/Roles/Standard/Impostor/Support/Framer.cs +++ b/Roles/Standard/Impostor/Support/Framer.cs @@ -55,8 +55,8 @@ public class Framer : RoleBase { FramedPlayers.Add(target.PlayerId); killer.SetKillCooldown(); + killer.RpcRemoveAbilityUse(notify: false); killer.Notify(string.Format(Translator.GetString("Framer.TargetFramedNotify"), target.PlayerId.ColoredPlayerName())); - killer.RpcRemoveAbilityUse(); }); } } \ No newline at end of file diff --git a/Roles/Standard/Impostor/Support/Gangster.cs b/Roles/Standard/Impostor/Support/Gangster.cs index 53f21ccce..82a3b2e45 100644 --- a/Roles/Standard/Impostor/Support/Gangster.cs +++ b/Roles/Standard/Impostor/Support/Gangster.cs @@ -81,7 +81,7 @@ public class Gangster : RoleBase { if (!killer.GetCustomSubRoles().FindFirst(x => x.IsConverted(), out CustomRoles convertedAddon)) convertedAddon = CustomRoles.Madmate; - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); target.RpcSetCustomRole(convertedAddon); var sender = CustomRpcSender.Create("Gangster.OnCheckMurder", SendOption.Reliable); diff --git a/Roles/Standard/Impostor/Support/Gatekeeper.cs b/Roles/Standard/Impostor/Support/Gatekeeper.cs index fe7dd314c..749e1aae1 100644 --- a/Roles/Standard/Impostor/Support/Gatekeeper.cs +++ b/Roles/Standard/Impostor/Support/Gatekeeper.cs @@ -82,7 +82,7 @@ public class Gatekeeper : RoleBase if (room && MarkedRooms.TryAdd(room.RoomId, -1)) { - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); if (MarkedRooms.Count > MaxRoomsMarkedAtOnce.GetInt()) MarkedRooms.Remove(MarkedRooms.Keys.First()); diff --git a/Roles/Standard/Impostor/Support/Hypnotist.cs b/Roles/Standard/Impostor/Support/Hypnotist.cs index 29912fc38..d20aa1252 100644 --- a/Roles/Standard/Impostor/Support/Hypnotist.cs +++ b/Roles/Standard/Impostor/Support/Hypnotist.cs @@ -80,7 +80,7 @@ public class Hypnotist : RoleBase public override void OnPet(PlayerControl pc) { if (pc.GetAbilityUseLimit() < 1) return; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); Timer = new CountdownTimer(AbilityDuration.GetInt(), () => { diff --git a/Roles/Standard/Impostor/Support/Nullifier.cs b/Roles/Standard/Impostor/Support/Nullifier.cs index e6a3eed55..91ba4aed7 100644 --- a/Roles/Standard/Impostor/Support/Nullifier.cs +++ b/Roles/Standard/Impostor/Support/Nullifier.cs @@ -88,7 +88,7 @@ internal class Nullifier : RoleBase Vigilante.SendRPC(target.PlayerId); break; default: - target.RpcRemoveAbilityUse(); + target.RpcRemoveAbilityUse(notify: false); break; } diff --git a/Roles/Standard/Impostor/Support/Occultist.cs b/Roles/Standard/Impostor/Support/Occultist.cs index 14a7f2770..351d3ea07 100644 --- a/Roles/Standard/Impostor/Support/Occultist.cs +++ b/Roles/Standard/Impostor/Support/Occultist.cs @@ -121,7 +121,7 @@ public class Occultist : RoleBase OccultistPC.Notify(string.Format(Translator.GetString("OccultistRevived"), target.PlayerId.ColoredPlayerName())); }, onCanceled: () => Revives.Remove(target.PlayerId)); - reporter.RpcRemoveAbilityUse(); + reporter.RpcRemoveAbilityUse(notify: false); reporter.Notify(string.Format(Translator.GetString("OccultistReviving"), target.PlayerId.ColoredPlayerName()), ReviveTime.GetInt()); return false; diff --git a/Roles/Standard/Neutral/Benign/Backstabber.cs b/Roles/Standard/Neutral/Benign/Backstabber.cs index 27624cf6c..26cfa904c 100644 --- a/Roles/Standard/Neutral/Benign/Backstabber.cs +++ b/Roles/Standard/Neutral/Benign/Backstabber.cs @@ -82,7 +82,7 @@ public class Backstabber : RoleBase if (targetTeam == EHR.Team.Crewmate && killer.AmOwner) Achievements.Type.StabbingTheBack.Complete(); - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); killer.Notify(string.Format(Translator.GetString("Backstabber.MurderNotify"), Utils.ColorString(targetTeam.GetColor(), Translator.GetString(targetTeam.ToString())), Translator.GetString($"BackstabberTeam.{Team}")), 10f); } diff --git a/Roles/Standard/Neutral/Benign/Investor.cs b/Roles/Standard/Neutral/Benign/Investor.cs index 6f85c09c1..845040f94 100644 --- a/Roles/Standard/Neutral/Benign/Investor.cs +++ b/Roles/Standard/Neutral/Benign/Investor.cs @@ -63,7 +63,7 @@ public class Investor : RoleBase { if (MarkedPlayers.Add(target.PlayerId)) { - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); Utils.SendRPC(CustomRPC.SyncRoleData, killer.PlayerId, target.PlayerId); killer.SetKillCooldown(AbilityCooldown.GetInt()); diff --git a/Roles/Standard/Neutral/Benign/Pursuer.cs b/Roles/Standard/Neutral/Benign/Pursuer.cs index 58ae44b83..1327fb2dd 100644 --- a/Roles/Standard/Neutral/Benign/Pursuer.cs +++ b/Roles/Standard/Neutral/Benign/Pursuer.cs @@ -79,7 +79,7 @@ public class Pursuer : RoleBase { if (!pc || !target || !pc.Is(CustomRoles.Pursuer)) return; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); clientList.Add(target.PlayerId); NotActiveList.Add(pc.PlayerId); pc.SetKillCooldown(); diff --git a/Roles/Standard/Neutral/Evil/Gaslighter.cs b/Roles/Standard/Neutral/Evil/Gaslighter.cs index 901d0345f..0087778cb 100644 --- a/Roles/Standard/Neutral/Evil/Gaslighter.cs +++ b/Roles/Standard/Neutral/Evil/Gaslighter.cs @@ -206,9 +206,9 @@ public class Gaslighter : RoleBase target.RpcGuardAndKill(killer); target.RpcGuardAndKill(target); target.Notify(CustomRoles.Monarch.ColoredTextByRole(Translator.GetString("KnightedByMonarch"))); - Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); killer.SetKillCooldown(); + Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); break; case Round.Curse: killer.RPCPlayCustomSound("Curse"); @@ -221,10 +221,10 @@ public class Gaslighter : RoleBase killer.RPCPlayCustomSound("Shield"); target.RPCPlayCustomSound("Shield"); ShieldedPlayers.Add(target.PlayerId); + killer.RpcRemoveAbilityUse(notify: false); + killer.SetKillCooldown(); Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: target); - killer.RpcRemoveAbilityUse(); - killer.SetKillCooldown(); break; } diff --git a/Roles/Standard/Neutral/Evil/Vulture.cs b/Roles/Standard/Neutral/Evil/Vulture.cs index a92c777f4..f30adfd79 100644 --- a/Roles/Standard/Neutral/Evil/Vulture.cs +++ b/Roles/Standard/Neutral/Evil/Vulture.cs @@ -159,7 +159,7 @@ public class Vulture : RoleBase return false; } - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); CooldownTimer = new CountdownTimer(VultureReportCD.GetInt(), () => { CooldownTimer = null; diff --git a/Roles/Standard/Neutral/Killing/Blockade.cs b/Roles/Standard/Neutral/Killing/Blockade.cs index b90c932cb..d44ec93d3 100644 --- a/Roles/Standard/Neutral/Killing/Blockade.cs +++ b/Roles/Standard/Neutral/Killing/Blockade.cs @@ -89,7 +89,7 @@ public class Blockade : RoleBase void PlaceBlockade(PlayerControl pc) { if (pc.GetAbilityUseLimit() < 1f) return; - pc.RpcRemoveAbilityUse(); + pc.RpcRemoveAbilityUse(notify: false); Blockades.Add(pc.Pos()); pc.Notify(Translator.GetString("MarkDone")); diff --git a/Roles/Standard/Neutral/Killing/Cherokious.cs b/Roles/Standard/Neutral/Killing/Cherokious.cs index b14579900..906c66e82 100644 --- a/Roles/Standard/Neutral/Killing/Cherokious.cs +++ b/Roles/Standard/Neutral/Killing/Cherokious.cs @@ -52,8 +52,9 @@ internal class Cherokious : RoleBase if (pc.GetAbilityUseLimit() < 1) return; PlayerControl target = ExternalRpcPetPatch.SelectKillButtonTarget(pc); + if (!target) return; if (target.Is(CustomRoles.Spy) && !Spy.OnKillAttempt(pc, target)) return; - if (target != null && pc.RpcCheckAndMurder(target)) pc.RpcRemoveAbilityUse(); + if (pc.RpcCheckAndMurder(target)) pc.RpcRemoveAbilityUse(); } public override bool CanUseKillButton(PlayerControl pc) diff --git a/Roles/Standard/Neutral/Killing/Cultist.cs b/Roles/Standard/Neutral/Killing/Cultist.cs index 644581272..1bdba2aa4 100644 --- a/Roles/Standard/Neutral/Killing/Cultist.cs +++ b/Roles/Standard/Neutral/Killing/Cultist.cs @@ -104,7 +104,7 @@ public class Cultist : RoleBase if (CanBeCharmed(target)) { - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); target.RpcSetCustomRole(CustomRoles.Charmed); diff --git a/Roles/Standard/Neutral/Killing/Jackal.cs b/Roles/Standard/Neutral/Killing/Jackal.cs index 56f552253..f5876f167 100644 --- a/Roles/Standard/Neutral/Killing/Jackal.cs +++ b/Roles/Standard/Neutral/Killing/Jackal.cs @@ -164,7 +164,7 @@ public class Jackal : RoleBase var sender = CustomRpcSender.Create("Jackal.OnCheckMurder", SendOption.Reliable); var hasValue = false; - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); target.RpcSetCustomRole(CustomRoles.Sidekick); target.RpcChangeRoleBasis(CustomRoles.Sidekick); SidekickId = target.PlayerId; diff --git a/Roles/Standard/Neutral/Killing/Quarry.cs b/Roles/Standard/Neutral/Killing/Quarry.cs index e10739be6..4d9824907 100644 --- a/Roles/Standard/Neutral/Killing/Quarry.cs +++ b/Roles/Standard/Neutral/Killing/Quarry.cs @@ -120,7 +120,7 @@ public class Quarry : RoleBase Utils.SendRPC(CustomRPC.SyncRoleData, QuarryId, 2); }, onTick: () => { - if (target == null || !target.IsAlive()) + if (!target || !target.IsAlive()) { TargetId = byte.MaxValue; SeekTimer = null; diff --git a/Roles/Standard/Neutral/Killing/Sprayer.cs b/Roles/Standard/Neutral/Killing/Sprayer.cs index 4eb0b5549..1d9ac9cf0 100644 --- a/Roles/Standard/Neutral/Killing/Sprayer.cs +++ b/Roles/Standard/Neutral/Killing/Sprayer.cs @@ -141,7 +141,7 @@ internal class Sprayer : RoleBase Vector2 pos = SprayerPC.Pos(); Traps[pos] = new(pos, SprayerPC); - SprayerPC.RpcRemoveAbilityUse(); + SprayerPC.RpcRemoveAbilityUse(notify: false); if (SprayerId.GetAbilityUseLimit() > 0) SprayerPC.AddAbilityCD(CD.GetInt()); diff --git a/Roles/Standard/Neutral/Killing/Virus.cs b/Roles/Standard/Neutral/Killing/Virus.cs index 3df7f9e08..bc132fdb8 100644 --- a/Roles/Standard/Neutral/Killing/Virus.cs +++ b/Roles/Standard/Neutral/Killing/Virus.cs @@ -121,7 +121,7 @@ public class Virus : RoleBase if (!CanBeInfected(target)) return; byte id = PlayerIdList[0]; - Utils.GetPlayerById(id).RpcRemoveAbilityUse(); + Utils.GetPlayerById(id)?.RpcRemoveAbilityUse(); if (KillInfectedPlayerAfterMeeting.GetBool()) { diff --git a/Roles/Standard/Neutral/Pariah/Spirit.cs b/Roles/Standard/Neutral/Pariah/Spirit.cs index 34648c20a..085d5a131 100644 --- a/Roles/Standard/Neutral/Pariah/Spirit.cs +++ b/Roles/Standard/Neutral/Pariah/Spirit.cs @@ -75,7 +75,7 @@ public class Spirit : RoleBase break; } - shapeshifter.RpcRemoveAbilityUse(); + shapeshifter.RpcRemoveAbilityUse(notify: false); shapeshifter.SyncSettings(); target.Notify(string.Format(Translator.GetString("SpiritTarget"), CustomRoles.Spirit.ToColoredString())); Utils.NotifyRoles(SpecifySeer: shapeshifter, SpecifyTarget: target); diff --git a/Roles/Standard/Neutral/Pariah/Starspawn.cs b/Roles/Standard/Neutral/Pariah/Starspawn.cs index 560035248..38430fa4d 100644 --- a/Roles/Standard/Neutral/Pariah/Starspawn.cs +++ b/Roles/Standard/Neutral/Pariah/Starspawn.cs @@ -70,7 +70,7 @@ public class Starspawn : RoleBase public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) { if (!IsolatedPlayers.Add(target.PlayerId)) return false; - killer.RpcRemoveAbilityUse(); + killer.RpcRemoveAbilityUse(notify: false); killer.SetKillCooldown(AbilityCooldown.GetFloat()); Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); Utils.SendRPC(CustomRPC.SyncRoleData, killer.PlayerId, target.PlayerId);