diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 8e3ccaa31..c4fcbb651 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -922,6 +922,13 @@ internal static class CustomRolesHelper return type.GetMethod("OnMeetingShapeshift")?.DeclaringType == type; } + public bool UsesJudgeAbilityAsTrigger() + { + if (!Options.UseJudgeAbilityAsTrigger.GetBool()) return false; + Type type = role.GetRoleClass().GetType(); + return type.GetMethod("OnJudge")?.DeclaringType == type; + } + public bool PetActivatedAbility() { if (Options.CurrentGameMode == CustomGameMode.CaptureTheFlag) return true; diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index ae44e2bbf..1e12ee058 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -61,6 +61,13 @@ internal static class ExtendedPlayerControl return role.UsesMeetingShapeshift(); } + public bool UsesJudgeAbilityAsTrigger() + { + CustomRoles role = player.GetCustomRole(); + if (player.IsModdedClient() && role is CustomRoles.Councillor or CustomRoles.Inspector or CustomRoles.JudgeOld or CustomRoles.Retributionist or CustomRoles.Starspawn or CustomRoles.Swapper or CustomRoles.Ventriloquist) return false; + return role.UsesJudgeAbilityAsTrigger(); + } + public bool CanUseVent() { try { return player.CanUseVent(player.GetClosestVent()?.Id ?? int.MaxValue); } diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index a2e412016..a69d60123 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -798,6 +798,7 @@ public static class Options public static OptionItem EveryoneSeesDeadPlayersRoles; public static OptionItem UsePhantomBasis; public static OptionItem UsePhantomBasisForNKs; + public static OptionItem UseJudgeAbilityAsTrigger; public static OptionItem UseMeetingShapeshift; public static OptionItem UseMeetingShapeshiftForGuessing; public static OptionItem AutoKickStart; @@ -2502,7 +2503,12 @@ public static class Options .SetParent(UsePhantomBasis) .SetColor(new Color32(255, 255, 44, byte.MaxValue)); - UseMeetingShapeshift = new BooleanOptionItem(23865, "UseMeetingShapeshift", true, TabGroup.TaskSettings) + UseJudgeAbilityAsTrigger = new BooleanOptionItem(23865, "UseJudgeAbilityAsTrigger", true, TabGroup.TaskSettings) + .SetGameMode(CustomGameMode.Standard) + .SetHeader(true) + .SetColor(Palette.Orange); + + UseMeetingShapeshift = new BooleanOptionItem(23867, "UseMeetingShapeshift", false, TabGroup.TaskSettings) .SetGameMode(CustomGameMode.Standard) .SetHeader(true) .SetColor(Palette.Orange); diff --git a/Modules/RPC.cs b/Modules/RPC.cs index 214b48daf..8a7f2a334 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -235,7 +235,8 @@ internal static class RPCHandlerPatch [RpcCalls.CheckSpore] = 5, [RpcCalls.CheckShapeshift] = 25, [RpcCalls.CheckVanish] = 25, - [RpcCalls.CheckAppear] = 25 + [RpcCalls.CheckAppear] = 25, + [RpcCalls.QueueOverruleVotes] = 3 }; public static void WhiteListFromRateLimitUntil(byte id, long timestamp) diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index 6854864fa..302395b43 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -1067,7 +1067,16 @@ internal static class MeetingHudStartPatch sender.SendMessage(); }, 3f, "SetName To Chat"); - if (Options.UseMeetingShapeshift.GetBool()) + if (Options.UseJudgeAbilityAsTrigger.GetBool()) + { + LateTask.New(() => + { + if (!MeetingHud.Instance || MeetingHud.Instance.state is MeetingHud.MeetingStates.Results or MeetingHud.MeetingStates.Proceeding) return; + + Main.EnumerateAlivePlayerControls().DoIf(x => x.UsesJudgeAbilityAsTrigger(), x => x.RpcSetRoleDesync(RoleTypes.Judge, x.OwnerId)); + }, 1f, "Set Shapeshifter Role For Meeting Use"); + } + else if (Options.UseMeetingShapeshift.GetBool()) { LateTask.New(() => { @@ -1376,17 +1385,26 @@ internal static class MeetingHudOnDestroyPatch Main.LastVotedPlayerInfo = null; - if (meetingSS && !AntiBlackout.SkipTasks) + if (!AntiBlackout.SkipTasks) { - bool restrictions = Options.GuesserNumRestrictions.GetBool(); - - foreach (PlayerControl pc in Main.CachedAlivePlayerControls()) + if (Options.UseJudgeAbilityAsTrigger.GetBool()) { - if (pc.UsesMeetingShapeshift() || (meetingSSForGuessing && !pc.IsModdedClient() && GuessManager.StartMeetingPatch.CanGuess(pc, restrictions))) - pc.RpcSetRoleDesync(pc.GetRoleTypes(), pc.OwnerId); + foreach (PlayerControl pc in Main.CachedAlivePlayerControls()) + if (pc.UsesJudgeAbilityAsTrigger()) + pc.RpcSetRoleDesync(pc.GetRoleTypes(), pc.OwnerId); + } + else if (meetingSS) + { + bool restrictions = Options.GuesserNumRestrictions.GetBool(); - if (pc.IsImpostor()) - pc.RpcSetRoleGlobal(pc.GetRoleTypes()); + foreach (PlayerControl pc in Main.CachedAlivePlayerControls()) + { + if (pc.UsesMeetingShapeshift() || (meetingSSForGuessing && !pc.IsModdedClient() && GuessManager.StartMeetingPatch.CanGuess(pc, restrictions))) + pc.RpcSetRoleDesync(pc.GetRoleTypes(), pc.OwnerId); + + if (pc.IsImpostor()) + pc.RpcSetRoleGlobal(pc.GetRoleTypes()); + } } } } @@ -1603,30 +1621,46 @@ internal static class MeetingHudHandleRpcPatch { public static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1)] MessageReader reader) { - if (callId == (byte)RpcCalls.CloseMeeting) + switch (callId) { - if (AmongUsClient.Instance.AmHost) + case (byte)RpcCalls.QueueOverruleVotes: + { + byte judgePlayerId = reader.ReadByte(); + byte targetPlayerId = reader.ReadByte(); + + PlayerControl judge = judgePlayerId.GetPlayer(); + PlayerControl target = targetPlayerId.GetPlayer(); + + if (judge && target && judge.UsesJudgeAbilityAsTrigger() && Main.PlayerStates.TryGetValue(judgePlayerId, out PlayerState state)) + return !state.Role.OnJudge(judge, target); + break; + } + case (byte)RpcCalls.CloseMeeting when AmongUsClient.Instance.AmHost: { EAC.WarnHost(4); Logger.Warn("MeetingHud.HandleRpc CloseMeeting is being called, impossible to receive as host.", "MeetingHudHandleRpcPatch"); return false; } - - Logger.Info("Received Close Meeting Rpc", "MeetingHudHandleRpcPatch"); - - if (reader.BytesRemaining > 6) + case (byte)RpcCalls.CloseMeeting: { - try - { - string temp = reader.ReadString(); + Logger.Info("Received Close Meeting Rpc", "MeetingHudHandleRpcPatch"); - if (temp.Contains(" 6) + { + try { - Logger.Info($"Read Name From Rpc: {temp}", "MeetingHudHandleRpcPatch"); - CheckForEndVotingPatch.EjectionText = temp; + string temp = reader.ReadString(); + + if (temp.Contains("Neutral <#ff1919>Killing (Desynced) roles <#ffff00>(EXPERIMENTAL)", + "UseJudgeAbilityAsTrigger": "Use Judge Ability in Meetings to Select Targets Instead of Commands/Votes", "UseMeetingShapeshift": "Use Shapeshift Menu in Meetings to Select Targets Instead of Commands/Votes", "UseMeetingShapeshiftForGuessing": "Even For Guessing (can cause frequent disconnects) (EXTREMELY EXPERIMENTAL ALPHA VERSION)", "EveryoneCanVent": "Everyone Can Vent", diff --git a/Roles/RoleBase.cs b/Roles/RoleBase.cs index ca3d8c9f9..9bb13d6c3 100644 --- a/Roles/RoleBase.cs +++ b/Roles/RoleBase.cs @@ -156,6 +156,11 @@ public abstract class RoleBase : IComparable return false; } + public virtual bool OnJudge(PlayerControl voter, PlayerControl target) + { + return false; + } + public virtual void OnReportDeadBody() { } public virtual bool CheckReportDeadBody(PlayerControl reporter, NetworkedPlayerInfo target, PlayerControl killer) diff --git a/Roles/Standard/Coven/Empress.cs b/Roles/Standard/Coven/Empress.cs index d41bdb3e4..1eb3e7095 100644 --- a/Roles/Standard/Coven/Empress.cs +++ b/Roles/Standard/Coven/Empress.cs @@ -99,7 +99,7 @@ public class Empress : CovenBase SetKillCooldown(EmpressId); } - public override bool OnVote(PlayerControl voter, PlayerControl target) + public override bool OnJudge(PlayerControl voter, PlayerControl target) { if (Starspawn.IsDayBreak || voter == null || target == null || Main.DontCancelVoteList.Contains(voter.PlayerId) || voter.PlayerId != target.PlayerId || Empowered) return false; diff --git a/Roles/Standard/Crewmate/Investigate/Captain.cs b/Roles/Standard/Crewmate/Investigate/Captain.cs index 830addd2f..bd925b012 100644 --- a/Roles/Standard/Crewmate/Investigate/Captain.cs +++ b/Roles/Standard/Crewmate/Investigate/Captain.cs @@ -40,7 +40,7 @@ public class Captain : RoleBase TargetRooms = []; } - public override bool OnVote(PlayerControl voter, PlayerControl target) + public override bool OnJudge(PlayerControl voter, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (voter == null || target == null || voter.PlayerId == target.PlayerId || Main.DontCancelVoteList.Contains(voter.PlayerId)) return false; diff --git a/Roles/Standard/Crewmate/Investigate/FortuneTeller.cs b/Roles/Standard/Crewmate/Investigate/FortuneTeller.cs index 4954958ce..93adabdbd 100644 --- a/Roles/Standard/Crewmate/Investigate/FortuneTeller.cs +++ b/Roles/Standard/Crewmate/Investigate/FortuneTeller.cs @@ -93,7 +93,7 @@ public class FortuneTeller : RoleBase }, 8f, log: false); } - public override bool OnVote(PlayerControl player, PlayerControl target) + public override bool OnJudge(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (player == null || target == null) return false; diff --git a/Roles/Standard/Crewmate/Investigate/Inquisitor.cs b/Roles/Standard/Crewmate/Investigate/Inquisitor.cs index 1cc787772..78be710e2 100644 --- a/Roles/Standard/Crewmate/Investigate/Inquisitor.cs +++ b/Roles/Standard/Crewmate/Investigate/Inquisitor.cs @@ -35,7 +35,7 @@ public class Inquisitor : RoleBase playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat()); } - public override bool OnVote(PlayerControl voter, PlayerControl target) + public override bool OnJudge(PlayerControl voter, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (!voter || !target || voter.PlayerId == target.PlayerId || Main.DontCancelVoteList.Contains(voter.PlayerId)) return false; diff --git a/Roles/Standard/Crewmate/Investigate/Markseeker.cs b/Roles/Standard/Crewmate/Investigate/Markseeker.cs index 86c04262f..3513035ee 100644 --- a/Roles/Standard/Crewmate/Investigate/Markseeker.cs +++ b/Roles/Standard/Crewmate/Investigate/Markseeker.cs @@ -46,7 +46,7 @@ internal class Markseeker : RoleBase } /* - public override bool OnVote(PlayerControl player, PlayerControl target) + public override bool OnJudge(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (player == null || target == null || player.PlayerId == target.PlayerId || MarkedId != byte.MaxValue || Main.DontCancelVoteList.Contains(player.PlayerId)) return false; diff --git a/Roles/Standard/Crewmate/Investigate/Oracle.cs b/Roles/Standard/Crewmate/Investigate/Oracle.cs index 353608aeb..22005b337 100644 --- a/Roles/Standard/Crewmate/Investigate/Oracle.cs +++ b/Roles/Standard/Crewmate/Investigate/Oracle.cs @@ -57,7 +57,7 @@ public class Oracle : RoleBase playerId.SetAbilityUseLimit(CheckLimitOpt.GetFloat()); } - public override bool OnVote(PlayerControl player, PlayerControl target) + public override bool OnJudge(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (player == null || target == null) return false; diff --git a/Roles/Standard/Crewmate/Investigate/Scout.cs b/Roles/Standard/Crewmate/Investigate/Scout.cs index c847a7a8a..ee564a64f 100644 --- a/Roles/Standard/Crewmate/Investigate/Scout.cs +++ b/Roles/Standard/Crewmate/Investigate/Scout.cs @@ -97,7 +97,7 @@ public class Scout : RoleBase 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) + public override bool OnJudge(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) 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; diff --git a/Roles/Standard/Crewmate/Investigate/Socialite.cs b/Roles/Standard/Crewmate/Investigate/Socialite.cs index 3ed03b414..904a8cb27 100644 --- a/Roles/Standard/Crewmate/Investigate/Socialite.cs +++ b/Roles/Standard/Crewmate/Investigate/Socialite.cs @@ -102,7 +102,7 @@ public class Socialite : RoleBase return true; } - public override bool OnVote(PlayerControl pc, PlayerControl target) + public override bool OnJudge(PlayerControl pc, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (pc == null || target == null || pc.PlayerId == target.PlayerId || Main.DontCancelVoteList.Contains(pc.PlayerId)) return false; diff --git a/Roles/Standard/Crewmate/Investigate/Soothsayer.cs b/Roles/Standard/Crewmate/Investigate/Soothsayer.cs index 11d0ae97c..be0ce4b41 100644 --- a/Roles/Standard/Crewmate/Investigate/Soothsayer.cs +++ b/Roles/Standard/Crewmate/Investigate/Soothsayer.cs @@ -41,7 +41,7 @@ public class Soothsayer : RoleBase Instances?.Remove(this); } - public override bool OnVote(PlayerControl player, PlayerControl target) + public override bool OnJudge(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (player == null || target == null || player.PlayerId == target.PlayerId) return false; diff --git a/Roles/Standard/Crewmate/Miscellaneous/Ricochet.cs b/Roles/Standard/Crewmate/Miscellaneous/Ricochet.cs index f281e9623..38bc1fcc0 100644 --- a/Roles/Standard/Crewmate/Miscellaneous/Ricochet.cs +++ b/Roles/Standard/Crewmate/Miscellaneous/Ricochet.cs @@ -92,7 +92,7 @@ public class Ricochet : RoleBase return true; } - public override bool OnVote(PlayerControl pc, PlayerControl target) + public override bool OnJudge(PlayerControl pc, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (target == null || pc == null || pc.PlayerId == target.PlayerId || Main.DontCancelVoteList.Contains(pc.PlayerId)) return false; diff --git a/Roles/Standard/Crewmate/Miscellaneous/Tether.cs b/Roles/Standard/Crewmate/Miscellaneous/Tether.cs index 129a5cee0..c387491c2 100644 --- a/Roles/Standard/Crewmate/Miscellaneous/Tether.cs +++ b/Roles/Standard/Crewmate/Miscellaneous/Tether.cs @@ -114,7 +114,7 @@ public class Tether : RoleBase AURoleOptions.EngineerInVentMaxTime = 1f; } - public override bool OnVote(PlayerControl pc, PlayerControl target) + public override bool OnJudge(PlayerControl pc, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (pc == null || target == null || pc.PlayerId == target.PlayerId || Main.DontCancelVoteList.Contains(pc.PlayerId)) return false; diff --git a/Roles/Standard/Crewmate/Power/NiceEraser.cs b/Roles/Standard/Crewmate/Power/NiceEraser.cs index 2e0e88547..7094fb0ba 100644 --- a/Roles/Standard/Crewmate/Power/NiceEraser.cs +++ b/Roles/Standard/Crewmate/Power/NiceEraser.cs @@ -57,7 +57,7 @@ internal class NiceEraser : RoleBase PlayerIdList.Remove(playerId); } - public override bool OnVote(PlayerControl player, PlayerControl target) + public override bool OnJudge(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (player == null || target == null) return false; diff --git a/Roles/Standard/Crewmate/Power/President.cs b/Roles/Standard/Crewmate/Power/President.cs index 89e44c0fd..380509f86 100644 --- a/Roles/Standard/Crewmate/Power/President.cs +++ b/Roles/Standard/Crewmate/Power/President.cs @@ -240,7 +240,7 @@ public class President : RoleBase return target.PlayerId == PresidentId && IsRevealed; } - public override bool OnVote(PlayerControl voter, PlayerControl target) + public override bool OnJudge(PlayerControl voter, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (voter.PlayerId != PresidentId || !IsRecruiting || Main.DontCancelVoteList.Contains(voter.PlayerId)) return false; diff --git a/Roles/Standard/Crewmate/Support/Cleanser.cs b/Roles/Standard/Crewmate/Support/Cleanser.cs index 458ff2d13..c223cb0e4 100644 --- a/Roles/Standard/Crewmate/Support/Cleanser.cs +++ b/Roles/Standard/Crewmate/Support/Cleanser.cs @@ -90,7 +90,7 @@ public class Cleanser : RoleBase cs.CleanserUses = Limit; } - public override bool OnVote(PlayerControl voter, PlayerControl target) + public override bool OnJudge(PlayerControl voter, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (DidVote[voter.PlayerId] || Main.DontCancelVoteList.Contains(voter.PlayerId)) return false; diff --git a/Roles/Standard/Crewmate/Support/Negotiator.cs b/Roles/Standard/Crewmate/Support/Negotiator.cs index 2ecf76186..bd29e2059 100644 --- a/Roles/Standard/Crewmate/Support/Negotiator.cs +++ b/Roles/Standard/Crewmate/Support/Negotiator.cs @@ -124,7 +124,7 @@ public class Negotiator : RoleBase } } - public override bool OnVote(PlayerControl voter, PlayerControl target) + public override bool OnJudge(PlayerControl voter, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (target == null || voter == null || voter.PlayerId == target.PlayerId || TargetId != byte.MaxValue || voter.GetAbilityUseLimit() < 1f || MinVotingTimeLeftToNegotiate.GetInt() > MeetingTimeManager.VotingTimeLeft || Main.DontCancelVoteList.Contains(voter.PlayerId)) return false; diff --git a/Roles/Standard/Impostor/Support/EvilEraser.cs b/Roles/Standard/Impostor/Support/EvilEraser.cs index eacd698da..92065c5d0 100644 --- a/Roles/Standard/Impostor/Support/EvilEraser.cs +++ b/Roles/Standard/Impostor/Support/EvilEraser.cs @@ -98,7 +98,7 @@ internal class EvilEraser : RoleBase return true; } - public override bool OnVote(PlayerControl player, PlayerControl target) + public override bool OnJudge(PlayerControl player, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (player == null || target == null || EraseMethod.GetInt() == 0) return false; diff --git a/Roles/Standard/Impostor/Support/Godfather.cs b/Roles/Standard/Impostor/Support/Godfather.cs index 802a09f4b..b413b5748 100644 --- a/Roles/Standard/Impostor/Support/Godfather.cs +++ b/Roles/Standard/Impostor/Support/Godfather.cs @@ -22,7 +22,7 @@ internal class Godfather : RoleBase On = false; } - public override bool OnVote(PlayerControl voter, PlayerControl target) + public override bool OnJudge(PlayerControl voter, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (voter == null || target == null || voter.PlayerId == target.PlayerId || Main.DontCancelVoteList.Contains(voter.PlayerId)) return false; diff --git a/Roles/Standard/Neutral/Pariah/Clerk.cs b/Roles/Standard/Neutral/Pariah/Clerk.cs index ad3d0a882..13e157b68 100644 --- a/Roles/Standard/Neutral/Pariah/Clerk.cs +++ b/Roles/Standard/Neutral/Pariah/Clerk.cs @@ -42,7 +42,7 @@ public class Clerk : RoleBase playerId.SetAbilityUseLimit(AbilityUseLimit.GetInt()); } - public override bool OnVote(PlayerControl voter, PlayerControl target) + public override bool OnJudge(PlayerControl voter, PlayerControl target) { if (Starspawn.IsDayBreak) return false; if (Main.DontCancelVoteList.Contains(voter.PlayerId)) return false;