From ca3d3939ffd210686139b3fb5087270a2afe0412 Mon Sep 17 00:00:00 2001 From: Gurge44 Date: Tue, 30 Jul 2024 19:37:15 +0200 Subject: [PATCH] .... --- CustomRoles.cs | 1 + GameMode/SoloKombatManager.cs | 39 ++++---- Modules/CustomNetObject.cs | 10 ++ Modules/CustomRolesHelper.cs | 58 ++++++----- Modules/DevManager.cs | 1 + Modules/ExtendedPlayerControl.cs | 2 - Modules/OptionHolder.cs | 8 +- Modules/Utils.cs | 3 +- Patches/CheckGameEndPatch.cs | 12 ++- Patches/GameOptionsMenuPatch.cs | 21 +++- Patches/HudPatch.cs | 34 +------ Patches/SabotageSystemPatch.cs | 1 + Patches/ShipStatusPatch.cs | 1 + README.md | 2 +- Resources/Lang/en_US.json | 21 +++- Roles/Crewmate/Bodyguard.cs | 2 + Roles/Crewmate/Car.cs | 71 ++++++++++---- Roles/Crewmate/Catcher.cs | 159 +++++++++++++++++++++++++++++++ Roles/Crewmate/Judge.cs | 5 +- Roles/Neutral/Executioner.cs | 6 +- Roles/Neutral/Jackal.cs | 3 - main.cs | 8 ++ 22 files changed, 350 insertions(+), 118 deletions(-) create mode 100644 Roles/Crewmate/Catcher.cs diff --git a/CustomRoles.cs b/CustomRoles.cs index 1d8f0ae3a..db96b18ed 100644 --- a/CustomRoles.cs +++ b/CustomRoles.cs @@ -137,6 +137,7 @@ public enum CustomRoles Bodyguard, CameraMan, Car, + Catcher, CyberStar, // Celebrity Chameleon, Chef, diff --git a/GameMode/SoloKombatManager.cs b/GameMode/SoloKombatManager.cs index c561c3277..dd5aee1eb 100644 --- a/GameMode/SoloKombatManager.cs +++ b/GameMode/SoloKombatManager.cs @@ -19,7 +19,7 @@ internal static class SoloKombatManager public static Dictionary PlayerDF = []; private static Dictionary OriginalSpeed = []; - private static Dictionary KBScore = []; + public static Dictionary KBScore = []; public static int RoundTime; private static readonly Dictionary NameNotify = []; @@ -219,7 +219,7 @@ internal static class SoloKombatManager if (!target.SoloAlive()) { - OnPlyaerDead(target); + OnPlayerDead(target); OnPlayerKill(killer); } @@ -254,25 +254,21 @@ internal static class SoloKombatManager private static void PlayerRandomSpwan(PlayerControl pc) { - SpawnMap map; - switch (Main.NormalOptions.MapId) + SpawnMap map = Main.CurrentMap switch { - case 0: - map = new SkeldSpawnMap(); - map.RandomTeleport(pc); - break; - case 1: - map = new MiraHQSpawnMap(); - map.RandomTeleport(pc); - break; - case 2: - map = new PolusSpawnMap(); - map.RandomTeleport(pc); - break; - } + MapNames.Skeld => new SkeldSpawnMap(), + MapNames.Mira => new MiraHQSpawnMap(), + MapNames.Polus => new PolusSpawnMap(), + MapNames.Airship => new AirshipSpawnMap(), + MapNames.Fungle => new FungleSpawnMap(), + MapNames.Dleks => new DleksSpawnMap(), + _ => null + }; + + map?.RandomTeleport(pc); } - private static void OnPlyaerDead(PlayerControl target) + private static void OnPlayerDead(PlayerControl target) { OriginalSpeed.Remove(target.PlayerId); OriginalSpeed.Add(target.PlayerId, Main.AllPlayerSpeed[target.PlayerId]); @@ -296,7 +292,7 @@ internal static class SoloKombatManager float addRate = IRandom.Instance.Next(3, 5 + GetRankOfScore(killer.PlayerId)) / 100f; addRate *= KB_KillBonusMultiplier.GetFloat(); float addin; - switch (IRandom.Instance.Next(0, 3)) + switch (IRandom.Instance.Next(0, 4)) { case 0: addin = PlayerHPMax[killer.PlayerId] * addRate; @@ -313,6 +309,11 @@ internal static class SoloKombatManager PlayerATK[killer.PlayerId] += addin; AddNameNotify(killer, string.Format(Translator.GetString("KB_Buff_ATK"), addin.ToString("0.0#####"))); break; + case 3: + addin = Math.Max(PlayerDF[killer.PlayerId], 1f) * addRate; + PlayerDF[killer.PlayerId] += addin; + AddNameNotify(killer, string.Format(Translator.GetString("KB_Buff_DF"), addin.ToString("0.0#####"))); + break; } } diff --git a/Modules/CustomNetObject.cs b/Modules/CustomNetObject.cs index 13221a6b1..2b0d737a9 100644 --- a/Modules/CustomNetObject.cs +++ b/Modules/CustomNetObject.cs @@ -375,6 +375,7 @@ namespace EHR Toilet, BlackHole, SprayedArea, + CatcherTrap } internal sealed class Explosion : CustomNetObject @@ -555,4 +556,13 @@ namespace EHR Main.AllAlivePlayerControls.ExceptBy(visibleList, x => x.PlayerId).Do(Hide); } } + + internal sealed class CatcherTrap : CustomNetObject + { + public CatcherTrap(Vector2 position, PlayerControl catcher) + { + CreateNetObject("\u2588\u2588<#ccffda>\u2588<#ccffda>\u2588\u2588\u2588
\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588\u2588
<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588
<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588
\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588<#ccffda>\u2588\u2588
\u2588\u2588<#ccffda>\u2588<#ccffda>\u2588\u2588\u2588
", position, CustomObjectTypes.CatcherTrap); + Main.AllAlivePlayerControls.Without(catcher).Do(Hide); + } + } } \ No newline at end of file diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 32b5d922d..562e0e34a 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -189,6 +189,7 @@ internal static class CustomRolesHelper CustomRoles.Philantropist => CustomRoles.Crewmate, CustomRoles.Tornado => CustomRoles.Crewmate, CustomRoles.Druid => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer, + CustomRoles.Catcher => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer, CustomRoles.Insight => CustomRoles.Crewmate, CustomRoles.Tunneler => CustomRoles.Crewmate, CustomRoles.Detour => CustomRoles.Crewmate, @@ -562,39 +563,37 @@ internal static class CustomRolesHelper public static bool IsNE(this CustomRoles role) => role is CustomRoles.Jester or - CustomRoles.Gamer or - CustomRoles.Arsonist or + CustomRoles.God or CustomRoles.Executioner or CustomRoles.Doomsayer or + CustomRoles.Mario or + CustomRoles.Terrorist or + CustomRoles.Collector or + CustomRoles.Vulture or + CustomRoles.Workaholic or + CustomRoles.Deathknight or CustomRoles.Innocent; public static bool IsNB(this CustomRoles role) => role is CustomRoles.Opportunist or CustomRoles.Lawyer or CustomRoles.Amnesiac or - CustomRoles.God or CustomRoles.Postman or CustomRoles.SchrodingersCat or CustomRoles.Predator or CustomRoles.Pursuer or CustomRoles.FFF or + CustomRoles.Revolutionist or + CustomRoles.Impartial or + CustomRoles.Shifter or CustomRoles.Sunnyboy or CustomRoles.Maverick or CustomRoles.Romantic or CustomRoles.VengefulRomantic or - CustomRoles.Totocalcio; - - public static bool IsNC(this CustomRoles role) => role is - CustomRoles.Mario or - CustomRoles.Shifter or - CustomRoles.Terrorist or - CustomRoles.Revolutionist or - CustomRoles.Impartial or - CustomRoles.Vulture or CustomRoles.Phantasm or - CustomRoles.Workaholic or - CustomRoles.Collector or - CustomRoles.Provocateur; + CustomRoles.Provocateur or + CustomRoles.SoulHunter or + CustomRoles.Totocalcio; public static bool IsCK(this CustomRoles role) => role is CustomRoles.SwordsMan or @@ -1150,8 +1149,8 @@ internal static class CustomRolesHelper { if (role.IsImpostor()) return RoleOptionType.Impostor; if (role.IsCrewmate()) return role.GetDYRole(load: true) == RoleTypes.Impostor ? RoleOptionType.Crewmate_ImpostorBased : RoleOptionType.Crewmate_Normal; - if (role.IsNeutral(check: true)) return role.IsNK(check: true) ? RoleOptionType.Neutral_Killing : RoleOptionType.Neutral_NonKilling; - return RoleOptionType.Crewmate_Normal; + if (role.IsNeutral(check: true)) return role.IsNK(check: true) ? RoleOptionType.Neutral_Killing : role.IsNB() ? RoleOptionType.Neutral_Benign : RoleOptionType.Neutral_Evil; + return RoleOptionType.Crewmate_Miscellaneous; } public static Color GetRoleOptionTypeColor(this RoleOptionType type) => type switch @@ -1159,7 +1158,8 @@ internal static class CustomRolesHelper RoleOptionType.Impostor => Palette.ImpostorRed, RoleOptionType.Crewmate_Normal => Palette.CrewmateBlue, RoleOptionType.Crewmate_ImpostorBased => Utils.GetRoleColor(CustomRoles.Sheriff), - RoleOptionType.Neutral_NonKilling => Utils.GetRoleColor(CustomRoles.Sprayer), + RoleOptionType.Neutral_Evil => Utils.GetRoleColor(CustomRoles.Sprayer), + RoleOptionType.Neutral_Benign => Utils.GetRoleColor(CustomRoles.TaskManager), RoleOptionType.Neutral_Killing => Utils.GetRoleColor(CustomRoles.Traitor), _ => Utils.GetRoleColor(CustomRoles.SwordsMan) }; @@ -1169,7 +1169,8 @@ internal static class CustomRolesHelper RoleOptionType.Impostor => TabGroup.ImpostorRoles, RoleOptionType.Crewmate_Normal => TabGroup.CrewmateRoles, RoleOptionType.Crewmate_ImpostorBased => TabGroup.CrewmateRoles, - RoleOptionType.Neutral_NonKilling => TabGroup.NeutralRoles, + RoleOptionType.Neutral_Evil => TabGroup.NeutralRoles, + RoleOptionType.Neutral_Benign => TabGroup.NeutralRoles, RoleOptionType.Neutral_Killing => TabGroup.NeutralRoles, _ => TabGroup.OtherRoles }; @@ -1179,7 +1180,8 @@ internal static class CustomRolesHelper RoleOptionType.Impostor => SimpleRoleOptionType.Impostor, RoleOptionType.Crewmate_Normal => SimpleRoleOptionType.Crewmate, RoleOptionType.Crewmate_ImpostorBased => SimpleRoleOptionType.Crewmate, - RoleOptionType.Neutral_NonKilling => SimpleRoleOptionType.NNK, + RoleOptionType.Neutral_Evil => SimpleRoleOptionType.NNK, + RoleOptionType.Neutral_Benign => SimpleRoleOptionType.NNK, RoleOptionType.Neutral_Killing => SimpleRoleOptionType.NK, _ => SimpleRoleOptionType.Crewmate }; @@ -1219,10 +1221,18 @@ internal static class CustomRolesHelper #pragma warning restore IDE0079 public enum RoleOptionType { - Impostor, - Crewmate_Normal, - Crewmate_ImpostorBased, - Neutral_NonKilling, + Impostor_Killing, + Impostor_Support, + Impostor_Concealing, + Impostor_Miscellaneous, + Crewmate_Investigate, + Crewmate_Support, + Crewmate_Power, + Crewmate_Killing, + Crewmate_Chaos, + Crewmate_Miscellaneous, + Neutral_Evil, + Neutral_Benign, Neutral_Killing } diff --git a/Modules/DevManager.cs b/Modules/DevManager.cs index 7e7b1a4f0..9dbc8017c 100644 --- a/Modules/DevManager.cs +++ b/Modules/DevManager.cs @@ -104,6 +104,7 @@ public static class DevManager new(code: "cleardress#6310", color: "#ffffff", tag: "<#00BFFF>一<#48D1CC>个<#7CFC00>热狗<#32CD32>uwu", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "ABoringCat"), new(code: "rainypearl#9545", color: "#A020F0", tag: "YouTuber", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "bielModzs"), new(code: "kiltedbill#4145", color: "null", tag: "null", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "tsaki84"), + new(code: "onlyfax#3941", color: "#ff0000", tag: "YouTuber", isUp: true, isDev: false, deBug: false, colorCmd: false, upName: "The Nick AG"), // Sponsor new(code: "recentduct#6068", color: "#FF00FF", tag: "高冷男模法师", isUp: false, isDev: false, colorCmd: false, deBug: true, upName: null), diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 57a57af63..727dcb370 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -729,7 +729,6 @@ static class ExtendedPlayerControl public static bool CanUseSabotage(this PlayerControl pc) { if (!pc.IsAlive() || pc.Data.Role.Role == RoleTypes.GuardianAngel) return false; - return Main.PlayerStates.TryGetValue(pc.PlayerId, out var state) && state.Role.CanUseSabotage(pc); } @@ -972,7 +971,6 @@ static class ExtendedPlayerControl public static bool IsNeutralKiller(this PlayerControl player) => player.Is(CustomRoles.Bloodlust) || player.GetCustomRole().IsNK(); public static bool IsNeutralBenign(this PlayerControl player) => player.GetCustomRole().IsNB(); public static bool IsNeutralEvil(this PlayerControl player) => player.GetCustomRole().IsNE(); - public static bool IsNeutralChaos(this PlayerControl player) => player.GetCustomRole().IsNC(); public static bool IsSnitchTarget(this PlayerControl player) => player.Is(CustomRoles.Bloodlust) || player.GetCustomRole().IsSnitchTarget(); public static bool IsMadmate(this PlayerControl player) => player.Is(CustomRoles.Madmate) || player.GetCustomRole().IsMadmate(); diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index 43802db5a..1112bb0da 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -541,6 +541,7 @@ public static class Options public static OptionItem DisableAirshipViewingDeckLightsPanel; public static OptionItem DisableAirshipGapRoomLightsPanel; public static OptionItem DisableAirshipCargoLightsPanel; + public static OptionItem NKWinsBySabotageIfNoImpAlive; // Guesser Mode public static OptionItem GuesserMode; @@ -1085,7 +1086,8 @@ public static class Options var categorySuffix = roleClasses.Key switch { RoleOptionType.Neutral_Killing => "NK", - RoleOptionType.Neutral_NonKilling => "NNK", + RoleOptionType.Neutral_Evil => "NNK", + RoleOptionType.Neutral_Benign => "NNK", _ => string.Empty }; new TextOptionItem(titleId, $"ROT.Basic{categorySuffix}", tab) @@ -1521,6 +1523,10 @@ public static class Options .SetParent(LightsOutSpecialSettings) .SetGameMode(CustomGameMode.Standard); + NKWinsBySabotageIfNoImpAlive = new BooleanOptionItem(22520, "NKWinsBySabotageIfNoImpAlive", false, TabGroup.GameSettings) + .SetColor(new Color32(243, 96, 96, byte.MaxValue)) + .SetGameMode(CustomGameMode.Standard); + LoadingPercentage = 73; diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 9183c4c8b..978dbbacd 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -1774,7 +1774,7 @@ public static class Utils return add; } - public static string ColoredPlayerName(this byte id) => ColorString(Main.PlayerColors.GetValueOrDefault(id, Color.white), Main.AllPlayerNames.GetValueOrDefault(id, $"Someone (ID {id})")); + public static string ColoredPlayerName(this byte id) => ColorString(Main.PlayerColors.GetValueOrDefault(id, Color.white), Main.AllPlayerNames.GetValueOrDefault(id, GetPlayerById(id)?.GetRealName() ?? $"Someone (ID {id})")); public static PlayerControl GetPlayerById(int PlayerId, bool fast = true) { @@ -2426,6 +2426,7 @@ public static class Utils CustomRoles.Tornado => Tornado.TornadoCooldown.GetInt(), CustomRoles.Sentinel => Sentinel.PatrolCooldown.GetInt(), CustomRoles.Druid => Druid.VentCooldown.GetInt(), + CustomRoles.Catcher => Catcher.AbilityCooldown.GetInt(), CustomRoles.Sentry => EHR.Impostor.Sentry.ShowInfoCooldown.GetInt(), CustomRoles.ToiletMaster => ToiletMaster.AbilityCooldown.GetInt(), CustomRoles.Sniper => Options.DefaultShapeshiftCooldown.GetInt(), diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index b7fba6fcc..84c5a8cc2 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -55,12 +55,14 @@ class GameEndChecker Main.AllPlayerControls.Do(pc => Camouflage.RpcSetSkin(pc, ForceRevert: true, RevertToDefault: true, GameEnd: true)); - if (reason == GameOverReason.ImpostorBySabotage && (CustomRoles.Jackal.RoleExist() || CustomRoles.Sidekick.RoleExist()) && Jackal.CanWinBySabotageWhenNoImpAlive.GetBool() && !Main.AllAlivePlayerControls.Any(x => x.GetCustomRole().IsImpostorTeam())) + if (reason == GameOverReason.ImpostorBySabotage && Options.NKWinsBySabotageIfNoImpAlive.GetBool() && !Main.AllAlivePlayerControls.Any(x => x.IsImpostor()) && Main.AllAlivePlayerControls.Count(x => x.IsNeutralKiller()) == 1) { - reason = GameOverReason.ImpostorByKill; - WinnerIds.Clear(); - ResetAndSetWinner(CustomWinner.Jackal); - WinnerRoles.Add(CustomRoles.Jackal); + var winner = Main.AllAlivePlayerControls.First(x => x.IsNeutralKiller()); + var winnerRole = winner.GetCustomRole(); + + ResetAndSetWinner((CustomWinner)winnerRole); + WinnerRoles.Add(winnerRole); + WinnerIds.Add(winner.PlayerId); } switch (WinnerTeam) diff --git a/Patches/GameOptionsMenuPatch.cs b/Patches/GameOptionsMenuPatch.cs index a3fca4b32..0f8d7ac07 100644 --- a/Patches/GameOptionsMenuPatch.cs +++ b/Patches/GameOptionsMenuPatch.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Linq; using BepInEx.Unity.IL2CPP.Utils.Collections; +using EHR.Modules; using EHR.Patches; using HarmonyLib; using Il2CppSystem.Collections.Generic; @@ -603,6 +604,7 @@ public static class StringOptionPatch name = name.RemoveHtmlTags(); if (Options.UsePets.GetBool() && role.PetActivatedAbility()) name += Translator.GetString("SupportsPetIndicator"); if (!Options.UsePets.GetBool() && role.OnlySpawnsWithPets()) name += Translator.GetString("RequiresPetIndicator"); + name += GetGhostRoleTeam(); __instance.TitleText.fontWeight = FontWeight.Black; __instance.TitleText.outlineColor = new(255, 255, 255, 255); __instance.TitleText.outlineWidth = 0.04f; @@ -610,6 +612,23 @@ public static class StringOptionPatch __instance.TitleText.color = Color.white; name = $"{name}"; SetupHelpIcon(role, __instance); + + string GetGhostRoleTeam() + { + var instance = GhostRolesManager.CreateGhostRoleInstance(role); + if (instance == null) return string.Empty; + var team = instance.Team; + if ((int)team is 1 or 2 or 4) return Utils.ColorString(team.GetTeamColor(), Translator.GetString($"{team}")); + Team[] teams = (int)team switch + { + 3 => [Team.Impostor, Team.Neutral], + 5 => [Team.Impostor, Team.Crewmate], + 6 => [Team.Neutral, Team.Crewmate], + 7 => [Team.Impostor, Team.Neutral, Team.Crewmate], + _ => [] + }; + return $"{string.Join('/', teams.Select(x => Utils.ColorString(x.GetTeamColor(), Translator.GetString($"ShortTeamName.{x}"))))}"; + } } __instance.TitleText.text = name; @@ -917,10 +936,10 @@ public class GameSettingMenuPatch GameSettingsLabel.text = Translator.GetString($"Mode{Options.CurrentGameMode}").Split(':').Last().TrimStart(' '); if (IsRussian) { - GameSettingsLabel.transform.localScale = new(0.7f, 0.7f, 1f); GameSettingsLabel.transform.localPosition = new Vector3(-3.77f, 1.62f, -4); } + var GameSettingsLabelPos = GameSettingsLabel.transform.localPosition; var gmCycler = Object.Instantiate(GMinus, GameSettingsLabel.transform, true); diff --git a/Patches/HudPatch.cs b/Patches/HudPatch.cs index 411dd231c..bf515e765 100644 --- a/Patches/HudPatch.cs +++ b/Patches/HudPatch.cs @@ -644,42 +644,16 @@ class TaskPanelBehaviourPatch AllText += $"\r\n{GetString("PVP.RCO")}: {SoloKombatManager.PlayerHPReco[lpc.PlayerId]}"; AllText += "\r\n"; - Dictionary SummaryText = []; - foreach (var id in Main.PlayerStates.Keys) - { - string name = Main.AllPlayerNames[id].RemoveHtmlTags().Replace("\r\n", string.Empty); - string summary = $"{Utils.GetProgressText(id)} {Utils.ColorString(Main.PlayerColors[id], name)}"; - if (Utils.GetProgressText(id).Trim() == string.Empty) continue; - SummaryText[id] = summary; - } - - List<(int, byte)> list = []; - foreach (var id in Main.PlayerStates.Keys) list.Add((SoloKombatManager.GetRankOfScore(id), id)); - list.Sort(); - AllText = list.Where(x => SummaryText.ContainsKey(x.Item2)).Aggregate(AllText, (current, id) => current + "\r\n" + SummaryText[id.Item2]); - - AllText = $"{AllText}"; + AllText += Main.PlayerStates.Keys.OrderBy(SoloKombatManager.GetRankOfScore).Aggregate("", (s, x) => $"{s}\r\n{SoloKombatManager.GetRankOfScore(x)}. {x.ColoredPlayerName()} -{string.Format(GetString("KillCount"), SoloKombatManager.KBScore.GetValueOrDefault(x, 0))}"); + AllText += ""; break; case CustomGameMode.FFA: - Dictionary SummaryText2 = []; - foreach (var id in Main.PlayerStates.Keys) - { - string name = Main.AllPlayerNames[id].RemoveHtmlTags().Replace("\r\n", string.Empty); - string summary = $"{Utils.GetProgressText(id)} {Utils.ColorString(Main.PlayerColors[id], name)}"; - if (Utils.GetProgressText(id).Trim() == string.Empty) continue; - SummaryText2[id] = summary; - } - - List<(int, byte)> list2 = []; - foreach (var id in Main.PlayerStates.Keys) list2.Add((FFAManager.GetRankOfScore(id), id)); - list2.Sort(); - AllText = list2.Where(x => SummaryText2.ContainsKey(x.Item2)).Aggregate(AllText, (current, id) => current + "\r\n" + SummaryText2[id.Item2]); - - AllText = $"{AllText}"; + AllText += Main.PlayerStates.Keys.OrderBy(FFAManager.GetRankOfScore).Aggregate("", (s, x) => $"{s}\r\n{FFAManager.GetRankOfScore(x)}. {x.ColoredPlayerName()} -{string.Format(GetString("KillCount"), FFAManager.KillCount.GetValueOrDefault(x, 0))}"); + AllText += ""; break; case CustomGameMode.MoveAndStop: diff --git a/Patches/SabotageSystemPatch.cs b/Patches/SabotageSystemPatch.cs index f4b15063f..9c18a3a23 100644 --- a/Patches/SabotageSystemPatch.cs +++ b/Patches/SabotageSystemPatch.cs @@ -376,6 +376,7 @@ public static class SabotageSystemTypeRepairDamagePatch } if (player.Is(Team.Impostor) && !player.IsAlive() && Options.DeadImpCantSabotage.GetBool()) return false; + if (!player.Is(Team.Impostor) && !player.IsAlive()) return false; bool allow = player.GetCustomRole() switch { CustomRoles.Jackal when Jackal.CanSabotage.GetBool() => true, diff --git a/Patches/ShipStatusPatch.cs b/Patches/ShipStatusPatch.cs index a5c506d25..ddf11558d 100644 --- a/Patches/ShipStatusPatch.cs +++ b/Patches/ShipStatusPatch.cs @@ -131,6 +131,7 @@ class RepairSystemPatch } if (player.Is(Team.Impostor) && !player.IsAlive() && Options.DeadImpCantSabotage.GetBool()) return false; + if (!player.Is(Team.Impostor) && !player.IsAlive()) return false; return player.GetCustomRole() switch { CustomRoles.Jackal when Jackal.CanSabotage.GetBool() => true, diff --git a/README.md b/README.md index 03e1c9fd7..802e91f1a 100644 --- a/README.md +++ b/README.md @@ -389,8 +389,8 @@ - Dx (PT-BR) - PH_Gaming (PT-BR) - Tomix (PT-BR) -- aviiiv (PT-BR) - HyperAtill (RU) +- ABoringCat (ZH-CN) # License for the Custom Team Assigner application diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 141ad509f..898bed1e4 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -22,6 +22,9 @@ "TeamCrewmate": "Crewmate", "TeamMadmate": "Madmate", "Neutral": "Neutral", + "ShortTeamName.Impostor": "Imp", + "ShortTeamName.Crewmate": "Crew", + "ShortTeamName.Neutral": "Neut", "YouAreCrewmate": "TEAM: CREWMATES", "YouAreImpostor": ".", "YouAreNeutral": "TEAM: NEUTRAL", @@ -55,7 +58,8 @@ "ROT.MadMates": "★ Madmate Roles", "ROT.Crewmate_Normal": "★ Non-Killing Crewmate Roles", "ROT.Crewmate_ImpostorBased": "★ Crewmate Roles With Kill Buttons", - "ROT.Neutral_NonKilling": "★ Non-Killing Neutral Roles", + "ROT.Neutral_Evil": "★ Neutral Evil Roles", + "ROT.Neutral_Benign": "★ Neutral Benign Roles", "ROT.Neutral_Killing": "★ Neutral Killing Roles", "ROT.Vanilla": "★ Vanilla Roles", "ROT.Basic": "★ Basic Roles", @@ -1329,7 +1333,6 @@ "ExecutionerChangeRolesAfterTargetKilled": "When Target Dies, Executioner becomes", "CanTargetNeutralBenign": "Can Target Neutral Benign", "CanTargetNeutralEvil": "Can Target Neutral Evil", - "CanTargetNeutralChaos": "Can Target Neutral Chaos", "SidekickSheriffCanGoBerserk": "Recruited Sheriff Can Go Nuts", "LawyerCanTargetImpostor": "Can Target Impostors", "LawyerCanTargetNeutralKiller": "Can Target Neutral Killers", @@ -1648,7 +1651,6 @@ "CharmedCountMode.None": "Nothing", "CharmedCountMode.Succubus": "Cultist", "CharmedCountMode.Original": "Original Team", - "JackalCanWinBySabotageWhenNoImpAlive": "When all Impostors are dead, the Jackal wins by sabotage instead", "ResetKillCooldownWhenPlayerGetKilled": "Reset kill cooldown if someone gets killed by another player", "ResetKillCooldownOn": "Kill Cooldown On Reset", "JackalCanRecruitSidekick": "Can recruit Sidekick", @@ -1930,6 +1932,8 @@ "KB_Buff_HPMax": "Gain: HP Max +{0}", "KB_Buff_HPReco": "Gain: Recover HP Per Second +{0}", "KB_Buff_ATK": "Gain: Attack +{0}", + "KB_Buff_DF": "Gain: Defense +{0}", + "NKWinsBySabotageIfNoImpAlive": "<#ffab1b>Neutral <#ff1919>Killers win by critical sabotage time limit if there are no <#ff1919>Impostors alive", "MorticianGetNoInfo": "According to your inspection, {0} did not seem to have contact with anyone during their lifetime.", "MorticianGetInfo": "According to your inspection, the last person {0} came into contact with during their lifetime was {1}.", "MediumshipHelp": "/ms yes to agree\\n/ms no to disagree", @@ -2370,7 +2374,6 @@ "JudgeCanTrialNeutralB": "Can trial Neutral Benign", "JudgeCanTrialNeutralK": "Can trial Neutral Killing", "JudgeCanTrialNeutralE": "Can trial Neutral Evil", - "JudgeCanTrialNeutralC": "Can trial Neutral Chaos", "JudgeCanTrialSidekick": "Can trial Sidekick", "JudgeCanTrialInfected": "Can trial Infected", "JudgeCanTrialContagious": "Can trial Contagious", @@ -4010,6 +4013,16 @@ "SimonDontShort": "<#ffa500>Must not do instruction", "SimonDoMode": "<#00ffa5>Mode: Do instruction", "SimonDontMode": "<#00ffa5>Mode: Do NOT do instruction", + "Catcher": "Catcher", + "CatcherInfo": "Catch Players' Roles", + "CatcherInfoLong": "(Crewmates):\\nAs the Catcher, you can place traps by venting/petting. When a player steps on a trap, their role will be recorded. In the next meeting, you'll get a message of the list of roles caught in your trap.\\n\\nNote: You don't know who stepped on your trap and who has which role.\\nYou can place multiple traps, and you'll get a message of all roles caught in all traps.", + "Catcher.TrapPlaceDelay": "Trap Placement Delay (after venting) (if not using pets)", + "Catcher.CatchRange": "Trap Catch Range", + "Catcher.MinPlayersTrappedToShowInfo": "Minimum Number of Players Trapped to Show Info", + "Catcher.TrapPlaced": "Trap placed", + "Catcher.CaughtRoles": "Roles caught in your traps:\\n", + "Catcher.NotEnoughCaughtRoles": "Not enough players have stepped on your traps to show the roles.", + "Catcher.Suffix": "<#00ffa5>Placing trap in {0}s", "Generator": "Generator", "GeneratorInfo": "Power up the ship", "GeneratorInfoLong": "(Impostors):\\nAs the Generator, you generate charges every second. You can only kill/vent/shapeshift/sabotage if you have enough charges to do so. You also lose charges while shapeshifted.", diff --git a/Roles/Crewmate/Bodyguard.cs b/Roles/Crewmate/Bodyguard.cs index 2433dcc38..d364d2628 100644 --- a/Roles/Crewmate/Bodyguard.cs +++ b/Roles/Crewmate/Bodyguard.cs @@ -36,6 +36,8 @@ namespace EHR.Crewmate public static bool OnAnyoneCheckMurder(PlayerControl killer, PlayerControl target) { + if (killer.IsCrewmate()) return true; + if (killer.PlayerId != target.PlayerId) { foreach (var bodyguard in Instances) diff --git a/Roles/Crewmate/Car.cs b/Roles/Crewmate/Car.cs index 6f3037165..29e8c4604 100644 --- a/Roles/Crewmate/Car.cs +++ b/Roles/Crewmate/Car.cs @@ -1,4 +1,5 @@ -using UnityEngine; +using System.Collections.Generic; +using UnityEngine; namespace EHR.Crewmate { @@ -7,7 +8,9 @@ namespace EHR.Crewmate public static bool On; private static OptionItem PropelDistance; + private int Count; + private HashSet CurrentlyPropelling; private Vector2 LastPosition; public static string Name => "
\u2588<#628d85>\u2588<#628d85>\u2588<#628d85>\u2588<#628d85>\u2588<#628d85>\u2588\u2588\u2588
<#586874>\u2588<#586874>\u2588<#547a96>\u2588<#547a96>\u2588<#6894b6>\u2588<#6894b6>\u2588<#586874>\u2588\u2588
<#586874>\u2588<#586874>\u2588<#586874>\u2588<#547a96>\u2588<#547a96>\u2588<#586874>\u2588<#586874>\u2588<#f5ee2e>\u2588
<#000000>\u2588<#0d233f>\u2588<#586874>\u2588<#586874>\u2588<#586874>\u2588<#f5ee2e>\u2588<#586874>\u2588<#517a9a>\u2588
<#000000>\u2588<#000000>\u2588\u2588<#000000>\u2588<#0d233f>\u2588<#586874>\u2588<#517a9a>\u2588\u2588
\u2588\u2588\u2588<#000000>\u2588<#000000>\u2588\u2588\u2588\u2588
"; @@ -31,6 +34,7 @@ namespace EHR.Crewmate On = true; LastPosition = Utils.GetPlayerById(playerId).Pos(); Count = 0; + CurrentlyPropelling = []; } public override void OnFixedUpdate(PlayerControl pc) @@ -41,47 +45,76 @@ namespace EHR.Crewmate Count = 0; var pos = pc.Pos(); - var direction = pos.x > LastPosition.x ? Direction.Right : pos.x < LastPosition.x ? Direction.Left : pos.y > LastPosition.y ? Direction.Up : Direction.Down; + if (Vector2.Distance(pos, LastPosition) < 0.1f) return; + + var direction = pos.x < LastPosition.x + ? pos.y < LastPosition.y + ? Direction.DownLeft + : pos.y > LastPosition.y + ? Direction.UpLeft + : Direction.Left + : pos.x > LastPosition.x + ? pos.y < LastPosition.y + ? Direction.DownRight + : pos.y > LastPosition.y + ? Direction.UpRight + : Direction.Right + : pos.y < LastPosition.y + ? Direction.Down + : pos.y > LastPosition.y + ? Direction.Up + : Direction.Left; + LastPosition = pos; - if (Main.AllAlivePlayerControls.Without(pc).Find(x => Vector2.Distance(pos, x.Pos()) < 1f, out var target)) + if (Main.AllAlivePlayerControls.Without(pc).Find(x => Vector2.Distance(pos, x.Pos()) < 1f, out var target) && CurrentlyPropelling.Add(target.PlayerId)) Main.Instance.StartCoroutine(Propel(pc, target, direction)); } - static System.Collections.IEnumerator Propel(PlayerControl car, PlayerControl target, Direction direction) + System.Collections.IEnumerator Propel(PlayerControl car, PlayerControl target, Direction direction) { var oldSpeed = Main.AllPlayerSpeed[target.PlayerId]; Main.AllPlayerSpeed[target.PlayerId] = Main.MinSpeed; target.MarkDirtySettings(); var pos = car.Pos(); - for (Vector2 newPos = target.Pos(); - Vector2.Distance(pos, newPos) < PropelDistance.GetFloat() && - !PhysicsHelpers.AnythingBetween(target.Collider, target.Collider.bounds.center, newPos, Constants.ShipOnlyMask, false) && - GameStates.IsInTask; - newPos += direction switch - { - Direction.Left => Vector2.left, - Direction.Right => Vector2.right, - Direction.Up => Vector2.up, - Direction.Down => Vector2.down, - _ => Vector2.zero - }) + var addVector = direction switch + { + Direction.Left => new(-0.25f, 0), + Direction.UpLeft => new(-0.25f, 0.25f), + Direction.Up => new(0, 0.25f), + Direction.UpRight => new(0.25f, 0.25f), + Direction.Right => new(0.25f, 0), + Direction.DownRight => new(0.25f, -0.25f), + Direction.Down => new(0, -0.25f), + Direction.DownLeft => new(-0.25f, -0.25f), + _ => Vector2.zero + }; + + var distance = PropelDistance.GetFloat(); + var collider = target.Collider; + for (var newPos = target.Pos(); Vector2.Distance(pos, newPos) < distance && !PhysicsHelpers.AnythingBetween(collider, collider.bounds.center, newPos, Constants.ShipOnlyMask, false) && GameStates.IsInTask; newPos += addVector) { target.TP(newPos, log: false); - yield return new WaitForSeconds(0.2f); + yield return new WaitForSeconds(0.05f); } Main.AllPlayerSpeed[target.PlayerId] = oldSpeed; target.MarkDirtySettings(); + + CurrentlyPropelling.Remove(target.PlayerId); } enum Direction { Left, - Right, + UpLeft, Up, - Down + UpRight, + Right, + DownRight, + Down, + DownLeft } } } \ No newline at end of file diff --git a/Roles/Crewmate/Catcher.cs b/Roles/Crewmate/Catcher.cs new file mode 100644 index 000000000..3427d3118 --- /dev/null +++ b/Roles/Crewmate/Catcher.cs @@ -0,0 +1,159 @@ +using System.Collections.Generic; +using System.Linq; +using AmongUs.GameOptions; +using EHR.Modules; +using UnityEngine; + +namespace EHR.Crewmate +{ + public class Catcher : RoleBase + { + public static bool On; + + public static OptionItem AbilityCooldown; + public static OptionItem TrapPlaceDelay; + public static OptionItem CatchRange; + public static OptionItem MinPlayersTrappedToShowInfo; + public static OptionItem AbilityUseLimit; + public static OptionItem AbilityUseGainWithEachTaskCompleted; + public static OptionItem AbilityChargesWhenFinishedTasks; + private byte CatcherId; + private Dictionary CaughtRoles; + private long DelayStartTS; + private long LastUpdate; + private Dictionary Traps; + + public override bool IsEnable => On; + + public static void SetupCustomOption() + { + int id = 648650; + const TabGroup tab = TabGroup.CrewmateRoles; + const CustomRoles role = CustomRoles.Catcher; + + Options.SetupRoleOptions(id++, tab, role); + + var parent = Options.CustomRoleSpawnChances[role]; + + AbilityCooldown = new FloatOptionItem(++id, "AbilityCooldown", new(0f, 180f, 0.5f), 10f, tab) + .SetParent(parent) + .SetValueFormat(OptionFormat.Seconds); + TrapPlaceDelay = new FloatOptionItem(++id, "Catcher.TrapPlaceDelay", new(0f, 180f, 1f), 5f, tab) + .SetParent(parent) + .SetValueFormat(OptionFormat.Seconds); + CatchRange = new FloatOptionItem(++id, "Catcher.CatchRange", new(0f, 10f, 0.25f), 1.5f, tab) + .SetParent(parent) + .SetValueFormat(OptionFormat.Multiplier); + MinPlayersTrappedToShowInfo = new IntegerOptionItem(++id, "Catcher.MinPlayersTrappedToShowInfo", new(1, 10, 1), 2, tab) + .SetParent(parent) + .SetValueFormat(OptionFormat.Players); + AbilityUseLimit = new IntegerOptionItem(++id, "AbilityUseLimit", new(0, 20, 1), 3, tab) + .SetParent(parent) + .SetValueFormat(OptionFormat.Times); + AbilityUseGainWithEachTaskCompleted = new FloatOptionItem(++id, "AbilityUseGainWithEachTaskCompleted", new(0f, 5f, 0.05f), 1f, tab) + .SetParent(parent) + .SetValueFormat(OptionFormat.Times); + AbilityChargesWhenFinishedTasks = new FloatOptionItem(++id, "AbilityChargesWhenFinishedTasks", new(0f, 5f, 0.05f), 0.2f, tab) + .SetParent(parent) + .SetValueFormat(OptionFormat.Times); + } + + public override void Init() + { + On = false; + } + + public override void Add(byte playerId) + { + On = true; + CatcherId = playerId; + Traps = []; + DelayStartTS = 0; + LastUpdate = Utils.TimeStamp; + CaughtRoles = []; + playerId.SetAbilityUseLimit(AbilityUseLimit.GetInt()); + } + + public override void ApplyGameOptions(IGameOptions opt, byte playerId) + { + if (Options.UsePets.GetBool()) return; + AURoleOptions.EngineerCooldown = AbilityCooldown.GetFloat(); + AURoleOptions.EngineerInVentMaxTime = 1f; + } + + void PlaceTrap(PlayerControl pc) + { + if (pc.GetAbilityUseLimit() < 1) return; + pc.RpcRemoveAbilityUse(); + + var pos = pc.Pos(); + Traps[pos] = new(pos, pc); + + pc.Notify(Translator.GetString("Catcher.TrapPlaced")); + } + + public override void OnPet(PlayerControl pc) + { + PlaceTrap(pc); + } + + public override void OnEnterVent(PlayerControl pc, Vent vent) + { + DelayStartTS = Utils.TimeStamp + 1; + SendRPC(); + } + + public override void OnFixedUpdate(PlayerControl pc) + { + if (!pc.IsAlive() || !GameStates.IsInTask || ExileController.Instance || Options.UsePets.GetBool() || DelayStartTS == 0) return; + + long now = Utils.TimeStamp; + if (now == LastUpdate) return; + LastUpdate = now; + + if (DelayStartTS + TrapPlaceDelay.GetInt() <= now) + { + PlaceTrap(pc); + DelayStartTS = 0; + SendRPC(); + } + + Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc); + pc.RpcResetAbilityCooldown(); + } + + public override void OnCheckPlayerPosition(PlayerControl pc) + { + if (CaughtRoles.ContainsKey(pc.PlayerId)) return; + + var pos = pc.Pos(); + var range = CatchRange.GetFloat(); + if (Traps.Keys.Any(x => Vector2.Distance(x, pos) <= range)) + CaughtRoles[pc.PlayerId] = pc.GetCustomRole(); + } + + public override void OnReportDeadBody() + { + LateTask.New(() => + { + if (CaughtRoles.Count >= MinPlayersTrappedToShowInfo.GetInt()) + { + var roles = string.Join(", ", CaughtRoles.Values.Select(x => x.ToColoredString())); + Utils.SendMessage("\n", CatcherId, Translator.GetString("Catcher.CaughtRoles") + roles); + } + else Utils.SendMessage("\n", CatcherId, Translator.GetString("Catcher.NotEnoughCaughtRoles")); + + CaughtRoles = []; + }, 10f, "Send Catcher Caught Roles"); + } + + void SendRPC() => Utils.SendRPC(CustomRPC.SyncRoleData, CatcherId, DelayStartTS); + public void ReceiveRPC(Hazel.MessageReader reader) => DelayStartTS = long.Parse(reader.ReadString()); + + public override string GetSuffix(PlayerControl seer, PlayerControl target, bool isHUD = false, bool isMeeting = false) + { + if (seer.PlayerId != target.PlayerId || seer.PlayerId != CatcherId || isMeeting || (seer.IsModClient() && !isHUD) || DelayStartTS == 0 || Options.UsePets.GetBool()) return string.Empty; + return string.Format(Translator.GetString("Catcher.Suffix"), TrapPlaceDelay.GetInt() - (Utils.TimeStamp - DelayStartTS)); + } + } +} \ No newline at end of file diff --git a/Roles/Crewmate/Judge.cs b/Roles/Crewmate/Judge.cs index df431e6fc..8156f7eb5 100644 --- a/Roles/Crewmate/Judge.cs +++ b/Roles/Crewmate/Judge.cs @@ -28,7 +28,6 @@ public class Judge : RoleBase private static OptionItem CanTrialNeutralB; private static OptionItem CanTrialNeutralK; private static OptionItem CanTrialNeutralE; - private static OptionItem CanTrialNeutralC; public static OptionItem JudgeAbilityUseGainWithEachTaskCompleted; public static OptionItem AbilityChargesWhenFinishedTasks; @@ -46,7 +45,6 @@ public class Judge : RoleBase CanTrialCrewKilling = new BooleanOptionItem(Id + 13, "JudgeCanTrialnCrewKilling", true, TabGroup.CrewmateRoles).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Judge]); CanTrialNeutralB = new BooleanOptionItem(Id + 14, "JudgeCanTrialNeutralB", false, TabGroup.CrewmateRoles).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Judge]); CanTrialNeutralE = new BooleanOptionItem(Id + 17, "JudgeCanTrialNeutralE", false, TabGroup.CrewmateRoles).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Judge]); - CanTrialNeutralC = new BooleanOptionItem(Id + 18, "JudgeCanTrialNeutralC", false, TabGroup.CrewmateRoles).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Judge]); CanTrialNeutralK = new BooleanOptionItem(Id + 15, "JudgeCanTrialNeutralK", true, TabGroup.CrewmateRoles).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Judge]); TryHideMsg = new BooleanOptionItem(Id + 11, "JudgeTryHideMsg", true, TabGroup.CrewmateRoles).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Judge]).SetColor(Color.green); JudgeAbilityUseGainWithEachTaskCompleted = new FloatOptionItem(Id + 19, "AbilityUseGainWithEachTaskCompleted", new(0f, 5f, 0.05f), 0.3f, TabGroup.CrewmateRoles).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Judge]).SetValueFormat(OptionFormat.Times); @@ -152,8 +150,7 @@ public class Judge : RoleBase if (targetRole.IsCK() && CanTrialCrewKilling.GetBool()) judgeSuicide = false; else if (targetRole.IsNB() && CanTrialNeutralB.GetBool()) judgeSuicide = false; else if (targetRole.IsNE() && CanTrialNeutralE.GetBool()) judgeSuicide = false; - else if (targetRole.IsNC() && CanTrialNeutralC.GetBool()) judgeSuicide = false; - else if (targetRole.IsNonNK() && !CanTrialNeutralB.GetBool() && !CanTrialNeutralE.GetBool() && !CanTrialNeutralC.GetBool() && !targetRole.IsNB() && !targetRole.IsNC() && !targetRole.IsNE() && CanTrialNeutralK.GetBool()) judgeSuicide = false; + else if (targetRole.IsNonNK() && !CanTrialNeutralB.GetBool() && !CanTrialNeutralE.GetBool() && !targetRole.IsNB() && !targetRole.IsNE() && CanTrialNeutralK.GetBool()) judgeSuicide = false; else if (targetRole.IsImpostor()) judgeSuicide = false; else if (targetRole.IsMadmate() && CanTrialMadmate.GetBool()) judgeSuicide = false; else if (targetRole is CustomRoles.Necromancer or CustomRoles.Deathknight && CanTrialNeutralK.GetBool()) judgeSuicide = false; diff --git a/Roles/Neutral/Executioner.cs b/Roles/Neutral/Executioner.cs index 34a4ea33d..519c1e222 100644 --- a/Roles/Neutral/Executioner.cs +++ b/Roles/Neutral/Executioner.cs @@ -16,7 +16,6 @@ public class Executioner : RoleBase private static OptionItem CanTargetNeutralKiller; private static OptionItem CanTargetNeutralBenign; private static OptionItem CanTargetNeutralEvil; - private static OptionItem CanTargetNeutralChaos; public static OptionItem KnowTargetRole; public static OptionItem CanGuessTarget; public static OptionItem ChangeRolesAfterTargetKilled; @@ -46,7 +45,6 @@ public class Executioner : RoleBase CanTargetNeutralKiller = new BooleanOptionItem(Id + 12, "ExecutionerCanTargetNeutralKiller", false, TabGroup.NeutralRoles).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); CanTargetNeutralBenign = new BooleanOptionItem(Id + 14, "CanTargetNeutralBenign", false, TabGroup.NeutralRoles).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); CanTargetNeutralEvil = new BooleanOptionItem(Id + 15, "CanTargetNeutralEvil", false, TabGroup.NeutralRoles).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); - CanTargetNeutralChaos = new BooleanOptionItem(Id + 16, "CanTargetNeutralChaos", false, TabGroup.NeutralRoles).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); KnowTargetRole = new BooleanOptionItem(Id + 13, "KnowTargetRole", false, TabGroup.NeutralRoles).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); CanGuessTarget = new BooleanOptionItem(Id + 17, "CanGuessTarget", false, TabGroup.NeutralRoles).SetParent(KnowTargetRole); ChangeRolesAfterTargetKilled = new StringOptionItem(Id + 11, "ExecutionerChangeRolesAfterTargetKilled", CRoleChangeRoles.Select(x => x.ToColoredString()).ToArray(), 1, TabGroup.NeutralRoles, noTranslation: true).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); @@ -67,8 +65,8 @@ public class Executioner : RoleBase try { List targetList = []; - targetList.AddRange(from target in Main.AllPlayerControls where playerId != target.PlayerId where CanTargetImpostor.GetBool() || !target.Is(CustomRoleTypes.Impostor) where CanTargetNeutralKiller.GetBool() || !target.IsNeutralKiller() where CanTargetNeutralBenign.GetBool() || !target.IsNeutralBenign() where CanTargetNeutralEvil.GetBool() || !target.IsNeutralEvil() where CanTargetNeutralChaos.GetBool() || !target.IsNeutralChaos() where target.GetCustomRole() is not (CustomRoles.GM or CustomRoles.SuperStar) where Main.LoversPlayers.TrueForAll(x => x.PlayerId != playerId) select target); - if (!CanTargetNeutralBenign.GetBool() && !CanTargetNeutralChaos.GetBool() && !CanTargetNeutralEvil.GetBool() && !CanTargetNeutralKiller.GetBool()) targetList.RemoveAll(x => x.GetCustomRole().IsNeutral() || x.Is(CustomRoles.Bloodlust)); + targetList.AddRange(from target in Main.AllPlayerControls where playerId != target.PlayerId where CanTargetImpostor.GetBool() || !target.Is(CustomRoleTypes.Impostor) where CanTargetNeutralKiller.GetBool() || !target.IsNeutralKiller() where CanTargetNeutralBenign.GetBool() || !target.IsNeutralBenign() where CanTargetNeutralEvil.GetBool() || !target.IsNeutralEvil() where target.GetCustomRole() is not (CustomRoles.GM or CustomRoles.SuperStar) where Main.LoversPlayers.TrueForAll(x => x.PlayerId != playerId) select target); + if (!CanTargetNeutralBenign.GetBool() && !CanTargetNeutralEvil.GetBool() && !CanTargetNeutralKiller.GetBool()) targetList.RemoveAll(x => x.GetCustomRole().IsNeutral() || x.Is(CustomRoles.Bloodlust)); if (targetList.Count == 0) { diff --git a/Roles/Neutral/Jackal.cs b/Roles/Neutral/Jackal.cs index e7a6d1588..e83242f87 100644 --- a/Roles/Neutral/Jackal.cs +++ b/Roles/Neutral/Jackal.cs @@ -14,7 +14,6 @@ public class Jackal : RoleBase private static OptionItem KillCooldown; private static OptionItem CanVent; public static OptionItem CanSabotage; - public static OptionItem CanWinBySabotageWhenNoImpAlive; public static OptionItem HasImpostorVision; public static OptionItem ResetKillCooldownWhenSbGetKilled; private static OptionItem ResetKillCooldownOn; @@ -42,8 +41,6 @@ public class Jackal : RoleBase .SetParent(CustomRoleSpawnChances[CustomRoles.Jackal]); CanSabotage = new BooleanOptionItem(Id + 4, "CanSabotage", true, TabGroup.NeutralRoles) .SetParent(CustomRoleSpawnChances[CustomRoles.Jackal]); - CanWinBySabotageWhenNoImpAlive = new BooleanOptionItem(Id + 5, "JackalCanWinBySabotageWhenNoImpAlive", true, TabGroup.NeutralRoles) - .SetParent(CanSabotage); HasImpostorVision = new BooleanOptionItem(Id + 6, "ImpostorVision", true, TabGroup.NeutralRoles) .SetParent(CustomRoleSpawnChances[CustomRoles.Jackal]); ResetKillCooldownWhenSbGetKilled = new BooleanOptionItem(Id + 7, "ResetKillCooldownWhenPlayerGetKilled", false, TabGroup.NeutralRoles) diff --git a/main.cs b/main.cs index 64bde0d9b..004c5ccf6 100644 --- a/main.cs +++ b/main.cs @@ -345,6 +345,7 @@ public class Main : BasePlugin { CustomRoles.Detour, "#ffd35c" }, { CustomRoles.Gaulois, "#42d1f5" }, { CustomRoles.Druid, "#ffb694" }, + { CustomRoles.Catcher, "#aaf2cf" }, { CustomRoles.Autocrat, "#e2ed64" }, { CustomRoles.LovingCrewmate, "#ff9ace" }, { CustomRoles.LovingImpostor, "#ff9ace" }, @@ -727,6 +728,13 @@ public enum Team Impostor = 1, Neutral = 2, Crewmate = 4 + + /* + * Impostor | Neutral = 3 + * Impostor | Crewmate = 5 + * Neutral | Crewmate = 6 + * Impostor | Neutral | Crewmate = 7 + */ } #pragma warning disable IDE0079 // Remove unnecessary suppression