diff --git a/Gamemodes/KingOfTheZones.cs b/Gamemodes/KingOfTheZones.cs index 6e952b2c2..0863bc862 100644 --- a/Gamemodes/KingOfTheZones.cs +++ b/Gamemodes/KingOfTheZones.cs @@ -324,7 +324,7 @@ public static class KingOfTheZones notify = notify.Insert(0, tutorial + "\n\n"); } - hasData |= writer.Notify(player, $"<#ffffff>{notify}", 100f); + hasData |= CustomRpcSenderExtensions.Notify(ref writer, player, $"<#ffffff>{notify}", 100f); Logger.Info($"{name} assigned to {team} team", "KOTZ"); if (!player.AmOwner) diff --git a/Modules/AntiBlackout.cs b/Modules/AntiBlackout.cs index 07047c73a..0cc16fbb8 100644 --- a/Modules/AntiBlackout.cs +++ b/Modules/AntiBlackout.cs @@ -4,6 +4,7 @@ using System.Linq; using AmongUs.GameOptions; using EHR.Modules; using EHR.Patches; +using Hazel; namespace EHR; @@ -64,16 +65,61 @@ public static class AntiBlackout } // Reset the role types for all players. - foreach (((byte seerId, byte targetId), (RoleTypes roleType, CustomRoles _)) in CachedRoleMap) + // First group all entries by target. + foreach (var targetGroup in CachedRoleMap.GroupBy(x => x.Key.TargetID)) { try { - PlayerControl seer = seerId.GetPlayer(); + byte targetId = targetGroup.Key; PlayerControl target = targetId.GetPlayer(); - if (!seer || !target || (seerId == targetId && seer.AmOwner && Utils.TempReviveHostRunning)) continue; + if (!target) continue; - if (target.IsAlive() && !Main.AfterMeetingDeathPlayers.ContainsKey(targetId) && Main.LastVotedPlayerInfo != target.Data) target.RpcSetRoleDesync(roleType, seer.OwnerId); - else target.RpcSetRoleDesync(GhostRolesManager.AssignedGhostRoles.TryGetValue(targetId, out var ghostRole) ? ghostRole.Instance.RoleTypes : seerId == targetId && !(target.Is(CustomRoleTypes.Impostor) && Options.DeadImpCantSabotage.GetBool()) && Main.PlayerStates.TryGetValue(targetId, out var state) && state.Role.CanUseSabotage(target) ? RoleTypes.ImpostorGhost : RoleTypes.CrewmateGhost, seer.OwnerId); + // Compute the role every seer should see. + Dictionary rolesForSeers = []; + + foreach (var entry in targetGroup) + { + byte seerId = entry.Key.SeerID; + + RoleTypes role = target.IsAlive() && !Main.AfterMeetingDeathPlayers.ContainsKey(targetId) && Main.LastVotedPlayerInfo != target.Data + ? entry.Value.RoleType + : GhostRolesManager.AssignedGhostRoles.TryGetValue(targetId, out var ghostRole) + ? ghostRole.Instance.RoleTypes + : seerId == targetId && + !(target.Is(CustomRoleTypes.Impostor) && Options.DeadImpCantSabotage.GetBool()) && + Main.PlayerStates.TryGetValue(targetId, out var state) && + state.Role.CanUseSabotage(target) + ? RoleTypes.ImpostorGhost + : RoleTypes.CrewmateGhost; + + rolesForSeers[seerId] = role; + } + + // Are they all identical? + RoleTypes firstRole = rolesForSeers.Values.First(); + bool identical = rolesForSeers.Values.All(r => r == firstRole); + + if (identical) + { + // Only 1 RPC is needed if the role type is the same for everyone. + target.RpcSetRoleGlobal(firstRole); + } + else + { + foreach ((byte seerId, RoleTypes roleTypes) in rolesForSeers) + { + try + { + PlayerControl seer = seerId.GetPlayer(); + + if (!seer || (seerId == targetId && seer.AmOwner && Utils.TempReviveHostRunning)) + continue; + + target.RpcSetRoleDesync(roleTypes, seer.OwnerId); + } + catch (Exception e) { Utils.ThrowException(e); } + } + } } catch (Exception e) { Utils.ThrowException(e); } } @@ -85,6 +131,8 @@ public static class AntiBlackout LateTask.New(() => { var elapsedSeconds = (int)ExileControllerWrapUpPatch.Stopwatch.Elapsed.TotalSeconds; + var sender = CustomRpcSender.Create("Exile Dead Players After Meeting", SendOption.Reliable); + var hasValue = false; foreach (PlayerControl pc in Main.EnumeratePlayerControls()) { @@ -109,7 +157,8 @@ public static class AntiBlackout if (pc.AmOwner && Utils.TempReviveHostRunning) continue; // Ensure that the players who are considered dead by the mod are actually dead in the game. - pc.RpcExiled(); + sender.RpcExiled(pc); + hasValue = true; if (GhostRolesManager.AssignedGhostRoles.TryGetValue(pc.PlayerId, out var ghostRole) && ghostRole.Instance.RoleTypes == RoleTypes.GuardianAngel) pc.AddAbilityCD(ghostRole.Instance.Cooldown); @@ -117,6 +166,8 @@ public static class AntiBlackout } catch (Exception e) { Utils.ThrowException(e); } } + + sender.SendMessage(dispose: !hasValue); // Only execute AfterMeetingTasks after everything is reset. LateTask.New(() => diff --git a/Modules/CustomRpcSender.cs b/Modules/CustomRpcSender.cs index 17c5d5fd6..fe17540f6 100644 --- a/Modules/CustomRpcSender.cs +++ b/Modules/CustomRpcSender.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Text; using AmongUs.GameOptions; using EHR.Modules; using EHR.Roles; @@ -579,7 +580,7 @@ public static class CustomRpcSenderExtensions static int GetSetNameRpcSize(uint netId, string playerName) { - int byteCount = System.Text.Encoding.UTF8.GetByteCount(playerName); + int byteCount = Encoding.UTF8.GetByteCount(playerName); return 3 + PackedUIntSize(netId) + 1 + 4 + PackedUIntSize((uint)byteCount) + byteCount + 1; } @@ -596,6 +597,39 @@ public static class CustomRpcSenderExtensions } } + public static bool Notify(ref CustomRpcSender sender, PlayerControl pc, string text, float time = 6f, bool overrideAll = false, bool log = true, bool setName = true) + { + if (!AmongUsClient.Instance.AmHost || !pc) return false; + if (!GameStates.IsInTask) return false; + if (!text.Contains('#') && !text.Contains("{text}"; + + long expireTS = Utils.TimeStamp + (long)time; + bool alreadyContainsKey = false; + + if (overrideAll || !NameNotifyManager.Notifies.TryGetValue(pc.PlayerId, out Dictionary notifies)) + NameNotifyManager.Notifies[pc.PlayerId] = new() { { text, expireTS } }; + else + { + alreadyContainsKey = notifies.ContainsKey(text); + notifies[text] = expireTS; + } + + bool returnValue = pc.IsNonHostModdedClient(); + if (returnValue) NameNotifyManager.SendRPC(sender, pc, text, expireTS, overrideAll); + + if (alreadyContainsKey) + { + if (log) Logger.Info($"Extended name notify for {pc.GetNameWithRole()}: {text} ({time}s)", "Name Notify"); + return returnValue; + } + + if (setName) returnValue |= Utils.WriteSetNameRpcsToSender(ref sender, false, false, false, false, false, false, pc, [pc], [], out bool senderWasCleared) && !senderWasCleared; + if (log) Logger.Info($"New name notify for {pc.GetNameWithRole()}: {text} ({time}s)", "Name Notify"); + + return returnValue; + } + extension(CustomRpcSender sender) { public bool RpcSetRole(PlayerControl player, RoleTypes role, int targetClientId = -1, bool noRpcForSelf = true, bool changeRoleMap = false) @@ -673,7 +707,7 @@ public static class CustomRpcSenderExtensions sender.EndRpc(); if (Options.CurrentGameMode == CustomGameMode.Standard && !MeetingStates.FirstMeeting && !AntiBlackout.SkipTasks && !ExileController.Instance && GameStates.IsInTask && killer.IsBeginner() && Main.GotShieldAnimationInfoThisGame.Add(killer.PlayerId)) - sender.Notify(killer, Translator.GetString("PleaseStopBeingDumb"), 10f); + Notify(ref sender, killer, Translator.GetString("PleaseStopBeingDumb"), 10f); returnValue = true; } @@ -797,39 +831,6 @@ public static class CustomRpcSenderExtensions sender.EndRpc(); } - public bool Notify(PlayerControl pc, string text, float time = 6f, bool overrideAll = false, bool log = true, bool setName = true) - { - if (!AmongUsClient.Instance.AmHost || !pc) return false; - if (!GameStates.IsInTask) return false; - if (!text.Contains("")) text = Utils.ColorString(Color.white, text); - if (!text.Contains("{text}"; - - long expireTS = Utils.TimeStamp + (long)time; - bool alreadyContainsKey = false; - - if (overrideAll || !NameNotifyManager.Notifies.TryGetValue(pc.PlayerId, out Dictionary notifies)) - NameNotifyManager.Notifies[pc.PlayerId] = new() { { text, expireTS } }; - else - { - alreadyContainsKey = notifies.ContainsKey(text); - notifies[text] = expireTS; - } - - bool returnValue = pc.IsNonHostModdedClient(); - if (returnValue) NameNotifyManager.SendRPC(sender, pc, text, expireTS, overrideAll); - - if (alreadyContainsKey) - { - if (log) Logger.Info($"Extended name notify for {pc.GetNameWithRole()}: {text} ({time}s)", "Name Notify"); - return returnValue; - } - - if (setName) returnValue |= Utils.WriteSetNameRpcsToSender(ref sender, false, false, false, false, false, false, pc, [pc], [], out bool senderWasCleared) && !senderWasCleared; - if (log) Logger.Info($"New name notify for {pc.GetNameWithRole()}: {text} ({time}s)", "Name Notify"); - - return returnValue; - } - public bool TP(PlayerControl pc, Vector2 location, bool noCheckState = false, bool log = true) { if (!AmongUsClient.Instance.AmHost) return false; diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index ed255ae23..a4ae2b271 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -584,7 +584,7 @@ internal static class ExtendedPlayerControl bool self = player.PlayerId == seer.PlayerId; if (!self && seer.HasDesyncRole() && !seerIsHost) - rememberRoleType = newRoleVN is CustomRoles.Noisemaker ? RoleTypes.Noisemaker : RoleTypes.Scientist; + rememberRoleType = newRoleVN is CustomRoles.Noisemaker ? RoleTypes.Noisemaker : RoleTypes.Crewmate; else rememberRoleType = newRoleType; @@ -598,10 +598,7 @@ internal static class ExtendedPlayerControl if (seer.IsAlive()) { - if (seerCustomRole.IsDesyncRole()) - rememberRoleType = seerIsHost ? RoleTypes.Crewmate : RoleTypes.Scientist; - else - rememberRoleType = seerRoleType; + rememberRoleType = seerCustomRole.IsDesyncRole() ? RoleTypes.Crewmate : seerRoleType; } else { @@ -610,7 +607,7 @@ internal static class ExtendedPlayerControl rememberRoleType = RoleTypes.CrewmateGhost; if (!playerIsKiller && seer.Is(Team.Impostor)) rememberRoleType = RoleTypes.ImpostorGhost; - StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(player.PlayerId, seer.PlayerId)] = (seerCustomRole.IsDesyncRole() ? seerIsHost ? RoleTypes.Crewmate : RoleTypes.Scientist : seerRoleType, seerCustomRole); + StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(player.PlayerId, seer.PlayerId)] = (seerCustomRole.IsDesyncRole() ? RoleTypes.Crewmate : seerRoleType, seerCustomRole); seer.RpcSetRoleDesync(rememberRoleType, player.OwnerId); continue; } @@ -642,7 +639,7 @@ internal static class ExtendedPlayerControl rememberRoleType = newRoleDY; } else - rememberRoleType = newRoleVN is CustomRoles.Noisemaker ? RoleTypes.Noisemaker : RoleTypes.Scientist; + rememberRoleType = newRoleVN is CustomRoles.Noisemaker ? RoleTypes.Noisemaker : RoleTypes.Crewmate; StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(seer.PlayerId, player.PlayerId)] = (rememberRoleType, newCustomRole); player.RpcSetRoleDesync(rememberRoleType, seerClientId); @@ -652,12 +649,12 @@ internal static class ExtendedPlayerControl CustomRoles seerCustomRole = seer.GetRoleMap().CustomRole; if (seer.IsAlive()) - rememberRoleType = newRoleVN is CustomRoles.Noisemaker ? RoleTypes.Noisemaker : RoleTypes.Scientist; + rememberRoleType = newRoleVN is CustomRoles.Noisemaker ? RoleTypes.Noisemaker : RoleTypes.Crewmate; else { rememberRoleType = RoleTypes.CrewmateGhost; - StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(player.PlayerId, seer.PlayerId)] = (seerCustomRole.GetVNRole() is CustomRoles.Noisemaker ? RoleTypes.Noisemaker : RoleTypes.Scientist, seerCustomRole); + StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(player.PlayerId, seer.PlayerId)] = (seerCustomRole.GetVNRole() is CustomRoles.Noisemaker ? RoleTypes.Noisemaker : RoleTypes.Crewmate, seerCustomRole); seer.RpcSetRoleDesync(rememberRoleType, player.OwnerId); continue; } @@ -856,12 +853,14 @@ internal static class ExtendedPlayerControl public void AddAbilityCD(int cd) { Main.AbilityCD[player.PlayerId] = (TimeStamp, cd); + if (!player.IsNonHostModdedClient()) return; SendRPC(CustomRPC.SyncAbilityCD, 1, player.PlayerId, cd); } public void RemoveAbilityCD() { - if (Main.AbilityCD.Remove(player.PlayerId)) SendRPC(CustomRPC.SyncAbilityCD, 3, player.PlayerId); + if (Main.AbilityCD.Remove(player.PlayerId) && player.IsNonHostModdedClient()) + SendRPC(CustomRPC.SyncAbilityCD, 3, player.PlayerId); } public float GetAbilityUseLimit() @@ -1225,7 +1224,7 @@ internal static class ExtendedPlayerControl { sender.TP(player, pcPos, noCheckState: true); sender.SetKillCooldown(player, timer); - sender.Notify(player, GetString("BlackScreenFixCompleteNotify")); + CustomRpcSenderExtensions.Notify(ref sender, player, GetString("BlackScreenFixCompleteNotify")); dummyGhost.NetTransform.SnapTo(ghostPos, (ushort)(dummyGhost.NetTransform.lastSequenceId + 328)); dummyGhost.NetTransform.SetDirtyBit(uint.MaxValue); diff --git a/Modules/Extensions/CollectionExtensions.cs b/Modules/Extensions/CollectionExtensions.cs index 1a5890885..d64557a86 100644 --- a/Modules/Extensions/CollectionExtensions.cs +++ b/Modules/Extensions/CollectionExtensions.cs @@ -457,11 +457,11 @@ public static class CollectionExtensions foreach (PlayerControl player in players) { - hasValue |= sender.Notify(player, text, time, overrideAll, log, setName); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, player, text, time, overrideAll, log, setName); // RpcSetName will handle oversized packets } - sender.SendMessage(dispose: !hasValue); + sender.SendMessage(dispose: !hasValue || sender.stream.Length <= 11); } public static void NotifyPlayers(this List players, string text, float time = 6f, bool overrideAll = false, bool log = true, bool setName = true) { @@ -470,11 +470,11 @@ public static class CollectionExtensions for (int index = 0; index < players.Count; index++) { - hasValue |= sender.Notify(players[index], text, time, overrideAll, log, setName); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, players[index], text, time, overrideAll, log, setName); // RpcSetName will handle oversized packets } - sender.SendMessage(dispose: !hasValue); + sender.SendMessage(dispose: !hasValue || sender.stream.Length <= 11); } #region ToValidPlayers diff --git a/Modules/GameOptionsSender/GameOptionsSender.cs b/Modules/GameOptionsSender/GameOptionsSender.cs index 3e4d8d4cf..089d5d660 100644 --- a/Modules/GameOptionsSender/GameOptionsSender.cs +++ b/Modules/GameOptionsSender/GameOptionsSender.cs @@ -98,6 +98,12 @@ public abstract class GameOptionsSender { while (GameStates.InGame || GameStates.IsLobby) { + if (AntiBlackout.SkipTasks) + { + yield return new WaitForSecondsRealtime(1f); + continue; + } + if (GameStates.InGame) { PackedWriterMessages = 0; diff --git a/Modules/NameNotifyManager.cs b/Modules/NameNotifyManager.cs index 4ce57dab9..0145ea086 100644 --- a/Modules/NameNotifyManager.cs +++ b/Modules/NameNotifyManager.cs @@ -24,7 +24,7 @@ public static class NameNotifyManager if (!GameStates.IsInTask) return; text = text.Trim(); - if (!text.Contains("") && !text.Contains("<#")) text = Utils.ColorString(Color.white, text); + if (!text.Contains('#') && !text.Contains("" + text + ""; long expireTS = Utils.TimeStamp + (long)time; diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 6f5a88613..71f2ba2cd 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -4071,6 +4071,7 @@ public static class Utils { var kcd = (int)Math.Round(Main.AllPlayerKillCooldown.TryGetValue(playerId, out float killCd) ? killCd : Options.AdjustedDefaultKillCooldown); Main.AbilityCD[playerId] = (TimeStamp, kcd); + if (playerId == 0 || !playerId.IsPlayerModdedClient()) return; SendRPC(CustomRPC.SyncAbilityCD, 1, playerId, kcd); return; } @@ -4156,6 +4157,7 @@ public static class Utils cd -= (int)ExileControllerWrapUpPatch.Stopwatch.Elapsed.TotalSeconds; Main.AbilityCD[playerId] = (TimeStamp, cd); + if (playerId == 0 || !playerId.IsPlayerModdedClient()) return; SendRPC(CustomRPC.SyncAbilityCD, 1, playerId, cd); } diff --git a/Patches/GameStartManagerPatch.cs b/Patches/GameStartManagerPatch.cs index b132b4a62..b79b0b534 100644 --- a/Patches/GameStartManagerPatch.cs +++ b/Patches/GameStartManagerPatch.cs @@ -384,7 +384,7 @@ public static class GameStartManagerPatch } } - if (!canStartGame) __instance.StartButton.gameObject.SetActive(false); + if (!canStartGame && __instance.StartButton) __instance.StartButton.gameObject.SetActive(false); } else { @@ -406,12 +406,15 @@ public static class GameStartManagerPatch } } - if (warningMessage == "") - WarningText.gameObject.SetActive(false); - else + if (WarningText) { - WarningText.text = warningMessage; - WarningText.gameObject.SetActive(true); + if (warningMessage == "") + WarningText.gameObject.SetActive(false); + else + { + WarningText.text = warningMessage; + WarningText.gameObject.SetActive(true); + } } __instance.RulesPresetText.text = GetString($"Preset_{OptionItem.CurrentPreset + 1}"); @@ -443,21 +446,24 @@ public static class GameStartManagerPatch TextMeshPro tmp = GameStartManagerStartPatch.GameCountdown; - if (tmp.text == string.Empty) + if (tmp) { - tmp.name = "LobbyInfoText"; - tmp.fontSize = tmp.fontSizeMin = tmp.fontSizeMax = 3f; - tmp.autoSizeTextContainer = true; - tmp.alignment = TextAlignmentOptions.Center; - tmp.color = Color.cyan; - tmp.outlineColor = Color.black; - tmp.outlineWidth = LangHasSensitiveOutlineText() ? 0.1f : 0.4f; - tmp.transform.localPosition += new Vector3(-0.625f, -0.12f, 0f); - tmp.transform.localScale = new(0.6f, 0.6f, 1f); - } + if (tmp.text == string.Empty) + { + tmp.name = "LobbyInfoText"; + tmp.fontSize = tmp.fontSizeMin = tmp.fontSizeMax = 3f; + tmp.autoSizeTextContainer = true; + tmp.alignment = TextAlignmentOptions.Center; + tmp.color = Color.cyan; + tmp.outlineColor = Color.black; + tmp.outlineWidth = LangHasSensitiveOutlineText() ? 0.1f : 0.4f; + tmp.transform.localPosition += new Vector3(-0.625f, -0.12f, 0f); + tmp.transform.localScale = new(0.6f, 0.6f, 1f); + } - tmp.text = suffix; - tmp.gameObject.SetActive(true); + tmp.text = suffix; + tmp.gameObject.SetActive(true); + } // Lobby timer if (GameData.Instance && HudManager.InstanceExists && AmongUsClient.Instance.NetworkMode != NetworkModes.LocalGame && GameStates.CurrentServerType == GameStates.ServerType.Vanilla) @@ -472,7 +478,7 @@ public static class GameStartManagerPatch { Warned = true; LobbyTimerExtensionUI lobbyTimerExtensionUI = HudManager.Instance.LobbyTimerExtensionUI; - lobbyTimerExtensionUI.timerText.transform.parent.transform.Find("Icon").gameObject.SetActive(true); + lobbyTimerExtensionUI.timerText.transform.parent.transform.Find("Icon")?.gameObject.SetActive(true); SoundManager.Instance.PlaySound(lobbyTimerExtensionUI.lobbyTimerPopUpSound, false); Utils.FlashColor(new(1f, 1f, 0f, 0.4f), 1.4f); } diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index cb5c0378d..363487e50 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -588,7 +588,7 @@ internal static class CheckMurderPatch target.RpcSetCustomRole(CustomRoles.Madmate); ExtendedPlayerControl.RpcSetCustomRole(target.PlayerId, CustomRoles.Madmate); var sender = CustomRpcSender.Create("RpcCheckAndMurder - Madmate", SendOption.Reliable); - sender.Notify(target, CustomRoles.Madmate.ColoredTextByRole(GetString("BecomeMadmateCuzMadmateMode"))); + CustomRpcSenderExtensions.Notify(ref sender, target, CustomRoles.Madmate.ColoredTextByRole(GetString("BecomeMadmateCuzMadmateMode"))); sender.RpcGuardAndKill(target, killer); sender.RpcGuardAndKill(target, target); sender.RpcGuardAndKill(killer, target); @@ -666,11 +666,7 @@ internal static class MurderPlayerPatch if (!Options.UsePets.GetBool() || Options.UsePhantomBasis.GetBool()) killer.RpcResetAbilityCooldown(); else - { - int cd = Options.DefaultShapeshiftCooldown.GetInt(); - Main.AbilityCD[killer.PlayerId] = (TimeStamp, cd); - SendRPC(CustomRPC.SyncAbilityCD, 1, killer.PlayerId, cd); - } + killer.AddAbilityCD(Options.DefaultShapeshiftCooldown.GetInt()); } } catch (Exception e) { ThrowException(e); } diff --git a/Roles/Standard/Crewmate/Support/Deputy.cs b/Roles/Standard/Crewmate/Support/Deputy.cs index aec4b3eb7..dc18e06ab 100644 --- a/Roles/Standard/Crewmate/Support/Deputy.cs +++ b/Roles/Standard/Crewmate/Support/Deputy.cs @@ -78,7 +78,7 @@ public class Deputy : RoleBase { if (killer.GetAbilityUseLimit() < 1) return false; - if (target != null && !target.Is(CustomRoles.Deputy)) + if (!target.Is(CustomRoles.Deputy)) { killer.RpcRemoveAbilityUse(); @@ -91,7 +91,7 @@ public class Deputy : RoleBase var sender = CustomRpcSender.Create("Deputy.OnCheckMurder", SendOption.Reliable); var hasValue = false; hasValue |= sender.SetKillCooldown(target, DeputyHandcuffCDForTarget.GetFloat()); - hasValue |= sender.Notify(target, CustomRoles.Deputy.ColoredTextByRole(GetString("HandcuffedByDeputy"))); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, CustomRoles.Deputy.ColoredTextByRole(GetString("HandcuffedByDeputy"))); if (target.IsModdedClient()) hasValue |= sender.RpcResetAbilityCooldown(target); else hasValue |= sender.RpcGuardAndKill(target, target); diff --git a/Roles/Standard/Crewmate/Support/Monarch.cs b/Roles/Standard/Crewmate/Support/Monarch.cs index 80a123855..3340f7552 100644 --- a/Roles/Standard/Crewmate/Support/Monarch.cs +++ b/Roles/Standard/Crewmate/Support/Monarch.cs @@ -81,12 +81,12 @@ public class Monarch : RoleBase var hasValue = false; killer.ResetKillCooldown(); - hasValue |= sender.Notify(killer, CustomRoles.Monarch.ColoredTextByRole(GetString("MonarchKnightedPlayer")), setName: false); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, killer, CustomRoles.Monarch.ColoredTextByRole(GetString("MonarchKnightedPlayer")), setName: false); hasValue |= sender.SetKillCooldown(killer); hasValue |= sender.NotifyRolesSpecific(killer, target, out sender, out bool cleared); if (cleared) hasValue = false; - hasValue |= sender.Notify(target, CustomRoles.Monarch.ColoredTextByRole(GetString("KnightedByMonarch")), setName: false); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, CustomRoles.Monarch.ColoredTextByRole(GetString("KnightedByMonarch")), setName: false); hasValue |= sender.RpcGuardAndKill(target, killer); hasValue |= sender.RpcGuardAndKill(target, target); hasValue |= sender.NotifyRolesSpecific(target, killer, out sender, out cleared); diff --git a/Roles/Standard/Crewmate/Support/Wizard.cs b/Roles/Standard/Crewmate/Support/Wizard.cs index 001418fa5..643750da4 100644 --- a/Roles/Standard/Crewmate/Support/Wizard.cs +++ b/Roles/Standard/Crewmate/Support/Wizard.cs @@ -219,7 +219,7 @@ public class Wizard : RoleBase hasValue |= sender.SetKillCooldown(killer); killer.RpcRemoveAbilityUse(); - hasValue |= sender.Notify(killer, string.Format(Translator.GetString("Wizard.BuffGivenNotify"), target.PlayerId.ColoredPlayerName(), Translator.GetString($"Wizard.Buff.{SelectedBuff}"), Math.Round(value, 1))); + 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); hasValue |= sender.NotifyRolesSpecific(killer, target, out sender, out bool cleared); if (cleared) hasValue = false; diff --git a/Roles/Standard/Impostor/Support/Gangster.cs b/Roles/Standard/Impostor/Support/Gangster.cs index 40a1b0cf0..53f21ccce 100644 --- a/Roles/Standard/Impostor/Support/Gangster.cs +++ b/Roles/Standard/Impostor/Support/Gangster.cs @@ -88,13 +88,13 @@ public class Gangster : RoleBase var hasValue = false; killer.ResetKillCooldown(); - hasValue |= sender.Notify(killer, Utils.ColorString(Utils.GetRoleColor(convertedAddon), GetString("GangsterSuccessfullyRecruited")), setName: false); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, killer, Utils.ColorString(Utils.GetRoleColor(convertedAddon), GetString("GangsterSuccessfullyRecruited")), setName: false); hasValue |= sender.SyncSettings(killer); hasValue |= sender.SetKillCooldown(killer); hasValue |= sender.NotifyRolesSpecific(killer, target, out sender, out bool cleared); if (cleared) hasValue = false; - hasValue |= sender.Notify(target, Utils.ColorString(Utils.GetRoleColor(convertedAddon), GetString("BeRecruitedByGangster")), setName: false); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, Utils.ColorString(Utils.GetRoleColor(convertedAddon), GetString("BeRecruitedByGangster")), setName: false); hasValue |= sender.RpcGuardAndKill(target, killer); hasValue |= sender.RpcGuardAndKill(target, target); hasValue |= sender.NotifyRolesSpecific(target, killer, out sender, out cleared); diff --git a/Roles/Standard/Neutral/Benign/Amnesiac.cs b/Roles/Standard/Neutral/Benign/Amnesiac.cs index dfdbdff91..8d03c432d 100644 --- a/Roles/Standard/Neutral/Benign/Amnesiac.cs +++ b/Roles/Standard/Neutral/Benign/Amnesiac.cs @@ -233,8 +233,8 @@ public class Amnesiac : RoleBase if (RememberAddons.GetBool()) target.GetCustomSubRoles().ForEach(x => amnesiac.RpcSetCustomRole(x)); - hasValue |= sender.Notify(amnesiac, amneNotifyString); - hasValue |= sender.Notify(target, CustomRoles.Amnesiac.ColoredTextByRole(GetString("AmnesiacRemembered"))); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, amnesiac, amneNotifyString); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, CustomRoles.Amnesiac.ColoredTextByRole(GetString("AmnesiacRemembered"))); hasValue |= sender.RpcGuardAndKill(target, amnesiac); hasValue |= sender.RpcGuardAndKill(target, target); diff --git a/Roles/Standard/Neutral/Benign/SchrodingersCat.cs b/Roles/Standard/Neutral/Benign/SchrodingersCat.cs index ba0be2a09..30f93923f 100644 --- a/Roles/Standard/Neutral/Benign/SchrodingersCat.cs +++ b/Roles/Standard/Neutral/Benign/SchrodingersCat.cs @@ -48,8 +48,8 @@ internal class SchrodingersCat : RoleBase hasValue |= sender.SetKillCooldown(killer, 5f); - hasValue |= sender.Notify(killer, string.Format(Translator.GetString("SchrodingersCat.Notify.KillerRecruited"), target.GetRealName(), CustomRoles.SchrodingersCat.ToColoredString()), 10f, setName: false); - hasValue |= sender.Notify(target, string.Format(Translator.GetString("SchrodingersCat.Notify.RecruitedByKiller"), killer.GetRealName(), killerRole.ToColoredString()), setName: false); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, killer, string.Format(Translator.GetString("SchrodingersCat.Notify.KillerRecruited"), target.GetRealName(), CustomRoles.SchrodingersCat.ToColoredString()), 10f, setName: false); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, string.Format(Translator.GetString("SchrodingersCat.Notify.RecruitedByKiller"), killer.GetRealName(), killerRole.ToColoredString()), setName: false); sender.SendMessage(!hasValue); diff --git a/Roles/Standard/Neutral/Killing/Agitator.cs b/Roles/Standard/Neutral/Killing/Agitator.cs index cb239d577..027915cd0 100644 --- a/Roles/Standard/Neutral/Killing/Agitator.cs +++ b/Roles/Standard/Neutral/Killing/Agitator.cs @@ -120,10 +120,10 @@ public class Agitator : RoleBase var sender = CustomRpcSender.Create("Agitator.OnCheckMurder", SendOption.Reliable); var hasValue = false; hasValue |= sender.RpcGuardAndKill(killer, killer); - hasValue |= sender.Notify(killer, GetString("AgitatorPassNotify")); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, killer, GetString("AgitatorPassNotify")); killer.ResetKillCooldown(); hasValue |= sender.SetKillCooldown(killer); - hasValue |= sender.Notify(target, GetString("AgitatorTargetNotify")); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, GetString("AgitatorTargetNotify")); AgitatorHasBombed = true; sender.SendMessage(!hasValue); diff --git a/Roles/Standard/Neutral/Killing/Cultist.cs b/Roles/Standard/Neutral/Killing/Cultist.cs index b1fd73c57..644581272 100644 --- a/Roles/Standard/Neutral/Killing/Cultist.cs +++ b/Roles/Standard/Neutral/Killing/Cultist.cs @@ -111,12 +111,12 @@ public class Cultist : RoleBase var sender = CustomRpcSender.Create("Cultist.OnCheckMurder", SendOption.Reliable); var hasValue = false; - hasValue |= sender.Notify(killer, CustomRoles.Cultist.ColoredTextByRole(GetString("CultistCharmedPlayer"))); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, killer, CustomRoles.Cultist.ColoredTextByRole(GetString("CultistCharmedPlayer"))); hasValue |= sender.SetKillCooldown(killer); hasValue |= sender.NotifyRolesSpecific(killer, target, out sender, out bool cleared); if (cleared) hasValue = false; - hasValue |= sender.Notify(target, CustomRoles.Cultist.ColoredTextByRole(GetString("CharmedByCultist"))); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, CustomRoles.Cultist.ColoredTextByRole(GetString("CharmedByCultist"))); hasValue |= sender.RpcGuardAndKill(target, killer); hasValue |= sender.RpcGuardAndKill(target, target); hasValue |= sender.NotifyRolesSpecific(target, killer, out sender, out cleared); diff --git a/Roles/Standard/Neutral/Killing/Jackal.cs b/Roles/Standard/Neutral/Killing/Jackal.cs index 006fb2793..56f552253 100644 --- a/Roles/Standard/Neutral/Killing/Jackal.cs +++ b/Roles/Standard/Neutral/Killing/Jackal.cs @@ -171,12 +171,12 @@ public class Jackal : RoleBase Main.ResetCamPlayerList.Add(target.PlayerId); - hasValue |= sender.Notify(killer, CustomRoles.Jackal.ColoredTextByRole(GetString("GangsterSuccessfullyRecruited")), setName: false); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, killer, CustomRoles.Jackal.ColoredTextByRole(GetString("GangsterSuccessfullyRecruited")), setName: false); hasValue |= sender.SetKillCooldown(killer, 3f); hasValue |= sender.NotifyRolesSpecific(killer, target, out sender, out bool cleared); if (cleared) hasValue = false; - hasValue |= sender.Notify(target, CustomRoles.Jackal.ColoredTextByRole(GetString("BeRecruitedByJackal")), setName: false); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, CustomRoles.Jackal.ColoredTextByRole(GetString("BeRecruitedByJackal")), setName: false); hasValue |= sender.RpcGuardAndKill(target, killer); hasValue |= sender.RpcGuardAndKill(target, target); hasValue |= sender.NotifyRolesSpecific(target, killer, out sender, out cleared); diff --git a/Roles/Standard/Neutral/Killing/Necromancer.cs b/Roles/Standard/Neutral/Killing/Necromancer.cs index 5f2c684de..23cea22a0 100644 --- a/Roles/Standard/Neutral/Killing/Necromancer.cs +++ b/Roles/Standard/Neutral/Killing/Necromancer.cs @@ -221,12 +221,12 @@ internal class Deathknight : RoleBase var sender = CustomRpcSender.Create("Deathknight.OnCheckMurder", SendOption.Reliable); var hasValue = false; - hasValue |= sender.Notify(killer, CustomRoles.Necromancer.ColoredTextByRole(GetString("DeathknightRecruitedPlayer"))); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, killer, CustomRoles.Necromancer.ColoredTextByRole(GetString("DeathknightRecruitedPlayer"))); hasValue |= sender.SetKillCooldown(killer); hasValue |= sender.NotifyRolesSpecific(killer, target, out sender, out bool cleared); if (cleared) hasValue = false; - hasValue |= sender.Notify(target, CustomRoles.Necromancer.ColoredTextByRole(GetString("RecruitedByDeathknight"))); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, CustomRoles.Necromancer.ColoredTextByRole(GetString("RecruitedByDeathknight"))); hasValue |= sender.RpcGuardAndKill(target, killer); hasValue |= sender.RpcGuardAndKill(target, target); hasValue |= sender.NotifyRolesSpecific(target, killer, out sender, out cleared); diff --git a/Roles/Standard/Neutral/Killing/Quarry.cs b/Roles/Standard/Neutral/Killing/Quarry.cs index b42120e31..e10739be6 100644 --- a/Roles/Standard/Neutral/Killing/Quarry.cs +++ b/Roles/Standard/Neutral/Killing/Quarry.cs @@ -139,7 +139,7 @@ public class Quarry : RoleBase var sender = CustomRpcSender.Create("QuarryTarget", SendOption.Reliable); var hasValue = false; - hasValue |= sender.Notify(target, string.Format(Translator.GetString("Quarry.TargetSeekBeginNotify"), CustomRoles.Quarry.ToColoredString())); + hasValue |= CustomRpcSenderExtensions.Notify(ref sender, target, string.Format(Translator.GetString("Quarry.TargetSeekBeginNotify"), CustomRoles.Quarry.ToColoredString())); hasValue |= sender.RpcSetRole(target, RoleTypes.Impostor, target.OwnerId); hasValue |= sender.RpcSetRole(shapeshifter, RoleTypes.Crewmate, target.OwnerId); hasValue |= sender.SetKillCooldown(target, 0.01f);