diff --git a/CTA/CustomTeamAssigner/CustomRoles.cs b/CTA/CustomTeamAssigner/CustomRoles.cs index 862a5e541..64d051948 100644 --- a/CTA/CustomTeamAssigner/CustomRoles.cs +++ b/CTA/CustomTeamAssigner/CustomRoles.cs @@ -145,6 +145,7 @@ public enum CustomRoles Tracker, Detective, Judge, + SpiritGuide, // Vanilla Remakes CrewmateEHR, @@ -155,6 +156,7 @@ public enum CustomRoles TrackerEHR, DetectiveEHR, JudgeEHR, + SpiritGuideEHR, // Crewmates diff --git a/CustomRoles.cs b/CustomRoles.cs index 862a5e541..64d051948 100644 --- a/CustomRoles.cs +++ b/CustomRoles.cs @@ -145,6 +145,7 @@ public enum CustomRoles Tracker, Detective, Judge, + SpiritGuide, // Vanilla Remakes CrewmateEHR, @@ -155,6 +156,7 @@ public enum CustomRoles TrackerEHR, DetectiveEHR, JudgeEHR, + SpiritGuideEHR, // Crewmates diff --git a/Main.cs b/Main.cs index 766cfd8e2..6782844db 100644 --- a/Main.cs +++ b/Main.cs @@ -487,6 +487,7 @@ public class Main : BaseUnityPlugin { CustomRoles.Noisemaker, "#ff4a62" }, { CustomRoles.Detective, "#625EEE" }, { CustomRoles.Judge, "#f8d85a" }, + { CustomRoles.SpiritGuide, "#D8E3E6" }, // Vanilla Remakes { CustomRoles.CrewmateEHR, "#8cffff" }, { CustomRoles.EngineerEHR, "#FF6A00" }, @@ -496,6 +497,7 @@ public class Main : BaseUnityPlugin { CustomRoles.NoisemakerEHR, "#ff4a62" }, { CustomRoles.DetectiveEHR, "#625EEE" }, { CustomRoles.JudgeEHR, "#f8d85a" }, + { CustomRoles.SpiritGuideEHR, "#D8E3E6" }, // Crewmates { CustomRoles.DoubleAgent, "#ff1919" }, { CustomRoles.Luckey, "#b8d7a3" }, diff --git a/Modules/AURoleOptions.cs b/Modules/AURoleOptions.cs index f961e4aa4..ed15eda27 100644 --- a/Modules/AURoleOptions.cs +++ b/Modules/AURoleOptions.cs @@ -138,6 +138,12 @@ public static class AURoleOptions set => Opt.SetFloat(FloatOptionNames.JudgeTaskRequirementPercentage, value); } + public static float SpiritGuideCooldown + { + get => Opt.GetFloat(FloatOptionNames.SpiritGuideCooldownSeconds); + set => Opt.SetFloat(FloatOptionNames.SpiritGuideCooldownSeconds, value); + } + public static void SetOpt(IGameOptions opt) { Opt = opt; diff --git a/Modules/CustomRoleSelector.cs b/Modules/CustomRoleSelector.cs index f20528195..de5317c60 100644 --- a/Modules/CustomRoleSelector.cs +++ b/Modules/CustomRoleSelector.cs @@ -21,7 +21,8 @@ internal static class CustomRoleSelector [RoleTypes.Phantom] = 0, [RoleTypes.Viper] = 0, [RoleTypes.Detective] = 0, - [RoleTypes.Judge] = 0 + [RoleTypes.Judge] = 0, + [RoleTypes.SpiritGuide] = 0 }; public static readonly Dictionary GameModeRoles = new() @@ -523,6 +524,7 @@ internal static class CustomRoleSelector [RoleTypes.Viper] = 0, [RoleTypes.Detective] = 0, [RoleTypes.Judge] = 0, + [RoleTypes.SpiritGuide] = 0 }; foreach (CustomRoles role in RoleResult.Values) diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 77a49cf12..66824682f 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -412,6 +412,7 @@ internal static class CustomRolesHelper CustomRoles.DetectiveEHR => CustomRoles.Detective, CustomRoles.ViperEHR => CustomRoles.Viper, CustomRoles.JudgeEHR => CustomRoles.Judge, + CustomRoles.SpiritGuideEHR => CustomRoles.SpiritGuide, // Hide And Seek CustomRoles.Hider => CustomRoles.Crewmate, @@ -470,6 +471,7 @@ internal static class CustomRolesHelper CustomRoles.Detective => CustomRoles.DetectiveEHR, CustomRoles.Viper => CustomRoles.ViperEHR, CustomRoles.Judge => CustomRoles.JudgeEHR, + CustomRoles.SpiritGuide => CustomRoles.SpiritGuideEHR, _ => role.IsImpostor() ? CustomRoles.ImpostorEHR : CustomRoles.CrewmateEHR }; } @@ -1379,6 +1381,8 @@ internal static class CustomRolesHelper CustomRoles.DetectiveEHR => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.Judge => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.JudgeEHR => RoleOptionType.Crewmate_Miscellaneous, + CustomRoles.SpiritGuide => RoleOptionType.Crewmate_Miscellaneous, + CustomRoles.SpiritGuideEHR => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.Addict => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.CameraMan => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.Carrier => RoleOptionType.Crewmate_Miscellaneous, @@ -1686,6 +1690,7 @@ internal static class CustomRolesHelper CustomRoles.Impostor or CustomRoles.Detective or CustomRoles.Judge or + CustomRoles.SpiritGuide or CustomRoles.Viper or CustomRoles.Phantom or CustomRoles.Shapeshifter; diff --git a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs index e8a8b97ae..3503d6d03 100644 --- a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs +++ b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs @@ -673,6 +673,9 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS case CustomRoles.JudgeEHR: AURoleOptions.JudgeTaskRequirementPercentage = CrewmateVanillaRoles.JudgeTaskRequirementPercentage.GetFloat(); break; + case CustomRoles.SpiritGuideEHR: + AURoleOptions.SpiritGuideCooldown = CrewmateVanillaRoles.SpiritGuideCooldown.GetFloat(); + break; } if (player.UsesJudgeAbilityAsTrigger()) diff --git a/Modules/OptionBackup/OptionBackupData.cs b/Modules/OptionBackup/OptionBackupData.cs index d006aa0ca..e448d69df 100644 --- a/Modules/OptionBackup/OptionBackupData.cs +++ b/Modules/OptionBackup/OptionBackupData.cs @@ -42,7 +42,7 @@ public class OptionBackupData // Since TryGetUInt is not implemented, get it separately AllValues.Add(new UIntOptionBackupValue(UInt32OptionNames.Keywords, (uint)option.Keywords)); - RoleTypes[] array = [RoleTypes.Scientist, RoleTypes.Engineer, RoleTypes.GuardianAngel, RoleTypes.Shapeshifter, RoleTypes.Noisemaker, RoleTypes.Phantom, RoleTypes.Tracker, RoleTypes.Viper, RoleTypes.Detective, RoleTypes.Judge]; + RoleTypes[] array = [RoleTypes.Scientist, RoleTypes.Engineer, RoleTypes.GuardianAngel, RoleTypes.Shapeshifter, RoleTypes.Noisemaker, RoleTypes.Phantom, RoleTypes.Tracker, RoleTypes.Viper, RoleTypes.Detective, RoleTypes.Judge, RoleTypes.SpiritGuide]; foreach (RoleTypes role in array) AllValues.Add(new RoleRateBackupValue(role, option.RoleOptions.GetNumPerGame(role), option.RoleOptions.GetChancePerGame(role))); } diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index fff29bc69..238e82b29 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -580,7 +580,8 @@ internal static class ChatCommands ClientData client = x.GetClient(); string name = Main.AllPlayerNames.GetValueOrDefault(x.PlayerId, string.Empty); string id = string.IsNullOrEmpty(name) ? $"ID {x.PlayerId}" : $" (ID {x.PlayerId})"; - return $"{name}{id} - {x.FriendCode} | {client?.GetHashedPuid()} | {client?.PlatformData.Platform}"; + string platform = (int?)client?.PlatformData.Platform == 112 ? "Starlight" : client?.PlatformData.Platform.ToString(); + return $"{name}{id} - {x.FriendCode} | {client?.GetHashedPuid()} | {platform}"; }) + "", player.PlayerId); } else if (byte.TryParse(args[1], out byte playerId)) @@ -590,7 +591,8 @@ internal static class ChatCommands ClientData client = pc.GetClient(); string name = Main.AllPlayerNames.GetValueOrDefault(pc.PlayerId, string.Empty); string id = string.IsNullOrEmpty(name) ? $"ID {pc.PlayerId}" : $" (ID {pc.PlayerId})"; - Utils.SendMessage($"{name}{id}:\n{pc.FriendCode}\n{client?.GetHashedPuid()}\n{client?.PlatformData.Platform}", player.PlayerId); + string platform = (int?)client?.PlatformData.Platform == 112 ? "Starlight" : client?.PlatformData.Platform.ToString(); + Utils.SendMessage($"{name}{id}:\n{pc.FriendCode}\n{client?.GetHashedPuid()}\n{platform}", player.PlayerId); } } diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index 13a7aebde..912f4cf84 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -1450,6 +1450,7 @@ internal static class GameEndChecker or RoleTypes.Tracker or RoleTypes.Detective or RoleTypes.Judge + or RoleTypes.SpiritGuide or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel) { diff --git a/Patches/IntroPatch.cs b/Patches/IntroPatch.cs index d92bf0e09..008f52604 100644 --- a/Patches/IntroPatch.cs +++ b/Patches/IntroPatch.cs @@ -842,7 +842,11 @@ internal static class BeginCrewmatePatch CustomRoles.Judge or CustomRoles.JudgeEHR or CustomRoles.Prosecutor - => GetIntroSound(RoleTypes.Judge), + => GetIntroSound(RoleTypes.Judge), + + CustomRoles.SpiritGuide + or CustomRoles.SpiritGuideEHR + => GetIntroSound(RoleTypes.SpiritGuide), CustomRoles.Phantom or CustomRoles.PhantomEHR diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs index 093520e63..ed9385873 100644 --- a/Patches/OnGameStartedPatch.cs +++ b/Patches/OnGameStartedPatch.cs @@ -57,7 +57,8 @@ internal static class ChangeRoleSettings RoleTypes.Tracker, RoleTypes.Detective, RoleTypes.Viper, - RoleTypes.Judge + RoleTypes.Judge, + RoleTypes.SpiritGuide }.Do(x => Main.NormalOptions.roleOptions.SetRoleRate(x, 0, 0)); if (Main.NormalOptions.MapId > 5 && !(Main.NormalOptions.MapId == 6 && SubmergedCompatibility.Loaded) && !Main.LIMap) @@ -1200,7 +1201,7 @@ internal static class StartGameHostPatch if (Options.CurrentGameMode == CustomGameMode.Standard) { - if (target.Is(Team.Crewmate) && roleType is not (RoleTypes.Crewmate or RoleTypes.Scientist or RoleTypes.Engineer or RoleTypes.Noisemaker or RoleTypes.Tracker or RoleTypes.Detective or RoleTypes.Judge or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel)) + if (target.Is(Team.Crewmate) && roleType is not (RoleTypes.Crewmate or RoleTypes.Scientist or RoleTypes.Engineer or RoleTypes.Noisemaker or RoleTypes.Tracker or RoleTypes.Detective or RoleTypes.Judge or RoleTypes.SpiritGuide or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel)) displayRole = RoleTypes.Crewmate; if (target.Is(Team.Impostor) && roleType is not (RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Phantom or RoleTypes.Viper or RoleTypes.ImpostorGhost)) diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 8aef0a274..b5a54e2dc 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -2633,6 +2633,7 @@ internal static class PlayerControlLocalSetRolePatch RoleTypes.Detective => CustomRoles.DetectiveEHR, RoleTypes.Viper => CustomRoles.ViperEHR, RoleTypes.Judge => CustomRoles.JudgeEHR, + RoleTypes.SpiritGuide => CustomRoles.SpiritGuideEHR, _ => CustomRoles.NotAssigned }; diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 5839f5584..5361e5841 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -630,10 +630,16 @@ "DetectiveEHRInfoLong": "(Crewmates):\\nAs the Detective, you can write down notes about each crime scene and interrogate suspects.\n\nThis role currently does not work due to a basis reset issue.", "AbilityButtonText.Detective": "Notes", "SecondaryAbilityButtonText.Detective": "Interrogate", + "Judge": "Judge", "JudgeEHR": "Judge", "JudgeEHRInfo": "Silence in the courtroom!", "JudgeEHRInfoLong": "(Crewmates):\\nOverrule a meeting to eject an Impostor.\\nIf wrong, you get ejected.", "JudgeTaskRequirementPercentage": "Task Unlock", + "SpiritGuide": "Influencer", + "SpiritGuideEHR": "Influencer", + "SpiritGuideEHRInfo": "Send messages to living Crewmates.", + "SpiritGuideEHRInfoLong": "(Crewmate Ghost):\\nSend messages to living Crewmates.", + "SpiritGuideCooldown": "Message Cooldown", "Detour": "Detour", "DetourInfo": "Pass Kills Onto Nearest Player", "DetourInfoLong": "(Crewmate):\\nAs the Detour, all kill button related actions on you will be blocked and instead happen on the closest player to you.", diff --git a/Roles/Standard/Crewmate/Investigate/Analyst.cs b/Roles/Standard/Crewmate/Investigate/Analyst.cs index f98d275fe..801d1ef36 100644 --- a/Roles/Standard/Crewmate/Investigate/Analyst.cs +++ b/Roles/Standard/Crewmate/Investigate/Analyst.cs @@ -84,6 +84,7 @@ internal class Analyst : RoleBase CustomRoles.Tracker or CustomRoles.TrackerEHR => CustomRoles.Scout.ColoredTextByRole(GetString("Tracker")), CustomRoles.Detective or CustomRoles.DetectiveEHR => CustomRoles.Forensic.ColoredTextByRole(GetString("Detective")), CustomRoles.Judge or CustomRoles.JudgeEHR => CustomRoles.Prosecutor.ColoredTextByRole(GetString("Judge")), + CustomRoles.SpiritGuide or CustomRoles.SpiritGuideEHR => CustomRoles.SpiritGuide.ColoredTextByRole(GetString("SpiritGuide")), _ => string.Empty } : string.Empty; diff --git a/Roles/Standard/Crewmate/Vanilla/CrewmateVanillaRoles.cs b/Roles/Standard/Crewmate/Vanilla/CrewmateVanillaRoles.cs index 34d108d40..8ca7e2fb2 100644 --- a/Roles/Standard/Crewmate/Vanilla/CrewmateVanillaRoles.cs +++ b/Roles/Standard/Crewmate/Vanilla/CrewmateVanillaRoles.cs @@ -16,6 +16,7 @@ internal class CrewmateVanillaRoles : IVanillaSettingHolder public static OptionItem TrackerDelay; public static OptionItem DetectiveSuspectLimit; public static OptionItem JudgeTaskRequirementPercentage; + public static OptionItem SpiritGuideCooldown; public TabGroup Tab => TabGroup.CrewmateRoles; public void SetupCustomOption() @@ -79,5 +80,11 @@ internal class CrewmateVanillaRoles : IVanillaSettingHolder JudgeTaskRequirementPercentage = new FloatOptionItem(5092, "JudgeTaskRequirementPercentage", new(0f, 100f, 25f), 50f, Tab) .SetParent(CustomRoleSpawnChances[CustomRoles.JudgeEHR]) .SetValueFormat(OptionFormat.Percent); + + SetupRoleOptions(5095, Tab, CustomRoles.SpiritGuideEHR); + + SpiritGuideCooldown = new FloatOptionItem(5097, "SpiritGuideCooldown", new(0f, 250f, 1f), 5f, Tab) + .SetParent(CustomRoleSpawnChances[CustomRoles.SpiritGuideEHR]) + .SetValueFormat(OptionFormat.Seconds); } } \ No newline at end of file diff --git a/Roles/Standard/Neutral/Killing/Jackal.cs b/Roles/Standard/Neutral/Killing/Jackal.cs index e6de023ef..3bad06840 100644 --- a/Roles/Standard/Neutral/Killing/Jackal.cs +++ b/Roles/Standard/Neutral/Killing/Jackal.cs @@ -221,13 +221,14 @@ public class Jackal : RoleBase public static void OnAnyoneDead() { - Instances.ForEach(x => + for (var index = Instances.Count - 1; index >= 0; index--) { - var pc = x.JackalId.GetPlayer(); + Jackal instance = Instances[index]; + var pc = instance.JackalId.GetPlayer(); if (pc && pc.IsAlive()) return; - - x.PromoteSidekick(); - }); + + instance.PromoteSidekick(); + } } }