From ebc86f2e2ca3ccee51c95cce23f5ce16800c86c3 Mon Sep 17 00:00:00 2001 From: Gurge44 Date: Thu, 11 Jun 2026 20:12:11 +0200 Subject: [PATCH] 30% --- Modules/ExtendedPlayerControl.cs | 9 ++-- Modules/GuessManager.cs | 2 +- Modules/NameColorManager.cs | 4 +- Modules/RPC.cs | 1 + Modules/Statistics.cs | 2 +- Modules/Utils.cs | 28 ++++++---- Patches/ChatCommandPatch.cs | 3 +- Patches/HudPatch.cs | 2 +- Patches/MeetingHudPatch.cs | 21 ++++---- Patches/OnGameStartedPatch.cs | 2 +- Patches/PlayerControlPatch.cs | 19 ++++--- Roles/RoleBase.cs | 2 +- Roles/Standard/Crewmate/Chaos/Randomizer.cs | 2 +- .../Standard/Crewmate/Investigate/Analyst.cs | 8 +-- .../Standard/Crewmate/Investigate/Coroner.cs | 28 ++++++---- .../Crewmate/Investigate/DoubleAgent.cs | 7 +-- Roles/Standard/Crewmate/Investigate/Druid.cs | 15 ++---- Roles/Standard/Crewmate/Investigate/Enigma.cs | 33 ++++++------ .../Standard/Crewmate/Investigate/Forensic.cs | 3 +- .../Crewmate/Investigate/FortuneTeller.cs | 25 ++++----- Roles/Standard/Crewmate/Investigate/Hacker.cs | 31 ++++++----- .../Standard/Crewmate/Investigate/Ignitor.cs | 15 ++---- .../Standard/Crewmate/Investigate/Imitator.cs | 16 +++--- .../Crewmate/Investigate/Inspector.cs | 24 +++++---- .../Crewmate/Investigate/Investigator.cs | 20 ++++--- .../Crewmate/Investigate/Markseeker.cs | 7 +-- Roles/Standard/Crewmate/Investigate/Medium.cs | 19 +++---- .../Crewmate/Investigate/MeetingManager.cs | 14 +++-- .../Crewmate/Investigate/Mortician.cs | 23 ++++---- Roles/Standard/Crewmate/Investigate/Oracle.cs | 17 +++--- .../Crewmate/Investigate/Perceiver.cs | 2 +- .../Standard/Crewmate/Investigate/Psychic.cs | 13 ++--- Roles/Standard/Crewmate/Investigate/Rabbit.cs | 14 ++--- Roles/Standard/Crewmate/Investigate/Scout.cs | 31 ++++++----- Roles/Standard/Crewmate/Investigate/Sentry.cs | 16 +++--- Roles/Standard/Crewmate/Investigate/Snitch.cs | 54 +++++++++++-------- .../Crewmate/Investigate/Socialite.cs | 20 ++++--- .../Crewmate/Investigate/Soothsayer.cs | 9 ++-- .../Crewmate/Investigate/Spiritualist.cs | 15 +++--- Roles/Standard/Crewmate/Investigate/Spy.cs | 25 ++++----- .../Crewmate/Investigate/Whisperer.cs | 27 ++++++---- .../Standard/Crewmate/Investigate/Witness.cs | 8 +-- Roles/Standard/Crewmate/Power/President.cs | 2 +- Roles/Standard/Impostor/Support/Nullifier.cs | 9 ++-- 44 files changed, 347 insertions(+), 300 deletions(-) diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index c4b179551..0afc807a8 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -1668,24 +1668,21 @@ internal static class ExtendedPlayerControl { if (!player || !target) return false; - Arsonist.IsDoused.TryGetValue((player.PlayerId, target.PlayerId), out bool isDoused); - return isDoused; + return Arsonist.IsDoused != null && Arsonist.IsDoused.GetValueOrDefault((player.PlayerId, target.PlayerId)); } public bool IsDrawPlayer(PlayerControl target) { if (!player || !target) return false; - Revolutionist.IsDraw.TryGetValue((player.PlayerId, target.PlayerId), out bool isDraw); - return isDraw; + return Revolutionist.IsDraw.GetValueOrDefault((player.PlayerId, target.PlayerId)); } public bool IsRevealedPlayer(PlayerControl target) { if (!player || !target) return false; - Investigator.IsRevealed.TryGetValue((player.PlayerId, target.PlayerId), out bool isDoused); - return isDoused; + return Investigator.IsRevealed.GetValueOrDefault((player.PlayerId, target.PlayerId)); } public void RpcSetDousedPlayer(PlayerControl target, bool isDoused) diff --git a/Modules/GuessManager.cs b/Modules/GuessManager.cs index a2a957f3c..c97ea3ef6 100644 --- a/Modules/GuessManager.cs +++ b/Modules/GuessManager.cs @@ -382,7 +382,7 @@ public static class GuessManager return true; } - if (Markseeker.PlayerIdList.Any(x => Main.PlayerStates[x].Role is Markseeker { TargetRevealed: true } ms && ms.MarkedId == target.PlayerId)) + if (Markseeker.PlayerIdList != null && Markseeker.PlayerIdList.Any(x => Main.PlayerStates[x].Role is Markseeker { TargetRevealed: true } ms && ms.MarkedId == target.PlayerId)) { ShowMessage("GuessMarkseekerTarget"); return true; diff --git a/Modules/NameColorManager.cs b/Modules/NameColorManager.cs index ea9360129..ac3063f53 100644 --- a/Modules/NameColorManager.cs +++ b/Modules/NameColorManager.cs @@ -162,7 +162,7 @@ public static class NameColorManager CustomRoles.Glitch when target.IsRoleBlocked() => Utils.GetRoleColorCode(seerRole), CustomRoles.Slenderman when Slenderman.IsBlinded(target.PlayerId) => "000000", CustomRoles.Aid when Aid.ShieldedPlayers.ContainsKey(target.PlayerId) => Utils.GetRoleColorCode(CustomRoles.Aid), - CustomRoles.Spy when Spy.SpyRedNameList.Contains(target.PlayerId) => "#BA4A00", + CustomRoles.Spy when Spy.SpyRedNameList != null && Spy.SpyRedNameList.Contains(target.PlayerId) => "#BA4A00", CustomRoles.Mastermind when Mastermind.ManipulateDelays.ContainsKey(target.PlayerId) => "#00ffa5", CustomRoles.Mastermind when Mastermind.ManipulatedPlayers.ContainsKey(target.PlayerId) => Utils.GetRoleColorCode(CustomRoles.Arsonist), CustomRoles.Hitman when (seerRoleClass as Hitman)?.TargetId == target.PlayerId => "000000", @@ -193,7 +193,7 @@ public static class NameColorManager CustomRoles.Wyrd when ((Wyrd)seerRoleClass).MarkedPlayers.Contains(target.PlayerId) => "000000", CustomRoles.Investor when ((Investor)seerRoleClass).MarkedPlayers.Contains(target.PlayerId) => "000000", CustomRoles.Stealth when ((Stealth)seerRoleClass).darkenedPlayers?.Any(x => x == target) ?? false => "000000", - CustomRoles.Snitch when Snitch.IsComplete.GetValueOrDefault(seer.PlayerId) && Snitch.IsSnitchTarget(target) => Utils.GetRoleColorCode(targetRole), + CustomRoles.Snitch when Snitch.IsComplete != null && Snitch.IsComplete.GetValueOrDefault(seer.PlayerId) && Snitch.IsSnitchTarget(target) => Utils.GetRoleColorCode(targetRole), _ => color }; diff --git a/Modules/RPC.cs b/Modules/RPC.cs index 67ee0f883..a50005031 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -763,6 +763,7 @@ internal static class RPCHandlerPatch byte investigatorId = reader.ReadByte(); byte revealId = reader.ReadByte(); bool revealed = reader.ReadBoolean(); + Investigator.IsRevealed ??= []; Investigator.IsRevealed[(investigatorId, revealId)] = revealed; break; } diff --git a/Modules/Statistics.cs b/Modules/Statistics.cs index 8ad481cda..fe6c074a0 100644 --- a/Modules/Statistics.cs +++ b/Modules/Statistics.cs @@ -328,7 +328,7 @@ public static class Statistics (targetState.Role is Follower tc && tc.BetPlayer == killer.PlayerId)) Achievements.Type.WhatHaveIDone.CompleteAfterGameEnd(); - if (targetState.MainRole == CustomRoles.Snitch && Snitch.IsExposed.TryGetValue(target.PlayerId, out bool exposed) && exposed) + if (targetState.MainRole == CustomRoles.Snitch && Snitch.IsExposed != null && Snitch.IsExposed.TryGetValue(target.PlayerId, out bool exposed) && exposed) Achievements.Type.ThatWasClose.CompleteAfterGameEnd(); switch (killerState.MainRole) diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 9a95bbd26..7e8a38382 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -531,7 +531,7 @@ public static class Utils if ((ExileController.Instance || targetState.IsDead || (GameStates.IsMeeting && MeetingHud.Instance.state is MeetingHud.VoteStates.Results or MeetingHud.VoteStates.Proceeding or MeetingHud.VoteStates.Voted or MeetingHud.VoteStates.NotVoted)) && !GameStates.IsEnded && Forger.Forges.TryGetValue(targetId, out var forgedRole)) targetMainRole = forgedRole; - if (!self && seerMainRole.IsImpostor() && targetMainRole == CustomRoles.DoubleAgent && DoubleAgent.ShownRoles.TryGetValue(targetId, out CustomRoles shownRole)) + if (!self && seerMainRole.IsImpostor() && targetMainRole == CustomRoles.DoubleAgent && DoubleAgent.ShownRoles != null && DoubleAgent.ShownRoles.TryGetValue(targetId, out CustomRoles shownRole)) targetMainRole = shownRole; var loversShowDifferentRole = false; @@ -3280,7 +3280,7 @@ public static class Utils if (Revolutionist.RevolutionistTimer.TryGetValue(seer.PlayerId, out (PlayerControl Player, float Timer) arKvp1) && arKvp1.Player == target) TargetMark.Append(CustomRoles.Revolutionist.ColoredTextByRole("○")); break; - case CustomRoles.Investigator when Investigator.InvestigatorTimer.TryGetValue(seer.PlayerId, out (PlayerControl PLAYER, float TIMER) arKvp2) && arKvp2.PLAYER == target: + case CustomRoles.Investigator when Investigator.InvestigatorTimer != null && Investigator.InvestigatorTimer.TryGetValue(seer.PlayerId, out (PlayerControl PLAYER, float TIMER) arKvp2) && arKvp2.PLAYER == target: TargetMark.Append(CustomRoles.Investigator.ColoredTextByRole("○")); break; case CustomRoles.Analyst when (Main.PlayerStates[seer.PlayerId].Role as Analyst).CurrentTarget.ID == target.PlayerId: @@ -3311,7 +3311,7 @@ public static class Utils if (!GameStates.IsLobby) { - if (seer.IsAlive() && seer.IsRevealedPlayer(target) && target.Is(CustomRoles.Trickster)) + if (seer.IsAlive() && Investigator.RandomRole != null && seer.IsRevealedPlayer(target) && target.Is(CustomRoles.Trickster)) { targetRoleText = Investigator.RandomRole[seer.PlayerId]; targetRoleText += Investigator.GetTaskState(); @@ -3590,11 +3590,14 @@ public static class Utils } var markList = Markseeker.PlayerIdList; - for (int i = 0; i < markList.Count; i++) + if (markList != null) { - if (states[markList[i]].Role is Markseeker { IsEnable: true, TargetRevealed: true } role && - role.MarkedId == targetId) - return true; + for (int i = 0; i < markList.Count; i++) + { + if (states[markList[i]].Role is Markseeker { IsEnable: true, TargetRevealed: true } role && + role.MarkedId == targetId) + return true; + } } switch (Options.CurrentGameMode) @@ -3730,11 +3733,14 @@ public static class Utils } var markList = Markseeker.PlayerIdList; - for (int i = 0; i < markList.Count; i++) + if (markList != null) { - if (states[markList[i]].Role is Markseeker { IsEnable: true, TargetRevealed: true } role && - role.MarkedId == targetId) - return true; + for (int i = 0; i < markList.Count; i++) + { + if (states[markList[i]].Role is Markseeker { IsEnable: true, TargetRevealed: true } role && + role.MarkedId == targetId) + return true; + } } if (local.Is(CustomRoles.God) && God.KnowInfo.GetValue() == 2) return true; diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index 20a8958ba..9960da1b3 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -801,7 +801,7 @@ internal static class ChatCommands { if (Starspawn.IsDayBreak) return; - if (!Imitator.PlayerIdList.Contains(player.PlayerId) || !player.IsAlive() || args.Length < 2 || !byte.TryParse(args[1], out byte targetId) || !Main.PlayerStates.TryGetValue(targetId, out PlayerState targetState)) return; + if (Imitator.PlayerIdList == null || !Imitator.PlayerIdList.Contains(player.PlayerId) || !player.IsAlive() || args.Length < 2 || !byte.TryParse(args[1], out byte targetId) || !Main.PlayerStates.TryGetValue(targetId, out PlayerState targetState)) return; if (!targetState.IsDead) { @@ -817,6 +817,7 @@ internal static class ChatCommands return; } + Imitator.ImitatingRole ??= []; Imitator.ImitatingRole[player.PlayerId] = targetState.MainRole; RPC.PlaySoundRPC(player.PlayerId, Sounds.TaskComplete); Logger.Info($"{player.GetRealName()} will be imitating as {targetState.MainRole}", "Imitator"); diff --git a/Patches/HudPatch.cs b/Patches/HudPatch.cs index b81b60667..0ef9ee3fd 100644 --- a/Patches/HudPatch.cs +++ b/Patches/HudPatch.cs @@ -857,7 +857,7 @@ internal static class MapBehaviourShowPatch PlayerControl player = PlayerControl.LocalPlayer; - if (player.GetCustomRole() == CustomRoles.Hacker && opts.Mode is not MapOptions.Modes.CountOverlay && Hacker.PlayerIdList.ContainsKey(player.PlayerId)) + if (player.GetCustomRole() == CustomRoles.Hacker && opts.Mode is not MapOptions.Modes.CountOverlay && Hacker.PlayerIdList != null && Hacker.PlayerIdList.ContainsKey(player.PlayerId)) { Logger.Info("Modded Client uses Map", "Hacker"); Hacker.MapHandle(player, __instance, opts); diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index b84d93d40..a374cfda3 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -823,7 +823,7 @@ internal static class MeetingHudStartPatch AddMsg(string.Format(GetString("SilencerDead"), playername, pc.PlayerId, CustomRoles.Silencer.ColoredTextByRole(GetString("SilencerKillTitle")))); } - if (Forensic.ForensicNotify.TryGetValue(pc.PlayerId, out string value1)) AddMsg(value1, pc.PlayerId, CustomRoles.Forensic.ColoredTextByRole(GetString("ForensicNoticeTitle"))); + if (Forensic.ForensicNotify != null && Forensic.ForensicNotify.TryGetValue(pc.PlayerId, out string value1)) AddMsg(value1, pc.PlayerId, CustomRoles.Forensic.ColoredTextByRole(GetString("ForensicNoticeTitle"))); if (Main.SleuthMsgs.TryGetValue(pc.PlayerId, out string msg)) AddMsg(msg, pc.PlayerId, CustomRoles.Sleuth.ColoredTextByRole(GetString("Sleuth"))); if (pc.Is(CustomRoles.Mimic) && !pc.IsAlive()) @@ -833,11 +833,11 @@ internal static class MeetingHudStartPatch .Do(x => mimicMsg += $"\n{x.GetNameWithRole(true)}"); } - if (Mortician.MsgToSend.TryGetValue(pc.PlayerId, out string value2)) AddMsg(value2, pc.PlayerId, CustomRoles.Mortician.ColoredTextByRole(GetString("MorticianCheckTitle"))); - if (Medium.ContactPlayer.ContainsValue(pc.PlayerId)) AddMsg(string.Format(GetString("MediumNotifySelf"), Main.AllPlayerNames[Medium.ContactPlayer.FirstOrDefault(x => x.Value == pc.PlayerId).Key], pc.GetAbilityUseLimit()), pc.PlayerId, CustomRoles.Medium.ColoredTextByRole(GetString("MediumTitle"))); - if (Medium.ContactPlayer.ContainsKey(pc.PlayerId) && (!Medium.OnlyReceiveMsgFromCrew.GetBool() || pc.IsCrewmate())) AddMsg(string.Format(GetString("MediumNotifyTarget"), Main.AllPlayerNames[Medium.ContactPlayer[pc.PlayerId]]), pc.PlayerId, CustomRoles.Medium.ColoredTextByRole(GetString("MediumTitle"))); + if (Mortician.MsgToSend != null && Mortician.MsgToSend.TryGetValue(pc.PlayerId, out string value2)) AddMsg(value2, pc.PlayerId, CustomRoles.Mortician.ColoredTextByRole(GetString("MorticianCheckTitle"))); + if (Medium.ContactPlayer != null && Medium.ContactPlayer.ContainsValue(pc.PlayerId)) AddMsg(string.Format(GetString("MediumNotifySelf"), Main.AllPlayerNames[Medium.ContactPlayer.FirstOrDefault(x => x.Value == pc.PlayerId).Key], pc.GetAbilityUseLimit()), pc.PlayerId, CustomRoles.Medium.ColoredTextByRole(GetString("MediumTitle"))); + if (Medium.ContactPlayer != null && Medium.ContactPlayer.ContainsKey(pc.PlayerId) && (!Medium.OnlyReceiveMsgFromCrew.GetBool() || pc.IsCrewmate())) AddMsg(string.Format(GetString("MediumNotifyTarget"), Main.AllPlayerNames[Medium.ContactPlayer[pc.PlayerId]]), pc.PlayerId, CustomRoles.Medium.ColoredTextByRole(GetString("MediumTitle"))); if (Virus.VirusNotify.TryGetValue(pc.PlayerId, out string value3)) AddMsg(value3, pc.PlayerId, CustomRoles.Virus.ColoredTextByRole(GetString("VirusNoticeTitle"))); - if (Enigma.MsgToSend.TryGetValue(pc.PlayerId, out string value4)) AddMsg(value4, pc.PlayerId, CustomRoles.Enigma.ColoredTextByRole(Enigma.MsgToSendTitle[pc.PlayerId])); + if (Enigma.MsgToSend != null && Enigma.MsgToSendTitle != null && Enigma.MsgToSend.TryGetValue(pc.PlayerId, out string value4)) AddMsg(value4, pc.PlayerId, CustomRoles.Enigma.ColoredTextByRole(Enigma.MsgToSendTitle[pc.PlayerId])); if (QuizMaster.On && QuizMaster.MessagesToSend.TryGetValue(pc.PlayerId, out string value5)) { @@ -860,11 +860,12 @@ internal static class MeetingHudStartPatch if (MsgToSend.Count > 0) LateTask.New(() => MsgToSend.Do(x => Utils.SendMessage(x.Text, x.SendTo, x.Title, importance: MessageImportance.High)), 9f, "Meeting Start Notify"); Main.SuperStarDead.Clear(); - Forensic.ForensicNotify.Clear(); + Forensic.ForensicNotify = null; Main.SleuthMsgs.Clear(); Virus.VirusNotify.Clear(); - Mortician.MsgToSend.Clear(); - Enigma.MsgToSend.Clear(); + Mortician.MsgToSend = null; + Enigma.MsgToSend = null; + Enigma.MsgToSendTitle = null; return; static void AddMsg(string text, byte sendTo = 255, string title = "") => MsgToSend.Add(new(text, sendTo, title)); @@ -930,14 +931,14 @@ internal static class MeetingHudStartPatch (Main.PlayerStates[target.PlayerId].deathReason == PlayerState.DeathReason.Vote && Options.SeeEjectedRolesInMeeting.GetBool()) || (CustomTeamManager.AreInSameCustomTeam(target.PlayerId, seer.PlayerId) && CustomTeamManager.IsSettingEnabledForPlayerTeam(target.PlayerId, CTAOption.KnowRoles)) || Main.PlayerStates.Values.Any(x => x.Role.KnowRole(seer, target)) || - Markseeker.PlayerIdList.Any(x => Main.PlayerStates[x].Role is Markseeker { IsEnable: true, TargetRevealed: true } ms && ms.MarkedId == target.PlayerId) || + (Markseeker.PlayerIdList != null && Markseeker.PlayerIdList.Any(x => Main.PlayerStates[x].Role is Markseeker { IsEnable: true, TargetRevealed: true } ms && ms.MarkedId == target.PlayerId)) || seer.IsRevealedPlayer(target) || (seer.Is(CustomRoles.God) && God.KnowInfo.GetValue() == 2) || seer.Is(CustomRoles.GM) || Main.GodMode.Value; - if (seer.IsAlive() && seer.IsRevealedPlayer(target) && target.Is(CustomRoles.Trickster)) + if (seer.IsAlive() && Investigator.RandomRole != null && seer.IsRevealedPlayer(target) && target.Is(CustomRoles.Trickster)) { roleTextMeeting.text = Investigator.RandomRole[seer.PlayerId]; roleTextMeeting.text += Investigator.GetTaskState(); diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs index 2d1c291e9..f4e9b39ee 100644 --- a/Patches/OnGameStartedPatch.cs +++ b/Patches/OnGameStartedPatch.cs @@ -132,7 +132,7 @@ internal static class ChangeRoleSettings Nemesis.NemesisRevenged = []; Warlock.IsCurseAndKill = []; Warlock.IsCursed = false; - Forensic.ForensicNotify = []; + Forensic.ForensicNotify = null; Provocateur.Provoked = []; Crusader.ForCrusade = []; Godfather.GodfatherTarget = byte.MaxValue; diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index cd78d2b56..ebfc79781 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -704,7 +704,9 @@ internal static class MurderPlayerPatch if (target.Is(CustomRoles.Stained)) Stained.OnDeath(target, killer); + if (Witness.On) { + Witness.AllKillers ??= []; Witness.AllKillers.Add(killer.PlayerId); PlayerControl killer1 = killer; LateTask.New(() => Witness.AllKillers.Remove(killer1.PlayerId), Options.WitnessTime.GetInt(), log: false); @@ -1110,7 +1112,7 @@ internal static class ReportDeadBodyPatch if (target) { - if (Coroner.UnreportablePlayers.Contains(target.PlayerId) + if ((Coroner.UnreportablePlayers != null && Coroner.UnreportablePlayers.Contains(target.PlayerId)) || Vulture.UnreportablePlayers.Contains(target.PlayerId) || (killer && killer.Is(CustomRoles.Goddess)) || Main.PlayerStates[target.PlayerId].deathReason == PlayerState.DeathReason.Gambled @@ -1455,18 +1457,21 @@ internal static class ReportDeadBodyPatch Main.LastVotedPlayerInfo = null; Arsonist.ArsonistTimer.Clear(); - Investigator.InvestigatorTimer.Clear(); + Investigator.InvestigatorTimer = null; Puppeteer.PuppeteerList.Clear(); Puppeteer.PuppeteerDelayList.Clear(); Veteran.VeteranInProtect.Clear(); Grenadier.GrenadierBlinding.Clear(); SecurityGuard.BlockSabo.Clear(); Grenadier.MadGrenadierBlinding.Clear(); - FortuneTeller.DidVote.Clear(); - Oracle.DidVote.Clear(); + FortuneTeller.DidVote = null; + Oracle.DidVote = null; - Imitator.ImitatingRole.ToArray().DoIf(x => !Main.PlayerStates.TryGetValue(x.Key, out var s) || s.MainRole != x.Value, x => Imitator.ImitatingRole.Remove(x.Key)); - Imitator.ImitatingRole.SetAllValues(CustomRoles.Imitator); + if (Imitator.ImitatingRole != null) + { + Imitator.ImitatingRole.ToArray().DoIf(x => !Main.PlayerStates.TryGetValue(x.Key, out var s) || s.MainRole != x.Value, x => Imitator.ImitatingRole.Remove(x.Key)); + Imitator.ImitatingRole.SetAllValues(CustomRoles.Imitator); + } foreach (PlayerState state in Main.PlayerStates.Values) { @@ -1881,7 +1886,7 @@ internal static class FixedUpdatePatch roleText = ColorString(roleTextData.Item2, roleTextData.Item1); } - if (!hideRoleText && PlayerControl.LocalPlayer.IsAlive() && PlayerControl.LocalPlayer.IsRevealedPlayer(player) && player.Is(CustomRoles.Trickster)) + if (!hideRoleText && PlayerControl.LocalPlayer.IsAlive() && Investigator.RandomRole != null && PlayerControl.LocalPlayer.IsRevealedPlayer(player) && player.Is(CustomRoles.Trickster)) { roleText = Investigator.RandomRole[lpId]; roleText += Investigator.GetTaskState(); diff --git a/Roles/RoleBase.cs b/Roles/RoleBase.cs index 537884a7a..8ce6edec8 100644 --- a/Roles/RoleBase.cs +++ b/Roles/RoleBase.cs @@ -128,7 +128,7 @@ public abstract class RoleBase : IComparable public virtual void OnVoteKick(PlayerControl pc, PlayerControl target) { - if (Imitator.PlayerIdList.Contains(pc.PlayerId)) + if (Imitator.PlayerIdList != null && Imitator.PlayerIdList.Contains(pc.PlayerId)) { string command = $"/imitate {target.PlayerId}"; ChatCommands.ImitateCommand(pc, command, command.Split(' ')); diff --git a/Roles/Standard/Crewmate/Chaos/Randomizer.cs b/Roles/Standard/Crewmate/Chaos/Randomizer.cs index 9451145c7..ff21ffe29 100644 --- a/Roles/Standard/Crewmate/Chaos/Randomizer.cs +++ b/Roles/Standard/Crewmate/Chaos/Randomizer.cs @@ -473,7 +473,7 @@ internal class Randomizer : RoleBase public static Stopwatch TimeSinceLastMeeting; private static Dictionary LastTP; - private static readonly List ToRemoveEffects; + private static List ToRemoveEffects; private static long LastDeathEffect; public static bool Exists; diff --git a/Roles/Standard/Crewmate/Investigate/Analyst.cs b/Roles/Standard/Crewmate/Investigate/Analyst.cs index 53895d730..4bcfd2382 100644 --- a/Roles/Standard/Crewmate/Investigate/Analyst.cs +++ b/Roles/Standard/Crewmate/Investigate/Analyst.cs @@ -21,7 +21,7 @@ internal class Analyst : RoleBase private static readonly Dictionary ReplacementDict = new() { { "Analyze", CustomRoles.Analyst.ColoredTextByRole("Analyze") } }; - public static Dictionary VentCount = []; + private static Dictionary VentCount; public (byte ID, long TIME) CurrentTarget = (byte.MaxValue, Utils.TimeStamp); public override bool IsEnable => PlayerId != byte.MaxValue; @@ -95,7 +95,7 @@ internal class Analyst : RoleBase private static int GetVentCount(byte id) { - return SeeVentCount.GetBool() ? VentCount.GetValueOrDefault(id, 0) : 0; + return SeeVentCount.GetBool() && VentCount != null ? VentCount.GetValueOrDefault(id, 0) : 0; } private static string GetAnalyzeResult(PlayerControl pc) @@ -106,7 +106,7 @@ internal class Analyst : RoleBase public override void Init() { PlayerId = byte.MaxValue; - VentCount = []; + VentCount = null; CurrentTarget = (byte.MaxValue, Utils.TimeStamp); } @@ -126,6 +126,8 @@ internal class Analyst : RoleBase { if (!AmongUsClient.Instance.AmHost) return; + VentCount ??= []; + if (!VentCount.TryAdd(pc.PlayerId, 1)) VentCount[pc.PlayerId]++; } diff --git a/Roles/Standard/Crewmate/Investigate/Coroner.cs b/Roles/Standard/Crewmate/Investigate/Coroner.cs index 349278e4c..a10132d4e 100644 --- a/Roles/Standard/Crewmate/Investigate/Coroner.cs +++ b/Roles/Standard/Crewmate/Investigate/Coroner.cs @@ -10,9 +10,9 @@ using static Translator; public class Coroner : RoleBase { private const int Id = 6400; - private static List PlayerIdList = []; + private static List PlayerIdList; - public static List UnreportablePlayers = []; + public static List UnreportablePlayers; public static OptionItem ArrowsPointingToDeadBody; public static OptionItem UseLimitOpt; @@ -24,7 +24,7 @@ public class Coroner : RoleBase private byte CoronerId; private List CoronerTargets = []; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => PlayerIdList is { Count: > 0 }; public override bool SeesArrowsToDeadBodies => ArrowsPointingToDeadBody.GetBool(); @@ -56,12 +56,13 @@ public class Coroner : RoleBase public override void Init() { - PlayerIdList = []; - UnreportablePlayers = []; + PlayerIdList = null; + UnreportablePlayers = null; } public override void Add(byte playerId) { + PlayerIdList ??= []; PlayerIdList.Add(playerId); playerId.SetAbilityUseLimit(UseLimitOpt.GetFloat()); CoronerTargets = []; @@ -70,15 +71,18 @@ public class Coroner : RoleBase public override void Remove(byte playerId) { - PlayerIdList.Remove(playerId); + PlayerIdList?.Remove(playerId); } public override void OnReportDeadBody() { - foreach (byte id in PlayerIdList) + if (PlayerIdList != null) { - TargetArrow.RemoveAllTarget(id); - LocateArrow.RemoveAllTarget(id); + foreach (byte id in PlayerIdList) + { + TargetArrow.RemoveAllTarget(id); + LocateArrow.RemoveAllTarget(id); + } } CoronerTargets.Clear(); @@ -107,7 +111,11 @@ public class Coroner : RoleBase pc.Notify(GetString("CoronerTrackRecorded")); pc.RpcRemoveAbilityUse(); - if (LeaveDeadBodyUnreportable.GetBool()) UnreportablePlayers.Add(target.PlayerId); + if (LeaveDeadBodyUnreportable.GetBool()) + { + UnreportablePlayers ??= []; + UnreportablePlayers.Add(target.PlayerId); + } if (NotifyKiller.GetBool()) killer.Notify(GetString("CoronerKillerNotify")); } diff --git a/Roles/Standard/Crewmate/Investigate/DoubleAgent.cs b/Roles/Standard/Crewmate/Investigate/DoubleAgent.cs index 5d6b9ed4a..df09b9492 100644 --- a/Roles/Standard/Crewmate/Investigate/DoubleAgent.cs +++ b/Roles/Standard/Crewmate/Investigate/DoubleAgent.cs @@ -9,7 +9,7 @@ public class DoubleAgent : RoleBase private const int Id = 645175; public static bool On; - public static Dictionary ShownRoles = []; + public static Dictionary ShownRoles; public override bool IsEnable => On; @@ -21,12 +21,13 @@ public class DoubleAgent : RoleBase public override void Init() { On = false; - ShownRoles = []; + ShownRoles = null; } public override void Add(byte playerId) { On = true; + ShownRoles ??= []; ShownRoles[playerId] = Main.CustomRoleValues.Where(x => x is not CustomRoles.DoubleAgent and not CustomRoles.LovingImpostor && x.IsImpostor() && !x.IsVanilla() && !x.IsForOtherGameMode() && x.GetMode() != 0).RandomElement(); } @@ -36,6 +37,6 @@ public class DoubleAgent : RoleBase public override bool KnowRole(PlayerControl seer, PlayerControl target) { if (base.KnowRole(seer, target)) return true; - return seer.IsImpostor() && ImpKnowAlliesRole.GetBool() && ShownRoles.ContainsKey(target.PlayerId); + return seer.IsImpostor() && ImpKnowAlliesRole.GetBool() && ShownRoles != null && ShownRoles.ContainsKey(target.PlayerId); } } \ No newline at end of file diff --git a/Roles/Standard/Crewmate/Investigate/Druid.cs b/Roles/Standard/Crewmate/Investigate/Druid.cs index 5a69e9df9..ba151ceee 100644 --- a/Roles/Standard/Crewmate/Investigate/Druid.cs +++ b/Roles/Standard/Crewmate/Investigate/Druid.cs @@ -14,7 +14,7 @@ namespace EHR.Roles; public class Druid : RoleBase { private const int Id = 642800; - private static List PlayerIdList = []; + private static bool On; public static OptionItem VentCooldown; private static OptionItem TriggerPlaceDelay; @@ -29,7 +29,7 @@ public class Druid : RoleBase private readonly StringBuilder Suffix = new(); private readonly StringBuilder HudText = new(); - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => On; public override void SetupCustomOption() { @@ -58,12 +58,12 @@ public class Druid : RoleBase public override void Init() { - PlayerIdList = []; + On = false; } public override void Add(byte playerId) { - PlayerIdList.Add(playerId); + On = true; DruidPC = GetPlayerById(playerId); playerId.SetAbilityUseLimit(UseLimitOpt.GetFloat()); TriggerIds = []; @@ -71,11 +71,6 @@ public class Druid : RoleBase DelayTimer = null; } - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - public override void ApplyGameOptions(IGameOptions opt, byte playerId) { AURoleOptions.EngineerCooldown = VentCooldown.GetInt(); @@ -166,7 +161,7 @@ public class Druid : RoleBase public override void OnCheckPlayerPosition(PlayerControl pc) { - if (!GameStates.IsInTask || Triggers.Count <= 0 || PlayerIdList.Contains(pc.PlayerId)) return; + if (!GameStates.IsInTask || Triggers.Count <= 0 || pc.Is(CustomRoles.Druid)) return; List toRemove = null; diff --git a/Roles/Standard/Crewmate/Investigate/Enigma.cs b/Roles/Standard/Crewmate/Investigate/Enigma.cs index d924231db..6cb14dbb3 100644 --- a/Roles/Standard/Crewmate/Investigate/Enigma.cs +++ b/Roles/Standard/Crewmate/Investigate/Enigma.cs @@ -8,8 +8,8 @@ namespace EHR.Roles; public class Enigma : RoleBase { private const int Id = 8460; - public static List PlayerIdList = []; - private static Dictionary> ShownClues = []; + private static List PlayerIdList; + private static Dictionary> ShownClues; private static OptionItem EnigmaClueStage1Tasks; private static OptionItem EnigmaClueStage2Tasks; @@ -18,8 +18,8 @@ public class Enigma : RoleBase private static OptionItem EnigmaClueStage3Probability; private static OptionItem EnigmaGetCluesWithoutReporting; - public static Dictionary MsgToSend = []; - public static Dictionary MsgToSendTitle = []; + public static Dictionary MsgToSend; + public static Dictionary MsgToSendTitle; private static readonly List EnigmaClues = [ @@ -49,7 +49,7 @@ public class Enigma : RoleBase new EnigmaFriendCodeClue { ClueStage = 3, EnigmaClueType = EnigmaClueType.FriendCodeClue } ]; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => PlayerIdList is { Count: > 0 }; public override void SetupCustomOption() { @@ -83,32 +83,34 @@ public class Enigma : RoleBase public override void Init() { - PlayerIdList = []; - ShownClues = []; - MsgToSend = []; - MsgToSendTitle = []; + PlayerIdList = null; + ShownClues = null; + MsgToSend = null; + MsgToSendTitle = null; } public override void Add(byte playerId) { + PlayerIdList ??= []; PlayerIdList.Add(playerId); + ShownClues ??= []; ShownClues[playerId] = []; } public override void Remove(byte playerId) { - PlayerIdList.Remove(playerId); + PlayerIdList?.Remove(playerId); } public static void OnReportDeadBody(PlayerControl player, NetworkedPlayerInfo targetInfo) { - if (targetInfo == null) return; + if (PlayerIdList == null || !targetInfo) return; PlayerControl target = Utils.GetPlayerById(targetInfo.PlayerId); - if (target == null) return; + if (!target) return; PlayerControl killer = target.GetRealKiller(); - if (killer == null) return; + if (!killer) return; var rd = IRandom.Instance; @@ -135,6 +137,7 @@ public class Enigma : RoleBase } else if (tasksCompleted >= EnigmaClueStage1Tasks.GetInt()) stage = 1; + ShownClues ??= []; List clues = EnigmaClues.FindAll(a => a.ClueStage <= stage && !ShownClues[playerId].Any(b => b.EnigmaClueType == a.EnigmaClueType && b.ClueStage == a.ClueStage)); if (clues.Count == 0) continue; @@ -146,9 +149,9 @@ public class Enigma : RoleBase string msg = clue.GetMessage(killer, showStageClue); ShownClues[playerId].Add(clue); - + MsgToSend ??= []; MsgToSend[playerId] = msg; - + MsgToSendTitle ??= []; MsgToSendTitle[playerId] = title; } } diff --git a/Roles/Standard/Crewmate/Investigate/Forensic.cs b/Roles/Standard/Crewmate/Investigate/Forensic.cs index f886460ab..c952a0f35 100644 --- a/Roles/Standard/Crewmate/Investigate/Forensic.cs +++ b/Roles/Standard/Crewmate/Investigate/Forensic.cs @@ -13,7 +13,7 @@ internal class Forensic : RoleBase public static OptionItem ForensicCanknowColorType; public static OptionItem ForensicCanknowColorTypeMinBodyAge; - public static Dictionary ForensicNotify = []; + public static Dictionary ForensicNotify; public override bool IsEnable => false; @@ -82,6 +82,7 @@ internal class Forensic : RoleBase msg += ";" + string.Format(Translator.GetString("ForensicNoticeColorType"), str, colors); } + ForensicNotify ??= []; ForensicNotify[player.PlayerId] = msg; } } \ No newline at end of file diff --git a/Roles/Standard/Crewmate/Investigate/FortuneTeller.cs b/Roles/Standard/Crewmate/Investigate/FortuneTeller.cs index 53f457b14..bce1dcdf5 100644 --- a/Roles/Standard/Crewmate/Investigate/FortuneTeller.cs +++ b/Roles/Standard/Crewmate/Investigate/FortuneTeller.cs @@ -11,7 +11,7 @@ public class FortuneTeller : RoleBase { private const int Id = 6700; private static int RolesPerCategory; - private static List PlayerIdList = []; + private static bool On; public static OptionItem CheckLimitOpt; public static OptionItem AccurateCheckMode; @@ -22,11 +22,11 @@ public class FortuneTeller : RoleBase public static OptionItem AbilityChargesWhenFinishedTasks; public static OptionItem CancelVote; - public static readonly List DidVote = []; + public static List DidVote; - private static Dictionary> AllPlayerRoleList = []; + private static Dictionary> AllPlayerRoleList; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => On; public override void SetupCustomOption() { @@ -62,14 +62,14 @@ public class FortuneTeller : RoleBase public override void Init() { - PlayerIdList = []; - AllPlayerRoleList = []; + On = false; + AllPlayerRoleList = null; RolesPerCategory = NumRolesListedForEachPlayer.GetInt(); } public override void Add(byte playerId) { - PlayerIdList.Add(playerId); + On = true; playerId.SetAbilityUseLimit(CheckLimitOpt.GetFloat()); LateTask.New(() => @@ -93,18 +93,13 @@ public class FortuneTeller : RoleBase }, 8f, log: false); } - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - public override bool OnVote(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (player == null || target == null) return false; - if (DidVote.Contains(player.PlayerId) || Main.DontCancelVoteList.Contains(player.PlayerId)) return false; - + DidVote ??= []; + if (DidVote.Contains(player.PlayerId) || Main.DontCancelVoteList.Contains(player.PlayerId) || AllPlayerRoleList == null) return false; DidVote.Add(player.PlayerId); if (player.GetAbilityUseLimit() < 1) @@ -146,7 +141,7 @@ public class FortuneTeller : RoleBase { try { - if (!AllPlayerRoleList.TryGetValue(id, out List list)) return; + if (AllPlayerRoleList == null || !AllPlayerRoleList.TryGetValue(id, out List list)) return; int index = list.IndexOf(previousRole); list.Remove(previousRole); diff --git a/Roles/Standard/Crewmate/Investigate/Hacker.cs b/Roles/Standard/Crewmate/Investigate/Hacker.cs index 85f57c82d..e8ad48b9a 100644 --- a/Roles/Standard/Crewmate/Investigate/Hacker.cs +++ b/Roles/Standard/Crewmate/Investigate/Hacker.cs @@ -15,9 +15,9 @@ using static Utils; public class Hacker : RoleBase { private const int Id = 641000; - public static Dictionary PlayerIdList = []; - public static Dictionary UseLimit = []; - public static Dictionary UseLimitSeconds = []; + public static Dictionary PlayerIdList; + public static Dictionary UseLimit; + public static Dictionary UseLimitSeconds; public static OptionItem AbilityCD; public static OptionItem UseLimitOpt; @@ -27,7 +27,7 @@ public class Hacker : RoleBase public static OptionItem ModdedClientCanMoveWhileViewingMap; public static OptionItem VanillaClientSeesInfoFor; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => PlayerIdList is { Count: > 0 }; public override void SetupCustomOption() { @@ -63,13 +63,17 @@ public class Hacker : RoleBase public override void Init() { - PlayerIdList = []; - UseLimit = []; - UseLimitSeconds = []; + PlayerIdList = null; + UseLimit = null; + UseLimitSeconds = null; } public override void Add(byte playerId) { + PlayerIdList ??= []; + UseLimit ??= []; + UseLimitSeconds ??= []; + PlayerIdList.TryAdd(playerId, GetPlayerById(playerId).IsModdedClient()); if (!GetPlayerById(playerId).IsModdedClient()) @@ -80,7 +84,7 @@ public class Hacker : RoleBase public override void Remove(byte playerId) { - PlayerIdList.Remove(playerId); + PlayerIdList?.Remove(playerId); } public override void ApplyGameOptions(IGameOptions opt, byte playerId) @@ -108,6 +112,7 @@ public class Hacker : RoleBase byte playerId = reader.ReadByte(); float secondsLeft = reader.ReadSingle(); + UseLimitSeconds ??= []; UseLimitSeconds[playerId] = secondsLeft; } @@ -124,9 +129,9 @@ public class Hacker : RoleBase private static void UseAbility(PlayerControl pc, bool pet) { - if (pc == null) return; + if (!pc) return; - if (pc.IsModdedClient() || !UseLimit.ContainsKey(pc.PlayerId)) return; + if (pc.IsModdedClient() || UseLimit == null || !UseLimit.ContainsKey(pc.PlayerId)) return; if (UseLimit[pc.PlayerId] >= 1) { @@ -145,7 +150,7 @@ public class Hacker : RoleBase public override void OnTaskComplete(PlayerControl pc, int completedTaskCount, int totalTaskCount) { - if (!pc.IsAlive()) return; + if (!pc.IsAlive() || UseLimit == null || UseLimitSeconds == null) return; if (completedTaskCount + 1 >= totalTaskCount) { @@ -213,7 +218,7 @@ public class Hacker : RoleBase public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false) { - if (!hud || seer == null) return string.Empty; + if (!hud || UseLimitSeconds == null) return string.Empty; return !seer.Is(CustomRoles.Hacker) ? string.Empty : $"{GetString("HackerAbilitySecondsLeft")}: {(int)UseLimitSeconds[seer.PlayerId]}s"; } @@ -221,7 +226,7 @@ public class Hacker : RoleBase public override void GetProgressText(byte playerId, bool comms, StringBuilder resultText) { if (playerId.IsPlayerModdedClient()) return; - if (!UseLimit.TryGetValue(playerId, out float limit)) return; + if (UseLimit == null || !UseLimit.TryGetValue(playerId, out float limit)) return; resultText.Append(GetTaskCount(playerId, comms)) .Append(" - ") diff --git a/Roles/Standard/Crewmate/Investigate/Ignitor.cs b/Roles/Standard/Crewmate/Investigate/Ignitor.cs index f1461452c..5fc38b902 100644 --- a/Roles/Standard/Crewmate/Investigate/Ignitor.cs +++ b/Roles/Standard/Crewmate/Investigate/Ignitor.cs @@ -10,7 +10,7 @@ using static Options; public class Ignitor : RoleBase // Candle Lighter from TOHY { private const int Id = 5280; - private static List PlayerIdList = []; + private static bool On; private static OptionItem OptionTaskStartVision; private static OptionItem OptionCountStartTime; @@ -18,12 +18,12 @@ public class Ignitor : RoleBase // Candle Lighter from TOHY private static OptionItem OptionTaskEndVision; private static OptionItem OptionTaskTimeMoveMeeting; private static OptionItem OptionTasksFinishedVision; + private bool Active = true; private float ElapsedTime; - private float UpdateTime; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => On; public override void SetupCustomOption() { @@ -57,23 +57,18 @@ public class Ignitor : RoleBase // Candle Lighter from TOHY public override void Init() { - PlayerIdList = []; + On = false; Active = true; } public override void Add(byte playerId) { - PlayerIdList.Add(playerId); + On = true; Active = true; UpdateTime = 1.0f; ElapsedTime = OptionTaskEndVisionTime.GetInt() + OptionCountStartTime.GetInt(); } - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - public override void ApplyGameOptions(IGameOptions opt, byte id) { float vision; diff --git a/Roles/Standard/Crewmate/Investigate/Imitator.cs b/Roles/Standard/Crewmate/Investigate/Imitator.cs index 5d8956a31..8c3753558 100644 --- a/Roles/Standard/Crewmate/Investigate/Imitator.cs +++ b/Roles/Standard/Crewmate/Investigate/Imitator.cs @@ -8,8 +8,8 @@ namespace EHR.Roles; public class Imitator : RoleBase { public static bool On; - public static List PlayerIdList = []; - public static Dictionary ImitatingRole = []; + public static List PlayerIdList; + public static Dictionary ImitatingRole; public override bool IsEnable => On; @@ -23,19 +23,23 @@ public class Imitator : RoleBase { if (GameStates.InGame && !Main.HasJustStarted) return; On = false; - PlayerIdList = []; - ImitatingRole = []; + PlayerIdList = null; + ImitatingRole = null; } public override void Add(byte playerId) { On = true; + ImitatingRole ??= []; ImitatingRole[playerId] = CustomRoles.Imitator; + PlayerIdList ??= []; PlayerIdList.Add(playerId); } public static void SetRoles() { + if (PlayerIdList == null || ImitatingRole == null) return; + foreach (byte id in PlayerIdList) { PlayerControl pc = id.GetPlayer(); @@ -62,7 +66,7 @@ public class Imitator : RoleBase { ChatCommands.ImitateCommand(PlayerControl.LocalPlayer, command, command.Split(' ')); - if (ImitatingRole.ContainsKey(PlayerControl.LocalPlayer.PlayerId)) + if (ImitatingRole != null && ImitatingRole.ContainsKey(PlayerControl.LocalPlayer.PlayerId)) { foreach (PlayerVoteArea pva in MeetingHud.Instance.playerStates) { @@ -99,7 +103,7 @@ public class Imitator : RoleBase { public static void Postfix(MeetingHud __instance) { - if (PlayerIdList.Contains(PlayerControl.LocalPlayer.PlayerId) && PlayerControl.LocalPlayer.IsAlive()) + if (PlayerIdList != null && PlayerIdList.Contains(PlayerControl.LocalPlayer.PlayerId) && PlayerControl.LocalPlayer.IsAlive()) CreateImitatorButton(__instance); } } diff --git a/Roles/Standard/Crewmate/Investigate/Inspector.cs b/Roles/Standard/Crewmate/Investigate/Inspector.cs index f6d48d203..4a98ffbe8 100644 --- a/Roles/Standard/Crewmate/Investigate/Inspector.cs +++ b/Roles/Standard/Crewmate/Investigate/Inspector.cs @@ -12,9 +12,9 @@ namespace EHR.Roles; public class Inspector : RoleBase { private const int Id = 6900; - private static List PlayerIdList = []; - private static Dictionary RoundCheckLimit = []; - private static Dictionary FirstPick = []; + private static List PlayerIdList; + private static Dictionary RoundCheckLimit; + private static Dictionary FirstPick; private static readonly string[] InspectorEgoistCountMode = [ @@ -32,7 +32,7 @@ public class Inspector : RoleBase public static OptionItem InspectorAbilityUseGainWithEachTaskCompleted; public static OptionItem InspectorChargesWhenFinishedTasks; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => PlayerIdList is { Count: > 0 }; public override void SetupCustomOption() { @@ -79,27 +79,29 @@ public class Inspector : RoleBase public override void Init() { - PlayerIdList = []; - RoundCheckLimit = []; - FirstPick = []; + PlayerIdList = null; + RoundCheckLimit = null; + FirstPick = null; } public override void Add(byte playerId) { + PlayerIdList ??= []; PlayerIdList.Add(playerId); playerId.SetAbilityUseLimit(InspectorCheckLimitMax.GetFloat()); + RoundCheckLimit ??= []; RoundCheckLimit.Add(playerId, InspectorCheckLimitPerMeeting.GetInt()); } public override void Remove(byte playerId) { - PlayerIdList.Remove(playerId); + PlayerIdList?.Remove(playerId); } public override void OnReportDeadBody() { RoundCheckLimit.Clear(); - foreach (byte pc in PlayerIdList) RoundCheckLimit.Add(pc, InspectorCheckLimitPerMeeting.GetInt()); + if (PlayerIdList != null) foreach (byte pc in PlayerIdList) RoundCheckLimit.Add(pc, InspectorCheckLimitPerMeeting.GetInt()); } public static bool InspectorCheckMsg(PlayerControl pc, string msg, bool isUI = false) @@ -144,7 +146,7 @@ public class Inspector : RoleBase bool outOfUses = pc.GetAbilityUseLimit() < 1; - if (outOfUses || RoundCheckLimit[pc.PlayerId] < 1) + if (outOfUses || RoundCheckLimit == null || RoundCheckLimit[pc.PlayerId] < 1) { if (outOfUses) { @@ -400,6 +402,8 @@ public class Inspector : RoleBase { PlayerControl pc = Utils.GetPlayerById(playerId); if (pc == null || !pc.IsAlive() || !GameStates.IsVoting || Starspawn.IsDayBreak) return; + + FirstPick ??= []; if (FirstPick.TryGetValue(lpcId, out byte firstPick)) { diff --git a/Roles/Standard/Crewmate/Investigate/Investigator.cs b/Roles/Standard/Crewmate/Investigate/Investigator.cs index 8a2a72fbc..d6d82dc6f 100644 --- a/Roles/Standard/Crewmate/Investigate/Investigator.cs +++ b/Roles/Standard/Crewmate/Investigate/Investigator.cs @@ -14,8 +14,8 @@ public class Investigator : RoleBase private const int Id = 9700; private const string FontSize = "1.7"; - public static Dictionary InvestigatorTimer = []; - public static Dictionary<(byte, byte), bool> IsRevealed = []; + public static Dictionary InvestigatorTimer; + public static Dictionary<(byte, byte), bool> IsRevealed; public static byte CurrentRevealTarget = byte.MaxValue; private static OptionItem InvestigatorCooldown; @@ -51,18 +51,21 @@ public class Investigator : RoleBase public override void Init() { On = false; - InvestigatorTimer = []; - IsRevealed = []; - RandomRole = []; + InvestigatorTimer = null; + IsRevealed = null; + RandomRole = null; } public override void Add(byte playerId) { On = true; + IsRevealed ??= []; + foreach (PlayerControl ar in Main.CachedAllPlayerControls()) IsRevealed[(playerId, ar.PlayerId)] = false; + RandomRole ??= []; RandomRole[playerId] = GetRandomCrewRoleString(); } @@ -88,6 +91,9 @@ public class Investigator : RoleBase float revealTime = InvestigatorTime.GetFloat(); killer.SetKillCooldown(revealTime == 0f ? InvestigatorCooldown.GetFloat() : revealTime); + InvestigatorTimer ??= []; + IsRevealed ??= []; + if (!IsRevealed[(killer.PlayerId, target.PlayerId)] && !InvestigatorTimer.ContainsKey(killer.PlayerId)) { InvestigatorTimer.TryAdd(killer.PlayerId, (target, 0f)); @@ -100,7 +106,7 @@ public class Investigator : RoleBase public override void OnFixedUpdate(PlayerControl player) { - if (GameStates.IsInTask && InvestigatorTimer.ContainsKey(player.PlayerId)) + if (GameStates.IsInTask && InvestigatorTimer != null && IsRevealed != null && InvestigatorTimer.ContainsKey(player.PlayerId)) { if (!player.IsAliveWithConditions()) { @@ -149,7 +155,7 @@ public class Investigator : RoleBase public override bool KnowRole(PlayerControl seer, PlayerControl target) { if (base.KnowRole(seer, target)) return true; - return IsRevealed.GetValueOrDefault((seer.PlayerId, target.PlayerId)); + return IsRevealed != null && IsRevealed.GetValueOrDefault((seer.PlayerId, target.PlayerId)); } private static string GetRandomCrewRoleString() diff --git a/Roles/Standard/Crewmate/Investigate/Markseeker.cs b/Roles/Standard/Crewmate/Investigate/Markseeker.cs index 80a66a68b..ae265bb26 100644 --- a/Roles/Standard/Crewmate/Investigate/Markseeker.cs +++ b/Roles/Standard/Crewmate/Investigate/Markseeker.cs @@ -5,7 +5,7 @@ namespace EHR.Roles; internal class Markseeker : RoleBase { private const int Id = 643550; - public static List PlayerIdList = []; + public static List PlayerIdList; public static bool On; public static OptionItem CancelVote; @@ -24,18 +24,19 @@ internal class Markseeker : RoleBase On = true; MarkedId = byte.MaxValue; TargetRevealed = false; + PlayerIdList ??= []; PlayerIdList.Add(playerId); } public override void Remove(byte playerId) { - PlayerIdList.Remove(playerId); + PlayerIdList?.Remove(playerId); } public override void Init() { On = false; - PlayerIdList = []; + PlayerIdList = null; } public override bool OnVote(PlayerControl player, PlayerControl target) diff --git a/Roles/Standard/Crewmate/Investigate/Medium.cs b/Roles/Standard/Crewmate/Investigate/Medium.cs index dad04e94a..62bf13c27 100644 --- a/Roles/Standard/Crewmate/Investigate/Medium.cs +++ b/Roles/Standard/Crewmate/Investigate/Medium.cs @@ -7,16 +7,16 @@ namespace EHR.Roles; public class Medium : RoleBase { private const int Id = 7200; - public static List PlayerIdList = []; + private static bool On; private static OptionItem ContactLimitOpt; public static OptionItem OnlyReceiveMsgFromCrew; public static OptionItem MediumAbilityUseGainWithEachTaskCompleted; public static OptionItem AbilityChargesWhenFinishedTasks; - public static Dictionary ContactPlayer = []; + public static Dictionary ContactPlayer; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => On; public override void SetupCustomOption() { @@ -40,21 +40,16 @@ public class Medium : RoleBase public override void Init() { - PlayerIdList = []; - ContactPlayer = []; + On = false; + ContactPlayer = null; } public override void Add(byte playerId) { - PlayerIdList.Add(playerId); + On = true; playerId.SetAbilityUseLimit(ContactLimitOpt.GetFloat()); } - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - public static void OnReportDeadBody(NetworkedPlayerInfo target) { ContactPlayer = []; @@ -72,7 +67,7 @@ public class Medium : RoleBase public static bool MsMsg(PlayerControl pc, string msg) { - if (!AmongUsClient.Instance.AmHost || !GameStates.IsMeeting || !pc || pc.IsAlive() || !ContactPlayer.TryGetValue(pc.PlayerId, out var contact) || (OnlyReceiveMsgFromCrew.GetBool() && !pc.IsCrewmate())) return false; + if (!AmongUsClient.Instance.AmHost || !GameStates.IsMeeting || !pc || pc.IsAlive() || ContactPlayer == null || !ContactPlayer.TryGetValue(pc.PlayerId, out var contact) || (OnlyReceiveMsgFromCrew.GetBool() && !pc.IsCrewmate())) return false; msg = msg.ToLower().Trim(); if (!CheckCommand(ref msg, "通灵|ms|medium", false)) return false; diff --git a/Roles/Standard/Crewmate/Investigate/MeetingManager.cs b/Roles/Standard/Crewmate/Investigate/MeetingManager.cs index 11e43dd17..cfac638b2 100644 --- a/Roles/Standard/Crewmate/Investigate/MeetingManager.cs +++ b/Roles/Standard/Crewmate/Investigate/MeetingManager.cs @@ -5,9 +5,9 @@ namespace EHR.Roles; public class MeetingManager : RoleBase { - public static List PlayerIdList = []; + private static List PlayerIdList; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => PlayerIdList is { Count: > 0 }; public override void SetupCustomOption() { @@ -16,45 +16,51 @@ public class MeetingManager : RoleBase public override void Init() { - PlayerIdList = []; + PlayerIdList = null; } public override void Add(byte playerId) { + PlayerIdList ??= []; PlayerIdList.Add(playerId); } public override void Remove(byte playerId) { - PlayerIdList.Remove(playerId); + PlayerIdList?.Remove(playerId); } public static void SendCommandUsedMessage(string command) { + if (PlayerIdList == null) return; foreach (byte id in PlayerIdList) LateTask.New(() => Utils.SendMessage(string.Format(GetString("MeetingManagerMessageAboutCommand"), command), id, CustomRoles.MeetingManager.ColoredTextByRole(GetString("MeetingManagerMessageTitle")), importance: MessageImportance.High), 1f, "Meeting Manager Messages"); } public static void OnGuess(PlayerControl dp, PlayerControl pc) { + if (PlayerIdList == null) return; foreach (byte id in PlayerIdList) LateTask.New(() => Utils.SendMessage(dp == pc ? string.Format(GetString("MeetingManagerMessageAboutMisguess"), dp.GetRealName().Replace("\n", " + ")) : string.Format(GetString("MeetingManagerMessageAboutGuessedRole"), dp.GetAllRoleName().Replace("\n", " + ")), id, CustomRoles.MeetingManager.ColoredTextByRole(GetString("MeetingManagerMessageTitle")), importance: MessageImportance.High), 1f, "Meeting Manager Messages"); } public static void OnTrial(PlayerControl dp, PlayerControl pc) { + if (PlayerIdList == null) return; foreach (byte id in PlayerIdList) LateTask.New(() => Utils.SendMessage(dp == pc ? string.Format(GetString("MeetingManagerMessageAboutJudgeSuicide"), dp.GetRealName().Replace("\n", " + "), CustomRoles.Judge.ToColoredString()) : string.Format(GetString("MeetingManagerMessageAboutGuessedRole"), dp.GetAllRoleName().Replace("\n", " + ")), id, CustomRoles.MeetingManager.ColoredTextByRole(GetString("MeetingManagerMessageTitle")), importance: MessageImportance.High), 1f, "Meeting Manager Messages"); } public static void OnSwap(PlayerControl tg1, PlayerControl tg2) { + if (PlayerIdList == null) return; foreach (byte id in PlayerIdList) LateTask.New(() => Utils.SendMessage(string.Format(GetString("MeetingManagerMessageAboutSwap"), CustomRoles.Swapper.ToColoredString(), tg1.GetRealName().Replace("\n", " + "), tg2.GetRealName().Replace("\n", " + ")), id, CustomRoles.MeetingManager.ColoredTextByRole(GetString("MeetingManagerMessageTitle")), importance: MessageImportance.High), 1f, "Meeting Manager Messages"); } public static void OnCompare(PlayerControl tg1, PlayerControl tg2) { + if (PlayerIdList == null) return; foreach (byte id in PlayerIdList) LateTask.New(() => Utils.SendMessage(string.Format(GetString("MeetingManagerMessageAboutCompare"), CustomRoles.Inspector.ToColoredString(), tg1.GetRealName().Replace("\n", " + "), tg2.GetRealName().Replace("\n", " + ")), id, CustomRoles.MeetingManager.ColoredTextByRole(GetString("MeetingManagerMessageTitle")), importance: MessageImportance.High), 1f, "Meeting Manager Messages"); } diff --git a/Roles/Standard/Crewmate/Investigate/Mortician.cs b/Roles/Standard/Crewmate/Investigate/Mortician.cs index d7914fbc4..4bd507720 100644 --- a/Roles/Standard/Crewmate/Investigate/Mortician.cs +++ b/Roles/Standard/Crewmate/Investigate/Mortician.cs @@ -7,16 +7,16 @@ namespace EHR.Roles; public class Mortician : RoleBase { private const int Id = 7400; - public static List PlayerIdList = []; + private static List PlayerIdList; private static OptionItem ShowArrows; - private static Dictionary LastPlayerName = []; - public static Dictionary MsgToSend = []; + private static Dictionary LastPlayerName; + public static Dictionary MsgToSend; private byte MorticianId; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => PlayerIdList is { Count: > 0 }; public override bool SeesArrowsToDeadBodies => ShowArrows.GetBool(); @@ -30,34 +30,37 @@ public class Mortician : RoleBase public override void Init() { - PlayerIdList = []; - LastPlayerName = []; - MsgToSend = []; + PlayerIdList = null; + LastPlayerName = null; + MsgToSend = null; } public override void Add(byte playerId) { + PlayerIdList ??= []; PlayerIdList.Add(playerId); MorticianId = playerId; } public override void Remove(byte playerId) { - PlayerIdList.Remove(playerId); + PlayerIdList?.Remove(playerId); } public static void OnPlayerDead(PlayerControl target) { + LastPlayerName ??= []; LastPlayerName.TryAdd(target.PlayerId, FastVector2.TryGetClosestPlayerInRangeTo(target, 4f, out PlayerControl closest) ? closest.GetRealName() : string.Empty); } public static void OnReportDeadBody(PlayerControl pc, NetworkedPlayerInfo target) { - foreach (byte apc in PlayerIdList) LocateArrow.RemoveAllTarget(apc); + if (PlayerIdList != null) foreach (byte apc in PlayerIdList) LocateArrow.RemoveAllTarget(apc); - if (!pc.Is(CustomRoles.Mortician) || target == null || pc.PlayerId == target.PlayerId) return; + if (!pc.Is(CustomRoles.Mortician) || target == null || pc.PlayerId == target.PlayerId || LastPlayerName == null) return; LastPlayerName.TryGetValue(target.PlayerId, out string name); + MsgToSend ??= []; MsgToSend.Add(pc.PlayerId, name == "" ? string.Format(Translator.GetString("MorticianGetNoInfo"), target.PlayerName) : string.Format(Translator.GetString("MorticianGetInfo"), target.PlayerName, name)); } diff --git a/Roles/Standard/Crewmate/Investigate/Oracle.cs b/Roles/Standard/Crewmate/Investigate/Oracle.cs index 45ffb219b..8287a7755 100644 --- a/Roles/Standard/Crewmate/Investigate/Oracle.cs +++ b/Roles/Standard/Crewmate/Investigate/Oracle.cs @@ -7,7 +7,7 @@ namespace EHR.Roles; public class Oracle : RoleBase { private const int Id = 7600; - private static List PlayerIdList = []; + private static bool On; public static OptionItem CheckLimitOpt; public static OptionItem HideVote; @@ -16,9 +16,9 @@ public class Oracle : RoleBase public static OptionItem AbilityChargesWhenFinishedTasks; public static OptionItem CancelVote; - public static readonly List DidVote = []; + public static readonly List DidVote; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => On; public override void SetupCustomOption() { @@ -48,27 +48,22 @@ public class Oracle : RoleBase public override void Init() { - PlayerIdList = []; + On = false; } public override void Add(byte playerId) { - PlayerIdList.Add(playerId); + On = true; playerId.SetAbilityUseLimit(CheckLimitOpt.GetFloat()); } - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - public override bool OnVote(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (player == null || target == null) return false; + DidVote ??= []; if (DidVote.Contains(player.PlayerId) || Main.DontCancelVoteList.Contains(player.PlayerId)) return false; - DidVote.Add(player.PlayerId); if (player.GetAbilityUseLimit() < 1) diff --git a/Roles/Standard/Crewmate/Investigate/Perceiver.cs b/Roles/Standard/Crewmate/Investigate/Perceiver.cs index a1dbdf1e9..f1a28fd29 100644 --- a/Roles/Standard/Crewmate/Investigate/Perceiver.cs +++ b/Roles/Standard/Crewmate/Investigate/Perceiver.cs @@ -14,7 +14,7 @@ internal class Perceiver : RoleBase public static OptionItem AbilityChargesWhenFinishedTasks; public static bool On; - private static int Id => 643360; + private const int Id = 643360; public override bool IsEnable => On; diff --git a/Roles/Standard/Crewmate/Investigate/Psychic.cs b/Roles/Standard/Crewmate/Investigate/Psychic.cs index f928df0bd..2e1d0d581 100644 --- a/Roles/Standard/Crewmate/Investigate/Psychic.cs +++ b/Roles/Standard/Crewmate/Investigate/Psychic.cs @@ -10,7 +10,7 @@ namespace EHR.Roles; public class Psychic : RoleBase { private const int Id = 7900; - private static List PlayerIdList = []; + private static bool On; private static OptionItem CanSeeNum; private static OptionItem Fresh; @@ -21,7 +21,7 @@ public class Psychic : RoleBase private List RedPlayer = []; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => On; public override void SetupCustomOption() { @@ -46,23 +46,18 @@ public class Psychic : RoleBase public override void Init() { - PlayerIdList = []; + On = false; RedPlayer = []; PsychicId = byte.MaxValue; } public override void Add(byte playerId) { - PlayerIdList.Add(playerId); + On = true; RedPlayer = []; PsychicId = playerId; } - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - private void SendRPC() { if (!IsEnable || !Utils.DoRPC) return; diff --git a/Roles/Standard/Crewmate/Investigate/Rabbit.cs b/Roles/Standard/Crewmate/Investigate/Rabbit.cs index c3c82ae26..bdc7d64cf 100644 --- a/Roles/Standard/Crewmate/Investigate/Rabbit.cs +++ b/Roles/Standard/Crewmate/Investigate/Rabbit.cs @@ -7,12 +7,12 @@ namespace EHR.Roles; internal class Rabbit : RoleBase { - private static readonly Dictionary RabbitStates = []; + private static Dictionary RabbitStates; private static OptionItem OptionTaskTrigger; private static int TaskTrigger; private static int Id => 643330; - public override bool IsEnable => RabbitStates.Count > 0; + public override bool IsEnable => RabbitStates is { Count: > 0 }; public override void SetupCustomOption() { @@ -26,37 +26,39 @@ internal class Rabbit : RoleBase public override void Init() { - RabbitStates.Clear(); + RabbitStates = null; TaskTrigger = OptionTaskTrigger.GetInt(); } public override void Add(byte playerId) { + RabbitStates ??= []; RabbitStates[playerId] = new(Utils.GetPlayerById(playerId)); } public override void Remove(byte playerId) { - RabbitStates.Remove(playerId); + RabbitStates?.Remove(playerId); } public static void ReceiveRPCStatic(MessageReader reader) { byte id = reader.ReadByte(); bool hasArrow = reader.ReadBoolean(); + RabbitStates ??= []; RabbitStates[id].ReceiveRPC(hasArrow); } public override void OnTaskComplete(PlayerControl pc, int completedTaskCount, int totalTaskCount) { - if (!RabbitStates.TryGetValue(pc.PlayerId, out RabbitState state)) return; + if (RabbitStates == null || !RabbitStates.TryGetValue(pc.PlayerId, out RabbitState state)) return; state.OnTaskComplete(); } public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false) { - if (seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || !RabbitStates.TryGetValue(seer.PlayerId, out RabbitState state)) return string.Empty; + if (seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || RabbitStates == null || !RabbitStates.TryGetValue(seer.PlayerId, out RabbitState state)) return string.Empty; string suffix = state.Suffix; return hud ? $"{suffix}" : suffix; diff --git a/Roles/Standard/Crewmate/Investigate/Scout.cs b/Roles/Standard/Crewmate/Investigate/Scout.cs index bdf654458..99f4c1157 100644 --- a/Roles/Standard/Crewmate/Investigate/Scout.cs +++ b/Roles/Standard/Crewmate/Investigate/Scout.cs @@ -11,7 +11,7 @@ namespace EHR.Roles; public class Scout : RoleBase { private const int Id = 8300; - private static List PlayerIdList = []; + private static bool On; private static OptionItem TrackLimitOpt; private static OptionItem OptionCanSeeLastRoomInMeeting; @@ -23,10 +23,10 @@ public class Scout : RoleBase public static bool CanSeeLastRoomInMeeting; - private static Dictionary> TrackerTarget = []; + private static Dictionary> TrackerTarget; private byte TrackerId; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => On; public override void SetupCustomOption() { @@ -58,24 +58,20 @@ public class Scout : RoleBase public override void Init() { - PlayerIdList = []; - TrackerTarget = []; + On = false; + TrackerTarget = null; CanSeeLastRoomInMeeting = OptionCanSeeLastRoomInMeeting.GetBool(); } public override void Add(byte playerId) { - PlayerIdList.Add(playerId); + On = true; playerId.SetAbilityUseLimit(TrackLimitOpt.GetFloat()); + TrackerTarget ??= []; TrackerTarget.Add(playerId, []); TrackerId = playerId; } - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - public static void SendRPC(byte trackerId = byte.MaxValue, byte targetId = byte.MaxValue) { if (!Utils.DoRPC) return; @@ -98,13 +94,13 @@ public class Scout : RoleBase public static string GetTargetMark(PlayerControl seer, PlayerControl target) { - return TrackerTarget.ContainsKey(seer.PlayerId) && TrackerTarget[seer.PlayerId].Contains(target.PlayerId) ? Utils.ColorString(seer.GetRoleColor(), "◀") : string.Empty; + return TrackerTarget != null && TrackerTarget.ContainsKey(seer.PlayerId) && TrackerTarget[seer.PlayerId].Contains(target.PlayerId) ? Utils.ColorString(seer.GetRoleColor(), "◀") : string.Empty; } public override bool OnVote(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) return false; - if (player == null || target == null || player.GetAbilityUseLimit() < 1f || player.PlayerId == target.PlayerId || TrackerTarget[player.PlayerId].Contains(target.PlayerId) || Main.DontCancelVoteList.Contains(player.PlayerId)) return false; + if (!player || !target || player.GetAbilityUseLimit() < 1f || player.PlayerId == target.PlayerId || TrackerTarget == null || TrackerTarget[player.PlayerId].Contains(target.PlayerId) || Main.DontCancelVoteList.Contains(player.PlayerId)) return false; player.RpcRemoveAbilityUse(); @@ -124,13 +120,14 @@ public class Scout : RoleBase public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false) { - if (seer.PlayerId != TrackerId || seer.PlayerId != target.PlayerId || !TrackerTarget.ContainsKey(seer.PlayerId) || meeting || hud) return string.Empty; + if (seer.PlayerId != TrackerId || seer.PlayerId != target.PlayerId || TrackerTarget == null || !TrackerTarget.ContainsKey(seer.PlayerId) || meeting || hud) return string.Empty; return TrackerTarget[seer.PlayerId].Aggregate(string.Empty, (current, trackTarget) => current + Utils.ColorString(CanGetColoredArrow.GetBool() ? Main.PlayerColors[trackTarget] : Color.white, TargetArrow.GetArrows(seer, trackTarget))) + LocateArrow.GetArrows(seer); } public static bool IsTrackTarget(PlayerControl seer, PlayerControl target) { - return seer.IsAlive() && PlayerIdList.Contains(seer.PlayerId) + return seer.IsAlive() && seer.Is(CustomRoles.Scout) + && TrackerTarget != null && TrackerTarget[seer.PlayerId].Contains(target.PlayerId) && target.IsAlive(); } @@ -153,7 +150,7 @@ public class Scout : RoleBase public static void OnPlayerDeath(PlayerControl player) { - if (!player) return; + if (TrackerTarget == null || !player) return; foreach (KeyValuePair> kvp in TrackerTarget) { @@ -168,6 +165,8 @@ public class Scout : RoleBase public override void AfterMeetingTasks() { + if (TrackerTarget == null) return; + foreach (KeyValuePair> kvp in TrackerTarget) { LocateArrow.RemoveAllTarget(kvp.Key); diff --git a/Roles/Standard/Crewmate/Investigate/Sentry.cs b/Roles/Standard/Crewmate/Investigate/Sentry.cs index 79701ad08..8d918a007 100644 --- a/Roles/Standard/Crewmate/Investigate/Sentry.cs +++ b/Roles/Standard/Crewmate/Investigate/Sentry.cs @@ -20,7 +20,7 @@ internal class Sentry : RoleBase public static OptionItem AbilityChargesWhenFinishedTasks; private static readonly Dictionary TeamsCanSeeInfo = []; - private static Vector2[] AvailableDevices = []; + private static Vector2[] AvailableDevices; private readonly Dictionary LastInfoSend = []; private readonly HashSet LastNotified = []; @@ -84,11 +84,6 @@ internal class Sentry : RoleBase DeadBodiesInRoom = []; playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat()); UsingDevice = []; - } - - public override void Init() - { - On = false; AvailableDevices = DisableDevice.DevicePos.Where(x => { @@ -120,6 +115,12 @@ internal class Sentry : RoleBase }).Select(x => x.Value).ToArray(); } + public override void Init() + { + On = false; + AvailableDevices = null; + } + public override void OnPet(PlayerControl pc) { PlainShipRoom room = pc.GetPlainShipRoom(); @@ -280,11 +281,10 @@ internal class Sentry : RoleBase public override void OnCheckPlayerPosition(PlayerControl pc) { - if (MonitoredRoom == null || MonitoredRoom == null || MonitoredRoom == null) return; + if (!MonitoredRoom || AvailableDevices == null) return; long now = Utils.TimeStamp; if (LastInfoSend.TryGetValue(pc.PlayerId, out long ts) && ts == now) return; - LastInfoSend[pc.PlayerId] = now; if (!CheckTeam(pc)) return; diff --git a/Roles/Standard/Crewmate/Investigate/Snitch.cs b/Roles/Standard/Crewmate/Investigate/Snitch.cs index 42ed47dbf..99aa8260a 100644 --- a/Roles/Standard/Crewmate/Investigate/Snitch.cs +++ b/Roles/Standard/Crewmate/Investigate/Snitch.cs @@ -8,7 +8,7 @@ namespace EHR.Roles; public class Snitch : RoleBase { private const int Id = 8000; - private static readonly List PlayerIdList = []; + private static List PlayerIdList; private static readonly Color RoleColor = Utils.GetRoleColor(CustomRoles.Snitch); private static OptionItem OptionEnableTargetArrow; @@ -25,14 +25,14 @@ public class Snitch : RoleBase private static bool CanFindMadmate; public static int RemainingTasksToBeFound; - public static readonly Dictionary IsExposed = []; - public static readonly Dictionary IsComplete = []; + public static Dictionary IsExposed = []; + public static Dictionary IsComplete = []; + private static HashSet TargetList = []; + private static Dictionary TargetColorlist = []; - private static readonly HashSet TargetList = []; - private static readonly Dictionary TargetColorlist = []; private byte SnitchId; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => PlayerIdList is { Count: > 0 }; public override void SetupCustomOption() { @@ -48,17 +48,16 @@ public class Snitch : RoleBase public override void Init() { - PlayerIdList.Clear(); - - IsExposed.Clear(); - IsComplete.Clear(); - - TargetList.Clear(); - TargetColorlist.Clear(); + PlayerIdList = null; + IsExposed = null; + IsComplete = null; + TargetList = null; + TargetColorlist = null; } public override void Add(byte playerId) { + PlayerIdList ??= []; PlayerIdList.Add(playerId); SnitchId = playerId; @@ -69,22 +68,24 @@ public class Snitch : RoleBase CanFindMadmate = OptionCanFindMadmate.GetBool(); RemainingTasksToBeFound = OptionRemainingTasks.GetInt(); + IsExposed ??= []; + IsComplete ??= []; IsExposed[playerId] = false; IsComplete[playerId] = false; } public override void Remove(byte playerId) { - PlayerIdList.Remove(playerId); - IsExposed.Remove(playerId); - IsComplete.Remove(playerId); + PlayerIdList?.Remove(playerId); + IsExposed?.Remove(playerId); + IsComplete?.Remove(playerId); } private static bool GetExpose(PlayerControl pc) { if (!pc.IsAlive() || pc.Is(CustomRoles.Madmate)) return false; - return IsExposed.GetValueOrDefault(pc.PlayerId); + return IsExposed != null && IsExposed.GetValueOrDefault(pc.PlayerId); } public static bool IsSnitchTarget(PlayerControl target) @@ -99,7 +100,7 @@ public class Snitch : RoleBase public static string GetWarningArrow(PlayerControl seer, PlayerControl target = null) { - if (GameStates.IsMeeting || !IsSnitchTarget(seer) || (target && seer.PlayerId != target.PlayerId)) return string.Empty; + if (GameStates.IsMeeting || !IsSnitchTarget(seer) || (target && seer.PlayerId != target.PlayerId) || PlayerIdList == null || IsExposed == null) return string.Empty; IEnumerable exposedSnitch = PlayerIdList.Where(s => !Main.PlayerStates[s].IsDead && IsExposed[s]); byte[] snitch = exposedSnitch as byte[] ?? exposedSnitch.ToArray(); @@ -121,10 +122,13 @@ public class Snitch : RoleBase var arrows = string.Empty; - foreach (byte targetId in TargetList) + if (TargetList != null) { - string arrow = TargetArrow.GetArrows(seer, targetId); - arrows += CanGetColoredArrow ? Utils.ColorString(TargetColorlist[targetId], arrow) : arrow; + foreach (byte targetId in TargetList) + { + string arrow = TargetArrow.GetArrows(seer, targetId); + arrows += CanGetColoredArrow && TargetColorlist != null ? Utils.ColorString(TargetColorlist[targetId], arrow) : arrow; + } } return arrows; @@ -134,7 +138,7 @@ public class Snitch : RoleBase { if (!pc.IsAlive() || pc.IsConverted() || pc.Is(CustomRoles.Madmate)) return; - if (!IsExposed.GetValueOrDefault(pc.PlayerId) && totalTaskCount - (completedTaskCount + 1) <= RemainingTasksToBeFound) + if (IsExposed != null && !IsExposed.GetValueOrDefault(pc.PlayerId) && totalTaskCount - (completedTaskCount + 1) <= RemainingTasksToBeFound) { foreach (PlayerControl target in Main.CachedAlivePlayerControls()) { @@ -146,7 +150,7 @@ public class Snitch : RoleBase IsExposed[pc.PlayerId] = true; } - if (IsComplete.GetValueOrDefault(pc.PlayerId) || completedTaskCount + 1 < totalTaskCount) return; + if (IsComplete == null || IsComplete.GetValueOrDefault(pc.PlayerId) || completedTaskCount + 1 < totalTaskCount) return; IsComplete[pc.PlayerId] = true; pc.Notify(Translator.GetString("SnitchDoneTasks")); @@ -162,10 +166,14 @@ public class Snitch : RoleBase TargetArrow.Add(pc.PlayerId, targetId); + TargetList ??= []; if (TargetList.Add(targetId)) { if (CanGetColoredArrow) + { + TargetColorlist ??= []; TargetColorlist.Add(targetId, target.GetRoleColor()); + } } Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: target); diff --git a/Roles/Standard/Crewmate/Investigate/Socialite.cs b/Roles/Standard/Crewmate/Investigate/Socialite.cs index 1c2bee6b8..3ed03b414 100644 --- a/Roles/Standard/Crewmate/Investigate/Socialite.cs +++ b/Roles/Standard/Crewmate/Investigate/Socialite.cs @@ -8,7 +8,7 @@ namespace EHR.Roles; public class Socialite : RoleBase { public static bool On; - private static List Instances = []; + private static List Instances; private static OptionItem Cooldown; public static OptionItem UsePet; @@ -35,12 +35,13 @@ public class Socialite : RoleBase public override void Init() { On = false; - Instances = []; + Instances = null; } public override void Add(byte playerId) { On = true; + Instances ??= []; Instances.Add(this); GuestList = []; SocialiteId = playerId; @@ -49,7 +50,7 @@ public class Socialite : RoleBase public override void Remove(byte playerId) { - Instances.Remove(this); + Instances?.Remove(this); } public override void SetKillCooldown(byte id) @@ -85,13 +86,16 @@ public class Socialite : RoleBase public static bool OnAnyoneCheckMurder(PlayerControl killer, PlayerControl target) { - foreach (Socialite socialite in Instances) + if (Instances != null) { - if (socialite.MarkedPlayerId == target.PlayerId && socialite.GuestList.Add(killer.PlayerId)) + foreach (Socialite socialite in Instances) { - Utils.SendRPC(CustomRPC.SyncRoleData, socialite.SocialiteId, 2, killer.PlayerId); - Utils.NotifyRoles(SpecifySeer: Utils.GetPlayerById(socialite.SocialiteId), SpecifyTarget: killer); - return false; + if (socialite.MarkedPlayerId == target.PlayerId && socialite.GuestList.Add(killer.PlayerId)) + { + Utils.SendRPC(CustomRPC.SyncRoleData, socialite.SocialiteId, 2, killer.PlayerId); + Utils.NotifyRoles(SpecifySeer: Utils.GetPlayerById(socialite.SocialiteId), SpecifyTarget: killer); + return false; + } } } diff --git a/Roles/Standard/Crewmate/Investigate/Soothsayer.cs b/Roles/Standard/Crewmate/Investigate/Soothsayer.cs index 642fa61a7..11d0ae97c 100644 --- a/Roles/Standard/Crewmate/Investigate/Soothsayer.cs +++ b/Roles/Standard/Crewmate/Investigate/Soothsayer.cs @@ -5,7 +5,7 @@ namespace EHR.Roles; public class Soothsayer : RoleBase { public static bool On; - private static List Instances = []; + private static List Instances; public static OptionItem CancelVote; @@ -25,19 +25,20 @@ public class Soothsayer : RoleBase public override void Init() { On = false; - Instances = []; + Instances = null; } public override void Add(byte playerId) { On = true; + Instances ??= []; Instances.Add(this); Target = byte.MaxValue; } public override void Remove(byte playerId) { - Instances.Remove(this); + Instances?.Remove(this); } public override bool OnVote(PlayerControl player, PlayerControl target) @@ -59,7 +60,7 @@ public class Soothsayer : RoleBase public static void OnAnyoneDeath(PlayerControl killer) { - if (killer == null) return; + if (!killer || Instances == null) return; Instances.DoIf(x => x.Target == killer.PlayerId, _ => Main.EnumerateAlivePlayerControls().Do(p => p.Notify(string.Format(Translator.GetString("SoothsayerDiedNotify"), Utils.ColorString(Main.PlayerColors.GetValueOrDefault(killer.PlayerId), killer.GetRealName())), 10f))); } } \ No newline at end of file diff --git a/Roles/Standard/Crewmate/Investigate/Spiritualist.cs b/Roles/Standard/Crewmate/Investigate/Spiritualist.cs index d3225795c..ee8b32a9f 100644 --- a/Roles/Standard/Crewmate/Investigate/Spiritualist.cs +++ b/Roles/Standard/Crewmate/Investigate/Spiritualist.cs @@ -10,17 +10,18 @@ internal class Spiritualist : RoleBase { private const int Id = 8100; - public static List PlayerIdList = []; + private static List PlayerIdList; private static OptionItem ShowGhostArrowEverySeconds; private static OptionItem ShowGhostArrowForSeconds; public static byte SpiritualistTarget; + private long LastGhostArrowShowTime; private long ShowGhostArrowUntil; private byte SpiritualistId; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => PlayerIdList is { Count: > 0 }; private bool ShowArrow { @@ -54,7 +55,7 @@ internal class Spiritualist : RoleBase public override void Init() { - PlayerIdList = []; + PlayerIdList = null; SpiritualistTarget = 0; LastGhostArrowShowTime = 0; ShowGhostArrowUntil = 0; @@ -62,6 +63,7 @@ internal class Spiritualist : RoleBase public override void Add(byte playerId) { + PlayerIdList ??= []; PlayerIdList.Add(playerId); SpiritualistTarget = byte.MaxValue; LastGhostArrowShowTime = 0; @@ -71,7 +73,7 @@ internal class Spiritualist : RoleBase public override void Remove(byte playerId) { - PlayerIdList.Remove(playerId); + PlayerIdList?.Remove(playerId); } public static void OnReportDeadBody(NetworkedPlayerInfo target) @@ -85,6 +87,8 @@ internal class Spiritualist : RoleBase public override void AfterMeetingTasks() { + if (PlayerIdList == null) return; + foreach (byte spiritualist in PlayerIdList) { PlayerControl player = spiritualist.GetPlayer(); @@ -132,8 +136,7 @@ internal class Spiritualist : RoleBase public static void RemoveTarget() { - foreach (byte spiritualist in PlayerIdList) TargetArrow.Remove(spiritualist, SpiritualistTarget); - + if (PlayerIdList != null) foreach (byte spiritualist in PlayerIdList) TargetArrow.Remove(spiritualist, SpiritualistTarget); SpiritualistTarget = byte.MaxValue; } } \ No newline at end of file diff --git a/Roles/Standard/Crewmate/Investigate/Spy.cs b/Roles/Standard/Crewmate/Investigate/Spy.cs index 30a5c223f..2e522bc80 100644 --- a/Roles/Standard/Crewmate/Investigate/Spy.cs +++ b/Roles/Standard/Crewmate/Investigate/Spy.cs @@ -11,15 +11,15 @@ using static Utils; public class Spy : RoleBase { private const int Id = 640400; - private static List PlayerIdList = []; - public static HashSet SpyRedNameList = []; + private static bool On; + public static HashSet SpyRedNameList; private static OptionItem SpyRedNameDur; private static OptionItem UseLimitOpt; public static OptionItem SpyAbilityUseGainWithEachTaskCompleted; public static OptionItem AbilityChargesWhenFinishedTasks; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => On; public override void SetupCustomOption() { @@ -44,21 +44,16 @@ public class Spy : RoleBase public override void Init() { - PlayerIdList = []; - SpyRedNameList = []; + On = false; + SpyRedNameList = null; } public override void Add(byte playerId) { - PlayerIdList.Add(playerId); + On = true; playerId.SetAbilityUseLimit(UseLimitOpt.GetFloat()); } - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - private static void SendRPC(int operate, byte id = byte.MaxValue) { if (!DoRPC) return; @@ -77,10 +72,11 @@ public class Spy : RoleBase { case 1: byte susId = reader.ReadByte(); + SpyRedNameList ??= []; SpyRedNameList.Add(susId); return; case 3: - SpyRedNameList.Remove(reader.ReadByte()); + SpyRedNameList?.Remove(reader.ReadByte()); return; } } @@ -90,6 +86,7 @@ public class Spy : RoleBase if (!target.Is(CustomRoles.Spy) || killer.PlayerId == target.PlayerId || target.GetAbilityUseLimit() < 1) return true; target.RpcRemoveAbilityUse(); + SpyRedNameList ??= []; SpyRedNameList.Add(killer.PlayerId); SendRPC(1, killer.PlayerId); NotifyRoles(SpecifySeer: target, SpecifyTarget: killer); @@ -97,12 +94,12 @@ public class Spy : RoleBase _ = new CountdownTimer(SpyRedNameDur.GetInt(), () => { - SpyRedNameList.Remove(killer.PlayerId); + SpyRedNameList?.Remove(killer.PlayerId); SendRPC(3, killer.PlayerId); NotifyRoles(SpecifySeer: target, SpecifyTarget: killer); }, onCanceled: () => { - SpyRedNameList.Remove(killer.PlayerId); + SpyRedNameList?.Remove(killer.PlayerId); SendRPC(3, killer.PlayerId); }); return false; diff --git a/Roles/Standard/Crewmate/Investigate/Whisperer.cs b/Roles/Standard/Crewmate/Investigate/Whisperer.cs index 033bc9942..aff5aad24 100644 --- a/Roles/Standard/Crewmate/Investigate/Whisperer.cs +++ b/Roles/Standard/Crewmate/Investigate/Whisperer.cs @@ -10,7 +10,7 @@ namespace EHR.Roles; public class Whisperer : RoleBase { public static bool On; - private static List Instances = []; + private static List Instances; public static OptionItem Cooldown; public static OptionItem Duration; @@ -19,7 +19,7 @@ public class Whisperer : RoleBase public static OptionItem AbilityChargesWhenFinishedTasks; private static DateTime LastMeetingStart; - private static Dictionary DeathInfo = []; + private static Dictionary DeathInfo; private int Count; private (string Name, int Percent) CurrentlyQuestioning; @@ -58,8 +58,8 @@ public class Whisperer : RoleBase public override void Init() { On = false; - Instances = []; - DeathInfo = []; + Instances = null; + DeathInfo = null; } public override void Add(byte playerId) @@ -68,6 +68,7 @@ public class Whisperer : RoleBase Count = 0; Souls = []; Info = []; + Instances ??= []; Instances.Add(this); WhispererId = playerId; CurrentlyQuestioning = (string.Empty, 0); @@ -76,7 +77,7 @@ public class Whisperer : RoleBase public override void Remove(byte playerId) { - Instances.Remove(this); + Instances?.Remove(this); } public override void OnPet(PlayerControl pc) @@ -135,7 +136,7 @@ public class Whisperer : RoleBase int next = IRandom.Instance.Next(7); if (state.deathReason == PlayerState.DeathReason.Disconnected) next = 2; (byte[] SameRoomPlayers, SystemTypes? ActiveSabotage) deathInfo = ([], null); - if (next > 3 && !DeathInfo.TryGetValue(soulPlayerId, out deathInfo)) next = IRandom.Instance.Next(4); + if (next > 3 && (DeathInfo == null || !DeathInfo.TryGetValue(soulPlayerId, out deathInfo))) next = IRandom.Instance.Next(4); if (pc.Is(CustomRoles.Autopsy) || pc.Is(CustomRoles.Doctor) || Options.EveryoneSeesDeathReasons.GetBool()) next = IRandom.Instance.Next(6); PlayerState killerState = Main.PlayerStates[ID]; @@ -196,16 +197,20 @@ public class Whisperer : RoleBase public static void OnAnyoneDied(PlayerControl target) { - foreach (Whisperer instance in Instances) + if (Instances != null) { - if (instance.Souls.Exists(x => x.Player.PlayerId == target.PlayerId)) continue; + foreach (Whisperer instance in Instances) + { + if (instance.Souls.Exists(x => x.Player.PlayerId == target.PlayerId)) continue; - instance.Souls.Add(new(target)); - Utils.SendRPC(CustomRPC.SyncRoleData, instance.WhispererId, 5, target.PlayerId); + instance.Souls.Add(new(target)); + Utils.SendRPC(CustomRPC.SyncRoleData, instance.WhispererId, 5, target.PlayerId); + } } var room = target.GetPlainShipRoom(); - DeathInfo[target.PlayerId] = (room == null ? [] : Main.EnumerateAlivePlayerControls().Where(x => x.IsInRoom(room)).Select(x => x.PlayerId).ToArray(), ShipStatusSystem.AllSabotage.FindFirst(Utils.IsActive, out var sabotage) ? sabotage : null); + DeathInfo ??= []; + DeathInfo[target.PlayerId] = (!room ? [] : Main.EnumerateAlivePlayerControls().Where(x => x.IsInRoom(room)).Select(x => x.PlayerId).ToArray(), ShipStatusSystem.AllSabotage.FindFirst(Utils.IsActive, out var sabotage) ? sabotage : null); } public override void OnReportDeadBody() diff --git a/Roles/Standard/Crewmate/Investigate/Witness.cs b/Roles/Standard/Crewmate/Investigate/Witness.cs index be4c2da9e..8be273b41 100644 --- a/Roles/Standard/Crewmate/Investigate/Witness.cs +++ b/Roles/Standard/Crewmate/Investigate/Witness.cs @@ -6,7 +6,7 @@ namespace EHR.Roles; internal class Witness : RoleBase { - public static HashSet AllKillers = []; + public static HashSet AllKillers; public static bool On; public override bool IsEnable => On; @@ -34,7 +34,7 @@ internal class Witness : RoleBase public override void Init() { On = false; - AllKillers = []; + AllKillers = null; } public override void SetKillCooldown(byte id) @@ -60,12 +60,12 @@ internal class Witness : RoleBase public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) { killer.SetKillCooldown(); - killer.Notify($"<#{(AllKillers.Contains(target.PlayerId) ? "ffff00>⚠" : "00ff00>✓")}"); + killer.Notify($"<#{(AllKillers != null && AllKillers.Contains(target.PlayerId) ? "ffff00>⚠" : "00ff00>✓")}"); return false; } public override void OnReportDeadBody() { - AllKillers.Clear(); + AllKillers = null; } } \ No newline at end of file diff --git a/Roles/Standard/Crewmate/Power/President.cs b/Roles/Standard/Crewmate/Power/President.cs index 5b351d114..5c745d5ce 100644 --- a/Roles/Standard/Crewmate/Power/President.cs +++ b/Roles/Standard/Crewmate/Power/President.cs @@ -190,7 +190,7 @@ public class President : RoleBase switch (Main.PlayerStates[player.PlayerId].Role) { - case Hacker when Hacker.UseLimit.ContainsKey(player.PlayerId): + case Hacker when Hacker.UseLimit != null && Hacker.UseLimit.ContainsKey(player.PlayerId): Hacker.UseLimit[player.PlayerId]++; break; default: diff --git a/Roles/Standard/Impostor/Support/Nullifier.cs b/Roles/Standard/Impostor/Support/Nullifier.cs index 5f94a5f4b..e6a3eed55 100644 --- a/Roles/Standard/Impostor/Support/Nullifier.cs +++ b/Roles/Standard/Impostor/Support/Nullifier.cs @@ -73,10 +73,13 @@ internal class Nullifier : RoleBase case CustomRoles.Hacker: if (target.IsModdedClient()) { - Hacker.UseLimitSeconds[target.PlayerId] -= Hacker.ModdedClientAbilityUseSecondsMultiplier.GetInt(); - Hacker.SendRPC(target.PlayerId, Hacker.UseLimitSeconds[target.PlayerId]); + if (Hacker.UseLimitSeconds != null) + { + Hacker.UseLimitSeconds[target.PlayerId] -= Hacker.ModdedClientAbilityUseSecondsMultiplier.GetInt(); + Hacker.SendRPC(target.PlayerId, Hacker.UseLimitSeconds[target.PlayerId]); + } } - else + else if (Hacker.UseLimit != null) Hacker.UseLimit[target.PlayerId]--; break;