diff --git a/EHR.sln.DotSettings b/EHR.sln.DotSettings index 273238515..546c183d8 100644 --- a/EHR.sln.DotSettings +++ b/EHR.sln.DotSettings @@ -39,4 +39,13 @@ UI VN False \ No newline at end of file + x:Key="/Default/Housekeeping/Layout/SolBuilderDuoView/ShowBuildProgressInToolWindow/@EntryValue">False + try +{ + $SELECTION$ +} +catch ($EXCEPTION$ $EX_NAME$) +{ + $SELSTART$Utils.ThrowException($EX_NAME$);$SELEND$ +} \ No newline at end of file diff --git a/GameMode/CTFManager.cs b/GameMode/CTFManager.cs index f78715153..6468b21b4 100644 --- a/GameMode/CTFManager.cs +++ b/GameMode/CTFManager.cs @@ -35,6 +35,7 @@ namespace EHR private static Dictionary PlayerTeams = []; private static Dictionary TeamData = []; private static Dictionary PlayerData = []; + private static Dictionary DefaultOutfits = []; private static bool ValidTag; public static bool IsDeathPossible => TaggedPlayersGet.GetValue() == 1; @@ -131,26 +132,35 @@ namespace EHR switch (aapc.Length) { case 0: + ResetSkins(); CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Draw); CustomWinnerHolder.WinnerIds = Main.PlayerStates.Keys.ToHashSet(); reason = GameOverReason.HumansDisconnect; return true; case 1: + ResetSkins(); TeamData[PlayerTeams[aapc[0].PlayerId]].SetAsWinner(); return true; default: // If WinnerData is already set, end the game - if (WinnerData.Team != "No one wins") return true; + if (WinnerData.Team != "No one wins") + { + ResetSkins(); + return true; + } // If all players are on the same team, end the game if (aapc.All(x => PlayerTeams[x.PlayerId] == CTFTeam.Blue) || aapc.All(x => PlayerTeams[x.PlayerId] == CTFTeam.Yellow)) { + ResetSkins(); TeamData[PlayerTeams[aapc[0].PlayerId]].SetAsWinner(); return true; } return false; } + + void ResetSkins() => DefaultOutfits.Select(x => (pc: x.Key.GetPlayer(), outfit: x.Value)).DoIf(x => x.pc != null && x.outfit != null, x => Utils.RpcChangeSkin(x.pc, x.outfit)); } public static void OnGameStart() @@ -160,6 +170,7 @@ namespace EHR TeamData = []; WinnerData = (Color.white, "No one wins"); PlayerData = Main.PlayerStates.Keys.ToDictionary(x => x, _ => new CTFPlayerData()); + DefaultOutfits = Main.AllPlayerControls.ToDictionary(x => x.PlayerId, x => x.Data.DefaultOutfit); ValidTag = false; // Check if the current game mode is Capture The Flag @@ -182,6 +193,7 @@ namespace EHR PlayerTeams[player.PlayerId] = CTFTeam.Blue; bluePlayers.Add(player.PlayerId); blueOutfit.PlayerName = player.GetRealName(); + blueOutfit.PetId = player.Data.DefaultOutfit.PetId; Utils.RpcChangeSkin(player, blueOutfit); } @@ -190,6 +202,7 @@ namespace EHR PlayerTeams[player.PlayerId] = CTFTeam.Yellow; yellowPlayers.Add(player.PlayerId); yellowOutfit.PlayerName = player.GetRealName(); + yellowOutfit.PetId = player.Data.DefaultOutfit.PetId; Utils.RpcChangeSkin(player, yellowOutfit); } @@ -221,6 +234,9 @@ namespace EHR break; } } + + pc.CheckAndSetUnshiftState(force: true); + pc.RpcResetAbilityCooldown(); } ValidTag = true; @@ -232,7 +248,7 @@ namespace EHR var targetTeam = PlayerTeams[target.PlayerId]; if (!ValidTag || PlayerTeams[killer.PlayerId] == targetTeam || TeamData.Values.Any(x => x.FlagCarrier == killer.PlayerId)) return; - killer.SetKillCooldown(); + new[] { killer, target }.Do(x => x.SetKillCooldown()); if (TeamData.FindFirst(x => x.Value.FlagCarrier == target.PlayerId, out var kvp)) { @@ -261,16 +277,21 @@ namespace EHR Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: killer); } - public static void OnPet(PlayerControl pc) + public static void TryPickUpFlag(PlayerControl pc) { if (!ValidTag) return; - Logger.Info($"{pc.GetRealName()} petted their pet", "CTF.OnPet"); + Logger.Info($"Received flag pickup request from {pc.GetRealName()}", "CTF"); // If the player is near the enemy's flag, pick it up var pos = pc.Pos(); var enemy = TeamData[PlayerTeams[pc.PlayerId].GetOppositeTeam()]; if (enemy.IsNearFlag(pos)) enemy.PickUpFlag(pc.PlayerId); } + public static void ApplyGameOptions(IGameOptions opt) + { + AURoleOptions.ShapeshifterCooldown = 1f; + } + static Color GetTeamColor(this CTFTeam team) { return team switch diff --git a/Modules/AntiBlackout.cs b/Modules/AntiBlackout.cs index e54c78fb5..4a7fa9134 100644 --- a/Modules/AntiBlackout.cs +++ b/Modules/AntiBlackout.cs @@ -15,7 +15,7 @@ public static class AntiBlackout /// ///Whether to override the ejection process /// - public static bool OverrideExiledPlayer => IsRequired && (IsSingleImpostor || Diff_CrewImp == 1); + public static bool OverrideExiledPlayer => IsRequired && (IsSingleImpostor || Diff_CrewImp == 1 || Main.AliveImpostorCount == 1); /// ///Is there only one impostor? @@ -25,7 +25,7 @@ public static class AntiBlackout /// ///Whether processing within AntiBlackout is required /// - public static bool IsRequired => Options.NoGameEnd.GetBool() || Main.AllPlayerControls.Any(x => x.GetCustomRole().GetCountTypes() is not CountTypes.Crew and not CountTypes.Impostor and not CountTypes.OutOfGame); + public static bool IsRequired => Options.NoGameEnd.GetBool() || Main.AllPlayerControls.Any(x => x.GetCountTypes() is not CountTypes.Crew and not CountTypes.Impostor and not CountTypes.OutOfGame); //|| Pirate.IsEnable; /// @@ -110,6 +110,7 @@ public static class AntiBlackout AmongUsClient.Instance.SendOrDisconnect(writer); writer.Recycle(); } + AmongUsClient.Instance.SendAllStreamedObjects(); } diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 5a1fc565a..3a3dea856 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -87,7 +87,7 @@ internal static class CustomRolesHelper { if (role.IsGhostRole()) return CustomRoles.GuardianAngel; if (role.IsVanilla()) return role; - if (checkDesyncRole && role.GetDYRole() == RoleTypes.Impostor) return CustomRoles.Impostor; + if (checkDesyncRole && role.IsDesyncRole()) return Enum.Parse(role.GetDYRole() + "EHR"); if (Options.UsePhantomBasis.GetBool() && role.SimpleAbilityTrigger()) return CustomRoles.Phantom; if (Options.UseUnshiftTrigger.GetBool() && role.SimpleAbilityTrigger()) return CustomRoles.Shapeshifter; bool UsePets = Options.UsePets.GetBool(); @@ -334,8 +334,7 @@ internal static class CustomRolesHelper public static CustomRoles GetErasedRole(this CustomRoles role) { if (role.IsVanilla()) return role; - var vnRole = role.GetVNRole(); - if (role.GetDYRole() == RoleTypes.Impostor) vnRole = CustomRoles.Impostor; + var vnRole = role.GetVNRole(checkDesyncRole: true); return vnRole switch { CustomRoles.Crewmate => CustomRoles.CrewmateEHR, @@ -369,7 +368,7 @@ internal static class CustomRolesHelper // Speedrun CustomRoles.Runner => RoleTypes.Crewmate, // Capture The Flag - CustomRoles.CTFPlayer => RoleTypes.Impostor, + CustomRoles.CTFPlayer => RoleTypes.Shapeshifter, // Standard CustomRoles.Executioner => Executioner.CRoleChangeRoles[Executioner.ChangeRolesAfterTargetKilled.GetValue()].GetDYRole(), CustomRoles.Sheriff => UsePets && Sheriff.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor, @@ -809,6 +808,7 @@ internal static class CustomRolesHelper public static bool CheckAddonConflict(CustomRoles role, PlayerControl pc) => role.IsAdditionRole() && (!Main.NeverSpawnTogetherCombos.TryGetValue(OptionItem.CurrentPreset, out var neverList) || !neverList.TryGetValue(pc.GetCustomRole(), out var bannedAddonList) || !bannedAddonList.Contains(role)) && pc.GetCustomRole() is not CustomRoles.GuardianAngelEHR and not CustomRoles.God && !pc.Is(CustomRoles.Madmate) && !pc.Is(CustomRoles.GM) && role is not CustomRoles.Lovers && !pc.Is(CustomRoles.Needy) && (!pc.HasSubRole() || pc.GetCustomSubRoles().Count < Options.NoLimitAddonsNumMax.GetInt()) && (!Options.AddonCanBeSettings.TryGetValue(role, out var o) || ((o.Imp.GetBool() || !pc.GetCustomRole().IsImpostor()) && (o.Neutral.GetBool() || !pc.GetCustomRole().IsNeutral()) && (o.Crew.GetBool() || !pc.IsCrewmate()))) && (!role.IsImpOnlyAddon() || pc.IsImpostor()) && role switch { + CustomRoles.Disco when pc.GetCustomRole() is CustomRoles.Chameleon or CustomRoles.Swooper or CustomRoles.Wraith or CustomRoles.Alchemist => false, CustomRoles.Egoist when pc.Is(CustomRoles.Gangster) => false, CustomRoles.Nimble when pc.Is(CustomRoles.Oxyman) => false, CustomRoles.Magnet when pc.Is(Team.Impostor) => false, @@ -820,7 +820,7 @@ internal static class CustomRolesHelper CustomRoles.Autopsy when pc.Is(CustomRoles.Doctor) || pc.Is(CustomRoles.Tracefinder) || pc.Is(CustomRoles.Scientist) || pc.Is(CustomRoles.ScientistEHR) || pc.Is(CustomRoles.Sunnyboy) => false, CustomRoles.Necroview when pc.Is(CustomRoles.Doctor) => false, CustomRoles.Lazy when pc.Is(CustomRoles.Speedrunner) => false, - CustomRoles.Mischievous when pc.Is(Team.Impostor) || pc.GetCustomRole().GetDYRole() != RoleTypes.Impostor || !pc.IsNeutralKiller() || Main.PlayerStates[pc.PlayerId].Role.CanUseSabotage(pc) => false, + CustomRoles.Mischievous when pc.Is(Team.Impostor) || (!pc.GetCustomRole().IsDesyncRole() && !pc.Is(CustomRoles.Bloodlust)) || !pc.IsNeutralKiller() || Main.PlayerStates[pc.PlayerId].Role.CanUseSabotage(pc) => false, CustomRoles.Loyal when pc.IsCrewmate() && !Options.CrewCanBeLoyal.GetBool() => false, CustomRoles.Lazy when pc.Is(CustomRoles.Needy) || pc.Is(CustomRoles.Snitch) || pc.Is(CustomRoles.Marshall) || pc.Is(CustomRoles.Transporter) || pc.Is(CustomRoles.Guardian) => false, CustomRoles.Brakar when pc.Is(CustomRoles.Dictator) => false, @@ -900,9 +900,9 @@ internal static class CustomRolesHelper CustomRoles.Busy when !pc.GetTaskState().hasTasks => false, CustomRoles.Truant when pc.Is(CustomRoles.SoulHunter) => false, CustomRoles.Nimble when !pc.IsCrewmate() => false, - CustomRoles.Physicist when !pc.IsCrewmate() || pc.GetCustomRole().GetDYRole() == RoleTypes.Impostor => false, - CustomRoles.Finder when !pc.IsCrewmate() || pc.GetCustomRole().GetDYRole() == RoleTypes.Impostor => false, - CustomRoles.Noisy when !pc.IsCrewmate() || pc.GetCustomRole().GetDYRole() == RoleTypes.Impostor => false, + CustomRoles.Physicist when !pc.IsCrewmate() || pc.GetCustomRole().IsDesyncRole() => false, + CustomRoles.Finder when !pc.IsCrewmate() || pc.GetCustomRole().IsDesyncRole() => false, + CustomRoles.Noisy when !pc.IsCrewmate() || pc.GetCustomRole().IsDesyncRole() => false, CustomRoles.Unreportable when pc.Is(CustomRoles.Bait) => false, CustomRoles.Oblivious when pc.Is(CustomRoles.Bloodhound) => false, CustomRoles.Oblivious when pc.Is(CustomRoles.Vulture) => false, @@ -939,8 +939,7 @@ internal static class CustomRolesHelper public static RoleTypes GetRoleTypes(this CustomRoles role) { - if (role.GetDYRole() == RoleTypes.Impostor) return RoleTypes.Impostor; - if (Enum.TryParse(role.GetVNRole().ToString(), ignoreCase: true, out var type)) return type; + if (Enum.TryParse(role.GetVNRole(checkDesyncRole: true).ToString(), ignoreCase: true, out var type)) return type; return role.IsImpostor() ? RoleTypes.Impostor : RoleTypes.Crewmate; } diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index e51b3581b..3efc012f2 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -186,11 +186,11 @@ static class ExtendedPlayerControl var callerMethod = caller.GetMethod(); string callerMethodName = callerMethod?.Name; string callerClassName = callerMethod?.DeclaringType?.FullName; - Logger.Warn(callerClassName + "." + callerMethodName + " tried to get a CountType, but the player was null", "GetCountTypes"); + Logger.Warn($"{callerClassName}.{callerMethodName} tried to get a CountType, but the player was null", "GetCountTypes"); return CountTypes.None; } - return Main.PlayerStates.TryGetValue(player.PlayerId, out var State) ? State.countTypes : CountTypes.None; + return Main.PlayerStates.TryGetValue(player.PlayerId, out var State) ? State.SubRoles.Contains(CustomRoles.Bloodlust) ? CountTypes.Bloodlust : State.countTypes : CountTypes.None; } public static void RpcSetNameEx(this PlayerControl player, string name) @@ -200,7 +200,7 @@ static class ExtendedPlayerControl Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] = name; } - Logger.Info($"Set:{player?.Data?.PlayerName}:{name} for All", "RpcSetNameEx"); + Logger.Info($"Set: {player?.Data?.PlayerName} => {name} for Everyone", "RpcSetNameEx"); player?.RpcSetName(name); } @@ -211,7 +211,7 @@ static class ExtendedPlayerControl if (!force && Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] == name) return; Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] = name; - Logger.Info($"Set:{player.Data?.PlayerName}:{name} for {seer.GetNameWithRole().RemoveHtmlTags()}", "RpcSetNamePrivate"); + Logger.Info($"Set: {player.Data?.PlayerName} => {name} for {seer.GetNameWithRole().RemoveHtmlTags()}", "RpcSetNamePrivate"); if (seer == null || player == null) return; @@ -649,7 +649,7 @@ static class ExtendedPlayerControl { CustomRoles role = pc.GetCustomRole(); if (!pc.IsAlive() || pc.Data.Role.Role == RoleTypes.GuardianAngel || Pelican.IsEaten(pc.PlayerId)) return false; - if (role.GetDYRole() == RoleTypes.Impostor || role.GetVNRole() is CustomRoles.Impostor or CustomRoles.Shapeshifter) return true; + if (role.GetVNRole(checkDesyncRole: true) is CustomRoles.Impostor or CustomRoles.Shapeshifter) return true; return pc.Is(CustomRoleTypes.Impostor) || pc.IsNeutralKiller() || role.IsTasklessCrewmate(); } @@ -930,10 +930,13 @@ static class ExtendedPlayerControl target.SetRealKiller(killer, NotOverRide: true); + if (target.Is(CustomRoles.Jackal)) + Jackal.Instances.Do(x => x.PromoteSidekick()); + switch (killer.PlayerId == target.PlayerId) { case true when killer.shapeshifting: - LateTask.New(() => { killer.RpcMurderPlayer(target, true); }, 1.5f, "Shapeshifting Suicide Delay"); + LateTask.New(() => killer.RpcMurderPlayer(target, true), 1.5f, "Shapeshifting Suicide Delay"); return; case false when !killer.Is(CustomRoles.Pestilence) && Main.PlayerStates[target.PlayerId].Role is SchrodingersCat cat: cat.OnCheckMurderAsTarget(killer, target); @@ -955,6 +958,19 @@ static class ExtendedPlayerControl reporter.RpcStartMeeting(target); } + public static void CheckAndSetUnshiftState(this PlayerControl pc, bool notify = true, bool force = false) + { + if (force || (pc.GetCustomRole().SimpleAbilityTrigger() && Options.UseUnshiftTrigger.GetBool() && (!pc.IsNeutralKiller() || Options.UseUnshiftTriggerForNKs.GetBool()))) + { + var target = Main.AllAlivePlayerControls.Without(pc).RandomElement(); + var outfit = pc.Data.DefaultOutfit; + pc.RpcShapeshift(target, false); + Main.CheckShapeshift[pc.PlayerId] = false; + RpcChangeSkin(pc, outfit); + if (notify) NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc, NoCache: true); + } + } + public static bool IsModClient(this PlayerControl player) => Main.PlayerVersion.ContainsKey(player.PlayerId); public static List GetPlayersInAbilityRangeSorted(this PlayerControl player, bool ignoreColliders = false) => GetPlayersInAbilityRangeSorted(player, _ => true, ignoreColliders); diff --git a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs index e96b47eda..9598a5533 100644 --- a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs +++ b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs @@ -161,19 +161,18 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS } else { - opt.SetVision(true); - opt.SetFloat(FloatOptionNames.CrewLightMod, 1.25f); - opt.SetFloat(FloatOptionNames.ImpostorLightMod, 1.25f); + SetMaxVision(); } break; case CustomGameMode.CaptureTheFlag: + CTFManager.ApplyGameOptions(opt); + SetMaxVision(); + break; case CustomGameMode.Speedrun: case CustomGameMode.HotPotato: case CustomGameMode.MoveAndStop: - opt.SetVision(true); - opt.SetFloat(FloatOptionNames.CrewLightMod, 1.25f); - opt.SetFloat(FloatOptionNames.ImpostorLightMod, 1.25f); + SetMaxVision(); break; case CustomGameMode.HideAndSeek: HnSManager.ApplyGameOptions(opt, player); @@ -231,6 +230,9 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS Main.PlayerStates[player.PlayerId].Role.ApplyGameOptions(opt, player.PlayerId); + if (player.Is(CustomRoles.Bloodlust) && Bloodlust.HasImpVision.GetBool()) + opt.SetVision(true); + if (Main.AllPlayerControls.Any(x => x.Is(CustomRoles.Bewilder) && !x.IsAlive() && x.GetRealKiller()?.PlayerId == player.PlayerId && !x.Is(CustomRoles.Hangman))) { opt.SetVision(false); @@ -272,9 +274,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS if (Minion.BlindPlayers.Contains(player.PlayerId)) { - opt.SetVision(false); - opt.SetFloat(FloatOptionNames.CrewLightMod, 0); - opt.SetFloat(FloatOptionNames.ImpostorLightMod, 0); + SetBlind(); } if (Sentinel.IsPatrolling(player.PlayerId)) @@ -297,15 +297,11 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS if (Randomizer.HasSuperVision(player)) { - opt.SetVision(true); - opt.SetFloat(FloatOptionNames.CrewLightMod, 1.5f); - opt.SetFloat(FloatOptionNames.ImpostorLightMod, 1.5f); + SetMaxVision(); } else if (Randomizer.IsBlind(player)) { - opt.SetVision(false); - opt.SetFloat(FloatOptionNames.CrewLightMod, 0); - opt.SetFloat(FloatOptionNames.ImpostorLightMod, 0); + SetBlind(); } var array = Main.PlayerStates[player.PlayerId].SubRoles; @@ -332,9 +328,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS Main.AllPlayerSpeed[player.PlayerId] = Options.MareSpeedDuringLightsOut.GetFloat(); break; case CustomRoles.Sleep when Utils.IsActive(SystemTypes.Electrical): - opt.SetVision(false); - opt.SetFloat(FloatOptionNames.CrewLightMod, 0); - opt.SetFloat(FloatOptionNames.ImpostorLightMod, 0); + SetBlind(); Main.AllPlayerSpeed[player.PlayerId] = Main.MinSpeed; break; case CustomRoles.Torch: @@ -390,23 +384,17 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS if (Magician.BlindPPL.ContainsKey(player.PlayerId)) { - opt.SetVision(false); - opt.SetFloat(FloatOptionNames.CrewLightMod, 0); - opt.SetFloat(FloatOptionNames.ImpostorLightMod, 0); + SetBlind(); } if (player.IsCrewmate() && Main.PlayerStates.Values.Any(s => s.Role is Adventurer { IsEnable: true } av && av.ActiveWeapons.Contains(Adventurer.Weapon.Lantern))) { - opt.SetVision(true); - opt.SetFloat(FloatOptionNames.CrewLightMod, 1.5f); - opt.SetFloat(FloatOptionNames.ImpostorLightMod, 1.5f); + SetMaxVision(); } if (Chemist.Instances.Any(x => x.IsBlinding && player.PlayerId != x.ChemistPC.PlayerId)) { - opt.SetVision(false); - opt.SetFloat(FloatOptionNames.CrewLightMod, 0); - opt.SetFloat(FloatOptionNames.ImpostorLightMod, 0); + SetBlind(); } if (Changeling.ChangedRole.TryGetValue(player.PlayerId, out var changed) && changed && player.GetRoleTypes() != RoleTypes.Shapeshifter) @@ -458,6 +446,20 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS AURoleOptions.ImpostorsCanSeeProtect = false; return opt; + + void SetMaxVision() + { + opt.SetVision(true); + opt.SetFloat(FloatOptionNames.CrewLightMod, 1.5f); + opt.SetFloat(FloatOptionNames.ImpostorLightMod, 1.5f); + } + + void SetBlind() + { + opt.SetVision(false); + opt.SetFloat(FloatOptionNames.CrewLightMod, 0f); + opt.SetFloat(FloatOptionNames.ImpostorLightMod, 0f); + } } catch (Exception e) { diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index 152a3b815..8241ea401 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -1224,7 +1224,7 @@ public static class Options FormatNameMode = new StringOptionItem(19403, "FormatNameMode", FormatNameModes, 0, TabGroup.SystemSettings); DisableEmojiName = new BooleanOptionItem(19404, "DisableEmojiName", true, TabGroup.SystemSettings); ChangeNameToRoleInfo = new BooleanOptionItem(19405, "ChangeNameToRoleInfo", true, TabGroup.SystemSettings); - ShowLongInfo = new BooleanOptionItem(19410, "ShowLongInfo", false, TabGroup.SystemSettings) + ShowLongInfo = new BooleanOptionItem(19420, "ShowLongInfo", false, TabGroup.SystemSettings) .SetParent(ChangeNameToRoleInfo); SendRoleDescriptionFirstMeeting = new BooleanOptionItem(19406, "SendRoleDescriptionFirstMeeting", true, TabGroup.SystemSettings); NoGameEnd = new BooleanOptionItem(19407, "NoGameEnd", false, TabGroup.SystemSettings) diff --git a/Modules/Utils.cs b/Modules/Utils.cs index f857a633c..7ed743db3 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -2149,6 +2149,8 @@ public static class Utils BeforeEnd2: + bool shouldSeeTargetAddons = new[] { seer, target }.All(x => x.Is(Team.Impostor)); + string TargetRoleText = (seer.Data.IsDead && Options.GhostCanSeeOtherRoles.GetBool()) || (seer.Is(CustomRoles.Mimic) && target.Data.IsDead && Options.MimicCanSeeDeadRoles.GetBool()) || @@ -2174,7 +2176,7 @@ public static class Utils (seer.IsRevealedPlayer(target) && !target.Is(CustomRoles.Trickster)) || seer.Is(CustomRoles.God) || target.Is(CustomRoles.GM) - ? $"{target.GetDisplayRoleName(seer.PlayerId != target.PlayerId && !seer.Data.IsDead)}{GetProgressText(target)}\r\n" + ? $"{target.GetDisplayRoleName(shouldSeeTargetAddons)}{GetProgressText(target)}\r\n" : string.Empty; if (Options.CurrentGameMode == CustomGameMode.CaptureTheFlag) TargetRoleText = string.Empty; @@ -2524,14 +2526,7 @@ public static class Utils if (Options.UsePets.GetBool()) pc.AddAbilityCD(includeDuration: false); - if (pc.GetCustomRole().SimpleAbilityTrigger() && Options.UseUnshiftTrigger.GetBool() && (!pc.IsNeutralKiller() || Options.UseUnshiftTriggerForNKs.GetBool())) - { - var target = Main.AllAlivePlayerControls.Without(pc).RandomElement(); - var outfit = pc.Data.DefaultOutfit; - pc.RpcShapeshift(target, false); - Main.CheckShapeshift[pc.PlayerId] = false; - RpcChangeSkin(pc, outfit); - } + pc.CheckAndSetUnshiftState(notify: false); AFKDetector.RecordPosition(pc); diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index 46e289dcd..043f39920 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -326,7 +326,7 @@ internal static class ChatCommands private static void HMCommand(ChatController __instance, PlayerControl player, string text, string[] args) { - if (args.Length < 2 || !int.TryParse(args[1], out int id) || id is > 3 or < 1) return; + if (Messenger.Sent.Contains(player.PlayerId) || args.Length < 2 || !int.TryParse(args[1], out int id) || id is > 3 or < 1) return; Main.Instance.StartCoroutine(SendOnMeeting()); return; diff --git a/Patches/CredentialsPatch.cs b/Patches/CredentialsPatch.cs index 0de308fd7..1dd7595fd 100644 --- a/Patches/CredentialsPatch.cs +++ b/Patches/CredentialsPatch.cs @@ -199,10 +199,10 @@ internal class TitleLogoPatch if (!(BottomButtonBounds = GameObject.Find("BottomButtonBounds"))) return; BottomButtonBounds.transform.localPosition -= new Vector3(0f, 0.1f, 0f); - __instance.settingsButton.buttonText.text = GetString("MainMenu.SettingsButton"); - __instance.inventoryButton.buttonText.text = GetString("MainMenu.InventoryButton"); - __instance.creditsButton.buttonText.text = GetString("MainMenu.CreditsButton"); - __instance.quitButton.buttonText.text = GetString("MainMenu.QuitButton"); + __instance.settingsButton?.ChangeButtonText(GetString("MainMenu.SettingsButton")); + __instance.inventoryButton?.ChangeButtonText(GetString("MainMenu.InventoryButton")); + __instance.creditsButton?.ChangeButtonText(GetString("MainMenu.CreditsButton")); + __instance.quitButton?.ChangeButtonText(GetString("MainMenu.QuitButton")); return; @@ -295,4 +295,4 @@ class OptionsMenuBehaviourOpenPatch if (GameStates.InGame && GameStates.IsMeeting) GuessManager.DestroyIDLabels(); } -} +} \ No newline at end of file diff --git a/Patches/ExilePatch.cs b/Patches/ExilePatch.cs index 829fa3165..4915ddcad 100644 --- a/Patches/ExilePatch.cs +++ b/Patches/ExilePatch.cs @@ -11,21 +11,16 @@ using HarmonyLib; namespace EHR.Patches; -class ExileControllerWrapUpPatch +static class ExileControllerWrapUpPatch { - private static NetworkedPlayerInfo antiBlackout_LastExiled; - - public static NetworkedPlayerInfo AntiBlackout_LastExiled - { - get => antiBlackout_LastExiled; - set => antiBlackout_LastExiled = value; - } + public static NetworkedPlayerInfo AntiBlackout_LastExiled { get; set; } static void WrapUpPostfix(NetworkedPlayerInfo exiled) { if (AntiBlackout.OverrideExiledPlayer) { exiled = AntiBlackout_LastExiled; + Logger.SendInGame(Translator.GetString("AntiBlackout.OverrideExiledPlayer")); } bool DecidedWinner = false; @@ -44,12 +39,13 @@ class ExileControllerWrapUpPatch { if (!Options.InnocentCanWinByImp.GetBool() && role.IsImpostor()) { - Logger.Info("冤罪的目标是内鬼,非常可惜啊", "Exeiled Winner Check"); + Logger.Info("The exiled player is an impostor, but the Innocent cannot win due to the settings", "Exeiled Winner Check"); } else { CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Innocent); - Main.AllPlayerControls.Where(x => x.Is(CustomRoles.Innocent) && !x.IsAlive() && x.GetRealKiller()?.PlayerId == exiled.PlayerId) + Main.AllPlayerControls + .Where(x => x.Is(CustomRoles.Innocent) && !x.IsAlive() && x.GetRealKiller()?.PlayerId == exiled.PlayerId) .Do(x => CustomWinnerHolder.WinnerIds.Add(x.PlayerId)); DecidedWinner = true; } diff --git a/Patches/IntroPatch.cs b/Patches/IntroPatch.cs index 47b0c4719..8177535b4 100644 --- a/Patches/IntroPatch.cs +++ b/Patches/IntroPatch.cs @@ -651,7 +651,7 @@ class BeginImpostorPatch return true; } - if (PlayerControl.LocalPlayer.IsCrewmate() && role.GetDYRole() == RoleTypes.Impostor) + if (PlayerControl.LocalPlayer.IsCrewmate() && role.IsDesyncRole()) { yourTeam = new(); yourTeam.Add(PlayerControl.LocalPlayer); @@ -784,21 +784,7 @@ class IntroCutsceneDestroyPatch if (Options.UseUnshiftTrigger.GetBool()) { - LateTask.New(() => - { - foreach (var pc in Main.AllAlivePlayerControls) - { - if (pc.GetCustomRole().SimpleAbilityTrigger() && Options.UseUnshiftTrigger.GetBool() && (!pc.IsNeutralKiller() || Options.UseUnshiftTriggerForNKs.GetBool())) - { - var target = Main.AllAlivePlayerControls.Without(pc).RandomElement(); - var outfit = pc.Data.DefaultOutfit; - pc.RpcShapeshift(target, false); - Main.CheckShapeshift[pc.PlayerId] = false; - Utils.RpcChangeSkin(pc, outfit); - Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc, NoCache: true); - } - } - }, 2f, "UnshiftTrigger SS"); + LateTask.New(() => Main.AllAlivePlayerControls.Do(x => x.CheckAndSetUnshiftState()), 2f, "UnshiftTrigger SS"); } if (PlayerControl.LocalPlayer.Is(CustomRoles.GM)) diff --git a/Patches/MainMenuManagerPatch.cs b/Patches/MainMenuManagerPatch.cs index 2398bf49b..5bb9b714b 100644 --- a/Patches/MainMenuManagerPatch.cs +++ b/Patches/MainMenuManagerPatch.cs @@ -92,7 +92,7 @@ public static class MainMenuManagerPatch var bak = GameObject.Find("BackgroundTexture"); if (bak == null || !bak.active) return; var pos2 = bak.transform.position; - Vector3 lerp2 = Vector3.Lerp(pos2, new Vector3(pos2.x, 7.1f, pos2.z), Time.deltaTime * 1.4f); + Vector3 lerp2 = Vector3.Lerp(pos2, new(pos2.x, 7.1f, pos2.z), Time.deltaTime * 1.4f); bak.transform.position = lerp2; if (pos2.y > 7f) ShowedBak = true; } @@ -115,9 +115,9 @@ public static class MainMenuManagerPatch { GitHubButton = CreateButton( "GitHubButton", - new Vector3(-2.3f, -1.3f, 1f), - new Color32(153, 153, 153, byte.MaxValue), - new Color32(209, 209, 209, byte.MaxValue), + new(-2.3f, -1.3f, 1f), + new(153, 153, 153, byte.MaxValue), + new(209, 209, 209, byte.MaxValue), () => Application.OpenURL("https://github.com/Gurge44/EndlessHostRoles"), Translator.GetString("GitHub")); //"GitHub" } @@ -129,9 +129,9 @@ public static class MainMenuManagerPatch { DiscordButton = CreateButton( "DiscordButton", - new Vector3(-0.5f, -1.3f, 1f), - new Color32(88, 101, 242, byte.MaxValue), - new Color32(148, 161, byte.MaxValue, byte.MaxValue), + new(-0.5f, -1.3f, 1f), + new(88, 101, 242, byte.MaxValue), + new(148, 161, byte.MaxValue, byte.MaxValue), () => Application.OpenURL("https://discord.com/invite/m3ayxfumC8"), Translator.GetString("Discord")); //"Discord" } @@ -143,9 +143,9 @@ public static class MainMenuManagerPatch { WebsiteButton = CreateButton( "WebsiteButton", - new Vector3(1.3f, -1.3f, 1f), - new Color32(251, 81, 44, byte.MaxValue), - new Color32(211, 77, 48, byte.MaxValue), + new(1.3f, -1.3f, 1f), + new(251, 81, 44, byte.MaxValue), + new(211, 77, 48, byte.MaxValue), () => Application.OpenURL("https://sites.google.com/view/ehr-au"), Translator.GetString("Website")); //"Website" } diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index 141942a01..b709d3cb8 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -746,7 +746,8 @@ class MeetingHudStartPatch { var pc = Utils.GetPlayerById(pva.TargetPlayerId); if (pc == null) continue; - var RoleTextData = Utils.GetRoleText(PlayerControl.LocalPlayer.PlayerId, pc.PlayerId); + bool shouldSeeTargetAddons = new[] { PlayerControl.LocalPlayer, pc }.All(x => x.Is(Team.Impostor)); + var RoleTextData = Utils.GetRoleText(PlayerControl.LocalPlayer.PlayerId, pc.PlayerId, shouldSeeTargetAddons); var roleTextMeeting = Object.Instantiate(pva.NameText, pva.NameText.transform, true); roleTextMeeting.transform.localPosition = new(0f, -0.18f, 0f); roleTextMeeting.fontSize = 1.4f; @@ -807,7 +808,7 @@ class MeetingHudStartPatch Logger.Info("The ship has " + (Options.SyncedButtonCount.GetFloat() - Options.UsedButtonCount) + " buttons left", "SyncButtonMode"); } - if (AntiBlackout.OverrideExiledPlayer && MeetingStates.FirstMeeting) + if (AntiBlackout.OverrideExiledPlayer && (MeetingStates.FirstMeeting || Main.RealOptionsData.GetInt(Int32OptionNames.NumImpostors) > 1)) { LateTask.New(() => { Utils.SendMessage(GetString("Warning.OverrideExiledPlayer"), 255, Utils.ColorString(Color.red, GetString("DefaultSystemMessageTitle"))); }, 5f, "Warning OverrideExiledPlayer"); } diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs index fdc6a8aa2..5ccd2872c 100644 --- a/Patches/OnGameStartedPatch.cs +++ b/Patches/OnGameStartedPatch.cs @@ -742,7 +742,7 @@ internal class SelectRolesPatch addonList.ForEach(x => state.SetSubRole(x)); } - if (state.MainRole.IsImpostor()) state.SubRoles.RemoveAll(x => x.IsImpOnlyAddon()); + if (!state.MainRole.IsImpostor()) state.SubRoles.RemoveAll(x => x.IsImpOnlyAddon()); } foreach (var pair in Main.PlayerStates) diff --git a/Patches/PetActionsPatch.cs b/Patches/PetActionsPatch.cs index 4563dc491..15986359b 100644 --- a/Patches/PetActionsPatch.cs +++ b/Patches/PetActionsPatch.cs @@ -19,25 +19,20 @@ class LocalPetPatch public static bool Prefix(PlayerControl __instance) { - if (!Options.UsePets.GetBool() && Options.CurrentGameMode != CustomGameMode.CaptureTheFlag) return true; + if (!Options.UsePets.GetBool()) return true; if (!(AmongUsClient.Instance.AmHost && AmongUsClient.Instance.AmClient)) return true; if (GameStates.IsLobby || !__instance.IsAlive()) return true; if (__instance.petting) return true; __instance.petting = true; - bool ctf = Options.CurrentGameMode == CustomGameMode.CaptureTheFlag; - if (ctf) goto Skip; - if (!LastProcess.ContainsKey(__instance.PlayerId)) LastProcess.TryAdd(__instance.PlayerId, Utils.TimeStamp - 2); if (LastProcess[__instance.PlayerId] + 1 >= Utils.TimeStamp) return true; - Skip: - ExternalRpcPetPatch.Prefix(__instance.MyPhysics, (byte)RpcCalls.Pet); LastProcess[__instance.PlayerId] = Utils.TimeStamp; - return ctf || !__instance.GetCustomRole().PetActivatedAbility(); + return !__instance.GetCustomRole().PetActivatedAbility(); } public static void Postfix(PlayerControl __instance) @@ -55,7 +50,7 @@ class ExternalRpcPetPatch public static void Prefix(PlayerPhysics __instance, [HarmonyArgument(0)] byte callID) { - if (GameStates.IsLobby || (!Options.UsePets.GetBool() && Options.CurrentGameMode != CustomGameMode.CaptureTheFlag) || !AmongUsClient.Instance.AmHost || (RpcCalls)callID != RpcCalls.Pet) return; + if (GameStates.IsLobby || !Options.UsePets.GetBool() || !AmongUsClient.Instance.AmHost || (RpcCalls)callID != RpcCalls.Pet) return; var pc = __instance.myPlayer; var physics = __instance; @@ -74,12 +69,6 @@ class ExternalRpcPetPatch && pc.GetCustomRole().PetActivatedAbility()) physics.CancelPet(); - if (Options.CurrentGameMode == CustomGameMode.CaptureTheFlag) - { - CTFManager.OnPet(pc); - return; - } - if (!LastProcess.ContainsKey(pc.PlayerId)) LastProcess.TryAdd(pc.PlayerId, Utils.TimeStamp - 2); if (LastProcess[pc.PlayerId] + 1 >= Utils.TimeStamp) return; LastProcess[pc.PlayerId] = Utils.TimeStamp; diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index f4cb592f4..ed43b8ff8 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -759,6 +759,9 @@ class ShapeshiftPatch isSSneeded = false; } + if (Options.CurrentGameMode == CustomGameMode.CaptureTheFlag) + CTFManager.TryPickUpFlag(shapeshifter); + var role = shapeshifter.GetCustomRole(); bool forceCancel = role.ForceCancelShapeshift(); bool unshiftTrigger = role.SimpleAbilityTrigger() && Options.UseUnshiftTrigger.GetBool() && (!role.IsNeutral() || Options.UseUnshiftTriggerForNKs.GetBool()); @@ -806,12 +809,7 @@ class ShapeshiftPatch if (unshiftTrigger) { - var rndTarget = Main.AllAlivePlayerControls.Without(shapeshifter).RandomElement(); - var outfit = shapeshifter.Data.DefaultOutfit; - shapeshifter.RpcShapeshift(rndTarget, false); - Main.CheckShapeshift[shapeshifter.PlayerId] = false; - RpcChangeSkin(shapeshifter, outfit); - NotifyRoles(SpecifySeer: shapeshifter, SpecifyTarget: shapeshifter, NoCache: true); + shapeshifter.CheckAndSetUnshiftState(force: true); shapeshifter.AddAbilityCD(); } @@ -1224,7 +1222,8 @@ class FixedUpdatePatch { var player = __instance; var playerId = player.PlayerId; - var localPlayer = player.PlayerId == PlayerControl.LocalPlayer.PlayerId; // Updates that are independent of the player are only executed for the local player. + var lpId = PlayerControl.LocalPlayer.PlayerId; + var localPlayer = player.PlayerId == lpId; // Updates that are independent of the player are only executed for the local player. bool lowLoad = false; if (Options.LowLoadMode.GetBool()) @@ -1437,7 +1436,9 @@ class FixedUpdatePatch if (GameStates.IsInGame) { - var RoleTextData = GetRoleText(PlayerControl.LocalPlayer.PlayerId, playerId); + bool shouldSeeTargetAddons = new[] { PlayerControl.LocalPlayer, player }.All(x => x.Is(Team.Impostor)); + + var RoleTextData = GetRoleText(lpId, playerId, shouldSeeTargetAddons); RoleText.text = RoleTextData.Item1; RoleText.color = RoleTextData.Item2; @@ -1448,7 +1449,7 @@ class FixedUpdatePatch if (!PlayerControl.LocalPlayer.Data.IsDead && PlayerControl.LocalPlayer.IsRevealedPlayer(__instance) && __instance.Is(CustomRoles.Trickster)) { - RoleText.text = Farseer.RandomRole[PlayerControl.LocalPlayer.PlayerId]; + RoleText.text = Farseer.RandomRole[lpId]; RoleText.text += Farseer.GetTaskState(); } @@ -1997,7 +1998,7 @@ class CoEnterVentPatch Circumvent.OnCoEnterVent(__instance, id); } - if (__instance.myPlayer.GetCustomRole().GetDYRole() == RoleTypes.Impostor && !Main.PlayerStates[__instance.myPlayer.PlayerId].Role.CanUseImpostorVentButton(__instance.myPlayer) && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.HideAndSeek && !__instance.myPlayer.Is(CustomRoles.Nimble) && !__instance.myPlayer.Is(CustomRoles.Bloodlust)) + if (__instance.myPlayer.GetCustomRole().IsDesyncRole() && !Main.PlayerStates[__instance.myPlayer.PlayerId].Role.CanUseImpostorVentButton(__instance.myPlayer) && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.HideAndSeek && !__instance.myPlayer.Is(CustomRoles.Nimble) && !__instance.myPlayer.Is(CustomRoles.Bloodlust)) { LateTask.New(() => __instance.RpcBootFromVent(id), 0.5f, "CannotUseVentBootFromVent"); } diff --git a/Patches/SabotageSystemPatch.cs b/Patches/SabotageSystemPatch.cs index 68a4a837d..d25060be2 100644 --- a/Patches/SabotageSystemPatch.cs +++ b/Patches/SabotageSystemPatch.cs @@ -168,18 +168,11 @@ public static class MushroomMixupSabotageSystemPatch // After MushroomMixup sabotage, shapeshift cooldown sets to 0 foreach (var pc in Main.AllAlivePlayerControls) { - // Reset Ability Cooldown To Default For Alive Players + // Reset Ability Cooldown To Default For Living Players pc.RpcResetAbilityCooldown(); // Redo Unshift Trigger due to mushroom mixup breaking it - if (pc.GetCustomRole().SimpleAbilityTrigger() && Options.UseUnshiftTrigger.GetBool() && (!pc.IsNeutralKiller() || Options.UseUnshiftTriggerForNKs.GetBool())) - { - var target = Main.AllAlivePlayerControls.Without(pc).RandomElement(); - var outfit = pc.Data.DefaultOutfit; - pc.RpcShapeshift(target, false); - Main.CheckShapeshift[pc.PlayerId] = false; - Utils.RpcChangeSkin(pc, outfit); - } + pc.CheckAndSetUnshiftState(); } }, 1.2f, "Reset Ability Cooldown Arter Mushroom Mixup"); diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 79e16609b..c52e5193a 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -2651,6 +2651,7 @@ "AntiBlackOutLoggerSendInGame": "Because of an unknown error, the game will end to prevent black screen.", "AntiBlackOutRequestHostToForceEnd": "You were the reason of the black screen, you are asking the host to stop the game...", "AntiBlackOutHostRejectForceEnd": "You were the reason of the black screen, and the host is not going to end the game, we will disconnect you soon.", + "AntiBlackout.OverrideExiledPlayer": "To prevent black screens, the ejection screen shows a tied vote. (Players still get ejected, this is just visual.)", "GitHub": "GitHub", "Discord": "Discord", "Website": "Website", diff --git a/Roles/AddOns/Crewmate/Workhorse.cs b/Roles/AddOns/Crewmate/Workhorse.cs index d409c249d..9ffa8aaa1 100644 --- a/Roles/AddOns/Crewmate/Workhorse.cs +++ b/Roles/AddOns/Crewmate/Workhorse.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using AmongUs.GameOptions; using UnityEngine; using static EHR.Options; @@ -61,7 +60,7 @@ public class Workhorse : IAddon { if (!pc.IsAlive() || IsThisRole(pc.PlayerId)) return false; if (pc.Is(CustomRoles.Needy) || pc.Is(CustomRoles.Lazy) || pc.Is(CustomRoles.Bloodlust)) return false; - if (pc.GetCustomRole().GetDYRole() == RoleTypes.Impostor || pc.GetCustomRole().GetVNRole() is CustomRoles.Impostor or CustomRoles.Shapeshifter) return false; + if (pc.GetCustomRole().GetVNRole(checkDesyncRole: true) is CustomRoles.Impostor or CustomRoles.Shapeshifter) return false; var taskState = pc.GetTaskState(); if (taskState.CompletedTasksCount + 1 < taskState.AllTasksCount) return false; diff --git a/Roles/Crewmate/Analyst.cs b/Roles/Crewmate/Analyst.cs index 1264ed06e..0209a10c0 100644 --- a/Roles/Crewmate/Analyst.cs +++ b/Roles/Crewmate/Analyst.cs @@ -55,17 +55,15 @@ namespace EHR.Crewmate private static string GetRoleBasis(CustomRoles role) => SeeRoleBasis.GetBool() - ? role.GetDYRole() == RoleTypes.Impostor - ? Utils.ColorString(Utils.GetRoleColor(CustomRoles.Impostor), GetString("Impostor")) - : role.GetVNRole() switch - { - CustomRoles.Impostor => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Impostor), GetString("Impostor")), - CustomRoles.Shapeshifter => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Speedrunner), GetString("Shapeshifter")), - CustomRoles.Crewmate => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Crewmate), GetString("Crewmate")), - CustomRoles.Engineer => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Autocrat), GetString("Engineer")), - CustomRoles.Scientist => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Doctor), GetString("Scientist")), - _ => string.Empty - } + ? role.GetVNRole(checkDesyncRole: true) switch + { + CustomRoles.Impostor => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Impostor), GetString("Impostor")), + CustomRoles.Shapeshifter => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Speedrunner), GetString("Shapeshifter")), + CustomRoles.Crewmate => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Crewmate), GetString("Crewmate")), + CustomRoles.Engineer => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Autocrat), GetString("Engineer")), + CustomRoles.Scientist => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Doctor), GetString("Scientist")), + _ => string.Empty + } : string.Empty; private static int GetKillCount(byte id) => SeeKillCount.GetBool() ? Main.PlayerStates.Count(x => x.Value.GetRealKiller() == id) : 0; diff --git a/Roles/Crewmate/President.cs b/Roles/Crewmate/President.cs index 5c8d8226f..efa8bcff7 100644 --- a/Roles/Crewmate/President.cs +++ b/Roles/Crewmate/President.cs @@ -209,7 +209,7 @@ namespace EHR.Crewmate if (!target.Is(CustomRoleTypes.Crewmate) || target.IsConverted()) return true; CustomRoles role = GovernmentRecruitRoles[DecreeSettings[Decree.GovernmentRecruiting][1].GetValue()]; - if (role.GetDYRole() == RoleTypes.Impostor && target.GetRoleTypes() != RoleTypes.Impostor) role = CustomRoles.NiceGuesser; + if (role.IsDesyncRole() && target.GetRoleTypes() != role.GetDYRole()) role = CustomRoles.NiceGuesser; target.RpcSetCustomRole(role); Utils.SendMessage("\n", target.PlayerId, Translator.GetString("President.Recruit.TargetNotifyMessage")); Main.DontCancelVoteList.Add(voter.PlayerId); diff --git a/Roles/Crewmate/Psychic.cs b/Roles/Crewmate/Psychic.cs index 3d717febc..0b2e4d51a 100644 --- a/Roles/Crewmate/Psychic.cs +++ b/Roles/Crewmate/Psychic.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using AmongUs.GameOptions; using EHR.Impostor; using EHR.Modules; using Hazel; @@ -78,7 +77,7 @@ public class Psychic : RoleBase { if (target == null || seer == null) return false; if (Main.PlayerStates[seer.PlayerId].Role is not Psychic ph) return false; - if (seer.Is(CustomRoles.Madmate)) return target.GetCustomRole().IsNeutral() || target.GetCustomRole().GetDYRole() == RoleTypes.Impostor; + if (seer.Is(CustomRoles.Madmate)) return target.GetCustomRole().IsNeutral() || target.GetCustomRole().GetCrewmateRoleCategory() == RoleOptionType.Crewmate_Killing; return ph.RedPlayer != null && ph.RedPlayer.Contains(target.PlayerId); } diff --git a/Roles/Impostor/Changeling.cs b/Roles/Impostor/Changeling.cs index 558d65ddf..ecd8a1d29 100644 --- a/Roles/Impostor/Changeling.cs +++ b/Roles/Impostor/Changeling.cs @@ -49,11 +49,11 @@ namespace EHR.Impostor IEnumerable result = AvailableRoles.GetValue() switch { 0 => allRoles, - 1 => allRoles.Where(x => x.GetVNRole() is CustomRoles.Impostor or CustomRoles.ImpostorEHR), - 2 => allRoles.Where(x => x.GetVNRole() is CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR), + 1 => allRoles.Where(x => x.GetVNRole(checkDesyncRole: true) is CustomRoles.Impostor or CustomRoles.ImpostorEHR), + 2 => allRoles.Where(x => x.GetVNRole(checkDesyncRole: true) is CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR), 3 => allRoles.Where(x => x.GetMode() != 0), - 4 => allRoles.Where(x => x.GetVNRole() is CustomRoles.Impostor or CustomRoles.ImpostorEHR && x.GetMode() != 0), - 5 => allRoles.Where(x => x.GetVNRole() is CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR && x.GetMode() != 0), + 4 => allRoles.Where(x => x.GetVNRole(checkDesyncRole: true) is CustomRoles.Impostor or CustomRoles.ImpostorEHR && x.GetMode() != 0), + 5 => allRoles.Where(x => x.GetVNRole(checkDesyncRole: true) is CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR && x.GetMode() != 0), _ => allRoles }; diff --git a/Roles/Impostor/Councillor.cs b/Roles/Impostor/Councillor.cs index 4a2f99e87..df4dfdedf 100644 --- a/Roles/Impostor/Councillor.cs +++ b/Roles/Impostor/Councillor.cs @@ -24,8 +24,8 @@ public class Councillor : RoleBase private static OptionItem KillCooldown; public static OptionItem CouncillorAbilityUseGainWithEachKill; private static Dictionary MeetingKillLimit = []; - private byte CouncillorId; + private byte CouncillorId; public override bool IsEnable => playerIdList.Count > 0; public override void SetupCustomOption() diff --git a/Roles/Neutral/Doppelganger.cs b/Roles/Neutral/Doppelganger.cs index 532495413..f5e0876c3 100644 --- a/Roles/Neutral/Doppelganger.cs +++ b/Roles/Neutral/Doppelganger.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using EHR.Impostor; using EHR.Modules; @@ -201,17 +202,25 @@ public class Doppelganger : RoleBase public override void OnReportDeadBody() { - if (ResetMode.GetValue() == 1 && TotalSteals[DGId] > 0) + try { - var pc = Utils.GetPlayerById(DGId); - var currentTarget = Main.AllPlayerControls.FirstOrDefault(x => x.GetRealName() == DoppelVictim[pc.PlayerId]); - if (currentTarget != null) + if (ResetMode.GetValue() == 1 && TotalSteals[DGId] > 0) { - RpcChangeSkin(currentTarget, DoppelPresentSkin[currentTarget.PlayerId]); - RpcChangeSkin(pc, DoppelDefaultSkin[pc.PlayerId]); - DoppelVictim[pc.PlayerId] = string.Empty; + var pc = Utils.GetPlayerById(DGId); + if (pc == null) return; + var currentTarget = Main.AllPlayerControls.FirstOrDefault(x => x?.GetRealName() == DoppelVictim[pc.PlayerId]); + if (currentTarget != null) + { + RpcChangeSkin(currentTarget, DoppelPresentSkin[currentTarget.PlayerId]); + RpcChangeSkin(pc, DoppelDefaultSkin[pc.PlayerId]); + DoppelVictim[pc.PlayerId] = string.Empty; + } } } + catch (Exception e) + { + Utils.ThrowException(e); + } } public override void OnFixedUpdate(PlayerControl pc) diff --git a/Roles/Neutral/Jackal.cs b/Roles/Neutral/Jackal.cs index 2116f1129..433614a87 100644 --- a/Roles/Neutral/Jackal.cs +++ b/Roles/Neutral/Jackal.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using AmongUs.GameOptions; @@ -9,7 +10,7 @@ namespace EHR.Neutral; public class Jackal : RoleBase { private const int Id = 12100; - private static List PlayerIdList = []; + public static List Instances = []; private static OptionItem KillCooldown; private static OptionItem CanVent; @@ -30,7 +31,7 @@ public class Jackal : RoleBase public static bool On; private byte SidekickId; - public override bool IsEnable => PlayerIdList.Count > 0; + public override bool IsEnable => Instances.Count > 0; public override void SetupCustomOption() { @@ -80,14 +81,14 @@ public class Jackal : RoleBase public override void Init() { - PlayerIdList = []; + Instances = []; On = false; } public override void Add(byte playerId) { On = true; - PlayerIdList.Add(playerId); + Instances.Add(this); playerId.SetAbilityUseLimit(1); SidekickId = byte.MaxValue; } @@ -148,11 +149,24 @@ public class Jackal : RoleBase public override void OnFixedUpdate(PlayerControl pc) { - if (!SKPromotesToJackal.GetBool() || pc.IsAlive()) return; - var sk = SidekickId.GetPlayer(); - if (sk == null || !sk.Is(CustomRoles.Sidekick)) return; - sk.RpcSetCustomRole(CustomRoles.Jackal); - if (!PromotedSKCanRecruit.GetBool()) sk.SetAbilityUseLimit(0); + if (pc.IsAlive()) return; + PromoteSidekick(); + } + + public void PromoteSidekick() + { + try + { + if (!SKPromotesToJackal.GetBool()) return; + var sk = SidekickId.GetPlayer(); + if (sk == null || !sk.Is(CustomRoles.Sidekick)) return; + sk.RpcSetCustomRole(CustomRoles.Jackal); + if (!PromotedSKCanRecruit.GetBool()) sk.SetAbilityUseLimit(0); + } + catch (Exception e) + { + Utils.ThrowException(e); + } } }