From 110697d3b0d7b38f50e0ae530e877534da4eb07d Mon Sep 17 00:00:00 2001 From: Gurge44 Date: Fri, 13 Sep 2024 09:32:35 +0200 Subject: [PATCH] /readycheck & bug fixes --- EHR.csproj | 2 +- Modules/ExtendedPlayerControl.cs | 16 +++- Modules/GameState.cs | 2 +- Modules/GhostRolesManager.cs | 2 +- Modules/Utils.cs | 6 +- Patches/ChatCommandPatch.cs | 33 +++++++ Patches/Crowded.cs | 158 +------------------------------ Patches/OnGameStartedPatch.cs | 2 +- Patches/PlayerControlPatch.cs | 3 - Resources/Lang/en_US.json | 7 +- Roles/Neutral/Enderman.cs | 2 +- Roles/Neutral/Evolver.cs | 2 +- Roles/Neutral/Hookshot.cs | 2 +- Roles/Neutral/Innocent.cs | 2 +- Roles/Neutral/Jackal.cs | 16 ++-- Roles/Neutral/Magician.cs | 2 +- Roles/Neutral/Maverick.cs | 2 +- Roles/Neutral/Mycologist.cs | 2 +- Roles/Neutral/Patroller.cs | 2 +- Roles/Neutral/Provocateur.cs | 2 +- Roles/Neutral/Revolutionist.cs | 2 +- Roles/Neutral/Rogue.cs | 2 +- Roles/Neutral/RouleteGrandeur.cs | 2 +- Roles/Neutral/Simon.cs | 2 +- Roles/Neutral/Sprayer.cs | 2 +- Roles/Neutral/Tiger.cs | 2 +- Roles/Neutral/Traitor.cs | 2 +- Roles/Neutral/WeaponMaster.cs | 2 +- 28 files changed, 86 insertions(+), 195 deletions(-) diff --git a/EHR.csproj b/EHR.csproj index a31768c5f..4b919b20a 100644 --- a/EHR.csproj +++ b/EHR.csproj @@ -7,7 +7,7 @@ EHR Gurge44 preview - E:\Program Files\AmongUs + C:\Program Files\Epic Games\AmongUs true True $(MSBuildProjectName) diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 78ce62107..b6ef499e5 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -307,8 +307,20 @@ static class ExtendedPlayerControl var newRoleType = newCustomRole.GetRoleTypes(); RoleTypes remeberRoleType; - if (Options.CurrentGameMode == CustomGameMode.Speedrun && newCustomRole == CustomRoles.Runner) - newRoleType = SpeedrunManager.CanKill.Contains(player.PlayerId) ? RoleTypes.Impostor : RoleTypes.Crewmate; + newRoleType = Options.CurrentGameMode switch + { + CustomGameMode.Speedrun when newCustomRole == CustomRoles.Runner => SpeedrunManager.CanKill.Contains(player.PlayerId) ? RoleTypes.Impostor : RoleTypes.Crewmate, + CustomGameMode.Standard when StartGameHostPatch.BasisChangingAddons.FindFirst(x => x.Value.Contains(player.PlayerId), out var kvp) => kvp.Key switch + { + CustomRoles.Bloodlust when newRoleType is RoleTypes.Crewmate or RoleTypes.Engineer or RoleTypes.Scientist => RoleTypes.Impostor, + CustomRoles.Nimble when newRoleType is RoleTypes.Crewmate or RoleTypes.Scientist => RoleTypes.Engineer, + CustomRoles.Physicist when newRoleType == RoleTypes.Crewmate => RoleTypes.Scientist, + CustomRoles.Finder when newRoleType is RoleTypes.Crewmate or RoleTypes.Scientist => RoleTypes.Tracker, + CustomRoles.Noisy when newRoleType == RoleTypes.Crewmate => RoleTypes.Noisemaker, + _ => newRoleType + }, + _ => newRoleType + }; var oldRoleIsDesync = playerRole.IsDesyncRole(); var newRoleIsDesync = newCustomRole.IsDesyncRole(); diff --git a/Modules/GameState.cs b/Modules/GameState.cs index 0067b77fc..cbee46fee 100644 --- a/Modules/GameState.cs +++ b/Modules/GameState.cs @@ -140,7 +140,7 @@ public class PlayerState(byte playerId) if (Lyncher.On) Lyncher.Instances.ForEach(x => x.OnRoleChange(PlayerId)); if (!role.Is(Team.Impostor)) SubRoles.ToArray().DoIf(x => x.IsImpOnlyAddon(), RemoveSubRole); - if (role is CustomRoles.Sidekick or CustomRoles.Necromancer or CustomRoles.Deathknight or CustomRoles.Refugee) RemoveSubRole(CustomRoles.Nimble); + if (role is CustomRoles.Sidekick or CustomRoles.Necromancer or CustomRoles.Deathknight or CustomRoles.Refugee) SubRoles.ToArray().DoIf(StartGameHostPatch.BasisChangingAddons.ContainsKey, RemoveSubRole); if (role == CustomRoles.Sidekick && Jackal.Instances.FindFirst(x => x.SidekickId == byte.MaxValue || x.SidekickId.GetPlayer() == null, out var jackal)) jackal.SidekickId = PlayerId; diff --git a/Modules/GhostRolesManager.cs b/Modules/GhostRolesManager.cs index bd0ecd271..aac1818f6 100644 --- a/Modules/GhostRolesManager.cs +++ b/Modules/GhostRolesManager.cs @@ -18,7 +18,7 @@ namespace EHR.Modules public static void Initialize() { AssignedGhostRoles = []; - GhostRoles = Enum.GetValues().Where(x => x != CustomRoles.EvilSpirit && x.IsGhostRole() && x.IsEnable()).ToList(); + GhostRoles = Enum.GetValues().Where(x => x != CustomRoles.EvilSpirit && x.IsGhostRole() && x.IsEnable() && x.GetMode() != 0).ToList(); Logger.Msg($"Ghost roles: {GhostRoles.Join()}", "GhostRoles"); Haunter.AllHauntedPlayers = []; diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 4e822bdb0..57e162cc6 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -2027,10 +2027,10 @@ public static class Utils ? SeerRealName : seerTeam switch { - Team.Impostor when seer.IsMadmate() => $"{GetString("YouAreMadmate")}\n{(showLongInfo ? longInfo : seer.GetRoleInfo()) + mHelp}", + Team.Impostor when seer.IsMadmate() => $"{GetString("YouAreMadmate")}\n{(showLongInfo ? longInfo : seer.GetRoleInfo()) + mHelp}", Team.Impostor => $"\n{(showLongInfo ? longInfo : seer.GetRoleInfo()) + mHelp}", - Team.Crewmate => $"{GetString("YouAreCrewmate")}\n{(showLongInfo ? longInfo : seer.GetRoleInfo()) + mHelp}", - Team.Neutral => $"{GetString("YouAreNeutral")}\n{(showLongInfo ? longInfo : seer.GetRoleInfo()) + mHelp}", + Team.Crewmate => $"{GetString("YouAreCrewmate")}\n{(showLongInfo ? longInfo : seer.GetRoleInfo()) + mHelp}", + Team.Neutral => $"{GetString("YouAreNeutral")}\n{(showLongInfo ? longInfo : seer.GetRoleInfo()) + mHelp}", _ => SeerRealName }; } diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index 585e461a6..c5c1fde00 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -103,6 +103,8 @@ internal static class ChatCommands public static Dictionary> DraftRoles = []; public static Dictionary DraftResult = []; + private static HashSet ReadyPlayers = []; + public static void LoadCommands() { AllCommands = @@ -170,6 +172,8 @@ internal static class ChatCommands new(["unmute", "размут"], "{id}", GetString("CommandDescription.Unmute"), Command.UsageLevels.Host, Command.UsageTimes.Always, UnmuteCommand, true, [GetString("CommandArgs.Unmute.Id")]), new(["draftstart", "ds", "драфтстарт"], "", GetString("CommandDescription.DraftStart"), Command.UsageLevels.Host, Command.UsageTimes.InLobby, DraftStartCommand, true), new(["draft", "драфт"], "{number}", GetString("CommandDescription.Draft"), Command.UsageLevels.Everyone, Command.UsageTimes.InLobby, DraftCommand, true, [GetString("CommandArgs.Draft.Number")]), + new(["readycheck", "rc", "готов"], "", GetString("CommandDescription.ReadyCheck"), Command.UsageLevels.Host, Command.UsageTimes.InLobby, ReadyCheckCommand, true), + new(["ready", "готов", "г"], "", GetString("CommandDescription.Ready"), Command.UsageLevels.Everyone, Command.UsageTimes.InLobby, ReadyCommand, true), // Commands with action handled elsewhere new(["shoot", "guess", "bet", "bt", "st", "угадать", "бт"], "{id} {role}", GetString("CommandDescription.Guess"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, (_, _, _, _) => { }, true, [GetString("CommandArgs.Guess.Id"), GetString("CommandArgs.Guess.Role")]), @@ -298,6 +302,35 @@ internal static class ChatCommands // --------------------------------------------------------------------------------------------------------------------------------------------- + private static void ReadyCheckCommand(ChatController __instance, PlayerControl player, string text, string[] args) + { + Utils.SendMessage(GetString("ReadyCheckMessage"), title: GetString("ReadyCheckTitle")); + ReadyPlayers = [player.PlayerId]; + Main.Instance.StartCoroutine(Countdown()); + return; + + System.Collections.IEnumerator Countdown() + { + var timer = 30f; + while (timer > 0f) + { + if (!GameStates.IsLobby) yield break; + if (Main.AllPlayerControls.Select(x => x.PlayerId).All(ReadyPlayers.Contains)) break; + timer -= Time.deltaTime; + yield return null; + } + + var notReadyPlayers = Main.AllPlayerControls.Select(x => x.PlayerId).Except(ReadyPlayers).ToArray(); + if (notReadyPlayers.Length == 0) Utils.SendMessage("\n", player.PlayerId, GetString("EveryoneReadyTitle")); + else Utils.SendMessage(string.Join(", ", notReadyPlayers.Select(x => x.ColoredPlayerName())), player.PlayerId, string.Format(GetString("PlayersNotReadyTitle"), notReadyPlayers.Length)); + } + } + + private static void ReadyCommand(ChatController __instance, PlayerControl player, string text, string[] args) + { + ReadyPlayers.Add(player.PlayerId); + } + private static void DraftStartCommand(ChatController __instance, PlayerControl player, string text, string[] args) { DraftResult = []; diff --git a/Patches/Crowded.cs b/Patches/Crowded.cs index 546e24633..35ff62843 100644 --- a/Patches/Crowded.cs +++ b/Patches/Crowded.cs @@ -1,167 +1,13 @@ -using System; -using AmongUs.GameOptions; +using AmongUs.GameOptions; using HarmonyLib; -using TMPro; -using UnityEngine; -// All errors in this file are fake, the build succeeds -// https://github.com/CrowdedMods/CrowdedMod/blob/master/src/CrowdedMod/Patches/CreateGameOptionsPatches.cs +// https://github.com/CrowdedMods/CrowdedMod/blob/master/src/CrowdedMod namespace EHR.Patches { internal static class Crowded { - public const int MaxPlayers = 127; public const int MaxImpostors = 127 / 2; - [HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.Awake))] - public static class CreateOptionsPickerAwake - { - public static void Postfix(CreateOptionsPicker __instance) - { - if (__instance.mode != SettingsMode.Host) return; - - { - var firstButtonRenderer = __instance.MaxPlayerButtons[0]; - firstButtonRenderer.GetComponentInChildren().text = "-"; - firstButtonRenderer.enabled = false; - - var firstButtonButton = firstButtonRenderer.GetComponent(); - firstButtonButton.OnClick.RemoveAllListeners(); - firstButtonButton.OnClick.AddListener((Action)(() => - { - for (var i = 1; i < 11; i++) - { - var playerButton = __instance.MaxPlayerButtons[i]; - - var tmp = playerButton.GetComponentInChildren(); - var newValue = Mathf.Max(byte.Parse(tmp.text) - 10, byte.Parse(playerButton.name) - 2); - tmp.text = newValue.ToString(); - } - - __instance.UpdateMaxPlayersButtons(__instance.GetTargetOptions()); - })); - Object.Destroy(firstButtonRenderer); - - var lastButtonRenderer = __instance.MaxPlayerButtons[^1]; - lastButtonRenderer.GetComponentInChildren().text = "+"; - lastButtonRenderer.enabled = false; - - var lastButtonButton = lastButtonRenderer.GetComponent(); - lastButtonButton.OnClick.RemoveAllListeners(); - lastButtonButton.OnClick.AddListener((Action)(() => - { - for (var i = 1; i < 11; i++) - { - var playerButton = __instance.MaxPlayerButtons[i]; - - var tmp = playerButton.GetComponentInChildren(); - var newValue = Mathf.Min(byte.Parse(tmp.text) + 10, - MaxPlayers - 14 + byte.Parse(playerButton.name)); - tmp.text = newValue.ToString(); - } - - __instance.UpdateMaxPlayersButtons(__instance.GetTargetOptions()); - })); - Object.Destroy(lastButtonRenderer); - - for (var i = 1; i < 11; i++) - { - var playerButton = __instance.MaxPlayerButtons[i].GetComponent(); - var text = playerButton.GetComponentInChildren(); - - playerButton.OnClick.RemoveAllListeners(); - playerButton.OnClick.AddListener((Action)(() => - { - var maxPlayers = byte.Parse(text.text); - var maxImp = Mathf.Min(__instance.GetTargetOptions().NumImpostors, maxPlayers / 2); - __instance.GetTargetOptions().SetInt(Int32OptionNames.NumImpostors, maxImp); - __instance.ImpostorButtons[1].TextMesh.text = maxImp.ToString(); - __instance.SetMaxPlayersButtons(maxPlayers); - })); - } - - foreach (var button in __instance.MaxPlayerButtons) - { - button.enabled = button.GetComponentInChildren().text == __instance.GetTargetOptions().MaxPlayers.ToString(); - } - } - - { - var secondButton = __instance.ImpostorButtons[1]; - secondButton.SpriteRenderer.enabled = false; - Object.Destroy(secondButton.transform.FindChild("ConsoleHighlight").gameObject); - Object.Destroy(secondButton.PassiveButton); - Object.Destroy(secondButton.BoxCollider); - - var secondButtonText = secondButton.TextMesh; - secondButtonText.text = __instance.GetTargetOptions().NumImpostors.ToString(); - - var firstButton = __instance.ImpostorButtons[0]; - firstButton.SpriteRenderer.enabled = false; - firstButton.TextMesh.text = "-"; - - var firstPassiveButton = firstButton.PassiveButton; - firstPassiveButton.OnClick.RemoveAllListeners(); - firstPassiveButton.OnClick.AddListener((Action)(() => - { - var newVal = Mathf.Clamp( - byte.Parse(secondButtonText.text) - 1, - 1, - __instance.GetTargetOptions().MaxPlayers / 2 - ); - __instance.SetImpostorButtons(newVal); - secondButtonText.text = newVal.ToString(); - })); - - var thirdButton = __instance.ImpostorButtons[2]; - thirdButton.SpriteRenderer.enabled = false; - thirdButton.TextMesh.text = "+"; - - var thirdPassiveButton = thirdButton.PassiveButton; - thirdPassiveButton.OnClick.RemoveAllListeners(); - thirdPassiveButton.OnClick.AddListener((Action)(() => - { - var newVal = Mathf.Clamp( - byte.Parse(secondButtonText.text) + 1, - 1, - __instance.GetTargetOptions().MaxPlayers / 2 - ); - __instance.SetImpostorButtons(newVal); - secondButtonText.text = newVal.ToString(); - })); - } - } - } - - [HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateMaxPlayersButtons))] - public static class CreateOptionsPickerUpdateMaxPlayersButtons - { - public static bool Prefix(CreateOptionsPicker __instance, [HarmonyArgument(0)] IGameOptions opts) - { - if (__instance.CrewArea) - { - __instance.CrewArea.SetCrewSize(opts.MaxPlayers, opts.NumImpostors); - } - - var selectedAsString = opts.MaxPlayers.ToString(); - for (var i = 1; i < __instance.MaxPlayerButtons.Count - 1; i++) - { - __instance.MaxPlayerButtons[i].enabled = __instance.MaxPlayerButtons[i].GetComponentInChildren().text == selectedAsString; - } - - return false; - } - } - - [HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateImpostorsButtons))] - public static class CreateOptionsPickerUpdateImpostorsButtons - { - public static bool Prefix() - { - return false; - } - } - [HarmonyPatch(typeof(GameOptionsData), nameof(GameOptionsData.AreInvalid))] public static class InvalidOptionsPatches { diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs index a61abc560..7b3ffc36b 100644 --- a/Patches/OnGameStartedPatch.cs +++ b/Patches/OnGameStartedPatch.cs @@ -367,7 +367,7 @@ internal static class StartGameHostPatch { private static AmongUsClient AUClient; - private static readonly Dictionary> BasisChangingAddons = []; + public static readonly Dictionary> BasisChangingAddons = []; private static Dictionary RoleTypeNums = []; private static readonly Dictionary DataDisconnected = []; diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index a9a6cbd24..fc7f3fcbe 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -494,9 +494,6 @@ static class CheckMurderPatch case CustomRoles.Medic: Medic.IsDead(target); break; - case CustomRoles.Monarch when killer.Is(CustomRoles.Knighted): - Notify("KnightedKillMonarch"); - return false; case CustomRoles.Gambler when Gambler.IsShielded.ContainsKey(target.PlayerId): Notify("SomeSortOfProtection"); killer.SetKillCooldown(time: 5f); diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 5faaf2f8c..3395a898c 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -1153,6 +1153,10 @@ "MenuTitle.GuesserModeRoles": "★ Roles and Add-ons for Guesser Mode ★", "MenuTitle.CTA": "★ Custom Team Assignment ★", "MenuTitle.AFKDetection": "★ AFK Detection ★", + "ReadyCheckMessage": "The host is checking how many players are ready to start the game.\\nPlease type /ready to let the host know you're not AFK.", + "ReadyCheckTitle": "<#ffff00>★ Ready Check ★", + "EveryoneReadyTitle": "<#00ff00>Everyone is ready!", + "PlayersNotReadyTitle": "<#ffff00><#ffab1b>{0} players are not ready! They are:", "ShieldPersonDiedFirst": "Shield the person who got killed first in the last game", "SidekickKnowOtherSidekick": "Sidekicks know each other", "SidekickKnowOtherSidekickRole": "Sidekicks know the roles of other Sidekicks", @@ -1701,7 +1705,6 @@ "RefugeeKillImpostor": "Renegades cannot kill Impostors", "GAGuarded": "This player is shielded by the Guardian Angel", "TraitorKillImpostor": "As the Traitor, you cannot kill Impostors", - "KnightedKillMonarch": "As the Knighted player, you cannot kill the Monarch", "RookieKillRoundOne": "As a Rookie, you cannot kill in the first round", "SocialiteTarget": "The target of the Socialite cannot be killed\\nYou were added to their Guest List", "BodyguardProtected": "The Bodyguard protected your target and died for them", @@ -2256,6 +2259,8 @@ "CommandDescription.Unmute": "Unmute someone.", "CommandDescription.DraftStart": "Start drafting, where players may choose their roles for the next game", "CommandDescription.Draft": "Choose a role in the draft", + "CommandDescription.ReadyCheck": "Start a ready check (check who is AFK) (results displayed after 30s)", + "CommandDescription.Ready": "Mark yourself as ready", "CommandArgs.ChangeSetting.Name": "The setting's name to change (leave blank to view all settings)", "CommandArgs.ChangeSetting.UnknownValue": "(leave blank to view what to put here)", "CommandArgs.Rename.Name": "Your new name", diff --git a/Roles/Neutral/Enderman.cs b/Roles/Neutral/Enderman.cs index 7b88b3b2d..3f45d607a 100644 --- a/Roles/Neutral/Enderman.cs +++ b/Roles/Neutral/Enderman.cs @@ -47,7 +47,7 @@ namespace EHR.Neutral public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool(); - public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool()); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool())); public override void ApplyGameOptions(IGameOptions opt, byte id) { diff --git a/Roles/Neutral/Evolver.cs b/Roles/Neutral/Evolver.cs index d7fe0a468..f237b3561 100644 --- a/Roles/Neutral/Evolver.cs +++ b/Roles/Neutral/Evolver.cs @@ -61,7 +61,7 @@ namespace EHR.Neutral public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = Stats.KillCooldown; public override bool CanUseImpostorVentButton(PlayerControl pc) => pc.inVent || Stats is { CanVent: true, VentUseLimit: > 0 }; - public override bool CanUseSabotage(PlayerControl pc) => Stats is { CanSabotage: true, SabotageUseLimit: > 0 }; + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || Stats is { CanSabotage: true, SabotageUseLimit: > 0 }; public override void ApplyGameOptions(IGameOptions opt, byte playerId) { diff --git a/Roles/Neutral/Hookshot.cs b/Roles/Neutral/Hookshot.cs index 14f3c09b6..b51cd6acc 100644 --- a/Roles/Neutral/Hookshot.cs +++ b/Roles/Neutral/Hookshot.cs @@ -49,7 +49,7 @@ namespace EHR.Neutral public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool(); - public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool()); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool())); public override void ApplyGameOptions(IGameOptions opt, byte id) { diff --git a/Roles/Neutral/Innocent.cs b/Roles/Neutral/Innocent.cs index 8e4e58a79..b8f3ebbe2 100644 --- a/Roles/Neutral/Innocent.cs +++ b/Roles/Neutral/Innocent.cs @@ -31,7 +31,7 @@ namespace EHR.Neutral public override bool CanUseSabotage(PlayerControl pc) { - return false; + return pc.Is(CustomRoles.Mischievous); } public override void ApplyGameOptions(IGameOptions opt, byte playerId) diff --git a/Roles/Neutral/Jackal.cs b/Roles/Neutral/Jackal.cs index 95ffd872e..d838b930c 100644 --- a/Roles/Neutral/Jackal.cs +++ b/Roles/Neutral/Jackal.cs @@ -96,7 +96,7 @@ public class Jackal : RoleBase public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool(); - public override bool CanUseSabotage(PlayerControl pc) => CanSabotage.GetBool() && pc.IsAlive(); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (CanSabotage.GetBool() && pc.IsAlive()); public override void SetButtonTexts(HudManager hud, byte id) { @@ -116,11 +116,11 @@ public class Jackal : RoleBase public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) { - if (killer.GetAbilityUseLimit() < 1 || !CanBeSidekick(target, out var needBasisChange, out var targetRoleType)) return true; + if (killer.GetAbilityUseLimit() < 1 || !CanBeSidekick(target)) return true; killer.RpcRemoveAbilityUse(); - target.RpcSetCustomRole(targetRoleType == RoleTypes.Shapeshifter ? CustomRoles.Recruit : CustomRoles.Sidekick); - if (needBasisChange) target.RpcChangeRoleBasis(CustomRoles.Sidekick); + target.RpcSetCustomRole(CustomRoles.Sidekick); + target.RpcChangeRoleBasis(CustomRoles.Sidekick); SidekickId = target.PlayerId; Main.ResetCamPlayerList.Add(target.PlayerId); @@ -139,13 +139,11 @@ public class Jackal : RoleBase return false; } - private static bool CanBeSidekick(PlayerControl pc, out bool needBasisChange, out RoleTypes targetRoleType) + private static bool CanBeSidekick(PlayerControl pc) { - targetRoleType = pc.GetRoleTypes(); - needBasisChange = targetRoleType is not RoleTypes.Impostor and not RoleTypes.Shapeshifter; if (!CanRecruitImpostors.GetBool() && pc.Is(CustomRoleTypes.Impostor)) return false; if (!CanRecruitMadmates.GetBool() && pc.IsMadmate()) return false; - return pc != null && !pc.Is(CustomRoles.Sidekick) && !pc.Is(CustomRoles.Recruit) && !pc.Is(CustomRoles.Loyal) && !pc.Is(CustomRoles.Rascal) && !pc.Is(CustomRoles.Charmed) && !pc.Is(CustomRoles.Contagious) && pc.GetCustomRole().IsAbleToBeSidekicked(); + return pc != null && !pc.Is(CustomRoles.Sidekick) && !pc.IsConverted() && pc.GetCustomRole().IsAbleToBeSidekicked(); } public override void OnFixedUpdate(PlayerControl pc) @@ -196,7 +194,7 @@ public class Sidekick : RoleBase public override bool CanUseImpostorVentButton(PlayerControl pc) => Jackal.CanVentSK.GetBool(); public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = Jackal.KillCooldownSK.GetFloat(); public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(Jackal.HasImpostorVision.GetBool()); - public override bool CanUseSabotage(PlayerControl pc) => Jackal.CanSabotageSK.GetBool() && pc.IsAlive(); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (Jackal.CanSabotageSK.GetBool() && pc.IsAlive()); public override void SetButtonTexts(HudManager __instance, byte id) { diff --git a/Roles/Neutral/Magician.cs b/Roles/Neutral/Magician.cs index 3987b1cf2..6560a0532 100644 --- a/Roles/Neutral/Magician.cs +++ b/Roles/Neutral/Magician.cs @@ -103,7 +103,7 @@ public class Magician : RoleBase public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool(); - public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool()); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool())); public override void ApplyGameOptions(IGameOptions opt, byte id) { diff --git a/Roles/Neutral/Maverick.cs b/Roles/Neutral/Maverick.cs index ab8f722e4..4181c9b42 100644 --- a/Roles/Neutral/Maverick.cs +++ b/Roles/Neutral/Maverick.cs @@ -48,7 +48,7 @@ public class Maverick : RoleBase public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(HasImpostorVision.GetBool()); public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool(); public override bool CanUseKillButton(PlayerControl pc) => pc.IsAlive(); - public override bool CanUseSabotage(PlayerControl pc) => false; + public override bool CanUseSabotage(PlayerControl pc) => pc.Is(CustomRoles.Mischievous); public override string GetProgressText(byte playerId, bool comms) { diff --git a/Roles/Neutral/Mycologist.cs b/Roles/Neutral/Mycologist.cs index 5a4afd791..155f5ec6c 100644 --- a/Roles/Neutral/Mycologist.cs +++ b/Roles/Neutral/Mycologist.cs @@ -166,6 +166,6 @@ namespace EHR.Neutral public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) => IsEnable && target != null && InfectedPlayers.Contains(target.PlayerId); public override void AfterMeetingTasks() => Mycologist_.AddAbilityCD(CD.GetInt()); public override bool CanUseImpostorVentButton(PlayerControl pc) => true; - public override bool CanUseSabotage(PlayerControl pc) => SpreadAction.GetValue() == 1 && pc.IsAlive(); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (SpreadAction.GetValue() == 1 && pc.IsAlive()); } } \ No newline at end of file diff --git a/Roles/Neutral/Patroller.cs b/Roles/Neutral/Patroller.cs index cbe7c078c..346a4114c 100644 --- a/Roles/Neutral/Patroller.cs +++ b/Roles/Neutral/Patroller.cs @@ -57,7 +57,7 @@ namespace EHR.Neutral public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = Utils.GetPlayerById(id).GetPlainShipRoom() == RoomBoosts[Boost.Cooldown] ? DecreasedKillCooldown.GetFloat() : KillCooldown.GetFloat(); public override bool CanUseImpostorVentButton(PlayerControl pc) => pc.inVent || pc.GetAbilityUseLimit() > 0 || pc.GetPlainShipRoom() == RoomBoosts[Boost.Vent]; - public override bool CanUseSabotage(PlayerControl pc) => pc.GetPlainShipRoom() == RoomBoosts[Boost.Sabotage]; + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (pc.GetPlainShipRoom() == RoomBoosts[Boost.Sabotage]); public override void ApplyGameOptions(IGameOptions opt, byte id) { diff --git a/Roles/Neutral/Provocateur.cs b/Roles/Neutral/Provocateur.cs index f9a8a238a..96af6a211 100644 --- a/Roles/Neutral/Provocateur.cs +++ b/Roles/Neutral/Provocateur.cs @@ -34,7 +34,7 @@ namespace EHR.Neutral public override bool CanUseSabotage(PlayerControl pc) { - return false; + return pc.Is(CustomRoles.Mischievous); } public override void SetKillCooldown(byte id) diff --git a/Roles/Neutral/Revolutionist.cs b/Roles/Neutral/Revolutionist.cs index d0f1e4163..7afb75c26 100644 --- a/Roles/Neutral/Revolutionist.cs +++ b/Roles/Neutral/Revolutionist.cs @@ -66,7 +66,7 @@ namespace EHR.Neutral public override bool CanUseSabotage(PlayerControl pc) { - return false; + return pc.Is(CustomRoles.Mischievous); } public override void SetKillCooldown(byte id) diff --git a/Roles/Neutral/Rogue.cs b/Roles/Neutral/Rogue.cs index 5a2187998..cd7f3538e 100644 --- a/Roles/Neutral/Rogue.cs +++ b/Roles/Neutral/Rogue.cs @@ -63,7 +63,7 @@ namespace EHR.Neutral public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = GotRewards.Contains(Reward.DecreasedKillCooldown) ? KillCooldown.GetFloat() / 2f : KillCooldown.GetFloat(); public override bool CanUseImpostorVentButton(PlayerControl pc) => CurrentTask.Objective == Objective.VentXTimes || CanVent.GetBool(); - public override bool CanUseSabotage(PlayerControl pc) => GotRewards.Contains(Reward.Sabotage) || GotRewards.Contains(Reward.Morph); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (GotRewards.Contains(Reward.Sabotage) || GotRewards.Contains(Reward.Morph)); public override void ApplyGameOptions(IGameOptions opt, byte id) { diff --git a/Roles/Neutral/RouleteGrandeur.cs b/Roles/Neutral/RouleteGrandeur.cs index 6ae653d41..ccd3e50f9 100644 --- a/Roles/Neutral/RouleteGrandeur.cs +++ b/Roles/Neutral/RouleteGrandeur.cs @@ -55,7 +55,7 @@ namespace EHR.Neutral public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KCD; public override bool CanUseImpostorVentButton(PlayerControl pc) => true; - public override bool CanUseSabotage(PlayerControl pc) => !(Options.UsePhantomBasis.GetBool() && Options.UsePhantomBasisForNKs.GetBool()); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (!(Options.UsePhantomBasis.GetBool() && Options.UsePhantomBasisForNKs.GetBool())); public override void ApplyGameOptions(IGameOptions opt, byte id) { diff --git a/Roles/Neutral/Simon.cs b/Roles/Neutral/Simon.cs index 3e7badba0..0bb65b9f4 100644 --- a/Roles/Neutral/Simon.cs +++ b/Roles/Neutral/Simon.cs @@ -60,7 +60,7 @@ namespace EHR.Neutral public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); public override bool CanUseImpostorVentButton(PlayerControl pc) => pc.IsAlive(); - public override bool CanUseSabotage(PlayerControl pc) => !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool()); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (!(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool())); public override void ApplyGameOptions(IGameOptions opt, byte id) { diff --git a/Roles/Neutral/Sprayer.cs b/Roles/Neutral/Sprayer.cs index fed26173f..caaa577ba 100644 --- a/Roles/Neutral/Sprayer.cs +++ b/Roles/Neutral/Sprayer.cs @@ -81,7 +81,7 @@ namespace EHR.Neutral public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool(); - public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool()); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool())); public override void ApplyGameOptions(IGameOptions opt, byte id) { diff --git a/Roles/Neutral/Tiger.cs b/Roles/Neutral/Tiger.cs index db9f211bb..f969e4863 100644 --- a/Roles/Neutral/Tiger.cs +++ b/Roles/Neutral/Tiger.cs @@ -66,7 +66,7 @@ namespace EHR.Neutral public override bool CanUseSabotage(PlayerControl pc) { - return pc.IsAlive() && !(Options.UsePhantomBasis.GetBool() && Options.UsePhantomBasisForNKs.GetBool()); + return base.CanUseSabotage(pc) || (pc.IsAlive() && !(Options.UsePhantomBasis.GetBool() && Options.UsePhantomBasisForNKs.GetBool())); } public override void ApplyGameOptions(IGameOptions opt, byte playerId) diff --git a/Roles/Neutral/Traitor.cs b/Roles/Neutral/Traitor.cs index f4dfdbaea..d09873c0f 100644 --- a/Roles/Neutral/Traitor.cs +++ b/Roles/Neutral/Traitor.cs @@ -43,5 +43,5 @@ public class Traitor : RoleBase public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(HasImpostorVision.GetBool()); public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool(); - public override bool CanUseSabotage(PlayerControl pc) => CanSabotage.GetBool() && pc.IsAlive(); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (CanSabotage.GetBool() && pc.IsAlive()); } \ No newline at end of file diff --git a/Roles/Neutral/WeaponMaster.cs b/Roles/Neutral/WeaponMaster.cs index a4a50a56e..9e578af4c 100644 --- a/Roles/Neutral/WeaponMaster.cs +++ b/Roles/Neutral/WeaponMaster.cs @@ -89,7 +89,7 @@ public class WeaponMaster : RoleBase public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool(); - public override bool CanUseSabotage(PlayerControl pc) => pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool()); + public override bool CanUseSabotage(PlayerControl pc) => base.CanUseSabotage(pc) || (pc.IsAlive() && !(UsePhantomBasis.GetBool() && UsePhantomBasisForNKs.GetBool())); public override void ApplyGameOptions(IGameOptions opt, byte id) {