diff --git a/EHR.sln.DotSettings b/EHR.sln.DotSettings index 6363112ab..fce429932 100644 --- a/EHR.sln.DotSettings +++ b/EHR.sln.DotSettings @@ -54,6 +54,9 @@ UI VN <Policy><Descriptor Staticness="Any" AccessRightKinds="Private, Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="non_field_members_should_be_pascal_case"><ElementKinds><Kind Name="PROPERTY" /><Kind Name="EVENT" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="Get" Suffix="" Style="AaBb" /></Policy> + + True + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Parameters"><ElementKinds><Kind Name="PARAMETER" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="__" Suffix="" Style="aaBb" /></Policy></Policy> True True True diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 4910defd2..44d58ddd0 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -812,7 +812,7 @@ internal static class CustomRolesHelper public static bool OnlySpawnsWithPets(this CustomRoles role) { - return !(Options.UseUnshiftTrigger.GetBool() && (!role.IsNeutral() || Options.UseUnshiftTriggerForNKs.GetBool()) && role.SimpleAbilityTrigger() && role != CustomRoles.Chemist && !role.AlwaysUsesUnshift()) && OnlySpawnsWithPetsRoleList.Contains(role); + return !(((Options.UseUnshiftTrigger.GetBool() && (!role.IsNeutral() || Options.UseUnshiftTriggerForNKs.GetBool())) || (Options.UsePhantomBasis.GetBool() && (!role.IsNeutral() || Options.UsePhantomBasisForNKs.GetBool()))) && role.SimpleAbilityTrigger() && role != CustomRoles.Chemist && !role.AlwaysUsesUnshift()) && OnlySpawnsWithPetsRoleList.Contains(role); } public static bool NeedUpdateOnLights(this CustomRoles role) diff --git a/Modules/DevManager.cs b/Modules/DevManager.cs index af51367e7..c55e03a47 100644 --- a/Modules/DevManager.cs +++ b/Modules/DevManager.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json; +using UnityEngine; using UnityEngine.Networking; namespace EHR; @@ -19,12 +20,27 @@ public static class DevManager return Tags.GetValueOrDefault(code, DefaultDevUser); } + private static int Attempts; + private static IEnumerator FetchTags() { + Attempts++; + + if (Attempts > 10) + { + Logger.Error($"Failed to fetch tags after {Attempts - 1} attempts.", "DevManager.FetchTags"); + yield break; + } + UnityWebRequest request = UnityWebRequest.Get("https://gurge44.pythonanywhere.com/get_all_tags"); yield return request.SendWebRequest(); - if (request.result is UnityWebRequest.Result.ConnectionError or UnityWebRequest.Result.ProtocolError) { Logger.Error($"Error fetching tags: {request.error}", "DevManager.FetchTags"); } + if (request.result != UnityWebRequest.Result.Success) + { + Logger.Error($"Error fetching tags: {request.error}", "DevManager.FetchTags"); + yield return new WaitForSeconds(300f); + yield return FetchTags(); + } else { try diff --git a/Modules/RPC.cs b/Modules/RPC.cs index ba17b8cfd..6d4a87812 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -841,7 +841,7 @@ internal static class RPCHandlerPatch } case CustomRPC.SyncLobbyTimer: { - GameStartManagerPatch.Timer = reader.ReadPackedInt32(); + GameStartManagerPatch.TimerStartTS = long.Parse(reader.ReadString()); break; } case CustomRPC.SetGamerHealth: diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index 27b15c2a7..fc2db6722 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -504,6 +504,12 @@ internal static class ChatCommands if (!player.Is(CustomRoles.NoteKiller) || args.Length < 2) return; if (!player.IsLocalPlayer()) ChatManager.SendPreviousMessagesToAll(); + + if (!NoteKiller.CanGuess) + { + Utils.SendMessage(GetString("DeathNoteCommand.CanNotGuess"), player.PlayerId); + return; + } var guess = args[1].ToLower(); guess = char.ToUpper(guess[0]) + guess[1..]; @@ -511,6 +517,7 @@ internal static class ChatCommands if (deadPlayer == 0 && (!NoteKiller.RealNames.TryGetValue(0, out var name) || name != guess)) { + NoteKiller.CanGuess = false; Utils.SendMessage(GetString("DeathNoteCommand.WrongName"), player.PlayerId); return; } @@ -519,6 +526,7 @@ internal static class ChatCommands if (pc == null || !pc.IsAlive()) { + NoteKiller.CanGuess = false; Utils.SendMessage(GetString("DeathNoteCommand.PlayerNotFoundOrDead"), player.PlayerId); return; } @@ -1696,9 +1704,13 @@ internal static class ChatCommands else Main.SetRoles[targetPc.PlayerId] = roleToSet; - var playername = $"{Utils.ColorString(Main.PlayerColors.TryGetValue(resultId, out Color32 textColor) ? textColor : Color.white, targetPc.GetRealName())}"; - var rolename = $" {GetString(roleToSet.ToString())} "; - Utils.SendMessage("\n", player.PlayerId, string.Format(GetString("RoleSelected"), playername, rolename)); + Utils.SendMessage("\n", player.PlayerId, string.Format(GetString("RoleSelected"), resultId.ColoredPlayerName(), roleToSet.ToColoredString())); + + if (roleToSet.OnlySpawnsWithPets() && !Options.UsePets.GetBool()) + { + Options.UsePets.SetValue(1); + Utils.SendMessage(GetString("SetRoleUsePetsMessage"), player.PlayerId); + } } private static void UpCommand(ChatController __instance, PlayerControl player, string text, string[] args) diff --git a/Patches/GameStartManagerPatch.cs b/Patches/GameStartManagerPatch.cs index 584e3f121..f120b35f0 100644 --- a/Patches/GameStartManagerPatch.cs +++ b/Patches/GameStartManagerPatch.cs @@ -27,7 +27,8 @@ public static class GameStartManagerUpdatePatch public static class GameStartManagerPatch { - public static float Timer { get; set; } = 600f; + public static float Timer => Math.Max(0, 600f - (Utils.TimeStamp - TimerStartTS)); + public static long TimerStartTS; [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.Start))] public static class GameStartManagerStartPatch @@ -67,7 +68,7 @@ public static class GameStartManagerPatch __instance.GameRoomNameCode.text = GameCode.IntToGameName(AmongUsClient.Instance.GameId); // Reset lobby countdown timer - Timer = 600f; + TimerStartTS = Utils.TimeStamp; HideName = Object.Instantiate(__instance.GameRoomNameCode, __instance.GameRoomNameCode.transform); @@ -145,7 +146,9 @@ public static class GameStartManagerPatch { Main.UpdateTime = 0; - if (((GameData.Instance?.PlayerCount >= MinPlayer && Timer <= MinWait) || Timer <= MaxWait) && !GameStates.IsCountDown) + float timer = Timer; + + if (((GameData.Instance?.PlayerCount >= MinPlayer && timer <= MinWait) || timer <= MaxWait) && !GameStates.IsCountDown) { PlayerControl[] invalidColor = Main.AllPlayerControls.Where(p => p.Data.DefaultOutfit.ColorId < 0 || Palette.PlayerColors.Length <= p.Data.DefaultOutfit.ColorId).ToArray(); @@ -331,11 +334,12 @@ public static class GameStartManagerPatch // Lobby timer if (!GameData.Instance || AmongUsClient.Instance.NetworkMode == NetworkModes.LocalGame) return; - Timer = Mathf.Max(0f, Timer -= Time.deltaTime); - int minutes = (int)Timer / 60; - int seconds = (int)Timer % 60; + float timer = Timer; + + int minutes = (int)timer / 60; + int seconds = (int)timer % 60; var suffix = $"{minutes:00}:{seconds:00}"; - if (Timer <= 60) suffix = Utils.ColorString((int)Timer % 2 == 0 ? Color.yellow : Color.red, suffix); + if (timer <= 60) suffix = Utils.ColorString((int)timer % 2 == 0 ? Color.yellow : Color.red, suffix); if (Options.NoGameEnd.GetBool()) suffix = suffix.Insert(0, Utils.ColorString(Color.yellow, $"{GetString("NoGameEnd").ToUpper()}") + Utils.ColorString(Color.gray, " | ")); diff --git a/Patches/PlayerJoinAndLeftPatch.cs b/Patches/PlayerJoinAndLeftPatch.cs index 22a92aefb..d60cb8725 100644 --- a/Patches/PlayerJoinAndLeftPatch.cs +++ b/Patches/PlayerJoinAndLeftPatch.cs @@ -385,7 +385,7 @@ internal static class InnerNetClientSpawnPatch else { MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncLobbyTimer, SendOption.Reliable, client.Id); - writer.WritePacked((int)GameStartManagerPatch.Timer); + writer.WritePacked((int)GameStartManagerPatch.TimerStartTS); AmongUsClient.Instance.FinishRpcImmediately(writer); } } diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 52e9e2789..41ec3775f 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -2771,7 +2771,7 @@ "JudgeCanTrialInfected": "Can trial Infected", "JudgeCanTrialContagious": "Can trial Contagious", "PostLobbyCodeToEHRDiscordServer": "Post the lobby code to the EHR Discord server", - "Message.LobbyCodeSent": "<#00ffa5>✓ <#00a5ff>The lobby code has been sent to the EHR Discord server.\\nYou can disable this feature in the settings (System Settings tab).", + "Message.LobbyCodeSent": "<#00ffa5>✓ <#00a5ff>The lobby code has been sent to the EHR website. This will help others find EHR lobbies.\\nYou can disable this feature in the settings (System Settings tab).", "ActiveRolesList": "Active Roles List", "ForExample": "Example Use", "HideGameCodes": "Hide Game Code", @@ -3770,7 +3770,7 @@ "TeleportedByTornado": "\\nOh no! You just got caught up in a tornado,\\nand you flew to over here!", "Deadlined": "Deadlined", "DeadlinedInfo": "Must do a task or use kill button each round", - "DeadlinedInfoLong": "(Add-ons):\\nAs the Deadlined, you must complete a task or kill someone each round. If you fail to do so, you will die. If a meeting gets called too soon after the previous one ends (time set by the host), you won't die. This doesn't apply on the first round.", + "DeadlinedInfoLong": "(Add-ons):\\nAs the Deadlined, you must complete a task or use your kill button (not necessarily for directly killing) on someone each round. If you fail to do so, you will die. If a meeting gets called too soon after the previous one ends (time set by the host), you won't die. This doesn't apply on the first round.", "Prefix.Deadlined": "Deadlined", "Deadlined.InactiveTime": "Safe Time After Meetings", "Deadlined.MustDoTask": "<#ffa500>You must complete a task or kill before the next meeting", @@ -4269,6 +4269,7 @@ "NKWC.XKills": "Kill X Players", "NoteKiller.NumPlayersToKill": "Number of Players to Kill (X =)", "NoteKiller.OthersSelfSuffix": "There's a {0} in the game. Your real name is <#00ffa5>{1}", + "DeathNoteCommand.CanNotGuess": "You've already tried to guess someone's real name and failed, so you can't guess again until the next meeting.", "DeathNoteCommand.WrongName": "You've entered the wrong name.", "DeathNoteCommand.PlayerNotFoundOrDead": "The player with this name was not found or is already dead.", "DeathNoteCommand.Success": "Success! {0} was killed.", diff --git a/Roles/Neutral/NoteKiller.cs b/Roles/Neutral/NoteKiller.cs index c07789352..5d178d15c 100644 --- a/Roles/Neutral/NoteKiller.cs +++ b/Roles/Neutral/NoteKiller.cs @@ -26,14 +26,22 @@ public class NoteKiller : RoleBase private static readonly string[] Names = [ - "John", "Jane", "Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Hank", - "Ivy", "Jack", "Kate", "Liam", "Mia", "Nina", "Oliver", "Penny", "Quinn", "Ryan" + "John", "Jane", "Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Hank", + "Ivy", "Jack", "Kate", "Liam", "Nina", "Oliver", "Penny", "Quinn", "Ryan", + "Thomas", "Uma", "Victor", "William", "Yang", "Zach", "Zoe", "Xavier", "Zoey", + "Abigail", "Alex", "Amelia", "Ava", "Bella", "Charlotte", "Chloe", "Daisy", "Emily", + "Ella", "Evelyn", "Faith", "Florence", "Grace", "Hannah", "Isabella", "Isabelle", + "Isabel", "Jasmine", "Jocelyn", "Julia", "Katherine", "Lily", "Madeline", "Madison", + "Madelyn", "Margaret", "Maria", "Matilda", "Mia", "Mila", "Miranda", "Natalie", + "Nora", "Olivia", "Penelope", "Piper", "Poppy", "Riley", "Rose", "Sophia", "Sofia", + "Sophie", "Stella", "Sydney", "Taylor", "Victoria", "Violet", "Willow", "Xander" ]; public static Dictionary RealNames = []; private static Dictionary ShownClues = []; private static long ShowClueEndTimeStamp; public static int Kills; + public static bool CanGuess; private static byte NoteKillerID; @@ -82,6 +90,7 @@ public class NoteKiller : RoleBase { On = true; NoteKillerID = playerId; + CanGuess = true; } public override bool CanUseKillButton(PlayerControl pc) @@ -170,6 +179,11 @@ public class NoteKiller : RoleBase return false; } + public override void AfterMeetingTasks() + { + CanGuess = true; + } + public void ReceiveRPC(MessageReader reader) { switch (reader.ReadPackedInt32())