diff --git a/Strings.xlsx b/Strings.xlsx index e8ba7d53..2a6c3a12 100644 Binary files a/Strings.xlsx and b/Strings.xlsx differ diff --git a/TheOtherRoles/Buttons/Buttons.cs b/TheOtherRoles/Buttons/Buttons.cs index c9c0ca00..1ef7f18b 100644 --- a/TheOtherRoles/Buttons/Buttons.cs +++ b/TheOtherRoles/Buttons/Buttons.cs @@ -27,6 +27,7 @@ internal static class HudManagerStartPatch private static CustomButton deputyHandcuffButton; public static CustomButton timeMasterShieldButton; private static CustomButton amnisiacRememberButton; + private static CustomButton specterRememberButton; public static CustomButton veteranAlertButton; public static CustomButton medicShieldButton; private static CustomButton shifterShiftButton; @@ -166,7 +167,7 @@ public static void setCustomButtonCooldowns() garlicButton.MaxTimer = defaultMaxTimer; jackalKillButton.MaxTimer = Jackal.cooldown; werewolfKillButton.MaxTimer = Werewolf.killCooldown; - sidekickKillButton.MaxTimer = Sidekick.cooldown; + sidekickKillButton.MaxTimer = Jackal.cooldown; jackalSidekickButton.MaxTimer = Jackal.createSidekickCooldown; eraserButton.MaxTimer = Eraser.cooldown; placeJackInTheBoxButton.MaxTimer = Trickster.placeBoxCooldown; @@ -179,6 +180,7 @@ public static void setCustomButtonCooldowns() arsonistButton.MaxTimer = Arsonist.cooldown; vultureEatButton.MaxTimer = Vulture.cooldown; amnisiacRememberButton.MaxTimer = defaultMaxTimer; + specterRememberButton.MaxTimer = 10f; grenadierFlashButton.MaxTimer = Grenadier.cooldown; bomberGiveButton.MaxTimer = 0f; bomberGiveButton.Timer = 0f; @@ -447,15 +449,10 @@ public static void createButtonsPostfix(HudManager __instance) engineerRepairButton = new CustomButton( () => { - engineerRepairButton.Timer = 0f; - var usedRepairWriter = StartRPC(CachedPlayer.LocalPlayer.PlayerControl, CustomRPC.EngineerUsedRepair); - usedRepairWriter.EndRPC(); - RPCProcedure.engineerUsedRepair(); - SoundEffectsManager.play("engineerRepair"); foreach (var task in CachedPlayer.LocalPlayer.PlayerControl.myTasks.GetFastEnumerator()) if (task.TaskType == TaskTypes.FixLights) { - var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl.NetId, CustomRPC.EngineerFixLights); + var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl.NetId, CustomRPC.FixLights); writer.EndRPC(); RPCProcedure.FixLights(); } @@ -484,10 +481,13 @@ public static void createButtonsPostfix(HudManager __instance) } else if (SubmergedCompatibility.IsSubmerged && task.TaskType == SubmergedCompatibility.RetrieveOxygenMask) { - var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl, CustomRPC.EngineerFixSubmergedOxygen); + var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl, CustomRPC.FixSubmergedOxygen); writer.EndRPC(); RPCProcedure.FixSubmergedOxygen(); } + SoundEffectsManager.play("engineerRepair"); + Engineer.remainingFixes--; + engineerRepairButton.Timer = 0f; }, () => { @@ -496,14 +496,7 @@ public static void createButtonsPostfix(HudManager __instance) }, () => { - var sabotageActive = false; - foreach (var task in CachedPlayer.LocalPlayer.PlayerControl.myTasks.GetFastEnumerator()) - if (task.TaskType == TaskTypes.FixLights || task.TaskType == TaskTypes.RestoreOxy || - task.TaskType == TaskTypes.ResetReactor || task.TaskType == TaskTypes.ResetSeismic || - task.TaskType == TaskTypes.FixComms || task.TaskType == TaskTypes.StopCharles - || (SubmergedCompatibility.IsSubmerged && task.TaskType == SubmergedCompatibility.RetrieveOxygenMask)) - sabotageActive = true; - return sabotageActive && Engineer.remainingFixes > 0 && CachedPlayer.LocalPlayer.PlayerControl.CanMove; + return isSabotageActive() && Engineer.remainingFixes > 0 && CachedPlayer.LocalPlayer.PlayerControl.CanMove; }, () => { @@ -519,12 +512,10 @@ public static void createButtonsPostfix(HudManager __instance) ghostEngineerButton = new CustomButton( () => { - ghostEngineerButton.Timer = 0f; - SoundEffectsManager.play("engineerRepair"); foreach (var task in CachedPlayer.LocalPlayer.PlayerControl.myTasks.GetFastEnumerator()) if (task.TaskType == TaskTypes.FixLights) { - var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl, CustomRPC.EngineerFixLights); + var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl, CustomRPC.FixLights); writer.EndRPC(); RPCProcedure.FixLights(); } @@ -553,26 +544,22 @@ public static void createButtonsPostfix(HudManager __instance) } else if (SubmergedCompatibility.IsSubmerged && task.TaskType == SubmergedCompatibility.RetrieveOxygenMask) { - var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl, CustomRPC.EngineerFixSubmergedOxygen); + var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl, CustomRPC.FixSubmergedOxygen); writer.EndRPC(); RPCProcedure.FixSubmergedOxygen(); } + GhostEngineer.Fixes = true; + ghostEngineerButton.Timer = 0f; + SoundEffectsManager.play("engineerRepair"); }, () => { return GhostEngineer.player != null && GhostEngineer.player == CachedPlayer.LocalPlayer.PlayerControl && - !GhostEngineer.Fixes && CachedPlayer.LocalPlayer.Data.IsDead; + !GhostEngineer.Fixes && CachedPlayer.LocalPlayer.Data.IsDead && !GhostEngineer.Fixes; }, () => { - var sabotageActive = false; - foreach (var task in CachedPlayer.LocalPlayer.PlayerControl.myTasks.GetFastEnumerator()) - if (task.TaskType == TaskTypes.FixLights || task.TaskType == TaskTypes.RestoreOxy || - task.TaskType == TaskTypes.ResetReactor || task.TaskType == TaskTypes.ResetSeismic || - task.TaskType == TaskTypes.FixComms || task.TaskType == TaskTypes.StopCharles - || (SubmergedCompatibility.IsSubmerged && task.TaskType == SubmergedCompatibility.RetrieveOxygenMask)) - sabotageActive = true; - return sabotageActive && Engineer.remainingFixes > 0 && CachedPlayer.LocalPlayer.PlayerControl.CanMove; + return isSabotageActive() && GhostEngineer.Fixes && CachedPlayer.LocalPlayer.PlayerControl.CanMove; }, () => { }, Engineer.buttonSprite, @@ -1889,6 +1876,17 @@ public static void createButtonsPostfix(HudManager __instance) () => { if (checkAndDoVetKill(Jackal.currentTarget)) return; + var target = Jackal.currentTarget; + + if (Jackal.killFakeImpostor && target.Data.Role.IsImpostor) + { + //uncheckedMurderPlayer(Jackal.jackal.PlayerId, player.PlayerId, 1); + checkMurderAttemptAndKill(PlayerControl.LocalPlayer, target); + GameHistory.RpcOverrideDeathReasonAndKiller(target, CustomDeathReason.FakeSK, PlayerControl.LocalPlayer); + jackalSidekickButton.Timer = jackalSidekickButton.MaxTimer; + return; + } + var writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.JackalCreatesSidekick, SendOption.Reliable); writer.Write(Jackal.currentTarget.PlayerId); @@ -1900,12 +1898,12 @@ public static void createButtonsPostfix(HudManager __instance) }, () => { - return Jackal.canCreateSidekick && Jackal.jackal != null && - Jackal.jackal == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; + return Jackal.canCreateSidekick && Jackal.jackal.Any(x => x.IsAlive() && x == CachedPlayer.LocalPlayer.PlayerControl); }, () => { - showTargetNameOnButton(Jackal.currentTarget, jackalSidekickButton, GetString("jackalSidekickText")); // Show now text since the button already says sidekick + // Show now text since the button already says sidekick + showTargetNameOnButton(Jackal.currentTarget, jackalSidekickButton, GetString("jackalSidekickText")); return Jackal.canCreateSidekick && Jackal.currentTarget != null && CachedPlayer.LocalPlayer.PlayerControl.CanMove; }, @@ -1921,15 +1919,14 @@ public static void createButtonsPostfix(HudManager __instance) () => { if (checkAndDoVetKill(Jackal.currentTarget)) return; - if (checkMurderAttemptAndKill(Jackal.jackal, Jackal.currentTarget) == + if (checkMurderAttemptAndKill(CachedPlayer.LocalPlayer.PlayerControl, Jackal.currentTarget) == MurderAttemptResult.SuppressKill) return; jackalKillButton.Timer = jackalKillButton.MaxTimer; - Jackal.currentTarget = null; }, () => { - return Jackal.jackal != null && Jackal.jackal == CachedPlayer.LocalPlayer.PlayerControl && + return Jackal.jackal != null && Jackal.jackal.Any(x => x == CachedPlayer.LocalPlayer.PlayerControl) && !CachedPlayer.LocalPlayer.Data.IsDead; }, () => @@ -1950,15 +1947,15 @@ public static void createButtonsPostfix(HudManager __instance) { /* On Use */ var invisibleWriter = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.SetJackalSwoop, SendOption.Reliable, -1); - invisibleWriter.Write(Jackal.jackal.PlayerId); + invisibleWriter.Write(CachedPlayer.LocalPlayer.PlayerControl.PlayerId); invisibleWriter.Write(byte.MinValue); AmongUsClient.Instance.FinishRpcImmediately(invisibleWriter); - RPCProcedure.setJackalSwoop(Jackal.jackal.PlayerId, byte.MinValue); + RPCProcedure.setJackalSwoop(CachedPlayer.LocalPlayer.PlayerControl.PlayerId, byte.MinValue); }, () => { /* Can See */ return Jackal.jackal != null && Jackal.canSwoop && - Jackal.jackal == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; + Jackal.jackal.Any(x => x == CachedPlayer.LocalPlayer.PlayerControl) && !CachedPlayer.LocalPlayer.Data.IsDead; }, () => { /* On Click */ @@ -1985,22 +1982,22 @@ public static void createButtonsPostfix(HudManager __instance) sidekickKillButton = new CustomButton( () => { - if (checkAndDoVetKill(Sidekick.currentTarget)) return; - if (checkMurderAttemptAndKill(Sidekick.sidekick, Sidekick.currentTarget) == + if (checkAndDoVetKill(Jackal.currentTarget2)) return; + if (checkMurderAttemptAndKill(Jackal.sidekick, Jackal.currentTarget2) == MurderAttemptResult.SuppressKill) return; sidekickKillButton.Timer = sidekickKillButton.MaxTimer; - Sidekick.currentTarget = null; + Jackal.currentTarget2 = null; }, () => { - return Sidekick.canKill && Sidekick.sidekick != null && - Sidekick.sidekick == CachedPlayer.LocalPlayer.PlayerControl && + return Jackal.sidekickCanKill && Jackal.sidekick != null && + Jackal.sidekick == CachedPlayer.LocalPlayer.PlayerControl && !CachedPlayer.LocalPlayer.Data.IsDead; }, () => { - showTargetNameOnButton(Sidekick.currentTarget, sidekickKillButton, GetString("killButtonText")); - return Sidekick.currentTarget && CachedPlayer.LocalPlayer.PlayerControl.CanMove; + showTargetNameOnButton(Jackal.currentTarget2, sidekickKillButton, GetString("killButtonText")); + return Jackal.currentTarget2 && CachedPlayer.LocalPlayer.PlayerControl.CanMove; }, () => { sidekickKillButton.Timer = sidekickKillButton.MaxTimer; }, __instance.KillButton.graphic.sprite, @@ -2170,7 +2167,7 @@ public static void createButtonsPostfix(HudManager __instance) writer.Write(0.01f); AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.Mine(id, Miner.miner, buff, 0.01f); + RPCProcedure.Mine(id, buff, 0.01f); }, () => { @@ -3217,7 +3214,7 @@ public static void createButtonsPostfix(HudManager __instance) SendOption.Reliable); writer.Write(playerInfo.PlayerId); AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.amnisiacTakeRole(playerInfo.PlayerId); + Amnisiac.TakeRole(playerInfo.PlayerId, PlayerControl.LocalPlayer.PlayerId); break; } } @@ -3225,7 +3222,7 @@ public static void createButtonsPostfix(HudManager __instance) }, () => { - return Amnisiac.amnisiac != null && Amnisiac.amnisiac == CachedPlayer.LocalPlayer.PlayerControl && + return Amnisiac.player != null && Amnisiac.player.Any(x => x == CachedPlayer.LocalPlayer.PlayerControl) && !CachedPlayer.LocalPlayer.Data.IsDead; }, () => @@ -3241,6 +3238,65 @@ public static void createButtonsPostfix(HudManager __instance) buttonText: GetString("RememberText") ); + specterRememberButton = new CustomButton( + () => { }, + () => + { + return Specter.player != null && Specter.player == CachedPlayer.LocalPlayer.PlayerControl && + CachedPlayer.LocalPlayer.Data.IsDead & Specter.remember; + }, + () => + { + var array = Physics2D.OverlapCircleAll(CachedPlayer.LocalPlayer.PlayerControl.GetTruePosition(), + CachedPlayer.LocalPlayer.PlayerControl.MaxReportDistance * 0.36f, + Constants.PlayersOnlyMask).Where(collider => collider.tag == "DeadBody") + .Select(collider => collider.GetComponent()) + .Where(deadBody => deadBody != null); + + return array.Any(db => db.ParentId != CachedPlayer.LocalId) && PlayerControl.LocalPlayer.CanMove; + }, + () => + { + Specter.remember = true; + specterRememberButton.Timer = 10f; + }, + Amnisiac.buttonSprite, + ButtonPositions.upperRowCenter, + __instance, + abilityInput.keyCode, + true, + 2f, + () => + { + foreach (var collider2D in Physics2D.OverlapCircleAll(CachedPlayer.LocalPlayer.PlayerControl.GetTruePosition(), + CachedPlayer.LocalPlayer.PlayerControl.MaxReportDistance, Constants.PlayersOnlyMask)) + if (collider2D.tag == "DeadBody") + { + var component = collider2D.GetComponent(); + if (component && !component.Reported) + { + var truePosition = CachedPlayer.LocalPlayer.PlayerControl.GetTruePosition(); + var truePosition2 = component.TruePosition; + if (Vector2.Distance(truePosition2, truePosition) <= + CachedPlayer.LocalPlayer.PlayerControl.MaxReportDistance && + CachedPlayer.LocalPlayer.PlayerControl.CanMove && + !PhysicsHelpers.AnythingBetween(truePosition, truePosition2, + Constants.ShipAndObjectsMask, false)) + { + var playerInfo = GameData.Instance.GetPlayerById(component.ParentId); + PlayerControl.LocalPlayer.transform.position = component.transform.position; + var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl, CustomRPC.SpecterTakeRole); + writer.Write(playerInfo.PlayerId); + writer.EndRPC(); + Specter.TakeRole(playerInfo.PlayerId); + break; + } + } + } + }, + buttonText: GetString("SpecterButton") + ); + // Medium button mediumButton = new CustomButton( () => @@ -3901,12 +3957,11 @@ public static void createButtonsPostfix(HudManager __instance) if (Terrorist.selfExplosion) { var loacl = Terrorist.terrorist.PlayerId; - var writer1 = AmongUsClient.Instance.StartRpcImmediately(Terrorist.terrorist.NetId, - (byte)CustomRPC.UncheckedMurderPlayer, SendOption.Reliable); + var writer1 = StartRPC(Terrorist.terrorist, CustomRPC.UncheckedMurderPlayer); writer1.Write(loacl); writer1.Write(loacl); writer1.Write(byte.MaxValue); - AmongUsClient.Instance.FinishRpcImmediately(writer1); + writer1.EndRPC(); RPCProcedure.uncheckedMurderPlayer(loacl, loacl, byte.MaxValue); } @@ -4006,13 +4061,18 @@ public static void createButtonsPostfix(HudManager __instance) Thief.thief.clearAllTasks(); } + if (result is MurderAttemptResult.ReverseKill or MurderAttemptResult.BodyGuardKill) + { + checkMurderAttemptAndKill(CachedPlayer.LocalPlayer.PlayerControl, target); + } + // Steal role if survived. if (!Thief.thief.Data.IsDead && result == MurderAttemptResult.PerformKill) { var writer = StartRPC(CachedPlayer.LocalPlayer.PlayerControl.NetId, CustomRPC.ThiefStealsRole); writer.Write(target.PlayerId); writer.EndRPC(); - RPCProcedure.thiefStealsRole(target.PlayerId); + Thief.StealsRole(target.PlayerId); } // Kill the victim (after becoming their role - so that no win is triggered for other teams) diff --git a/TheOtherRoles/Buttons/CustomButton.cs b/TheOtherRoles/Buttons/CustomButton.cs index 32840d8f..156b0079 100644 --- a/TheOtherRoles/Buttons/CustomButton.cs +++ b/TheOtherRoles/Buttons/CustomButton.cs @@ -148,11 +148,10 @@ public static void ResetAllCooldowns(float Time = -1) Error($"NullReferenceException from MeetingEndedUpdate().HasButton(), if theres only one warning its fine\n{e}", "CustomButton"); } } + var time = Time == -1 ? ModOption.KillCooddown : Time; + CachedPlayer.LocalPlayer.PlayerControl.killTimer = time; } - /// - /// 重置玩家击杀按钮cd - /// public static void resetKillButton(PlayerControl p, float time = -1) { if (p.IsDead()) return; diff --git a/TheOtherRoles/CustomCosmetics/CosmeticsManager.cs b/TheOtherRoles/CustomCosmetics/CosmeticsManager.cs index 41249424..38acffee 100644 --- a/TheOtherRoles/CustomCosmetics/CosmeticsManager.cs +++ b/TheOtherRoles/CustomCosmetics/CosmeticsManager.cs @@ -25,7 +25,7 @@ public class CosmeticsManager : ManagerBase hasCosmetics = CustomCosmeticsFlags.Hat }; - internal static string RepositoryUrl => "https://raw.githubusercontent.com/TheOtherRolesAU/TheOtherHats/master".GithubUrl(); + internal static string RepositoryUrl => "https://hats.mxyx.club/master"; public static void Load() { diff --git a/TheOtherRoles/Helper/Helpers.cs b/TheOtherRoles/Helper/Helpers.cs index 5551aacd..b5e8a1d3 100644 --- a/TheOtherRoles/Helper/Helpers.cs +++ b/TheOtherRoles/Helper/Helpers.cs @@ -67,7 +67,6 @@ public enum LogLevel public static class Helpers { public static bool zoomOutStatus; - public static bool InGame => AmongUsClient.Instance != null && AmongUsClient.Instance.GameState == InnerNetClient.GameStates.Started; public static bool IsCountDown => GameStartManager.InstanceExists && GameStartManager.Instance.startState == GameStartManager.StartingStates.Countdown; public static bool InMeeting => InGame && MeetingHud.Instance; @@ -90,24 +89,25 @@ public static class Helpers /// public static bool hasFakeTasks(this PlayerControl player) { + if (player == Specter.player) return false; return player == Werewolf.werewolf || player == Doomsayer.doomsayer || player == Juggernaut.juggernaut || player == Jester.jester || player == Arsonist.arsonist || - player == Jackal.jackal || - player == Sidekick.sidekick || - player == Pavlovsdogs.pavlovsowner || + player == Witness.player || player == PartTimer.partTimer || player == Akujo.akujo || player == Swooper.swooper || player == Lawyer.lawyer || player == Executioner.executioner || player == Vulture.vulture || - Pursuer.pursuer.Contains(player) || - Survivor.survivor.Contains(player) || - Pavlovsdogs.pavlovsdogs.Contains(player) || - Jackal.formerJackals.Contains(player); + player == Jackal.sidekick || + player == Pavlovsdogs.pavlovsowner || + Jackal.jackal.Any(x => x == player) || + Pursuer.pursuer.Any(x => x == player) || + Survivor.survivor.Any(x => x == player) || + Pavlovsdogs.pavlovsdogs.Any(x => x == player); } /// @@ -136,10 +136,10 @@ public static bool killingCrewAlive() public static bool hasImpVision(GameData.PlayerInfo player) { return player.Role.IsImpostor - || (((Jackal.jackal != null && Jackal.jackal.PlayerId == player.PlayerId) || Jackal.formerJackals.Any(x => x.PlayerId == player.PlayerId)) && Jackal.hasImpostorVision) - || (Sidekick.sidekick != null && Sidekick.sidekick.PlayerId == player.PlayerId && Sidekick.hasImpostorVision) + || (Jackal.jackal.Any(p => p.PlayerId == player.PlayerId) && Jackal.hasImpostorVision) + || (Jackal.sidekick != null && Jackal.sidekick.PlayerId == player.PlayerId && Jackal.hasImpostorVision) || (Pavlovsdogs.pavlovsowner != null && Pavlovsdogs.pavlovsowner.PlayerId == player.PlayerId && Pavlovsdogs.hasImpostorVision) - || (Pavlovsdogs.pavlovsdogs != null && Pavlovsdogs.pavlovsdogs.Any(p => p.PlayerId == player.PlayerId) && Pavlovsdogs.hasImpostorVision) + || (Pavlovsdogs.pavlovsdogs.Any(p => p.PlayerId == player.PlayerId) && Pavlovsdogs.hasImpostorVision) || (Spy.spy != null && Spy.spy.PlayerId == player.PlayerId && Spy.hasImpostorVision) || (Juggernaut.juggernaut != null && Juggernaut.juggernaut.PlayerId == player.PlayerId && Juggernaut.hasImpostorVision) || (Jester.jester != null && Jester.jester.PlayerId == player.PlayerId && Jester.hasImpostorVision) @@ -174,11 +174,11 @@ public static bool roleCanUseVents(this PlayerControl player) { roleCouldUse = true; } - else if (Jackal.canUseVents && Jackal.jackal != null && Jackal.jackal == player) + else if (Jackal.canUseVents && Jackal.jackal != null && Jackal.jackal.Any(x => x == player)) { roleCouldUse = true; } - else if (Sidekick.canUseVents && Sidekick.sidekick != null && Sidekick.sidekick == player) + else if (Jackal.canUseVents && Jackal.sidekick != null && Jackal.sidekick == player) { roleCouldUse = true; } @@ -253,7 +253,7 @@ public static bool checkAndDoVetKill(PlayerControl target) public static bool isNeutral(PlayerControl player) { var roleInfo = RoleInfo.getRoleInfoForPlayer(player, false).FirstOrDefault(); - return roleInfo != null && roleInfo.roleTeam == RoleType.Neutral; + return roleInfo != null && roleInfo.roleType == RoleType.Neutral; } public static bool isKillerNeutral(PlayerControl player) @@ -263,17 +263,17 @@ public static bool isKillerNeutral(PlayerControl player) player == Werewolf.werewolf || player == Swooper.swooper || player == Arsonist.arsonist || - player == Jackal.jackal || - player == Sidekick.sidekick || + player == Jackal.sidekick || player == Pavlovsdogs.pavlovsowner || + Jackal.jackal.Contains(player) || Pavlovsdogs.pavlovsdogs.Contains(player)); } public static bool isEvilNeutral(PlayerControl player) { return isNeutral(player) && - player != Amnisiac.amnisiac && player != PartTimer.partTimer && + !Amnisiac.player.Contains(player) && !Pursuer.pursuer.Contains(player) && !Survivor.survivor.Contains(player); } @@ -345,7 +345,7 @@ public static bool roleCanSabotage(this PlayerControl player) { var roleCouldUse = false; if (ModOption.disableSabotage) return false; - if (Jackal.canSabotage && (player == Jackal.jackal || player == Sidekick.sidekick || Jackal.formerJackals.Contains(player)) && !ModOption.disableSabotage) + if (Jackal.canSabotage && (Jackal.jackal.Contains(player) || player == Jackal.sidekick) && !ModOption.disableSabotage) roleCouldUse = true; if (Pavlovsdogs.canSabotage && (player == Pavlovsdogs.pavlovsowner || Pavlovsdogs.pavlovsdogs.Any(p => p == player)) && !ModOption.disableSabotage) roleCouldUse = true; @@ -475,7 +475,7 @@ public static void showTargetNameOnButton(PlayerControl target, CustomButton but // set to morphed player else if (Morphling.morphling != null && Morphling.morphTarget != null && target == Morphling.morphling && Morphling.morphTimer > 0) text = Morphling.morphTarget.Data.PlayerName; else if (target == Swooper.swooper && Swooper.isInvisable) text = defaultText; - else if (target == Jackal.jackal && Jackal.isInvisable) text = defaultText; + else if (Jackal.jackal.Any(p => p == target) && Jackal.isInvisable) text = defaultText; //else if (target == PhantomRole.phantomRole) text = defaultText; else if (target == null) text = defaultText; // Set text to defaultText if no target else text = target.Data.PlayerName; // Set text to playername @@ -630,7 +630,7 @@ public static List allRoleInfos() var allRoleInfo = new List(); foreach (var role in RoleInfo.allRoleInfos) { - if (role.roleTeam == RoleType.Modifier) continue; + if (role.roleType is RoleType.Modifier or RoleType.GhostRole or RoleType.Special) continue; allRoleInfo.Add(role); } return allRoleInfo; @@ -639,7 +639,7 @@ public static List allRoleInfos() public static List onlineRoleInfos() { var role = new List(); - role.AddRange(CachedPlayer.AllPlayers.Select(n => RoleInfo.getRoleInfoForPlayer(n, false)).SelectMany(n => n)); + role.AddRange(CachedPlayer.AllPlayers.Select(n => RoleInfo.getRoleInfoForPlayer(n, false, false)).SelectMany(x => x)); return role; } @@ -651,12 +651,15 @@ public static PlayerControl playerById(byte id) return null; } - public static Dictionary allPlayersById() + public static bool isSabotageActive() { - var res = new Dictionary(); - foreach (PlayerControl player in CachedPlayer.AllPlayers) - res.Add(player.PlayerId, player); - return res; + foreach (var task in CachedPlayer.LocalPlayer.PlayerControl.myTasks.GetFastEnumerator()) + if (task.TaskType == TaskTypes.FixLights || task.TaskType == TaskTypes.RestoreOxy || + task.TaskType == TaskTypes.ResetReactor || task.TaskType == TaskTypes.ResetSeismic || + task.TaskType == TaskTypes.FixComms || task.TaskType == TaskTypes.StopCharles + || (SubmergedCompatibility.IsSubmerged && task.TaskType == SubmergedCompatibility.RetrieveOxygenMask)) + return true; + return false; } public static void handleVampireBiteOnBodyReport() @@ -755,7 +758,7 @@ public static Color getTeamColor(RoleType team) RoleType.Impostor => Palette.ImpostorRed, RoleType.Neutral => Color.gray, RoleType.Modifier => Color.yellow, - RoleType.Special => Palette.Purple, + RoleType.GhostRole => new Color32(159, 127, 209, byte.MaxValue), _ => Palette.White }; } @@ -939,7 +942,7 @@ public static bool hidePlayerName(PlayerControl source, PlayerControl target) return true; // No names are visible if (SurveillanceMinigamePatch.nightVisionIsActive) return true; if (Ninja.isInvisble && Ninja.ninja == target) return true; - if (Jackal.isInvisable && Jackal.jackal == target) return true; + if (Jackal.isInvisable && Jackal.jackal.Any(p => p == target)) return true; if (Swooper.isInvisable && Swooper.swooper == target) return true; if (ModOption.hideOutOfSightNametags && InGame && source.IsAlive() && !isFungle && PhysicsHelpers.AnythingBetween(localPlayer.GetTruePosition(), target.GetTruePosition(), Constants.ShadowMask, false)) @@ -948,15 +951,13 @@ public static bool hidePlayerName(PlayerControl source, PlayerControl target) if (!ModOption.hidePlayerNames) return false; // All names are visible if (source == null || target == null) return true; if (source == target) return false; // Player sees his own name - if (source.Data.Role.IsImpostor && (target.Data.Role.IsImpostor || target == Spy.spy || - (target == Sidekick.sidekick && Sidekick.wasTeamRed) || - (target == Jackal.jackal && Jackal.wasTeamRed))) + if (source.Data.Role.IsImpostor && (target.Data.Role.IsImpostor || target == Spy.spy)) return false; // Members of team Impostors see the names of Impostors/Spies if ((source == Lovers.lover1 || source == Lovers.lover2) && (target == Lovers.lover1 || target == Lovers.lover2)) return false; // Members of team Lovers see the names of each other - if ((source == Jackal.jackal || source == Sidekick.sidekick) - && (target == Jackal.jackal || target == Sidekick.sidekick)) + if ((Jackal.jackal.Any(p => p == source) || source == Jackal.sidekick) + && (Jackal.jackal.Any(p => p == target) || target == Jackal.sidekick)) return false; // Members of team Jackal see the names of each other if ((source == Pavlovsdogs.pavlovsowner || Pavlovsdogs.pavlovsdogs.Any(x => x == target)) && (target == Pavlovsdogs.pavlovsowner || Pavlovsdogs.pavlovsdogs.Any(x => x == target))) @@ -1289,7 +1290,7 @@ public static MurderAttemptResult checkMurderAttemptAndKill(PlayerControl killer CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.MimicMimicRole, SendOption.Reliable); writerMimic.Write(target.PlayerId); AmongUsClient.Instance.FinishRpcImmediately(writerMimic); - RPCProcedure.mimicMimicRole(target.PlayerId); + Mimic.MimicRole(target.PlayerId); } MurderPlayer(killer, target, showAnimation); diff --git a/TheOtherRoles/Modules/ChatCommands.cs b/TheOtherRoles/Modules/ChatCommands.cs index 086b4ea7..e8992f39 100644 --- a/TheOtherRoles/Modules/ChatCommands.cs +++ b/TheOtherRoles/Modules/ChatCommands.cs @@ -259,9 +259,7 @@ public static void Postfix(ChatBubble __instance, [HarmonyArgument(0)] string pl .FirstOrDefault(x => x.Data != null && x.Data.PlayerName.Equals(playerName, StringComparison.Ordinal)); if (CachedPlayer.LocalPlayer != null && CachedPlayer.LocalPlayer.Data.Role.IsImpostor && __instance != null - && ((Spy.spy != null && sourcePlayer.PlayerId == Spy.spy.PlayerId) - || (Sidekick.sidekick != null && Sidekick.wasTeamRed && sourcePlayer.PlayerId == Sidekick.sidekick.PlayerId) - || (Jackal.jackal != null && Jackal.wasTeamRed && sourcePlayer.PlayerId == Jackal.jackal.PlayerId))) + && (Spy.spy != null && sourcePlayer.PlayerId == Spy.spy.PlayerId)) { __instance.NameText.color = Palette.ImpostorRed; } diff --git a/TheOtherRoles/Options/CustomOptionHolder.cs b/TheOtherRoles/Options/CustomOptionHolder.cs index 48c0c583..2260fdab 100644 --- a/TheOtherRoles/Options/CustomOptionHolder.cs +++ b/TheOtherRoles/Options/CustomOptionHolder.cs @@ -1,3 +1,5 @@ +using TheOtherRoles.Roles.Ghost; +using TheOtherRoles.Roles.Neutral; using UnityEngine; using static TheOtherRoles.Options.CustomOption; using Types = TheOtherRoles.Options.CustomOption.CustomOptionType; @@ -124,7 +126,6 @@ public class CustomOptionHolder public static CustomOption jackalCanUseVents; public static CustomOption jackalCanUseSabo; public static CustomOption jackalCanCreateSidekick; - public static CustomOption jackalCanImpostorFindSidekick; public static CustomOption sidekickPromotesToJackal; public static CustomOption sidekickCanKill; public static CustomOption sidekickCanUseVents; @@ -228,6 +229,9 @@ public class CustomOptionHolder public static CustomOption ghostEngineerSpawnRate; + public static CustomOption specterSpawnRate; + public static CustomOption specterResetRole; + public static CustomOption sheriffSpawnRate; public static CustomOption sheriffMisfireKills; public static CustomOption sheriffCooldown; @@ -383,6 +387,8 @@ public class CustomOptionHolder public static CustomOption executionerPromotesToLawyer; public static CustomOption executionerOnTargetDead; + public static CustomOption witnessSpawnRate; + public static CustomOption balancerSpawnRate; public static CustomOption balancerCount; public static CustomOption balancerVoteTime; @@ -900,6 +906,9 @@ public static void Load() //-------------------------- Neutral Options 20000-29999 -------------------------- // + specterSpawnRate = Create(50020, Types.Neutral, cs(Specter.color, "SpecterOptions"), rates, null, true); + specterResetRole = Create(50021, Types.Neutral, "amnisiacResetRole", true, specterSpawnRate); + survivorSpawnRate = Create(20280, Types.Neutral, cs(Survivor.color, "Survivor"), rates, null, true); survivorVestEnable = Create(20281, Types.Neutral, "survivorVestEnable", true, survivorSpawnRate); survivorVestNumber = Create(20282, Types.Neutral, "survivorVestNumber", 5f, 1f, 20f, 1f, survivorVestEnable); @@ -930,7 +939,6 @@ public static void Load() jackalCanCreateSidekick = Create(20135, Types.Neutral, cs(Jackal.color, "jackalCanCreateSidekick"), false, jackalSpawnRate); jackalCreateSidekickCooldown = Create(20136, Types.Neutral, "jackalCreateSidekickCooldown", 25f, 10f, 60f, 2.5f, jackalCanCreateSidekick); jackalkillFakeImpostor = Create(20145, Types.Neutral, cs(Palette.ImpostorRed, "jackalkillFakeImpostor"), false, jackalCanCreateSidekick); - jackalCanImpostorFindSidekick = Create(20137, Types.Neutral, cs(Palette.ImpostorRed, "jackalCanImpostorFindSidekick"), true, jackalCanCreateSidekick); sidekickCanKill = Create(20138, Types.Neutral, "sidekickCanKill", true, jackalCanCreateSidekick); sidekickCanUseVents = Create(20139, Types.Neutral, "sidekickCanUseVents", true, jackalCanCreateSidekick); sidekickPromotesToJackal = Create(20140, Types.Neutral, "sidekickPromotesToJackal", false, jackalCanCreateSidekick); @@ -1001,6 +1009,8 @@ public static void Load() partTimerDeathTurn = Create(20292, Types.Neutral, "partTimerDeathTurn", 2, 1, 6, 1, partTimerSpawnRate); partTimerKnowsRole = Create(20293, Types.Neutral, "partTimerIsCheckTargetRole", true, partTimerSpawnRate); + witnessSpawnRate = Create(20301, Types.Neutral, cs(Witness.color, "Witness"), rates, null, true); + doomsayerSpawnRate = Create(20221, Types.Neutral, cs(Doomsayer.color, "Doomsayer"), rates, null, true); doomsayerCooldown = Create(20222, Types.Neutral, "doomsayerCooldown", 20f, 2.5f, 60f, 2.5f, doomsayerSpawnRate); doomsayerHasMultipleShotsPerMeeting = Create(20223, Types.Neutral, "doomsayerHasMultipleShotsPerMeeting", true, doomsayerSpawnRate); @@ -1029,7 +1039,7 @@ public static void Load() //-------------------------- Crewmate Options 30000-39999 -------------------------- // - ghostEngineerSpawnRate = Create(50010, Types.Crewmate, cs(GhostEngineer.color, "GhostEngineer"), rates, null, true); + ghostEngineerSpawnRate = Create(50010, Types.Crewmate, cs(GhostEngineer.color, "GhostEngineerOptions"), rates, null, true); guesserSpawnRate = Create(30100, Types.Crewmate, cs(Vigilante.color, "Vigilante"), rates, null, true); guesserNumberOfShots = Create(30101, Types.Crewmate, "guesserNumberOfShots", 3f, 1f, 15f, 1f, guesserSpawnRate); diff --git a/TheOtherRoles/Options/CustomOptions.cs b/TheOtherRoles/Options/CustomOptions.cs index 5c7817f1..e0ed2da9 100644 --- a/TheOtherRoles/Options/CustomOptions.cs +++ b/TheOtherRoles/Options/CustomOptions.cs @@ -366,7 +366,7 @@ public static void Postfix(GameOptionsMenu __instance) copyButton.transform.localPosition += Vector3.down * 0.8f; var copyButtonPassive = copyButton.GetComponent(); var copyButtonRenderer = copyButton.GetComponent(); - copyButtonRenderer.sprite = UnityHelper.loadSpriteFromResources("TheOtherRoles.Resources.CopyButton.png", 175f); + copyButtonRenderer.sprite = new ResourceSprite("TheOtherRoles.Resources.CopyButton.png", 175f); copyButtonPassive.OnClick.RemoveAllListeners(); copyButtonPassive.OnClick = new Button.ButtonClickedEvent(); copyButtonPassive.OnClick.AddListener((Action)(() => @@ -1151,9 +1151,9 @@ private static string buildOptionsOfType(CustomOptionType type, bool headerOnly) if (option.id == 30170) //Deputy sb.AppendLine($"- {cs(Deputy.color, "Deputy".Translate())}: {option.getString()}"); else if (option.id == 20142) - sb.AppendLine($"- {cs(Sidekick.color, "jackalSwoopChance".Translate())}: {option.getString()}"); + sb.AppendLine($"- {cs(Jackal.color, "jackalSwoopChance".Translate())}: {option.getString()}"); else if (option.id == 20135) //Sidekick - sb.AppendLine($"- {cs(Sidekick.color, "Sidekick".Translate())}: {option.getString()}"); + sb.AppendLine($"- {cs(Jackal.color, "Sidekick".Translate())}: {option.getString()}"); } if (headerOnly) return sb.ToString(); diff --git a/TheOtherRoles/Patches/EndGamePatch.cs b/TheOtherRoles/Patches/EndGamePatch.cs index 14c55f2b..33e5837e 100644 --- a/TheOtherRoles/Patches/EndGamePatch.cs +++ b/TheOtherRoles/Patches/EndGamePatch.cs @@ -104,8 +104,6 @@ public static void Prefix(AmongUsClient __instance, [HarmonyArgument(0)] ref End public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)] ref EndGameResult endGameResult) { - Message("娓告垙缁撴潫"); - AdditionalTempData.clear(); List killRole = [ @@ -148,15 +146,15 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)] ref En notWinners.AddRange(new[] { Jester.jester, - Sidekick.sidekick, - Amnisiac.amnisiac, - Jackal.jackal, + Jackal.sidekick, Arsonist.arsonist, Swooper.swooper, Vulture.vulture, Werewolf.werewolf, Lawyer.lawyer, Executioner.executioner, + Witness.player, + Specter.player, Thief.thief, Juggernaut.juggernaut, Doomsayer.doomsayer, @@ -165,8 +163,9 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)] ref En Pavlovsdogs.pavlovsowner, }.Where(p => p != null)); + notWinners.AddRange(Amnisiac.player.Where(p => p != null)); notWinners.AddRange(Pavlovsdogs.pavlovsdogs.Where(p => p != null)); - notWinners.AddRange(Jackal.formerJackals.Where(p => p != null)); + notWinners.AddRange(Jackal.jackal.Where(p => p != null)); notWinners.AddRange(Pursuer.pursuer.Where(p => p != null)); notWinners.AddRange(Survivor.survivor.Where(p => p != null)); if (Akujo.honmeiCannotFollowWin && Akujo.honmei != null) notWinners.Add(Akujo.honmei); @@ -190,7 +189,7 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)] ref En var loversWin = Lovers.existingAndAlive() && (gameOverReason == (GameOverReason)CustomGameOverReason.LoversWin || (GameManager.Instance.DidHumansWin(gameOverReason) && !Lovers.existingWithKiller())); var teamJackalWin = gameOverReason == (GameOverReason)CustomGameOverReason.TeamJackalWin && - (Jackal.jackal.IsAlive() || Sidekick.sidekick.IsAlive()); + (Jackal.jackal.Any(x => x.IsAlive()) || Jackal.sidekick.IsAlive()); var teamPavlovsWin = gameOverReason == (GameOverReason)CustomGameOverReason.TeamPavlovsWin && (Pavlovsdogs.pavlovsowner.IsAlive() || Pavlovsdogs.pavlovsdogs.Any(p => p.IsAlive())); var vultureWin = Vulture.vulture != null && gameOverReason == (GameOverReason)CustomGameOverReason.VultureWin; @@ -307,23 +306,19 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)] ref En // Jackal wins if nobody except jackal is alive AdditionalTempData.winCondition = WinCondition.JackalWin; TempData.winners = new Il2CppSystem.Collections.Generic.List(); - var wpd = new WinningPlayerData(Jackal.jackal.Data); - wpd.IsImpostor = false; - TempData.winners.Add(wpd); - // If there is a sidekick. The sidekick also wins - if (Sidekick.sidekick != null) - { - var wpdSidekick = new WinningPlayerData(Sidekick.sidekick.Data); - wpdSidekick.IsImpostor = false; - TempData.winners.Add(wpdSidekick); - } - - foreach (var player in Jackal.formerJackals) + foreach (var player in Jackal.jackal) { var wpdFormerJackal = new WinningPlayerData(player.Data); wpdFormerJackal.IsImpostor = false; TempData.winners.Add(wpdFormerJackal); } + // If there is a sidekick. The sidekick also wins + if (Jackal.sidekick != null) + { + var wpdSidekick = new WinningPlayerData(Jackal.sidekick.Data); + wpdSidekick.IsImpostor = false; + TempData.winners.Add(wpdSidekick); + } } else if (teamPavlovsWin) { @@ -498,6 +493,7 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)] ref En TempData.winners.Add(new WinningPlayerData(PartTimer.partTimer.Data)); AdditionalTempData.additionalWinConditions.Add(WinCondition.AdditionalPartTimerWin); } + Message($"娓告垙缁撴潫{AdditionalTempData.winCondition}"); // Reset Settings RPCProcedure.resetVariables(); } @@ -1091,6 +1087,15 @@ private static void EndGameForSabotage(ShipStatus __instance) } } +[HarmonyPatch(typeof(GameManager), nameof(GameManager.RpcEndGame))] +internal class RPCEndGamePatch +{ + public static void Postfix(ref GameOverReason endReason) + { + Message($"娓告垙缁撴潫{(CustomGameOverReason)endReason}"); + } +} + internal class PlayerStatistics { public PlayerStatistics(ShipStatus __instance) @@ -1157,13 +1162,13 @@ private void GetPlayerCounts() if (lover) impLover = true; } - if (Jackal.jackal != null && Jackal.jackal.PlayerId == playerInfo.PlayerId) + if (Jackal.jackal != null && Jackal.jackal.Any(x => x.PlayerId == playerInfo.PlayerId)) { numJackalAlive++; if (lover) jackalLover = true; } - if (Sidekick.sidekick != null && Sidekick.sidekick.PlayerId == playerInfo.PlayerId) + if (Jackal.sidekick != null && Jackal.sidekick.PlayerId == playerInfo.PlayerId) { numJackalAlive++; if (lover) jackalLover = true; diff --git a/TheOtherRoles/Patches/ExileControllerPatch.cs b/TheOtherRoles/Patches/ExileControllerPatch.cs index 2904433e..1cf85289 100644 --- a/TheOtherRoles/Patches/ExileControllerPatch.cs +++ b/TheOtherRoles/Patches/ExileControllerPatch.cs @@ -282,6 +282,7 @@ public static void Prefix(GameObject obj) private static void WrapUpPostfix(GameData.PlayerInfo exiled) { + if (CachedPlayer.LocalPlayer.IsDead && Specter.player != PlayerControl.LocalPlayer) CanSeeRoleInfo = true; // Prosecutor win condition if (exiled != null && Executioner.executioner != null && Executioner.target != null && Executioner.target.PlayerId == exiled.PlayerId && !Executioner.executioner.Data.IsDead) @@ -445,6 +446,11 @@ private static void WrapUpPostfix(GameData.PlayerInfo exiled) if (!Yoyo.markStaysOverMeeting) Silhouette.clearSilhouettes(); + + if (AmongUsClient.Instance.AmHost) + { + LastImpostor.promoteToLastImpostor(); + } } [HarmonyPatch(typeof(ExileController), nameof(ExileController.WrapUp))] diff --git a/TheOtherRoles/Patches/HauntMenuMinigamePatch.cs b/TheOtherRoles/Patches/HauntMenuMinigamePatch.cs index d8694ddd..508c238c 100644 --- a/TheOtherRoles/Patches/HauntMenuMinigamePatch.cs +++ b/TheOtherRoles/Patches/HauntMenuMinigamePatch.cs @@ -44,7 +44,7 @@ public static void MatchesFilterPostfix(HauntMenuMinigame __instance, PlayerCont if (__instance.filterMode == HauntMenuMinigame.HauntFilters.Impostor) { var info = RoleInfo.getRoleInfoForPlayer(pc, false); - __result = (pc.Data.Role.IsImpostor || info.Any(x => x.roleTeam == RoleType.Neutral)) && !pc.Data.IsDead; + __result = (pc.Data.Role.IsImpostor || info.Any(x => x.roleType == RoleType.Neutral)) && !pc.Data.IsDead; } } @@ -87,19 +87,26 @@ public static void UpdatePostfix(HauntMenuMinigame __instance) [HarmonyPatch(typeof(AbilityButton), nameof(AbilityButton.Update))] public static void showOrHideAbilityButtonPostfix(AbilityButton __instance) { - var isGameMode = GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek; - if (CachedPlayer.LocalPlayer.Data.IsDead && CanSeeRoleInfo && (CustomOptionHolder.finishTasksBeforeHauntingOrZoomingOut.GetBool() || isGameMode)) + var isHideNSeek = GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek; + + // player has haunt button. + var (playerCompleted, playerTotal) = TasksHandler.taskInfo(CachedPlayer.LocalPlayer.Data); + var numberOfLeftTasks = playerTotal - playerCompleted; + + if (!InGame || InMeeting || !CanSeeRoleInfo) { - // player has haunt button. - var (playerCompleted, playerTotal) = TasksHandler.taskInfo(CachedPlayer.LocalPlayer.Data); - var numberOfLeftTasks = playerTotal - playerCompleted; - if (numberOfLeftTasks <= 0 || isGameMode) - __instance.Show(); - else - __instance.Hide(); + HudManager.Instance.AbilityButton?.gameObject.SetActive(false); + return; } + else if (CustomOptionHolder.finishTasksBeforeHauntingOrZoomingOut.GetBool() && PlayerControl.LocalPlayer.isCrew() && numberOfLeftTasks > 0) + { + HudManager.Instance.AbilityButton.gameObject.SetActive(false); + return; + } + HudManager.Instance.AbilityButton.gameObject.SetActive(PlayerControl.LocalPlayer.IsDead() || isHideNSeek); } } + [HarmonyPatch(typeof(HauntMenuMinigame), nameof(HauntMenuMinigame.Start))] public static class AddNeutralHauntPatch { diff --git a/TheOtherRoles/Patches/IntroPatch.cs b/TheOtherRoles/Patches/IntroPatch.cs index 2808618d..1972a816 100644 --- a/TheOtherRoles/Patches/IntroPatch.cs +++ b/TheOtherRoles/Patches/IntroPatch.cs @@ -45,7 +45,7 @@ public static void Prefix(IntroCutscene __instance) player.gameObject.SetActive(false); //娓告垙寮濮嬫椂閲嶇疆cd - CachedPlayer.LocalPlayer.PlayerControl.SetKillTimer(ModOption.ButtonCooldown); + CustomButton.ResetAllCooldowns(ModOption.ButtonCooldown); if (CachedPlayer.LocalPlayer.PlayerControl == Arsonist.arsonist && p != Arsonist.arsonist) { @@ -208,9 +208,9 @@ public static void setupIntroTeamIcons(IntroCutscene __instance, ref List yourTeam) { var infos = RoleInfo.getRoleInfoForPlayer(CachedPlayer.LocalPlayer.PlayerControl); - var roleInfo = infos.FirstOrDefault(info => info.roleTeam != RoleType.Modifier); + var roleInfo = infos.FirstOrDefault(info => info.roleType != RoleType.Modifier); if (roleInfo == null) return; - if (roleInfo.roleTeam == RoleType.Neutral) + if (roleInfo.roleType == RoleType.Neutral) { var neutralColor = new Color32(76, 84, 78, 255); __instance.BackgroundBar.material.color = roleInfo.color; @@ -264,8 +264,8 @@ public static void SetRoleTexts(IntroCutscene __instance) { // Don't override the intro of the vanilla roles var infos = RoleInfo.getRoleInfoForPlayer(CachedPlayer.LocalPlayer.PlayerControl); - var roleInfo = infos.FirstOrDefault(info => info.roleTeam != RoleType.Modifier); - var modifierInfo = infos.FirstOrDefault(info => info.roleTeam == RoleType.Modifier); + var roleInfo = infos.FirstOrDefault(info => info.roleType != RoleType.Modifier); + var modifierInfo = infos.FirstOrDefault(info => info.roleType == RoleType.Modifier); __instance.RoleBlurbText.text = ""; if (roleInfo != null) diff --git a/TheOtherRoles/Patches/LobbyRoleList.cs b/TheOtherRoles/Patches/LobbyRoleList.cs index e566eac4..697ff4df 100644 --- a/TheOtherRoles/Patches/LobbyRoleList.cs +++ b/TheOtherRoles/Patches/LobbyRoleList.cs @@ -55,6 +55,21 @@ public static void RoleSummaryOnClick() newtitle.transform.localPosition = new Vector3(1f, 0.17f, -2f); newtitle.transform.localScale = Vector3.one * 2.5f; + // 娣诲姞閫鍑烘寜閽 + Transform exitButtonTransform = Object.Instantiate(buttonTemplate, container.transform); + exitButtonTransform.name = "RolesSummaryUIExit"; + exitButtonTransform.GetComponent().size = new Vector2(1f, 1f); + exitButtonTransform.GetComponent().sprite = new ResourceSprite("ExitButton.png", 135f); + exitButtonTransform.localPosition = new Vector3(4.4f, 1.3f, -5); // 閫鍑烘寜閽殑浣嶇疆 + exitButtonTransform.localScale = new Vector3(1f, 1.05f, 1f); // 閫鍑烘寜閽殑缂╂斁 + + PassiveButton exitButton = exitButtonTransform.GetComponent(); + Button.ButtonClickedEvent exitOnClick = exitButton.OnClick = new Button.ButtonClickedEvent(); + exitOnClick.AddListener((Action)(() => + { + Object.Destroy(RolesSummaryUI); + })); + List buttons = new(); for (int i = 0; i < Teams.Count; i++) @@ -80,7 +95,7 @@ public static void RoleSummaryOnClick() teamid = RoleType.Modifier; break; case "GhostRole": - team = cs(new Color32(25, 68, 142, byte.MaxValue), GetString("GhostRoleText")); + team = cs(new Color32(159, 127, 209, byte.MaxValue), GetString("GhostRoleText")); teamid = RoleType.GhostRole; break; } @@ -137,20 +152,51 @@ public static void roleInfosOnclick(string team, RoleType teamId) TextMeshPro newtitle = Object.Instantiate(textTemplate, container.transform); newtitle.text = team; - newtitle.outlineWidth = 0.1f; + newtitle.outlineWidth = 0.01f; newtitle.transform.localPosition = new Vector3(0f, 2.8f, -2f); newtitle.transform.localScale = Vector3.one * 2.5f; + // 娣诲姞閫鍑烘寜閽 + Transform exitButtonTransform = Object.Instantiate(buttonTemplate, container.transform); + exitButtonTransform.name = "RoleListExit"; + exitButtonTransform.GetComponent().size = new Vector2(1f, 1f); + exitButtonTransform.GetComponent().sprite = new ResourceSprite("ExitButton.png", 135f); + exitButtonTransform.localPosition = new Vector3(5.8f, 0.5f, -5); + exitButtonTransform.localScale = new Vector3(1f, 1f, 1f); + + PassiveButton exitButton = exitButtonTransform.GetComponent(); + Button.ButtonClickedEvent exitOnClick = exitButton.OnClick = new Button.ButtonClickedEvent(); + exitOnClick.AddListener((Action)(() => + { + Object.Destroy(RolesSummaryUI); + })); + + // 娣诲姞杩斿洖鎸夐挳 + Transform backButtonTransform = Object.Instantiate(buttonTemplate, container.transform); + backButtonTransform.name = "RoleListBack"; + backButtonTransform.GetComponent().size = new Vector2(1f, 1f); + backButtonTransform.GetComponent().sprite = new ResourceSprite("BackButton.png", 135f); + backButtonTransform.localPosition = new Vector3(5.8f, 1.5f, -5); + backButtonTransform.localScale = new Vector3(1f, 1f, 1f); + + PassiveButton backButton = backButtonTransform.GetComponent(); + Button.ButtonClickedEvent backOnClick = backButton.OnClick = new Button.ButtonClickedEvent(); + backOnClick.AddListener((Action)(() => + { + Object.Destroy(container.gameObject); + _ = new LateTask(RoleSummaryOnClick, 0.05f); + })); + List buttons = new(); int count = 0; bool gameStarted = AmongUsClient.Instance.GameState == InnerNet.InnerNetClient.GameStates.Started; foreach (RoleInfo roleInfo in RoleInfo.allRoleInfos) { - if (roleInfo.roleTeam == RoleType.Modifier && teamId != RoleType.Modifier) continue; - else if (roleInfo.roleTeam == RoleType.Neutral && teamId != RoleType.Neutral) continue; - else if (roleInfo.roleTeam == RoleType.Impostor && teamId != RoleType.Impostor) continue; - else if (roleInfo.roleTeam == RoleType.Crewmate && teamId != RoleType.Crewmate) continue; - else if (roleInfo.roleTeam == RoleType.GhostRole && teamId != RoleType.GhostRole) continue; + if (roleInfo.roleType == RoleType.Modifier && teamId != RoleType.Modifier) continue; + else if (roleInfo.roleType == RoleType.Neutral && teamId != RoleType.Neutral) continue; + else if (roleInfo.roleType == RoleType.Impostor && teamId != RoleType.Impostor) continue; + else if (roleInfo.roleType == RoleType.Crewmate && teamId != RoleType.Crewmate) continue; + else if (roleInfo.roleType == RoleType.GhostRole && teamId != RoleType.GhostRole) continue; Transform buttonTransform = Object.Instantiate(buttonTemplate, container.transform); buttonTransform.name = cs(roleInfo.color, roleInfo.Name) + " Button"; @@ -192,6 +238,7 @@ private static void AddInfoCard(RoleInfo roleInfo) string roleSettingDescription = roleInfo.FullDescription != "" ? roleInfo.FullDescription : roleInfo.ShortDescription; string coloredHelp = cs(Color.white, roleSettingDescription); + Transform buttonTemplate = HudManager.Instance.SettingsButton.transform; GameObject roleCard = Object.Instantiate(new GameObject("RoleCard"), HudManager.Instance.transform); SpriteRenderer roleCardRend = roleCard.AddComponent(); roleCard.layer = 5; @@ -199,6 +246,37 @@ private static void AddInfoCard(RoleInfo roleInfo) roleCard.transform.localScale = new Vector3(0.68f, 0.68f, 1f); RolesSummaryUI = roleCard.gameObject; + // 娣诲姞閫鍑烘寜閽 + Transform exitButtonTransform = Object.Instantiate(buttonTemplate, roleCardRend.transform); + exitButtonTransform.name = "RoleCardExit"; + exitButtonTransform.GetComponent().size = new Vector2(1f, 1f); + exitButtonTransform.GetComponent().sprite = new ResourceSprite("ExitButton.png", 135f); + exitButtonTransform.localPosition = new Vector3(5.35f, 0.5f, -5); + exitButtonTransform.localScale = new Vector3(1f, 1f, 1f); + + PassiveButton exitButton = exitButtonTransform.GetComponent(); + Button.ButtonClickedEvent exitOnClick = exitButton.OnClick = new Button.ButtonClickedEvent(); + exitOnClick.AddListener((Action)(() => + { + Object.Destroy(RolesSummaryUI); + })); + + // 娣诲姞杩斿洖鎸夐挳 + Transform backButtonTransform = Object.Instantiate(buttonTemplate, roleCardRend.transform); + backButtonTransform.name = "RoleCardBack"; + backButtonTransform.GetComponent().size = new Vector2(1f, 1f); + backButtonTransform.GetComponent().sprite = new ResourceSprite("BackButton.png", 135f); + backButtonTransform.localPosition = new Vector3(5.35f, 1.5f, -5); + backButtonTransform.localScale = new Vector3(1f, 1f, 1f); + + PassiveButton backButton = backButtonTransform.GetComponent(); + Button.ButtonClickedEvent backOnClick = backButton.OnClick = new Button.ButtonClickedEvent(); + backOnClick.AddListener((Action)(() => + { + Object.Destroy(roleCardRend.gameObject); + _ = new LateTask(RoleSummaryOnClick, 0); + })); + roleCardRend.sprite = UnityHelper.loadSpriteFromResources("TheOtherRoles.Resources.LobbyRoleInfo.SummaryScreen.png", 110f); infoButtonText = Object.Instantiate(HudManager.Instance.TaskPanel.taskText, roleCard.transform); diff --git a/TheOtherRoles/Patches/MeetingHudPatch.cs b/TheOtherRoles/Patches/MeetingHudPatch.cs index 9ad7321e..3c060a43 100644 --- a/TheOtherRoles/Patches/MeetingHudPatch.cs +++ b/TheOtherRoles/Patches/MeetingHudPatch.cs @@ -174,7 +174,8 @@ private static void populateButtonsPostfix(MeetingHud __instance) var checkbox = Object.Instantiate(template, playerVoteArea.transform, true); checkbox.transform.position = template.transform.position; checkbox.transform.localPosition = new Vector3(-0.95f, 0.03f, -1.3f); - if (HandleGuesser.isGuesserGm && HandleGuesser.isGuesser(CachedPlayer.LocalPlayer.PlayerId)) + if ((HandleGuesser.isGuesserGm && HandleGuesser.isGuesser(CachedPlayer.LocalPlayer.PlayerId)) + || (CachedPlayer.LocalPlayer.PlayerId == Mimic.mimic.PlayerId)) checkbox.transform.localPosition = new Vector3(-0.5f, 0.03f, -1.3f); var renderer = checkbox.GetComponent(); renderer.sprite = Swapper.spriteCheck; @@ -466,26 +467,6 @@ private static bool Prefix(MeetingHud __instance) VoterId = playerVoteArea.TargetPlayerId, VotedForId = playerVoteArea.VotedFor }); - /* - if (Tiebreaker.tiebreaker == null || Balancer.currentAbilityUser != null || playerVoteArea.TargetPlayerId != Tiebreaker.tiebreaker.PlayerId) - continue; - - var tiebreakerVote = playerVoteArea.VotedFor; - if (swapped1 != null && swapped2 != null) - { - if (tiebreakerVote == swapped1.TargetPlayerId) tiebreakerVote = swapped2.TargetPlayerId; - else if (tiebreakerVote == swapped2.TargetPlayerId) tiebreakerVote = swapped1.TargetPlayerId; - } - - if (potentialExiled.FindAll(x => x != null && x.PlayerId == tiebreakerVote).Count <= 0 || - (potentialExiled.Count <= 1 && !skipIsTie)) continue; - exiled = potentialExiled.ToArray().FirstOrDefault(v => v.PlayerId == tiebreakerVote); - tie = false; - tiebreakerHandled = true; - var writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, - (byte)CustomRPC.SetTiebreak, SendOption.Reliable); - AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.setTiebreak();*/ } states = statesList.ToArray(); @@ -570,7 +551,8 @@ private static bool Prefix(MeetingHud __instance, Il2CppStructArray Swapper.charges--; } - __instance.TitleText.text = FastDestroyableSingleton.Instance.GetString(StringNames.MeetingVotingResults, new Il2CppReferenceArray(0)); + __instance.TitleText.text = FastDestroyableSingleton.Instance + .GetString(StringNames.MeetingVotingResults, new Il2CppReferenceArray(0)); var allNums = new Dictionary(); __instance.TitleText.text = Object.FindObjectOfType().GetString(StringNames.MeetingVotingResults, []); @@ -593,10 +575,9 @@ private static bool Prefix(MeetingHud __instance, Il2CppStructArray playerVoteArea.ClearForResults(); var num2 = 0; - var mayorVotesDisplayed = 0; for (var j = 0; j < states.Length; j++) { - if (Prosecutor.ProsecuteThisMeeting) continue; + if (Prosecutor.ProsecuteThisMeeting && Prosecutor.prosecutor.IsAlive()) break; var voterState = states[j]; var playerById = GameData.Instance.GetPlayerById(voterState.VoterId); if (playerById == null) @@ -606,57 +587,61 @@ private static bool Prefix(MeetingHud __instance, Il2CppStructArray else if (i == 0 && voterState.SkippedVote && !playerById.IsDead) { __instance.BloopAVoteIcon(playerById, num, __instance.SkippedVoting.transform); + + if (Mayor.mayor != null && voterState.VoterId == Mayor.mayor.PlayerId && Mayor.Revealed) + for (var repeat = 1; repeat < Mayor.Vote; repeat++) + __instance.BloopAVoteIcon(playerById, num, __instance.SkippedVoting.transform); + num++; } else if (voterState.VotedForId == targetPlayerId && !playerById.IsDead) { __instance.BloopAVoteIcon(playerById, num2, playerVoteArea.transform); + + if (Mayor.mayor != null && voterState.VoterId == Mayor.mayor.PlayerId && Mayor.Revealed) + for (var repeat = 1; repeat < Mayor.Vote; repeat++) + __instance.BloopAVoteIcon(playerById, num2, playerVoteArea.transform); + num2++; } - - // Mayor vote, redo this iteration to place a second vote - if (Mayor.mayor == null || voterState.VoterId != (sbyte)Mayor.mayor.PlayerId - || mayorVotesDisplayed >= Mayor.Vote - 1 || !Mayor.Revealed) - { - mayorVotesDisplayed = 0; - continue; - }; - mayorVotesDisplayed++; - j--; } - for (var stateIdx = 0; stateIdx < states.Length; stateIdx++) + for (var j = 0; j < states.Length; j++) { - var voteState = states[stateIdx]; - var playerInfo = GameData.Instance.GetPlayerById(voteState.VoterId); + var voterState = states[j]; + var playerById = GameData.Instance.GetPlayerById(voterState.VoterId); + if (Prosecutor.prosecutor == null) continue; - if (Prosecutor.prosecutor.Data.IsDead || Prosecutor.prosecutor.Data.Disconnected) continue; + if (Prosecutor.prosecutor.IsDead()) continue; if (Prosecutor.ProsecuteThisMeeting) { - if (voteState.VoterId == Prosecutor.prosecutor.PlayerId) + byte targetId = playerVoteArea.TargetPlayerId; + if (doSwap) { - if (playerInfo == null) + if (playerVoteArea.TargetPlayerId == swapped2.TargetPlayerId) targetId = swapped1.TargetPlayerId; + if (playerVoteArea.TargetPlayerId == swapped1.TargetPlayerId) targetId = swapped2.TargetPlayerId; + } + + if (voterState.VoterId == Prosecutor.prosecutor.PlayerId) + { + if (playerById == null) { - Error(string.Format("鎵句笉鍒版姇绁ㄨ呯殑鐜╁淇℃伅: {0}", voteState.VoterId)); + Error($"鎵句笉鍒版姇绁ㄨ呯殑鐜╁淇℃伅: {voterState.VoterId}"); Prosecutor.Prosecuted = true; } - else if (i == 0 && voteState.SkippedVote) + else if (i == 0 && voterState.SkippedVote) { - __instance.BloopAVoteIcon(playerInfo, amountOfSkippedVoters, __instance.SkippedVoting.transform); - __instance.BloopAVoteIcon(playerInfo, amountOfSkippedVoters, __instance.SkippedVoting.transform); - __instance.BloopAVoteIcon(playerInfo, amountOfSkippedVoters, __instance.SkippedVoting.transform); - __instance.BloopAVoteIcon(playerInfo, amountOfSkippedVoters, __instance.SkippedVoting.transform); - __instance.BloopAVoteIcon(playerInfo, amountOfSkippedVoters, __instance.SkippedVoting.transform); + for (var repeat = 0; repeat < 6; repeat++) + __instance.BloopAVoteIcon(playerById, allNums[i], playerVoteArea.transform); + amountOfSkippedVoters += 6; Prosecutor.Prosecuted = true; } - else if (voteState.VotedForId == playerVoteArea.TargetPlayerId) + else if (voterState.VotedForId == targetId) { - __instance.BloopAVoteIcon(playerInfo, allNums[i], playerVoteArea.transform); - __instance.BloopAVoteIcon(playerInfo, allNums[i], playerVoteArea.transform); - __instance.BloopAVoteIcon(playerInfo, allNums[i], playerVoteArea.transform); - __instance.BloopAVoteIcon(playerInfo, allNums[i], playerVoteArea.transform); - __instance.BloopAVoteIcon(playerInfo, allNums[i], playerVoteArea.transform); + for (var repeat = 0; repeat < 6; repeat++) + __instance.BloopAVoteIcon(playerById, allNums[i], playerVoteArea.transform); + allNums[i] += 6; Prosecutor.Prosecuted = true; } @@ -802,7 +787,7 @@ public static void Prefix(PlayerControl __instance, [HarmonyArgument(0)] GameDat { var timeBeforeMeeting = (float)(DateTime.UtcNow - entry.time).TotalMilliseconds / 1000; msg += Portalmaker.logShowsTime ? $"{(int)timeBeforeMeeting} 绉掑墠: " : ""; - msg = msg + $"{entry.name} 浣跨敤浜嗘槦闂╘n"; + msg += $"{entry.name} 浣跨敤浜嗘槦闂╘n"; } FastDestroyableSingleton.Instance.Chat.AddChat(Portalmaker.portalmaker, $"{msg}"); @@ -925,7 +910,7 @@ public static void Postfix(MeetingHud __instance) { Message("浼氳寮濮"); shookAlready = false; - if (CachedPlayer.LocalPlayer.IsDead) CanSeeRoleInfo = true; + if (CachedPlayer.LocalPlayer.IsDead && Specter.player != PlayerControl.LocalPlayer) CanSeeRoleInfo = true; // Remove first kill shield firstKillPlayer = null; diff --git a/TheOtherRoles/Patches/PlayerControlPatch.cs b/TheOtherRoles/Patches/PlayerControlPatch.cs index 17ed3b47..f5176331 100644 --- a/TheOtherRoles/Patches/PlayerControlPatch.cs +++ b/TheOtherRoles/Patches/PlayerControlPatch.cs @@ -98,7 +98,9 @@ private static void setBasePlayerOutlines() shouldShowGhostInfo()); } - if ((CachedPlayer.LocalPlayer.Data.IsDead || (BodyGuard.showShielded && target == BodyGuard.guarded)) && BodyGuard.guarded != null && target == BodyGuard.guarded) + if (BodyGuard.guarded != null && (CachedPlayer.LocalPlayer.Data.IsDead || + CachedPlayer.LocalPlayer.PlayerControl == BodyGuard.bodyguard || + (BodyGuard.showShielded && CachedPlayer.LocalPlayer.PlayerControl == BodyGuard.guarded))) { hasVisibleShield = true; color = new Color32(205, 150, 100, byte.MaxValue); @@ -324,7 +326,10 @@ private static void vampireSetTarget() if (Vampire.vampire == null || Vampire.vampire != CachedPlayer.LocalPlayer.PlayerControl) return; PlayerControl target = null; - if (Spy.spy != null || Sidekick.wasSpy || Jackal.wasSpy) + + var untargetablePlayers = new List(); + + if (Spy.spy != null) { if (Spy.impostorsCanKillAnyone) { @@ -332,12 +337,12 @@ private static void vampireSetTarget() } else { - target = setTarget(true, true, [Spy.spy, Sidekick.wasTeamRed ? Sidekick.sidekick : null, Jackal.wasTeamRed ? Jackal.jackal : null]); + target = setTarget(true, true); } } else { - target = setTarget(true, true, [Sidekick.wasImpostor ? Sidekick.sidekick : null, Jackal.wasImpostor ? Jackal.jackal : null]); + target = setTarget(true, true); } bool targetNearGarlic = false; @@ -352,38 +357,44 @@ private static void vampireSetTarget() private static void jackalSetTarget() { - if (Jackal.jackal == null || Jackal.jackal != CachedPlayer.LocalPlayer.PlayerControl) return; - var untargetablePlayers = new List(); - if (Sidekick.sidekick != null) untargetablePlayers.Add(Sidekick.sidekick); - //if (Jackal.jackal != null && Jackal.isInvisable) untargetablePlayers.Add(Jackal.jackal); - // Exclude Jackal from targeting the Mini unless it has grown up - if (Mini.mini != null && !Mini.isGrownUp()) untargetablePlayers.Add(Mini.mini); - Jackal.currentTarget = setTarget(untargetablePlayers: untargetablePlayers); - setPlayerOutline(Jackal.currentTarget, Palette.ImpostorRed); + if (Jackal.jackal.Any(x => x.IsAlive() && x.PlayerId == CachedPlayer.LocalId)) + { + var untargetablePlayers = new List(); + foreach (var p in Jackal.jackal) + { + untargetablePlayers.Add(p); + } + if (Jackal.sidekick != null) untargetablePlayers.Add(Jackal.sidekick); + if (Mini.mini != null && !Mini.isGrownUp()) untargetablePlayers.Add(Mini.mini); + Jackal.currentTarget = setTarget(untargetablePlayers: untargetablePlayers); + setPlayerOutline(Jackal.currentTarget, Palette.ImpostorRed); + } } private static void sidekickSetTarget() { - if (Sidekick.sidekick == null || Sidekick.sidekick != CachedPlayer.LocalPlayer.PlayerControl) return; + if (Jackal.sidekick == null || Jackal.sidekick != CachedPlayer.LocalPlayer.PlayerControl) return; var untargetablePlayers = new List(); - if (Jackal.jackal != null) untargetablePlayers.Add(Jackal.jackal); - if (Mini.mini != null && !Mini.isGrownUp()) - untargetablePlayers.Add(Mini.mini); // Exclude Sidekick from targeting the Mini unless it has grown up - Sidekick.currentTarget = setTarget(untargetablePlayers: untargetablePlayers); - if (Sidekick.canKill) setPlayerOutline(Sidekick.currentTarget, Palette.ImpostorRed); + foreach (var p in Jackal.jackal) + { + untargetablePlayers.Add(p); + } + if (Mini.mini != null && !Mini.isGrownUp()) untargetablePlayers.Add(Mini.mini); + Jackal.currentTarget2 = setTarget(untargetablePlayers: untargetablePlayers); + setPlayerOutline(Jackal.currentTarget2, Palette.ImpostorRed); } private static void sidekickCheckPromotion() { // If LocalPlayer is Sidekick, the Jackal is disconnected and Sidekick promotion is enabled, then trigger promotion - if (Sidekick.sidekick == null || Sidekick.sidekick != CachedPlayer.LocalPlayer.PlayerControl) return; - if (Sidekick.sidekick.Data.IsDead || !Sidekick.promotesToJackal) return; - if (Jackal.jackal == null || Jackal.jackal?.Data?.Disconnected == true) + if (Jackal.sidekick.IsDead()|| !Jackal.promotesToJackal || Jackal.sidekick != CachedPlayer.LocalPlayer.PlayerControl) return; + if (Jackal.jackal.Count > 0 && Jackal.jackal.All(x => x.IsDead())) { var writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.SidekickPromotes, SendOption.Reliable); + writer.Write(Jackal.sidekick.PlayerId); AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.sidekickPromotes(); + RPCProcedure.sidekickPromotes(CachedPlayer.LocalId); } } @@ -402,8 +413,6 @@ private static void eraserSetTarget() var untargetables = new List(); if (Spy.spy != null) untargetables.Add(Spy.spy); - if (Sidekick.wasTeamRed) untargetables.Add(Sidekick.sidekick); - if (Jackal.wasTeamRed) untargetables.Add(Jackal.jackal); Eraser.currentTarget = setTarget(!Eraser.canEraseAnyone, untargetablePlayers: Eraser.canEraseAnyone ? [] : untargetables); setPlayerOutline(Eraser.currentTarget, Eraser.color); @@ -432,8 +441,8 @@ private static void deputyUpdate() private static void engineerUpdate() { var jackalHighlight = Engineer.highlightForTeamJackal && - (CachedPlayer.LocalPlayer.PlayerControl == Jackal.jackal || - CachedPlayer.LocalPlayer.PlayerControl == Sidekick.sidekick); + (Jackal.jackal.Any(x => x == CachedPlayer.LocalPlayer.PlayerControl) || + CachedPlayer.LocalPlayer.PlayerControl == Jackal.sidekick); var impostorHighlight = Engineer.highlightForImpostors && CachedPlayer.LocalPlayer.Data.Role.IsImpostor; if ((jackalHighlight || impostorHighlight) && MapUtilities.CachedShipStatus?.AllVents != null) foreach (var vent in MapUtilities.CachedShipStatus.AllVents) @@ -468,7 +477,7 @@ private static void impostorSetTarget() } PlayerControl target; - if (Spy.spy != null || Sidekick.wasSpy || Jackal.wasSpy) + if (Spy.spy != null) { if (Spy.impostorsCanKillAnyone) { @@ -476,12 +485,12 @@ private static void impostorSetTarget() } else { - target = setTarget(true, true, [Spy.spy, Sidekick.wasTeamRed ? Sidekick.sidekick : null, Jackal.wasTeamRed ? Jackal.jackal : null]); + target = setTarget(true, true, [Spy.spy]); } } else { - target = setTarget(true, true, [Sidekick.wasImpostor ? Sidekick.sidekick : null, Jackal.wasImpostor ? Jackal.jackal : null]); + target = setTarget(true, true); } FastDestroyableSingleton.Instance.KillButton.SetTarget(target); // Includes setPlayerOutline(target, Palette.ImpstorRed); @@ -516,14 +525,14 @@ private static void swooperUpdate() AmongUsClient.Instance.FinishRpcImmediately(invisibleWriter); RPCProcedure.setSwoop(Swooper.swooper.PlayerId, byte.MaxValue); } - if (Jackal.isInvisable && Jackal.swoopTimer <= 0 && Jackal.jackal == CachedPlayer.LocalPlayer.PlayerControl) + if (Jackal.isInvisable && Jackal.swoopTimer <= 0 && Jackal.jackal.Any(x => x == CachedPlayer.LocalPlayer.PlayerControl)) { var invisibleWriter = AmongUsClient.Instance.StartRpcImmediately( CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.SetJackalSwoop, SendOption.Reliable); - invisibleWriter.Write(Jackal.jackal.PlayerId); + invisibleWriter.Write(CachedPlayer.LocalPlayer.PlayerId); invisibleWriter.Write(byte.MaxValue); AmongUsClient.Instance.FinishRpcImmediately(invisibleWriter); - RPCProcedure.setJackalSwoop(Jackal.jackal.PlayerId, byte.MaxValue); + RPCProcedure.setJackalSwoop(CachedPlayer.LocalPlayer.PlayerId, byte.MaxValue); } } @@ -721,16 +730,6 @@ public static void GiantSizeUpdate(PlayerControl p) { if (Giant.giant == null || InMeeting) return; - DeadBody[] array = Object.FindObjectsOfType(); - foreach (var body in array.Where(x => x.ParentId == Giant.giant.PlayerId)) - { - try - { - body.transform.localScale = new Vector3(Giant.size, Giant.size, 1f); - } - catch (Exception e) { Error(e, "updateGiant"); } - } - var collider = p.Collider.CastFast(); collider.offset = Mini.defaultColliderOffset * Vector2.down; @@ -745,41 +744,41 @@ public static void GiantSizeUpdate(PlayerControl p) public static void updatePlayerInfo() { - var local = CachedPlayer.LocalPlayer.PlayerControl; - //var colorBlindTextMeetingInitialLocalPos = new Vector3(0.3384f, -0.16666f, -0.01f); - //var colorBlindTextMeetingInitialLocalScale = new Vector3(0.9f, 1f, 1f); foreach (PlayerControl p in CachedPlayer.AllPlayers) { - // Colorblind Text in Meeting var playerVoteArea = MeetingHud.Instance?.playerStates?.FirstOrDefault(x => x.TargetPlayerId == p.PlayerId); if (playerVoteArea != null && playerVoteArea.ColorBlindName.gameObject.active) { playerVoteArea.ColorBlindName.transform.localPosition = new Vector3(-0.93f, -0.2f, -0.1f); - //playerVoteArea.ColorBlindName.transform.localScale = colorBlindTextMeetingInitialLocalScale * 0.8f; + playerVoteArea.ColorBlindName.fontSize *= 1.75f; } - // This moves both the name AND the colorblindtext behind objects (if the player is behind the object), like the rock on polus p.cosmetics.nameText.transform.parent.SetLocalZ(-0.0001f); - if (p == local || local.Data.IsDead || - (Lawyer.lawyerKnowsRole && local == Lawyer.lawyer && p == Lawyer.target) || + bool canSeeRole = (Lawyer.lawyerKnowsRole && local == Lawyer.lawyer && p == Lawyer.target) || (PartTimer.knowsRole && local == PartTimer.partTimer && p == PartTimer.target) || (local == PartTimer.target && p == PartTimer.partTimer) || (Akujo.knowsRoles && local == Akujo.akujo && (p == Akujo.honmei || Akujo.keeps.Any(x => x.PlayerId == p.PlayerId))) || - (p == Mayor.mayor && Mayor.Revealed) || - (local == Slueth.slueth && Slueth.reported.Any(x => x.PlayerId == p.PlayerId)) || - (ModOption.impostorSeeRoles && Spy.spy == null && CachedPlayer.LocalPlayer.Data.Role.IsImpostor && - !CachedPlayer.LocalPlayer.IsDead && p == (p.Data.Role.IsImpostor && !p.Data.IsDead)) || - (local == Poucher.poucher && Poucher.killed.Any(x => x.PlayerId == p.PlayerId))) + (ModOption.impostorSeeRoles && Spy.spy == null && PlayerControl.LocalPlayer.isImpostor() && + PlayerControl.LocalPlayer.IsAlive() && p.isImpostor() && p.IsAlive()); + + bool reported = ((local == Slueth.slueth && Slueth.reported.Any(x => x.PlayerId == p.PlayerId)) + || (local == Poucher.poucher && Poucher.killed.Any(x => x.PlayerId == p.PlayerId))) && p.IsDead(); + + if (p == local || local.Data.IsDead || canSeeRole || reported || (Mayor.mayor == p && Mayor.Revealed)) { + var roleNames = RoleInfo.GetRolesString(p, true, false, false, true); + var mainRole = RoleInfo.GetRolesString(p, true, false, false, false); + var allRoleText = RoleInfo.GetRolesString(p, true, true, true, true); + var playerInfoTransform = p.cosmetics.nameText.transform.parent.FindChild("Info"); var playerInfo = playerInfoTransform != null ? playerInfoTransform.GetComponent() : null; if (playerInfo == null) { playerInfo = Object.Instantiate(p.cosmetics.nameText, p.cosmetics.nameText.transform.parent); playerInfo.transform.localPosition += Vector3.up * 0.225f; - playerInfo.fontSize *= 0.75f; + playerInfo.fontSize *= 0.8f; playerInfo.gameObject.name = "Info"; playerInfo.color = playerInfo.color.SetAlpha(1f); } @@ -803,8 +802,6 @@ public static void updatePlayerInfo() } var (tasksCompleted, tasksTotal) = TasksHandler.taskInfo(p.Data); - var roleNames = RoleInfo.GetRolesString(p, true, false, false, true); - var allRoleText = RoleInfo.GetRolesString(p, true, true, true, true); var taskInfo = tasksTotal > 0 ? $"({tasksCompleted}/{tasksTotal})" : ""; var playerInfoText = ""; @@ -820,46 +817,42 @@ public static void updatePlayerInfo() } meetingInfoText = $"{allRoleText} {taskInfo}".Trim(); } - else if (ModOption.impostorSeeRoles - && Spy.spy == null - && CachedPlayer.LocalPlayer.Data.Role.IsImpostor - && !CachedPlayer.LocalPlayer.Data.IsDead - && p.Data.Role.IsImpostor && !p.Data.IsDead) - { - playerInfoText = roleNames; - meetingInfoText = playerInfoText; - } - else if (Lawyer.lawyerKnowsRole && local == Lawyer.lawyer && p == Lawyer.target) - { - playerInfoText = roleNames; - meetingInfoText = playerInfoText; - } - else if (PartTimer.partTimer == local && PartTimer.target != null && p == PartTimer.target && local.IsAlive()) + else if (reported) { - playerInfoText = roleNames; - meetingInfoText = playerInfoText; + meetingInfoText = playerInfoText = mainRole; } else if (local.IsAlive() && Mayor.mayor == p && Mayor.Revealed) { meetingInfoText = "Mayor".Translate(); } - else if (local == PartTimer.target && p == PartTimer.partTimer) + else if (canSeeRole) { - playerInfoText = roleNames; - meetingInfoText = playerInfoText; + meetingInfoText = playerInfoText = roleNames; } - else if (CanSeeRoleInfo) + else { - playerInfoText = $"{allRoleText} {taskInfo}".Trim(); - meetingInfoText = playerInfoText; + if (CanSeeRoleInfo) + { + playerInfoText = $"{allRoleText} {taskInfo}".Trim(); + meetingInfoText = playerInfoText; + } } playerInfo.text = playerInfoText; playerInfo.gameObject.SetActive(p.Visible); if (meetingInfo != null) - meetingInfo.text = MeetingHud.Instance.state == MeetingHud.VoteStates.Results - ? "" - : meetingInfoText; + { + meetingInfo.text = MeetingHud.Instance.state == MeetingHud.VoteStates.Results ? "" : meetingInfoText; + } + } + else + { + if (local != p && p != null) + { + var playerInfoTransform = p.cosmetics.nameText.transform.parent.FindChild("Info"); + var playerInfo = playerInfoTransform?.GetComponent(); + if (playerInfo != null) playerInfo.text = ""; + } } } } @@ -1091,9 +1084,7 @@ private static void bountyHunterUpdate() var possibleTargets = new List(); foreach (PlayerControl p in CachedPlayer.AllPlayers) if (!p.Data.IsDead && !p.Data.Disconnected && p != p.Data.Role.IsImpostor && p != Spy.spy && - (p != Sidekick.sidekick || !Sidekick.wasTeamRed) && (p != Jackal.jackal || !Jackal.wasTeamRed) && - (p != Mini.mini || Mini.isGrownUp()) && (BountyHunter.bountyHunter.getPartner() == null || - p != BountyHunter.bountyHunter.getPartner())) + (p != Mini.mini || Mini.isGrownUp()) && (BountyHunter.bountyHunter.getPartner() == null || p != BountyHunter.bountyHunter.getPartner())) possibleTargets.Add(p); if (possibleTargets.Count == 0) return; BountyHunter.bounty = possibleTargets[rnd.Next(0, possibleTargets.Count)]; @@ -1181,9 +1172,9 @@ private static void vultureUpdate() private static void amnisiacUpdate() { - if (Amnisiac.amnisiac == null || CachedPlayer.LocalPlayer.PlayerControl != Amnisiac.amnisiac || - Amnisiac.localArrows == null || !Amnisiac.showArrows) return; - if (Amnisiac.amnisiac.Data.IsDead) + if (Amnisiac.player.Count < 1 || Amnisiac.player.Any(x => x.PlayerId != CachedPlayer.LocalId) + || Amnisiac.localArrows == null || !Amnisiac.showArrows) return; + if (Amnisiac.player.Any(x => x.PlayerId == CachedPlayer.LocalId)) { foreach (var arrow in Amnisiac.localArrows) Object.Destroy(arrow.arrow); Amnisiac.localArrows = []; @@ -1556,8 +1547,6 @@ private static void witchSetTarget() // Also target players that have already been spelled, to hide spells that were blanks/blocked by shields untargetables = []; if (Spy.spy != null && !Witch.canSpellAnyone) untargetables.Add(Spy.spy); - if (Sidekick.wasTeamRed && !Witch.canSpellAnyone) untargetables.Add(Sidekick.sidekick); - if (Jackal.wasTeamRed && !Witch.canSpellAnyone) untargetables.Add(Jackal.jackal); } Witch.currentTarget = setTarget(!Witch.canSpellAnyone, untargetablePlayers: untargetables); @@ -1570,8 +1559,6 @@ private static void ninjaSetTarget() var untargetables = new List(); if (Spy.spy != null && !Spy.impostorsCanKillAnyone) untargetables.Add(Spy.spy); if (Mini.mini != null && !Mini.isGrownUp()) untargetables.Add(Mini.mini); - if (Sidekick.wasTeamRed && !Spy.impostorsCanKillAnyone) untargetables.Add(Sidekick.sidekick); - if (Jackal.wasTeamRed && !Spy.impostorsCanKillAnyone) untargetables.Add(Jackal.jackal); Ninja.currentTarget = setTarget(Spy.spy == null || !Spy.impostorsCanKillAnyone, untargetablePlayers: untargetables); setPlayerOutline(Ninja.currentTarget, Ninja.color); @@ -1647,7 +1634,7 @@ public static void miniCooldownUpdate() HudManagerStartPatch.sheriffKillButton.MaxTimer = Sheriff.cooldown * multiplier; HudManagerStartPatch.vampireKillButton.MaxTimer = Vampire.cooldown * multiplier; HudManagerStartPatch.jackalKillButton.MaxTimer = Jackal.cooldown * multiplier; - HudManagerStartPatch.sidekickKillButton.MaxTimer = Sidekick.cooldown * multiplier; + HudManagerStartPatch.sidekickKillButton.MaxTimer = Jackal.cooldown * multiplier; HudManagerStartPatch.warlockCurseButton.MaxTimer = Warlock.cooldown * multiplier; HudManagerStartPatch.pavlovsdogsKillButton.MaxTimer = Pavlovsdogs.cooldown * multiplier; HudManagerStartPatch.witchSpellButton.MaxTimer = (Witch.cooldown + Witch.currentCooldownAddition) * multiplier; @@ -1932,10 +1919,10 @@ public static void Prefix(PlayerPhysics __instance) { var correctOffset = !isCamoComms && Camouflager.camouflageTimer <= 0f && !MushroomSabotageActive() && (__instance.myPlayer == Mini.mini || - (Morphling.morphling != null && - __instance.myPlayer == Morphling.morphling && - Morphling.morphTarget == Mini.mini && - Morphling.morphTimer > 0f)); + (Morphling.morphling != null && + __instance.myPlayer == Morphling.morphling && + Morphling.morphTarget == Mini.mini && + Morphling.morphTimer > 0f)); correctOffset = correctOffset && !(Mini.mini == Morphling.morphling && Morphling.morphTimer > 0f); if (correctOffset) { @@ -1945,26 +1932,44 @@ public static void Prefix(PlayerPhysics __instance) } } - [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.Revive))] internal class PlayerControlRevivePatch { public static void Postfix(PlayerControl __instance) { - foreach (var p in PlayerControl.AllPlayerControls) + CanSeeRoleInfo = false; + + RPCProcedure.clearGhostRoles(__instance.PlayerId); + + if (__instance.isLover() && Lovers.otherLover(__instance)?.IsDead() == true) + { + Lovers.otherLover(__instance)?.Revive(); + } + + if (Akujo.akujo?.Data?.IsDead == true && Akujo.honmei?.Data?.IsDead == true) { - if (p != __instance && p != null) + var target = __instance == Akujo.akujo ? Akujo.honmei : __instance == Akujo.honmei ? Akujo.akujo : null; + + if (target?.Data.IsDead == true) { - var playerInfoTransform = p.cosmetics.nameText.transform.parent.FindChild("Info"); - var playerInfo = playerInfoTransform?.GetComponent(); - if (playerInfo != null) playerInfo.text = ""; + target.Revive(); + } + } + + DeadBody[] array = Object.FindObjectsOfType(); + for (var i = 0; i < array.Length; i++) + { + if (GameData.Instance.GetPlayerById(array[i].ParentId).PlayerId == __instance.PlayerId) + { + Object.Destroy(array[i].gameObject); + break; } } } } [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CmdReportDeadBody))] -internal class PlayerControlCmdReportDeadBodyPatch +internal class BodyReportPatch { public static bool Prefix(PlayerControl __instance) { @@ -1973,11 +1978,7 @@ public static bool Prefix(PlayerControl __instance) handleTrapperTrapOnBodyReport(); return true; } -} -[HarmonyPatch(typeof(PlayerControl), nameof(CachedPlayer.LocalPlayer.PlayerControl.CmdReportDeadBody))] -internal class BodyReportPatch -{ private static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] GameData.PlayerInfo target) { // Medic or Detective report @@ -2120,14 +2121,15 @@ public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] Player LastImpostor.promoteToLastImpostor(); } - // Sidekick promotion trigger on murder - if (Sidekick.promotesToJackal && Sidekick.sidekick != null && !Sidekick.sidekick.Data.IsDead && - target == Jackal.jackal && Jackal.jackal == CachedPlayer.LocalPlayer.PlayerControl) + // Sidekick promotion trigger on exile + if (Jackal.promotesToJackal && Jackal.sidekick != null && !Jackal.sidekick.Data.IsDead && + Jackal.jackal.Any(x => x == __instance && x == PlayerControl.LocalPlayer)) { var writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.SidekickPromotes, SendOption.Reliable); + writer.Write(Jackal.sidekick.PlayerId); AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.sidekickPromotes(); + RPCProcedure.sidekickPromotes(Jackal.sidekick.PlayerId); } // Pursuer promotion trigger on murder (the host sends the call such that everyone recieves the update before a possible game End) @@ -2285,7 +2287,7 @@ public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] Player { color = Color.white; if (target.Data.Role.IsImpostor) color = Color.red; - else if (RoleInfo.getRoleInfoForPlayer(target, false).FirstOrDefault().roleTeam == RoleType.Neutral) color = Color.blue; + else if (RoleInfo.getRoleInfoForPlayer(target, false).FirstOrDefault().roleType == RoleType.Neutral) color = Color.blue; } showFlash(color, 1.75f); @@ -2378,9 +2380,10 @@ public static void Postfix(PlayerControl __instance) var deadPlayer = new DeadPlayer(__instance, DateTime.UtcNow, CustomDeathReason.Exile, null); DeadPlayers.Add(deadPlayer); + if (__instance == PlayerControl.LocalPlayer && Specter.player != PlayerControl.LocalPlayer) CanSeeRoleInfo = true; + // Remove fake tasks when player dies - if (__instance.hasFakeTasks() || __instance == Lawyer.lawyer || __instance == Pursuer.pursuer.Contains(__instance) || - __instance == Thief.thief) + if (__instance.hasFakeTasks() || __instance == Pursuer.pursuer.Contains(__instance) || __instance == Thief.thief) __instance.clearAllTasks(); // Lover suicide trigger on exile @@ -2401,13 +2404,14 @@ public static void Postfix(PlayerControl __instance) } // Sidekick promotion trigger on exile - if (Sidekick.promotesToJackal && Sidekick.sidekick != null && !Sidekick.sidekick.Data.IsDead && - __instance == Jackal.jackal && Jackal.jackal == CachedPlayer.LocalPlayer.PlayerControl) + if (Jackal.promotesToJackal && Jackal.sidekick != null && !Jackal.sidekick.Data.IsDead && + Jackal.jackal.Any(x => x == __instance && x == PlayerControl.LocalPlayer)) { var writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.SidekickPromotes, SendOption.Reliable); + writer.Write(Jackal.sidekick.PlayerId); AmongUsClient.Instance.FinishRpcImmediately(writer); - RPCProcedure.sidekickPromotes(); + RPCProcedure.sidekickPromotes(Jackal.sidekick.PlayerId); } // Pursuer promotion trigger on exile & suicide (the host sends the call such that everyone recieves the update before a possible game End) diff --git a/TheOtherRoles/Patches/RoleAssignmentPatch.cs b/TheOtherRoles/Patches/RoleAssignmentPatch.cs index 5134313f..4f707a2d 100644 --- a/TheOtherRoles/Patches/RoleAssignmentPatch.cs +++ b/TheOtherRoles/Patches/RoleAssignmentPatch.cs @@ -147,6 +147,7 @@ public static RoleAssignmentData getRoleAssignmentData() neutralSettings.Add((byte)RoleId.Jester, CustomOptionHolder.jesterSpawnRate.GetSelection()); neutralSettings.Add((byte)RoleId.Lawyer, CustomOptionHolder.lawyerSpawnRate.GetSelection()); neutralSettings.Add((byte)RoleId.Executioner, CustomOptionHolder.executionerSpawnRate.GetSelection()); + neutralSettings.Add((byte)RoleId.Witness, CustomOptionHolder.witnessSpawnRate.GetSelection()); neutralSettings.Add((byte)RoleId.Vulture, CustomOptionHolder.vultureSpawnRate.GetSelection()); neutralSettings.Add((byte)RoleId.Doomsayer, CustomOptionHolder.doomsayerSpawnRate.GetSelection()); neutralSettings.Add((byte)RoleId.Akujo, CustomOptionHolder.akujoSpawnRate.GetSelection()); @@ -439,7 +440,7 @@ private static void assignRoleTargets(RoleAssignmentData data) // Lawyer foreach (PlayerControl p in CachedPlayer.AllPlayers) if (!p.Data.IsDead && !p.Data.Disconnected && p != Lovers.lover1 && p != Lovers.lover2 && - (p.Data.Role.IsImpostor || p == Swooper.swooper || p == Jackal.jackal || p == Juggernaut.juggernaut || + (p.Data.Role.IsImpostor || p == Swooper.swooper || Jackal.jackal.Any(x => x == p) || p == Juggernaut.juggernaut || p == Werewolf.werewolf || (Lawyer.targetCanBeJester && p == Jester.jester))) possibleTargets.Add(p); @@ -646,8 +647,11 @@ private static void assignGuesserGamemodeToPlayers(List playerLis { var IndexList = new Queue(); - if (Jackal.jackal != null && forceJackal) - IndexList.Enqueue(Jackal.jackal); + if (forceJackal) + { + foreach (var jackalPlayer in Jackal.jackal) + if (jackalPlayer != null) IndexList.Enqueue(jackalPlayer); + } if (Pavlovsdogs.pavlovsowner != null && forcePavlovsowner) IndexList.Enqueue(Pavlovsdogs.pavlovsowner); @@ -826,8 +830,8 @@ private static void assignModifiersToPlayers(List modifiers, List x.Data.Role.IsImpostor - || x == Jackal.jackal - || x == Sidekick.sidekick + || Jackal.jackal.Any(p => p == x) + || x == Jackal.sidekick || x == Lawyer.lawyer || x == Pavlovsdogs.pavlovsowner); } diff --git a/TheOtherRoles/Patches/UpdatePatch.cs b/TheOtherRoles/Patches/UpdatePatch.cs index 7de6bd10..07f74962 100644 --- a/TheOtherRoles/Patches/UpdatePatch.cs +++ b/TheOtherRoles/Patches/UpdatePatch.cs @@ -136,19 +136,21 @@ private static void setNameColors() } } - if (Jackal.jackal != null && Jackal.jackal == localPlayer) + if (Jackal.jackal != null && Jackal.jackal.Any(x => x == localPlayer)) { // Jackal can see his sidekick - setPlayerNameColor(Jackal.jackal, Jackal.color); - if (Sidekick.sidekick != null) setPlayerNameColor(Sidekick.sidekick, Jackal.color); + foreach(var p in Jackal.jackal) + setPlayerNameColor(p, Jackal.color); + if (Jackal.sidekick != null) setPlayerNameColor(Jackal.sidekick, Jackal.color); } // No else if here, as a Lover of team Jackal needs the colors - if (Sidekick.sidekick != null && Sidekick.sidekick == localPlayer) + if (Jackal.sidekick != null && Jackal.sidekick == localPlayer) { // Sidekick can see the jackal - setPlayerNameColor(Sidekick.sidekick, Sidekick.color); - if (Jackal.jackal != null) setPlayerNameColor(Jackal.jackal, Jackal.color); + setPlayerNameColor(Jackal.sidekick, Jackal.color); + foreach (var p in Jackal.jackal) + setPlayerNameColor(p, Jackal.color); } if (Pavlovsdogs.pavlovsowner != null && Pavlovsdogs.pavlovsowner == localPlayer) @@ -218,11 +220,7 @@ private static void setNameColors() // No else if here, as the Impostors need the Spy name to be colored if (Spy.spy != null && localPlayer.Data.Role.IsImpostor) setPlayerNameColor(Spy.spy, Spy.color); - if (Sidekick.sidekick != null && Sidekick.wasTeamRed && localPlayer.Data.Role.IsImpostor) - setPlayerNameColor(Sidekick.sidekick, Spy.color); - if (Jackal.jackal != null && Jackal.wasTeamRed && localPlayer.Data.Role.IsImpostor) - setPlayerNameColor(Jackal.jackal, Spy.color); // Crewmate roles with no changes: Mini // Impostor roles with no changes: Morphling, Camouflager, Vampire, Godfather, Eraser, Janitor, Cleaner, Warlock, BountyHunter, Witch and Mafioso } @@ -339,24 +337,15 @@ private static void setNameTags() player.NameText.text += suffix; } - // Former Thief - if (Thief.formerThief != null && (Thief.formerThief == local || local.Data.IsDead)) - { - var suffix = cs(Thief.color, " $"); - Thief.formerThief.cosmetics.nameText.text += suffix; - if (MeetingHud.Instance != null) - foreach (var player in MeetingHud.Instance.playerStates) - if (player.TargetPlayerId == Thief.formerThief.PlayerId) - player.NameText.text += suffix; - } - // Display lighter / darker color for all alive players if (CachedPlayer.LocalPlayer != null && MeetingHud.Instance != null && ModOption.showLighterDarker) + { foreach (var player in MeetingHud.Instance.playerStates) { var target = playerById(player.TargetPlayerId); if (target != null) player.NameText.text += $" ({(isLighterColor(target) ? "娴" : "娣")})"; } + } // Add medic shield info: if (MeetingHud.Instance != null && Medic.medic != null && Medic.shielded != null && Medic.shieldVisible(Medic.shielded)) @@ -397,7 +386,7 @@ public static void miniUpdate() (Mini.mini == Morphling.morphling && Morphling.morphTimer > 0f) || (Mini.mini == Ninja.ninja && Ninja.isInvisble) || SurveillanceMinigamePatch.nightVisionIsActive || (Mini.mini == Swooper.swooper && Swooper.isInvisable) || - (Mini.mini == Jackal.jackal && Jackal.isInvisable) || isActiveCamoComms) return; + (Jackal.jackal.Any(x => x == Mini.mini) && Jackal.isInvisable) || isActiveCamoComms) return; var growingProgress = Mini.growingProgress(); var scale = (growingProgress * 0.35f) + 0.35f; @@ -474,6 +463,20 @@ private static void updateMapButton(HudManager __instance) __instance.MapButton.HeldButtonSprite.color = Trapper.playersOnMap.Any() ? Trapper.color : Color.white; } + public static void updateGiantSize(HudManager __instance) + { + if (Giant.giant == null) return; + DeadBody[] array = UnityEngine.Object.FindObjectsOfType(); + foreach (var body in array.Where(x => x.ParentId == Giant.giant.PlayerId)) + { + try + { + body.transform.localScale = new Vector3(Giant.size, Giant.size, 1f); + } + catch { } + } + } + private static void Postfix(HudManager __instance) { var player = PlayerControl.LocalPlayer; @@ -510,6 +513,7 @@ private static void Postfix(HudManager __instance) // Meeting hide buttons if needed (used for the map usage, because closing the map would show buttons) updateSabotageButton(__instance); updateUseButton(__instance); + updateGiantSize(__instance); updateBlindReport(); updateMapButton(__instance); if (!MeetingHud.Instance) __instance.AbilityButton?.Update(); diff --git a/TheOtherRoles/RPC.cs b/TheOtherRoles/RPC.cs index b62f7637..3c97e315 100644 --- a/TheOtherRoles/RPC.cs +++ b/TheOtherRoles/RPC.cs @@ -8,7 +8,6 @@ using Hazel; using InnerNet; using PowerTools; -using Reactor.Networking.Extensions; using Reactor.Utilities; using Reactor.Utilities.Extensions; using TheOtherRoles.Buttons; @@ -63,6 +62,7 @@ public enum RoleId Executioner, Pursuer, PartTimer, + Witness, Doomsayer, Arsonist, Jackal, @@ -134,6 +134,7 @@ public enum RoleId Shifter, GhostEngineer = 200, + Specter, } public enum CustomRPC @@ -156,12 +157,10 @@ public enum CustomRPC ShareGameMode = 95, // Role functionality - EngineerFixLights = 100, - EngineerFixSubmergedOxygen, - EngineerUsedRepair, + FixLights = 100, + FixSubmergedOxygen, CleanBody, DissectionBody, - RandomDeadBodyPosition, Mine, ShowIndomitableFlash, DragBody, @@ -198,6 +197,7 @@ public enum CustomRPC PavlovsCreateDog, SidekickPromotes, ErasePlayerRoles, + ClearGhostRoles, SetFutureErased, SetFutureReveal, SetFutureShifted, @@ -207,6 +207,7 @@ public enum CustomRPC PlaceNinjaTrace, PlacePortal, AmnisiacTakeRole, + SpecterTakeRole, MimicMimicRole, UsePortal, PlaceJackInTheBox, @@ -252,7 +253,6 @@ public enum CustomRPC TriggerTrap, PlaceBomb, DefuseBomb, - //ShareRoom, YoyoMarkLocation, YoyoBlink, BalancerBalance, @@ -410,7 +410,7 @@ public static void setRole(byte roleId, byte playerId) TimeMaster.timeMaster = player; break; case RoleId.Amnisiac: - Amnisiac.amnisiac = player; + Amnisiac.player.Add(player); break; case RoleId.PartTimer: PartTimer.partTimer = player; @@ -445,6 +445,9 @@ public static void setRole(byte roleId, byte playerId) case RoleId.Butcher: Butcher.butcher = player; break; + case RoleId.Witness: + Witness.player = player; + break; case RoleId.Hacker: Hacker.hacker = player; break; @@ -458,10 +461,10 @@ public static void setRole(byte roleId, byte playerId) Snitch.snitch = player; break; case RoleId.Jackal: - Jackal.jackal = player; + Jackal.jackal.Add(player); break; case RoleId.Sidekick: - Sidekick.sidekick = player; + Jackal.sidekick = player; break; case RoleId.Pavlovsowner: Pavlovsdogs.pavlovsowner = player; @@ -679,6 +682,9 @@ public static void setGhostRole(byte playerId, byte roleId) case RoleId.GhostEngineer: GhostEngineer.player = player; break; + case RoleId.Specter: + Specter.player = player; + break; } } @@ -760,16 +766,6 @@ public static void FixSubmergedOxygen() SubmergedCompatibility.RepairOxygen(); } - public static void engineerUsedRepair() - { - Engineer.remainingFixes--; - /* - if (Helpers.shouldShowGhostInfo()) - { - Helpers.showFlash(Engineer.color, 0.5f, "Engineer Fix"); - }*/ - } - public static void showIndomitableFlash() { if (Indomitable.indomitable == CachedPlayer.LocalPlayer.PlayerControl) showFlash(Indomitable.color); @@ -818,10 +814,6 @@ public static void dissectionBody(byte playerId, byte killerId) } } - public static void RandomDeadBodyPosition(byte parentId, Vector2 position) - { - } - public static void dragBody(byte playerId) { DeadBody[] array = Object.FindObjectsOfType(); @@ -883,667 +875,6 @@ public static void impostorPromotesToLastImpostor(byte targetId) LastImpostor.lastImpostor = target; } - public static void amnisiacTakeRole(byte targetId) - { - var target = playerById(targetId); - var amnisiac = Amnisiac.amnisiac; - if (target == null || amnisiac == null) return; - var targetInfo = RoleInfo.getRoleInfoForPlayer(target); - var roleInfo = targetInfo.FirstOrDefault(info => info.roleTeam != RoleType.Modifier); - switch (roleInfo!.roleId) - { - case RoleId.Crewmate: - Amnisiac.clearAndReload(); - break; - case RoleId.Impostor: - Helpers.turnToImpostor(Amnisiac.amnisiac); - Amnisiac.clearAndReload(); - break; - case RoleId.Jester: - if (Amnisiac.resetRole) Jester.clearAndReload(); - Jester.jester = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - - case RoleId.BodyGuard: - if (Amnisiac.resetRole) BodyGuard.clearAndReload(); - BodyGuard.bodyguard = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Werewolf: - if (Amnisiac.resetRole) Werewolf.clearAndReload(); - Werewolf.werewolf = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - - case RoleId.Prosecutor: - Prosecutor.prosecutor = target; - Amnisiac.clearAndReload(); - break; - - case RoleId.Mayor: - if (Amnisiac.resetRole) Mayor.clearAndReload(); - Mayor.mayor = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Portalmaker: - if (Amnisiac.resetRole) Portalmaker.clearAndReload(); - Portalmaker.portalmaker = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Engineer: - if (Amnisiac.resetRole) Engineer.clearAndReload(); - Engineer.engineer = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Sheriff: - // Never reload Sheriff - if (Sheriff.formerDeputy != null && Sheriff.formerDeputy == Sheriff.sheriff) - { - Sheriff.formerDeputy = null; - Deputy.deputy = amnisiac; - } - else Sheriff.sheriff = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Deputy: - if (Amnisiac.resetRole) Deputy.clearAndReload(); - Deputy.deputy = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Poucher: - if (!Poucher.spawnModifier) - { - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Poucher.clearAndReload(); - Poucher.poucher = amnisiac; - Amnisiac.clearAndReload(); - } - break; - - case RoleId.Butcher: - Helpers.turnToImpostor(Amnisiac.amnisiac); - Butcher.butcher = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Detective: - if (Amnisiac.resetRole) Detective.clearAndReload(); - Detective.detective = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Balancer: - if (Amnisiac.resetRole) Balancer.clearAndReload(); - Balancer.balancer = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.InfoSleuth: - InfoSleuth.infoSleuth = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.TimeMaster: - if (Amnisiac.resetRole) TimeMaster.clearAndReload(); - TimeMaster.timeMaster = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Veteran: - if (Amnisiac.resetRole) Veteran.clearAndReload(); - Veteran.veteran = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Medic: - if (Amnisiac.resetRole) Medic.clearAndReload(); - Medic.medic = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Shifter: - if (Amnisiac.resetRole) Shifter.clearAndReload(); - Shifter.shifter = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Swapper: - if (Amnisiac.resetRole) Swapper.clearAndReload(); - Swapper.swapper = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.PartTimer: - if (Amnisiac.resetRole) PartTimer.clearAndReload(); - PartTimer.partTimer = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Seer: - if (Amnisiac.resetRole) Seer.clearAndReload(); - Seer.seer = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Morphling: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Morphling.clearAndReload(); - Morphling.morphling = amnisiac; - Amnisiac.clearAndReload(); - break; - case RoleId.Bomber: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Bomber.clearAndReload(); - Bomber.bomber = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Yoyo: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Yoyo.clearAndReload(); - Yoyo.yoyo = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Terrorist: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Terrorist.clearAndReload(); - Terrorist.terrorist = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Camouflager: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Camouflager.clearAndReload(); - Camouflager.camouflager = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Hacker: - if (Amnisiac.resetRole) Hacker.clearAndReload(); - Hacker.hacker = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Tracker: - if (Amnisiac.resetRole) Tracker.clearAndReload(); - Tracker.tracker = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Vampire: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Vampire.clearAndReload(); - Vampire.vampire = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Snitch: - if (Amnisiac.resetRole) Snitch.clearAndReload(); - Snitch.snitch = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Jackal: - Jackal.jackal = amnisiac; - Jackal.formerJackals.Add(target); - Amnisiac.clearAndReload(); - break; - - case RoleId.Sidekick: - Jackal.formerJackals.Add(target); - if (Amnisiac.resetRole) Sidekick.clearAndReload(); - Sidekick.sidekick = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Pavlovsowner: - Pavlovsdogs.pavlovsdogs.Add(Pavlovsdogs.pavlovsowner); - Pavlovsdogs.pavlovsowner = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Pavlovsdogs: - Pavlovsdogs.pavlovsdogs.Add(amnisiac); - Amnisiac.clearAndReload(); - break; - - case RoleId.Eraser: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Eraser.clearAndReload(); - Eraser.eraser = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Spy: - if (Amnisiac.resetRole) Spy.clearAndReload(); - Spy.spy = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Trickster: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Trickster.clearAndReload(); - Trickster.trickster = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Mimic: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Mimic.clearAndReload(false); - Mimic.mimic = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Cleaner: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Cleaner.clearAndReload(); - Cleaner.cleaner = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Warlock: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Warlock.clearAndReload(); - Warlock.warlock = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Grenadier: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Grenadier.clearAndReload(); - Grenadier.grenadier = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.SecurityGuard: - if (Amnisiac.resetRole) SecurityGuard.clearAndReload(); - SecurityGuard.securityGuard = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Survivor: - Survivor.survivor.Add(amnisiac); - Amnisiac.clearAndReload(); - break; - - case RoleId.Arsonist: - if (Amnisiac.resetRole) Arsonist.clearAndReload(); - Arsonist.arsonist = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - - if (CachedPlayer.LocalPlayer.PlayerControl == Arsonist.arsonist) - { - var playerCounter = 0; - var bottomLeft = new Vector3( - -FastDestroyableSingleton.Instance.UseButton.transform.localPosition.x, - FastDestroyableSingleton.Instance.UseButton.transform.localPosition.y, - FastDestroyableSingleton.Instance.UseButton.transform.localPosition.z); - foreach (PlayerControl p in CachedPlayer.AllPlayers) - if (playerIcons.ContainsKey(p.PlayerId) && p != Arsonist.arsonist) - { - //Arsonist.poolIcons.Add(p); - if (Arsonist.dousedPlayers.Contains(p)) - playerIcons[p.PlayerId].setSemiTransparent(false); - else - playerIcons[p.PlayerId].setSemiTransparent(true); - - playerIcons[p.PlayerId].transform.localPosition = bottomLeft + - new Vector3(-0.25f, -0.25f, 0) + - (Vector3.right * playerCounter++ * 0.35f); - playerIcons[p.PlayerId].transform.localScale = Vector3.one * 0.2f; - playerIcons[p.PlayerId].gameObject.SetActive(true); - } - } - - break; - - case RoleId.Assassin: - Helpers.turnToImpostor(Amnisiac.amnisiac); - // Never Reload Guesser - Assassin.assassin.Add(amnisiac); - Amnisiac.clearAndReload(); - break; - - case RoleId.Vigilante: - // Never Reload Guesser - if (Amnisiac.resetRole) Vigilante.clearAndReload(); - Vigilante.vigilante = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.BountyHunter: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) BountyHunter.clearAndReload(); - BountyHunter.bountyHunter = amnisiac; - Amnisiac.clearAndReload(); - - BountyHunter.bountyUpdateTimer = 0f; - if (CachedPlayer.LocalPlayer.PlayerControl == BountyHunter.bountyHunter) - { - var bottomLeft = - new Vector3(-FastDestroyableSingleton.Instance.UseButton.transform.localPosition.x, - FastDestroyableSingleton.Instance.UseButton.transform.localPosition.y, - FastDestroyableSingleton.Instance.UseButton.transform.localPosition.z) + - new Vector3(-0.25f, 1f, 0); - BountyHunter.cooldownText = - Object.Instantiate(FastDestroyableSingleton.Instance.KillButton.cooldownTimerText, - FastDestroyableSingleton.Instance.transform); - BountyHunter.cooldownText.alignment = TextAlignmentOptions.Center; - BountyHunter.cooldownText.transform.localPosition = bottomLeft + new Vector3(0f, -1f, -1f); - BountyHunter.cooldownText.gameObject.SetActive(true); - - foreach (PlayerControl p in CachedPlayer.AllPlayers) - if (playerIcons.ContainsKey(p.PlayerId)) - { - playerIcons[p.PlayerId].setSemiTransparent(false); - playerIcons[p.PlayerId].transform.localPosition = bottomLeft + new Vector3(0f, -1f, 0); - playerIcons[p.PlayerId].transform.localScale = Vector3.one * 0.4f; - playerIcons[p.PlayerId].gameObject.SetActive(false); - } - } - - break; - - case RoleId.Vulture: - if (Amnisiac.resetRole) Vulture.clearAndReload(); - Vulture.vulture = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - - case RoleId.Executioner: - Executioner.executioner = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - - case RoleId.Medium: - if (Amnisiac.resetRole) Medium.clearAndReload(); - Medium.medium = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Lawyer: - // Never reset Lawyer - Lawyer.lawyer = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - - case RoleId.Pursuer: - if (Amnisiac.resetRole) Pursuer.clearAndReload(); - Pursuer.pursuer.Add(amnisiac); - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - - case RoleId.Witch: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Witch.clearAndReload(); - Witch.witch = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Jumper: - if (Amnisiac.resetRole) Jumper.clearAndReload(); - Jumper.jumper = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Escapist: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Escapist.clearAndReload(); - Escapist.escapist = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Thief: - if (Amnisiac.resetRole) Thief.clearAndReload(); - Thief.thief = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - - case RoleId.Trapper: - if (Amnisiac.resetRole) Trapper.clearAndReload(); - Trapper.trapper = amnisiac; - Amnisiac.clearAndReload(); - break; - case RoleId.Juggernaut: - if (Amnisiac.resetRole) Juggernaut.clearAndReload(); - Juggernaut.juggernaut = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - case RoleId.Doomsayer: - if (Amnisiac.resetRole) Doomsayer.clearAndReload(); - Doomsayer.doomsayer = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - case RoleId.Swooper: - if (Amnisiac.resetRole) Swooper.clearAndReload(); - Swooper.swooper = amnisiac; - Amnisiac.clearAndReload(); - Amnisiac.amnisiac = target; - break; - case RoleId.Ninja: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Ninja.clearAndReload(); - Ninja.ninja = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Akujo: - Helpers.turnToImpostor(Akujo.akujo); - if (Amnisiac.resetRole) Mimic.clearAndReload(); - Akujo.akujo = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Blackmailer: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Blackmailer.clearAndReload(); - Blackmailer.blackmailer = amnisiac; - Amnisiac.clearAndReload(); - break; - - case RoleId.Miner: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Miner.clearAndReload(); - Miner.miner = amnisiac; - Amnisiac.clearAndReload(); - break; - case RoleId.Undertaker: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) Undertaker.clearAndReload(); - Undertaker.undertaker = amnisiac; - Amnisiac.clearAndReload(); - break; - case RoleId.Prophet: - if (Amnisiac.resetRole) Prophet.clearAndReload(); - Prophet.prophet = amnisiac; - Amnisiac.clearAndReload(); - break; - case RoleId.EvilTrapper: - Helpers.turnToImpostor(Amnisiac.amnisiac); - if (Amnisiac.resetRole) EvilTrapper.clearAndReload(); - EvilTrapper.evilTrapper = amnisiac; - Amnisiac.clearAndReload(); - break; - case RoleId.Gambler: - Gambler.gambler = amnisiac; - Amnisiac.clearAndReload(); - break; - } - } - - public static void mimicMimicRole(byte targetId) - { - var target = playerById(targetId); - if (target == null || Mimic.mimic == null) return; - var targetInfo = RoleInfo.getRoleInfoForPlayer(target); - var roleInfo = targetInfo.FirstOrDefault(info => info.roleTeam != RoleType.Modifier); - switch (roleInfo!.roleId) - { - case RoleId.BodyGuard: - if (Amnisiac.resetRole) BodyGuard.clearAndReload(); - BodyGuard.bodyguard = Mimic.mimic; - bodyGuardGuardButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Mayor: - if (Amnisiac.resetRole) Mayor.clearAndReload(); - Mayor.mayor = Mimic.mimic; - mayorMeetingButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - - Mimic.hasMimic = true; - break; - - case RoleId.Prosecutor: - if (Amnisiac.resetRole) Prosecutor.clearAndReload(); - Prosecutor.prosecutor = Mimic.mimic; - Prosecutor.diesOnIncorrectPros = false; - Mimic.hasMimic = true; - break; - - case RoleId.InfoSleuth: - InfoSleuth.infoSleuth = Mimic.mimic; - Mimic.hasMimic = true; - break; - - case RoleId.Trapper: - if (Amnisiac.resetRole) Trapper.clearAndReload(); - Trapper.trapper = Mimic.mimic; - trapperButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Portalmaker: - if (Amnisiac.resetRole) Portalmaker.clearAndReload(); - Portalmaker.portalmaker = Mimic.mimic; - portalmakerPlacePortalButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Engineer: - if (Amnisiac.resetRole) Engineer.clearAndReload(); - Engineer.engineer = Mimic.mimic; - engineerRepairButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Jumper: - if (Amnisiac.resetRole) Jumper.clearAndReload(); - Jumper.jumper = Mimic.mimic; - jumperMarkButton.PositionOffset = CustomButton.ButtonPositions.lowerRowCenter; - jumperJumpButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Detective: - if (Amnisiac.resetRole) Detective.clearAndReload(); - Detective.detective = Mimic.mimic; - Mimic.hasMimic = true; - break; - /* - case RoleId.NiceGuesser: - if (Amnisiac.resetRole) //Guesser.clearAndReload(); - Guesser.niceGuesser = Mimic.mimic; - Mimic.hasMimic = true; - break; - */ - case RoleId.TimeMaster: - if (Amnisiac.resetRole) TimeMaster.clearAndReload(); - TimeMaster.timeMaster = Mimic.mimic; - timeMasterShieldButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Veteran: - if (Amnisiac.resetRole) Veteran.clearAndReload(); - Veteran.veteran = Mimic.mimic; - veteranAlertButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Medic: - if (Amnisiac.resetRole) Medic.clearAndReload(); - Medic.medic = Mimic.mimic; - medicShieldButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Swapper: - if (Amnisiac.resetRole) Swapper.clearAndReload(); - Swapper.swapper = Mimic.mimic; - Mimic.hasMimic = true; - break; - - case RoleId.Seer: - if (Amnisiac.resetRole) Seer.clearAndReload(); - Seer.seer = Mimic.mimic; - Mimic.hasMimic = true; - break; - - case RoleId.Hacker: - if (Amnisiac.resetRole) Hacker.clearAndReload(); - Hacker.hacker = Mimic.mimic; - hackerAdminTableButton.PositionOffset = CustomButton.ButtonPositions.upperRowFarLeft; - hackerVitalsButton.PositionOffset = CustomButton.ButtonPositions.lowerRowFarLeft; - hackerButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Tracker: - if (Amnisiac.resetRole) Tracker.clearAndReload(); - Tracker.tracker = Mimic.mimic; - trackerTrackPlayerButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.SecurityGuard: - if (Amnisiac.resetRole) SecurityGuard.clearAndReload(); - SecurityGuard.securityGuard = Mimic.mimic; - securityGuardButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - securityGuardCamButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Medium: - if (Amnisiac.resetRole) Medium.clearAndReload(); - Medium.medium = Mimic.mimic; - mediumButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - - case RoleId.Balancer: - if (Amnisiac.resetRole) Balancer.clearAndReload(); - Balancer.balancer = Mimic.mimic; - Mimic.hasMimic = true; - break; - - case RoleId.Prophet: - if (Amnisiac.resetRole) Prophet.clearAndReload(); - Prophet.prophet = Mimic.mimic; - prophetButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; - Mimic.hasMimic = true; - break; - } - } - public static void turnToImpostor(byte targetId) { var player = playerById(targetId); @@ -1832,34 +1163,11 @@ public static void jackalCreatesSidekick(byte targetId) } } - if (Jackal.killFakeImpostor && target.Data.Role.IsImpostor) - { - //uncheckedMurderPlayer(Jackal.jackal.PlayerId, player.PlayerId, 1); - Jackal.jackal.MurderPlayer(target, MurderResultFlags.Succeeded); - OverrideDeathReasonAndKiller(target, CustomDeathReason.FakeSK, Jackal.jackal); - return; - } - - var wasSpy = Spy.spy != null && target == Spy.spy; - var wasImpostor = target.Data.Role.IsImpostor; // This can only be reached if impostors can be sidekicked. FastDestroyableSingleton.Instance.SetRole(target, RoleTypes.Crewmate); - if (target == Lawyer.lawyer && Lawyer.target != null) - { - var playerInfoTransform = Lawyer.target.cosmetics.nameText.transform.parent.FindChild("Info"); - var playerInfo = playerInfoTransform?.GetComponent(); - if (playerInfo != null) playerInfo.text = ""; - } erasePlayerRoles(target.PlayerId); - Sidekick.sidekick = target; - if (target.PlayerId == CachedPlayer.LocalPlayer.PlayerId) - CachedPlayer.LocalPlayer.PlayerControl.moveable = true; - if ((wasSpy || wasImpostor) && !Jackal.CanImpostorFindSidekick) - { - Sidekick.wasTeamRed = true; - Sidekick.wasSpy = wasSpy; - Sidekick.wasImpostor = wasImpostor; - } + Jackal.sidekick = target; + if (target == CachedPlayer.LocalPlayer.PlayerControl) SoundEffectsManager.play("jackalSidekick"); if (HandleGuesser.isGuesserGm && CustomOptionHolder.guesserGamemodeSidekickIsAlwaysGuesser.GetBool() && !HandleGuesser.isGuesser(targetId)) setGuesserGm(targetId); @@ -1867,26 +1175,16 @@ public static void jackalCreatesSidekick(byte targetId) Jackal.canCreateSidekick = false; } - public static void sidekickPromotes() - { - removeCurrentJackal(); - Jackal.jackal = Sidekick.sidekick; - Jackal.canCreateSidekick = Jackal.jackalPromotedFromSidekickCanCreateSidekick; - Jackal.wasTeamRed = Sidekick.wasTeamRed; - Jackal.wasSpy = Sidekick.wasSpy; - Jackal.wasImpostor = Sidekick.wasImpostor; - Sidekick.clearAndReload(); - return; - } - - public static void removeCurrentJackal() + public static void sidekickPromotes(byte playerId) { - if (Jackal.jackal != null && !Jackal.formerJackals.Any(x => x.PlayerId == Jackal.jackal.PlayerId)) - Jackal.formerJackals.Add(Jackal.jackal); - Jackal.jackal = null; - Jackal.currentTarget = null; - Jackal.cooldown = CustomOptionHolder.jackalKillCooldown.GetFloat(); - Jackal.createSidekickCooldown = CustomOptionHolder.jackalCreateSidekickCooldown.GetFloat(); + if (Jackal.jackal.All(x => x.IsDead())) + { + var player = playerById(playerId); + Jackal.jackal.Add(player); + Jackal.sidekick = null; + Jackal.canCreateSidekick = Jackal.jackalPromotedFromSidekickCanCreateSidekick; + return; + } } public static void jackalCanSwooper(bool chance) @@ -1939,8 +1237,6 @@ public static void erasePlayerRoles(byte playerId, bool ignoreModifier = true) if (player == null) return; // Crewmate roles - if (Assassin.assassin.Any(x => x.PlayerId == player.PlayerId)) - Assassin.assassin.RemoveAll(x => x.PlayerId == player.PlayerId); if (player == Swooper.swooper) Swooper.clearAndReload(); if (player == Mayor.mayor) Mayor.clearAndReload(); if (player == Prosecutor.prosecutor) Prosecutor.clearAndReload(); @@ -1952,7 +1248,6 @@ public static void erasePlayerRoles(byte playerId, bool ignoreModifier = true) if (player == Deputy.deputy) Deputy.deputy = null; if (player == Detective.detective) Detective.clearAndReload(); if (player == TimeMaster.timeMaster) TimeMaster.clearAndReload(); - if (player == Amnisiac.amnisiac) Amnisiac.clearAndReload(); if (player == Veteran.veteran) Veteran.clearAndReload(); if (player == Medic.medic) Medic.clearAndReload(); if (player == Seer.seer) Seer.clearAndReload(); @@ -2000,15 +1295,15 @@ public static void erasePlayerRoles(byte playerId, bool ignoreModifier = true) if (player == Miner.miner) Miner.clearAndReload(); if (player == Arsonist.arsonist) Arsonist.clearAndReload(); if (Guesser.isGuesser(player.PlayerId)) Guesser.clear(player.PlayerId); - if (player == Jackal.jackal) + + if (Jackal.jackal.Any(x => x == player)) { - Jackal.jackal = null; - sidekickPromotes(); + Jackal.jackal.RemoveAll(x => x == player); + if (Jackal.sidekick.IsAlive()) sidekickPromotes(Jackal.sidekick.PlayerId); } - if (player == Shifter.shifter) Shifter.clearAndReload(); if (player == Pavlovsdogs.pavlovsowner) Pavlovsdogs.pavlovsowner = null; - if (player == Sidekick.sidekick) Sidekick.clearAndReload(); + if (player == Jackal.sidekick) Jackal.sidekick = null; if (player == BountyHunter.bountyHunter) BountyHunter.clearAndReload(); if (player == Vulture.vulture) Vulture.clearAndReload(); if (player == Executioner.executioner) Executioner.clearAndReload(); @@ -2018,34 +1313,31 @@ public static void erasePlayerRoles(byte playerId, bool ignoreModifier = true) if (player == Doomsayer.doomsayer) Doomsayer.clearAndReload(); if (player == Akujo.akujo) Akujo.clearAndReload(); if (player == PartTimer.partTimer) PartTimer.clearAndReload(); - if (player == Specoality.specoality) Specoality.clearAndReload(); - if (Pavlovsdogs.pavlovsdogs.Any(x => x.PlayerId == player.PlayerId)) - Pavlovsdogs.pavlovsdogs.RemoveAll(x => x.PlayerId == player.PlayerId); - if (Pursuer.pursuer.Any(x => x.PlayerId == player.PlayerId)) - Pursuer.pursuer.RemoveAll(x => x.PlayerId == player.PlayerId); - if (Survivor.survivor.Any(x => x.PlayerId == player.PlayerId)) - Survivor.survivor.RemoveAll(x => x.PlayerId == player.PlayerId); + if (player == Cursed.cursed) Cursed.clearAndReload(); + if (player == Shifter.shifter) Shifter.clearAndReload(); + + Assassin.assassin.RemoveAll(x => x.PlayerId == player.PlayerId); + Amnisiac.player.RemoveAll(x => x.PlayerId == playerId); + Pavlovsdogs.pavlovsdogs.RemoveAll(x => x.PlayerId == player.PlayerId); + Pursuer.pursuer.RemoveAll(x => x.PlayerId == player.PlayerId); + Survivor.survivor.RemoveAll(x => x.PlayerId == player.PlayerId); // Modifier if (!ignoreModifier) { - if (player == Lovers.lover1 || player == Lovers.lover2) - Lovers.clearAndReload(); // The whole Lover couple is being erased - if (Bait.bait.Any(x => x.PlayerId == player.PlayerId)) - Bait.bait.RemoveAll(x => x.PlayerId == player.PlayerId); - if (Bloody.bloody.Any(x => x.PlayerId == player.PlayerId)) - Bloody.bloody.RemoveAll(x => x.PlayerId == player.PlayerId); - if (AntiTeleport.antiTeleport.Any(x => x.PlayerId == player.PlayerId)) - AntiTeleport.antiTeleport.RemoveAll(x => x.PlayerId == player.PlayerId); - if (Sunglasses.sunglasses.Any(x => x.PlayerId == player.PlayerId)) - Sunglasses.sunglasses.RemoveAll(x => x.PlayerId == player.PlayerId); - if (Torch.torch.Any(x => x.PlayerId == player.PlayerId)) - Torch.torch.RemoveAll(x => x.PlayerId == player.PlayerId); - if (Flash.flash.Any(x => x.PlayerId == player.PlayerId)) - Flash.flash.RemoveAll(x => x.PlayerId == player.PlayerId); - if (Multitasker.multitasker.Any(x => x.PlayerId == player.PlayerId)) - Multitasker.multitasker.RemoveAll(x => x.PlayerId == player.PlayerId); + Bait.bait.RemoveAll(x => x.PlayerId == player.PlayerId); + Bloody.bloody.RemoveAll(x => x.PlayerId == player.PlayerId); + AntiTeleport.antiTeleport.RemoveAll(x => x.PlayerId == player.PlayerId); + Sunglasses.sunglasses.RemoveAll(x => x.PlayerId == player.PlayerId); + Torch.torch.RemoveAll(x => x.PlayerId == player.PlayerId); + Flash.flash.RemoveAll(x => x.PlayerId == player.PlayerId); + Multitasker.multitasker.RemoveAll(x => x.PlayerId == player.PlayerId); + Vip.vip.RemoveAll(x => x.PlayerId == player.PlayerId); + Invert.invert.RemoveAll(x => x.PlayerId == player.PlayerId); + Chameleon.chameleon.RemoveAll(x => x.PlayerId == player.PlayerId); + if (player == Lovers.lover1 || player == Lovers.lover2) Lovers.clearAndReload(); // The whole Lover couple is being erased + if (player == Specoality.specoality) Specoality.clearAndReload(); if (player == Tiebreaker.tiebreaker) Tiebreaker.clearAndReload(); if (player == Mini.mini) Mini.clearAndReload(); if (player == Aftermath.aftermath) Aftermath.clearAndReload(); @@ -2059,15 +1351,17 @@ public static void erasePlayerRoles(byte playerId, bool ignoreModifier = true) if (player == Tunneler.tunneler) Tunneler.clearAndReload(); if (player == Slueth.slueth) Slueth.clearAndReload(); if (player == Blind.blind) Blind.clearAndReload(); - if (player == Cursed.cursed) Cursed.clearAndReload(); - if (Vip.vip.Any(x => x.PlayerId == player.PlayerId)) Vip.vip.RemoveAll(x => x.PlayerId == player.PlayerId); - if (Invert.invert.Any(x => x.PlayerId == player.PlayerId)) - Invert.invert.RemoveAll(x => x.PlayerId == player.PlayerId); - if (Chameleon.chameleon.Any(x => x.PlayerId == player.PlayerId)) - Chameleon.chameleon.RemoveAll(x => x.PlayerId == player.PlayerId); } } + public static void clearGhostRoles(byte playerId) + { + var player = playerById(playerId); + + if (player == GhostEngineer.player) GhostEngineer.ClearAndReload(); + if (player == Specter.player) Specter.ClearAndReload(); + } + public static void infoSleuthTarget(byte playerId) { var player = playerById(playerId); @@ -2353,7 +1647,7 @@ public static void akujoSuicide(byte akujoId) } } - public static void Mine(int ventId, PlayerControl role, byte[] buff, float zAxis) + public static void Mine(int ventId, byte[] buff, float zAxis) { var position = Vector3.zero; position.x = BitConverter.ToSingle(buff, 0 * sizeof(float)); @@ -2446,8 +1740,8 @@ public static void setJackalSwoop(byte playerId, byte flag) target.setLook("", 6, "", "", "", ""); var color = Color.clear; - var canSee = Jackal.jackal == CachedPlayer.LocalPlayer.PlayerControl || - Sidekick.sidekick == CachedPlayer.LocalPlayer.PlayerControl || + var canSee = Jackal.jackal.Any(x => x == CachedPlayer.LocalPlayer.PlayerControl) || + Jackal.sidekick == CachedPlayer.LocalPlayer.PlayerControl || CachedPlayer.LocalPlayer.Data.IsDead; if (canSee) color.a = 0.1f; target.cosmetics.currentBodySprite.BodySprite.color = color; @@ -2469,7 +1763,7 @@ public static void placeTrap(byte[] buff) var pos = Vector3.zero; pos.x = BitConverter.ToSingle(buff, 0 * sizeof(float)); pos.y = BitConverter.ToSingle(buff, 1 * sizeof(float)) - 0.2f; - KillTrap trap = new(pos); + _ = new KillTrap(pos); } public static void clearTrap() @@ -2685,7 +1979,7 @@ public static void guesserShoot(byte killerId, byte dyingTargetId, byte guessedT { var roleInfo = RoleInfo.allRoleInfos.FirstOrDefault(x => (byte)x.roleId == guessedRoleId); if (Thief.thief.IsAlive() && Thief.tiefCanKill(dyingTarget, guesser)) - thiefStealsRole(dyingTarget.PlayerId); + Thief.StealsRole(dyingTarget.PlayerId); } if ((Akujo.akujo != null && dyingTarget == Akujo.akujo) || (Akujo.honmei != null && dyingTarget == Akujo.honmei)) @@ -2911,116 +2205,6 @@ public static void setChatNotificationOverlay(byte localPlayerId, byte targetPla } } - public static void thiefStealsRole(byte playerId) - { - var target = playerById(playerId); - var thief = Thief.thief; - if (target == null) return; - if (target == Sheriff.sheriff) Sheriff.sheriff = thief; - if (target == Deputy.deputy) Deputy.deputy = thief; - if (target == Veteran.veteran) Veteran.veteran = thief; - if (target == Jackal.jackal) - { - Jackal.jackal = thief; - Jackal.formerJackals.Add(target); - } - - if (target == Sidekick.sidekick) - { - Sidekick.sidekick = thief; - Jackal.formerJackals.Add(target); - if (HandleGuesser.isGuesserGm && CustomOptionHolder.guesserGamemodeSidekickIsAlwaysGuesser.GetBool() && !HandleGuesser.isGuesser(thief.PlayerId)) - setGuesserGm(thief.PlayerId); - } - if (target == Pavlovsdogs.pavlovsowner) - { - Pavlovsdogs.pavlovsdogs.Add(target); - Pavlovsdogs.pavlovsowner = thief; - if (HandleGuesser.isGuesserGm && CustomOptionHolder.guesserGamemodePavlovsdogIsAlwaysGuesser.GetBool() && !HandleGuesser.isGuesser(thief.PlayerId)) - setGuesserGm(thief.PlayerId); - } - if (Pavlovsdogs.pavlovsdogs.Any(x => x == target)) - { - Pavlovsdogs.pavlovsdogs.Add(thief); - } - if (target == Poucher.poucher && !Poucher.spawnModifier) Poucher.poucher = thief; - if (target == Butcher.butcher) Butcher.butcher = thief; - if (target == Morphling.morphling) Morphling.morphling = thief; - if (target == Camouflager.camouflager) Camouflager.camouflager = thief; - if (target == Vampire.vampire) Vampire.vampire = thief; - if (target == Eraser.eraser) Eraser.eraser = thief; - if (target == Trickster.trickster) Trickster.trickster = thief; - if (target == Gambler.gambler) Gambler.gambler = thief; - if (target == Cleaner.cleaner) Cleaner.cleaner = thief; - if (target == Warlock.warlock) Warlock.warlock = thief; - if (target == Grenadier.grenadier) Grenadier.grenadier = thief; - if (target == BountyHunter.bountyHunter) BountyHunter.bountyHunter = thief; - if (target == Witch.witch) - { - Witch.witch = thief; - if (MeetingHud.Instance) - if (Witch.witchVoteSavesTargets) // In a meeting, if the thief guesses the witch, all targets are saved or no target is saved. - Witch.futureSpelled = new List(); - else // If thief kills witch during the round, remove the thief from the list of spelled people, keep the rest - Witch.futureSpelled.RemoveAll(x => x.PlayerId == thief.PlayerId); - } - - if (target == Ninja.ninja) Ninja.ninja = thief; - if (target == Escapist.escapist) Escapist.escapist = thief; - if (target == Terrorist.terrorist) Terrorist.terrorist = thief; - if (target == Bomber.bomber) Bomber.bomber = thief; - if (target == Miner.miner) Miner.miner = thief; - if (target == Undertaker.undertaker) Undertaker.undertaker = thief; - if (target == Mimic.mimic) - { - Mimic.mimic = thief; - Mimic.hasMimic = false; - } - if (target == Yoyo.yoyo) - { - Yoyo.yoyo = thief; - Yoyo.markedLocation = null; - } - if (target.Data.Role.IsImpostor) - { - RoleManager.Instance.SetRole(Thief.thief, RoleTypes.Impostor); - FastDestroyableSingleton.Instance.KillButton.SetCoolDown(Thief.thief.killTimer, - GameOptionsManager.Instance.currentNormalGameOptions.KillCooldown); - } - - if (target == Werewolf.werewolf) - { - Survivor.survivor.Add(target); - Werewolf.werewolf = thief; - } - if (target == Arsonist.arsonist) - { - Survivor.survivor.Add(target); - Arsonist.arsonist = thief; - } - if (target == Juggernaut.juggernaut) - { - Survivor.survivor.Add(target); - Juggernaut.juggernaut = thief; - } - if (target == Swooper.swooper) - { - Survivor.survivor.Add(target); - Swooper.swooper = thief; - } - - if (target == Deputy.deputy) Deputy.deputy = thief; - if (target == Veteran.veteran) Veteran.veteran = thief; - if (target == Blackmailer.blackmailer) Blackmailer.blackmailer = thief; - if (target == EvilTrapper.evilTrapper) EvilTrapper.evilTrapper = thief; - - if (Lawyer.lawyer != null && target == Lawyer.target) - Lawyer.target = thief; - if (Thief.thief == PlayerControl.LocalPlayer) CustomButton.ResetAllCooldowns(); - Thief.clearAndReload(); - Thief.formerThief = thief; // After clearAndReload, else it would get reset... - } - public static void setTrap(byte[] buff) { if (Trapper.trapper == null) return; @@ -3110,13 +2294,6 @@ public static void defuseBomb() terroristButton.isEffectActive = false; terroristButton.actionButton.cooldownTimerText.color = Palette.EnabledColor; } - /* - public static void shareRoom(byte playerId, byte roomId) - { - if (Snitch.playerRoomMap.ContainsKey(playerId)) Snitch.playerRoomMap[playerId] = roomId; - else Snitch.playerRoomMap.Add(playerId, roomId); - } - */ } [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.HandleRpc))] @@ -3230,15 +2407,12 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) // Role functionality - case CustomRPC.EngineerFixLights: + case CustomRPC.FixLights: RPCProcedure.FixLights(); break; - case CustomRPC.EngineerFixSubmergedOxygen: + case CustomRPC.FixSubmergedOxygen: RPCProcedure.FixSubmergedOxygen(); break; - case CustomRPC.EngineerUsedRepair: - RPCProcedure.engineerUsedRepair(); - break; case CustomRPC.UseCameraTime: RPCProcedure.useCameraTime(reader.ReadSingle()); @@ -3256,10 +2430,6 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) RPCProcedure.dissectionBody(reader.ReadByte(), reader.ReadByte()); break; - case CustomRPC.RandomDeadBodyPosition: - RPCProcedure.RandomDeadBodyPosition(reader.ReadByte(), reader.ReadVector2()); - break; - case CustomRPC.BlackmailPlayer: RPCProcedure.blackmailPlayer(reader.ReadByte()); break; @@ -3285,7 +2455,11 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) break; case CustomRPC.AmnisiacTakeRole: - RPCProcedure.amnisiacTakeRole(reader.ReadByte()); + Amnisiac.TakeRole(reader.ReadByte(), reader.ReadByte()); + break; + + case CustomRPC.SpecterTakeRole: + Specter.TakeRole(reader.ReadByte()); break; case CustomRPC.ImpostorPromotesToLastImpostor: @@ -3293,7 +2467,7 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) break; case CustomRPC.MimicMimicRole: - RPCProcedure.mimicMimicRole(reader.ReadByte()); + Mimic.MimicRole(reader.ReadByte()); break; case CustomRPC.ShowIndomitableFlash: @@ -3381,7 +2555,7 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) break; case CustomRPC.SidekickPromotes: - RPCProcedure.sidekickPromotes(); + RPCProcedure.sidekickPromotes(reader.ReadByte()); break; case CustomRPC.ErasePlayerRoles: @@ -3390,6 +2564,10 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) Eraser.alreadyErased.Add(eraseTarget); break; + case CustomRPC.ClearGhostRoles: + RPCProcedure.clearGhostRoles(reader.ReadByte()); + break; + case CustomRPC.SetFutureErased: RPCProcedure.setFutureErased(reader.ReadByte()); break; @@ -3523,10 +2701,9 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) case CustomRPC.Mine: var newVentId = reader.ReadInt32(); - var role = playerById(reader.ReadByte()); var pos = reader.ReadBytesAndSize(); var zAxis = reader.ReadSingle(); - RPCProcedure.Mine(newVentId, role, pos, zAxis); + RPCProcedure.Mine(newVentId, pos, zAxis); break; case CustomRPC.TurnToImpostor: @@ -3535,7 +2712,7 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) case CustomRPC.ThiefStealsRole: var thiefTargetId = reader.ReadByte(); - RPCProcedure.thiefStealsRole(thiefTargetId); + Thief.StealsRole(thiefTargetId); break; case CustomRPC.SetTrap: @@ -3583,13 +2760,6 @@ private static bool Prefix([HarmonyArgument(0)] byte callId, [HarmonyArgument(1) case CustomRPC.ShareGhostInfo: RPCProcedure.receiveGhostInfo(reader.ReadByte(), reader); break; - /* - case CustomRPC.ShareRoom: - var roomPlayer = reader.ReadByte(); - var roomId = reader.ReadByte(); - RPCProcedure.shareRoom(roomPlayer, roomId); - break; - */ case CustomRPC.MayorMeeting: if (AmongUsClient.Instance.AmHost) { diff --git a/TheOtherRoles/Resources/BackButton.png b/TheOtherRoles/Resources/BackButton.png new file mode 100644 index 00000000..2be40b76 Binary files /dev/null and b/TheOtherRoles/Resources/BackButton.png differ diff --git a/TheOtherRoles/Resources/CreditsButton.png b/TheOtherRoles/Resources/CreditsButton.png deleted file mode 100644 index dd8f59f4..00000000 Binary files a/TheOtherRoles/Resources/CreditsButton.png and /dev/null differ diff --git a/TheOtherRoles/Resources/ExitButton.png b/TheOtherRoles/Resources/ExitButton.png new file mode 100644 index 00000000..9b7efbb2 Binary files /dev/null and b/TheOtherRoles/Resources/ExitButton.png differ diff --git a/TheOtherRoles/Resources/LoversChat.png b/TheOtherRoles/Resources/LoversChat.png deleted file mode 100644 index 26d093c5..00000000 Binary files a/TheOtherRoles/Resources/LoversChat.png and /dev/null differ diff --git a/TheOtherRoles/Resources/TabIconGhost.png b/TheOtherRoles/Resources/TabIconGhost.png new file mode 100644 index 00000000..ee3fb03a Binary files /dev/null and b/TheOtherRoles/Resources/TabIconGhost.png differ diff --git a/TheOtherRoles/Resources/stringData.json b/TheOtherRoles/Resources/stringData.json index 242b6413..3fafe9a5 100644 --- a/TheOtherRoles/Resources/stringData.json +++ b/TheOtherRoles/Resources/stringData.json @@ -124,6 +124,10 @@ "0": "Modifier Settings", "13": "闄勫姞鑳藉姏璁剧疆" }, + "ghostSettings": { + "0": "Ghost Roles Settings", + "13": "骞界伒鑱屼笟璁剧疆" + }, "guesserSettings": { "0": "Guesser Mode Settings", "13": "璧屾ā寮忚缃" @@ -1247,6 +1251,10 @@ "0": "Vulture Can Use Vents", "13": "鍙娇鐢ㄧ閬" }, + "SpecterOptions": { + "0": "Specter (Ghost)", + "13": "鎬ㄧ伒 (骞界伒鑱屼笟)" + }, "jesterCanCallEmergency": { "0": "Jester Can Call Emergency Meeting", "13": "灏忎笐鍙彫寮浼氳" @@ -1590,6 +1598,10 @@ "0": "Thief Can Guess To Steal A Role (If Guesser)", "13": "韬唤绐冭醇鍙氳繃鐚滄祴绐冨彇韬唤\n(璧屾ā寮)" }, + "GhostEngineerOptions": { + "0": "Engineer (Ghost)", + "13": "鐏甸瓊宸ョ▼甯 (骞界伒鑱屼笟)" + }, "guesserNumberOfShots": { "0": "Vigilante Number Of Shots", "13": "鍙寽娴嬫鏁" @@ -3000,6 +3012,10 @@ "0": "FLASH", "13": "闂厜寮" }, + "SpecterButton": { + "0": "Revive", + "13": "澶嶆椿" + }, "GuessserGMInfo": { "0": " (Guesser)", "13": " (璧屾)" @@ -3328,6 +3344,16 @@ "PursuerShortDesc": { "13": "娲讳笅鍘伙紒" }, + "Witness": { + "0": "Witness", + "13": "姹$偣璇佷汉" + }, + "WitnessIntroDesc": { + "13": "閭e氨鍙墿涓嬧︹︿簡" + }, + "WitnessShortDesc": { + "13": "璁╁ぇ瀹剁寽璋佹槸鐪熷嚩" + }, "PartTimer": { "0": "PartTimer", "13": "鎵撳伐浠" @@ -4000,6 +4026,16 @@ "GhostEngineerShortDesc": { "13": "寮ヨˉ鐢熷墠鏈兘淇ソ椋炶埞鐨勯仐鎲" }, + "Specter": { + "0": "Specter", + "13": "鎬ㄧ伒" + }, + "SpecterIntroDesc": { + "13": "鎴戣繕涓嶆兂姝" + }, + "SpecterShortDesc": { + "13": "鎴戣繕涓嶆兂姝" + }, "Hunter": { "0": "Hunter", "13": "鐚庝汉" @@ -4320,5 +4356,11 @@ }, "ShifterFullDesc": { "13": "鍒嗛厤闃佃惀锛氳埞鍛橈紙鍙缃负涓珛浜ゆ崲甯堬級\n\n鍙互鍦ㄨ鍔ㄩ樁娈典笌鍏朵粬鐜╁浜ゆ崲涓昏亴涓氾紝浜ゆ崲鏃跺悕瀛椾笅鏂逛細鏈夐粍瀛楁彁绀猴紝\n浼氳缁撴潫鍚庣敓鏁堛俓n濡傛灉浜ゆ崲鐩爣涓嶆槸鑸瑰憳锛堝彲浠ヨ缃級锛屼細璁粨鏉熸椂浼氳嚜鏉銆俓n锛堜氦鎹㈠畬姣曞悗姝ら檮鍔犺兘鍔涙秷澶憋級\n鎻愮ず锛氫氦鎹㈠笀鐨勭洰鐨勪富瑕佹槸涓轰簡缁ф壙鑸瑰憳寮哄姏鑱屼笟锛孿n渚嬪鍦ㄨ闀挎浜″墠灏嗚韩浠戒氦鎹€俓n\n娉細鐜╁姝讳骸涓嶄細褰卞搷浜ゆ崲鏁堟灉銆" + }, + "GhostEngineerFullDesc": { + "13": "鍒嗛厤闃佃惀锛氳埞鍛榎n\n鍦ㄦ鍚庡彲浠ヤ慨澶嶄竴娆$牬鍧" + }, + "SpecterFullDesc": { + "13": "鍒嗛厤闃佃惀锛氭棤闃佃惀涓珛锛堣焙鐙笺佸反鐢礇澶 浠ュ強 寰嬪笀 鏃犳硶鑾峰緱锛塡n\n姝讳骸鍚庡彲浠ラ噸鏂板鎵惧案浣撳娲伙紝浣嗕笉鑳芥槸鑷繁鐨勫案浣揬n娉細澶嶆椿鏃朵細娑堣椾竴鍏疯鐜╁鐨勫案浣撱" } } \ No newline at end of file diff --git a/TheOtherRoles/Roles/Crewmate/Deputy.cs b/TheOtherRoles/Roles/Crewmate/Deputy.cs index f12d9d0e..7a9b8da2 100644 --- a/TheOtherRoles/Roles/Crewmate/Deputy.cs +++ b/TheOtherRoles/Roles/Crewmate/Deputy.cs @@ -56,12 +56,12 @@ public static void clearAndReload(bool resetCuffs = true) { if (resetCuffs) { + handcuffedPlayers = []; + handcuffedKnows = []; + HudManagerStartPatch.setAllButtonsHandcuffedStatus(false, true); } deputy = null; currentTarget = null; - handcuffedPlayers = []; - handcuffedKnows = []; - HudManagerStartPatch.setAllButtonsHandcuffedStatus(false, true); promotesToSheriff = CustomOptionHolder.deputyGetsPromoted.GetSelection(); remainingHandcuffs = CustomOptionHolder.deputyNumberOfHandcuffs.GetFloat(); handcuffCooldown = CustomOptionHolder.deputyHandcuffCooldown.GetFloat(); diff --git a/TheOtherRoles/Roles/Crewmate/Medium.cs b/TheOtherRoles/Roles/Crewmate/Medium.cs index 22fb8516..b66ccf3f 100644 --- a/TheOtherRoles/Roles/Crewmate/Medium.cs +++ b/TheOtherRoles/Roles/Crewmate/Medium.cs @@ -61,8 +61,7 @@ public static string getInfo(PlayerControl target, PlayerControl killer) infos.Add(SpecialMediumInfo.ImpostorTeamkill); } - if (target == Sidekick.sidekick && - (killer == Jackal.jackal || Jackal.formerJackals.Any(x => x.PlayerId == killer.PlayerId))) + if (target == Jackal.sidekick && Jackal.jackal.Any(x => x.PlayerId == killer.PlayerId)) infos.Add(SpecialMediumInfo.JackalKillsSidekick); if (target == Lawyer.lawyer && killer == Lawyer.target) infos.Add(SpecialMediumInfo.LawyerKilledByClient); if (Medium.target.wasCleaned) infos.Add(SpecialMediumInfo.BodyCleaned); diff --git a/TheOtherRoles/Roles/Crewmate/Prophet.cs b/TheOtherRoles/Roles/Crewmate/Prophet.cs index e6e07d30..0349171a 100644 --- a/TheOtherRoles/Roles/Crewmate/Prophet.cs +++ b/TheOtherRoles/Roles/Crewmate/Prophet.cs @@ -34,7 +34,7 @@ public static bool IsRed(PlayerControl p) if (killCrewAsRed && (p == Sheriff.sheriff || p == Deputy.deputy || p == Veteran.veteran)) return true; - if (benignNeutralAsRed && isNeutral(p) && (p == Amnisiac.amnisiac || Pursuer.pursuer.Contains(p) || Survivor.survivor.Contains(p))) return true; + if (benignNeutralAsRed && isNeutral(p) && (Amnisiac.player.Contains(p) || Pursuer.pursuer.Contains(p) || Survivor.survivor.Contains(p))) return true; return evilNeutralAsRed && isEvilNeutral(p); } diff --git a/TheOtherRoles/Roles/Crewmate/Prosecutor.cs b/TheOtherRoles/Roles/Crewmate/Prosecutor.cs index 9bc4f2d3..98671c99 100644 --- a/TheOtherRoles/Roles/Crewmate/Prosecutor.cs +++ b/TheOtherRoles/Roles/Crewmate/Prosecutor.cs @@ -200,17 +200,16 @@ public class ExilePros { public static void ExileControllerPostfix(ExileController __instance) { - if (Prosecutor.prosecutor != null) + if (Prosecutor.prosecutor != null && Prosecutor.ProsecuteThisMeeting) { - if (Prosecutor.ProsecuteThisMeeting) + var exiled = __instance.exiled?.Object; + if (exiled != null && exiled == exiled.isCrew() && Prosecutor.diesOnIncorrectPros) { - var exiled = __instance.exiled?.Object; - if (exiled != null && exiled != (exiled.Data.Role.IsImpostor || isKillerNeutral(exiled) || isEvilNeutral(exiled)) && Prosecutor.diesOnIncorrectPros) - Prosecutor.prosecutor.Exiled(); - if (exiled == null) - Prosecutor.Prosecuted = false; - Prosecutor.ProsecuteThisMeeting = false; + Prosecutor.prosecutor.Exiled(); } + + if (exiled == null) Prosecutor.Prosecuted = false; + Prosecutor.ProsecuteThisMeeting = false; } } diff --git a/TheOtherRoles/Roles/Crewmate/Sheriff.cs b/TheOtherRoles/Roles/Crewmate/Sheriff.cs index f734ded7..8bd36933 100644 --- a/TheOtherRoles/Roles/Crewmate/Sheriff.cs +++ b/TheOtherRoles/Roles/Crewmate/Sheriff.cs @@ -40,8 +40,8 @@ public static bool sheriffCanKillNeutral(PlayerControl target) { return (target != Mini.mini || Mini.isGrownUp()) && (target.Data.Role.IsImpostor || - Jackal.jackal == target || - Sidekick.sidekick == target || + Jackal.jackal.Any(x => x == target) || + Jackal.sidekick == target || Juggernaut.juggernaut == target || Werewolf.werewolf == target || Swooper.swooper == target || @@ -50,15 +50,15 @@ public static bool sheriffCanKillNeutral(PlayerControl target) (spyCanDieToSheriff && Spy.spy == target) || (canKillNeutrals && (Akujo.akujo == target || isKillerNeutral(target) || - (Survivor.survivor.Contains(target) && canKillSurvivor) || + (Survivor.survivor.Any(p => p == target) && canKillSurvivor) || (Jester.jester == target && canKillJester) || (Vulture.vulture == target && canKillVulture) || (Thief.thief == target && canKillThief) || - (Amnisiac.amnisiac == target && canKillAmnesiac) || + (Amnisiac.player.Any(p => p == target) && canKillAmnesiac) || (PartTimer.partTimer == target && canKillPartTimer) || (Lawyer.lawyer == target && canKillLawyer) || (Executioner.executioner == target && canKillExecutioner) || - (Pursuer.pursuer.Contains(target) && canKillPursuer) || + (Pursuer.pursuer.Any(p => p == target) && canKillPursuer) || (Doomsayer.doomsayer == target && canKillDoomsayer)))); } diff --git a/TheOtherRoles/Roles/Ghost/Specter.cs b/TheOtherRoles/Roles/Ghost/Specter.cs new file mode 100644 index 00000000..2ca65698 --- /dev/null +++ b/TheOtherRoles/Roles/Ghost/Specter.cs @@ -0,0 +1,325 @@ +锘縰sing System.Linq; +using UnityEngine; + +namespace TheOtherRoles.Roles.Ghost; + +public class Specter +{ + public static PlayerControl player; + public static Color color = new Color32(154, 147, 80, byte.MaxValue); + public static bool remember; + + public static bool resetRole; + + public static void ClearAndReload() + { + player = null; + remember = false; + resetRole = CustomOptionHolder.specterResetRole.GetBool(); + } + + public static void TakeRole(byte targetId) + { + var target = playerById(targetId); + if (player == null || target == null) return; + var local = player; + RPCProcedure.erasePlayerRoles(local.PlayerId); + var targetInfo = RoleInfo.getRoleInfoForPlayer(target); + var roleInfo = targetInfo.FirstOrDefault(info => info.roleType is not RoleType.Modifier and not RoleType.GhostRole); + if (target.isImpostor()) turnToImpostor(player); + + DeadBody[] array = Object.FindObjectsOfType(); + for (var i = 0; i < array.Length; i++) + { + if (GameData.Instance.GetPlayerById(array[i].ParentId).PlayerId == targetId) + { + Object.Destroy(array[i].gameObject); + break; + } + } + + if (roleInfo != null) switch (roleInfo.roleId) + { + case RoleId.Amnisiac: + Amnisiac.player.Add(local); + break; + case RoleId.Impostor: + break; + case RoleId.Morphling: + if (resetRole) Morphling.clearAndReload(); + Morphling.morphling = local; + break; + case RoleId.Bomber: + if (resetRole) Bomber.clearAndReload(); + Bomber.bomber = local; + break; + case RoleId.Poucher: + if (resetRole) Poucher.clearAndReload(); + Poucher.poucher = local; + break; + case RoleId.Butcher: + if (resetRole) Butcher.clearAndReload(); + Butcher.butcher = local; + break; + case RoleId.Mimic: + if (resetRole) Mimic.clearAndReload(); + Mimic.mimic = local; + break; + case RoleId.Camouflager: + if (resetRole) Camouflager.clearAndReload(); + Camouflager.camouflager = local; + break; + case RoleId.Miner: + if (resetRole) Miner.clearAndReload(); + Miner.miner = local; + break; + case RoleId.Eraser: + if (resetRole) Eraser.clearAndReload(); + Eraser.eraser = local; + break; + case RoleId.Vampire: + if (resetRole) Vampire.clearAndReload(); + Vampire.vampire = local; + break; + case RoleId.Undertaker: + if (resetRole) Undertaker.clearAndReload(); + Undertaker.undertaker = local; + break; + case RoleId.Escapist: + if (resetRole) Escapist.clearAndReload(); + Escapist.escapist = local; + break; + case RoleId.Warlock: + if (resetRole) Warlock.clearAndReload(); + Warlock.warlock = local; + break; + case RoleId.Trickster: + if (resetRole) Trickster.clearAndReload(); + Trickster.trickster = local; + break; + case RoleId.BountyHunter: + if (resetRole) BountyHunter.clearAndReload(); + BountyHunter.bountyHunter = local; + break; + case RoleId.Cleaner: + if (resetRole) Cleaner.clearAndReload(); + Cleaner.cleaner = local; + break; + case RoleId.Terrorist: + if (resetRole) Terrorist.clearAndReload(); + Terrorist.terrorist = local; + break; + case RoleId.Blackmailer: + if (resetRole) Blackmailer.clearAndReload(); + Blackmailer.blackmailer = local; + break; + case RoleId.Witch: + if (resetRole) Witch.clearAndReload(); + Witch.witch = local; + break; + case RoleId.Ninja: + if (resetRole) Ninja.clearAndReload(); + Ninja.ninja = local; + break; + case RoleId.Yoyo: + if (resetRole) Yoyo.clearAndReload(); + Yoyo.yoyo = local; + break; + case RoleId.EvilTrapper: + if (resetRole) EvilTrapper.clearAndReload(); + EvilTrapper.evilTrapper = local; + break; + case RoleId.Gambler: + if (resetRole) Gambler.clearAndReload(); + Gambler.gambler = local; + break; + case RoleId.Grenadier: + if (resetRole) Grenadier.clearAndReload(); + Grenadier.grenadier = local; + break; + case RoleId.Survivor: + Survivor.survivor.RemoveAll(x => x == target); + Survivor.survivor.Add(local); + Amnisiac.player.Add(target); + break; + case RoleId.Jester: + Jester.jester = local; + Amnisiac.player.Add(target); + break; + case RoleId.Vulture: + Vulture.vulture = local; + Amnisiac.player.Add(target); + break; + case RoleId.Lawyer: + Lawyer.lawyer = local; + Amnisiac.player.Add(target); + break; + case RoleId.Executioner: + Executioner.executioner = local; + Amnisiac.player.Add(target); + break; + case RoleId.Pursuer: + Pursuer.pursuer.RemoveAll(x => x == target); + Pursuer.pursuer.Add(local); + Amnisiac.player.Add(target); + break; + case RoleId.PartTimer: + PartTimer.partTimer = local; + Amnisiac.player.Add(target); + break; + case RoleId.Witness: + Witness.player = local; + Amnisiac.player.Add(target); + break; + case RoleId.Doomsayer: + Doomsayer.doomsayer = local; + Amnisiac.player.Add(target); + break; + case RoleId.Arsonist: + Arsonist.arsonist = local; + Amnisiac.player.Add(target); + break; + case RoleId.Jackal: + Jackal.jackal.Add(local); + break; + case RoleId.Sidekick: + Jackal.sidekick = local; + Jackal.jackal.Add(target); + break; + case RoleId.Pavlovsowner: + Pavlovsdogs.pavlovsowner = local; + Pavlovsdogs.pavlovsdogs.Add(target); + break; + case RoleId.Pavlovsdogs: + Pavlovsdogs.pavlovsdogs.Add(local); + break; + case RoleId.Werewolf: + Werewolf.werewolf = local; + Amnisiac.player.Add(target); + break; + case RoleId.Swooper: + Swooper.swooper = local; + Amnisiac.player.Add(target); + break; + case RoleId.Juggernaut: + Juggernaut.juggernaut = local; + Amnisiac.player.Add(target); + break; + case RoleId.Akujo: + Akujo.akujo = local; + Amnisiac.player.Add(target); + break; + case RoleId.Thief: + Thief.thief = local; + Amnisiac.player.Add(target); + break; + case RoleId.Crewmate: + break; + case RoleId.Vigilante: + if (resetRole) Vigilante.clearAndReload(); + Vigilante.vigilante = local; + break; + case RoleId.Mayor: + if (resetRole) Mayor.clearAndReload(); + Mayor.mayor = local; + break; + case RoleId.Prosecutor: + if (resetRole) Prosecutor.clearAndReload(); + Prosecutor.prosecutor = local; + break; + case RoleId.Portalmaker: + if (resetRole) Portalmaker.clearAndReload(); + Portalmaker.portalmaker = local; + break; + case RoleId.Engineer: + if (resetRole) Engineer.clearAndReload(); + Engineer.engineer = local; + break; + case RoleId.Sheriff: + if (Sheriff.formerDeputy != null && Sheriff.formerDeputy == Sheriff.sheriff) + { + Sheriff.formerDeputy = null; + Deputy.deputy = local; + } + else Sheriff.sheriff = local; + break; + case RoleId.Deputy: + if (Amnisiac.resetRole) Deputy.clearAndReload(false); + Deputy.deputy = local; + break; + case RoleId.BodyGuard: + if (Amnisiac.resetRole) BodyGuard.clearAndReload(); + BodyGuard.bodyguard = local; + break; + case RoleId.Jumper: + if (Amnisiac.resetRole) Jumper.clearAndReload(); + Jumper.jumper = local; + break; + case RoleId.Detective: + if (Amnisiac.resetRole) Detective.clearAndReload(); + Detective.detective = local; + break; + case RoleId.TimeMaster: + if (Amnisiac.resetRole) TimeMaster.clearAndReload(); + TimeMaster.timeMaster = local; + break; + case RoleId.Veteran: + if (Amnisiac.resetRole) Veteran.clearAndReload(); + Veteran.veteran = local; + break; + case RoleId.Medic: + if (Amnisiac.resetRole) Medic.clearAndReload(); + Medic.medic = local; + break; + case RoleId.Swapper: + if (Amnisiac.resetRole) Swapper.clearAndReload(); + Swapper.swapper = local; + break; + case RoleId.Seer: + if (Amnisiac.resetRole) Seer.clearAndReload(); + Seer.seer = local; + break; + case RoleId.Hacker: + if (Amnisiac.resetRole) Hacker.clearAndReload(); + Hacker.hacker = local; + break; + case RoleId.Tracker: + if (Amnisiac.resetRole) Tracker.clearAndReload(); + Tracker.tracker = local; + break; + case RoleId.Snitch: + if (Amnisiac.resetRole) Snitch.clearAndReload(); + Snitch.snitch = local; + break; + case RoleId.Prophet: + if (Amnisiac.resetRole) Prophet.clearAndReload(); + Prophet.prophet = local; + break; + case RoleId.InfoSleuth: + if (resetRole) InfoSleuth.clearAndReload(); + break; + case RoleId.Spy: + if (Amnisiac.resetRole) Spy.clearAndReload(); + Spy.spy = local; + break; + case RoleId.SecurityGuard: + if (Amnisiac.resetRole) SecurityGuard.clearAndReload(); + SecurityGuard.securityGuard = local; + break; + case RoleId.Medium: + if (Amnisiac.resetRole) Medium.clearAndReload(); + Medium.medium = local; + break; + case RoleId.Trapper: + if (Amnisiac.resetRole) Trapper.clearAndReload(); + Trapper.trapper = local; + break; + case RoleId.Balancer: + if (Amnisiac.resetRole) Balancer.clearAndReload(); + Balancer.balancer = local; + break; + } + if (local != null) RPCProcedure.clearGhostRoles(local.PlayerId); + local.Revive(); + } +} \ No newline at end of file diff --git a/TheOtherRoles/Roles/Guesser.cs b/TheOtherRoles/Roles/Guesser.cs index 24f299a7..de251fe0 100644 --- a/TheOtherRoles/Roles/Guesser.cs +++ b/TheOtherRoles/Roles/Guesser.cs @@ -240,13 +240,16 @@ static void CreatePage(bool IsNext, MeetingHud __instance, Transform container) if (CachedPlayer.LocalId == Doomsayer.doomsayer?.PlayerId) { - if (!Doomsayer.canGuessImpostor && roleInfo.roleTeam == RoleType.Impostor) + if (!Doomsayer.canGuessImpostor && roleInfo.roleType == RoleType.Impostor) continue; - if (!Doomsayer.canGuessNeutral && roleInfo.roleTeam == RoleType.Neutral) + if (!Doomsayer.canGuessNeutral && roleInfo.roleType == RoleType.Neutral) continue; } - if (roleInfo.roleTeam == RoleType.Modifier && ModOption.allowModGuess && !roleInfo.isGuessable) + if (roleInfo.roleType == RoleType.Modifier && ModOption.allowModGuess && !roleInfo.isGuessable) + continue; + + if (roleInfo.roleType == RoleType.GhostRole) continue; // remove all roles that cannot spawn due to the settings from the ui. @@ -285,7 +288,8 @@ static void CreatePage(bool IsNext, MeetingHud __instance, Transform container) void CreateRole(RoleInfo roleInfo = null) { - RoleType team = roleInfo?.roleTeam ?? RoleType.Crewmate; + if (roleInfo.roleType is RoleType.GhostRole or RoleType.Special) return; + RoleType team = roleInfo?.roleType ?? RoleType.Crewmate; //Color color = roleInfo?.color ?? Color.white; //RoleId role = roleInfo?.roleId ?? RoleId.Crewmate; diff --git a/TheOtherRoles/Roles/Impostor/Camouflager.cs b/TheOtherRoles/Roles/Impostor/Camouflager.cs index 7b267bda..5371208e 100644 --- a/TheOtherRoles/Roles/Impostor/Camouflager.cs +++ b/TheOtherRoles/Roles/Impostor/Camouflager.cs @@ -23,7 +23,7 @@ public static void resetCamouflage() { if ((p == Ninja.ninja && Ninja.isInvisble) || (p == Swooper.swooper && Swooper.isInvisable) - || (p == Jackal.jackal && Jackal.isInvisable)) + || (Jackal.jackal.Any(x => x == p) && Jackal.isInvisable)) continue; p.setDefaultLook(); camoComms = false; diff --git a/TheOtherRoles/Roles/Impostor/Mimic.cs b/TheOtherRoles/Roles/Impostor/Mimic.cs index 458ca16a..4b8c3581 100644 --- a/TheOtherRoles/Roles/Impostor/Mimic.cs +++ b/TheOtherRoles/Roles/Impostor/Mimic.cs @@ -1,5 +1,8 @@ 锘縰sing System.Collections.Generic; +using System.Linq; +using TheOtherRoles.Buttons; using UnityEngine; +using static TheOtherRoles.Buttons.HudManagerStartPatch; namespace TheOtherRoles.Roles.Impostor; @@ -16,4 +19,161 @@ public static void clearAndReload(bool clearList = true) mimic = null; if (clearList) hasMimic = false; } + + + public static void MimicRole(byte targetId) + { + var target = playerById(targetId); + if (target == null || mimic == null) return; + var targetInfo = RoleInfo.getRoleInfoForPlayer(target); + var roleInfo = targetInfo.FirstOrDefault(info => info.roleType != RoleType.Modifier); + switch (roleInfo!.roleId) + { + case RoleId.BodyGuard: + if (Amnisiac.resetRole) BodyGuard.clearAndReload(); + BodyGuard.bodyguard = mimic; + bodyGuardGuardButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Mayor: + if (Amnisiac.resetRole) Mayor.clearAndReload(); + Mayor.mayor = mimic; + mayorMeetingButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + + hasMimic = true; + break; + + case RoleId.Prosecutor: + if (Amnisiac.resetRole) Prosecutor.clearAndReload(); + Prosecutor.prosecutor = mimic; + Prosecutor.diesOnIncorrectPros = false; + hasMimic = true; + break; + + case RoleId.InfoSleuth: + InfoSleuth.infoSleuth = mimic; + hasMimic = true; + break; + + case RoleId.Trapper: + if (Amnisiac.resetRole) Trapper.clearAndReload(); + Trapper.trapper = mimic; + trapperButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Portalmaker: + if (Amnisiac.resetRole) Portalmaker.clearAndReload(); + Portalmaker.portalmaker = mimic; + portalmakerPlacePortalButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Engineer: + if (Amnisiac.resetRole) Engineer.clearAndReload(); + Engineer.engineer = mimic; + engineerRepairButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Jumper: + if (Amnisiac.resetRole) Jumper.clearAndReload(); + Jumper.jumper = mimic; + jumperMarkButton.PositionOffset = CustomButton.ButtonPositions.lowerRowCenter; + jumperJumpButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Detective: + if (Amnisiac.resetRole) Detective.clearAndReload(); + Detective.detective = mimic; + hasMimic = true; + break; + /* + case RoleId.NiceGuesser: + if (Amnisiac.resetRole) //Guesser.clearAndReload(); + Guesser.niceGuesser = Mimic.mimic; + Mimic.hasMimic = true; + break; + */ + case RoleId.TimeMaster: + if (Amnisiac.resetRole) TimeMaster.clearAndReload(); + TimeMaster.timeMaster = mimic; + timeMasterShieldButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Veteran: + if (Amnisiac.resetRole) Veteran.clearAndReload(); + Veteran.veteran = mimic; + veteranAlertButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Medic: + if (Amnisiac.resetRole) Medic.clearAndReload(); + Medic.medic = mimic; + medicShieldButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Swapper: + if (Amnisiac.resetRole) Swapper.clearAndReload(); + Swapper.swapper = mimic; + hasMimic = true; + break; + + case RoleId.Seer: + if (Amnisiac.resetRole) Seer.clearAndReload(); + Seer.seer = mimic; + hasMimic = true; + break; + + case RoleId.Hacker: + if (Amnisiac.resetRole) Hacker.clearAndReload(); + Hacker.hacker = mimic; + hackerAdminTableButton.PositionOffset = CustomButton.ButtonPositions.upperRowFarLeft; + hackerVitalsButton.PositionOffset = CustomButton.ButtonPositions.lowerRowFarLeft; + hackerButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Tracker: + if (Amnisiac.resetRole) Tracker.clearAndReload(); + Tracker.tracker = mimic; + trackerTrackPlayerButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.SecurityGuard: + if (Amnisiac.resetRole) SecurityGuard.clearAndReload(); + SecurityGuard.securityGuard = mimic; + securityGuardButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + securityGuardCamButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Medium: + if (Amnisiac.resetRole) Medium.clearAndReload(); + Medium.medium = mimic; + mediumButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + + case RoleId.Balancer: + if (Amnisiac.resetRole) Balancer.clearAndReload(); + Balancer.balancer = mimic; + hasMimic = true; + break; + + case RoleId.Prophet: + if (Amnisiac.resetRole) Prophet.clearAndReload(); + Prophet.prophet = mimic; + prophetButton.PositionOffset = CustomButton.ButtonPositions.upperRowLeft; + hasMimic = true; + break; + } + } + } diff --git a/TheOtherRoles/Roles/Modifier/Aftermath.cs b/TheOtherRoles/Roles/Modifier/Aftermath.cs index 6550c645..dd20d1dc 100644 --- a/TheOtherRoles/Roles/Modifier/Aftermath.cs +++ b/TheOtherRoles/Roles/Modifier/Aftermath.cs @@ -153,7 +153,7 @@ public static void aftermathTrigger(byte playerId, byte killerId) writer.WriteBytesAndSize(buff); writer.Write(0.01f); AmongUsClient.Instance.FinishRpcImmediately(writer); - Mine(id, Miner.miner, buff, 0.01f); + Mine(id, buff, 0.01f); minerMineButton.Timer = minerMineButton.MaxTimer; } else if (Escapist.escapist == killer) @@ -368,14 +368,14 @@ public static void aftermathTrigger(byte playerId, byte killerId) setSwoop(Swooper.swooper.PlayerId, byte.MinValue); swooperSwoopButton.Timer = swooperSwoopButton.MaxTimer + Swooper.duration; } - else if (Jackal.jackal == killer && Jackal.canSwoop) + else if (Jackal.jackal.Any(x => x == killer) && Jackal.canSwoop) { var invisibleWriter = AmongUsClient.Instance.StartRpcImmediately(killer.NetId, (byte)CustomRPC.SetJackalSwoop, SendOption.Reliable); invisibleWriter.Write(killer.PlayerId); invisibleWriter.Write(byte.MinValue); AmongUsClient.Instance.FinishRpcImmediately(invisibleWriter); - setJackalSwoop(Jackal.jackal.PlayerId, byte.MinValue); + setJackalSwoop(killer.PlayerId, byte.MinValue); jackalSwoopButton.Timer = jackalSwoopButton.MaxTimer + Jackal.duration; } } diff --git a/TheOtherRoles/Roles/Modifier/Chameleon.cs b/TheOtherRoles/Roles/Modifier/Chameleon.cs index a5905920..54ebbcc2 100644 --- a/TheOtherRoles/Roles/Modifier/Chameleon.cs +++ b/TheOtherRoles/Roles/Modifier/Chameleon.cs @@ -47,7 +47,7 @@ public static void update() { if ((chameleonPlayer == Ninja.ninja && Ninja.isInvisble) || (chameleonPlayer == Swooper.swooper && Swooper.isInvisable) || - (chameleonPlayer == Jackal.jackal && Jackal.isInvisable)) continue; // Dont make Ninja visible... + (Jackal.jackal.Any(x => x == chameleonPlayer) && Jackal.isInvisable)) continue; // Dont make Ninja visible... // check movement by animation var playerPhysics = chameleonPlayer.MyPhysics; var currentPhysicsAnim = playerPhysics.Animations.Animator.GetCurrentAnimation(); diff --git a/TheOtherRoles/Roles/Modifier/Lovers.cs b/TheOtherRoles/Roles/Modifier/Lovers.cs index 0876b6dd..0bc8cbe8 100644 --- a/TheOtherRoles/Roles/Modifier/Lovers.cs +++ b/TheOtherRoles/Roles/Modifier/Lovers.cs @@ -33,7 +33,7 @@ public static bool existingAndAlive() public static PlayerControl otherLover(PlayerControl oneLover) { - if (!existingAndAlive()) return null; + if (!existing()) return null; if (oneLover == lover1) return lover2; if (oneLover == lover2) return lover1; return null; diff --git a/TheOtherRoles/Roles/Modifier/Shifter.cs b/TheOtherRoles/Roles/Modifier/Shifter.cs index 0c37d246..b427333b 100644 --- a/TheOtherRoles/Roles/Modifier/Shifter.cs +++ b/TheOtherRoles/Roles/Modifier/Shifter.cs @@ -18,9 +18,9 @@ public static bool isShiftNeutral(PlayerControl player) if (shiftNeutral && shiftALLNeutra) { return player != null && ( - player == Jackal.jackal || - player == Sidekick.sidekick || + player == Jackal.sidekick || player == Pavlovsdogs.pavlovsowner || + Jackal.jackal.Any(x => x == player) || Pavlovsdogs.pavlovsdogs.Any(x => x == player) || player == Akujo.akujo || player == Lawyer.lawyer); @@ -28,15 +28,15 @@ public static bool isShiftNeutral(PlayerControl player) else if (shiftNeutral) { return player != null && ( - player == Jackal.jackal || - player == Sidekick.sidekick || + player == Jackal.sidekick || player == Werewolf.werewolf || player == Lawyer.lawyer || player == Juggernaut.juggernaut || player == Akujo.akujo || + player == Swooper.swooper || player == Pavlovsdogs.pavlovsowner || - Pavlovsdogs.pavlovsdogs.Any(x => x == player) || - player == Swooper.swooper); + Jackal.jackal.Any(x => x == player) || + Pavlovsdogs.pavlovsdogs.Any(x => x == player)); } return player != null && isNeutral(player); } @@ -172,10 +172,11 @@ public static void shiftRole(PlayerControl player1, PlayerControl player2, bool if (repeat) shiftRole(player2, player1, false); Prosecutor.prosecutor = player1; } - else if (Amnisiac.amnisiac != null && Amnisiac.amnisiac == player2) + else if (Amnisiac.player != null && Amnisiac.player.Any(x => x.PlayerId == player2.PlayerId)) { if (repeat) shiftRole(player2, player1, false); - Amnisiac.amnisiac = player1; + Amnisiac.player.RemoveAll(p => p.PlayerId == player2.PlayerId); + Amnisiac.player.Add(player1); } else if (Jester.jester != null && Jester.jester == player2) { @@ -254,6 +255,11 @@ public static void shiftRole(PlayerControl player1, PlayerControl player2, bool if (repeat) shiftRole(player2, player1, false); Balancer.balancer = player1; } + else if (Witness.player != null && Witness.player == player2) + { + if (repeat) shiftRole(player2, player1, false); + Witness.player = player1; + } } public static void clearAndReload() diff --git a/TheOtherRoles/Roles/Neutral/Amnisiac.cs b/TheOtherRoles/Roles/Neutral/Amnisiac.cs index c8d646a8..55b3a730 100644 --- a/TheOtherRoles/Roles/Neutral/Amnisiac.cs +++ b/TheOtherRoles/Roles/Neutral/Amnisiac.cs @@ -1,13 +1,16 @@ 锘縰sing System.Collections.Generic; using TheOtherRoles.Objects; +using TheOtherRoles.Utilities; +using TMPro; using UnityEngine; using Object = UnityEngine.Object; +using static TheOtherRoles.Options.ModOption; namespace TheOtherRoles.Roles.Neutral; public class Amnisiac { - public static PlayerControl amnisiac; + public static List player = new(); public static List localArrows = new(); public static Color color = new(0.5f, 0.7f, 1f, 1f); public static List poolIcons = new(); @@ -19,7 +22,7 @@ public class Amnisiac public static void clearAndReload() { - amnisiac = null; + player.Clear(); showArrows = CustomOptionHolder.amnisiacShowArrows.GetBool(); resetRole = CustomOptionHolder.amnisiacResetRole.GetBool(); if (localArrows != null) @@ -28,4 +31,477 @@ public static void clearAndReload() Object.Destroy(arrow.arrow); localArrows.Clear(); } + + public static void TakeRole(byte targetId, byte playerId) + { + var target = playerById(targetId); + var local = playerById(playerId); + if (target == null || local == null) return; + var targetInfo = RoleInfo.getRoleInfoForPlayer(target, false, false); + var roleInfo = targetInfo.FirstOrDefault(); + if (target.isImpostor()) turnToImpostor(local); + switch (roleInfo!.roleId) + { + case RoleId.Crewmate: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + break; + case RoleId.Impostor: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + break; + case RoleId.Jester: + if (resetRole) Jester.clearAndReload(); + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Jester.jester = local; + player.Add(target); + break; + case RoleId.Juggernaut: + if (resetRole) Juggernaut.clearAndReload(); + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Juggernaut.juggernaut = local; + player.Add(target); + break; + case RoleId.Doomsayer: + if (resetRole) Doomsayer.clearAndReload(); + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Doomsayer.doomsayer = local; + player.Add(target); + break; + case RoleId.Swooper: + if (resetRole) Swooper.clearAndReload(); + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Swooper.swooper = local; + player.Add(target); + break; + case RoleId.Vulture: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Vulture.clearAndReload(); + Vulture.vulture = local; + player.Add(target); + break; + + case RoleId.Executioner: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Executioner.executioner = local; + player.Add(target); + break; + + case RoleId.Lawyer: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Lawyer.lawyer = local; + player.Add(target); + break; + + case RoleId.Akujo: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Akujo.akujo = local; + player.Add(target); + break; + + case RoleId.Pursuer: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Pursuer.clearAndReload(); + Pursuer.pursuer.Add(local); + player.Add(target); + break; + + case RoleId.Jackal: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Jackal.jackal.Add(local); + break; + + case RoleId.Sidekick: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Jackal.jackal.Add(target); + Jackal.sidekick = local; + break; + + case RoleId.Survivor: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Survivor.survivor.Add(local); + break; + + case RoleId.Thief: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Thief.clearAndReload(); + Thief.thief = local; + player.Add(target); + break; + + case RoleId.Pavlovsowner: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Pavlovsdogs.pavlovsdogs.Add(Pavlovsdogs.pavlovsowner); + Pavlovsdogs.pavlovsowner = local; + break; + + case RoleId.Pavlovsdogs: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Pavlovsdogs.pavlovsdogs.Add(local); + break; + + case RoleId.Werewolf: + if (resetRole) Werewolf.clearAndReload(); + Werewolf.werewolf = local; + player.RemoveAll(x => x.PlayerId == local.PlayerId); + player.Add(target); + break; + + + case RoleId.Witness: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Witness.ClearAndReload(); + Witness.player = local; + player.Add(target); + break; + + case RoleId.Arsonist: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Arsonist.clearAndReload(); + Arsonist.arsonist = local; + player.Add(target); + + if (CachedPlayer.LocalPlayer.PlayerControl == Arsonist.arsonist) + { + var playerCounter = 0; + var bottomLeft = new Vector3( + -FastDestroyableSingleton.Instance.UseButton.transform.localPosition.x, + FastDestroyableSingleton.Instance.UseButton.transform.localPosition.y, + FastDestroyableSingleton.Instance.UseButton.transform.localPosition.z); + foreach (PlayerControl p in CachedPlayer.AllPlayers) + if (playerIcons.ContainsKey(p.PlayerId) && p != Arsonist.arsonist) + { + //Arsonist.poolIcons.Add(p); + if (Arsonist.dousedPlayers.Contains(p)) + playerIcons[p.PlayerId].setSemiTransparent(false); + else + playerIcons[p.PlayerId].setSemiTransparent(true); + + playerIcons[p.PlayerId].transform.localPosition = bottomLeft + + new Vector3(-0.25f, -0.25f, 0) + + (Vector3.right * playerCounter++ * 0.35f); + playerIcons[p.PlayerId].transform.localScale = Vector3.one * 0.2f; + playerIcons[p.PlayerId].gameObject.SetActive(true); + } + } + break; + + case RoleId.Mimic: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Mimic.clearAndReload(false); + Mimic.mimic = local; + break; + + case RoleId.BodyGuard: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) BodyGuard.clearAndReload(); + BodyGuard.bodyguard = local; + break; + + case RoleId.Prosecutor: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Prosecutor.clearAndReload(); + Prosecutor.prosecutor = target; + break; + + case RoleId.Mayor: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Mayor.clearAndReload(); + Mayor.mayor = local; + break; + + case RoleId.Portalmaker: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Portalmaker.clearAndReload(); + Portalmaker.portalmaker = local; + break; + + case RoleId.Engineer: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Engineer.clearAndReload(); + Engineer.engineer = local; + break; + + case RoleId.Sheriff: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (Sheriff.formerDeputy != null && Sheriff.formerDeputy == Sheriff.sheriff) + { + Sheriff.formerDeputy = null; + Deputy.deputy = local; + } + else Sheriff.sheriff = local; + break; + + case RoleId.Deputy: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Deputy.clearAndReload(false); + Deputy.deputy = local; + break; + + case RoleId.Butcher: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Butcher.butcher = local; + break; + + case RoleId.Detective: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Detective.clearAndReload(); + Detective.detective = local; + break; + + case RoleId.Balancer: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Balancer.clearAndReload(); + Balancer.balancer = local; + break; + + case RoleId.InfoSleuth: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + InfoSleuth.infoSleuth = local; + break; + + case RoleId.TimeMaster: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) TimeMaster.clearAndReload(); + TimeMaster.timeMaster = local; + break; + + case RoleId.Veteran: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Veteran.clearAndReload(); + Veteran.veteran = local; + break; + + case RoleId.Medic: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Medic.clearAndReload(); + Medic.medic = local; + break; + + case RoleId.Swapper: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Swapper.clearAndReload(); + Swapper.swapper = local; + break; + + case RoleId.PartTimer: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) PartTimer.clearAndReload(); + PartTimer.partTimer = local; + break; + + case RoleId.Seer: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Seer.clearAndReload(); + Seer.seer = local; + break; + + case RoleId.Morphling: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Morphling.clearAndReload(); + Morphling.morphling = local; + break; + case RoleId.Bomber: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Bomber.clearAndReload(); + Bomber.bomber = local; + break; + + case RoleId.Yoyo: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Yoyo.clearAndReload(); + Yoyo.yoyo = local; + break; + + case RoleId.Terrorist: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Terrorist.clearAndReload(); + Terrorist.terrorist = local; + break; + + case RoleId.Camouflager: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Camouflager.clearAndReload(); + Camouflager.camouflager = local; + break; + + case RoleId.Hacker: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Hacker.clearAndReload(); + Hacker.hacker = local; + break; + + case RoleId.Tracker: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Tracker.clearAndReload(); + Tracker.tracker = local; + break; + + case RoleId.Vampire: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Vampire.clearAndReload(); + Vampire.vampire = local; + break; + + case RoleId.Snitch: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Snitch.clearAndReload(); + Snitch.snitch = local; + break; + + case RoleId.Eraser: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Eraser.clearAndReload(); + Eraser.eraser = local; + break; + + case RoleId.Spy: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Spy.clearAndReload(); + Spy.spy = local; + break; + + case RoleId.Trickster: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Trickster.clearAndReload(); + Trickster.trickster = local; + break; + + case RoleId.Cleaner: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Cleaner.clearAndReload(); + Cleaner.cleaner = local; + break; + + case RoleId.Warlock: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Warlock.clearAndReload(); + Warlock.warlock = local; + break; + + case RoleId.Grenadier: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Grenadier.clearAndReload(); + Grenadier.grenadier = local; + break; + + case RoleId.SecurityGuard: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) SecurityGuard.clearAndReload(); + SecurityGuard.securityGuard = local; + break; + + case RoleId.Assassin: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Assassin.assassin.Add(local); + break; + + case RoleId.Vigilante: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Vigilante.clearAndReload(); + Vigilante.vigilante = local; + break; + + case RoleId.BountyHunter: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) BountyHunter.clearAndReload(); + BountyHunter.bountyHunter = local; + + BountyHunter.bountyUpdateTimer = 0f; + if (CachedPlayer.LocalPlayer.PlayerControl == BountyHunter.bountyHunter) + { + var bottomLeft = + new Vector3(-FastDestroyableSingleton.Instance.UseButton.transform.localPosition.x, + FastDestroyableSingleton.Instance.UseButton.transform.localPosition.y, + FastDestroyableSingleton.Instance.UseButton.transform.localPosition.z) + + new Vector3(-0.25f, 1f, 0); + BountyHunter.cooldownText = + Object.Instantiate(FastDestroyableSingleton.Instance.KillButton.cooldownTimerText, + FastDestroyableSingleton.Instance.transform); + BountyHunter.cooldownText.alignment = TextAlignmentOptions.Center; + BountyHunter.cooldownText.transform.localPosition = bottomLeft + new Vector3(0f, -1f, -1f); + BountyHunter.cooldownText.gameObject.SetActive(true); + + foreach (PlayerControl p in CachedPlayer.AllPlayers) + if (playerIcons.ContainsKey(p.PlayerId)) + { + playerIcons[p.PlayerId].setSemiTransparent(false); + playerIcons[p.PlayerId].transform.localPosition = bottomLeft + new Vector3(0f, -1f, 0); + playerIcons[p.PlayerId].transform.localScale = Vector3.one * 0.4f; + playerIcons[p.PlayerId].gameObject.SetActive(false); + } + } + break; + + case RoleId.Medium: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Medium.clearAndReload(); + Medium.medium = local; + break; + + case RoleId.Witch: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Witch.clearAndReload(); + Witch.witch = local; + break; + + case RoleId.Jumper: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Jumper.clearAndReload(); + Jumper.jumper = local; + break; + + case RoleId.Escapist: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Escapist.clearAndReload(); + Escapist.escapist = local; + break; + + case RoleId.Trapper: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Trapper.clearAndReload(); + Trapper.trapper = local; + break; + case RoleId.Ninja: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Ninja.clearAndReload(); + Ninja.ninja = local; + break; + + case RoleId.Blackmailer: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Blackmailer.clearAndReload(); + Blackmailer.blackmailer = local; + break; + + case RoleId.Miner: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Miner.clearAndReload(); + Miner.miner = local; + break; + case RoleId.Undertaker: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Undertaker.clearAndReload(); + Undertaker.undertaker = local; + break; + case RoleId.Prophet: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Prophet.clearAndReload(); + Prophet.prophet = local; + break; + case RoleId.EvilTrapper: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) EvilTrapper.clearAndReload(); + EvilTrapper.evilTrapper = local; + break; + case RoleId.Gambler: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + Gambler.gambler = local; + break; + case RoleId.Poucher: + player.RemoveAll(x => x.PlayerId == local.PlayerId); + if (resetRole) Poucher.clearAndReload(); + Poucher.poucher = local; + break; + } + } + } diff --git a/TheOtherRoles/Roles/Neutral/Jackal.cs b/TheOtherRoles/Roles/Neutral/Jackal.cs index 50de8072..d6eebe9f 100644 --- a/TheOtherRoles/Roles/Neutral/Jackal.cs +++ b/TheOtherRoles/Roles/Neutral/Jackal.cs @@ -7,11 +7,12 @@ namespace TheOtherRoles.Roles.Neutral; public class Jackal { - public static PlayerControl jackal; + public static List jackal = new(); + public static PlayerControl sidekick; public static Color color = new Color32(0, 180, 235, byte.MaxValue); public static PlayerControl currentTarget; - public static List formerJackals = new(); + public static PlayerControl currentTarget2; public static float cooldown = 30f; public static float createSidekickCooldown = 30f; @@ -19,12 +20,12 @@ public class Jackal public static bool canCreateSidekick = true; public static bool jackalPromotedFromSidekickCanCreateSidekick = true; public static bool hasImpostorVision; - public static bool CanImpostorFindSidekick; public static bool canSabotage; public static bool killFakeImpostor; - public static bool wasTeamRed; - public static bool wasImpostor; - public static bool wasSpy; + + public static bool sidekickCanUseVents; + public static bool sidekickCanKill; + public static bool promotesToJackal; public static float chanceSwoop; public static bool canSwoop; @@ -47,9 +48,10 @@ public static void setSwoop() public static void clearAndReload() { - jackal = null; - formerJackals.Clear(); + jackal.Clear(); + sidekick = null; currentTarget = null; + currentTarget2 = null; isInvisable = false; cooldown = CustomOptionHolder.jackalKillCooldown.GetFloat(); swoopCooldown = CustomOptionHolder.jackalSwooperCooldown.GetFloat(); @@ -57,12 +59,14 @@ public static void clearAndReload() createSidekickCooldown = CustomOptionHolder.jackalCreateSidekickCooldown.GetFloat(); canUseVents = CustomOptionHolder.jackalCanUseVents.GetBool(); canSabotage = CustomOptionHolder.jackalCanUseSabo.GetBool(); - CanImpostorFindSidekick = CustomOptionHolder.jackalCanImpostorFindSidekick.GetBool(); canCreateSidekick = CustomOptionHolder.jackalCanCreateSidekick.GetBool(); jackalPromotedFromSidekickCanCreateSidekick = CustomOptionHolder.jackalPromotedFromSidekickCanCreateSidekick.GetBool(); hasImpostorVision = CustomOptionHolder.jackalAndSidekickHaveImpostorVision.GetBool(); killFakeImpostor = CustomOptionHolder.jackalkillFakeImpostor.GetBool(); - wasTeamRed = wasImpostor = wasSpy = false; chanceSwoop = CustomOptionHolder.jackalChanceSwoop.GetSelection() / 10f; + + sidekickCanUseVents = CustomOptionHolder.sidekickCanUseVents.GetBool(); + sidekickCanKill = CustomOptionHolder.sidekickCanKill.GetBool(); + promotesToJackal = CustomOptionHolder.sidekickPromotesToJackal.GetBool(); } } diff --git a/TheOtherRoles/Roles/Neutral/Sidekick.cs b/TheOtherRoles/Roles/Neutral/Sidekick.cs deleted file mode 100644 index 9e1eb489..00000000 --- a/TheOtherRoles/Roles/Neutral/Sidekick.cs +++ /dev/null @@ -1,33 +0,0 @@ -锘縰sing UnityEngine; - -namespace TheOtherRoles.Roles.Neutral; - -public class Sidekick -{ - public static PlayerControl sidekick; - public static Color color = new Color32(0, 180, 235, byte.MaxValue); - - public static PlayerControl currentTarget; - - public static bool wasTeamRed; - public static bool wasImpostor; - public static bool wasSpy; - - public static float cooldown = 30f; - public static bool canUseVents = true; - public static bool canKill = true; - public static bool promotesToJackal = true; - public static bool hasImpostorVision; - - public static void clearAndReload() - { - sidekick = null; - currentTarget = null; - cooldown = CustomOptionHolder.jackalKillCooldown.GetFloat(); - canUseVents = CustomOptionHolder.sidekickCanUseVents.GetBool(); - canKill = CustomOptionHolder.sidekickCanKill.GetBool(); - promotesToJackal = CustomOptionHolder.sidekickPromotesToJackal.GetBool(); - hasImpostorVision = CustomOptionHolder.jackalAndSidekickHaveImpostorVision.GetBool(); - wasTeamRed = wasImpostor = wasSpy = false; - } -} diff --git a/TheOtherRoles/Roles/Neutral/Thief.cs b/TheOtherRoles/Roles/Neutral/Thief.cs index 7c503e9a..7e2cf9a1 100644 --- a/TheOtherRoles/Roles/Neutral/Thief.cs +++ b/TheOtherRoles/Roles/Neutral/Thief.cs @@ -1,4 +1,8 @@ -锘縰sing System.Linq; +锘縰sing System.Collections.Generic; +using System.Linq; +using AmongUs.GameOptions; +using TheOtherRoles.Buttons; +using TheOtherRoles.Utilities; using UnityEngine; namespace TheOtherRoles.Roles.Neutral; @@ -39,8 +43,8 @@ public static void clearAndReload() public static bool tiefCanKill(PlayerControl target, PlayerControl killer) { return killer == thief && (target.Data.Role.IsImpostor || - target == Jackal.jackal || - target == Sidekick.sidekick || + Jackal.jackal.Any(x => x == target) || + target == Jackal.sidekick || target == Werewolf.werewolf || target == Juggernaut.juggernaut || target == Swooper.swooper || @@ -50,4 +54,114 @@ public static bool tiefCanKill(PlayerControl target, PlayerControl killer) (canKillDeputy && target == Deputy.deputy) || (canKillVeteran && target == Veteran.veteran)); } + + public static void StealsRole(byte playerId) + { + var target = playerById(playerId); + var thief = Thief.thief; + if (target == null) return; + if (target == Sheriff.sheriff) Sheriff.sheriff = thief; + if (target == Deputy.deputy) Deputy.deputy = thief; + if (target == Veteran.veteran) Veteran.veteran = thief; + if (Jackal.jackal.Any(x => x == target)) + { + Jackal.jackal.Add(thief); + } + + if (target == Jackal.sidekick) + { + Jackal.sidekick = thief; + Jackal.jackal.Add(target); + if (HandleGuesser.isGuesserGm && CustomOptionHolder.guesserGamemodeSidekickIsAlwaysGuesser.GetBool() && !HandleGuesser.isGuesser(thief.PlayerId)) + RPCProcedure.setGuesserGm(thief.PlayerId); + } + if (target == Pavlovsdogs.pavlovsowner) + { + Pavlovsdogs.pavlovsdogs.Add(target); + Pavlovsdogs.pavlovsowner = thief; + if (HandleGuesser.isGuesserGm && CustomOptionHolder.guesserGamemodePavlovsdogIsAlwaysGuesser.GetBool() && !HandleGuesser.isGuesser(thief.PlayerId)) + RPCProcedure.setGuesserGm(thief.PlayerId); + } + if (Pavlovsdogs.pavlovsdogs.Any(x => x == target)) + { + Pavlovsdogs.pavlovsdogs.Add(thief); + } + if (target == Poucher.poucher && !Poucher.spawnModifier) Poucher.poucher = thief; + if (target == Butcher.butcher) Butcher.butcher = thief; + if (target == Morphling.morphling) Morphling.morphling = thief; + if (target == Camouflager.camouflager) Camouflager.camouflager = thief; + if (target == Vampire.vampire) Vampire.vampire = thief; + if (target == Eraser.eraser) Eraser.eraser = thief; + if (target == Trickster.trickster) Trickster.trickster = thief; + if (target == Gambler.gambler) Gambler.gambler = thief; + if (target == Cleaner.cleaner) Cleaner.cleaner = thief; + if (target == Warlock.warlock) Warlock.warlock = thief; + if (target == Grenadier.grenadier) Grenadier.grenadier = thief; + if (target == BountyHunter.bountyHunter) BountyHunter.bountyHunter = thief; + if (target == Witch.witch) + { + Witch.witch = thief; + if (MeetingHud.Instance) + if (Witch.witchVoteSavesTargets) // In a meeting, if the thief guesses the witch, all targets are saved or no target is saved. + Witch.futureSpelled = new List(); + else // If thief kills witch during the round, remove the thief from the list of spelled people, keep the rest + Witch.futureSpelled.RemoveAll(x => x.PlayerId == thief.PlayerId); + } + + if (target == Ninja.ninja) Ninja.ninja = thief; + if (target == Escapist.escapist) Escapist.escapist = thief; + if (target == Terrorist.terrorist) Terrorist.terrorist = thief; + if (target == Bomber.bomber) Bomber.bomber = thief; + if (target == Miner.miner) Miner.miner = thief; + if (target == Undertaker.undertaker) Undertaker.undertaker = thief; + if (target == Mimic.mimic) + { + Mimic.mimic = thief; + Mimic.hasMimic = false; + } + if (target == Yoyo.yoyo) + { + Yoyo.yoyo = thief; + Yoyo.markedLocation = null; + } + if (target.Data.Role.IsImpostor) + { + RoleManager.Instance.SetRole(Thief.thief, RoleTypes.Impostor); + FastDestroyableSingleton.Instance.KillButton.SetCoolDown(Thief.thief.killTimer, + GameOptionsManager.Instance.currentNormalGameOptions.KillCooldown); + } + + if (target == Werewolf.werewolf) + { + Survivor.survivor.Add(target); + Werewolf.werewolf = thief; + } + if (target == Arsonist.arsonist) + { + Survivor.survivor.Add(target); + Arsonist.arsonist = thief; + } + if (target == Juggernaut.juggernaut) + { + Survivor.survivor.Add(target); + Juggernaut.juggernaut = thief; + } + if (target == Swooper.swooper) + { + Survivor.survivor.Add(target); + Swooper.swooper = thief; + } + + if (target == Deputy.deputy) Deputy.deputy = thief; + if (target == Veteran.veteran) Veteran.veteran = thief; + if (target == Blackmailer.blackmailer) Blackmailer.blackmailer = thief; + if (target == EvilTrapper.evilTrapper) EvilTrapper.evilTrapper = thief; + + if (Lawyer.lawyer != null && target == Lawyer.target) + Lawyer.target = thief; + if (Thief.thief == PlayerControl.LocalPlayer) CustomButton.ResetAllCooldowns(); + Thief.clearAndReload(); + Thief.formerThief = thief; // After clearAndReload, else it would get reset... + } + } diff --git a/TheOtherRoles/Roles/Neutral/Witness.cs b/TheOtherRoles/Roles/Neutral/Witness.cs new file mode 100644 index 00000000..8f929f39 --- /dev/null +++ b/TheOtherRoles/Roles/Neutral/Witness.cs @@ -0,0 +1,21 @@ +锘縰sing UnityEngine; + +namespace TheOtherRoles.Roles.Neutral; + +public class Witness +{ + public static PlayerControl player; + public static Color color = new Color32(123, 170, 255, byte.MaxValue); + public static PlayerControl target; + + public static int markTimer; + public static int winCount; + + public static void ClearAndReload() + { + player = null; + target = null; + markTimer = 30; + winCount = 3; + } +} diff --git a/TheOtherRoles/Roles/RoleHelpers.cs b/TheOtherRoles/Roles/RoleHelpers.cs index fc7be7f2..180e1a64 100644 --- a/TheOtherRoles/Roles/RoleHelpers.cs +++ b/TheOtherRoles/Roles/RoleHelpers.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using MonoMod.Utils; @@ -32,7 +33,8 @@ public static bool CanMultipleShots(PlayerControl dyingTarget) } public static Dictionary blockedRolePairings = new(); - public static Dictionary GhostRoles = new(); + public static Dictionary> GhostRoles = new(); + public static List GhostPlayer = new(); public static void blockRole() { @@ -166,10 +168,18 @@ public static void ResetRoleSelection() { RoleId.Watcher, CustomOptionHolder.modifierWatcher.GetSelection()} }); GhostRoles.Clear(); - GhostRoles.AddRange(new() + GhostPlayer.Clear(); + + GhostRoles[AssignType.Crewmate] = new List { - { RoleId.GhostEngineer, CustomOptionHolder.ghostEngineerSpawnRate.GetSelection()} - }); + new(RoleId.GhostEngineer, CustomOptionHolder.ghostEngineerSpawnRate.GetSelection()), + }; + + + GhostRoles[AssignType.otherNeutral] = new List + { + new(RoleId.Specter, CustomOptionHolder.specterSpawnRate.GetSelection()) + }; } public static void clearAndReloadRoles() @@ -205,7 +215,6 @@ public static void clearAndReloadRoles() Vampire.clearAndReload(); Snitch.clearAndReload(); Jackal.clearAndReload(); - Sidekick.clearAndReload(); Pavlovsdogs.clearAndReload(); Eraser.clearAndReload(); Spy.clearAndReload(); @@ -272,6 +281,7 @@ public static void clearAndReloadRoles() Specoality.clearAndReload(); GhostEngineer.ClearAndReload(); + Specter.ClearAndReload(); // Gamemodes HandleGuesser.clearAndReload(); @@ -289,39 +299,96 @@ public static bool Prefix([HarmonyArgument(0)] PlayerControl player) if (player.IsAlive() || player == null) return false; return true; } + + public static bool otherNeutral(PlayerControl player) + { + if (isNeutral(player) && + player != Jackal.sidekick && + player != Pavlovsdogs.pavlovsowner && + player != Akujo.akujo && + player != Lawyer.lawyer && + !Pavlovsdogs.pavlovsdogs.Contains(player) && + !Jackal.jackal.Contains(player) && + (player != PartTimer.partTimer || PartTimer.target != null)) + return true; + return false; + } + public static void Postfix([HarmonyArgument(0)] PlayerControl player) { - if (player.IsAlive()) return; - int RolesCount = GhostRoles.Sum(role => role.Value); - if (RolesCount == 0) return; + if (GhostPlayer.Contains(player)) return; + + if (player.isCrew()) TryAssignRole(player, AssignType.Crewmate); + + if (otherNeutral(player)) TryAssignRole(player, AssignType.otherNeutral); + } - if (player.isCrew() && GhostRoles.Any(x => x.Value > 0)) + private static void TryAssignRole(PlayerControl player, AssignType assignType) + { + if (!GhostRoles.TryGetValue(assignType, out var roles) || roles.Count == 0) return; + roles = roles.OrderBy(x => Guid.NewGuid()).ToList(); + foreach (var role in roles.Where(x => x.SpawnRate == 10 && !x.Assigned).ToList()) { - foreach (var role in GhostRoles.Where(x => x.Value == 10)) - { - var write = StartRPC(PlayerControl.LocalPlayer, CustomRPC.SetGhostRole); - write.Write(player.PlayerId); - write.Write((byte)role.Key); - write.EndRPC(); - RPCProcedure.setGhostRole(player.PlayerId, (byte)role.Key); - GhostRoles.Remove(role.Key); - return; - } + AssignRoleToPlayer(player, role, roles); + return; + } - foreach (var role in GhostRoles.Where(x => x.Value is > 0 and < 10)) + int maxCount = roles.Count; + int count = 0; + int rd = rnd.Next(1, 10); + while (count < maxCount) + { + bool assigned = false; + foreach (var role in roles.Where(x => x.SpawnRate is > 0 and < 10 && !x.Assigned).ToList()) { - if (rnd.Next(1, 11) <= role.Value) + if (rnd.Next(1, 11) <= role.SpawnRate) { - var write = StartRPC(PlayerControl.LocalPlayer, CustomRPC.SetGhostRole); - write.Write(player.PlayerId); - write.Write((byte)role.Key); - write.EndRPC(); - RPCProcedure.setGhostRole(player.PlayerId, (byte)role.Key); - GhostRoles.Remove(role.Key); + AssignRoleToPlayer(player, role, roles); + assigned = true; break; } } + if (assigned) break; + rd += rnd.Next(0, 3); + count++; } } + + private static void AssignRoleToPlayer(PlayerControl player, Assignment role, List roles) + { + var write = StartRPC(PlayerControl.LocalPlayer, CustomRPC.SetGhostRole); + write.Write(player.PlayerId); + write.Write((byte)role.RoleId); + write.EndRPC(); + + RPCProcedure.setGhostRole(player.PlayerId, (byte)role.RoleId); + roles.Remove(role); + GhostPlayer.Add(player); + role.Assigned = true; + } + } + + public class Assignment + { + public RoleId RoleId { get; set; } + public int SpawnRate { get; set; } + public bool Assigned { get; set; } + + public Assignment(RoleId roleId, int Rate) + { + RoleId = roleId; + SpawnRate = Rate; + } } -} \ No newline at end of file + + public enum AssignType + { + None, + Crewmate, + Impostor, + Neutral, + otherNeutral, + Custom + } +} + diff --git a/TheOtherRoles/Roles/RoleInfo.cs b/TheOtherRoles/Roles/RoleInfo.cs index 1e53793b..5de5c426 100644 --- a/TheOtherRoles/Roles/RoleInfo.cs +++ b/TheOtherRoles/Roles/RoleInfo.cs @@ -7,7 +7,7 @@ namespace TheOtherRoles.Roles; -public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleTeam, bool isGuessable = false) +public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleType, bool isGuessable = false) { public string Name => GetString(nameKey); public string IntroDescription => GetString(nameKey + "IntroDesc"); @@ -16,7 +16,7 @@ public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleTeam public Color color = color; public RoleId roleId = roleId; - public RoleType roleTeam = roleTeam; + public RoleType roleType = roleType; public bool isGuessable = isGuessable; private readonly string nameKey = name; @@ -54,7 +54,7 @@ public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleTeam public static RoleInfo pursuer = new("Pursuer", Pursuer.color, RoleId.Pursuer, RoleType.Neutral); public static RoleInfo partTimer = new("PartTimer", PartTimer.color, RoleId.PartTimer, RoleType.Neutral); public static RoleInfo jackal = new("Jackal", Jackal.color, RoleId.Jackal, RoleType.Neutral); - public static RoleInfo sidekick = new("Sidekick", Sidekick.color, RoleId.Sidekick, RoleType.Neutral); + public static RoleInfo sidekick = new("Sidekick", Jackal.color, RoleId.Sidekick, RoleType.Neutral); public static RoleInfo pavlovsowner = new("Pavlovsowner", Pavlovsdogs.color, RoleId.Pavlovsowner, RoleType.Neutral); public static RoleInfo pavlovsdogs = new("Pavlovsdogs", Pavlovsdogs.color, RoleId.Pavlovsdogs, RoleType.Neutral); public static RoleInfo swooper = new("Swooper", Swooper.color, RoleId.Swooper, RoleType.Neutral); @@ -124,6 +124,7 @@ public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleTeam public static RoleInfo shifter = new("Shifter", Color.yellow, RoleId.Shifter, RoleType.Modifier); public static RoleInfo ghostEngineer = new("GhostEngineer", GhostEngineer.color, RoleId.GhostEngineer, RoleType.GhostRole); + public static RoleInfo specter = new("Specter", Specter.color, RoleId.Specter, RoleType.GhostRole); public static List allRoleInfos = [ @@ -229,7 +230,8 @@ public class RoleInfo(string name, Color color, RoleId roleId, RoleType roleTeam chameleon, shifter, - ghostEngineer + ghostEngineer, + specter, ]; public static List getRoleInfoForPlayer(PlayerControl p, bool showModifier = true, bool showGhost = true) @@ -305,7 +307,6 @@ public static List getRoleInfoForPlayer(PlayerControl p, bool showModi if (p == Terrorist.terrorist) infos.Add(terrorist); if (p == Detective.detective) infos.Add(detective); if (p == TimeMaster.timeMaster) infos.Add(timeMaster); - if (p == Amnisiac.amnisiac) infos.Add(amnisiac); if (p == Veteran.veteran) infos.Add(veteran); if (p == Grenadier.grenadier) infos.Add(grenadier); if (p == Medic.medic) infos.Add(medic); @@ -315,8 +316,6 @@ public static List getRoleInfoForPlayer(PlayerControl p, bool showModi if (p == Hacker.hacker) infos.Add(hacker); if (p == Tracker.tracker) infos.Add(tracker); if (p == Snitch.snitch) infos.Add(snitch); - if (p == Jackal.jackal || (Jackal.formerJackals != null && Jackal.formerJackals.Any(x => x.PlayerId == p.PlayerId))) infos.Add(jackal); - if (p == Sidekick.sidekick) infos.Add(sidekick); if (p == Spy.spy) infos.Add(spy); if (p == SecurityGuard.securityGuard) infos.Add(securityGuard); if (p == Arsonist.arsonist) infos.Add(arsonist); @@ -342,14 +341,18 @@ public static List getRoleInfoForPlayer(PlayerControl p, bool showModi if (p == Juggernaut.juggernaut) infos.Add(juggernaut); if (p == Doomsayer.doomsayer) infos.Add(doomsayer); if (p == Akujo.akujo) infos.Add(akujo); + if (p == Jackal.sidekick) infos.Add(sidekick); if (p == Pavlovsdogs.pavlovsowner) infos.Add(pavlovsowner); - if (p == Pavlovsdogs.pavlovsdogs.Any(x => x.PlayerId == p.PlayerId)) infos.Add(pavlovsdogs); + if (Jackal.jackal.Any(x => x != null && x.PlayerId == p.PlayerId)) infos.Add(jackal); + if (Amnisiac.player.Any(x => x.PlayerId == p.PlayerId)) infos.Add(amnisiac); + if (Pavlovsdogs.pavlovsdogs.Any(x => x.PlayerId == p.PlayerId)) infos.Add(pavlovsdogs); if (Pursuer.pursuer.Any(x => x.PlayerId == p.PlayerId)) infos.Add(pursuer); if (Survivor.survivor.Any(x => x.PlayerId == p.PlayerId)) infos.Add(survivor); if (showGhost) { if (p == GhostEngineer.player) infos.Add(ghostEngineer); + if (p == Specter.player) infos.Add(specter); } if (infos.Count == count) @@ -368,11 +371,11 @@ public static string GetRolesString(PlayerControl p, bool useColors, bool showMo if (onlyGhostRole) { - var ghostRoleInfo = getRoleInfoForPlayer(p, false, true).FirstOrDefault(x => x.roleTeam == RoleType.GhostRole); + var ghostRoleInfo = getRoleInfoForPlayer(p, false, true).FirstOrDefault(x => x.roleType == RoleType.GhostRole); if (p.Data.IsDead && ghostRoleInfo != null) { - roleName = string.Join(" ", getRoleInfoForPlayer(p, false, true).Where(x => x.roleTeam is RoleType.GhostRole or RoleType.Modifier) + roleName = string.Join(" ", getRoleInfoForPlayer(p, false, true).Where(x => x.roleType is RoleType.GhostRole or RoleType.Modifier) .Select(x => useColors ? cs(x.color, x.Name) : x.Name).ToArray()); } } @@ -383,7 +386,7 @@ public static string GetRolesString(PlayerControl p, bool useColors, bool showMo if (Executioner.target != null && p.PlayerId == Executioner.target.PlayerId && CachedPlayer.LocalPlayer.PlayerControl != Executioner.target) roleName += useColors ? cs(Executioner.color, " 搂") : " 搂"; - if (p == Jackal.jackal && Jackal.canSwoop) roleName += "JackalIsSwooperInfo".Translate(); + if (Jackal.jackal.Any(x => x == p && x.IsAlive()) && Jackal.canSwoop) roleName += "JackalIsSwooperInfo".Translate(); if (HandleGuesser.isGuesserGm && HandleGuesser.isGuesser(p.PlayerId)) roleName += "GuessserGMInfo".Translate(); @@ -409,6 +412,8 @@ public static string GetRolesString(PlayerControl p, bool useColors, bool showMo roleName = cs(Warlock.color, "(琚笅鍜) ") + roleName; if (p == Ninja.ninjaMarked) roleName = cs(Ninja.color, "(琚爣璁) ") + roleName; + if (p == Thief.formerThief) + roleName += cs(Thief.color, " (绐)"); if (Pursuer.blankedList.Contains(p) && !p.Data.IsDead) roleName = cs(Pursuer.color, "(琚绌哄寘寮) ") + roleName; if (Witch.futureSpelled.Contains(p) && !MeetingHud.Instance) // This is already displayed in meetings! diff --git a/TheOtherRoles/TasksHandler.cs b/TheOtherRoles/TasksHandler.cs index d6f31c13..0f149cc3 100644 --- a/TheOtherRoles/TasksHandler.cs +++ b/TheOtherRoles/TasksHandler.cs @@ -31,7 +31,7 @@ private static bool ShouldCountTasks(GameData.PlayerInfo playerInfo) { return !(playerInfo.Object && playerInfo.Object.hasAliveKillingLover()) && playerInfo.PlayerId != Thief.thief?.PlayerId - && playerInfo.PlayerId != Amnisiac.amnisiac?.PlayerId + && !Amnisiac.player.Any(x => x.PlayerId == playerInfo.PlayerId) && playerInfo.PlayerId != Akujo.honmei?.PlayerId; } @@ -39,7 +39,7 @@ private static bool Prefix(GameData __instance) { var totalTasks = 0; var completedTasks = 0; - //浠诲姟缁撶畻 + foreach (var playerInfo in GameData.Instance.AllPlayers.GetFastEnumerator()) { if (!ShouldCountTasks(playerInfo)) diff --git a/TheOtherRoles/TheOtherRoles.csproj b/TheOtherRoles/TheOtherRoles.csproj index 3693fa4b..9e49520d 100644 --- a/TheOtherRoles/TheOtherRoles.csproj +++ b/TheOtherRoles/TheOtherRoles.csproj @@ -1,7 +1,7 @@ 锘 net6.0 - 1.0.9.9 + 1.1.0.0 TheOtherUs mxyx-club latest