From 01eeb57f2000c82a85ce9ba1f6acd8081fc8286d Mon Sep 17 00:00:00 2001 From: Gurge44 Date: Tue, 18 Mar 2025 13:28:23 +0100 Subject: [PATCH] Spawn protection in KOTZ & more teams --- GameMode/KingOfTheZones.cs | 48 ++- Modules/Camouflague.cs | 36 +- Modules/CustomRpcSender.cs | 5 + Modules/ExtendedPlayerControl.cs | 32 +- Modules/PetsHelper.cs | 6 +- Modules/Utils.cs | 623 ++++++++++++++++--------------- Patches/ChatControlPatch.cs | 4 +- Patches/LadderPatch.cs | 3 +- Patches/PlayerControlPatch.cs | 45 +-- Resources/Lang/en_US.json | 12 +- Roles/AddOns/Common/Disco.cs | 5 +- Roles/Impostor/Devourer.cs | 3 +- Roles/Impostor/Penguin.cs | 2 +- Roles/Neutral/Doppelganger.cs | 2 +- Roles/Neutral/Magistrate.cs | 2 +- 15 files changed, 449 insertions(+), 379 deletions(-) diff --git a/GameMode/KingOfTheZones.cs b/GameMode/KingOfTheZones.cs index b1ee66b6a..e067d5fe9 100644 --- a/GameMode/KingOfTheZones.cs +++ b/GameMode/KingOfTheZones.cs @@ -26,6 +26,7 @@ public static class KingOfTheZones private static OptionItem PointsToWin; private static OptionItem GameEndsByTimeLimit; private static OptionItem MaxGameLength; + private static OptionItem SpawnProtectionTime; public static (Color Color, string Team) WinnerData = (Color.white, "No one wins"); @@ -71,6 +72,7 @@ public static class KingOfTheZones private static Dictionary DefaultOutfits = []; private static Dictionary PlayerTeams = []; private static Dictionary RespawnTimes = []; + private static Dictionary SpawnProtectionTimes = []; private static Dictionary Points = []; private static Dictionary ZoneDomination = []; private static Dictionary ZoneMoveSchedules = []; @@ -97,7 +99,7 @@ public static class KingOfTheZones var color = ColorUtility.TryParseHtmlString("#ff0000", out Color c) ? c : Color.red; const CustomGameMode gameMode = CustomGameMode.KingOfTheZones; - NumTeams = new IntegerOptionItem(id++, "KingOfTheZones.NumTeams", new(2, 4, 1), 2, TabGroup.GameSettings) + NumTeams = new IntegerOptionItem(id++, "KingOfTheZones.NumTeams", new(2, 7, 1), 2, TabGroup.GameSettings) .SetHeader(true) .SetGameMode(gameMode) .SetColor(color); @@ -171,6 +173,12 @@ public static class KingOfTheZones .SetValueFormat(OptionFormat.Seconds) .SetGameMode(gameMode) .SetColor(color); + + SpawnProtectionTime = new IntegerOptionItem(id, "KingOfTheZones.SpawnProtectionTime", new(0, 30, 1), 5, TabGroup.GameSettings) + .SetHeader(true) + .SetValueFormat(OptionFormat.Seconds) + .SetGameMode(gameMode) + .SetColor(color); } static Color GetColor(this KOTZTeam team) @@ -181,6 +189,9 @@ public static class KingOfTheZones KOTZTeam.Yellow => Color.yellow, KOTZTeam.Blue => Color.cyan, KOTZTeam.Green => Color.green, + KOTZTeam.Tan => Palette.Brown, + KOTZTeam.Rose => Color.magenta, + KOTZTeam.Orange => Palette.Orange, _ => Color.white }; } @@ -191,8 +202,11 @@ public static class KingOfTheZones { KOTZTeam.Red => 0, KOTZTeam.Yellow => 5, - KOTZTeam.Blue => 1, + KOTZTeam.Blue => 10, KOTZTeam.Green => 11, + KOTZTeam.Tan => 16, + KOTZTeam.Rose => 13, + KOTZTeam.Orange => 4, _ => 7 }; } @@ -214,6 +228,7 @@ public static class KingOfTheZones ZoneMoveSchedules = []; ZoneDowntimeExpire = []; RespawnTimes = []; + SpawnProtectionTimes = []; TimeLeft = 0; DefaultOutfits = Main.AllPlayerControls.ToDictionary(x => x.PlayerId, x => x.Data.DefaultOutfit); @@ -412,6 +427,9 @@ public static class KingOfTheZones KOTZTeam.Yellow => "#ffff00", KOTZTeam.Blue => "#00ffff", KOTZTeam.Green => "#00ff00", + KOTZTeam.Tan => "#A88E8E", + KOTZTeam.Rose => "#FAB8EB", + KOTZTeam.Orange => "#ff8800", _ => "#ffffff" }; @@ -423,7 +441,7 @@ public static class KingOfTheZones public static void OnCheckMurder(PlayerControl killer, PlayerControl target) { - if (!Main.IntroDestroyed || !GameGoing || PlayerTeams[killer.PlayerId] == PlayerTeams[target.PlayerId]) return; + if (!Main.IntroDestroyed || !GameGoing || PlayerTeams[killer.PlayerId] == PlayerTeams[target.PlayerId] || SpawnProtectionTimes.ContainsKey(target.PlayerId)) return; PlayerControl[] pcs = [killer, target]; if (pcs.Any(x => RespawnTimes.ContainsKey(x.PlayerId))) return; @@ -446,6 +464,8 @@ public static class KingOfTheZones if (RespawnTimes.TryGetValue(seer.PlayerId, out var respawnTS)) sb.AppendLine(string.Format(GetString("KOTZ.Suffix.RespawnTime"), respawnTS - now)); + else if (SpawnProtectionTimes.TryGetValue(seer.PlayerId, out var protectionEndTS)) + sb.AppendLine(string.Format(GetString("KOTZ.Suffix.SpawnProtectionTime"), protectionEndTS - now)); var room = seer.GetPlainShipRoom(); var zone = room == null ? SystemTypes.Hallway : room.RoomId; @@ -526,7 +546,10 @@ public static class KingOfTheZones Red, Yellow, Blue, - Green + Green, + Tan, + Rose, + Orange } [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))] @@ -604,6 +627,19 @@ public static class KingOfTheZones toRemove.ForEach(x => ZoneDowntimeExpire.Remove(x)); } } + + if (SpawnProtectionTimes.Count > 0) + { + List toRemove = []; + + foreach ((byte id, long protectionTS) in SpawnProtectionTimes) + { + if (protectionTS > now) continue; + toRemove.Add(id); + } + + toRemove.ForEach(x => SpawnProtectionTimes.Remove(x)); + } if (RespawnTimes.Count > 0) { @@ -619,6 +655,10 @@ public static class KingOfTheZones player.SetKillCooldown(TagCooldown.GetInt()); player.MarkDirtySettings(); RPC.PlaySoundRPC(player.PlayerId, Sounds.TaskComplete); + + int spawnProtectionTime = SpawnProtectionTime.GetInt(); + if (spawnProtectionTime > 0) SpawnProtectionTimes[id] = now + spawnProtectionTime; + toRemove.Add(id); Logger.Info($"{Main.AllPlayerNames[id]} respawned", "KOTZ"); diff --git a/Modules/Camouflague.cs b/Modules/Camouflague.cs index be12c6d4d..70dbd4068 100644 --- a/Modules/Camouflague.cs +++ b/Modules/Camouflague.cs @@ -4,6 +4,7 @@ using AmongUs.Data; using EHR.Impostor; using EHR.Modules; using EHR.Neutral; +using Hazel; namespace EHR; @@ -126,6 +127,8 @@ public static class Camouflage private static IEnumerator UpdateCamouflageStatusAsync() { + var sender = CustomRpcSender.Create("Camouflage.RpcSetSkin", SendOption.Reliable); + foreach (PlayerControl pc in Main.AllPlayerControls) { if (pc.inVent || pc.walkingToVent || pc.onLadder || pc.inMovingPlat) @@ -134,26 +137,24 @@ public static class Camouflage continue; } - RpcSetSkin(pc); - - if (!IsCamouflage && !pc.IsAlive()) - PetsHelper.RpcRemovePet(pc); + RpcSetSkin(pc, sender: sender); yield return null; } + sender.SendMessage(); yield return Utils.NotifyEveryoneAsync(speed: 5); } - public static void RpcSetSkin(PlayerControl target, bool ForceRevert = false, bool RevertToDefault = false, bool GameEnd = false, bool Revive = false, bool NotCommsOrCamo = false) + public static void RpcSetSkin(PlayerControl target, bool forceRevert = false, bool revertToDefault = false, bool gameEnd = false, bool revive = false, bool notCommsOrCamo = false, CustomRpcSender sender = null) { - if (!AmongUsClient.Instance.AmHost || (!Options.CommsCamouflage.GetBool() && !Camouflager.On && !Revive && !NotCommsOrCamo) || target == null || (BlockCamouflage && !ForceRevert && !RevertToDefault && !GameEnd && !Revive && !NotCommsOrCamo)) return; + if (!AmongUsClient.Instance.AmHost || (!Options.CommsCamouflage.GetBool() && !Camouflager.On && !revive && !notCommsOrCamo) || target == null || (BlockCamouflage && !forceRevert && !revertToDefault && !gameEnd && !revive && !notCommsOrCamo)) return; Logger.Info($"New outfit for {target.GetNameWithRole()}", "Camouflage.RpcSetSkin"); byte id = target.PlayerId; - if (IsCamouflage && !target.IsAlive() && target.Data.IsDead && !Revive) + if (IsCamouflage && !target.IsAlive() && target.Data.IsDead && !revive) { Logger.Info("Player is dead, returning", "Camouflage.RpcSetSkin"); return; @@ -161,15 +162,15 @@ public static class Camouflage NetworkedPlayerInfo.PlayerOutfit newOutfit = CamouflageOutfit; - if (!IsCamouflage || ForceRevert) + if (!IsCamouflage || forceRevert) { - if (id.IsPlayerShifted() && !RevertToDefault) id = Main.ShapeshiftTarget[id]; + if (id.IsPlayerShifted() && !revertToDefault) id = Main.ShapeshiftTarget[id]; - if (!GameEnd && Doppelganger.DoppelPresentSkin.TryGetValue(id, out NetworkedPlayerInfo.PlayerOutfit value)) + if (!gameEnd && Doppelganger.DoppelPresentSkin.TryGetValue(id, out NetworkedPlayerInfo.PlayerOutfit value)) newOutfit = value; else { - if (GameEnd && Doppelganger.DoppelVictim.TryGetValue(id, out string value1)) + if (gameEnd && Doppelganger.DoppelVictim.TryGetValue(id, out string value1)) { PlayerControl dpc = Utils.GetPlayerById(id); dpc?.RpcSetName(value1); @@ -181,6 +182,9 @@ public static class Camouflage SetPetForOutfitIfNecessary(newOutfit); + if (Options.RemovePetsAtDeadPlayers.GetBool() && !target.IsAlive()) + newOutfit.PetId = string.Empty; + // if the current Outfit is the same, return if (newOutfit.Compare(target.Data.DefaultOutfit)) { @@ -190,8 +194,14 @@ public static class Camouflage Logger.Info($"Setting new outfit: {newOutfit.GetString()}", "Camouflage.RpcSetSkin"); - var sender = CustomRpcSender.Create($"Camouflage.RpcSetSkin({target.Data.PlayerName})"); + bool noSender = sender == null; + if (noSender) sender = CustomRpcSender.Create($"Camouflage.RpcSetSkin({target.Data.PlayerName})", SendOption.Reliable); + WriteToSender(sender, target, newOutfit); + if (noSender) sender.SendMessage(); + } + private static void WriteToSender(CustomRpcSender sender, PlayerControl target, NetworkedPlayerInfo.PlayerOutfit newOutfit) + { target.SetColor(newOutfit.ColorId); sender.AutoStartRpc(target.NetId, (byte)RpcCalls.SetColor) @@ -230,8 +240,6 @@ public static class Camouflage .Write(newOutfit.PetId) .Write(target.GetNextRpcSequenceId(RpcCalls.SetPetStr)) .EndRpc(); - - sender.SendMessage(); } public static void OnFixedUpdate(PlayerControl pc) diff --git a/Modules/CustomRpcSender.cs b/Modules/CustomRpcSender.cs index 3a15a3ead..bdae786a0 100644 --- a/Modules/CustomRpcSender.cs +++ b/Modules/CustomRpcSender.cs @@ -315,6 +315,11 @@ public class CustomRpcSender return Write(w => w.WriteNetObject(obj)); } + public CustomRpcSender WriteVector2(Vector2 vector2) + { + return Write(w => NetHelpers.WriteVector2(vector2, w)); + } + #endregion } diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 64cac968d..357c45fb1 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -275,6 +275,34 @@ internal static class ExtendedPlayerControl writer.Write(name); AmongUsClient.Instance.FinishRpcImmediately(writer); } + + // From TOH: https://github.com/tukasa0001/TownOfHost + public static void RpcSetName(this CustomRpcSender sender, PlayerControl player, string name, PlayerControl seer = null) + { + bool seerIsNull = seer == null; + var targetClientId = seerIsNull ? -1 : seer.GetClientId(); + + name = name.Replace("color=", string.Empty); + + switch (seerIsNull) + { + case true when Main.LastNotifyNames.Where(x => x.Key.Item1 == player.PlayerId).All(x => x.Value == name): + case false when Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] == name: + return; + case true: + Main.AllPlayerControls.Do(x => Main.LastNotifyNames[(player.PlayerId, x.PlayerId)] = name); + break; + default: + Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] = name; + break; + } + + sender.AutoStartRpc(player.NetId, (byte)RpcCalls.SetName, targetClientId) + .Write(player.Data.NetId) + .Write(name) + .Write(false) + .EndRpc(); + } // By TommyXL public static void RpcSetPetDesync(this PlayerControl player, string petId, PlayerControl seer) @@ -580,7 +608,7 @@ internal static class ExtendedPlayerControl return; } - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)RpcCalls.Exiled, SendOption.None, clientId); + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)RpcCalls.Exiled, SendOption.Reliable, clientId); AmongUsClient.Instance.FinishRpcImmediately(writer); } @@ -1002,7 +1030,7 @@ internal static class ExtendedPlayerControl else { // If target is not host - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(target.NetId, (byte)RpcCalls.ProtectPlayer, SendOption.None, target.GetClientId()); + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(target.NetId, (byte)RpcCalls.ProtectPlayer, SendOption.Reliable, target.GetClientId()); writer.WriteNetObject(target); writer.Write(0); AmongUsClient.Instance.FinishRpcImmediately(writer); diff --git a/Modules/PetsHelper.cs b/Modules/PetsHelper.cs index 6dee0e6c7..d1eb5af75 100644 --- a/Modules/PetsHelper.cs +++ b/Modules/PetsHelper.cs @@ -1,4 +1,6 @@ -namespace EHR.Modules; +using Hazel; + +namespace EHR.Modules; public static class PetsHelper { @@ -11,7 +13,7 @@ public static class PetsHelper public static void SetPet(PlayerControl pc, string petId) { - var sender = CustomRpcSender.Create("Remove Pet From Dead Player"); + var sender = CustomRpcSender.Create("Remove Pet From Dead Player", SendOption.Reliable); pc.SetPet(petId); pc.Data.DefaultOutfit.PetSequenceId += 10; diff --git a/Modules/Utils.cs b/Modules/Utils.cs index e49016a22..5e4b888c5 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -162,6 +162,9 @@ public static class Utils NetHelpers.WriteVector2(location, messageWriter); messageWriter.Write(newSid); AmongUsClient.Instance.FinishRpcImmediately(messageWriter); + + var sender = CustomRpcSender.Create($"TP {pc.GetNameWithRole()}", sendOption); + sender.WriteNetObject() if (log) Logger.Info($"{pc.GetNameWithRole().RemoveHtmlTags()} => {location}", "TP"); @@ -1965,24 +1968,6 @@ public static class Utils return GameData.Instance.AllPlayers.ToArray().FirstOrDefault(info => info.PlayerId == playerId); } - public static IEnumerator NotifyEveryoneAsync(int speed = 2, bool noCache = true) - { - if (GameStates.IsMeeting) yield break; - - var count = 0; - PlayerControl[] aapc = Main.AllAlivePlayerControls; - - foreach (PlayerControl seer in aapc) - { - foreach (PlayerControl target in aapc) - { - if (GameStates.IsMeeting) yield break; - NotifyRoles(SpecifySeer: seer, SpecifyTarget: target, NoCache: noCache); - if (count++ % speed == 0) yield return null; - } - } - } - public static void SetupLongRoleDescriptions() { try @@ -2028,309 +2013,344 @@ public static class Utils catch (Exception e) { ThrowException(e); } } + public static IEnumerator NotifyEveryoneAsync(int speed = 2, bool noCache = true) + { + if (GameStates.IsMeeting) yield break; + + var count = 0; + PlayerControl[] aapc = Main.AllAlivePlayerControls; + + foreach (PlayerControl seer in aapc) + { + CustomRpcSender sender = null; + + foreach (PlayerControl target in aapc) + { + if (GameStates.IsMeeting) yield break; + WriteSetNameRpcsToSender(ref sender, false, noCache, false, false, false, false, seer, [seer], [target]); + if (count++ % speed == 0) yield return null; + } + + sender?.SendMessage(); + } + } + [SuppressMessage("ReSharper", "InconsistentNaming")] public static void NotifyRoles(bool ForMeeting = false, PlayerControl SpecifySeer = null, PlayerControl SpecifyTarget = null, bool NoCache = false, bool ForceLoop = false, bool CamouflageIsForMeeting = false, bool GuesserIsForMeeting = false, bool MushroomMixup = false) { if (!SetUpRoleTextPatch.IsInIntro && ((SpecifySeer != null && SpecifySeer.IsModClient() && (CustomGameMode.Standard.IsActiveOrIntegrated() || SpecifySeer.IsHost())) || !AmongUsClient.Instance.AmHost || (GameStates.IsMeeting && !ForMeeting))) return; - DoNotifyRoles(ForMeeting, SpecifySeer, SpecifyTarget, NoCache, ForceLoop, CamouflageIsForMeeting, GuesserIsForMeeting, MushroomMixup); - } - - private static void DoNotifyRoles(bool forMeeting = false, PlayerControl specifySeer = null, PlayerControl specifyTarget = null, bool noCache = false, bool forceLoop = false, bool camouflageIsForMeeting = false, bool guesserIsForMeeting = false, bool mushroomMixup = false) - { + PlayerControl[] apc = Main.AllPlayerControls; - - PlayerControl[] seerList = specifySeer != null ? [specifySeer] : apc; - PlayerControl[] targetList = specifyTarget != null ? [specifyTarget] : apc; - - long now = TimeStamp; + PlayerControl[] seerList = SpecifySeer != null ? [SpecifySeer] : apc; + PlayerControl[] targetList = SpecifyTarget != null ? [SpecifyTarget] : apc; // seer: Players who can see changes made here // target: Players subject to changes that seer can see foreach (PlayerControl seer in seerList) { - try + CustomRpcSender sender = null; + WriteSetNameRpcsToSender(ref sender, ForMeeting, NoCache, ForceLoop, CamouflageIsForMeeting, GuesserIsForMeeting, MushroomMixup, seer, seerList, targetList); + sender?.SendMessage(); + } + + if (!CustomGameMode.Standard.IsActiveOrIntegrated()) return; + + string seers = seerList.Length == apc.Length ? "Everyone" : string.Join(", ", seerList.Select(x => x.GetRealName())); + string targets = targetList.Length == apc.Length ? "Everyone" : string.Join(", ", targetList.Select(x => x.GetRealName())); + if (seers.Length == 0) seers = "\u2205"; + + if (targets.Length == 0) targets = "\u2205"; + + Logger.Info($" Seers: {seers} ---- Targets: {targets}", "NR"); + } + + private static void WriteSetNameRpcsToSender(ref CustomRpcSender sender, bool forMeeting, bool noCache, bool forceLoop, bool camouflageIsForMeeting, bool guesserIsForMeeting, bool mushroomMixup, PlayerControl seer, PlayerControl[] seerList, PlayerControl[] targetList) + { + long now = TimeStamp; + + try + { + if (seer == null || seer.Data.Disconnected || (seer.IsModClient() && (seer.IsHost() || CustomGameMode.Standard.IsActiveOrIntegrated()))) + return; + + sender ??= CustomRpcSender.Create("NotifyRoles", SendOption.Reliable); + sender.StartMessage(seer.GetClientId()); + + // During intro scene, set team name for non-modded clients and skip the rest. + string SelfName; + Team seerTeam = seer.GetTeam(); + CustomRoles seerRole = seer.GetCustomRole(); + + if (SetUpRoleTextPatch.IsInIntro && (seerRole.IsDesyncRole() || seer.Is(CustomRoles.Bloodlust)) && CustomGameMode.Standard.IsActiveOrIntegrated()) { - if (seer == null || seer.Data.Disconnected || (seer.IsModClient() && (seer.IsHost() || CustomGameMode.Standard.IsActiveOrIntegrated()))) continue; + const string iconTextLeft = "\u21e8"; + const string iconTextRight = "\u21e6"; + const string roleNameUp = "\n \n"; - // During intro scene, set team name for non-modded clients and skip the rest. - string SelfName; - Team seerTeam = seer.GetTeam(); - CustomRoles seerRole = seer.GetCustomRole(); + var selfTeamName = $"{iconTextLeft} {ColorString(seerTeam.GetColor(), $"{seerTeam}")} {iconTextRight}\n \n"; + SelfName = $"{selfTeamName}\r\n{seerRole.ToColoredString()}{roleNameUp}"; - if (SetUpRoleTextPatch.IsInIntro && (seerRole.IsDesyncRole() || seer.Is(CustomRoles.Bloodlust)) && CustomGameMode.Standard.IsActiveOrIntegrated()) + sender.RpcSetName(seer, SelfName, seer); + return; + } + + if (GameStates.IsLobby) + { + SelfName = seer.GetRealName(); + sender.RpcSetName(seer, SelfName); + return; + } + + if (seer.Is(CustomRoles.Car) && !forMeeting) + { + sender.RpcSetName(seer, Car.Name); + return; + } + + if (forMeeting && Magistrate.CallCourtNextMeeting) + { + SelfName = seer.Is(CustomRoles.Magistrate) ? GetString("Magistrate.CourtName") : GetString("Magistrate.JuryName"); + sender.RpcSetName(seer, SelfName); + return; + } + + var fontSize = "1.7"; + + if (forMeeting && (seer.GetClient().PlatformData.Platform == Platforms.Playstation || seer.GetClient().PlatformData.Platform == Platforms.Switch)) + fontSize = "70%"; + + // Text containing progress, such as tasks + string SelfTaskText = GameStates.IsLobby ? string.Empty : GetProgressText(seer); + + SelfMark.Clear(); + SelfSuffix.Clear(); + + if (!GameStates.IsLobby) + { + if (AntiBlackout.SkipTasks) SelfSuffix.AppendLine(GetString("AntiBlackoutSkipTasks")); + + if (!CustomGameMode.Standard.IsActiveOrIntegrated()) goto GameMode0; + + SelfMark.Append(Snitch.GetWarningArrow(seer)); + if (Main.LoversPlayers.Exists(x => x.PlayerId == seer.PlayerId)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.Lovers), " ♥")); + + if (BallLightning.IsGhost(seer)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.BallLightning), "■")); + + SelfMark.Append(Medic.GetMark(seer, seer)); + SelfMark.Append(Gaslighter.GetMark(seer, seer, forMeeting)); + SelfMark.Append(Gamer.TargetMark(seer, seer)); + SelfMark.Append(Sniper.GetShotNotify(seer.PlayerId)); + if (Silencer.ForSilencer.Contains(seer.PlayerId)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.Silencer), "╳")); + + GameMode0: + + if (Options.CurrentGameMode is not CustomGameMode.Standard and not CustomGameMode.HideAndSeek) goto GameMode; + + Main.PlayerStates.Values.Do(x => SelfSuffix.Append(x.Role.GetSuffix(seer, seer, meeting: forMeeting))); + + SelfSuffix.Append(Spurt.GetSuffix(seer)); + SelfSuffix.Append(Dynamo.GetSuffix(seer)); + + SelfSuffix.Append(CustomTeamManager.GetSuffix(seer)); + + if (!forMeeting) { - const string iconTextLeft = "\u21e8"; - const string iconTextRight = "\u21e6"; - const string roleNameUp = "\n \n"; - - var selfTeamName = $"{iconTextLeft} {ColorString(seerTeam.GetColor(), $"{seerTeam}")} {iconTextRight}\n \n"; - SelfName = $"{selfTeamName}\r\n{seerRole.ToColoredString()}{roleNameUp}"; - - seer.RpcSetNamePrivate(SelfName, seer); - continue; - } - - if (GameStates.IsLobby) - { - SelfName = seer.GetRealName(); - seer.RpcSetNameEx(SelfName); - continue; - } - - if (seer.Is(CustomRoles.Car) && !forMeeting) - { - seer.RpcSetNamePrivate(Car.Name, force: noCache); - continue; - } - - if (forMeeting && Magistrate.CallCourtNextMeeting) - { - SelfName = seer.Is(CustomRoles.Magistrate) ? GetString("Magistrate.CourtName") : GetString("Magistrate.JuryName"); - seer.RpcSetNameEx(SelfName); - continue; - } - - var fontSize = "1.7"; - - if (forMeeting && (seer.GetClient().PlatformData.Platform == Platforms.Playstation || seer.GetClient().PlatformData.Platform == Platforms.Switch)) - fontSize = "70%"; - - // Text containing progress, such as tasks - string SelfTaskText = GameStates.IsLobby ? string.Empty : GetProgressText(seer); - - SelfMark.Clear(); - SelfSuffix.Clear(); - - if (!GameStates.IsLobby) - { - if (AntiBlackout.SkipTasks) SelfSuffix.AppendLine(GetString("AntiBlackoutSkipTasks")); - - if (!CustomGameMode.Standard.IsActiveOrIntegrated()) goto GameMode0; - - SelfMark.Append(Snitch.GetWarningArrow(seer)); - if (Main.LoversPlayers.Exists(x => x.PlayerId == seer.PlayerId)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.Lovers), " ♥")); - - if (BallLightning.IsGhost(seer)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.BallLightning), "■")); - - SelfMark.Append(Medic.GetMark(seer, seer)); - SelfMark.Append(Gaslighter.GetMark(seer, seer, forMeeting)); - SelfMark.Append(Gamer.TargetMark(seer, seer)); - SelfMark.Append(Sniper.GetShotNotify(seer.PlayerId)); - if (Silencer.ForSilencer.Contains(seer.PlayerId)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.Silencer), "╳")); - - GameMode0: - - if (Options.CurrentGameMode is not CustomGameMode.Standard and not CustomGameMode.HideAndSeek) goto GameMode; - - Main.PlayerStates.Values.Do(x => SelfSuffix.Append(x.Role.GetSuffix(seer, seer, meeting: forMeeting))); - - SelfSuffix.Append(Spurt.GetSuffix(seer)); - SelfSuffix.Append(Dynamo.GetSuffix(seer)); - - SelfSuffix.Append(CustomTeamManager.GetSuffix(seer)); - - if (!forMeeting) + if (Options.UsePets.GetBool() && Main.AbilityCD.TryGetValue(seer.PlayerId, out (long StartTimeStamp, int TotalCooldown) time)) { - if (Options.UsePets.GetBool() && Main.AbilityCD.TryGetValue(seer.PlayerId, out (long StartTimeStamp, int TotalCooldown) time)) - { - long remainingCD = time.TotalCooldown - (now - time.StartTimeStamp) + 1; - SelfSuffix.Append(string.Format(GetString("CDPT"), remainingCD > 60 ? "> 60" : remainingCD)); - } - - if (seer.Is(CustomRoles.Asthmatic)) SelfSuffix.Append(Asthmatic.GetSuffixText(seer.PlayerId)); - if (seer.Is(CustomRoles.Sonar)) SelfSuffix.Append(Sonar.GetSuffix(seer, forMeeting)); - if (seer.Is(CustomRoles.Deadlined)) SelfSuffix.Append(Deadlined.GetSuffix(seer)); - if (seer.Is(CustomRoles.Introvert)) SelfSuffix.Append(Introvert.GetSelfSuffix(seer)); - if (seer.Is(CustomRoles.Allergic)) SelfSuffix.Append(Allergic.GetSelfSuffix(seer)); - - SelfSuffix.Append(Bloodmoon.GetSuffix(seer)); - SelfSuffix.Append(Haunter.GetSuffix(seer)); - - switch (seerRole) - { - case CustomRoles.SuperStar when Options.EveryOneKnowSuperStar.GetBool(): - SelfMark.Append(ColorString(GetRoleColor(CustomRoles.SuperStar), "★")); - break; - case CustomRoles.Monitor: - if (AntiAdminer.IsAdminWatch) SelfSuffix.Append($"{GetString("AntiAdminerAD")} ({AntiAdminer.PlayersNearDevices.Where(x => x.Value.Contains(AntiAdminer.Device.Admin)).Select(x => x.Key.ColoredPlayerName()).Join()})"); - if (AntiAdminer.IsVitalWatch) SelfSuffix.Append($"{GetString("AntiAdminerVI")} ({AntiAdminer.PlayersNearDevices.Where(x => x.Value.Contains(AntiAdminer.Device.Vitals)).Select(x => x.Key.ColoredPlayerName()).Join()})"); - if (AntiAdminer.IsDoorLogWatch) SelfSuffix.Append($"{GetString("AntiAdminerDL")} ({AntiAdminer.PlayersNearDevices.Where(x => x.Value.Contains(AntiAdminer.Device.DoorLog)).Select(x => x.Key.ColoredPlayerName()).Join()})"); - if (AntiAdminer.IsCameraWatch) SelfSuffix.Append($"{GetString("AntiAdminerCA")} ({AntiAdminer.PlayersNearDevices.Where(x => x.Value.Contains(AntiAdminer.Device.Camera)).Select(x => x.Key.ColoredPlayerName()).Join()})"); - break; - case CustomRoles.AntiAdminer: - if (AntiAdminer.IsAdminWatch) SelfSuffix.Append(GetString("AntiAdminerAD")); - if (AntiAdminer.IsVitalWatch) SelfSuffix.Append(GetString("AntiAdminerVI")); - if (AntiAdminer.IsDoorLogWatch) SelfSuffix.Append(GetString("AntiAdminerDL")); - if (AntiAdminer.IsCameraWatch) SelfSuffix.Append(GetString("AntiAdminerCA")); - break; - } - } - else - { - SelfMark.Append(Witch.GetSpelledMark(seer.PlayerId, forMeeting)); - if (forMeeting) SelfMark.Append(Wasp.GetStungMark(seer.PlayerId)); - if (forMeeting) SelfMark.Append(SpellCaster.IsSpelled(seer.PlayerId) ? ColorString(Team.Coven.GetColor(), "\u25c0") : string.Empty); + long remainingCD = time.TotalCooldown - (now - time.StartTimeStamp) + 1; + SelfSuffix.Append(string.Format(GetString("CDPT"), remainingCD > 60 ? "> 60" : remainingCD)); } - GameMode: + if (seer.Is(CustomRoles.Asthmatic)) SelfSuffix.Append(Asthmatic.GetSuffixText(seer.PlayerId)); + if (seer.Is(CustomRoles.Sonar)) SelfSuffix.Append(Sonar.GetSuffix(seer, false)); + if (seer.Is(CustomRoles.Deadlined)) SelfSuffix.Append(Deadlined.GetSuffix(seer)); + if (seer.Is(CustomRoles.Introvert)) SelfSuffix.Append(Introvert.GetSelfSuffix(seer)); + if (seer.Is(CustomRoles.Allergic)) SelfSuffix.Append(Allergic.GetSelfSuffix(seer)); - switch (Options.CurrentGameMode) + SelfSuffix.Append(Bloodmoon.GetSuffix(seer)); + SelfSuffix.Append(Haunter.GetSuffix(seer)); + + switch (seerRole) { - case CustomGameMode.FFA: - SelfSuffix.Append(FreeForAll.GetPlayerArrow(seer)); + case CustomRoles.SuperStar when Options.EveryOneKnowSuperStar.GetBool(): + SelfMark.Append(ColorString(GetRoleColor(CustomRoles.SuperStar), "★")); break; - case CustomGameMode.SoloKombat: - SelfSuffix.Append(SoloPVP.GetDisplayHealth(seer, true)); + case CustomRoles.Monitor: + if (AntiAdminer.IsAdminWatch) SelfSuffix.Append($"{GetString("AntiAdminerAD")} ({AntiAdminer.PlayersNearDevices.Where(x => x.Value.Contains(AntiAdminer.Device.Admin)).Select(x => x.Key.ColoredPlayerName()).Join()})"); + if (AntiAdminer.IsVitalWatch) SelfSuffix.Append($"{GetString("AntiAdminerVI")} ({AntiAdminer.PlayersNearDevices.Where(x => x.Value.Contains(AntiAdminer.Device.Vitals)).Select(x => x.Key.ColoredPlayerName()).Join()})"); + if (AntiAdminer.IsDoorLogWatch) SelfSuffix.Append($"{GetString("AntiAdminerDL")} ({AntiAdminer.PlayersNearDevices.Where(x => x.Value.Contains(AntiAdminer.Device.DoorLog)).Select(x => x.Key.ColoredPlayerName()).Join()})"); + if (AntiAdminer.IsCameraWatch) SelfSuffix.Append($"{GetString("AntiAdminerCA")} ({AntiAdminer.PlayersNearDevices.Where(x => x.Value.Contains(AntiAdminer.Device.Camera)).Select(x => x.Key.ColoredPlayerName()).Join()})"); break; - case CustomGameMode.MoveAndStop: - SelfSuffix.Append(MoveAndStop.GetSuffixText(seer)); - break; - case CustomGameMode.HotPotato: - SelfSuffix.Append(HotPotato.GetSuffixText(seer.PlayerId)); - break; - case CustomGameMode.Speedrun: - SelfSuffix.Append(Speedrun.GetSuffixText(seer)); - break; - case CustomGameMode.HideAndSeek: - SelfSuffix.Append(CustomHnS.GetSuffixText(seer, seer)); - break; - case CustomGameMode.CaptureTheFlag: - SelfSuffix.Append(CaptureTheFlag.GetSuffixText(seer, seer)); - break; - case CustomGameMode.NaturalDisasters: - SelfSuffix.Append(NaturalDisasters.SuffixText()); - break; - case CustomGameMode.RoomRush: - SelfSuffix.Append(RoomRush.GetSuffix(seer)); - break; - case CustomGameMode.KingOfTheZones: - SelfSuffix.Append(KingOfTheZones.GetSuffix(seer)); - break; - case CustomGameMode.AllInOne: - bool alive = seer.IsAlive(); - if (alive) SelfSuffix.Append(SoloPVP.GetDisplayHealth(seer, true) + "\n"); - if (alive) SelfSuffix.Append(MoveAndStop.GetSuffixText(seer) + "\n"); - SelfSuffix.Append(HotPotato.GetSuffixText(seer.PlayerId) + "\n"); - if (alive && !seer.Is(CustomRoles.Killer)) SelfSuffix.Append(string.Format(GetString("DamoclesTimeLeft"), Speedrun.Timers[seer.PlayerId]) + "\n"); - SelfSuffix.Append(NaturalDisasters.SuffixText() + "\n"); - const StringSplitOptions splitFlags = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries; - SelfSuffix.Append(RoomRush.GetSuffix(seer).Split('\n', splitFlags).Join(delimiter: " - ")); + case CustomRoles.AntiAdminer: + if (AntiAdminer.IsAdminWatch) SelfSuffix.Append(GetString("AntiAdminerAD")); + if (AntiAdminer.IsVitalWatch) SelfSuffix.Append(GetString("AntiAdminerVI")); + if (AntiAdminer.IsDoorLogWatch) SelfSuffix.Append(GetString("AntiAdminerDL")); + if (AntiAdminer.IsCameraWatch) SelfSuffix.Append(GetString("AntiAdminerCA")); break; } } - - string SeerRealName = seer.GetRealName(forMeeting); - - if (!GameStates.IsLobby) + else { - if ((CustomGameMode.FFA.IsActiveOrIntegrated() && FreeForAll.FFATeamMode.GetBool()) || CustomGameMode.HotPotato.IsActiveOrIntegrated()) - SeerRealName = SeerRealName.ApplyNameColorData(seer, seer, forMeeting); - - if (!forMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.AllInOne) - { - CustomTeamManager.CustomTeam team = CustomTeamManager.GetCustomTeam(seer.PlayerId); - - if (team != null) - { - SeerRealName = ColorString( - team.RoleRevealScreenBackgroundColor == "*" || !ColorUtility.TryParseHtmlString(team.RoleRevealScreenBackgroundColor, out Color teamColor) - ? Color.yellow - : teamColor, - string.Format( - GetString("CustomTeamHelp"), - team.RoleRevealScreenTitle == "*" - ? team.TeamName - : team.RoleRevealScreenTitle, - team.RoleRevealScreenSubtitle == "*" - ? string.Empty - : team.RoleRevealScreenSubtitle)); - } - else if (CustomGameMode.HideAndSeek.IsActiveOrIntegrated()) - { - if (GameStartTimeStamp + 40 > now) SeerRealName = CustomHnS.GetRoleInfoText(seer); - } - else if (Options.ChangeNameToRoleInfo.GetBool() && !seer.IsModClient() && Options.CurrentGameMode == CustomGameMode.Standard) - { - bool showLongInfo = LongRoleDescriptions.TryGetValue(seer.PlayerId, out (string Text, int Duration, bool Long) description) && GameStartTimeStamp + description.Duration > now; - string mHelp = (!showLongInfo || description.Long) ? "\n" + GetString("MyRoleCommandHelp") : string.Empty; - string color = seerTeam.GetTextColor(); - string teamStr = seerTeam == Team.Impostor && seer.IsMadmate() ? "Madmate" : seerTeam.ToString(); - string info = (showLongInfo ? description.Text : seer.GetRoleInfo()) + mHelp; - SeerRealName = $"{GetString($"YouAre{teamStr}")}\n{info}"; - } - } - - if (GameStartTimeStamp + 50 > TimeStamp && Main.HasPlayedGM.TryGetValue(Options.CurrentGameMode, out var playedFCs) && !playedFCs.Contains(seer.FriendCode)) - SelfSuffix.Append("\n\n" + GetString($"GameModeTutorial.{Options.CurrentGameMode}")); + SelfMark.Append(Witch.GetSpelledMark(seer.PlayerId, true)); + SelfMark.Append(Wasp.GetStungMark(seer.PlayerId)); + SelfMark.Append(SpellCaster.IsSpelled(seer.PlayerId) ? ColorString(Team.Coven.GetColor(), "\u25c0") : string.Empty); } - bool noRoleText = GameStates.IsLobby || Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones; + GameMode: - // Combine seer's job title and SelfTaskText with seer's player name and SelfMark - string SelfRoleName = noRoleText ? string.Empty : $"{seer.GetDisplayRoleName()}{SelfTaskText}"; - string SelfDeathReason = seer.KnowDeathReason(seer) && !noRoleText ? $"\n『{ColorString(GetRoleColor(CustomRoles.Doctor), GetVitalText(seer.PlayerId))}』" : string.Empty; - SelfName = $"{ColorString(noRoleText ? Color.white : seer.GetRoleColor(), SeerRealName)}{SelfDeathReason}{SelfMark}"; - - if (!CustomGameMode.Standard.IsActiveOrIntegrated() || GameStates.IsLobby) goto GameMode2; - - SelfName = seerRole switch + switch (Options.CurrentGameMode) { - CustomRoles.Arsonist when seer.IsDouseDone() => $"{ColorString(seer.GetRoleColor(), GetString("EnterVentToWin"))}", - CustomRoles.Revolutionist when seer.IsDrawDone() => $">{ColorString(seer.GetRoleColor(), string.Format(GetString("EnterVentWinCountDown"), Revolutionist.RevolutionistCountdown.GetValueOrDefault(seer.PlayerId, 10)))}", - _ => SelfName - }; + case CustomGameMode.FFA: + SelfSuffix.Append(FreeForAll.GetPlayerArrow(seer)); + break; + case CustomGameMode.SoloKombat: + SelfSuffix.Append(SoloPVP.GetDisplayHealth(seer, true)); + break; + case CustomGameMode.MoveAndStop: + SelfSuffix.Append(MoveAndStop.GetSuffixText(seer)); + break; + case CustomGameMode.HotPotato: + SelfSuffix.Append(HotPotato.GetSuffixText(seer.PlayerId)); + break; + case CustomGameMode.Speedrun: + SelfSuffix.Append(Speedrun.GetSuffixText(seer)); + break; + case CustomGameMode.HideAndSeek: + SelfSuffix.Append(CustomHnS.GetSuffixText(seer, seer)); + break; + case CustomGameMode.CaptureTheFlag: + SelfSuffix.Append(CaptureTheFlag.GetSuffixText(seer, seer)); + break; + case CustomGameMode.NaturalDisasters: + SelfSuffix.Append(NaturalDisasters.SuffixText()); + break; + case CustomGameMode.RoomRush: + SelfSuffix.Append(RoomRush.GetSuffix(seer)); + break; + case CustomGameMode.KingOfTheZones: + SelfSuffix.Append(KingOfTheZones.GetSuffix(seer)); + break; + case CustomGameMode.AllInOne: + bool alive = seer.IsAlive(); + if (alive) SelfSuffix.Append(SoloPVP.GetDisplayHealth(seer, true) + "\n"); + if (alive) SelfSuffix.Append(MoveAndStop.GetSuffixText(seer) + "\n"); + SelfSuffix.Append(HotPotato.GetSuffixText(seer.PlayerId) + "\n"); + if (alive && !seer.Is(CustomRoles.Killer)) SelfSuffix.Append(string.Format(GetString("DamoclesTimeLeft"), Speedrun.Timers[seer.PlayerId]) + "\n"); + SelfSuffix.Append(NaturalDisasters.SuffixText() + "\n"); + const StringSplitOptions splitFlags = StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries; + SelfSuffix.Append(RoomRush.GetSuffix(seer).Split('\n', splitFlags).Join(delimiter: " - ")); + break; + } + } - if (Pelican.IsEaten(seer.PlayerId)) SelfName = $"{ColorString(GetRoleColor(CustomRoles.Pelican), GetString("EatenByPelican"))}"; + string SeerRealName = seer.GetRealName(forMeeting); - if (Deathpact.IsInActiveDeathpact(seer)) SelfName = Deathpact.GetDeathpactString(seer); + if (!GameStates.IsLobby) + { + if ((CustomGameMode.FFA.IsActiveOrIntegrated() && FreeForAll.FFATeamMode.GetBool()) || CustomGameMode.HotPotato.IsActiveOrIntegrated()) + SeerRealName = SeerRealName.ApplyNameColorData(seer, seer, forMeeting); - // Devourer - if (Devourer.HideNameOfConsumedPlayer.GetBool() && Devourer.PlayerIdList.Any(x => Main.PlayerStates[x].Role is Devourer { IsEnable: true } dv && dv.PlayerSkinsCosumed.Contains(seer.PlayerId)) && !camouflageIsForMeeting) SelfName = GetString("DevouredName"); - - // Camouflage - if (Camouflage.IsCamouflage && !camouflageIsForMeeting) SelfName = $"{SelfName}"; - - GameMode2: - - if (!GameStates.IsLobby) + if (!forMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.AllInOne) { - if (NameNotifyManager.GetNameNotify(seer, out string name) && name.Length > 0) SelfName = name; + CustomTeamManager.CustomTeam team = CustomTeamManager.GetCustomTeam(seer.PlayerId); - switch (Options.CurrentGameMode) + if (team != null) { - case CustomGameMode.SoloKombat: - SoloPVP.GetNameNotify(seer, ref SelfName); - SelfName = $"{SelfTaskText}\r\n{SelfName}"; - break; - case CustomGameMode.FFA: - SelfName = $"{SelfTaskText}\r\n{SelfName}"; - break; - default: - SelfName = $"{SelfRoleName}\r\n{SelfName}"; - break; + SeerRealName = ColorString( + team.RoleRevealScreenBackgroundColor == "*" || !ColorUtility.TryParseHtmlString(team.RoleRevealScreenBackgroundColor, out Color teamColor) + ? Color.yellow + : teamColor, + string.Format( + GetString("CustomTeamHelp"), + team.RoleRevealScreenTitle == "*" + ? team.TeamName + : team.RoleRevealScreenTitle, + team.RoleRevealScreenSubtitle == "*" + ? string.Empty + : team.RoleRevealScreenSubtitle)); + } + else if (CustomGameMode.HideAndSeek.IsActiveOrIntegrated()) + { + if (GameStartTimeStamp + 40 > now) SeerRealName = CustomHnS.GetRoleInfoText(seer); + } + else if (Options.ChangeNameToRoleInfo.GetBool() && !seer.IsModClient() && Options.CurrentGameMode == CustomGameMode.Standard) + { + bool showLongInfo = LongRoleDescriptions.TryGetValue(seer.PlayerId, out (string Text, int Duration, bool Long) description) && GameStartTimeStamp + description.Duration > now; + string mHelp = (!showLongInfo || description.Long) ? "\n" + GetString("MyRoleCommandHelp") : string.Empty; + string color = seerTeam.GetTextColor(); + string teamStr = seerTeam == Team.Impostor && seer.IsMadmate() ? "Madmate" : seerTeam.ToString(); + string info = (showLongInfo ? description.Text : seer.GetRoleInfo()) + mHelp; + SeerRealName = $"{GetString($"YouAre{teamStr}")}\n{info}"; } - - SelfName += SelfSuffix.ToString() == string.Empty ? string.Empty : $"\r\n{SelfSuffix}"; - if (!forMeeting) SelfName += "\r\n"; } - seer.RpcSetNamePrivate(SelfName, force: noCache); + if (GameStartTimeStamp + 50 > TimeStamp && Main.HasPlayedGM.TryGetValue(Options.CurrentGameMode, out var playedFCs) && !playedFCs.Contains(seer.FriendCode)) + SelfSuffix.Append("\n\n" + GetString($"GameModeTutorial.{Options.CurrentGameMode}")); + } - // Run the second loop only when necessary, such as when seer is dead - if (seer.Data.IsDead || !seer.IsAlive() || noCache || camouflageIsForMeeting || mushroomMixup || IsActive(SystemTypes.MushroomMixupSabotage) || forceLoop || seerList.Length == 1 || targetList.Length == 1) + bool noRoleText = GameStates.IsLobby || Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones; + + // Combine seer's job title and SelfTaskText with seer's player name and SelfMark + string SelfRoleName = noRoleText ? string.Empty : $"{seer.GetDisplayRoleName()}{SelfTaskText}"; + string SelfDeathReason = seer.KnowDeathReason(seer) && !noRoleText ? $"\n『{ColorString(GetRoleColor(CustomRoles.Doctor), GetVitalText(seer.PlayerId))}』" : string.Empty; + SelfName = $"{ColorString(noRoleText ? Color.white : seer.GetRoleColor(), SeerRealName)}{SelfDeathReason}{SelfMark}"; + + if (!CustomGameMode.Standard.IsActiveOrIntegrated() || GameStates.IsLobby) goto GameMode2; + + SelfName = seerRole switch + { + CustomRoles.Arsonist when seer.IsDouseDone() => $"{ColorString(seer.GetRoleColor(), GetString("EnterVentToWin"))}", + CustomRoles.Revolutionist when seer.IsDrawDone() => $">{ColorString(seer.GetRoleColor(), string.Format(GetString("EnterVentWinCountDown"), Revolutionist.RevolutionistCountdown.GetValueOrDefault(seer.PlayerId, 10)))}", + _ => SelfName + }; + + if (Pelican.IsEaten(seer.PlayerId)) SelfName = $"{ColorString(GetRoleColor(CustomRoles.Pelican), GetString("EatenByPelican"))}"; + + if (Deathpact.IsInActiveDeathpact(seer)) SelfName = Deathpact.GetDeathpactString(seer); + + // Devourer + if (Devourer.HideNameOfConsumedPlayer.GetBool() && Devourer.PlayerIdList.Any(x => Main.PlayerStates[x].Role is Devourer { IsEnable: true } dv && dv.PlayerSkinsCosumed.Contains(seer.PlayerId)) && !camouflageIsForMeeting) SelfName = GetString("DevouredName"); + + // Camouflage + if (Camouflage.IsCamouflage && !camouflageIsForMeeting) SelfName = $"{SelfName}"; + + GameMode2: + + if (!GameStates.IsLobby) + { + if (NameNotifyManager.GetNameNotify(seer, out string name) && name.Length > 0) SelfName = name; + + switch (Options.CurrentGameMode) { - foreach (PlayerControl target in targetList) + case CustomGameMode.SoloKombat: + SoloPVP.GetNameNotify(seer, ref SelfName); + SelfName = $"{SelfTaskText}\r\n{SelfName}"; + break; + case CustomGameMode.FFA: + SelfName = $"{SelfTaskText}\r\n{SelfName}"; + break; + default: + SelfName = $"{SelfRoleName}\r\n{SelfName}"; + break; + } + + SelfName += SelfSuffix.ToString() == string.Empty ? string.Empty : $"\r\n{SelfSuffix}"; + if (!forMeeting) SelfName += "\r\n"; + } + + sender.RpcSetName(seer, SelfName, seer); + + // Run the second loop only when necessary, such as when seer is dead + if (seer.Data.IsDead || !seer.IsAlive() || noCache || camouflageIsForMeeting || mushroomMixup || IsActive(SystemTypes.MushroomMixupSabotage) || forceLoop || seerList.Length == 1 || targetList.Length == 1) + { + foreach (PlayerControl target in targetList) + { + try { if (target.PlayerId == seer.PlayerId) continue; - if (target.Is(CustomRoles.Car) && !forMeeting) - { - target.RpcSetNamePrivate(Car.Name, seer, noCache); - continue; - } - - if ((IsActive(SystemTypes.MushroomMixupSabotage) || mushroomMixup) && target.IsAlive() && !seer.Is(CustomRoleTypes.Impostor) && Main.ResetCamPlayerList.Contains(seer.PlayerId)) - target.RpcSetNamePrivate("", force: noCache); + if ((IsActive(SystemTypes.MushroomMixupSabotage) || mushroomMixup) && !forMeeting && target.IsAlive() && !seer.Is(CustomRoleTypes.Impostor) && seer.HasDesyncRole()) + sender.RpcSetName(target, "", seer); else { TargetMark.Clear(); @@ -2547,33 +2567,34 @@ public static class Utils var TargetName = $"{TargetRoleText}{TargetPlayerName}{TargetDeathReason}{TargetMark}"; TargetName += GameStates.IsLobby || TargetSuffix.ToString() == string.Empty ? string.Empty : $"\r\n{TargetSuffix}"; - target.RpcSetNamePrivate(TargetName, seer, noCache); + sender.RpcSetName(target, TargetName, seer); } } + catch (Exception ex) + { + if (LastNotifyRolesErrorTS != now) + { + Logger.Error($"Error - seer = {seer.GetNameWithRole()}, target = {target.GetNameWithRole()}:", "NR"); + ThrowException(ex); + LastNotifyRolesErrorTS = now; + } + else + Logger.Error($"Error - seer = {seer.GetNameWithRole()}, target = {target.GetNameWithRole()}: {ex}", "NR"); + } } } - catch (Exception ex) - { - if (LastNotifyRolesErrorTS != now) - { - Logger.Error($"Error for {seer.GetNameWithRole()}:", "NR"); - ThrowException(ex); - LastNotifyRolesErrorTS = now; - } - else - Logger.Error($"Error for {seer.GetNameWithRole()}: {ex}", "NR"); - } } - - if (!CustomGameMode.Standard.IsActiveOrIntegrated()) return; - - string seers = seerList.Length == apc.Length ? "Everyone" : string.Join(", ", seerList.Select(x => x.GetRealName())); - string targets = targetList.Length == apc.Length ? "Everyone" : string.Join(", ", targetList.Select(x => x.GetRealName())); - if (seers.Length == 0) seers = "\u2205"; - - if (targets.Length == 0) targets = "\u2205"; - - Logger.Info($" Seers: {seers} ---- Targets: {targets}", "NR"); + catch (Exception ex) + { + if (LastNotifyRolesErrorTS != now) + { + Logger.Error($"Error for {seer.GetNameWithRole()}:", "NR"); + ThrowException(ex); + LastNotifyRolesErrorTS = now; + } + else + Logger.Error($"Error for {seer.GetNameWithRole()}: {ex}", "NR"); + } } public static void MarkEveryoneDirtySettings() @@ -2610,7 +2631,7 @@ public static class Utils if (newOutfit.Compare(pc.Data.DefaultOutfit)) return; - var sender = CustomRpcSender.Create($"Utils.RpcChangeSkin({pc.Data.PlayerName})"); + var sender = CustomRpcSender.Create($"Utils.RpcChangeSkin({pc.Data.PlayerName})", SendOption.Reliable); pc.SetName(newOutfit.PlayerName); diff --git a/Patches/ChatControlPatch.cs b/Patches/ChatControlPatch.cs index 4e57b6356..e582141d9 100644 --- a/Patches/ChatControlPatch.cs +++ b/Patches/ChatControlPatch.cs @@ -226,7 +226,7 @@ public static class ChatManager string[] filtered = ChatHistory.Where(a => Utils.GetPlayerById(Convert.ToByte(a.Split(':')[0].Trim())).IsAlive()).ToArray(); - for (int i = clear ? 0 : filtered.Length; i < 20; i++) + for (int i = clear ? 0 : filtered.Length; i < 30; i++) { PlayerControl player = x[r.Next(0, totalAlive)]; FastDestroyableSingleton.Instance.Chat.AddChat(player, Utils.EmptyMessage); @@ -280,7 +280,7 @@ public static class ChatManager var clientId = target.GetClientId(); if (clientId == -1) return; - Loop.Times(20, _ => SendRPC(sender, Utils.EmptyMessage, clientId)); + Loop.Times(30, _ => SendRPC(sender, Utils.EmptyMessage, clientId)); return; } diff --git a/Patches/LadderPatch.cs b/Patches/LadderPatch.cs index bcbda3403..f4452554d 100644 --- a/Patches/LadderPatch.cs +++ b/Patches/LadderPatch.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using HarmonyLib; +using Hazel; using UnityEngine; namespace EHR; @@ -44,7 +45,7 @@ public class FallFromLadder Vector2 targetPos = (Vector2)TargetLadderData[player.PlayerId] + new Vector2(0.1f, 0f); var num = (ushort)(NetHelpers.XRange.ReverseLerp(targetPos.x) * 65535f); var num2 = (ushort)(NetHelpers.YRange.ReverseLerp(targetPos.y) * 65535f); - var sender = CustomRpcSender.Create("LadderFallRpc"); + var sender = CustomRpcSender.Create("LadderFallRpc", SendOption.Reliable); sender.AutoStartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo) .Write(num) diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index ebf71ac2d..3f5653aed 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -1174,7 +1174,7 @@ internal static class ReportDeadBodyPatch foreach (PlayerControl pc in Main.AllPlayerControls) { if (Main.CheckShapeshift.ContainsKey(pc.PlayerId) && !Doppelganger.DoppelVictim.ContainsKey(pc.PlayerId)) - Camouflage.RpcSetSkin(pc, RevertToDefault: true); + Camouflage.RpcSetSkin(pc, revertToDefault: true); if (Main.CurrentMap == MapNames.Fungle && (pc.IsMushroomMixupActive() || IsActive(SystemTypes.MushroomMixupSabotage))) pc.FixMixedUpOutfit(); @@ -1197,7 +1197,7 @@ internal static class ReportDeadBodyPatch pc.RpcShapeshift(pc, false); if (Camouflage.IsCamouflage && !Magistrate.CallCourtNextMeeting) - Camouflage.RpcSetSkin(pc, RevertToDefault: true, ForceRevert: true); + Camouflage.RpcSetSkin(pc, revertToDefault: true, forceRevert: true); if (Magistrate.CallCourtNextMeeting) { @@ -2357,45 +2357,4 @@ static class BootFromVentPatch { return !GameStates.IsInTask || ExileController.Instance || __instance == null || __instance.myPlayer == null || !__instance.myPlayer.IsAlive() || !__instance.myPlayer.Is(CustomRoles.Nimble); } -} - -// From https://github.com/Rabek009/MoreGamemodes - by Rabek009 -[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RpcSetColor))] -static class RpcSetColorPatch -{ - public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] byte bodyColor) - { - if (DateTime.UtcNow.Month < 4) return true; - if (!AmongUsClient.Instance.AmHost) return true; - - if (AmongUsClient.Instance.AmClient) - __instance.SetColor(bodyColor); - - MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(__instance.NetId, (byte)RpcCalls.SetColor, SendOption.None); - messageWriter.Write(__instance.Data.NetId); - messageWriter.Write(bodyColor); - AmongUsClient.Instance.FinishRpcImmediately(messageWriter); - return false; - } -} - -// From https://github.com/Rabek009/MoreGamemodes - by Rabek009 -[HarmonyPatch(typeof(CustomNetworkTransform), nameof(CustomNetworkTransform.RpcSnapTo))] -static class RpcSnapToPatch -{ - public static bool Prefix(CustomNetworkTransform __instance, [HarmonyArgument(0)] Vector2 position) - { - if (DateTime.UtcNow.Month < 4) return true; - if (!AmongUsClient.Instance.AmHost) return true; - - if (AmongUsClient.Instance.AmClient) - __instance.SnapTo(position, (ushort)(__instance.lastSequenceId + 1)); - - ushort num = (ushort)(__instance.lastSequenceId + 2); - MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(__instance.NetId, (byte)RpcCalls.SnapTo, SendOption.None); - NetHelpers.WriteVector2(position, messageWriter); - messageWriter.Write(num); - AmongUsClient.Instance.FinishRpcImmediately(messageWriter); - return false; - } } \ No newline at end of file diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 82e24c4e0..9f89563b0 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -33,10 +33,10 @@ "ShortTeamName.Crewmate": "Crew", "ShortTeamName.Neutral": "Neut", "ShortTeamName.Coven": "Coven", - "YouAreCrewmate": "You are a Crewmate", - "YouAreImpostor": "You are an Impostor", - "YouAreNeutral": "You are a Neutral", - "YouAreCoven": "You are in the Coven", + "YouAreCrewmate": "TEAM: Crewmates", + "YouAreImpostor": "TEAM: Impostors", + "YouAreNeutral": "ALIGNMENT: Neutral", + "YouAreCoven": "TEAM: Coven", "YouAreMadmate": "You are in the Impostor team", "CrewmateSub": "Find and exile the Impostors", "ImpostorSub": "Sabotage and kill everyone", @@ -4849,6 +4849,9 @@ "KOTZ.Notify.AssignedToTeam.Blue": "<#ffffff>You are in the Blue Team", "KOTZ.Notify.AssignedToTeam.Green": "<#ffffff>You are in the Green Team", "KOTZ.Notify.AssignedToTeam.Yellow": "<#ffffff>You are in the Yellow Team", + "KOTZ.Notify.AssignedToTeam.Tan": "<#ffffff>You are in the Tan Team", + "KOTZ.Notify.AssignedToTeam.Rose": "<#ffffff>You are in the Rose Team", + "KOTZ.Notify.AssignedToTeam.Orange": "<#ffffff>You are in the Orange Team", "KOTZ.Notify.ShortIntro": "Welcome to another game of <#ff0000>King Of The Zones!\\nWe're playing in <#ffff44>{0} teams and with <#ffff44>{1} zones.\\nTag cooldown is <#ffff44>{2} seconds, respawn time is <#ffff44>{3} seconds.\\nGood luck and have fun!", "KOTZ.Notify.Tutorial.Basics": "The current game mode is <#ff0000>King Of The Zones.\\nPlayers are split into <#ffff44>{0} teams.\\nSome rooms on the map represent a zone. There are <#ffff44>{1} zones.\\nIf a team has more players in a room than\\nall other teams, they control the zone.\\nEvery second, each team gets 1 point for each zone controlled.", "KOTZ.Notify.Tutorial.GameEnd.PointsOnly": "When a team reaches <#ffff44>{0} points, the game ends and that team wins.\\nIf 2 or more teams reach the goal at the same time,\\nthe game waits for the tie to be broken.", @@ -4863,6 +4866,7 @@ "KOTZ.SuffixHelp.Points": "Points: ", "KOTZ.SuffixHelp.GameEndTimer": "Game End Timer: ", "KOTZ.Suffix.RespawnTime": "<#ff0000>DEAD <#888888>- RESPAWNING IN <#ffff44>{0}", + "KOTZ.Suffix.SpawnProtectionTime": "<#00ffa5>Under Spawn Protection <#888888>- <#ffff44>{0}", "KOTZ.Suffix.PointsToWin": "First to <#ffff44>{0} points wins", "KOTZ.Suffix.Tie": "Tie: {0}\\nWaiting for the tie to break....", "KOTZ.EndScreen.Winner.Red": "The Red Team wins!", diff --git a/Roles/AddOns/Common/Disco.cs b/Roles/AddOns/Common/Disco.cs index d4f253643..ff1f88983 100644 --- a/Roles/AddOns/Common/Disco.cs +++ b/Roles/AddOns/Common/Disco.cs @@ -23,8 +23,9 @@ internal class Disco : IAddon pc.SetColor(colorId); - try { pc.RpcSetColor((byte)colorId); } - catch + if (GameStates.CurrentServerType is GameStates.ServerType.ModdedWithCNOSupport or GameStates.ServerType.ModdedWithoutCNOSupport) + pc.RpcSetColor((byte)colorId); + else { var sender = CustomRpcSender.Create($"Disco.ChangeColor({pc.Data.PlayerName})"); diff --git a/Roles/Impostor/Devourer.cs b/Roles/Impostor/Devourer.cs index 4f58bca40..4f30dfe37 100644 --- a/Roles/Impostor/Devourer.cs +++ b/Roles/Impostor/Devourer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using AmongUs.GameOptions; using EHR.Neutral; +using Hazel; using static EHR.Options; using static EHR.Translator; @@ -128,7 +129,7 @@ public class Devourer : RoleBase private static void SetSkin(PlayerControl target, NetworkedPlayerInfo.PlayerOutfit outfit) { - var sender = CustomRpcSender.Create($"Camouflage.RpcSetSkin({target.Data.PlayerName})"); + var sender = CustomRpcSender.Create($"Devourer.RpcSetSkin({target.Data.PlayerName})", SendOption.Reliable); target.SetColor(outfit.ColorId); diff --git a/Roles/Impostor/Penguin.cs b/Roles/Impostor/Penguin.cs index d7f9cc7f4..94ae97e2f 100644 --- a/Roles/Impostor/Penguin.cs +++ b/Roles/Impostor/Penguin.cs @@ -368,7 +368,7 @@ public class Penguin : RoleBase abductVictim.NetTransform.SnapTo(Penguin_.transform.position, (ushort)sId); Penguin_.Kill(abductVictim); - var sender = CustomRpcSender.Create("PenguinMurder"); + var sender = CustomRpcSender.Create("PenguinMurder", SendOption.Reliable); { sender.AutoStartRpc(abductVictim.NetTransform.NetId, (byte)RpcCalls.SnapTo); diff --git a/Roles/Neutral/Doppelganger.cs b/Roles/Neutral/Doppelganger.cs index d0940d619..005dc02f2 100644 --- a/Roles/Neutral/Doppelganger.cs +++ b/Roles/Neutral/Doppelganger.cs @@ -146,7 +146,7 @@ public class Doppelganger : RoleBase private static void RpcChangeSkin(PlayerControl pc, NetworkedPlayerInfo.PlayerOutfit newOutfit) { - var sender = CustomRpcSender.Create($"Doppelganger.RpcChangeSkin({pc.Data.PlayerName})"); + var sender = CustomRpcSender.Create($"Doppelganger.RpcChangeSkin({pc.Data.PlayerName})", SendOption.Reliable); pc.SetName(newOutfit.PlayerName); diff --git a/Roles/Neutral/Magistrate.cs b/Roles/Neutral/Magistrate.cs index ab7954433..18908ea26 100644 --- a/Roles/Neutral/Magistrate.cs +++ b/Roles/Neutral/Magistrate.cs @@ -37,7 +37,7 @@ public class Magistrate : RoleBase public override void AfterMeetingTasks() { CallCourtNextMeeting = false; - Main.AllPlayerControls.Do(x => Camouflage.RpcSetSkin(x, NotCommsOrCamo: true)); + Main.AllPlayerControls.Do(x => Camouflage.RpcSetSkin(x, notCommsOrCamo: true)); } public override void ApplyGameOptions(IGameOptions opt, byte playerId)