From 7d9689d8090762c163c7c12e1d02182138fa4662 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:31:00 -0500 Subject: [PATCH 01/15] The Beginning of the Apocalypse Added Death, moved Soul Collector and Plaguebearer to new Neutral Apocalypse category, let apocalypse members see eachother, wincons are broken right now --- Modules/AntiBlackout.cs | 2 +- Modules/CustomRolesHelper.cs | 29 +++++++++++++++++---------- Modules/ExtendedPlayerControl.cs | 3 ++- Modules/GameState.cs | 1 + Modules/NameColorManager.cs | 3 +++ Modules/OptionHolder.cs | 11 +++++++--- Modules/Utils.cs | 1 + Patches/MeetingHudPatch.cs | 5 +++++ Patches/PlayerControlPatch.cs | 4 ++++ Resources/Lang/en_US.json | 7 +++++++ Resources/roleColor.json | 1 + Roles/AddOns/Common/Susceptible.cs | 11 ++++++++++ Roles/Crewmate/Divinator.cs | 1 + Roles/Neutral/PlagueBearer.cs | 1 + Roles/Neutral/SoulCollector.cs | 32 +++++++++++++++++++++++++++--- main.cs | 3 ++- 16 files changed, 95 insertions(+), 20 deletions(-) diff --git a/Modules/AntiBlackout.cs b/Modules/AntiBlackout.cs index 2bd851e45..f8ba878cf 100644 --- a/Modules/AntiBlackout.cs +++ b/Modules/AntiBlackout.cs @@ -44,7 +44,7 @@ public static class AntiBlackout || PotionMaster.IsEnable || Wraith.IsEnable || Necromancer.IsEnable || Doppelganger.IsEnable || PlagueDoctor.IsEnable || CustomRoles.Sidekick.RoleExist(true) - || (CustomRoles.Arsonist.RoleExist(true) && Options.ArsonistCanIgniteAnytime.GetBool()); + || (CustomRoles.Arsonist.RoleExist(true) && Options.ArsonistCanIgniteAnytime.GetBool()) || CustomRoles.Death.RoleExist(true); /// ///Difference between the number of non-impostors and the number of imposters diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index ce87ad761..8f6ddfb34 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -510,8 +510,7 @@ CustomRoles.BloodKnight or CustomRoles.Spiritcaller or CustomRoles.PlagueBearer or CustomRoles.Agitater or - CustomRoles.RuthlessRomantic or - CustomRoles.Pestilence; + CustomRoles.RuthlessRomantic; } public static bool IsNonNK(this CustomRoles role) // ROLE ASSIGNING, NOT NEUTRAL TYPE { @@ -595,7 +594,6 @@ CustomRoles.Collector or CustomRoles.Succubus or CustomRoles.Phantom or CustomRoles.Mario or - CustomRoles.SoulCollector or CustomRoles.Pirate or CustomRoles.Terrorist or CustomRoles.Vulture or @@ -604,6 +602,14 @@ CustomRoles.Solsticer or CustomRoles.Revolutionist or CustomRoles.Provocateur; } + public static bool IsNA(this CustomRoles role) + { + return role is + CustomRoles.PlagueBearer or + CustomRoles.Pestilence or + CustomRoles.SoulCollector or + CustomRoles.Death; + } public static bool IsSnitchTarget(this CustomRoles role) { if (role is CustomRoles.Arsonist && Options.ArsonistCanIgniteAnytime.GetBool()) return true; @@ -769,6 +775,7 @@ CustomRoles.NWitch or CustomRoles.Shroud or CustomRoles.Wraith or CustomRoles.SoulCollector or + CustomRoles.Death or CustomRoles.Vulture or CustomRoles.Taskinator or CustomRoles.Convict or @@ -1882,8 +1889,10 @@ public static CountTypes GetCountTypes(this CustomRoles role) CustomRoles.Shroud => CountTypes.Shroud, CustomRoles.Werewolf => CountTypes.Werewolf, CustomRoles.Wraith => CountTypes.Wraith, - CustomRoles.Pestilence => CountTypes.Pestilence, - CustomRoles.PlagueBearer => CountTypes.PlagueBearer, + CustomRoles.Pestilence => CountTypes.Apocalypse, + CustomRoles.PlagueBearer => CountTypes.Apocalypse, + CustomRoles.SoulCollector => CountTypes.Apocalypse, + CustomRoles.Death => CountTypes.Apocalypse, CustomRoles.Agitater => CountTypes.Agitater, CustomRoles.Parasite => CountTypes.Impostor, CustomRoles.NSerialKiller => CountTypes.NSerialKiller, @@ -1958,7 +1967,7 @@ public static CountTypes GetCountTypes(this CustomRoles role) CustomRoles.Pickpocket => CustomWinner.Pickpocket, CustomRoles.Traitor => CustomWinner.Traitor, CustomRoles.Vulture => CustomWinner.Vulture, - CustomRoles.Pestilence => CustomWinner.Pestilence, + CustomRoles.Pestilence => CustomWinner.Apocalypse, CustomRoles.Medusa => CustomWinner.Medusa, CustomRoles.Spiritcaller => CustomWinner.Spiritcaller, CustomRoles.Glitch => CustomWinner.Glitch, @@ -1967,7 +1976,8 @@ public static CountTypes GetCountTypes(this CustomRoles role) CustomRoles.Doomsayer => CustomWinner.Doomsayer, CustomRoles.Shroud => CustomWinner.Shroud, CustomRoles.Seeker => CustomWinner.Seeker, - CustomRoles.SoulCollector => CustomWinner.SoulCollector, + CustomRoles.SoulCollector => CustomWinner.Apocalypse, + CustomRoles.Death => CustomWinner.Apocalypse, CustomRoles.RuthlessRomantic => CustomWinner.RuthlessRomantic, CustomRoles.Mini => CustomWinner.NiceMini, CustomRoles.Doppelganger => CustomWinner.Doppelganger, @@ -1993,8 +2003,6 @@ public static CountTypes GetCountTypes(this CustomRoles role) CountTypes.Shroud => CustomRoles.Shroud, CountTypes.Werewolf => CustomRoles.Werewolf, CountTypes.Wraith => CustomRoles.Wraith, - CountTypes.Pestilence => CustomRoles.Pestilence, - CountTypes.PlagueBearer => CustomRoles.PlagueBearer, CountTypes.Agitater => CustomRoles.Agitater, CountTypes.NSerialKiller => CustomRoles.NSerialKiller, CountTypes.Juggernaut => CustomRoles.Juggernaut, @@ -2062,8 +2070,7 @@ public enum CountTypes Traitor, Medusa, Spiritcaller, - Pestilence, - PlagueBearer, + Apocalypse, Glitch, Arsonist, Huntsman, diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index e52901f3d..7468532cc 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -1315,6 +1315,7 @@ public static List GetPlayersInAbilityRangeSorted(this PlayerCont 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 IsNeutralApocalypse(this PlayerControl player) => player.GetCustomRole().IsNA(); public static bool IsNonNeutralKiller(this PlayerControl player) => player.GetCustomRole().IsNonNK(); public static bool IsSnitchTarget(this PlayerControl player) => player.GetCustomRole().IsSnitchTarget(); @@ -1371,7 +1372,7 @@ public static bool KnowRoleTarget(PlayerControl seer, PlayerControl target) (target.Is(CustomRoles.President) && seer.Is(CustomRoles.Madmate) && President.MadmatesSeePresident.GetBool() && President.CheckPresidentReveal[target.PlayerId] == true) || (target.Is(CustomRoles.President) && seer.GetCustomRole().IsNeutral() && President.NeutralsSeePresident.GetBool() && President.CheckPresidentReveal[target.PlayerId] == true) || (target.Is(CustomRoles.President) && (seer.GetCustomRole().IsImpostorTeam()) && President.ImpsSeePresident.GetBool() && President.CheckPresidentReveal[target.PlayerId] == true)) return true; - + else if (target.IsNeutralApocalypse() && seer.IsNeutralApocalypse()) return true; else return false; } diff --git a/Modules/GameState.cs b/Modules/GameState.cs index 9f7f01904..0bef11f84 100644 --- a/Modules/GameState.cs +++ b/Modules/GameState.cs @@ -363,6 +363,7 @@ public enum DeathReason Targeted, Retribution, WrongAnswer, + Armageddon, //Please add all new roles with deathreason & new deathreason in Susceptible.CallEnabledAndChange etc = -1, diff --git a/Modules/NameColorManager.cs b/Modules/NameColorManager.cs index eefe04a2d..87913d3bc 100644 --- a/Modules/NameColorManager.cs +++ b/Modules/NameColorManager.cs @@ -230,6 +230,9 @@ private static bool KnowTargetRoleColor(PlayerControl seer, PlayerControl target // PlagueDoctor if (seer.Is(CustomRoles.PlagueDoctor) && target.Is(CustomRoles.PlagueDoctor)) color = Main.roleColors[CustomRoles.PlagueDoctor]; + // Apocalypse + if (seer.IsNeutralApocalypse() && target.IsNeutralApocalypse()) color = Main.roleColors[CustomRoles.Death]; + if (color != "") return true; else return seer == target || (Main.GodMode.Value && seer.AmOwner) diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index c0efc721a..1225b2f1d 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -2339,7 +2339,6 @@ public static void Load() */ Solsticer.SetupCustomOption(); - SoulCollector.SetupCustomOption(); SetupRoleOptions(15400, TabGroup.NeutralRoles, CustomRoles.Terrorist); CanTerroristSuicideWin = BooleanOptionItem.Create(15402, "CanTerroristSuicideWin", false, TabGroup.NeutralRoles, false) @@ -2424,8 +2423,6 @@ public static void Load() Poisoner.SetupCustomOption(); - PlagueBearer.SetupCustomOption(); - PotionMaster.SetupCustomOption(); Pyromaniac.SetupCustomOption(); @@ -2447,6 +2444,14 @@ public static void Load() Wraith.SetupCustomOption(); + TextOptionItem.Create(10000015, "RoleType.NeutralApocalypse", TabGroup.NeutralRoles) + .SetGameMode(CustomGameMode.Standard) + .SetColor(new Color32(127, 140, 141, byte.MaxValue)); + + PlagueBearer.SetupCustomOption(); + + SoulCollector.SetupCustomOption(); + #endregion #region Add-Ons Settings diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 763b5ffea..c742fb2a3 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -546,6 +546,7 @@ public static bool HasTasks(GameData.PlayerInfo p, bool ForRecompute = true) case CustomRoles.Agitater: case CustomRoles.Jinx: case CustomRoles.SoulCollector: + case CustomRoles.Death: case CustomRoles.SchrodingersCat: case CustomRoles.Parasite: case CustomRoles.Crusader: diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index ba41cf5fb..a266342fd 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -930,6 +930,11 @@ void AddMsg(string text, byte sendTo = 255, string title = "") string separator = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.English or SupportedLangs.Russian ? "], [" : "】, 【"; AddMsg(string.Format(GetString("BaitAdviceAlive"), string.Join(separator, baitAliveList)), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Bait), GetString("BaitAliveTitle"))); } + //Death Notify + if (CustomRoles.Death.RoleExist()) + { + AddMsg(string.Format(GetString("SoulCollectorTransform")), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.SoulCollector), GetString("ApocalypseIsNigh"))); + } string MimicMsg = ""; foreach (var pc in Main.AllPlayerControls) { diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 0f859e52e..639e130b4 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -2638,6 +2638,10 @@ public static void Postfix(PlayerControl __instance) PlagueBearer.OnFixedUpdate(player); break; + case CustomRoles.SoulCollector: + SoulCollector.BecomeDeath(player); + break; + case CustomRoles.Farseer: Farseer.OnFixedUpdate(player); break; diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 8580542b2..6b64a1434 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -721,6 +721,7 @@ "PixieInfo": "Tag 'em, Bag 'em, and Eject 'em!", "OccultistInfo": "Kill and curse your enemies", "SoulCollectorInfo": "Predict deaths to collect souls", + "DeathInfo": "Enact Armageddon", "SchrodingersCatInfo": "The cat is both alive and dead until observed.", "RomanticInfo": "Protect your partner to win together", "VengefulRomanticInfo": "Revenge your partner to win together", @@ -1016,6 +1017,7 @@ "PixieInfoLong": "(Neutrals):\nAs the Pixie, Mark upto x amount of targets each round by using kill button on them. When the meeting starts, your job is to have one of the marked targets ejected. If unsuccessful you will suicide, except if you didn't mark any targets or all the targets are dead. The selected targets resets to 0 after the meeting ends. If you succeed you will gain a point. You see all your targets in colored names.\n\nYou win with the winning team when you have certain amounts of points set by the host.", "OccultistInfoLong": "(Neutrals):\nAs the Occultist, you can curse players or kill them.\nCursing a player works the same as spelling as a Spellcaster.", "SoulCollectorInfoLong": "(Neutrals):\nAs a Soul Collector, you vote players to predict their death. If the prediction is correct and the target dies in the next round you collect their soul. \n\nYou win by collecting configurable number of souls set by the host", + "DeathInfoLong": "(Neutrals): \nOnce the Soul Collector has collected their needed souls, they become Death and a meeting is immediately called. If Death is not ejected by the end of this meeting, Death kills everyone and wins.", "SchrodingersCatInfoLong": "(Neutrals):\nAs Schrodingers Cat, if someone attempts to use the kill button on you, you will block the action and join their team. This blocking ability works only once. By default, you don't have a victory condition, meaning you win only after switching teams.\nIn Addition to this, you will be counted as nothing in the game.\n\nNote: If the killing machine attempts to use their kill button on you, the interaction is not blocked, and you will die.", "RomanticInfoLong": "(Neutrals):\nThe Romantic can pick their lover partner using their kill button (this can be done at any point of the game). Once they've picked their partner, they can use their kill button to give their partner a temporary shield which protects them from attacks. If their lover partner dies, the Romantic's role will change according to the following conditions:\n1. If their partner was an Impostor, the Romantic becomes the Refugee\n2. If their partner was a Neutral Killer, then they become Ruthless Romantic.\n3. If their partner was a Crewmate or a non-killing neutral, the Romantic becomes the Vengeful Romantic. \n\nThe Romantic wins with the winning team if their partner wins.\nNote : If your role changes your win condition will be changed accordingly", "RuthlessRomanticInfoLong": "(Neutrals):\nYou change your roles from Romantic if your partner (A neutral killer) is killed. As Ruthless Romantic, you win if you kill everyone and be the last one standing. If you win your dead partner also wins with you.", @@ -2008,6 +2010,7 @@ "DeathReason.Trap": "Trapped", "DeathReason.Targeted": "Targeted", "DeathReason.Retribution": "Retribution", + "DeathReason.Armageddon": "Armageddon", "OnlyEnabledDeathReasons": "Only Enabled Death Reasons", "Alive": "Alive", "Win": " Wins!", @@ -2709,6 +2712,9 @@ "SoulCollectorTitle": "SOUL COLLECTOR", "CollectOwnSoulOpt": "Can collect their own soul", "SoulCollectorSelfVote": "Host settings do not allow you to collect your own soul", + "SoulCollectorToDeath": "You have become Death!!!", + "SoulCollectorTransform": "Now Soul Collector has become Death, Destroyer of Worlds and Horseman of the Apocalypse!

Find them and vote them out before it's too late!", + "ApocalypseIsNigh": "[ The Apocalypse Is Nigh! ]", "ChronomancerKillCooldown": "Time to fully charge the kill button", "Occultist Text Strings": "", "CursesLookLikeSpells": "Curses look like spells", @@ -3465,6 +3471,7 @@ "RoleType.NeutralBenign": "★ Neutral Benign Roles", "RoleType.NeutralChaos": "★ Neutral Chaos Roles", "RoleType.NeutralKilling": "★ Neutral Killing Roles", + "RoleType.NeutralApocalypse": "★ Neutral Apocalypse Roles", "RoleType.Harmful": "★ Harmful Add-ons", "RoleType.Support": "★ Supportive Add-ons", "RoleType.Helpful": "★ Helpful Add-ons", diff --git a/Resources/roleColor.json b/Resources/roleColor.json index 7ca903155..ba057224c 100644 --- a/Resources/roleColor.json +++ b/Resources/roleColor.json @@ -147,6 +147,7 @@ "Seeker": "#ffaa00", "Pixie": "#00FF00", "SoulCollector": "#A675A1", + "Death": "#ff174f", "Imitator": "#B3D94C", "Doppelganger": "#f6f4a3", "GM": "#ff5b70", diff --git a/Roles/AddOns/Common/Susceptible.cs b/Roles/AddOns/Common/Susceptible.cs index f7ae69476..388b972ff 100644 --- a/Roles/AddOns/Common/Susceptible.cs +++ b/Roles/AddOns/Common/Susceptible.cs @@ -328,6 +328,17 @@ public static void CallEnabledAndChange(PlayerControl victim) } break; + case PlayerState.DeathReason.Armageddon: + if (!CustomRoles.SoulCollector.IsEnable()) + { + Main.PlayerStates[victim.PlayerId].deathReason = PlayerState.DeathReason.Kill; + } + else + { + goto default; + } + break; + default: while (Main.PlayerStates[victim.PlayerId].deathReason != randomReason) Main.PlayerStates[victim.PlayerId].deathReason = randomReason; diff --git a/Roles/Crewmate/Divinator.cs b/Roles/Crewmate/Divinator.cs index a17bf0578..70b5a57d5 100644 --- a/Roles/Crewmate/Divinator.cs +++ b/Roles/Crewmate/Divinator.cs @@ -196,6 +196,7 @@ public static void OnVote(PlayerControl player, PlayerControl target) CustomRoles.Greedier, CustomRoles.Merchant, CustomRoles.SoulCollector, + CustomRoles.Death, CustomRoles.Trickster], [CustomRoles.Pestilence, diff --git a/Roles/Neutral/PlagueBearer.cs b/Roles/Neutral/PlagueBearer.cs index ab89f51cc..9c87af4f6 100644 --- a/Roles/Neutral/PlagueBearer.cs +++ b/Roles/Neutral/PlagueBearer.cs @@ -135,6 +135,7 @@ public static bool OnCheckMurderPestilence(PlayerControl killer, PlayerControl t if (target.Is(CustomRoles.Opportunist) && target.AllTasksCompleted()) return true; if (target.Is(CustomRoles.Veteran) && Main.VeteranInProtect.ContainsKey(target.PlayerId)) return true; if (target.Is(CustomRoles.TimeMaster) && Main.TimeMasterInProtect.ContainsKey(target.PlayerId)) return true; + if (target.IsNeutralApocalypse()) return false; if (IsIndirectKill(killer)) return false; killer.SetRealKiller(target); target.RpcMurderPlayerV3(killer); diff --git a/Roles/Neutral/SoulCollector.cs b/Roles/Neutral/SoulCollector.cs index d5820b223..f6ac36824 100644 --- a/Roles/Neutral/SoulCollector.cs +++ b/Roles/Neutral/SoulCollector.cs @@ -116,13 +116,39 @@ public static void OnPlayerDead(PlayerControl deadPlayer) if (SoulCollectorPoints[playerId] >= SoulCollectorPointsOpt.GetInt()) { SoulCollectorPoints[playerId] = SoulCollectorPointsOpt.GetInt(); - if (!CustomWinnerHolder.CheckForConvertedWinner(playerId)) + } + } + } + public static void BecomeDeath(PlayerControl player) + { + if (SoulCollectorPoints[player.PlayerId] < SoulCollectorPointsOpt.GetInt()) return; + + player.RpcSetCustomRole(CustomRoles.Death); + player.Notify(GetString("SoulCollectorToDeath")); + PlayerControl.LocalPlayer.NoCheckStartMeeting(null, force: true); + KillIfNotEjected(player); + + } + public static void KillIfNotEjected(PlayerControl player) + { + var deathList = new List(); + foreach (var pc in Main.AllAlivePlayerControls) + { + if (pc.GetCustomRole() is CustomRoles.PlagueBearer or CustomRoles.Pestilence or CustomRoles.Death or CustomRoles.SoulCollector) continue; + if (player != null && player.IsAlive()) + { + if (!Main.AfterMeetingDeathPlayers.ContainsKey(pc.PlayerId)) + { + pc.SetRealKiller(player); + deathList.Add(pc.PlayerId); + } + else { - CustomWinnerHolder.ResetAndSetWinner(CustomWinner.SoulCollector); - CustomWinnerHolder.WinnerIds.Add(playerId); + Main.AfterMeetingDeathPlayers.Remove(pc.PlayerId); } } } + CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Armageddon, [.. deathList]); } } \ No newline at end of file diff --git a/main.cs b/main.cs index c24a8ddbd..048763a79 100644 --- a/main.cs +++ b/main.cs @@ -717,6 +717,7 @@ public enum CustomRoles Collector, Succubus, //cultist CursedSoul, + Death, Gamer, //demon Doomsayer, Doppelganger, @@ -931,7 +932,6 @@ public enum CustomWinner Pickpocket = CustomRoles.Pickpocket, Traitor = CustomRoles.Traitor, Vulture = CustomRoles.Vulture, - Pestilence = CustomRoles.Pestilence, Medusa = CustomRoles.Medusa, Spiritcaller = CustomRoles.Spiritcaller, Glitch = CustomRoles.Glitch, @@ -946,6 +946,7 @@ public enum CustomWinner NiceMini = CustomRoles.Mini, Doppelganger = CustomRoles.Doppelganger, Solsticer = CustomRoles.Solsticer, + Apocalypse = CustomRoles.Pestilence, } public enum AdditionalWinners { From 646b930975725fabd8b3b00ef941bb5dd38dc170 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Sun, 11 Feb 2024 12:27:29 -0500 Subject: [PATCH 02/15] Pestilence cant kill Apoc members + attempt at wincons --- Modules/CustomRolesHelper.cs | 6 +++--- Patches/CheckGameEndPatch.cs | 10 ++++++++-- Roles/Neutral/PlagueBearer.cs | 2 +- main.cs | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 8f6ddfb34..a9bb9dbc8 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -1967,7 +1967,7 @@ public static CountTypes GetCountTypes(this CustomRoles role) CustomRoles.Pickpocket => CustomWinner.Pickpocket, CustomRoles.Traitor => CustomWinner.Traitor, CustomRoles.Vulture => CustomWinner.Vulture, - CustomRoles.Pestilence => CustomWinner.Apocalypse, + CustomRoles.Pestilence => CustomWinner.Pestilence, CustomRoles.Medusa => CustomWinner.Medusa, CustomRoles.Spiritcaller => CustomWinner.Spiritcaller, CustomRoles.Glitch => CustomWinner.Glitch, @@ -1976,8 +1976,8 @@ public static CountTypes GetCountTypes(this CustomRoles role) CustomRoles.Doomsayer => CustomWinner.Doomsayer, CustomRoles.Shroud => CustomWinner.Shroud, CustomRoles.Seeker => CustomWinner.Seeker, - CustomRoles.SoulCollector => CustomWinner.Apocalypse, - CustomRoles.Death => CustomWinner.Apocalypse, + CustomRoles.SoulCollector => CustomWinner.SoulCollector, + CustomRoles.Death => CustomWinner.Death, CustomRoles.RuthlessRomantic => CustomWinner.RuthlessRomantic, CustomRoles.Mini => CustomWinner.NiceMini, CustomRoles.Doppelganger => CustomWinner.Doppelganger, diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index 12b803e78..4c53fdef6 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -90,6 +90,12 @@ public static bool Prefix() CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); } break; + case CustomWinner.Plaguebearer or CustomWinner.Pestilence or CustomWinner.SoulCollector or CustomWinner.Death: + if (pc.IsNeutralApocalypse() && !CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) + { + CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); + } + break; case CustomWinner.CursedSoul: if (pc.Is(CustomRoles.Soulless) && !CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) { @@ -158,7 +164,7 @@ public static bool Prefix() foreach (var pc in Main.AllPlayerControls) { if (pc.Is(CustomRoles.Phantom) && pc.GetPlayerTaskState().IsTaskFinished && pc.Data.IsDead - && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Pestilence || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Spiritcaller || CustomWinnerHolder.WinnerTeam == CustomWinner.Quizmaster ) && (Options.PhantomSnatchesWin.GetBool() || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) //|| CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist + && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Plaguebearer || CustomWinnerHolder.WinnerTeam == CustomWinner.Pestilence || CustomWinnerHolder.WinnerTeam == CustomWinner.SoulCollector || CustomWinnerHolder.WinnerTeam == CustomWinner.Death || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Spiritcaller || CustomWinnerHolder.WinnerTeam == CustomWinner.Quizmaster ) && (Options.PhantomSnatchesWin.GetBool() || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) //|| CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist { reason = GameOverReason.ImpostorByKill; if (!CustomWinnerHolder.CheckForConvertedWinner(pc.PlayerId)) @@ -171,7 +177,7 @@ public static bool Prefix() foreach (var pc in Main.AllPlayerControls) { if (pc.Is(CustomRoles.CursedSoul) && !pc.Data.IsDead - && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Pestilence || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Jester || CustomWinnerHolder.WinnerTeam == CustomWinner.Executioner || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) // || CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist + && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Plaguebearer || CustomWinnerHolder.WinnerTeam == CustomWinner.Pestilence || CustomWinnerHolder.WinnerTeam == CustomWinner.SoulCollector || CustomWinnerHolder.WinnerTeam == CustomWinner.Death || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Jester || CustomWinnerHolder.WinnerTeam == CustomWinner.Executioner || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) // || CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist { reason = GameOverReason.ImpostorByKill; if (!CustomWinnerHolder.CheckForConvertedWinner(pc.PlayerId)) diff --git a/Roles/Neutral/PlagueBearer.cs b/Roles/Neutral/PlagueBearer.cs index 9c87af4f6..05f08cb8c 100644 --- a/Roles/Neutral/PlagueBearer.cs +++ b/Roles/Neutral/PlagueBearer.cs @@ -130,12 +130,12 @@ public static bool IsIndirectKill(PlayerControl killer) public static bool OnCheckMurderPestilence(PlayerControl killer, PlayerControl target) { if (killer == null || target == null) return false; + if (target.IsNeutralApocalypse()) return true; if (!PestilenceList.Contains(target.PlayerId)) return false; if (target.Is(CustomRoles.Guardian) && target.AllTasksCompleted()) return true; if (target.Is(CustomRoles.Opportunist) && target.AllTasksCompleted()) return true; if (target.Is(CustomRoles.Veteran) && Main.VeteranInProtect.ContainsKey(target.PlayerId)) return true; if (target.Is(CustomRoles.TimeMaster) && Main.TimeMasterInProtect.ContainsKey(target.PlayerId)) return true; - if (target.IsNeutralApocalypse()) return false; if (IsIndirectKill(killer)) return false; killer.SetRealKiller(target); target.RpcMurderPlayerV3(killer); diff --git a/main.cs b/main.cs index 048763a79..2ed3ddf66 100644 --- a/main.cs +++ b/main.cs @@ -936,17 +936,18 @@ public enum CustomWinner Spiritcaller = CustomRoles.Spiritcaller, Glitch = CustomRoles.Glitch, Plaguebearer = CustomRoles.PlagueBearer, + Pestilence = CustomRoles.Pestilence, PlagueDoctor = CustomRoles.PlagueDoctor, Masochist = CustomRoles.Masochist, Doomsayer = CustomRoles.Doomsayer, Shroud = CustomRoles.Shroud, Seeker = CustomRoles.Seeker, SoulCollector = CustomRoles.SoulCollector, + Death = CustomRoles.Death, RuthlessRomantic = CustomRoles.RuthlessRomantic, NiceMini = CustomRoles.Mini, Doppelganger = CustomRoles.Doppelganger, Solsticer = CustomRoles.Solsticer, - Apocalypse = CustomRoles.Pestilence, } public enum AdditionalWinners { From f50f3e4f11bc0f0272b8a0f90df88ff6f88cb9c6 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Mon, 12 Feb 2024 15:10:37 -0500 Subject: [PATCH 03/15] win con attempt still dont work but im getting somewhere maybe --- Patches/CheckGameEndPatch.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index 4c53fdef6..c5599969d 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -90,12 +90,6 @@ public static bool Prefix() CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); } break; - case CustomWinner.Plaguebearer or CustomWinner.Pestilence or CustomWinner.SoulCollector or CustomWinner.Death: - if (pc.IsNeutralApocalypse() && !CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) - { - CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); - } - break; case CustomWinner.CursedSoul: if (pc.Is(CustomRoles.Soulless) && !CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) { @@ -146,6 +140,7 @@ public static bool Prefix() break; } } + if (CustomWinnerHolder.WinnerIds.Any(x => Utils.GetPlayerById(x).IsNeutralApocalypse())) Main.AllPlayerControls.Where(x => x.IsNeutralApocalypse()).Do(x => CustomWinnerHolder.WinnerIds.Add(x.PlayerId)); if (CustomWinnerHolder.WinnerTeam is not CustomWinner.Draw and not CustomWinner.None and not CustomWinner.Error) { foreach (var pc in Main.AllPlayerControls) From 76cd9704ec66888f7bd94832c9cfa69c444296d7 Mon Sep 17 00:00:00 2001 From: ryuk201198 <138619164+ryuk201198@users.noreply.github.com> Date: Tue, 13 Feb 2024 21:42:15 +0530 Subject: [PATCH 04/15] fix apocalypse win condition --- Modules/CustomRolesHelper.cs | 9 +++++---- Patches/CheckGameEndPatch.cs | 14 ++++++++++---- Patches/OutroPatch.cs | 3 ++- Resources/Lang/en_US.json | 10 ++++++---- Resources/roleColor.json | 5 +++-- main.cs | 10 ++++++---- 6 files changed, 32 insertions(+), 19 deletions(-) diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index a9bb9dbc8..2a93d6b7b 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -1967,17 +1967,17 @@ public static CountTypes GetCountTypes(this CustomRoles role) CustomRoles.Pickpocket => CustomWinner.Pickpocket, CustomRoles.Traitor => CustomWinner.Traitor, CustomRoles.Vulture => CustomWinner.Vulture, - CustomRoles.Pestilence => CustomWinner.Pestilence, + CustomRoles.Apocalypse => CustomWinner.Apocalypse, CustomRoles.Medusa => CustomWinner.Medusa, CustomRoles.Spiritcaller => CustomWinner.Spiritcaller, CustomRoles.Glitch => CustomWinner.Glitch, - CustomRoles.PlagueBearer => CustomWinner.Plaguebearer, + //CustomRoles.PlagueBearer => CustomWinner.Apocalypse, CustomRoles.Masochist => CustomWinner.Masochist, CustomRoles.Doomsayer => CustomWinner.Doomsayer, CustomRoles.Shroud => CustomWinner.Shroud, CustomRoles.Seeker => CustomWinner.Seeker, - CustomRoles.SoulCollector => CustomWinner.SoulCollector, - CustomRoles.Death => CustomWinner.Death, + //CustomRoles.SoulCollector => CustomWinner.Apocalypse, + //CustomRoles.Death => CustomWinner.Apocalypse, CustomRoles.RuthlessRomantic => CustomWinner.RuthlessRomantic, CustomRoles.Mini => CustomWinner.NiceMini, CustomRoles.Doppelganger => CustomWinner.Doppelganger, @@ -2023,6 +2023,7 @@ public static CountTypes GetCountTypes(this CustomRoles role) CountTypes.Spiritcaller => CustomRoles.Spiritcaller, CountTypes.Arsonist => CustomRoles.Arsonist, CountTypes.RuthlessRomantic => CustomRoles.RuthlessRomantic, + CountTypes.Apocalypse => CustomRoles.Apocalypse, //CountTypes.Impostor => CustomRoles.ImpostorTOHE, //CountTypes.Crew => CustomRoles.CrewmateTOHE, //CountTypes.None => throw new System.NotImplementedException(), diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index c5599969d..8345a9269 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -84,6 +84,13 @@ public static bool Prefix() CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); } break; + case CustomWinner.Apocalypse: + if ((pc.IsNeutralApocalypse()) && (countType == CountTypes.Apocalypse || pc.Is(CustomRoles.Soulless)) + && !CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) + { + CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); + } + break; case CustomWinner.Succubus: if (pc.Is(CustomRoles.Charmed) && !CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) { @@ -140,7 +147,6 @@ public static bool Prefix() break; } } - if (CustomWinnerHolder.WinnerIds.Any(x => Utils.GetPlayerById(x).IsNeutralApocalypse())) Main.AllPlayerControls.Where(x => x.IsNeutralApocalypse()).Do(x => CustomWinnerHolder.WinnerIds.Add(x.PlayerId)); if (CustomWinnerHolder.WinnerTeam is not CustomWinner.Draw and not CustomWinner.None and not CustomWinner.Error) { foreach (var pc in Main.AllPlayerControls) @@ -159,7 +165,7 @@ public static bool Prefix() foreach (var pc in Main.AllPlayerControls) { if (pc.Is(CustomRoles.Phantom) && pc.GetPlayerTaskState().IsTaskFinished && pc.Data.IsDead - && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Plaguebearer || CustomWinnerHolder.WinnerTeam == CustomWinner.Pestilence || CustomWinnerHolder.WinnerTeam == CustomWinner.SoulCollector || CustomWinnerHolder.WinnerTeam == CustomWinner.Death || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Spiritcaller || CustomWinnerHolder.WinnerTeam == CustomWinner.Quizmaster ) && (Options.PhantomSnatchesWin.GetBool() || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) //|| CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist + && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Apocalypse || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Spiritcaller || CustomWinnerHolder.WinnerTeam == CustomWinner.Quizmaster ) && (Options.PhantomSnatchesWin.GetBool() || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) //|| CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist { reason = GameOverReason.ImpostorByKill; if (!CustomWinnerHolder.CheckForConvertedWinner(pc.PlayerId)) @@ -172,7 +178,7 @@ public static bool Prefix() foreach (var pc in Main.AllPlayerControls) { if (pc.Is(CustomRoles.CursedSoul) && !pc.Data.IsDead - && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Plaguebearer || CustomWinnerHolder.WinnerTeam == CustomWinner.Pestilence || CustomWinnerHolder.WinnerTeam == CustomWinner.SoulCollector || CustomWinnerHolder.WinnerTeam == CustomWinner.Death || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Jester || CustomWinnerHolder.WinnerTeam == CustomWinner.Executioner || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) // || CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist + && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Apocalypse || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Jester || CustomWinnerHolder.WinnerTeam == CustomWinner.Executioner || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) // || CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist { reason = GameOverReason.ImpostorByKill; if (!CustomWinnerHolder.CheckForConvertedWinner(pc.PlayerId)) @@ -639,7 +645,7 @@ public static bool CheckGameEndByLivingPlayers(out GameOverReason reason) var winnerRole = winners[0].Key.GetNeutralCustomRoleFromCountType(); reason = GameOverReason.ImpostorByKill; CustomWinnerHolder.ResetAndSetWinner(winnerRole.GetNeutralCustomWinnerFromRole()); - CustomWinnerHolder.WinnerRoles.Add(winnerRole); + if (winnerRole != CustomRoles.Apocalypse) CustomWinnerHolder.WinnerRoles.Add(winnerRole); } else if (winnnerLength == 0) { diff --git a/Patches/OutroPatch.cs b/Patches/OutroPatch.cs index 272229b68..c51745c21 100644 --- a/Patches/OutroPatch.cs +++ b/Patches/OutroPatch.cs @@ -79,7 +79,8 @@ public static void Postfix(AmongUsClient __instance, [HarmonyArgument(0)] ref En } foreach (var team in CustomWinnerHolder.WinnerRoles.ToArray()) { - winner.AddRange(Main.AllPlayerControls.Where(p => p.Is(team) && !winner.Contains(p))); + if (team == CustomRoles.Apocalypse) winner.AddRange(Main.AllPlayerControls.Where(x => x.IsNeutralApocalypse() && !winner.Contains(x))); + else winner.AddRange(Main.AllPlayerControls.Where(p => p.Is(team) && !winner.Contains(p))); } Main.winnerNameList = []; diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 6b64a1434..302f384f0 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -254,6 +254,7 @@ "Collector": "Collector", "Provocateur": "Provocateur", "BloodKnight": "Blood Knight", + "Apocalypse": "Apocalypse Team", "PlagueBearer": "Plaguebearer", "Pestilence": "Pestilence", "Glitch": "Glitch", @@ -981,8 +982,8 @@ "NWitchInfoLong": "(Neutrals):\nAs the Witch, your job is to survive to the end while preventing the crew from winning the game.\nYou can make others kill just like a Puppeteer, except you can also make the Lazy Guy and/or Lazy kill since you're using magic rather than manipulation.\n\nIf you die, you lose. If crewmates win, you also lose.", "ProvocateurInfoLong": "(Neutrals):\nAs the Provocateur, you can kill any target with the kill button. If the target loses at the end of the game, the Provocateur wins with the winning team.", "BloodKnightInfoLong": "(Neutrals):\nThe Blood Knight wins when they're the last killing role alive and the amount of crewmates is lower or equal to the amount of Blood Knights. The Blood Knight gains a temporary shield after every kill that makes them immortal for a few seconds.", - "PlagueBearerInfoLong": "(Neutrals):\nAs the Plaguebearer, plague everyone using your kill button to turn into Pestilence.\nOnce you turn into Pestilence you will become immortal and gain the ability to kill.\nIn addition to this, after turning into Pestilence you will kill anyone who tries to kill you.\n\nTo win, turn into Pestilence and kill everyone.", - "PestilenceInfoLong": "(Neutrals):\nAs Pestilence, you're an unstoppable machine.\nAny attack towards you will be reflected back towards them.\nIndirect kills don't even kill you.\n\nOnly way to kill Pestilence is by vote, or by it misguessing.", + "PlagueBearerInfoLong": "(Apocalypse):\nAs the Plaguebearer, plague everyone using your kill button to turn into Pestilence.\nOnce you turn into Pestilence you will become immortal and gain the ability to kill.\nIn addition to this, after turning into Pestilence you will kill anyone who tries to kill you.\n\nTo win, turn into Pestilence and kill everyone.", + "PestilenceInfoLong": "(Apocalypse):\nAs Pestilence, you're an unstoppable machine.\nAny attack towards you will be reflected back towards them.\nIndirect kills don't even kill you.\n\nOnly way to kill Pestilence is by vote, or by it misguessing.", "TotocalcioInfoLong": "(Neutrals):\nThe Follower can use their Kill button on someone to start following them and can use the Kill button again to switch the following target. If the Follower's target wins, the Follower will win along with them. Note: The Follower can also win after they die.", "SuccubusInfoLong": "(Neutrals):\nAs the Cultist, your kill button is used to Charm others, making them win with you. To win, charm all who pose a threat and gain majority.\nDepending on settings, you may be able to charm Neutrals, and those you Charm may count as their original team, nothing, or a Cultist to determine when you win due to majority.", "NSerialKillerInfoLong": "(Neutrals):\nAs the Serial Killer, you win if you are the last player alive. Depending on settings, you will not be impacted by any harmful interactions and may have a teammate.", @@ -1016,8 +1017,8 @@ "SeekerInfoLong": "(Neutrals):\nAs the seeker, use your kill button to tag the target. If seeker tags wrong player a point is deducted and if seeker tags correct player a point will be added.\nAdditionally, the seeker will not be able to move for 5 seconds after every meeting and after getting a new target\n\n The seeker needs to collect certain number of points set by the host to win", "PixieInfoLong": "(Neutrals):\nAs the Pixie, Mark upto x amount of targets each round by using kill button on them. When the meeting starts, your job is to have one of the marked targets ejected. If unsuccessful you will suicide, except if you didn't mark any targets or all the targets are dead. The selected targets resets to 0 after the meeting ends. If you succeed you will gain a point. You see all your targets in colored names.\n\nYou win with the winning team when you have certain amounts of points set by the host.", "OccultistInfoLong": "(Neutrals):\nAs the Occultist, you can curse players or kill them.\nCursing a player works the same as spelling as a Spellcaster.", - "SoulCollectorInfoLong": "(Neutrals):\nAs a Soul Collector, you vote players to predict their death. If the prediction is correct and the target dies in the next round you collect their soul. \n\nYou win by collecting configurable number of souls set by the host", - "DeathInfoLong": "(Neutrals): \nOnce the Soul Collector has collected their needed souls, they become Death and a meeting is immediately called. If Death is not ejected by the end of this meeting, Death kills everyone and wins.", + "SoulCollectorInfoLong": "(Apocalypse):\nAs a Soul Collector, you vote players to predict their death. If the prediction is correct and the target dies in the next round you collect their soul. \n\nYou win by collecting configurable number of souls set by the host", + "DeathInfoLong": "(Apocalypse): \nOnce the Soul Collector has collected their needed souls, they become Death and a meeting is immediately called. If Death is not ejected by the end of this meeting, Death kills everyone and wins.", "SchrodingersCatInfoLong": "(Neutrals):\nAs Schrodingers Cat, if someone attempts to use the kill button on you, you will block the action and join their team. This blocking ability works only once. By default, you don't have a victory condition, meaning you win only after switching teams.\nIn Addition to this, you will be counted as nothing in the game.\n\nNote: If the killing machine attempts to use their kill button on you, the interaction is not blocked, and you will die.", "RomanticInfoLong": "(Neutrals):\nThe Romantic can pick their lover partner using their kill button (this can be done at any point of the game). Once they've picked their partner, they can use their kill button to give their partner a temporary shield which protects them from attacks. If their lover partner dies, the Romantic's role will change according to the following conditions:\n1. If their partner was an Impostor, the Romantic becomes the Refugee\n2. If their partner was a Neutral Killer, then they become Ruthless Romantic.\n3. If their partner was a Crewmate or a non-killing neutral, the Romantic becomes the Vengeful Romantic. \n\nThe Romantic wins with the winning team if their partner wins.\nNote : If your role changes your win condition will be changed accordingly", "RuthlessRomanticInfoLong": "(Neutrals):\nYou change your roles from Romantic if your partner (A neutral killer) is killed. As Ruthless Romantic, you win if you kill everyone and be the last one standing. If you win your dead partner also wins with you.", @@ -3492,6 +3493,7 @@ "AddonRoles": "【 ★ Add-ons ★ 】", "WinnerRoleText.Impostor": "Impostors Win!", "WinnerRoleText.Crewmate": "Crewmates Win!", + "WinnerRoleText.Apocalypse": "Acolytes of the Horsemen Win!", "WinnerRoleText.Terrorist": "Terrorist Wins!", "WinnerRoleText.Jester": "Jester Wins!", "WinnerRoleText.Lovers": "Lovers Win!", diff --git a/Resources/roleColor.json b/Resources/roleColor.json index ba057224c..ebbe1144b 100644 --- a/Resources/roleColor.json +++ b/Resources/roleColor.json @@ -1,4 +1,4 @@ -{ +{ "Crewmate": "#8cffff", "Engineer": "#8cffff", "Scientist": "#8cffff", @@ -230,5 +230,6 @@ "Tired": "#9cdff0", "SchrodingersCat": "#404040", "PlagueDoctor": "#ff6633", - "Rainbow": "#55FFCB" + "Rainbow": "#55FFCB", + "Apocalypse": "#CCCCCC" } diff --git a/main.cs b/main.cs index 2ed3ddf66..17f9278cf 100644 --- a/main.cs +++ b/main.cs @@ -782,6 +782,7 @@ public enum CustomRoles Workaholic, Wraith, Stealth, + Apocalypse, //two-way camp Mini, @@ -935,19 +936,20 @@ public enum CustomWinner Medusa = CustomRoles.Medusa, Spiritcaller = CustomRoles.Spiritcaller, Glitch = CustomRoles.Glitch, - Plaguebearer = CustomRoles.PlagueBearer, - Pestilence = CustomRoles.Pestilence, + //Plaguebearer = CustomRoles.PlagueBearer, + //Pestilence = CustomRoles.Pestilence, PlagueDoctor = CustomRoles.PlagueDoctor, Masochist = CustomRoles.Masochist, Doomsayer = CustomRoles.Doomsayer, Shroud = CustomRoles.Shroud, Seeker = CustomRoles.Seeker, - SoulCollector = CustomRoles.SoulCollector, - Death = CustomRoles.Death, + //SoulCollector = CustomRoles.SoulCollector, + //Death = CustomRoles.Death, RuthlessRomantic = CustomRoles.RuthlessRomantic, NiceMini = CustomRoles.Mini, Doppelganger = CustomRoles.Doppelganger, Solsticer = CustomRoles.Solsticer, + Apocalypse = CustomRoles.Apocalypse, } public enum AdditionalWinners { From 5b90b4ef1b4e429be0c62882af0a35876b946d41 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Tue, 13 Feb 2024 18:47:20 -0500 Subject: [PATCH 05/15] some pb fixes from ryuk basically just merged ryuk's "fix progress for plaguebearer in modded clients", also attempted more wincon stuff (it didnt work) and moved pb to non killing neutrals --- Modules/CustomRolesHelper.cs | 2 +- Modules/Utils.cs | 1 - Patches/CheckGameEndPatch.cs | 7 ++++++- Roles/Neutral/PlagueBearer.cs | 6 +++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index a9bb9dbc8..1ec44a157 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -508,7 +508,6 @@ CustomRoles.Shroud or CustomRoles.Virus or CustomRoles.BloodKnight or CustomRoles.Spiritcaller or - CustomRoles.PlagueBearer or CustomRoles.Agitater or CustomRoles.RuthlessRomantic; } @@ -528,6 +527,7 @@ CustomRoles.Opportunist or CustomRoles.Pursuer or CustomRoles.Shaman or CustomRoles.SoulCollector or + CustomRoles.PlagueBearer or CustomRoles.NWitch or CustomRoles.CursedSoul or CustomRoles.Doomsayer or diff --git a/Modules/Utils.cs b/Modules/Utils.cs index c742fb2a3..84b7628ec 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -2453,7 +2453,6 @@ public static Task DoNotifyRoles(bool isForMeeting = false, PlayerControl Specif if (PlagueBearer.IsPlagued(seer.PlayerId, target.PlayerId)) { TargetMark.Append($"●"); - PlagueBearer.SendRPC(seer, target); } break; diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index c5599969d..3ade81a08 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -140,7 +140,6 @@ public static bool Prefix() break; } } - if (CustomWinnerHolder.WinnerIds.Any(x => Utils.GetPlayerById(x).IsNeutralApocalypse())) Main.AllPlayerControls.Where(x => x.IsNeutralApocalypse()).Do(x => CustomWinnerHolder.WinnerIds.Add(x.PlayerId)); if (CustomWinnerHolder.WinnerTeam is not CustomWinner.Draw and not CustomWinner.None and not CustomWinner.Error) { foreach (var pc in Main.AllPlayerControls) @@ -384,7 +383,13 @@ public static bool Prefix() CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Romantic); } } + foreach (var pc in Main.AllPlayerControls.Where(x => x.IsNeutralApocalypse() && Main.AllAlivePlayerControls.All(p => p.IsNeutralApocalypse()))) { + if (!CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) + CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); + if (!CustomWinnerHolder.WinnerRoles.Contains(pc.GetCustomRole())) + CustomWinnerHolder.WinnerRoles.Add(pc.GetCustomRole()); + } foreach (var pc in Main.AllPlayerControls.Where(x => x.Is(CustomRoles.RuthlessRomantic)).ToArray()) { if (Romantic.BetPlayer.TryGetValue(pc.PlayerId, out var betTarget) && ( diff --git a/Roles/Neutral/PlagueBearer.cs b/Roles/Neutral/PlagueBearer.cs index 05f08cb8c..367ed523e 100644 --- a/Roles/Neutral/PlagueBearer.cs +++ b/Roles/Neutral/PlagueBearer.cs @@ -3,6 +3,7 @@ using TOHE.Roles.Impostor; using static TOHE.Options; using static TOHE.Translator; +using static UnityEngine.ParticleSystem.PlaybackState; namespace TOHE.Roles.Neutral; public static class PlagueBearer @@ -70,7 +71,6 @@ public static void SendRPC(PlayerControl player, PlayerControl target) AmongUsClient.Instance.FinishRpcImmediately(writer); } public static void ReceiveRPC(MessageReader reader) - { byte PlagueBearerId = reader.ReadByte(); byte PlaguedId = reader.ReadByte(); @@ -108,13 +108,13 @@ public static bool OnCheckMurder(PlayerControl killer, PlayerControl target) return false; } PlaguedList[killer.PlayerId].Add(target.PlayerId); - + SendRPC(killer, target); Utils.NotifyRoles(SpecifySeer: killer); killer.ResetKillCooldown(); killer.SetKillCooldown(); - Logger.Msg($"kill cooldown {PlagueBearerCD[killer.PlayerId]}", "PlagueBearer"); + Logger.Info($"kill cooldown {PlagueBearerCD[killer.PlayerId]}", "PlagueBearer"); return false; } From 0a9508e5ca9fd6439cc62c43b43117ab6dc5bddb Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Fri, 16 Feb 2024 22:40:09 -0500 Subject: [PATCH 06/15] Baker + few Soul Collector changes --- Modules/CustomRolesHelper.cs | 12 ++- Modules/ExtendedPlayerControl.cs | 1 + Modules/OptionHolder.cs | 4 +- Modules/RPC.cs | 3 + Modules/Utils.cs | 6 ++ Patches/HudPatch.cs | 4 + Patches/IntroPatch.cs | 2 +- Patches/MeetingHudPatch.cs | 8 +- Patches/PlayerControlPatch.cs | 7 ++ Patches/onGameStartedPatch.cs | 3 + Resources/Lang/en_US.json | 28 +++--- Resources/roleColor.json | 3 + Roles/Crewmate/Divinator.cs | 3 +- Roles/Neutral/Baker.cs | 141 +++++++++++++++++++++++++++++++ Roles/Neutral/SoulCollector.cs | 7 +- main.cs | 2 + 16 files changed, 213 insertions(+), 21 deletions(-) create mode 100644 Roles/Neutral/Baker.cs diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 70a1cecdc..32bb0f3bb 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -266,6 +266,7 @@ public static RoleTypes GetDYRole(this CustomRoles role) // Role has a kill butt CustomRoles.Spiritcaller => RoleTypes.Impostor, CustomRoles.ChiefOfPolice => RoleTypes.Impostor, CustomRoles.Quizmaster => RoleTypes.Impostor, + CustomRoles.Baker => RoleTypes.Impostor, _ => RoleTypes.GuardianAngel }; } @@ -528,6 +529,7 @@ CustomRoles.Pursuer or CustomRoles.Shaman or CustomRoles.SoulCollector or CustomRoles.PlagueBearer or + CustomRoles.Baker or CustomRoles.NWitch or CustomRoles.CursedSoul or CustomRoles.Doomsayer or @@ -608,7 +610,9 @@ public static bool IsNA(this CustomRoles role) CustomRoles.PlagueBearer or CustomRoles.Pestilence or CustomRoles.SoulCollector or - CustomRoles.Death; + CustomRoles.Death or + CustomRoles.Baker or + CustomRoles.Famine; } public static bool IsSnitchTarget(this CustomRoles role) { @@ -645,7 +649,6 @@ CustomRoles.Virus or CustomRoles.Succubus or CustomRoles.BloodKnight or CustomRoles.Spiritcaller or - CustomRoles.PlagueBearer or CustomRoles.Agitater or CustomRoles.RuthlessRomantic or CustomRoles.Shroud or @@ -799,6 +802,8 @@ CustomRoles.Pursuer or CustomRoles.Agitater or CustomRoles.PlagueBearer or CustomRoles.Pestilence or + CustomRoles.Baker or + CustomRoles.Famine or CustomRoles.Pirate or CustomRoles.Seeker or CustomRoles.Pixie or @@ -998,6 +1003,7 @@ CustomRoles.Pursuer or CustomRoles.Imitator or CustomRoles.Agitater or CustomRoles.PlagueBearer or + CustomRoles.Baker or CustomRoles.Pirate or CustomRoles.FFF or CustomRoles.Totocalcio or @@ -1893,6 +1899,8 @@ public static CountTypes GetCountTypes(this CustomRoles role) CustomRoles.PlagueBearer => CountTypes.Apocalypse, CustomRoles.SoulCollector => CountTypes.Apocalypse, CustomRoles.Death => CountTypes.Apocalypse, + CustomRoles.Baker => CountTypes.Apocalypse, + CustomRoles.Famine => CountTypes.Apocalypse, CustomRoles.Agitater => CountTypes.Agitater, CustomRoles.Parasite => CountTypes.Impostor, CustomRoles.NSerialKiller => CountTypes.NSerialKiller, diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 7468532cc..2a5c9b19c 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -550,6 +550,7 @@ public static bool CanUseKillButton(this PlayerControl pc) CustomRoles.Spiritcaller => pc.IsAlive(), CustomRoles.PlagueBearer => pc.IsAlive(), CustomRoles.Pestilence => pc.IsAlive(), + CustomRoles.Baker => pc.IsAlive(), CustomRoles.Pirate => pc.IsAlive(), CustomRoles.Pixie => pc.IsAlive(), CustomRoles.Seeker => pc.IsAlive(), diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index 1225b2f1d..f2c740b09 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -2448,10 +2448,12 @@ public static void Load() .SetGameMode(CustomGameMode.Standard) .SetColor(new Color32(127, 140, 141, byte.MaxValue)); + Baker.SetupCustomOption(); + PlagueBearer.SetupCustomOption(); SoulCollector.SetupCustomOption(); - + #endregion #region Add-Ons Settings diff --git a/Modules/RPC.cs b/Modules/RPC.cs index 9b5c85412..52f7b6bd3 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -1421,6 +1421,9 @@ public static void SetCustomRole(byte targetId, CustomRoles role) case CustomRoles.PlagueBearer: PlagueBearer.Add(targetId); break; + case CustomRoles.Baker: + Baker.Add(targetId); + break; case CustomRoles.Tracker: Tracker.Add(targetId); break; diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 84b7628ec..be8e06d9a 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -547,6 +547,8 @@ public static bool HasTasks(GameData.PlayerInfo p, bool ForRecompute = true) case CustomRoles.Jinx: case CustomRoles.SoulCollector: case CustomRoles.Death: + case CustomRoles.Baker: + case CustomRoles.Famine: case CustomRoles.SchrodingersCat: case CustomRoles.Parasite: case CustomRoles.Crusader: @@ -968,6 +970,10 @@ public static string GetProgressText(byte playerId, bool comms = false) var plagued = PlagueBearer.PlaguedPlayerCount(playerId); ProgressText.Append(ColorString(GetRoleColor(CustomRoles.PlagueBearer).ShadeColor(0.25f), $"({plagued.Item1}/{plagued.Item2})")); break; + case CustomRoles.Baker: + var breaded = Baker.BreadedPlayerCount(playerId); + ProgressText.Append(ColorString(GetRoleColor(CustomRoles.Baker).ShadeColor(0.25f), $"{breaded.Item1}/{breaded.Item2})")); + break; case CustomRoles.Doomsayer: var doomsayerguess = Doomsayer.GuessedPlayerCount(playerId); ProgressText.Append(ColorString(GetRoleColor(CustomRoles.Doomsayer).ShadeColor(0.25f), $"({doomsayerguess.Item1}/{doomsayerguess.Item2})")); diff --git a/Patches/HudPatch.cs b/Patches/HudPatch.cs index 3b5cd3396..858beac92 100644 --- a/Patches/HudPatch.cs +++ b/Patches/HudPatch.cs @@ -139,6 +139,10 @@ public static void Postfix(HudManager __instance) __instance.ReportButton.OverrideText(GetString("ReportButtonText")); __instance.KillButton.OverrideText(GetString("InfectiousKillButtonText")); break; + case CustomRoles.Baker: + __instance.ReportButton.OverrideText(GetString("ReportButtonText")); + __instance.KillButton.OverrideText(GetString("BakerKillButtonText")); + break; case CustomRoles.Pirate: __instance.ReportButton.OverrideText(GetString("ReportButtonText")); __instance.KillButton.OverrideText(GetString("PirateDuelButtonText")); diff --git a/Patches/IntroPatch.cs b/Patches/IntroPatch.cs index 389cba4ab..05abd332f 100644 --- a/Patches/IntroPatch.cs +++ b/Patches/IntroPatch.cs @@ -513,7 +513,7 @@ public static bool Prefix(IntroCutscene __instance, ref Il2CppSystem.Collections __instance.overlayHandle.color = Palette.CrewmateBlue; return false; } - else if (role is CustomRoles.Romantic or CustomRoles.Doppelganger or CustomRoles.Pyromaniac or CustomRoles.Huntsman or CustomRoles.RuthlessRomantic or CustomRoles.VengefulRomantic or CustomRoles.NSerialKiller or CustomRoles.Jackal or CustomRoles.Seeker or CustomRoles.Pixie or CustomRoles.Agitater or CustomRoles.CursedSoul or CustomRoles.Pirate or CustomRoles.Amnesiac or CustomRoles.Arsonist or CustomRoles.Sidekick or CustomRoles.Innocent or CustomRoles.Pelican or CustomRoles.Pursuer or CustomRoles.Revolutionist or CustomRoles.FFF or CustomRoles.Gamer or CustomRoles.Glitch or CustomRoles.Juggernaut or CustomRoles.DarkHide or CustomRoles.Provocateur or CustomRoles.BloodKnight or CustomRoles.NSerialKiller or CustomRoles.Werewolf or CustomRoles.Maverick or CustomRoles.NWitch or CustomRoles.Shroud or CustomRoles.Totocalcio or CustomRoles.Succubus or CustomRoles.Pelican or CustomRoles.Infectious or CustomRoles.Virus or CustomRoles.Pickpocket or CustomRoles.Traitor or CustomRoles.PlagueBearer or CustomRoles.Pestilence or CustomRoles.Spiritcaller or CustomRoles.Necromancer or CustomRoles.Medusa or CustomRoles.HexMaster or CustomRoles.Wraith or CustomRoles.Jinx or CustomRoles.Poisoner or CustomRoles.PotionMaster) //or CustomRoles.Occultist + else if (role is CustomRoles.Romantic or CustomRoles.Doppelganger or CustomRoles.Pyromaniac or CustomRoles.Huntsman or CustomRoles.RuthlessRomantic or CustomRoles.VengefulRomantic or CustomRoles.NSerialKiller or CustomRoles.Jackal or CustomRoles.Seeker or CustomRoles.Pixie or CustomRoles.Agitater or CustomRoles.CursedSoul or CustomRoles.Pirate or CustomRoles.Amnesiac or CustomRoles.Arsonist or CustomRoles.Sidekick or CustomRoles.Innocent or CustomRoles.Pelican or CustomRoles.Pursuer or CustomRoles.Revolutionist or CustomRoles.FFF or CustomRoles.Gamer or CustomRoles.Glitch or CustomRoles.Juggernaut or CustomRoles.DarkHide or CustomRoles.Provocateur or CustomRoles.BloodKnight or CustomRoles.NSerialKiller or CustomRoles.Werewolf or CustomRoles.Maverick or CustomRoles.NWitch or CustomRoles.Shroud or CustomRoles.Totocalcio or CustomRoles.Succubus or CustomRoles.Pelican or CustomRoles.Infectious or CustomRoles.Virus or CustomRoles.Pickpocket or CustomRoles.Traitor or CustomRoles.PlagueBearer or CustomRoles.Pestilence or CustomRoles.Baker or CustomRoles.Spiritcaller or CustomRoles.Necromancer or CustomRoles.Medusa or CustomRoles.HexMaster or CustomRoles.Wraith or CustomRoles.Jinx or CustomRoles.Poisoner or CustomRoles.PotionMaster) //or CustomRoles.Occultist { yourTeam = new Il2CppSystem.Collections.Generic.List(); yourTeam.Add(PlayerControl.LocalPlayer); diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index a266342fd..bfcb485f3 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -930,10 +930,14 @@ void AddMsg(string text, byte sendTo = 255, string title = "") string separator = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.English or SupportedLangs.Russian ? "], [" : "】, 【"; AddMsg(string.Format(GetString("BaitAdviceAlive"), string.Join(separator, baitAliveList)), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Bait), GetString("BaitAliveTitle"))); } - //Death Notify + //Apocalypse Notify if (CustomRoles.Death.RoleExist()) { - AddMsg(string.Format(GetString("SoulCollectorTransform")), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.SoulCollector), GetString("ApocalypseIsNigh"))); + AddMsg(string.Format(GetString("SoulCollectorTransform")), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Death), GetString("ApocalypseIsNigh"))); + } + if (CustomRoles.Baker.RoleExist()) + { + AddMsg(string.Format(GetString("BakerTransform")), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Famine), GetString("ApocalypseIsNigh"))); } string MimicMsg = ""; foreach (var pc in Main.AllPlayerControls) diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 639e130b4..65d3d02a3 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -470,6 +470,9 @@ public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerC if (!PlagueBearer.OnCheckMurder(killer, target)) return false; break; + case CustomRoles.Baker: + Baker.OnCheckMurder(killer, target); + return false; case CustomRoles.Pirate: if (!Pirate.OnCheckMurder(killer, target)) return false; @@ -2638,6 +2641,10 @@ public static void Postfix(PlayerControl __instance) PlagueBearer.OnFixedUpdate(player); break; + case CustomRoles.Baker: + Baker.OnFixedUpdate(player); + break; + case CustomRoles.SoulCollector: SoulCollector.BecomeDeath(player); break; diff --git a/Patches/onGameStartedPatch.cs b/Patches/onGameStartedPatch.cs index a593933ab..3297dfb26 100644 --- a/Patches/onGameStartedPatch.cs +++ b/Patches/onGameStartedPatch.cs @@ -1001,6 +1001,9 @@ public static void Postfix() case CustomRoles.PlagueBearer: PlagueBearer.Add(pc.PlayerId); break; + case CustomRoles.Baker: + Baker.Add(pc.PlayerId); + break; case CustomRoles.ParityCop: ParityCop.Add(pc.PlayerId); break; diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 302f384f0..3ec4160f1 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -705,8 +705,8 @@ "TaskinatorInfo": "Silent tasks, deadly blasts", "BenefactorInfo": "Task complete, shield elite!", "MedusaInfo": "Stone bodies by reporting them", - "BakerInfo": "Complete your tasks to poison your bread", - "FamineInfo": "Give out your poisoned bread", + "BakerInfo": "Feed Players Bread in order to become Famine", + "FamineInfo": "Starve Everyone", "SpiritcallerInfo": "Turn Players into Evil Spirits", "AmnesiacInfo": "Remember the role of a dead body", "ImitatorInfo": "Imitate a player's role", @@ -1002,8 +1002,8 @@ "TaskinatorInfoLong": "(Neutrals):\nAs the Taskinator, whenever you complete a task, the task will be bombed. When another player completes the bombed task, the bomb will detonate and the player will die.\n\nYou win if you survive till the end and Crew doesn't win.\n\n Note: Taskinator bombs ignore all protection.", "BenefactorInfoLong": "(Crewmates):\nAs the Benefactor, whenever you complete a task, the task will be marked. When another player completes the marked task, they get a temporary shield.\n\n Note: Shield only protects from direct kill attacks", "MedusaInfoLong": "(Neutrals):\nAs the Medusa, you can stone bodies much like cleaning a body.\nStoned bodies cannot be reported.\n\nKill everyone to win.", - "BakerInfoLong": "(Neutrals):\nAs the Baker, you give out bread.\nAt the beginning of each meeting, messages will be given out.\n\nIf you finish your tasks, you become Famine when a meeting is called.", - "FamineInfoLong": "(Neutrals):\nAs the Famine, you give out poisoned bread.\nPlayers with poisoned bread die at the end of the meeting if the Famine is not exiled.\n\nIf the Famine is alive at the end of the game, they steal the win.", + "BakerInfoLong":"(Apocalypse): \nAs the Baker, you can use your kill button on a player per round to give them Bread. \nOnce a set amount of players are alive with bread, you become Famine.", + "FamineInfoLong": "(Apocalypse): \nIf Famine is not voted out after the meeting they become Famine, every player without bread will starve (excluding other Apocalypse members).", "SpiritcallerInfoLong": "(Neutrals):\nAs the Spiritcaller, your victims become Evil Spirits after they die. These spirits can help you win by freezing other players for a short time and/or blocking their vision. Alternatively, the spirits can give you a shield that protects you briefly from an attempted kill.", "AmnesiacInfoLong": "(Neutrals):\nAs the Amnesiac, use your report button to remember a role.\n\nIf the target was an Impostor, you'll become a Refugee.\nIf the target was a crewmate, you'll become the target role if compatible (otherwise you become an Engineer).\nIf the target was a passive neutral or a neutral killer not specified, you'll become the role defined in the settings.\nIf the target was a neutral killer of a select few, you'll become the role they are.", "ImitatorInfoLong": "(Neutrals):\nAs the Imitator, use your kill button to imitate a player.\n\nYou'll either become a Sheriff, a Refugee or some Neutral", @@ -1017,7 +1017,7 @@ "SeekerInfoLong": "(Neutrals):\nAs the seeker, use your kill button to tag the target. If seeker tags wrong player a point is deducted and if seeker tags correct player a point will be added.\nAdditionally, the seeker will not be able to move for 5 seconds after every meeting and after getting a new target\n\n The seeker needs to collect certain number of points set by the host to win", "PixieInfoLong": "(Neutrals):\nAs the Pixie, Mark upto x amount of targets each round by using kill button on them. When the meeting starts, your job is to have one of the marked targets ejected. If unsuccessful you will suicide, except if you didn't mark any targets or all the targets are dead. The selected targets resets to 0 after the meeting ends. If you succeed you will gain a point. You see all your targets in colored names.\n\nYou win with the winning team when you have certain amounts of points set by the host.", "OccultistInfoLong": "(Neutrals):\nAs the Occultist, you can curse players or kill them.\nCursing a player works the same as spelling as a Spellcaster.", - "SoulCollectorInfoLong": "(Apocalypse):\nAs a Soul Collector, you vote players to predict their death. If the prediction is correct and the target dies in the next round you collect their soul. \n\nYou win by collecting configurable number of souls set by the host", + "SoulCollectorInfoLong": "(Apocalypse):\nAs a Soul Collector, you vote players to predict their death. If the prediction is correct and the target dies in the next round you collect their soul. \n\nOnce you collect the configurable amount of souls, you become Death.", "DeathInfoLong": "(Apocalypse): \nOnce the Soul Collector has collected their needed souls, they become Death and a meeting is immediately called. If Death is not ejected by the end of this meeting, Death kills everyone and wins.", "SchrodingersCatInfoLong": "(Neutrals):\nAs Schrodingers Cat, if someone attempts to use the kill button on you, you will block the action and join their team. This blocking ability works only once. By default, you don't have a victory condition, meaning you win only after switching teams.\nIn Addition to this, you will be counted as nothing in the game.\n\nNote: If the killing machine attempts to use their kill button on you, the interaction is not blocked, and you will die.", "RomanticInfoLong": "(Neutrals):\nThe Romantic can pick their lover partner using their kill button (this can be done at any point of the game). Once they've picked their partner, they can use their kill button to give their partner a temporary shield which protects them from attacks. If their lover partner dies, the Romantic's role will change according to the following conditions:\n1. If their partner was an Impostor, the Romantic becomes the Refugee\n2. If their partner was a Neutral Killer, then they become Ruthless Romantic.\n3. If their partner was a Crewmate or a non-killing neutral, the Romantic becomes the Vengeful Romantic. \n\nThe Romantic wins with the winning team if their partner wins.\nNote : If your role changes your win condition will be changed accordingly", @@ -2708,14 +2708,24 @@ "ChanceToMiss": "Chance to miss a kill", "ImpCanBeClumsy": "Impostors can become Clumsy", "NeutralCanBeClumsy": "Neutral Killers can become Clumsy", + "SoulCollectorPointsToWin": "Required number of souls", "SoulCollectorTarget": "You have predicted the death of {0}", "SoulCollectorTitle": "SOUL COLLECTOR", "CollectOwnSoulOpt": "Can collect their own soul", "SoulCollectorSelfVote": "Host settings do not allow you to collect your own soul", "SoulCollectorToDeath": "You have become Death!!!", - "SoulCollectorTransform": "Now Soul Collector has become Death, Destroyer of Worlds and Horseman of the Apocalypse!

Find them and vote them out before it's too late!", + "SoulCollectorTransform": "Now Soul Collector has become Death, Destroyer of Worlds and Horseman of the Apocalypse!

Find them and vote them out before it's too late!", + "CallMeetingIfDeath": "Call a meeting immediately after Death transforms", "ApocalypseIsNigh": "[ The Apocalypse Is Nigh! ]", + "BakerToFamine": "You have become Famine!!!", + "BakerTransform": "The Baker has transformed into Famine, Horseman of the Apocalypse! A famine has begun!", + "BakerAlreadyBreaded": "That player already has bread!", + "BakerBreadUsedAlready": "You've already given a player bread this round!", + "BakerBreaded": "Player given bread", + "BakerBreadNeededToTransform": "Required number of bread to become Famine", + "BakerCantBreadApoc": "You cannot give other Apocalypse members bread!", + "ChronomancerKillCooldown": "Time to fully charge the kill button", "Occultist Text Strings": "", "CursesLookLikeSpells": "Curses look like spells", @@ -2951,12 +2961,6 @@ "RetributionistCanKillNum": "Max retributions", "RetributionistKillTooManyDead": "Too many players are dead, you can't retribute.", "MinimumPlayersAliveToRetri": "Maximum players needed to block retributions", - "BakerChangeChances": "Chance that Baker turns into Famine", - "BakerChange": "The Baker has turned into Famine!\nThe bread is now poisoned.\nIf the Famine is not exiled, all players with poisoned bread die.", - "BakerChangeNow": "A player has poisoned bread!\nExile the Famine or that player dies.", - "BakerChangeNONE": "The Famine has not given out poisoned bread.\nNobody will die of poison, but you should still exile the Famine.", - "PanAliveMessageTitle": "BAKER ", - "PanAlive": "The Baker has given out bread.", "ImmuneToAttacksWhenTasksDone": "Immune to attacks on task completion", "OptionWatcher": "Can sell Watcher", "OptionKnighted": "Can sell Knighted", diff --git a/Resources/roleColor.json b/Resources/roleColor.json index ebbe1144b..9dacfd7dc 100644 --- a/Resources/roleColor.json +++ b/Resources/roleColor.json @@ -148,6 +148,9 @@ "Pixie": "#00FF00", "SoulCollector": "#A675A1", "Death": "#ff174f", + "Baker": "#bf9f7a", + "Famine": "#83461c", + "Apocalypse": "#ff174f", "Imitator": "#B3D94C", "Doppelganger": "#f6f4a3", "GM": "#ff5b70", diff --git a/Roles/Crewmate/Divinator.cs b/Roles/Crewmate/Divinator.cs index 70b5a57d5..e5104352c 100644 --- a/Roles/Crewmate/Divinator.cs +++ b/Roles/Crewmate/Divinator.cs @@ -245,7 +245,8 @@ public static void OnVote(PlayerControl player, PlayerControl target) CustomRoles.Lawyer, CustomRoles.Snitch, CustomRoles.Disperser, - CustomRoles.Doctor], + CustomRoles.Doctor, + CustomRoles.Baker], [CustomRoles.Councillor, CustomRoles.Dictator, diff --git a/Roles/Neutral/Baker.cs b/Roles/Neutral/Baker.cs new file mode 100644 index 000000000..c5f991469 --- /dev/null +++ b/Roles/Neutral/Baker.cs @@ -0,0 +1,141 @@ +using Hazel; +using System.Collections.Generic; +using TOHE.Roles.Impostor; +using static TOHE.Options; +using static TOHE.Translator; +using static UnityEngine.ParticleSystem.PlaybackState; + +namespace TOHE.Roles.Neutral; +public static class Baker +{ + private static readonly int Id = 27800; + public static List playerIdList = []; + public static bool IsEnable = false; + public static Dictionary> BreadList = []; + public static bool CanUseAbility; + + public static OptionItem BreadNeededToTransform; + + public static void SetupCustomOption() + { + SetupRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.Baker); + BreadNeededToTransform = IntegerOptionItem.Create(Id + 10, "BakerBreadNeededToTransform", new(1, 5, 1), 3, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Baker]) + .SetValueFormat(OptionFormat.Times); + } + public static void Init() + { + playerIdList = []; + BreadList = []; + IsEnable = false; + } + public static void Add(byte playerId) + { + playerIdList.Add(playerId); + BreadList[playerId] = []; + IsEnable = true; + } + + public static void SendRPC(PlayerControl player, PlayerControl target) + { + MessageWriter writer; + writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.setPlaguedPlayer, SendOption.Reliable, -1);//RPCによる同期 + writer.Write(player.PlayerId); + writer.Write(target.PlayerId); + AmongUsClient.Instance.FinishRpcImmediately(writer); + } + public static void ReceiveRPC(MessageReader reader) + { + byte BakerId = reader.ReadByte(); + byte BreadHolderId = reader.ReadByte(); + BreadList[BakerId].Add(BreadHolderId); + } + public static string GetProgressText(byte playerId) => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Baker).ShadeColor(0.25f), BreadList.TryGetValue(playerId, out var x) ? $"({x}/{BreadNeededToTransform.GetInt()})" : "Invalid"); + public static bool HasBread(byte pc, byte target) + { + return BreadList[pc].Contains(target); + } + public static bool OnCheckMurder(PlayerControl killer, PlayerControl target) + { + if (!CanUseAbility) + { + killer.Notify(GetString("BakerBreadUsedAlready")); + return false; + } + if (target.IsNeutralApocalypse()) + { + killer.Notify(GetString("BakerCantBreadApoc")); + return false; + } + if (HasBread(killer.PlayerId, target.PlayerId)) + { + killer.Notify(GetString("BakerAlreadyBreaded")); + return false; + } + BreadList[killer.PlayerId].Add(target.PlayerId); + SendRPC(killer, target); + Utils.NotifyRoles(SpecifySeer: killer); + killer.Notify(GetString("BakerBreaded")); + + Main.AllPlayerKillCooldown[killer.PlayerId] = 100000; + + Logger.Info($"Bread given to "+target.GetRealName(), "Baker"); + CanUseAbility = false; + return false; + } + public static void OnReportDeadBody() + { + CanUseAbility = true; + } + public static (int, int) BreadedPlayerCount(byte playerId) + { + int breaded = 0, all = BreadNeededToTransform.GetValue(); + foreach (var pc in Main.AllAlivePlayerControls) + { + if (pc.PlayerId == playerId) continue; + + if (HasBread(playerId, pc.PlayerId)) + breaded++; + } + return (breaded, all); + } + public static bool AllHasBread(PlayerControl player) + { + if (!player.Is(CustomRoles.Baker)) return false; + + var (countItem1, countItem2) = BreadedPlayerCount(player.PlayerId); + return countItem1 >= countItem2; + } + public static void OnFixedUpdate(PlayerControl player) + { + if (!AllHasBread(player)) return; + + player.RpcSetCustomRole(CustomRoles.Famine); + player.Notify(GetString("BakerToFamine")); + player.RpcGuardAndKill(player); + + } + public static void KillIfNotEjected(PlayerControl player) + { + var deathList = new List(); + var baker = player.PlayerId; + foreach (var pc in Main.AllAlivePlayerControls) + { + var notBaker = pc.PlayerId; + if (pc.IsNeutralApocalypse() || HasBread(baker, notBaker)) continue; + if (player != null && player.IsAlive()) + { + if (!Main.AfterMeetingDeathPlayers.ContainsKey(pc.PlayerId)) + { + pc.SetRealKiller(player); + deathList.Add(pc.PlayerId); + } + else + { + Main.AfterMeetingDeathPlayers.Remove(pc.PlayerId); + } + } + } + CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Armageddon, [.. deathList]); + } +} + diff --git a/Roles/Neutral/SoulCollector.cs b/Roles/Neutral/SoulCollector.cs index f6ac36824..f4f3b747a 100644 --- a/Roles/Neutral/SoulCollector.cs +++ b/Roles/Neutral/SoulCollector.cs @@ -17,6 +17,7 @@ public static class SoulCollector public static OptionItem SoulCollectorPointsOpt; public static OptionItem CollectOwnSoulOpt; + public static OptionItem CallMeetingIfDeath; public static void SetupCustomOption() { @@ -24,6 +25,7 @@ public static void SetupCustomOption() SoulCollectorPointsOpt = IntegerOptionItem.Create(Id + 10, "SoulCollectorPointsToWin", new(1, 14, 1), 3, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]) .SetValueFormat(OptionFormat.Times); CollectOwnSoulOpt = BooleanOptionItem.Create(Id + 11, "CollectOwnSoulOpt", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]); + CallMeetingIfDeath = BooleanOptionItem.Create(Id + 12, "CallMeetingIfDeath", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]); } public static void Init() { @@ -125,7 +127,8 @@ public static void BecomeDeath(PlayerControl player) player.RpcSetCustomRole(CustomRoles.Death); player.Notify(GetString("SoulCollectorToDeath")); - PlayerControl.LocalPlayer.NoCheckStartMeeting(null, force: true); + player.RpcGuardAndKill(player); + if (CallMeetingIfDeath.GetBool()) PlayerControl.LocalPlayer.NoCheckStartMeeting(null, force: true); KillIfNotEjected(player); } @@ -134,7 +137,7 @@ public static void KillIfNotEjected(PlayerControl player) var deathList = new List(); foreach (var pc in Main.AllAlivePlayerControls) { - if (pc.GetCustomRole() is CustomRoles.PlagueBearer or CustomRoles.Pestilence or CustomRoles.Death or CustomRoles.SoulCollector) continue; + if (pc.IsNeutralApocalypse()) continue; if (player != null && player.IsAlive()) { if (!Main.AfterMeetingDeathPlayers.ContainsKey(pc.PlayerId)) diff --git a/main.cs b/main.cs index 17f9278cf..8d62d5193 100644 --- a/main.cs +++ b/main.cs @@ -712,6 +712,7 @@ public enum CustomRoles Agitater, Amnesiac, Arsonist, + Baker, Bandit, BloodKnight, Collector, @@ -722,6 +723,7 @@ public enum CustomRoles Doomsayer, Doppelganger, Executioner, + Famine, Totocalcio, //follower Glitch, God, From c6c38839560a64a519bee087b6253c59bcdc34c6 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Sat, 17 Feb 2024 11:31:01 -0500 Subject: [PATCH 07/15] Update PlayerControlPatch.cs --- Patches/PlayerControlPatch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 65d3d02a3..74fc881b5 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -471,7 +471,7 @@ public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerC return false; break; case CustomRoles.Baker: - Baker.OnCheckMurder(killer, target); + if (!Baker.OnCheckMurder(killer, target)); return false; case CustomRoles.Pirate: if (!Pirate.OnCheckMurder(killer, target)) From 5763445fa054fce90413e3d4d7d4fd29a148309e Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Sat, 17 Feb 2024 20:24:15 -0500 Subject: [PATCH 08/15] Baker stuff Baker and Famine should be fully added now also target NA's added for some roles, i dont know every role that would need this setting --- Modules/GameState.cs | 1 + Modules/RPC.cs | 4 ++++ Modules/Utils.cs | 7 +++---- Patches/CheckGameEndPatch.cs | 2 -- Patches/MeetingHudPatch.cs | 2 +- Patches/PlayerControlPatch.cs | 7 +++++-- Patches/onGameStartedPatch.cs | 1 + Resources/Lang/en_US.json | 5 +++++ Roles/AddOns/Common/Susceptible.cs | 10 ++++++++++ Roles/Crewmate/Captain.cs | 4 +++- Roles/Crewmate/Jailer.cs | 2 ++ Roles/Crewmate/Psychic.cs | 2 ++ Roles/Neutral/Baker.cs | 25 ++++++++++++++++++------- 13 files changed, 55 insertions(+), 17 deletions(-) diff --git a/Modules/GameState.cs b/Modules/GameState.cs index 0bef11f84..c836e6f5e 100644 --- a/Modules/GameState.cs +++ b/Modules/GameState.cs @@ -364,6 +364,7 @@ public enum DeathReason Retribution, WrongAnswer, Armageddon, + Starved, //Please add all new roles with deathreason & new deathreason in Susceptible.CallEnabledAndChange etc = -1, diff --git a/Modules/RPC.cs b/Modules/RPC.cs index 52f7b6bd3..26b587725 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -172,6 +172,7 @@ enum CustomRPC SyncMiniCrewAge, SyncSabotageMasterSkill, QuizmasterMarkPlayer, + SetBreadedPlayer, //FFA SyncFFAPlayer, SyncFFANameNotify, @@ -430,6 +431,9 @@ public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] byte c case CustomRPC.setPlaguedPlayer: PlagueBearer.ReceiveRPC(reader); break; + case CustomRPC.SetBreadedPlayer: + Baker.ReceiveRPC(reader); + break; case CustomRPC.SetDrawPlayer: byte RevolutionistId = reader.ReadByte(); byte DrawId = reader.ReadByte(); diff --git a/Modules/Utils.cs b/Modules/Utils.cs index be8e06d9a..ac53606cb 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -717,6 +717,9 @@ public static string GetProgressText(byte playerId, bool comms = false) case CustomRoles.SoulCollector: ProgressText.Append(SoulCollector.GetProgressText(playerId)); break; + case CustomRoles.Baker: + ProgressText.Append(Baker.GetProgressText(playerId)); + break; case CustomRoles.Pixie: ProgressText.Append(Pixie.GetProgressText(playerId)); break; @@ -970,10 +973,6 @@ public static string GetProgressText(byte playerId, bool comms = false) var plagued = PlagueBearer.PlaguedPlayerCount(playerId); ProgressText.Append(ColorString(GetRoleColor(CustomRoles.PlagueBearer).ShadeColor(0.25f), $"({plagued.Item1}/{plagued.Item2})")); break; - case CustomRoles.Baker: - var breaded = Baker.BreadedPlayerCount(playerId); - ProgressText.Append(ColorString(GetRoleColor(CustomRoles.Baker).ShadeColor(0.25f), $"{breaded.Item1}/{breaded.Item2})")); - break; case CustomRoles.Doomsayer: var doomsayerguess = Doomsayer.GuessedPlayerCount(playerId); ProgressText.Append(ColorString(GetRoleColor(CustomRoles.Doomsayer).ShadeColor(0.25f), $"({doomsayerguess.Item1}/{doomsayerguess.Item2})")); diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index ca7276e8e..14531b020 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -394,8 +394,6 @@ public static bool Prefix() if (!CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); - if (!CustomWinnerHolder.WinnerRoles.Contains(pc.GetCustomRole())) - CustomWinnerHolder.WinnerRoles.Add(pc.GetCustomRole()); } foreach (var pc in Main.AllPlayerControls.Where(x => x.Is(CustomRoles.RuthlessRomantic)).ToArray()) { diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index bfcb485f3..13de94457 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -935,7 +935,7 @@ void AddMsg(string text, byte sendTo = 255, string title = "") { AddMsg(string.Format(GetString("SoulCollectorTransform")), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Death), GetString("ApocalypseIsNigh"))); } - if (CustomRoles.Baker.RoleExist()) + if (CustomRoles.Famine.RoleExist()) { AddMsg(string.Format(GetString("BakerTransform")), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Famine), GetString("ApocalypseIsNigh"))); } diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 65d3d02a3..79f6ba1e2 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -471,8 +471,9 @@ public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerC return false; break; case CustomRoles.Baker: - Baker.OnCheckMurder(killer, target); - return false; + if (!Baker.OnCheckMurder(killer, target)) + return false; + break; case CustomRoles.Pirate: if (!Pirate.OnCheckMurder(killer, target)) return false; @@ -1476,6 +1477,7 @@ public static void Postfix(PlayerControl __instance, [HarmonyArgument(0)] Player if (Tracefinder.IsEnable) Tracefinder.OnPlayerDead(target); if (Vulture.IsEnable) Vulture.OnPlayerDead(target); if (SoulCollector.IsEnable) SoulCollector.OnPlayerDead(target); + if (Baker.IsEnable) Baker.OnPlayerDead(target); if (Medic.IsEnable) Medic.IsDead(target); Utils.AfterPlayerDeathTasks(target); @@ -2338,6 +2340,7 @@ public static void AfterReportTasks(PlayerControl player, GameData.PlayerInfo ta if (Huntsman.IsEnable) Huntsman.OnReportDeadBody(); if (SerialKiller.IsEnable) SerialKiller.OnReportDeadBody(); if (SoulCollector.IsEnable) SoulCollector.OnReportDeadBody(); + if (Baker.IsEnable) Baker.OnReportDeadBody(); if (Puppeteer.IsEnable) Puppeteer.OnReportDeadBody(); if (Sniper.IsEnable) Sniper.OnReportDeadBody(); if (Undertaker.IsEnable) Undertaker.OnReportDeadBody(); diff --git a/Patches/onGameStartedPatch.cs b/Patches/onGameStartedPatch.cs index 3297dfb26..40de198c7 100644 --- a/Patches/onGameStartedPatch.cs +++ b/Patches/onGameStartedPatch.cs @@ -339,6 +339,7 @@ public static void Postfix(AmongUsClient __instance) Spiritcaller.Init(); Lurker.Init(); PlagueBearer.Init(); + Baker.Init(); Reverie.Init(); Doomsayer.Init(); Pirate.Init(); diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 3ec4160f1..b45fc2425 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -1649,6 +1649,7 @@ "NBareRed": "Neutral Benign can be red", "NEareRed": "Neutral Evil can be red", "NCareRed": "Neutral Chaos can be red", + "NAareRed": "Neutral Apocalypse can be red", "CrewKillingRed": "Crewmate Killings can be red", "PsychicCanSeeNum": "Max number of red names", "PsychicFresh": "New red names every meeting", @@ -2012,6 +2013,7 @@ "DeathReason.Targeted": "Targeted", "DeathReason.Retribution": "Retribution", "DeathReason.Armageddon": "Armageddon", + "DeathReason.Starved": "Starved", "OnlyEnabledDeathReasons": "Only Enabled Death Reasons", "Alive": "Alive", "Win": " Wins!", @@ -2725,6 +2727,7 @@ "BakerBreaded": "Player given bread", "BakerBreadNeededToTransform": "Required number of bread to become Famine", "BakerCantBreadApoc": "You cannot give other Apocalypse members bread!", + "BakerKillButtonText": "Bread", "ChronomancerKillCooldown": "Time to fully charge the kill button", "Occultist Text Strings": "", @@ -2993,6 +2996,7 @@ "CaptainCanTargetNE": "Captain can target Neutral Evil", "CaptainCanTargetNC": "Captain can target Neutral Chaos", "CaptainCanTargetNK": "Captain can target Neutral Killer", + "CaptainCanTargetNA": "Captain can target Neutral Apocalypse", "CaptainSpeedReduced": "Captain reduced your speed", "CaptainRevealTaskRequired": "Number of tasks completed after which Captain is revealed", "CaptainSlowTaskRequired": "Number of tasks completed after which target speed is reduced", @@ -3024,6 +3028,7 @@ "JailerNCCanBeExe": "Can execute Neutral Chaos", "JailerNECanBeExe": "Can execute Neutral Evil", "JailerNKCanBeExe": "Can execute Neutral Killing", + "JailerNACanBeExe": "Can execute Neutral Apocalypse", "JailerCKCanBeExe": "Can execute Crew Killing", "JailerTargetAlreadySelected": "You have already selected a target", "SuccessfullyJailed": "Target successfully jailed", diff --git a/Roles/AddOns/Common/Susceptible.cs b/Roles/AddOns/Common/Susceptible.cs index 388b972ff..38f82eab0 100644 --- a/Roles/AddOns/Common/Susceptible.cs +++ b/Roles/AddOns/Common/Susceptible.cs @@ -338,6 +338,16 @@ public static void CallEnabledAndChange(PlayerControl victim) goto default; } break; + case PlayerState.DeathReason.Starved: + if (!CustomRoles.Baker.IsEnable()) + { + Main.PlayerStates[victim.PlayerId].deathReason = PlayerState.DeathReason.Kill; + } + else + { + goto default; + } + break; default: while (Main.PlayerStates[victim.PlayerId].deathReason != randomReason) diff --git a/Roles/Crewmate/Captain.cs b/Roles/Crewmate/Captain.cs index 5bd22a31b..503be5ab4 100644 --- a/Roles/Crewmate/Captain.cs +++ b/Roles/Crewmate/Captain.cs @@ -26,6 +26,7 @@ public static class Captain public static OptionItem CaptainCanTargetNC; public static OptionItem CaptainCanTargetNE; public static OptionItem CaptainCanTargetNK; + public static OptionItem CaptainCanTargetNA; public static void SetupCustomOption() @@ -43,6 +44,7 @@ public static void SetupCustomOption() CaptainCanTargetNC = BooleanOptionItem.Create(Id + 18, "CaptainCanTargetNC", true, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Captain]); CaptainCanTargetNE = BooleanOptionItem.Create(Id + 19, "CaptainCanTargetNE", true, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Captain]); CaptainCanTargetNK = BooleanOptionItem.Create(Id + 20, "CaptainCanTargetNK", true, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Captain]); + CaptainCanTargetNK = BooleanOptionItem.Create(Id + 21, "CaptainCanTargetNA", true, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Captain]); OverrideTasksData.Create(Id + 21, TabGroup.CrewmateRoles, CustomRoles.Captain); } @@ -137,7 +139,7 @@ public static void OnTaskComplete(PlayerControl pc) (CaptainCanTargetNB.GetBool() && x.GetCustomRole().IsNB()) || (CaptainCanTargetNE.GetBool() && x.GetCustomRole().IsNE()) || (CaptainCanTargetNC.GetBool() && x.GetCustomRole().IsNC()) || - (CaptainCanTargetNK.GetBool() && x.GetCustomRole().IsNeutralKillerTeam()))).ToList(); + (CaptainCanTargetNK.GetBool() && x.GetCustomRole().IsNeutralKillerTeam()) || (CaptainCanTargetNA.GetBool() && x.GetCustomRole().IsNA()))).ToList(); Logger.Info($"Total Number of Potential Target {allTargets.Count}", "Total Captain Target"); if (allTargets.Count == 0) return; diff --git a/Roles/Crewmate/Jailer.cs b/Roles/Crewmate/Jailer.cs index 9bcfad639..01cb1b6da 100644 --- a/Roles/Crewmate/Jailer.cs +++ b/Roles/Crewmate/Jailer.cs @@ -22,6 +22,7 @@ public static class Jailer public static OptionItem NCCanBeExe; public static OptionItem NECanBeExe; public static OptionItem NKCanBeExe; + public static OptionItem NACanBeExe; public static OptionItem CKCanBeExe; public static OptionItem notifyJailedOnMeeting; @@ -37,6 +38,7 @@ public static void SetupCustomOption() NCCanBeExe = BooleanOptionItem.Create(Id + 13, "JailerNCCanBeExe", true, TabGroup.CrewmateRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Jailer]); NECanBeExe = BooleanOptionItem.Create(Id + 14, "JailerNECanBeExe", true, TabGroup.CrewmateRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Jailer]); NKCanBeExe = BooleanOptionItem.Create(Id + 15, "JailerNKCanBeExe", true, TabGroup.CrewmateRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Jailer]); + NACanBeExe = BooleanOptionItem.Create(Id + 19, "JailerNACanBeExe", true, TabGroup.CrewmateRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Jailer]); CKCanBeExe = BooleanOptionItem.Create(Id + 16, "JailerCKCanBeExe", false, TabGroup.CrewmateRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Jailer]); notifyJailedOnMeeting = BooleanOptionItem.Create(Id + 18, "notifyJailedOnMeeting", true, TabGroup.CrewmateRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Jailer]); } diff --git a/Roles/Crewmate/Psychic.cs b/Roles/Crewmate/Psychic.cs index 550264fb4..3db32d0b9 100644 --- a/Roles/Crewmate/Psychic.cs +++ b/Roles/Crewmate/Psychic.cs @@ -19,6 +19,7 @@ public static class Psychic private static OptionItem NBshowEvil; private static OptionItem NEshowEvil; private static OptionItem NCshowEvil; + private static OptionItem NAshowEvil; private static List RedPlayer = []; @@ -32,6 +33,7 @@ public static void SetupCustomOption() NBshowEvil = BooleanOptionItem.Create(Id + 4, "NBareRed", false, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Psychic]); NEshowEvil = BooleanOptionItem.Create(Id + 5, "NEareRed", true, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Psychic]); NCshowEvil = BooleanOptionItem.Create(Id + 7, "NCareRed", false, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Psychic]); + NAshowEvil = BooleanOptionItem.Create(Id + 8, "NAareRed", false, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Psychic]); } public static void Init() { diff --git a/Roles/Neutral/Baker.cs b/Roles/Neutral/Baker.cs index c5f991469..ed7e9c0be 100644 --- a/Roles/Neutral/Baker.cs +++ b/Roles/Neutral/Baker.cs @@ -1,5 +1,6 @@ using Hazel; using System.Collections.Generic; +using System.Linq; using TOHE.Roles.Impostor; using static TOHE.Options; using static TOHE.Translator; @@ -27,18 +28,19 @@ public static void Init() playerIdList = []; BreadList = []; IsEnable = false; + CanUseAbility = false; } public static void Add(byte playerId) { playerIdList.Add(playerId); BreadList[playerId] = []; IsEnable = true; + CanUseAbility = true; } public static void SendRPC(PlayerControl player, PlayerControl target) { - MessageWriter writer; - writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.setPlaguedPlayer, SendOption.Reliable, -1);//RPCによる同期 + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetBreadedPlayer, SendOption.Reliable, -1); writer.Write(player.PlayerId); writer.Write(target.PlayerId); AmongUsClient.Instance.FinishRpcImmediately(writer); @@ -49,7 +51,7 @@ public static void ReceiveRPC(MessageReader reader) byte BreadHolderId = reader.ReadByte(); BreadList[BakerId].Add(BreadHolderId); } - public static string GetProgressText(byte playerId) => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Baker).ShadeColor(0.25f), BreadList.TryGetValue(playerId, out var x) ? $"({x}/{BreadNeededToTransform.GetInt()})" : "Invalid"); + public static string GetProgressText(byte playerId) => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Baker).ShadeColor(0.25f), $"({BreadedPlayerCount(playerId).Item1}/{BreadedPlayerCount(playerId).Item2})"); public static bool HasBread(byte pc, byte target) { return BreadList[pc].Contains(target); @@ -76,8 +78,6 @@ public static bool OnCheckMurder(PlayerControl killer, PlayerControl target) Utils.NotifyRoles(SpecifySeer: killer); killer.Notify(GetString("BakerBreaded")); - Main.AllPlayerKillCooldown[killer.PlayerId] = 100000; - Logger.Info($"Bread given to "+target.GetRealName(), "Baker"); CanUseAbility = false; return false; @@ -86,9 +86,19 @@ public static void OnReportDeadBody() { CanUseAbility = true; } + public static void OnPlayerDead(PlayerControl deadPlayer) + { + foreach (var playerId in BreadList.Keys.ToArray()) + { + if (deadPlayer.PlayerId == playerId) + { + BreadList[playerId].Remove(playerId); + } + } + } public static (int, int) BreadedPlayerCount(byte playerId) { - int breaded = 0, all = BreadNeededToTransform.GetValue(); + int breaded = 0, all = BreadNeededToTransform.GetInt(); foreach (var pc in Main.AllAlivePlayerControls) { if (pc.PlayerId == playerId) continue; @@ -112,6 +122,7 @@ public static void OnFixedUpdate(PlayerControl player) player.RpcSetCustomRole(CustomRoles.Famine); player.Notify(GetString("BakerToFamine")); player.RpcGuardAndKill(player); + KillIfNotEjected(player); } public static void KillIfNotEjected(PlayerControl player) @@ -135,7 +146,7 @@ public static void KillIfNotEjected(PlayerControl player) } } } - CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Armageddon, [.. deathList]); + CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Starved, [.. deathList]); } } From 28a1c99aae08b1c2a5138c17e689aa44df84be13 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Sat, 17 Feb 2024 21:00:32 -0500 Subject: [PATCH 09/15] Soul Collector gains passive souls --- Resources/Lang/en_US.json | 2 ++ Roles/Neutral/SoulCollector.cs | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index b45fc2425..692a8f62b 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -2719,6 +2719,8 @@ "SoulCollectorToDeath": "You have become Death!!!", "SoulCollectorTransform": "Now Soul Collector has become Death, Destroyer of Worlds and Horseman of the Apocalypse!

Find them and vote them out before it's too late!", "CallMeetingIfDeath": "Call a meeting immediately after Death transforms", + "GetPassiveSouls": "Gain a passive soul every round", + "PassiveSoulGained": "You have gained a passive soul from the underworld.", "ApocalypseIsNigh": "[ The Apocalypse Is Nigh! ]", "BakerToFamine": "You have become Famine!!!", "BakerTransform": "The Baker has transformed into Famine, Horseman of the Apocalypse! A famine has begun!", diff --git a/Roles/Neutral/SoulCollector.cs b/Roles/Neutral/SoulCollector.cs index f4f3b747a..1e0aa2399 100644 --- a/Roles/Neutral/SoulCollector.cs +++ b/Roles/Neutral/SoulCollector.cs @@ -1,8 +1,10 @@ using Hazel; using System.Collections.Generic; +using System.Diagnostics.Metrics; using System.Linq; using static TOHE.Options; using static TOHE.Translator; +using static UnityEngine.GraphicsBuffer; namespace TOHE.Roles.Neutral; public static class SoulCollector @@ -18,6 +20,7 @@ public static class SoulCollector public static OptionItem SoulCollectorPointsOpt; public static OptionItem CollectOwnSoulOpt; public static OptionItem CallMeetingIfDeath; + public static OptionItem GetPassiveSouls; public static void SetupCustomOption() { @@ -26,6 +29,7 @@ public static void SetupCustomOption() .SetValueFormat(OptionFormat.Times); CollectOwnSoulOpt = BooleanOptionItem.Create(Id + 11, "CollectOwnSoulOpt", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]); CallMeetingIfDeath = BooleanOptionItem.Create(Id + 12, "CallMeetingIfDeath", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]); + GetPassiveSouls = BooleanOptionItem.Create(Id + 13, "GetPassiveSouls", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]); } public static void Init() { @@ -99,6 +103,11 @@ public static void OnReportDeadBody() { SoulCollectorTarget[playerId] = byte.MaxValue; DidVote[playerId] = false; + if (GetPassiveSouls.GetBool()) + { + SoulCollectorPoints[playerId]++; + Utils.SendMessage(GetString("PassiveSoulGained"), playerId, title: Utils.ColorString(Utils.GetRoleColor(CustomRoles.SoulCollector), GetString("SoulCollectorTitle"))); + } } } @@ -119,7 +128,9 @@ public static void OnPlayerDead(PlayerControl deadPlayer) { SoulCollectorPoints[playerId] = SoulCollectorPointsOpt.GetInt(); } + } + } public static void BecomeDeath(PlayerControl player) { From 27665882b0f1f77a8d0fee91d160cce6bbdcaf37 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Sat, 17 Feb 2024 23:08:04 -0500 Subject: [PATCH 10/15] Berserker + NA's own assign category Neutral APocalypse are now their own category for role assigning --- Modules/CustomRoleSelector.cs | 48 +++++++++++++++++++++++---- Modules/CustomRolesHelper.cs | 24 ++++++++++---- Modules/OptionHolder.cs | 62 +++++++++++++++++++++-------------- Patches/PlayerControlPatch.cs | 8 ++++- Resources/Lang/en_US.json | 10 ++++-- Resources/roleColor.json | 3 +- Roles/Crewmate/Divinator.cs | 1 + Roles/Crewmate/Snitch.cs | 4 ++- Roles/Neutral/Executioner.cs | 3 ++ main.cs | 3 +- 10 files changed, 121 insertions(+), 45 deletions(-) diff --git a/Modules/CustomRoleSelector.cs b/Modules/CustomRoleSelector.cs index b89733d59..1c778e70e 100644 --- a/Modules/CustomRoleSelector.cs +++ b/Modules/CustomRoleSelector.cs @@ -22,6 +22,7 @@ public static void SelectCustomRoles() int optImpNum = Main.RealOptionsData.GetInt(Int32OptionNames.NumImpostors); int optNonNeutralKillingNum = 0; int optNeutralKillingNum = 0; + int optNeutralApocalypseNum = 0; if (Options.NonNeutralKillingRolesMaxPlayer.GetInt() > 0 && Options.NonNeutralKillingRolesMaxPlayer.GetInt() >= Options.NonNeutralKillingRolesMinPlayer.GetInt()) { @@ -31,10 +32,15 @@ public static void SelectCustomRoles() { optNeutralKillingNum = rd.Next(Options.NeutralKillingRolesMinPlayer.GetInt(), Options.NeutralKillingRolesMaxPlayer.GetInt() + 1); } + if (Options.NeutralApocalypseRolesMaxPlayer.GetInt() > 0 && Options.NeutralApocalypseRolesMaxPlayer.GetInt() >= Options.NeutralApocalypseRolesMinPlayer.GetInt()) + { + optNeutralApocalypseNum = rd.Next(Options.NeutralApocalypseRolesMinPlayer.GetInt(), Options.NeutralApocalypseRolesMaxPlayer.GetInt() + 1); + } int readyRoleNum = 0; int readyNonNeutralKillingNum = 0; int readyNeutralKillingNum = 0; + int readyNeutralApocalypseNum = 0; List rolesToAssign = []; List roleList = []; @@ -51,6 +57,9 @@ public static void SelectCustomRoles() List NeutralKillingOnList = []; List NeutralKillingRateList = []; + List NeutralApocalypseOnList = []; + List NeutralApocalypseRateList = []; + List roleRateList = []; if (Options.CurrentGameMode == CustomGameMode.FFA) @@ -111,16 +120,18 @@ public static void SelectCustomRoles() else if (role.IsMini()) MiniOnList.Add(role); else if (role.IsNonNK()) NonNeutralKillingOnList.Add(role); else if (role.IsNK()) NeutralKillingOnList.Add(role); + else if (role.IsNA()) NeutralApocalypseOnList.Add(role); else roleOnList.Add(role); } // 职业设置为:启用 foreach (var role in roleList.ToArray()) if (role.GetMode() == 1) { - if (role.IsImpostor()) ImpRateList.Add(role); - else if (role.IsMini()) MiniRateList.Add(role); - else if (role.IsNonNK()) NonNeutralKillingRateList.Add(role); - else if (role.IsNK()) NeutralKillingRateList.Add(role); - else roleRateList.Add(role); + if (role.IsImpostor()) ImpRateList.Add(role); + else if (role.IsMini()) MiniRateList.Add(role); + else if (role.IsNonNK()) NonNeutralKillingRateList.Add(role); + else if (role.IsNK()) NeutralKillingRateList.Add(role); + else if (role.IsNA()) NeutralApocalypseRateList.Add(role); + else roleRateList.Add(role); } while (MiniOnList.Count == 1) @@ -235,7 +246,32 @@ public static void SelectCustomRoles() if (readyNeutralKillingNum >= optNeutralKillingNum) break; } } - + // Select NeutralApocalypse "Always" + while (NeutralApocalypseOnList.Count > 0 && optNeutralApocalypseNum > 0) + { + var select = NeutralApocalypseOnList[rd.Next(0, NeutralApocalypseOnList.Count)]; + NeutralApocalypseOnList.Remove(select); + rolesToAssign.Add(select); + readyRoleNum++; + readyNeutralApocalypseNum += select.GetCount(); + Logger.Info(select.ToString() + " Add to NeutralApocalypse waiting list (priority)", "CustomRoleSelector"); + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyNeutralApocalypseNum >= optNeutralApocalypseNum) break; + } + if (readyRoleNum < playerCount && readyNeutralApocalypseNum < optNeutralApocalypseNum) + { + while (NeutralApocalypseRateList.Count > 0 && optNeutralApocalypseNum > 0) + { + var select = NeutralApocalypseRateList[rd.Next(0, NeutralApocalypseRateList.Count)]; + NeutralApocalypseRateList.Remove(select); + rolesToAssign.Add(select); + readyRoleNum++; + readyNeutralApocalypseNum += select.GetCount(); + Logger.Info(select.ToString() + " Add to NeutralApocalypse waiting list", "CustomRoleSelector"); + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyNeutralApocalypseNum >= optNeutralApocalypseNum) break; + } + } // Select Crewmates "Always" while (roleOnList.Count > 0) { diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 32bb0f3bb..4b8636089 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -267,6 +267,8 @@ public static RoleTypes GetDYRole(this CustomRoles role) // Role has a kill butt CustomRoles.ChiefOfPolice => RoleTypes.Impostor, CustomRoles.Quizmaster => RoleTypes.Impostor, CustomRoles.Baker => RoleTypes.Impostor, + CustomRoles.Berserker => RoleTypes.Impostor, + CustomRoles.War => RoleTypes.Impostor, _ => RoleTypes.GuardianAngel }; } @@ -411,6 +413,8 @@ CustomRoles.Imitator or CustomRoles.Bandit or CustomRoles.Pestilence or CustomRoles.PlagueBearer or + CustomRoles.Baker or + CustomRoles.Famine or CustomRoles.Agitater or CustomRoles.Innocent or CustomRoles.Vulture or @@ -434,6 +438,7 @@ CustomRoles.DarkHide or // CustomRoles.PotionMaster or CustomRoles.Doomsayer or CustomRoles.SoulCollector or + CustomRoles.Death or CustomRoles.Pirate or CustomRoles.Seeker or CustomRoles.Pixie or @@ -469,6 +474,8 @@ CustomRoles.Pickpocket or CustomRoles.Traitor or CustomRoles.Virus or CustomRoles.Spiritcaller or + CustomRoles.Berserker or + CustomRoles.War or CustomRoles.Succubus; } @@ -527,9 +534,6 @@ CustomRoles.Maverick or CustomRoles.Opportunist or CustomRoles.Pursuer or CustomRoles.Shaman or - CustomRoles.SoulCollector or - CustomRoles.PlagueBearer or - CustomRoles.Baker or CustomRoles.NWitch or CustomRoles.CursedSoul or CustomRoles.Doomsayer or @@ -612,7 +616,10 @@ CustomRoles.Pestilence or CustomRoles.SoulCollector or CustomRoles.Death or CustomRoles.Baker or - CustomRoles.Famine; + CustomRoles.Famine or + CustomRoles.Berserker or + CustomRoles.War; + } public static bool IsSnitchTarget(this CustomRoles role) { @@ -698,7 +705,6 @@ CustomRoles.Warlock or CustomRoles.Undertaker or CustomRoles.RiftMaker or CustomRoles.Assassin or - CustomRoles.Berserker or CustomRoles.Hacker or CustomRoles.Visionary or CustomRoles.Miner or @@ -840,6 +846,8 @@ CustomRoles.Succubus or CustomRoles.Doomsayer or CustomRoles.Spiritcaller or CustomRoles.SchrodingersCat or + CustomRoles.Berserker or + CustomRoles.War or CustomRoles.Quizmaster; } /* public static bool IsCoven(this CustomRoles role) @@ -893,7 +901,6 @@ CustomRoles.DarkHide or CustomRoles.Necromancer or CustomRoles.Pirate or CustomRoles.Provocateur or - CustomRoles.SoulCollector or CustomRoles.Wraith or CustomRoles.Juggernaut or CustomRoles.Pelican or @@ -982,6 +989,8 @@ CustomRoles.VengefulRomantic or CustomRoles.Pixie or CustomRoles.Seeker or CustomRoles.SchrodingersCat or + CustomRoles.Berserker or + CustomRoles.War or CustomRoles.Quizmaster; } public static bool IsMadmate(this CustomRoles role) @@ -1524,7 +1533,6 @@ public static bool CheckAddonConfilct(CustomRoles role, PlayerControl pc, bool c case CustomRoles.Mare: if (pc.Is(CustomRoles.Underdog) - || pc.Is(CustomRoles.Berserker) || pc.Is(CustomRoles.Inhibitor) || pc.Is(CustomRoles.Saboteur) || pc.Is(CustomRoles.Swift) @@ -1901,6 +1909,8 @@ public static CountTypes GetCountTypes(this CustomRoles role) CustomRoles.Death => CountTypes.Apocalypse, CustomRoles.Baker => CountTypes.Apocalypse, CustomRoles.Famine => CountTypes.Apocalypse, + CustomRoles.Berserker => CountTypes.Apocalypse, + CustomRoles.War => CountTypes.Apocalypse, CustomRoles.Agitater => CountTypes.Agitater, CustomRoles.Parasite => CountTypes.Impostor, CustomRoles.NSerialKiller => CountTypes.NSerialKiller, diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index f2c740b09..c64a2b48a 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -469,6 +469,8 @@ public static CustomGameMode CurrentGameMode public static OptionItem NonNeutralKillingRolesMaxPlayer; public static OptionItem NeutralKillingRolesMinPlayer; public static OptionItem NeutralKillingRolesMaxPlayer; + public static OptionItem NeutralApocalypseRolesMinPlayer; + public static OptionItem NeutralApocalypseRolesMaxPlayer; public static OptionItem NeutralRoleWinTogether; public static OptionItem NeutralWinTogether; @@ -1165,6 +1167,14 @@ public static void Load() .SetGameMode(CustomGameMode.Standard) .SetValueFormat(OptionFormat.Players); + NeutralApocalypseRolesMinPlayer = IntegerOptionItem.Create(60022, "NeutralApocalypseRolesMinPlayer", new(0, 4, 1), 0, TabGroup.NeutralRoles, false) + .SetGameMode(CustomGameMode.Standard) + .SetHeader(true) + .SetValueFormat(OptionFormat.Players); + NeutralApocalypseRolesMaxPlayer = IntegerOptionItem.Create(60023, "NeutralApocalypseRolesMaxPlayer", new(0, 4, 1), 0, TabGroup.NeutralRoles, false) + .SetGameMode(CustomGameMode.Standard) + .SetValueFormat(OptionFormat.Players); + NeutralRoleWinTogether = BooleanOptionItem.Create(60017, "NeutralRoleWinTogether", false, TabGroup.NeutralRoles, false) .SetGameMode(CustomGameMode.Standard) @@ -1210,31 +1220,7 @@ public static void Load() */ Sans.SetupCustomOption(); - /* - * Berserker - */ - SetupRoleOptions(600, TabGroup.ImpostorRoles, CustomRoles.Berserker); - BerserkerKillCooldown = FloatOptionItem.Create(602, "BerserkerKillCooldown", new(25f, 250f, 2.5f), 35f, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]) - .SetValueFormat(OptionFormat.Seconds); - BerserkerMax = IntegerOptionItem.Create(603, "BerserkerMax", new(1, 10, 1), 4, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]) - .SetValueFormat(OptionFormat.Level); - BerserkerOneCanKillCooldown = BooleanOptionItem.Create(604, "BerserkerOneCanKillCooldown", true, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); - BerserkerOneKillCooldown = FloatOptionItem.Create(605, "BerserkerOneKillCooldown", new(10f, 45f, 2.5f), 15f, TabGroup.ImpostorRoles, false).SetParent(BerserkerOneCanKillCooldown) - .SetValueFormat(OptionFormat.Seconds); - BerserkerKillCooldownLevel = IntegerOptionItem.Create(606, "BerserkerLevelRequirement", new(1, 10, 1), 1, TabGroup.ImpostorRoles, false).SetParent(BerserkerOneCanKillCooldown) - .SetValueFormat(OptionFormat.Level); - BerserkerTwoCanScavenger = BooleanOptionItem.Create(607, "BerserkerTwoCanScavenger", true, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); - BerserkerScavengerLevel = IntegerOptionItem.Create(608, "BerserkerLevelRequirement", new(1, 10, 1), 2, TabGroup.ImpostorRoles, false).SetParent(BerserkerTwoCanScavenger) - .SetValueFormat(OptionFormat.Level); - BerserkerThreeCanBomber = BooleanOptionItem.Create(609, "BerserkerThreeCanBomber", true, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); - BerserkerBomberLevel = IntegerOptionItem.Create(610, "BerserkerLevelRequirement", new(1, 10, 1), 3, TabGroup.ImpostorRoles, false).SetParent(BerserkerThreeCanBomber) - .SetValueFormat(OptionFormat.Level); - //BerserkerFourCanFlash = BooleanOptionItem.Create(611, "BerserkerFourCanFlash", true, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); - //BerserkerSpeed = FloatOptionItem.Create(611, "BerserkerSpeed", new(1.5f, 5f, 0.25f), 2.5f, TabGroup.ImpostorRoles, false).SetParent(BerserkerOneCanKillCooldown) - // .SetValueFormat(OptionFormat.Multiplier); - BerserkerFourCanNotKill = BooleanOptionItem.Create(612, "BerserkerFourCanNotKill", true, TabGroup.ImpostorRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); - BerserkerImmortalLevel = IntegerOptionItem.Create(613, "BerserkerLevelRequirement", new(1, 10, 1), 4, TabGroup.ImpostorRoles, false).SetParent(BerserkerFourCanNotKill) - .SetValueFormat(OptionFormat.Level); + /* * Bomber @@ -2449,6 +2435,32 @@ public static void Load() .SetColor(new Color32(127, 140, 141, byte.MaxValue)); Baker.SetupCustomOption(); + /* + * Berserker + */ + SetupRoleOptions(600, TabGroup.NeutralRoles, CustomRoles.Berserker); + BerserkerKillCooldown = FloatOptionItem.Create(602, "BerserkerKillCooldown", new(25f, 250f, 2.5f), 35f, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]) + .SetValueFormat(OptionFormat.Seconds); + BerserkerMax = IntegerOptionItem.Create(603, "BerserkerMax", new(1, 10, 1), 4, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]) + .SetValueFormat(OptionFormat.Level); + BerserkerOneCanKillCooldown = BooleanOptionItem.Create(604, "BerserkerOneCanKillCooldown", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerOneKillCooldown = FloatOptionItem.Create(605, "BerserkerOneKillCooldown", new(10f, 45f, 2.5f), 15f, TabGroup.NeutralRoles, false).SetParent(BerserkerOneCanKillCooldown) + .SetValueFormat(OptionFormat.Seconds); + BerserkerKillCooldownLevel = IntegerOptionItem.Create(606, "BerserkerLevelRequirement", new(1, 10, 1), 1, TabGroup.NeutralRoles, false).SetParent(BerserkerOneCanKillCooldown) + .SetValueFormat(OptionFormat.Level); + BerserkerTwoCanScavenger = BooleanOptionItem.Create(607, "BerserkerTwoCanScavenger", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerScavengerLevel = IntegerOptionItem.Create(608, "BerserkerLevelRequirement", new(1, 10, 1), 2, TabGroup.NeutralRoles, false).SetParent(BerserkerTwoCanScavenger) + .SetValueFormat(OptionFormat.Level); + BerserkerThreeCanBomber = BooleanOptionItem.Create(609, "BerserkerThreeCanBomber", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerBomberLevel = IntegerOptionItem.Create(610, "BerserkerLevelRequirement", new(1, 10, 1), 3, TabGroup.NeutralRoles, false).SetParent(BerserkerThreeCanBomber) + .SetValueFormat(OptionFormat.Level); + //BerserkerFourCanFlash = BooleanOptionItem.Create(611, "BerserkerFourCanFlash", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); + //BerserkerSpeed = FloatOptionItem.Create(611, "BerserkerSpeed", new(1.5f, 5f, 0.25f), 2.5f, TabGroup.NeutralRoles, false).SetParent(BerserkerOneCanKillCooldown) + // .SetValueFormat(OptionFormat.Multiplier); + BerserkerFourCanNotKill = BooleanOptionItem.Create(612, "BerserkerFourCanNotKill", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerImmortalLevel = IntegerOptionItem.Create(613, "BerserkerLevelRequirement", new(1, 10, 1), 4, TabGroup.NeutralRoles, false).SetParent(BerserkerFourCanNotKill) + .SetValueFormat(OptionFormat.Level); + PlagueBearer.SetupCustomOption(); diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 79f6ba1e2..ac81984f2 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -738,7 +738,7 @@ public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerC { Main.AllPlayerKillCooldown[killer.PlayerId] = Options.BerserkerOneKillCooldown.GetFloat(); } - + if (target.IsNeutralApocalypse()) return true; if (Main.BerserkerKillMax[killer.PlayerId] == Options.BerserkerScavengerLevel.GetInt() && Options.BerserkerTwoCanScavenger.GetBool()) { killer.RpcTeleport(target.GetCustomPosition()); @@ -776,6 +776,12 @@ public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] PlayerC //{ // Main.AllPlayerSpeed[killer.PlayerId] = Options.BerserkerSpeed.GetFloat(); //} + if (Main.BerserkerKillMax[killer.PlayerId] >= Options.BerserkerImmortalLevel.GetInt() && Options.BerserkerFourCanNotKill.GetBool()) + { + killer.RpcSetCustomRole(CustomRoles.War); + killer.Notify(GetString("BerserkerToWar")); + killer.RpcGuardAndKill(killer); + } break; } diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 692a8f62b..551ac1e21 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -880,7 +880,8 @@ "GodfatherInfoLong": "(Impostors):\nAs the Godfather, you vote someone to make them your target.\nIn the next round if someone kills the target, the killer will turn into a Refugee.", "ChronomancerInfoLong": "(Impostors):\nAs the Chronomancer, you can charge up your kill button. Once activated the Chronomancer can use their kill button infinitely until they run out of charge.", "PitfallInfoLong": "(Impostors):\nAs the Pitfall, you use your shapeshift to mark the area around the shapeshift as a trap. Players who enter this area will be immobilized for a short period of time and their vision will be affected.", - "BerserkerInfoLong": "(Impostors):\nAs the Berserker, you level up with each kill.\nUpon reaching a certain level defined by the host, you unlock a new power.\n\nScavenged kills make your kills disappear.\nBombed kills make your kills explode.", + "BerserkerInfoLong": "(Apocalypse):\nAs the Berserker, you level up with each kill.\nUpon reaching a certain level defined by the host, you unlock a new power.\n\nScavenged kills make your kills disappear.\nBombed kills make your kills explode. After a certain level you become War.", + "WarInfoLong": "(Apocalypse):\nAs War, you are invincible and can kill anyone with your previous powers.", "EvilMiniInfoLong": "(Impostors):\nAs an Evil Mini, you are unkillable until you grow up and have a very long initial kill cooldown, which is drastically shortened as you grow up.", "BlackmailerInfoLong": "(Impostors):\nAs the Blackmailer, when you shift into a target you will blackmail that player, and the blackmailed player cannot speak.\n\nSpeaking by the blackmailed player will trigger the confusion command, please do not speak when the blackmailed player sees his icon", "InstigatorInfoLong": "(Impostors):\nAs the Instigator, it's your job to turn the crewmates against each other. Each time a Crewmate is voted out in a meeting, as long as you are alive, an additional Crewmate who voted for the innocent player will die after the meeting. The number of additional players dying is determined by the host.", @@ -1493,6 +1494,8 @@ "NonNeutralKillingRolesMaxPlayer": "Maximum amount of Non-Killing Neutrals", "NeutralKillingRolesMinPlayer": "Minimum amount of Neutral Killers", "NeutralKillingRolesMaxPlayer": "Maximum amount of Neutral Killers", + "NeutralApocalypseRolesMinPlayer": "Minimum amount of Neutral Apocalypse", + "NeutralApocalypseRolesMaxPlayer": "Maximum amount of Neutral Apocalypse", "ImpKnowAlliesRole": "Impostors know the roles of other Impostors", "ImpKnowWhosMadmate": "Impostors know Madmates", "MadmateKnowWhosImp": "Madmates know Impostors", @@ -1568,6 +1571,7 @@ "SnitchEnableTargetArrow": "See Arrow Towards Target", "SnitchCanGetArrowColor": "See Colored Arrows based on Team Colors", "SnitchCanFindNeutralKiller": "Can Find Neutral Killers", + "SnitchCanFindNeutralApoc": "Can Find Neutral Apocalypse", "SnitchCanFindMadmate": "Can Find Madmates", "SnitchRemainingTaskFound": "Remaining tasks to be known", "SpeedBoosterUpSpeed": "Increase Speed by", @@ -1584,6 +1588,7 @@ "CanTargetNeutralBenign": "Can Target Neutral Benign", "CanTargetNeutralEvil": "Can Target Neutral Evil", "CanTargetNeutralChaos": "Can Target Neutral Chaos", + "CanTargetNeutralApocalypse": "Can Target Neutral Apocalypse", "SidekickSheriffCanGoBerserk": "Recruited Sheriff Can Go Nuts", "LawyerCanTargetImpostor": "Can Target Impostors", "LawyerCanTargetNeutralKiller": "Can Target Neutral Killers", @@ -2787,11 +2792,12 @@ "BerserkerOneKillCooldown": "Kill cooldown after unlocking", "BerserkerTwoCanScavenger": "Unlock scavenged kills", "BerserkerThreeCanBomber": "Unlock bombed kills", - "BerserkerFourCanNotKill": "Unlock immortality", + "BerserkerFourCanNotKill": "Become War", "BerserkerMaxReached": "Maximum level reached!", "BerserkerLevelChanged": "Increased level to {0}", "BerserkerLevelRequirement": "Level requirement for unlock", "KilledByBerserker": "Killed by Berserker", + "BerserkerToWar": "You have become War!!!", "ImpCanBeUnlucky": "Impostors can become Unlucky", "CrewCanBeUnlucky": "Crewmates can become Unlucky", diff --git a/Resources/roleColor.json b/Resources/roleColor.json index 9dacfd7dc..207145c27 100644 --- a/Resources/roleColor.json +++ b/Resources/roleColor.json @@ -150,7 +150,6 @@ "Death": "#ff174f", "Baker": "#bf9f7a", "Famine": "#83461c", - "Apocalypse": "#ff174f", "Imitator": "#B3D94C", "Doppelganger": "#f6f4a3", "GM": "#ff5b70", @@ -234,5 +233,5 @@ "SchrodingersCat": "#404040", "PlagueDoctor": "#ff6633", "Rainbow": "#55FFCB", - "Apocalypse": "#CCCCCC" + "Apocalypse": "#ff174f" } diff --git a/Roles/Crewmate/Divinator.cs b/Roles/Crewmate/Divinator.cs index e5104352c..022b1c239 100644 --- a/Roles/Crewmate/Divinator.cs +++ b/Roles/Crewmate/Divinator.cs @@ -213,6 +213,7 @@ public static void OnVote(PlayerControl player, PlayerControl target) CustomRoles.Sans, CustomRoles.Minimalism, CustomRoles.Berserker, + CustomRoles.War, CustomRoles.OverKiller], [CustomRoles.Bloodhound, diff --git a/Roles/Crewmate/Snitch.cs b/Roles/Crewmate/Snitch.cs index 4db0090ae..a9d333132 100644 --- a/Roles/Crewmate/Snitch.cs +++ b/Roles/Crewmate/Snitch.cs @@ -18,6 +18,7 @@ public static class Snitch private static OptionItem OptionCanFindNeutralKiller; private static OptionItem OptionCanFindMadmate; private static OptionItem OptionRemainingTasks; + private static OptionItem OptionCanFindNeutralApoc; private static bool EnableTargetArrow; private static bool CanGetColoredArrow; @@ -39,6 +40,7 @@ public static void SetupCustomOption() OptionCanFindNeutralKiller = BooleanOptionItem.Create(Id + 12, "SnitchCanFindNeutralKiller", true, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Snitch]); OptionCanFindMadmate = BooleanOptionItem.Create(Id + 14, "SnitchCanFindMadmate", false, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Snitch]); OptionRemainingTasks = IntegerOptionItem.Create(Id + 13, "SnitchRemainingTaskFound", new(0, 10, 1), 1, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Snitch]); + OptionCanFindNeutralApoc = BooleanOptionItem.Create(Id + 15, "SnitchCanFindNeutralApoc", false, TabGroup.CrewmateRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Snitch]); OverrideTasksData.Create(Id + 20, TabGroup.CrewmateRoles, CustomRoles.Snitch); } public static void Init() @@ -83,7 +85,7 @@ private static bool GetExpose(PlayerControl pc) var snitchId = pc.PlayerId; return IsExposed[snitchId]; } - private static bool IsSnitchTarget(PlayerControl target) => IsEnable && (target.Is(CustomRoleTypes.Impostor) && !target.Is(CustomRoles.Trickster) || (target.IsSnitchTarget() && CanFindNeutralKiller) || (target.Is(CustomRoles.Madmate) && CanFindMadmate) || (target.Is(CustomRoles.Rascal) && CanFindMadmate)); + private static bool IsSnitchTarget(PlayerControl target) => IsEnable && (target.Is(CustomRoleTypes.Impostor) && !target.Is(CustomRoles.Trickster) || (target.IsSnitchTarget() && CanFindNeutralKiller) || (target.Is(CustomRoles.Madmate) && CanFindMadmate) || (target.Is(CustomRoles.Rascal) && CanFindMadmate) || (target.IsNeutralApocalypse() && OptionCanFindNeutralApoc.GetBool())); public static void CheckTask(PlayerControl snitch) { if (!snitch.IsAlive() || snitch.Is(CustomRoles.Madmate)) return; diff --git a/Roles/Neutral/Executioner.cs b/Roles/Neutral/Executioner.cs index 2ebc9efac..2abc5145a 100644 --- a/Roles/Neutral/Executioner.cs +++ b/Roles/Neutral/Executioner.cs @@ -18,6 +18,7 @@ public static class Executioner private static OptionItem CanTargetNeutralBenign; private static OptionItem CanTargetNeutralEvil; private static OptionItem CanTargetNeutralChaos; + private static OptionItem CanTargetNeutralApocalypse; public static OptionItem KnowTargetRole; public static OptionItem ChangeRolesAfterTargetKilled; @@ -47,6 +48,7 @@ public static void SetupCustomOption() CanTargetNeutralBenign = BooleanOptionItem.Create(Id + 14, "CanTargetNeutralBenign", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); CanTargetNeutralEvil = BooleanOptionItem.Create(Id + 15, "CanTargetNeutralEvil", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); CanTargetNeutralChaos = BooleanOptionItem.Create(Id + 16, "CanTargetNeutralChaos", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); + CanTargetNeutralApocalypse = BooleanOptionItem.Create(Id + 17, "CanTargetNeutralApocalypse", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); KnowTargetRole = BooleanOptionItem.Create(Id + 13, "KnowTargetRole", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); ChangeRolesAfterTargetKilled = StringOptionItem.Create(Id + 11, "ExecutionerChangeRolesAfterTargetKilled", ChangeRoles, 1, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Executioner]); } @@ -74,6 +76,7 @@ public static void Add(byte playerId) else if (!CanTargetNeutralBenign.GetBool() && target.GetCustomRole().IsNB()) continue; else if (!CanTargetNeutralEvil.GetBool() && target.GetCustomRole().IsNE()) continue; else if (!CanTargetNeutralChaos.GetBool() && target.GetCustomRole().IsNC()) continue; + else if (!CanTargetNeutralApocalypse.GetBool() && target.GetCustomRole().IsNA()) continue; if (target.GetCustomRole() is CustomRoles.GM or CustomRoles.SuperStar or CustomRoles.NiceMini or CustomRoles.EvilMini) continue; if (Utils.GetPlayerById(playerId).Is(CustomRoles.Lovers) && target.Is(CustomRoles.Lovers)) continue; diff --git a/main.cs b/main.cs index 8d62d5193..4f2d96f1f 100644 --- a/main.cs +++ b/main.cs @@ -553,7 +553,6 @@ public enum CustomRoles Hacker, //anonymous AntiAdminer, Bard, - Berserker, Blackmailer, Bomber, BountyHunter, @@ -714,6 +713,7 @@ public enum CustomRoles Arsonist, Baker, Bandit, + Berserker, BloodKnight, Collector, Succubus, //cultist @@ -779,6 +779,7 @@ public enum CustomRoles VengefulRomantic, Virus, Vulture, + War, Werewolf, NWitch, //witch Workaholic, From 750192fda3813040efef73b6fb84e793a2b02889 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Sun, 18 Feb 2024 22:58:07 -0500 Subject: [PATCH 11/15] Bers/War + finalize other stuff Berserker has been moved to NA, War basically just replaced level 4 of Bers with the option to make War have an even lower kd Baker now knows who they gave bread to Lawyer and Executioner now have the option to target NA's All NA's now have transform announce messages --- Modules/AntiBlackout.cs | 2 +- Modules/CustomRolesHelper.cs | 5 ++--- Modules/ExtendedPlayerControl.cs | 8 ++++++++ .../PlayerGameOptionsSender.cs | 3 +++ Modules/NameColorManager.cs | 2 +- Modules/OptionHolder.cs | 5 +++++ Modules/Utils.cs | 10 +++++++++- Patches/IntroPatch.cs | 2 +- Patches/MeetingHudPatch.cs | 8 ++++++++ Patches/PlayerControlPatch.cs | 8 +++++++- Resources/Lang/en_US.json | 19 ++++++++++++------- Resources/roleColor.json | 4 +++- Roles/Neutral/Baker.cs | 2 ++ Roles/Neutral/Lawyer.cs | 5 ++++- Roles/Neutral/SoulCollector.cs | 8 +++++--- 15 files changed, 71 insertions(+), 20 deletions(-) diff --git a/Modules/AntiBlackout.cs b/Modules/AntiBlackout.cs index f8ba878cf..f900d4789 100644 --- a/Modules/AntiBlackout.cs +++ b/Modules/AntiBlackout.cs @@ -44,7 +44,7 @@ public static class AntiBlackout || PotionMaster.IsEnable || Wraith.IsEnable || Necromancer.IsEnable || Doppelganger.IsEnable || PlagueDoctor.IsEnable || CustomRoles.Sidekick.RoleExist(true) - || (CustomRoles.Arsonist.RoleExist(true) && Options.ArsonistCanIgniteAnytime.GetBool()) || CustomRoles.Death.RoleExist(true); + || (CustomRoles.Arsonist.RoleExist(true) && Options.ArsonistCanIgniteAnytime.GetBool()) || SoulCollector.IsEnable || CustomRoles.Death.RoleExist(true) || Baker.IsEnable || CustomRoles.Famine.RoleExist(true) || CustomRoles.Berserker.RoleExist(true) || CustomRoles.War.RoleExist(true); /// ///Difference between the number of non-impostors and the number of imposters diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 4b8636089..a96d682d6 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -64,7 +64,6 @@ static class CustomRolesHelper CustomRoles.Dictator => CustomRoles.Crewmate, CustomRoles.Inhibitor => CustomRoles.Impostor, CustomRoles.Saboteur => CustomRoles.Impostor, - CustomRoles.Berserker => CustomRoles.Impostor, CustomRoles.Doctor => CustomRoles.Scientist, CustomRoles.ScientistTOHE => CustomRoles.Scientist, CustomRoles.Tracefinder => CustomRoles.Scientist, @@ -447,6 +446,8 @@ CustomRoles.RuthlessRomantic or CustomRoles.VengefulRomantic or CustomRoles.Doppelganger or CustomRoles.SchrodingersCat or + CustomRoles.Berserker or + CustomRoles.War or // CustomRoles.Juggernaut or // CustomRoles.Jinx or // CustomRoles.Poisoner or @@ -474,8 +475,6 @@ CustomRoles.Pickpocket or CustomRoles.Traitor or CustomRoles.Virus or CustomRoles.Spiritcaller or - CustomRoles.Berserker or - CustomRoles.War or CustomRoles.Succubus; } diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 2a5c9b19c..fe1a6a91f 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -551,6 +551,8 @@ public static bool CanUseKillButton(this PlayerControl pc) CustomRoles.PlagueBearer => pc.IsAlive(), CustomRoles.Pestilence => pc.IsAlive(), CustomRoles.Baker => pc.IsAlive(), + CustomRoles.Berserker => pc.IsAlive(), + CustomRoles.War => pc.IsAlive(), CustomRoles.Pirate => pc.IsAlive(), CustomRoles.Pixie => pc.IsAlive(), CustomRoles.Seeker => pc.IsAlive(), @@ -654,6 +656,7 @@ CustomRoles.ChiefOfPolice or // CustomRoles.Chameleon => true, CustomRoles.Parasite => true, CustomRoles.Refugee => true, + CustomRoles.Berserker or CustomRoles.War => true, CustomRoles.Spiritcaller => Spiritcaller.CanVent.GetBool(), CustomRoles.Quizmaster => Quizmaster.CanUseVentButton(pc), @@ -721,6 +724,8 @@ CustomRoles.Pickpocket or CustomRoles.PlagueBearer or CustomRoles.Necromancer or CustomRoles.Pestilence or + CustomRoles.Berserker or + CustomRoles.War or CustomRoles.Werewolf or // CustomRoles.Minion or CustomRoles.Spiritcaller or @@ -808,6 +813,9 @@ public static void ResetKillCooldown(this PlayerControl player) case CustomRoles.Berserker: Main.AllPlayerKillCooldown[player.PlayerId] = Options.BerserkerKillCooldown.GetFloat(); break; + case CustomRoles.War: + Main.AllPlayerKillCooldown[player.PlayerId] = Options.WarKillCooldown.GetFloat(); + break; case CustomRoles.Kamikaze: Kamikaze.SetKillCooldown(player.PlayerId); break; diff --git a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs index 5836d6ced..84b189578 100644 --- a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs +++ b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs @@ -190,6 +190,9 @@ public override IGameOptions BuildGameOptions() case CustomRoles.Pestilence: opt.SetVision(PlagueBearer.PestilenceHasImpostorVision.GetBool()); break; + case CustomRoles.Berserker or CustomRoles.War: + opt.SetVision(Options.BerserkerHasImpostorVision.GetBool()); + break; case CustomRoles.Pelican: Pelican.ApplyGameOptions(opt); break; diff --git a/Modules/NameColorManager.cs b/Modules/NameColorManager.cs index 87913d3bc..69a58c802 100644 --- a/Modules/NameColorManager.cs +++ b/Modules/NameColorManager.cs @@ -231,7 +231,7 @@ private static bool KnowTargetRoleColor(PlayerControl seer, PlayerControl target if (seer.Is(CustomRoles.PlagueDoctor) && target.Is(CustomRoles.PlagueDoctor)) color = Main.roleColors[CustomRoles.PlagueDoctor]; // Apocalypse - if (seer.IsNeutralApocalypse() && target.IsNeutralApocalypse()) color = Main.roleColors[CustomRoles.Death]; + if (seer.IsNeutralApocalypse() && target.IsNeutralApocalypse()) color = Main.roleColors[CustomRoles.Apocalypse]; if (color != "") return true; else return seer == target diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index c64a2b48a..84678c7ac 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -485,6 +485,7 @@ public static CustomGameMode CurrentGameMode public static OptionItem BerserkerKillCooldown; public static OptionItem BerserkerMax; + public static OptionItem BerserkerHasImpostorVision; public static OptionItem BerserkerOneCanKillCooldown; public static OptionItem BerserkerKillCooldownLevel; public static OptionItem BerserkerOneKillCooldown; @@ -496,6 +497,7 @@ public static CustomGameMode CurrentGameMode //public static OptionItem BerserkerSpeed; public static OptionItem BerserkerFourCanNotKill; public static OptionItem BerserkerImmortalLevel; + public static OptionItem WarKillCooldown; public static OptionItem BomberRadius; public static OptionItem BomberCanKill; @@ -2443,6 +2445,7 @@ public static void Load() .SetValueFormat(OptionFormat.Seconds); BerserkerMax = IntegerOptionItem.Create(603, "BerserkerMax", new(1, 10, 1), 4, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]) .SetValueFormat(OptionFormat.Level); + BerserkerHasImpostorVision = BooleanOptionItem.Create(615, "BerserkerHasImpostorVision", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); BerserkerOneCanKillCooldown = BooleanOptionItem.Create(604, "BerserkerOneCanKillCooldown", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); BerserkerOneKillCooldown = FloatOptionItem.Create(605, "BerserkerOneKillCooldown", new(10f, 45f, 2.5f), 15f, TabGroup.NeutralRoles, false).SetParent(BerserkerOneCanKillCooldown) .SetValueFormat(OptionFormat.Seconds); @@ -2460,6 +2463,8 @@ public static void Load() BerserkerFourCanNotKill = BooleanOptionItem.Create(612, "BerserkerFourCanNotKill", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Berserker]); BerserkerImmortalLevel = IntegerOptionItem.Create(613, "BerserkerLevelRequirement", new(1, 10, 1), 4, TabGroup.NeutralRoles, false).SetParent(BerserkerFourCanNotKill) .SetValueFormat(OptionFormat.Level); + WarKillCooldown = FloatOptionItem.Create(614, "WarKillCooldown", new(0f, 150f, 2.5f), 15f, TabGroup.NeutralRoles, false).SetParent(BerserkerFourCanNotKill) + .SetValueFormat(OptionFormat.Seconds); PlagueBearer.SetupCustomOption(); diff --git a/Modules/Utils.cs b/Modules/Utils.cs index ac53606cb..ec178b852 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -549,6 +549,8 @@ public static bool HasTasks(GameData.PlayerInfo p, bool ForRecompute = true) case CustomRoles.Death: case CustomRoles.Baker: case CustomRoles.Famine: + case CustomRoles.Berserker: + case CustomRoles.War: case CustomRoles.SchrodingersCat: case CustomRoles.Parasite: case CustomRoles.Crusader: @@ -2460,7 +2462,13 @@ public static Task DoNotifyRoles(bool isForMeeting = false, PlayerControl Specif TargetMark.Append($"●"); } break; - + case CustomRoles.Baker: + case CustomRoles.Famine: + if (Baker.HasBread(seer.PlayerId, target.PlayerId)) + { + TargetMark.Append($"●"); + } + break; case CustomRoles.Arsonist: if (seer.IsDousedPlayer(target)) TargetMark.Append($"▲"); diff --git a/Patches/IntroPatch.cs b/Patches/IntroPatch.cs index 05abd332f..4107041b2 100644 --- a/Patches/IntroPatch.cs +++ b/Patches/IntroPatch.cs @@ -513,7 +513,7 @@ public static bool Prefix(IntroCutscene __instance, ref Il2CppSystem.Collections __instance.overlayHandle.color = Palette.CrewmateBlue; return false; } - else if (role is CustomRoles.Romantic or CustomRoles.Doppelganger or CustomRoles.Pyromaniac or CustomRoles.Huntsman or CustomRoles.RuthlessRomantic or CustomRoles.VengefulRomantic or CustomRoles.NSerialKiller or CustomRoles.Jackal or CustomRoles.Seeker or CustomRoles.Pixie or CustomRoles.Agitater or CustomRoles.CursedSoul or CustomRoles.Pirate or CustomRoles.Amnesiac or CustomRoles.Arsonist or CustomRoles.Sidekick or CustomRoles.Innocent or CustomRoles.Pelican or CustomRoles.Pursuer or CustomRoles.Revolutionist or CustomRoles.FFF or CustomRoles.Gamer or CustomRoles.Glitch or CustomRoles.Juggernaut or CustomRoles.DarkHide or CustomRoles.Provocateur or CustomRoles.BloodKnight or CustomRoles.NSerialKiller or CustomRoles.Werewolf or CustomRoles.Maverick or CustomRoles.NWitch or CustomRoles.Shroud or CustomRoles.Totocalcio or CustomRoles.Succubus or CustomRoles.Pelican or CustomRoles.Infectious or CustomRoles.Virus or CustomRoles.Pickpocket or CustomRoles.Traitor or CustomRoles.PlagueBearer or CustomRoles.Pestilence or CustomRoles.Baker or CustomRoles.Spiritcaller or CustomRoles.Necromancer or CustomRoles.Medusa or CustomRoles.HexMaster or CustomRoles.Wraith or CustomRoles.Jinx or CustomRoles.Poisoner or CustomRoles.PotionMaster) //or CustomRoles.Occultist + else if (role is CustomRoles.Romantic or CustomRoles.Doppelganger or CustomRoles.Pyromaniac or CustomRoles.Huntsman or CustomRoles.RuthlessRomantic or CustomRoles.VengefulRomantic or CustomRoles.NSerialKiller or CustomRoles.Jackal or CustomRoles.Seeker or CustomRoles.Pixie or CustomRoles.Agitater or CustomRoles.CursedSoul or CustomRoles.Pirate or CustomRoles.Amnesiac or CustomRoles.Arsonist or CustomRoles.Sidekick or CustomRoles.Innocent or CustomRoles.Pelican or CustomRoles.Pursuer or CustomRoles.Revolutionist or CustomRoles.FFF or CustomRoles.Gamer or CustomRoles.Glitch or CustomRoles.Juggernaut or CustomRoles.DarkHide or CustomRoles.Provocateur or CustomRoles.BloodKnight or CustomRoles.NSerialKiller or CustomRoles.Werewolf or CustomRoles.Maverick or CustomRoles.NWitch or CustomRoles.Shroud or CustomRoles.Totocalcio or CustomRoles.Succubus or CustomRoles.Pelican or CustomRoles.Infectious or CustomRoles.Virus or CustomRoles.Pickpocket or CustomRoles.Traitor or CustomRoles.PlagueBearer or CustomRoles.Pestilence or CustomRoles.Baker or CustomRoles.Famine or CustomRoles.Berserker or CustomRoles.War or CustomRoles.Spiritcaller or CustomRoles.Necromancer or CustomRoles.Medusa or CustomRoles.HexMaster or CustomRoles.Wraith or CustomRoles.Jinx or CustomRoles.Poisoner or CustomRoles.PotionMaster) //or CustomRoles.Occultist { yourTeam = new Il2CppSystem.Collections.Generic.List(); yourTeam.Add(PlayerControl.LocalPlayer); diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index 13de94457..a5b78b6d7 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -939,6 +939,14 @@ void AddMsg(string text, byte sendTo = 255, string title = "") { AddMsg(string.Format(GetString("BakerTransform")), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Famine), GetString("ApocalypseIsNigh"))); } + if (CustomRoles.War.RoleExist()) + { + AddMsg(string.Format(GetString("BerserkerTransform")), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.War), GetString("ApocalypseIsNigh"))); + } + if (CustomRoles.Pestilence.RoleExist()) + { + AddMsg(string.Format(GetString("PestilenceTransform")), 255, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Pestilence), GetString("ApocalypseIsNigh"))); + } string MimicMsg = ""; foreach (var pc in Main.AllPlayerControls) { diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index ac81984f2..2bd87d2c6 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -3255,7 +3255,13 @@ public static void Postfix(PlayerControl __instance) if (PlagueBearer.IsPlagued(seer.PlayerId, target.PlayerId)) Mark.Append($"●"); break; - + case CustomRoles.Baker: + case CustomRoles.Famine: + if (Baker.HasBread(seer.PlayerId, target.PlayerId)) + { + Mark.Append($"●"); + } + break; case CustomRoles.Arsonist: if (seer.IsDousedPlayer(target)) Mark.Append($"▲"); diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 551ac1e21..e7f85d04e 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -582,6 +582,7 @@ "ChronomancerInfo": "Kill in bursts", "PitfallInfo": "Setup traps around the map", "BerserkerInfo": "Kill to increase your level", + "WarInfo": "Destroy everything", "EvilMiniInfo": "No one can hurt you until you grow up", "BlackmailerInfo": "Silence other players", "InstigatorInfo": "Sow discord among the crewmates", @@ -880,8 +881,8 @@ "GodfatherInfoLong": "(Impostors):\nAs the Godfather, you vote someone to make them your target.\nIn the next round if someone kills the target, the killer will turn into a Refugee.", "ChronomancerInfoLong": "(Impostors):\nAs the Chronomancer, you can charge up your kill button. Once activated the Chronomancer can use their kill button infinitely until they run out of charge.", "PitfallInfoLong": "(Impostors):\nAs the Pitfall, you use your shapeshift to mark the area around the shapeshift as a trap. Players who enter this area will be immobilized for a short period of time and their vision will be affected.", - "BerserkerInfoLong": "(Apocalypse):\nAs the Berserker, you level up with each kill.\nUpon reaching a certain level defined by the host, you unlock a new power.\n\nScavenged kills make your kills disappear.\nBombed kills make your kills explode. After a certain level you become War.", - "WarInfoLong": "(Apocalypse):\nAs War, you are invincible and can kill anyone with your previous powers.", + "BerserkerInfoLong": "(Apocalypse):\nAs the Berserker, you level up with each kill.\nUpon reaching a certain level defined by the host, you unlock a new power.\n\nScavenged kills make your kills disappear.\nBombed kills make your kills explode. Be careful when killing, as this can kill your other Apocalypse members if they are near. \nAfter a certain level you become War.", + "WarInfoLong": "(Apocalypse):\nAs War, you are invincible, have a lower kill cooldown, and can kill anyone with your previous powers.\nYour presence is announced to everyone the meeting after you transform.", "EvilMiniInfoLong": "(Impostors):\nAs an Evil Mini, you are unkillable until you grow up and have a very long initial kill cooldown, which is drastically shortened as you grow up.", "BlackmailerInfoLong": "(Impostors):\nAs the Blackmailer, when you shift into a target you will blackmail that player, and the blackmailed player cannot speak.\n\nSpeaking by the blackmailed player will trigger the confusion command, please do not speak when the blackmailed player sees his icon", "InstigatorInfoLong": "(Impostors):\nAs the Instigator, it's your job to turn the crewmates against each other. Each time a Crewmate is voted out in a meeting, as long as you are alive, an additional Crewmate who voted for the innocent player will die after the meeting. The number of additional players dying is determined by the host.", @@ -984,7 +985,7 @@ "ProvocateurInfoLong": "(Neutrals):\nAs the Provocateur, you can kill any target with the kill button. If the target loses at the end of the game, the Provocateur wins with the winning team.", "BloodKnightInfoLong": "(Neutrals):\nThe Blood Knight wins when they're the last killing role alive and the amount of crewmates is lower or equal to the amount of Blood Knights. The Blood Knight gains a temporary shield after every kill that makes them immortal for a few seconds.", "PlagueBearerInfoLong": "(Apocalypse):\nAs the Plaguebearer, plague everyone using your kill button to turn into Pestilence.\nOnce you turn into Pestilence you will become immortal and gain the ability to kill.\nIn addition to this, after turning into Pestilence you will kill anyone who tries to kill you.\n\nTo win, turn into Pestilence and kill everyone.", - "PestilenceInfoLong": "(Apocalypse):\nAs Pestilence, you're an unstoppable machine.\nAny attack towards you will be reflected back towards them.\nIndirect kills don't even kill you.\n\nOnly way to kill Pestilence is by vote, or by it misguessing.", + "PestilenceInfoLong": "(Apocalypse):\nAs Pestilence, you're an unstoppable machine.\nAny attack towards you will be reflected back towards them.\nIndirect kills don't even kill you.\n\nOnly way to kill Pestilence is by vote, or by it misguessing.\nYour presence is announced to everyone the meeting after you transform.", "TotocalcioInfoLong": "(Neutrals):\nThe Follower can use their Kill button on someone to start following them and can use the Kill button again to switch the following target. If the Follower's target wins, the Follower will win along with them. Note: The Follower can also win after they die.", "SuccubusInfoLong": "(Neutrals):\nAs the Cultist, your kill button is used to Charm others, making them win with you. To win, charm all who pose a threat and gain majority.\nDepending on settings, you may be able to charm Neutrals, and those you Charm may count as their original team, nothing, or a Cultist to determine when you win due to majority.", "NSerialKillerInfoLong": "(Neutrals):\nAs the Serial Killer, you win if you are the last player alive. Depending on settings, you will not be impacted by any harmful interactions and may have a teammate.", @@ -1004,7 +1005,7 @@ "BenefactorInfoLong": "(Crewmates):\nAs the Benefactor, whenever you complete a task, the task will be marked. When another player completes the marked task, they get a temporary shield.\n\n Note: Shield only protects from direct kill attacks", "MedusaInfoLong": "(Neutrals):\nAs the Medusa, you can stone bodies much like cleaning a body.\nStoned bodies cannot be reported.\n\nKill everyone to win.", "BakerInfoLong":"(Apocalypse): \nAs the Baker, you can use your kill button on a player per round to give them Bread. \nOnce a set amount of players are alive with bread, you become Famine.", - "FamineInfoLong": "(Apocalypse): \nIf Famine is not voted out after the meeting they become Famine, every player without bread will starve (excluding other Apocalypse members).", + "FamineInfoLong": "(Apocalypse): \nIf Famine is not voted out after the meeting they become Famine, every player without bread will starve (excluding other Apocalypse members).\nYou are invincible and your presence is announced to everyone the meeting after you transform.", "SpiritcallerInfoLong": "(Neutrals):\nAs the Spiritcaller, your victims become Evil Spirits after they die. These spirits can help you win by freezing other players for a short time and/or blocking their vision. Alternatively, the spirits can give you a shield that protects you briefly from an attempted kill.", "AmnesiacInfoLong": "(Neutrals):\nAs the Amnesiac, use your report button to remember a role.\n\nIf the target was an Impostor, you'll become a Refugee.\nIf the target was a crewmate, you'll become the target role if compatible (otherwise you become an Engineer).\nIf the target was a passive neutral or a neutral killer not specified, you'll become the role defined in the settings.\nIf the target was a neutral killer of a select few, you'll become the role they are.", "ImitatorInfoLong": "(Neutrals):\nAs the Imitator, use your kill button to imitate a player.\n\nYou'll either become a Sheriff, a Refugee or some Neutral", @@ -1019,7 +1020,7 @@ "PixieInfoLong": "(Neutrals):\nAs the Pixie, Mark upto x amount of targets each round by using kill button on them. When the meeting starts, your job is to have one of the marked targets ejected. If unsuccessful you will suicide, except if you didn't mark any targets or all the targets are dead. The selected targets resets to 0 after the meeting ends. If you succeed you will gain a point. You see all your targets in colored names.\n\nYou win with the winning team when you have certain amounts of points set by the host.", "OccultistInfoLong": "(Neutrals):\nAs the Occultist, you can curse players or kill them.\nCursing a player works the same as spelling as a Spellcaster.", "SoulCollectorInfoLong": "(Apocalypse):\nAs a Soul Collector, you vote players to predict their death. If the prediction is correct and the target dies in the next round you collect their soul. \n\nOnce you collect the configurable amount of souls, you become Death.", - "DeathInfoLong": "(Apocalypse): \nOnce the Soul Collector has collected their needed souls, they become Death and a meeting is immediately called. If Death is not ejected by the end of this meeting, Death kills everyone and wins.", + "DeathInfoLong": "(Apocalypse): \nOnce the Soul Collector has collected their needed souls, they become Death. Depending on the host's settings, a meeting may or may not be called immediately. If Death is not ejected by the end of the next meeting, Death kills everyone and wins.\nYou are invincible and your presence is announced to everyone the meeting after you transform.", "SchrodingersCatInfoLong": "(Neutrals):\nAs Schrodingers Cat, if someone attempts to use the kill button on you, you will block the action and join their team. This blocking ability works only once. By default, you don't have a victory condition, meaning you win only after switching teams.\nIn Addition to this, you will be counted as nothing in the game.\n\nNote: If the killing machine attempts to use their kill button on you, the interaction is not blocked, and you will die.", "RomanticInfoLong": "(Neutrals):\nThe Romantic can pick their lover partner using their kill button (this can be done at any point of the game). Once they've picked their partner, they can use their kill button to give their partner a temporary shield which protects them from attacks. If their lover partner dies, the Romantic's role will change according to the following conditions:\n1. If their partner was an Impostor, the Romantic becomes the Refugee\n2. If their partner was a Neutral Killer, then they become Ruthless Romantic.\n3. If their partner was a Crewmate or a non-killing neutral, the Romantic becomes the Vengeful Romantic. \n\nThe Romantic wins with the winning team if their partner wins.\nNote : If your role changes your win condition will be changed accordingly", "RuthlessRomanticInfoLong": "(Neutrals):\nYou change your roles from Romantic if your partner (A neutral killer) is killed. As Ruthless Romantic, you win if you kill everyone and be the last one standing. If you win your dead partner also wins with you.", @@ -2722,13 +2723,13 @@ "CollectOwnSoulOpt": "Can collect their own soul", "SoulCollectorSelfVote": "Host settings do not allow you to collect your own soul", "SoulCollectorToDeath": "You have become Death!!!", - "SoulCollectorTransform": "Now Soul Collector has become Death, Destroyer of Worlds and Horseman of the Apocalypse!

Find them and vote them out before it's too late!", + "SoulCollectorTransform": "Now Soul Collector has become Death, Destroyer of Worlds and Horseman of the Apocalypse!

Find them and vote them out before they bring forth Armageddon!", "CallMeetingIfDeath": "Call a meeting immediately after Death transforms", "GetPassiveSouls": "Gain a passive soul every round", "PassiveSoulGained": "You have gained a passive soul from the underworld.", "ApocalypseIsNigh": "[ The Apocalypse Is Nigh! ]", "BakerToFamine": "You have become Famine!!!", - "BakerTransform": "The Baker has transformed into Famine, Horseman of the Apocalypse! A famine has begun!", + "BakerTransform": "The Baker has transformed into Famine, Horseman of the Apocalypse! A famine has begun!", "BakerAlreadyBreaded": "That player already has bread!", "BakerBreadUsedAlready": "You've already given a player bread this round!", "BakerBreaded": "Player given bread", @@ -2788,6 +2789,7 @@ "BerserkerKillCooldown": "Berserker kill cooldown", "BerserkerMax": "Max level that Berserker can reach", + "BerserkerHasImpostorVision": "Berserker Has Impostor Vision", "BerserkerOneCanKillCooldown": "Unlock lower kill cooldown", "BerserkerOneKillCooldown": "Kill cooldown after unlocking", "BerserkerTwoCanScavenger": "Unlock scavenged kills", @@ -2798,6 +2800,8 @@ "BerserkerLevelRequirement": "Level requirement for unlock", "KilledByBerserker": "Killed by Berserker", "BerserkerToWar": "You have become War!!!", + "BerserkerTransform": "The Berserker has transformed into War, Horseman of the Apocalypse! Cry 'Havoc!', and let slip the dogs of war.", + "WarKillCooldown": "War kill cooldown", "ImpCanBeUnlucky": "Impostors can become Unlucky", "CrewCanBeUnlucky": "Crewmates can become Unlucky", @@ -3383,6 +3387,7 @@ "PestilenceHasImpostorVision": "Pestilence Has Impostor Vision", "GuessPestilence": "You just tried to guess Pestilence!\n\nSorry, Pestilence killed you.", "PestilenceImmune": "You can't kill Pestilence.", + "PestilenceTransform": "A Plague has consumed the Crew, transforming the Plaguebearer into Pestilence, Horseman of the Apocalypse!", "RomanticBetCooldown": "Pick Partner Cooldown", "RomanticProtectCooldown": "Protect Cooldown", "RomanticBetPlayer": "You picked your partner", diff --git a/Resources/roleColor.json b/Resources/roleColor.json index 207145c27..19b2145ff 100644 --- a/Resources/roleColor.json +++ b/Resources/roleColor.json @@ -233,5 +233,7 @@ "SchrodingersCat": "#404040", "PlagueDoctor": "#ff6633", "Rainbow": "#55FFCB", - "Apocalypse": "#ff174f" + "Apocalypse": "#ff174f", + "Berserker": "#FF0055", + "War": "#2B0804" } diff --git a/Roles/Neutral/Baker.cs b/Roles/Neutral/Baker.cs index ed7e9c0be..d4f20c2b5 100644 --- a/Roles/Neutral/Baker.cs +++ b/Roles/Neutral/Baker.cs @@ -129,6 +129,7 @@ public static void KillIfNotEjected(PlayerControl player) { var deathList = new List(); var baker = player.PlayerId; + if (Main.AfterMeetingDeathPlayers.ContainsKey(baker)) return; foreach (var pc in Main.AllAlivePlayerControls) { var notBaker = pc.PlayerId; @@ -145,6 +146,7 @@ public static void KillIfNotEjected(PlayerControl player) Main.AfterMeetingDeathPlayers.Remove(pc.PlayerId); } } + else return; } CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Starved, [.. deathList]); } diff --git a/Roles/Neutral/Lawyer.cs b/Roles/Neutral/Lawyer.cs index bb102716a..f01997a97 100644 --- a/Roles/Neutral/Lawyer.cs +++ b/Roles/Neutral/Lawyer.cs @@ -15,6 +15,7 @@ public static class Lawyer private static OptionItem CanTargetImpostor; private static OptionItem CanTargetNeutralKiller; + private static OptionItem CanTargetNeutralApocalypse; private static OptionItem CanTargetCrewmate; private static OptionItem CanTargetJester; public static OptionItem ChangeRolesAfterTargetKilled; @@ -49,6 +50,7 @@ public static void SetupCustomOption() // .SetValueFormat(OptionFormat.Multiplier); CanTargetImpostor = BooleanOptionItem.Create(Id + 10, "LawyerCanTargetImpostor", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Lawyer]); CanTargetNeutralKiller = BooleanOptionItem.Create(Id + 11, "LawyerCanTargetNeutralKiller", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Lawyer]); + CanTargetNeutralApocalypse = BooleanOptionItem.Create(Id + 17, "CanTargetNeutralApocalypse", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Lawyer]); CanTargetCrewmate = BooleanOptionItem.Create(Id + 12, "LawyerCanTargetCrewmate", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Lawyer]); CanTargetJester = BooleanOptionItem.Create(Id + 13, "LawyerCanTargetJester", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Lawyer]); KnowTargetRole = BooleanOptionItem.Create(Id + 14, "KnowTargetRole", false, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.Lawyer]); @@ -76,9 +78,10 @@ public static void Add(byte playerId) if (playerId == target.PlayerId) continue; else if (!CanTargetImpostor.GetBool() && target.Is(CustomRoleTypes.Impostor)) continue; else if (!CanTargetNeutralKiller.GetBool() && target.IsNeutralKiller()) continue; + else if (!CanTargetNeutralApocalypse.GetBool() && target.IsNeutralApocalypse()) continue; else if (!CanTargetCrewmate.GetBool() && target.Is(CustomRoleTypes.Crewmate)) continue; else if (!CanTargetJester.GetBool() && target.Is(CustomRoles.Jester)) continue; - else if (target.Is(CustomRoleTypes.Neutral) && !target.IsNeutralKiller() && !target.Is(CustomRoles.Jester)) continue; + else if (target.Is(CustomRoleTypes.Neutral) && !target.IsNeutralKiller() && !target.IsNeutralApocalypse() && !target.Is(CustomRoles.Jester)) continue; if (target.GetCustomRole() is CustomRoles.GM or CustomRoles.SuperStar or CustomRoles.NiceMini or CustomRoles.EvilMini) continue; if (Utils.GetPlayerById(playerId).Is(CustomRoles.Lovers) && target.Is(CustomRoles.Lovers)) continue; diff --git a/Roles/Neutral/SoulCollector.cs b/Roles/Neutral/SoulCollector.cs index 1e0aa2399..8434d4b84 100644 --- a/Roles/Neutral/SoulCollector.cs +++ b/Roles/Neutral/SoulCollector.cs @@ -1,7 +1,9 @@ using Hazel; +using InnerNet; using System.Collections.Generic; using System.Diagnostics.Metrics; using System.Linq; +using TOHE.Roles.Impostor; using static TOHE.Options; using static TOHE.Translator; using static UnityEngine.GraphicsBuffer; @@ -135,17 +137,16 @@ public static void OnPlayerDead(PlayerControl deadPlayer) public static void BecomeDeath(PlayerControl player) { if (SoulCollectorPoints[player.PlayerId] < SoulCollectorPointsOpt.GetInt()) return; - player.RpcSetCustomRole(CustomRoles.Death); player.Notify(GetString("SoulCollectorToDeath")); player.RpcGuardAndKill(player); if (CallMeetingIfDeath.GetBool()) PlayerControl.LocalPlayer.NoCheckStartMeeting(null, force: true); - KillIfNotEjected(player); - + if (GameStates.IsCanMove) KillIfNotEjected(player); } public static void KillIfNotEjected(PlayerControl player) { var deathList = new List(); + if (Main.AfterMeetingDeathPlayers.ContainsKey(player.PlayerId)) return; foreach (var pc in Main.AllAlivePlayerControls) { if (pc.IsNeutralApocalypse()) continue; @@ -161,6 +162,7 @@ public static void KillIfNotEjected(PlayerControl player) Main.AfterMeetingDeathPlayers.Remove(pc.PlayerId); } } + else return; } CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Armageddon, [.. deathList]); } From bae92ceb7e971f7fe4a82d60dd566ee685149f1a Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:56:49 -0500 Subject: [PATCH 12/15] baker id change --- Roles/Neutral/Baker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Roles/Neutral/Baker.cs b/Roles/Neutral/Baker.cs index d4f20c2b5..ab08e8707 100644 --- a/Roles/Neutral/Baker.cs +++ b/Roles/Neutral/Baker.cs @@ -9,7 +9,7 @@ namespace TOHE.Roles.Neutral; public static class Baker { - private static readonly int Id = 27800; + private static readonly int Id = 28000; public static List playerIdList = []; public static bool IsEnable = false; public static Dictionary> BreadList = []; From 4c85e46d397e6560a23436b067ba1d15c8a866f7 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Wed, 28 Feb 2024 17:42:47 -0500 Subject: [PATCH 13/15] add readme to .gitignore for myself if this shows up in the pr then I'll revert it this is for my own tasks that I have to do --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5f6b8eed9..cf14e5176 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ token.env FodyWeavers.xsd TOHE.sln -.vscode/settings.json \ No newline at end of file +.vscode/settings.json +README.md From 68beb15568ba6aa4c29e2627c49581708265db80 Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Wed, 28 Feb 2024 17:44:35 -0500 Subject: [PATCH 14/15] Revert last change woops --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index cf14e5176..9a155c3c8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,3 @@ token.env FodyWeavers.xsd TOHE.sln .vscode/settings.json -README.md From 0539a82b41b51dbe0f93665b2cde1d459977700f Mon Sep 17 00:00:00 2001 From: Marg <51059123+MargaretTheFool@users.noreply.github.com> Date: Tue, 9 Apr 2024 20:19:15 -0400 Subject: [PATCH 15/15] updated everything with rolebase --- Modules/CustomRolesHelper.cs | 52 + Modules/ExtendedPlayerControl.cs | 5 + Modules/GameState.cs | 5 + Modules/OptionHolder.cs | 26 + Patches/CheckGameEndPatch.cs | 17 + Resources/Lang/en_US.json | 3595 ++++++++++++++++++++++++ Resources/roleColor.json | 7 + Roles/AddOns/Common/Susceptible.cs | 20 + Roles/Core/AssignManager/RoleAssign.cs | 801 ++++++ Roles/Core/CustomRoleManager.cs | 389 +++ Roles/Neutral/Baker.cs | 123 + Roles/Neutral/Berserker.cs | 182 ++ Roles/Neutral/PlagueBearer.cs | 22 + Roles/Neutral/SoulCollector.cs | 59 +- main.cs | 52 + 15 files changed, 5354 insertions(+), 1 deletion(-) create mode 100644 Roles/Core/AssignManager/RoleAssign.cs create mode 100644 Roles/Core/CustomRoleManager.cs create mode 100644 Roles/Neutral/Berserker.cs diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 594fb4bc7..2514f9a11 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -415,7 +415,10 @@ CustomRoles.Bandit or CustomRoles.Pestilence or CustomRoles.PlagueBearer or CustomRoles.Baker or +<<<<<<< Updated upstream CustomRoles.Famine or +======= +>>>>>>> Stashed changes CustomRoles.Agitater or CustomRoles.Innocent or CustomRoles.Vulture or @@ -479,6 +482,11 @@ CustomRoles.Spiritcaller or CustomRoles.Succubus; } +<<<<<<< Updated upstream +======= + return role.IsNK() || role.IsNonNK() || role.IsNA(); + } +>>>>>>> Stashed changes public static bool IsNK(this CustomRoles role) { if (role == CustomRoles.Arsonist && Options.ArsonistCanIgniteAnytime.GetBool()) return true; @@ -518,6 +526,22 @@ CustomRoles.BloodKnight or CustomRoles.Spiritcaller or CustomRoles.Agitater or CustomRoles.RuthlessRomantic; +<<<<<<< Updated upstream +======= + } + public static bool IsNA(this CustomRoles role) + { + return role is + CustomRoles.PlagueBearer or + CustomRoles.Pestilence or + CustomRoles.SoulCollector or + CustomRoles.Death or + CustomRoles.Baker or + CustomRoles.Famine or + CustomRoles.Berserker or + CustomRoles.War; + +>>>>>>> Stashed changes } public static bool IsNonNK(this CustomRoles role) // ROLE ASSIGNING, NOT NEUTRAL TYPE { @@ -702,7 +726,12 @@ CustomRoles.Zombie or CustomRoles.Warlock or CustomRoles.Undertaker or CustomRoles.RiftMaker or +<<<<<<< Updated upstream CustomRoles.Assassin or +======= + CustomRoles.Ninja or + CustomRoles.Bloodmoon or +>>>>>>> Stashed changes CustomRoles.Anonymous or CustomRoles.Visionary or CustomRoles.Miner or @@ -1996,17 +2025,28 @@ public static CountTypes GetCountTypes(this CustomRoles role) CustomRoles.Pickpocket => CustomWinner.Pickpocket, CustomRoles.Traitor => CustomWinner.Traitor, CustomRoles.Vulture => CustomWinner.Vulture, +<<<<<<< Updated upstream CustomRoles.Apocalypse => CustomWinner.Apocalypse, CustomRoles.Medusa => CustomWinner.Medusa, CustomRoles.Spiritcaller => CustomWinner.Spiritcaller, CustomRoles.Glitch => CustomWinner.Glitch, //CustomRoles.PlagueBearer => CustomWinner.Apocalypse, +======= + CustomRoles.Medusa => CustomWinner.Medusa, + CustomRoles.Spiritcaller => CustomWinner.Spiritcaller, + CustomRoles.Glitch => CustomWinner.Glitch, + CustomRoles.Apocalypse => CustomWinner.Apocalypse, +>>>>>>> Stashed changes CustomRoles.Masochist => CustomWinner.Masochist, CustomRoles.Doomsayer => CustomWinner.Doomsayer, CustomRoles.Shroud => CustomWinner.Shroud, CustomRoles.Seeker => CustomWinner.Seeker, +<<<<<<< Updated upstream //CustomRoles.SoulCollector => CustomWinner.Apocalypse, //CustomRoles.Death => CustomWinner.Apocalypse, +======= + CustomRoles.PlagueDoctor => CustomWinner.PlagueDoctor, +>>>>>>> Stashed changes CustomRoles.RuthlessRomantic => CustomWinner.RuthlessRomantic, CustomRoles.Mini => CustomWinner.NiceMini, CustomRoles.Doppelganger => CustomWinner.Doppelganger, @@ -2031,6 +2071,11 @@ public static CountTypes GetCountTypes(this CustomRoles role) CountTypes.Shroud => CustomRoles.Shroud, CountTypes.Werewolf => CustomRoles.Werewolf, CountTypes.Wraith => CustomRoles.Wraith, +<<<<<<< Updated upstream +======= + /*CountTypes.Pestilence => CustomRoles.Pestilence, + CountTypes.PlagueBearer => CustomRoles.PlagueBearer,*/ +>>>>>>> Stashed changes CountTypes.Agitater => CustomRoles.Agitater, CountTypes.SerialKiller => CustomRoles.SerialKiller, CountTypes.Quizmaster => CustomRoles.Quizmaster, @@ -2053,11 +2098,14 @@ public static CountTypes GetCountTypes(this CustomRoles role) CountTypes.Arsonist => CustomRoles.Arsonist, CountTypes.RuthlessRomantic => CustomRoles.RuthlessRomantic, CountTypes.Apocalypse => CustomRoles.Apocalypse, +<<<<<<< Updated upstream //CountTypes.Impostor => CustomRoles.ImpostorTOHE, //CountTypes.Crew => CustomRoles.CrewmateTOHE, //CountTypes.None => throw new System.NotImplementedException(), //CountTypes.Charmed => throw new System.NotImplementedException(), //CountTypes.Rogue => throw new System.NotImplementedException(), +======= +>>>>>>> Stashed changes _ => throw new System.NotImplementedException() }; public static bool HasSubRole(this PlayerControl pc) => Main.PlayerStates[pc.PlayerId].SubRoles.Count > 0; @@ -2101,6 +2149,10 @@ public enum CountTypes Spiritcaller, Apocalypse, Quizmaster, +<<<<<<< Updated upstream +======= + Apocalypse, +>>>>>>> Stashed changes Glitch, Arsonist, Huntsman, diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index d015087cf..50cee6d16 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -1329,8 +1329,13 @@ public static List GetPlayersInAbilityRangeSorted(this PlayerCont public static bool IsNeutralChaos(this PlayerControl player) => player.GetCustomRole().IsNC(); public static bool IsNeutralApocalypse(this PlayerControl player) => player.GetCustomRole().IsNA(); public static bool IsNonNeutralKiller(this PlayerControl player) => player.GetCustomRole().IsNonNK(); +<<<<<<< Updated upstream public static bool IsSnitchTarget(this PlayerControl player) => player.GetCustomRole().IsSnitchTarget(); +======= + public static bool IsNeutralApocalypse(this PlayerControl player) => player.GetCustomRole().IsNA(); + +>>>>>>> Stashed changes public static bool KnowDeathReason(this PlayerControl seer, PlayerControl target) => (seer.Is(CustomRoles.Doctor) || seer.Is(CustomRoles.Autopsy) || (seer.Data.IsDead && Options.GhostCanSeeDeathReason.GetBool())) diff --git a/Modules/GameState.cs b/Modules/GameState.cs index 74c164c40..9dbd8f662 100644 --- a/Modules/GameState.cs +++ b/Modules/GameState.cs @@ -360,8 +360,13 @@ public enum DeathReason Targeted, Retribution, WrongAnswer, +<<<<<<< Updated upstream Armageddon, Starved, +======= + Starved, + Armageddon, +>>>>>>> Stashed changes //Please add all new roles with deathreason & new deathreason in Susceptible.CallEnabledAndChange etc = -1, diff --git a/Modules/OptionHolder.cs b/Modules/OptionHolder.cs index c4d156c3f..83b05161b 100644 --- a/Modules/OptionHolder.cs +++ b/Modules/OptionHolder.cs @@ -948,6 +948,10 @@ public static void Load() .SetGameMode(CustomGameMode.Standard) .SetValueFormat(OptionFormat.Players); +<<<<<<< Updated upstream +======= + +>>>>>>> Stashed changes NeutralRoleWinTogether = BooleanOptionItem.Create(60017, "NeutralRoleWinTogether", false, TabGroup.NeutralRoles, false) .SetGameMode(CustomGameMode.Standard) @@ -993,7 +997,10 @@ public static void Load() */ Arrogance.SetupCustomOption(); +<<<<<<< Updated upstream +======= +>>>>>>> Stashed changes /* * Bomber @@ -1084,7 +1091,12 @@ public static void Load() /* * Greedy */ +<<<<<<< Updated upstream Greedier.SetupCustomOption(); +======= + Greedy.SetupCustomOption(); + +>>>>>>> Stashed changes /* * Hangman @@ -2098,6 +2110,7 @@ public static void Load() */ Solsticer.SetupCustomOption(); +<<<<<<< Updated upstream SetupRoleOptions(15400, TabGroup.NeutralRoles, CustomRoles.Terrorist); CanTerroristSuicideWin = BooleanOptionItem.Create(15402, "CanTerroristSuicideWin", false, TabGroup.NeutralRoles, false) @@ -2113,6 +2126,11 @@ public static void Load() MarioVentCD = FloatOptionItem.Create(15503, "VentCooldown", new(0f, 180f, 1f), 15f, TabGroup.NeutralRoles, false) .SetParent(CustomRoleSpawnChances[CustomRoles.Mario]) .SetValueFormat(OptionFormat.Seconds); +======= + Terrorist.SetupCustomOptions(); + + Vector.SetupCustomOptions(); +>>>>>>> Stashed changes Vulture.SetupCustomOption(); @@ -2210,6 +2228,7 @@ public static void Load() .SetColor(new Color32(127, 140, 141, byte.MaxValue)); Baker.SetupCustomOption(); +<<<<<<< Updated upstream /* * Berserker */ @@ -2239,11 +2258,18 @@ public static void Load() WarKillCooldown = FloatOptionItem.Create(614, "WarKillCooldown", new(0f, 150f, 2.5f), 15f, TabGroup.NeutralRoles, false).SetParent(BerserkerFourCanNotKill) .SetValueFormat(OptionFormat.Seconds); +======= + + Berserker.SetupCustomOption(); +>>>>>>> Stashed changes PlagueBearer.SetupCustomOption(); SoulCollector.SetupCustomOption(); +<<<<<<< Updated upstream +======= +>>>>>>> Stashed changes #endregion #region Add-Ons Settings diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index eb9d14674..00afa9557 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -92,7 +92,11 @@ public static bool Prefix() CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); } break; +<<<<<<< Updated upstream case CustomWinner.Succubus: +======= + case CustomWinner.Cultist: +>>>>>>> Stashed changes if (pc.Is(CustomRoles.Charmed) && !CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) { CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); @@ -166,7 +170,11 @@ public static bool Prefix() foreach (var pc in Main.AllPlayerControls) { if (pc.Is(CustomRoles.Phantom) && pc.GetPlayerTaskState().IsTaskFinished && pc.Data.IsDead +<<<<<<< Updated upstream && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Apocalypse || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Spiritcaller || CustomWinnerHolder.WinnerTeam == CustomWinner.Quizmaster ) && (Options.PhantomSnatchesWin.GetBool() || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) //|| CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist +======= + && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Cultist || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Apocalypse || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Spiritcaller || CustomWinnerHolder.WinnerTeam == CustomWinner.Quizmaster ) && (Phantom.PhantomSnatchesWin.GetBool() || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) //|| CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist +>>>>>>> Stashed changes { reason = GameOverReason.ImpostorByKill; if (!CustomWinnerHolder.CheckForConvertedWinner(pc.PlayerId)) @@ -179,7 +187,11 @@ public static bool Prefix() foreach (var pc in Main.AllPlayerControls) { if (pc.Is(CustomRoles.CursedSoul) && !pc.Data.IsDead +<<<<<<< Updated upstream && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Succubus || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Apocalypse || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Rogue || CustomWinnerHolder.WinnerTeam == CustomWinner.Jester || CustomWinnerHolder.WinnerTeam == CustomWinner.Executioner || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) // || CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist +======= + && (((CustomWinnerHolder.WinnerTeam == CustomWinner.Impostor || CustomWinnerHolder.WinnerTeam == CustomWinner.Crewmate || CustomWinnerHolder.WinnerTeam == CustomWinner.Jackal || CustomWinnerHolder.WinnerTeam == CustomWinner.BloodKnight || CustomWinnerHolder.WinnerTeam == CustomWinner.SerialKiller || CustomWinnerHolder.WinnerTeam == CustomWinner.Juggernaut || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Doppelganger || CustomWinnerHolder.WinnerTeam == CustomWinner.PotionMaster || CustomWinnerHolder.WinnerTeam == CustomWinner.Poisoner || CustomWinnerHolder.WinnerTeam == CustomWinner.Cultist || CustomWinnerHolder.WinnerTeam == CustomWinner.Infectious || CustomWinnerHolder.WinnerTeam == CustomWinner.Jinx || CustomWinnerHolder.WinnerTeam == CustomWinner.Virus || CustomWinnerHolder.WinnerTeam == CustomWinner.Arsonist || CustomWinnerHolder.WinnerTeam == CustomWinner.Pelican || CustomWinnerHolder.WinnerTeam == CustomWinner.Wraith || CustomWinnerHolder.WinnerTeam == CustomWinner.Agitater || CustomWinnerHolder.WinnerTeam == CustomWinner.Apocalypse || CustomWinnerHolder.WinnerTeam == CustomWinner.Bandit || CustomWinnerHolder.WinnerTeam == CustomWinner.Jester || CustomWinnerHolder.WinnerTeam == CustomWinner.Executioner || CustomWinnerHolder.WinnerTeam == CustomWinner.PlagueDoctor)))) // || CustomWinnerHolder.WinnerTeam == CustomWinner.Occultist +>>>>>>> Stashed changes { reason = GameOverReason.ImpostorByKill; if (!CustomWinnerHolder.CheckForConvertedWinner(pc.PlayerId)) @@ -374,7 +386,12 @@ public static bool Prefix() CustomWinnerHolder.AdditionalWinnerTeams.Add(AdditionalWinners.Romantic); } } +<<<<<<< Updated upstream foreach (var pc in Main.AllPlayerControls.Where(x => x.IsNeutralApocalypse() && Main.AllAlivePlayerControls.All(p => p.IsNeutralApocalypse()))) { +======= + foreach (var pc in Main.AllPlayerControls.Where(x => x.IsNeutralApocalypse() && Main.AllAlivePlayerControls.All(p => p.IsNeutralApocalypse()))) + { +>>>>>>> Stashed changes if (!CustomWinnerHolder.WinnerIds.Contains(pc.PlayerId)) CustomWinnerHolder.WinnerIds.Add(pc.PlayerId); diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index eea8a6cfa..7508bc49d 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -1,4 +1,5 @@ { +<<<<<<< Updated upstream "LanguageID": "0", "TextBelowVersionText": "", "HostText": "Host", @@ -3749,4 +3750,3598 @@ "ImpCanBeStatue": "Impostors can become Statue", "CrewCanBeStatue": "Crewmates can become Statue", "NeutralCanBeStatue": "Neutrals can become Statue" +======= + "LanguageID": "0", + "HostText": "Host", + "HostColor": "#902efd", + "IconColor": "#4bf4ff", + "Icon": "♥", + "HideHostText": "Hide 'Host♥' Text", + "NameColor": "#ffc0cb", + "kofi": "Ko-Fi", + "update": "Update", + "GitHub": "GitHub", + "Discord": "Discord", + "Website": "Website", + "PlayerNameForRoleInfo": "Hi {0}, your role is:- \n", + + "SubText.Crewmate": "Find and exile the Impostors", + "SubText.Impostor": "Sabotage and kill everyone", + "SubText.Neutral": "Work alone to achieve your victory", + "SubText.Madmate": "Help the Impostors", + + "TypeImpostor": "Impostors", + "TypeCrewmate": "Crewmates", + "TypeNeutral": "Neutrals", + "TypeAddon": "Add-ons", + "GuesserMode": "Guesser Mode", + + "TeamImpostor": "Impostor", + "TeamNeutral": "Neutral", + "TeamCrewmate": "Crewmate", + "TeamMadmate": "Madmate", + + "YouAreCrewmate": "You are a Crewmate", + "YouAreImpostor": "You are an Impostor", + "YouAreNeutral": "You are a Neutral", + "YouAreMadmate": "You are a Madmate", + + + "Role_Crewmate": "Crewmate", + "Role_Jester": "Jester", + "Role_Opportunist": "Opportunist", + "Role_Celebrity": "Celebrity", + "Role_Bodyguard": "Bodyguard", + "Role_Dictator": "Dictator", + "Role_Mayor": "Mayor", + "Role_Doctor": "Doctor", + "Role_Maverick": "Maverick", + "Role_Pursuer": "Pursuer", + "Role_Follower": "Follower", + "Role_Amnesiac": "Amnesiac", + "Role_Imitator": "Imitator", + "Role_Sheriff": "Sheriff", + "Role_Knight": "Knight", + "Role_Deputy": "Deputy", + "Role_NoChange": "Don't change the role", + + "CrewmatesCanGuess": "Crewmates can guess", + "ImpostorsCanGuess": "Impostors can guess", + "NeutralKillersCanGuess": "Neutral Killers can guess", + "PassiveNeutralsCanGuess": "Passive Neutrals can guess", + + "CanGuessAddons": "Can Guess Add-ons", + "ShowOnlyEnabledRolesInGuesserUI": "Show Only Enabled Roles In Guesser UI", + "CrewCanGuessCrew": "Crewmates Can Guess Crewmate Roles", + "ImpCanGuessImp": "Impostors Can Guess Impostor Roles", + "GuessImmune": "Sorry, but target is immune to being guessed!", + + + "GM": "Game Master", + "Sunnyboy": "Sunnyboy", + "Bard": "Bard", + "Nuker": "Nuker", + "Crewmate": "Crewmate", + "CrewmateTOHE": "Crewmate", + "Engineer": "Engineer", + "EngineerTOHE": "Engineer", + "Scientist": "Scientist", + "ScientistTOHE": "Scientist", + "GuardianAngel": "Guardian Angel", + "GuardianAngelTOHE": "Guardian Angel", + "Impostor": "Impostor", + "ImpostorTOHE": "Impostor", + "Shapeshifter": "Shapeshifter", + "ShapeshifterTOHE": "Shapeshifter", + + "BountyHunter": "Bounty Hunter", + "Fireworker": "Fireworker", + "Mercenary": "Mercenary", + "ShapeMaster": "Shapemaster", + "Vampire": "Vampire", + "Vampiress": "Vampiress", + "Warlock": "Warlock", + "Ninja": "Ninja", + "Zombie": "Zombie", + "Anonymous": "Anonymous", + "Miner": "Miner", + "KillingMachine": "Killing Machine", + "Escapist": "Escapist", + "Witch": "Witch", + "Nemesis": "Nemesis", + "Bloodmoon": "Bloodmoon", + "Puppeteer": "Puppeteer", + "Mastermind": "Mastermind", + "TimeThief": "Time Thief", + "Sniper": "Sniper", + "Undertaker": "Undertaker", + "RiftMaker": "Rift Maker", + "EvilTracker": "Evil Tracker", + "EvilGuesser": "Evil Guesser", + "AntiAdminer": "Anti Adminer", + "Arrogance": "Arrogance", + "Bomber": "Bomber", + "Scavenger": "Scavenger", + "Trapster": "Trapster", + "Gangster": "Gangster", + "Cleaner": "Cleaner", + "Lightning": "Lightning", + "Greedy": "Greedy", + "CursedWolf": "Cursed Wolf", + "SoulCatcher": "Soul Catcher", + "QuickShooter": "Quick Shooter", + "Camouflager": "Camouflager", + "Eraser": "Eraser", + "Butcher": "Butcher", + "Hangman": "Hangman", + "Swooper": "Swooper", + "Crewpostor": "Crewpostor", + "Wildling": "Wildling", + "Trickster": "Trickster", + "Vindicator": "Vindicator", + "Parasite": "Parasite", + "Disperser": "Disperser", + "Inhibitor": "Inhibitor", + "Saboteur": "Saboteur", + "Councillor": "Councillor", + "Dazzler": "Dazzler", + "Deathpact": "Deathpact", + "Devourer": "Devourer", + "Consigliere": "Consigliere", + "Morphling": "Morphling", + "Twister": "Twister", + "Lurker": "Lurker", + "Visionary": "Visionary", + "Refugee": "Refugee", + "Underdog": "Underdog", + "Ludopath": "Ludopath", + "Godfather": "Godfather", + "Chronomancer": "Chronomancer", + "Pitfall": "Pitfall", + "Berserker": "Berserker", + "EvilMini": "Evil Mini", + "Blackmailer": "Blackmailer", + "Instigator": "Instigator", + "LazyGuy": "Lazy Guy", + "SuperStar": "Super Star", + "Celebrity": "Celebrity", + "Cleanser": "Cleanser", + "Keeper": "Keeper", + "Knight": "Knight", + "Mayor": "Mayor", + "Psychic": "Psychic", + "Mechanic": "Mechanic", + "Sheriff": "Sheriff", + "Vigilante": "Vigilante", + "Jailer": "Jailer", + "CopyCat": "Copycat", + "Snitch": "Snitch", + "Marshall": "Marshall", + "SpeedBooster": "Speed Booster", + "Doctor": "Doctor", + "Dictator": "Dictator", + "Detective": "Detective", + "NiceGuesser": "Nice Guesser", + "GuessMaster": "Guess Master", + "Transporter": "Transporter", + "TimeManager": "Time Manager", + "Veteran": "Veteran", + "Bastion": "Bastion", + "Bodyguard": "Bodyguard", + "Deceiver": "Deceiver", + "Grenadier": "Grenadier", + "Medic": "Medic", + "FortuneTeller": "Fortune Teller", + "Judge": "Judge", + "Mortician": "Mortician", + "Medium": "Medium", + "Pacifist": "Pacifist", + "Observer": "Observer", + "Monarch": "Monarch", + "Overseer": "Overseer", + "Coroner": "Coroner", + "Tracker": "Tracker", + "Merchant": "Merchant", + "President": "President", + "Hawk": "Hawk", + "Retributionist": "Retributionist", + "Deputy": "Deputy", + "Investigator": "Investigator", + "Guardian": "Guardian", + "Addict": "Addict", + "Mole": "Mole", + "Alchemist": "Alchemist", + "Tracefinder": "Tracefinder", + "Oracle": "Oracle", + "Spiritualist": "Spiritualist", + "Chameleon": "Chameleon", + "Inspector": "Inspector", + "Captain": "Captain", + "Admirer": "Admirer", + "TimeMaster": "Time Master", + "Crusader": "Crusader", + "Reverie": "Reverie", + "Lookout": "Lookout", + "Telecommunication": "Telecommunication", + "Lighter": "Lighter", + "TaskManager": "Task Manager", + "Witness": "Witness", + "Swapper": "Swapper", + "ChiefOfPolice": "Police Commissioner", + "NiceMini": "Nice Mini", + "Mini": "Mini", + "Spy": "Spy", + "Randomizer": "Randomizer", + "Enigma": "Enigma", + "Jester": "Jester", + "Arsonist": "Arsonist", + "Pyromaniac": "Pyromaniac", + "Kamikaze": "Kamikaze", + "Huntsman": "Huntsman", + "Terrorist": "Terrorist", + "Executioner": "Executioner", + "Lawyer": "Lawyer", + "Opportunist": "Opportunist", + "Vector": "Vector", + "Jackal": "Jackal", + "God": "God", + "Innocent": "Innocent", + "Stealth": "Stealth", + "Penguin": "Penguin", + "Pelican": "Pelican", + "PlagueDoctor": "Plague Scientist", + "Revolutionist": "Revolutionist", + "Hater": "Hater", + "Demon": "Demon", + "Stalker": "Stalker", + "Workaholic": "Workaholic", + "Solsticer": "Solsticer", + "Collector": "Collector", + "Provocateur": "Provocateur", + "BloodKnight": "Blood Knight", + "Apocalypse": "Apocalypse Team", + "PlagueBearer": "Plaguebearer", + "Pestilence": "Pestilence", + "SoulCollector": "Soul Collector", + "Death": "Death", + "Baker": "Baker", + "Famine": "Famine", + "War": "War", + "Glitch": "Glitch", + "Sidekick": "Sidekick", + "Follower": "Follower", + "Cultist": "Cultist", + "SerialKiller": "Serial Killer", + "Juggernaut": "Juggernaut", + "Infectious": "Infectious", + "Virus": "Virus", + "Pursuer": "Pursuer", + "Phantom": "Phantom", + "Pirate": "Pirate", + "Agitater": "Agitator", + "Maverick": "Maverick", + "CursedSoul": "Cursed Soul", + "Pickpocket": "Pickpocket", + "Traitor": "Traitor", + "Vulture": "Vulture", + "Taskinator": "Taskinator", + "Benefactor": "Benefactor", + "Medusa": "Medusa", + "Spiritcaller": "Spiritcaller", + "Amnesiac": "Amnesiac", + "Imitator": "Imitator", + "Bandit": "Bandit", + "Doppelganger": "Doppelganger", + "Masochist": "Masochist", + "Doomsayer": "Doomsayer", + "Shroud": "Shroud", + "Werewolf": "Werewolf", + "Shaman": "Shaman", + "Seeker": "Seeker", + "Pixie": "Pixie", + "Occultist": "Occultist", + "SchrodingersCat": "Schrodingers Cat", + "Romantic": "Romantic", + "VengefulRomantic": "Vengeful Romantic", + "RuthlessRomantic": "Ruthless Romantic", + "Poisoner": "Poisoner", + "HexMaster": "Hex Master", + "Wraith": "Wraith", + "Jinx": "Jinx", + "PotionMaster": "Potion Master", + "Necromancer": "Necromancer", + "Warden": "Warden", + "Minion": "Minion", + "LastImpostor": "Last Impostor", + "Overclocked": "Overclocked", + "Lovers": "Lovers", + "Madmate": "Madmate", + "Ntr": "Neptune", + "Watcher": "Watcher", + "Flash": "Flash", + "Torch": "Torch", + "Seer": "Seer", + "Tiebreaker": "Tiebreaker", + "Oblivious": "Oblivious", + "Bewilder": "Bewilder", + "Sunglasses": "Sunglasses", + "Workhorse": "Workhorse", + "Fool": "Fool", + "Avanger": "Avenger", + "Youtuber": "YouTuber", + "Egoist": "Egoist", + "TicketsStealer": "Stealer", + "Schizophrenic": "Schizophrenic", + "Mimic": "Mimic", + "Guesser": "Guesser", + "Necroview": "Necroview", + "Reach": "Reach", + "Charmed": "Charmed", + "Cleansed": "Cleansed", + "Bait": "Bait", + "Trapper": "Beartrap", + "Infected": "Infected", + "Onbound": "Onbound", + "Rebound": "Rebound", + "Mundane": "Mundane", + "Knighted": "Knighted", + "Unreportable": "Disregarded", + "Contagious": "Contagious", + "Lucky": "Lucky", + "Unlucky": "Unlucky", + "VoidBallot": "Void Ballot", + "Aware": "Aware", + "Fragile": "Fragile", + "DoubleShot": "Double Shot", + "Rascal": "Rascal", + "Soulless": "Soulless", + "Gravestone": "Gravestone", + "Lazy": "Lazy", + "Autopsy": "Autopsy", + "Loyal": "Loyal", + "EvilSpirit": "Evil Spirit", + "Recruit": "Recruit", + "Admired": "Admired", + "Glow": "Glow", + "Diseased": "Diseased", + "Antidote": "Antidote", + "Stubborn": "Stubborn", + "Swift": "Swift", + "Ghoul": "Ghoul", + "Bloodlust": "Bloodlust", + "Mare": "Mare", + "Burst": "Burst", + "Sleuth": "Sleuth", + "Clumsy": "Clumsy", + "Nimble": "Nimble", + "Circumvent": "Circumvent", + "Cyber": "Cyber", + "Hurried": "Hurried", + "Oiiai": "OIIAI", + "Influenced": "Influenced", + "Silent": "Silent", + "Susceptible": "Susceptible", + "Tricky": "Tricky", + "Rainbow": "Rainbow", + "Tired": "Tired", + "Statue": "Statue", + "BracketAddons": "Add Brackets To Add-ons", + "EngineerTOHEInfo": "Use the vents to catch the Impostors", + "ScientistTOHEInfo": "Access portable vitals from anywhere", + "ShapeshifterTOHEInfo": "Disguise as crewmates to frame them", + "GuardianAngelTOHEInfo": "Protect the crewmates from the Impostors", + "ImpostorTOHEInfo": "Kill and sabotage", + "CrewmateTOHEInfo": "Search for the Impostors", + "BountyHunterInfo": "Eliminate your target", + "FireworkerInfo": "Go out with a BANG", + "MercenaryInfo": "Keep killing, else you suicide", + "ShapeMasterInfo": "Swiftly kill with no shift cooldown", + "VampireInfo": "Your kills are delayed", + "VampiressInfo": "Your kills are delayed and direct", + "WarlockInfo": "Curse crewmates then shift to make them kill", + "NinjaInfo": "Mark a target, then shift to kill", + "ZombieInfo": "You are very slow", + "AnonymousInfo": "Force a player to report a body", + "MinerInfo": "Warp to your last used vent by shifting", + "KillingMachineInfo": "You can ONLY kill, but low cooldown", + "EscapistInfo": "Shift to mark places and warp back to them", + "WitchInfo": "Spell crewmates to kill them in meetings", + "NemesisInfo": "Kill when you're the last Impostor", + "BeforeNemesisInfo": "You can't kill yet", + "AfterNemesisInfo": "Now start killing", + "BloodmoonInfo": "Seek havoc upon the crewmates", + "PuppeteerInfo": "Make players kill for you", + "MastermindInfo": "Make others kill for you", + "TimeThiefInfo": "Lower meeting time by killing", + "SniperInfo": "Snipe players from a distance by shifting", + "UndertakerInfo": "Teleport dead body to a marked location", + "RiftMakerInfo": "Two rifts I trace, touch 'em to warp space", + "EvilTrackerInfo": "Track players by shifting", + "AntiAdminerInfo": "Know when players are near devices", + "ArroganceInfo": "With each kill you make, your cooldown decreases", + "BomberInfo": "Shapeshift to explode", + "TrapsterInfo": "Trap your kills", + "ScavengerInfo": "Your kills are unreportable", + "EvilGuesserInfo": "Guess crew roles in meetings to kill", + "GangsterInfo": "Convert players to your side", + "CleanerInfo": "Report bodies to make them unreportable", + "LightningInfo": "Convert players to Quantum Ghosts", + "GreedyInfo": "Your kill cooldown shifts", + "CursedWolfInfo": "You survive a few kill attempts", + "SoulCatcherInfo": "You swap places with your shift target", + "QuickShooterInfo": "Store ammo to offset kill cooldown", + "CamouflagerInfo": "Camouflage everyone for easy kills", + "EraserInfo": "Erase the role of your vote target", + "ButcherInfo": "Enjoy my beautiful work", + "HangmanInfo": "I will decide when your life will end", + "SwooperInfo": "Turn invisible temporarily", + "CrewpostorInfo": "Kill by completing tasks", + "WildlingInfo": "Kill with strength and disguise", + "TricksterInfo": "Kill and trick the crew", + "VindicatorInfo": "Use your extra votes to kill everyone", + "ParasiteInfo": "Help the Impostors kill the crew", + "DisperserInfo": "Teleport everyone to random vents", + "InhibitorInfo": "You cannot kill during sabotages", + "SaboteurInfo": "You can only kill during sabotages", + "CouncillorInfo": "Kill off crewmates during meetings", + "DazzlerInfo": "Reduce the vision of the crew", + "DeathpactInfo": "Assign players to a death pact", + "DevourerInfo": "Consume the skin of the crew", + "ConsigliereInfo": "Discover the roles of other players", + "MorphlingInfo": "You can only kill while shapeshifted", + "TwisterInfo": "Swap all player positions", + "LurkerInfo": "Reduce your kill cooldown by venting", + "ConvictInfo": "Your target died, now help the Impostors", + "VisionaryInfo": "You see the alignments of the living", + "RefugeeInfo": "Help the Impostors kill off the crew", + "UnderdogInfo": "Start killing on a low player count", + "LudopathInfo": "Your kill cooldown is random", + "GodfatherInfo": "Convert players to Refugees by voting", + "ChronomancerInfo": "Kill in bursts", + "PitfallInfo": "Setup traps around the map", + "BerserkerInfo": "Kill to increase your level", + "WarInfo": "Destroy everything", + "EvilMiniInfo": "No one can hurt you until you grow up", + "BlackmailerInfo": "Silence other players", + "InstigatorInfo": "Sow discord among the crewmates", + "LazyGuyInfo": "You're too lazy", + "SuperStarInfo": "Everyone knows you", + "CleanserInfo": "Erase All Addons of your vote target", + "KeeperInfo": "Reject the Eject, Keeper Protect!", + "MayorInfo": "Your vote counts multiple times", + "PsychicInfo": "One of the red names are evil", + "MechanicInfo": "Vent around and fix sabotages", + "SheriffInfo": "Shoot the Impostors", + "VigilanteInfo": "Not the hero we deserved but the hero we needed", + "JailerInfo": "Jail suspicious players", + "CopyCatInfo": "Use kill button to copy target's role", + "SnitchInfo": "Finish your tasks to find the Impostors", + "MarshallInfo": "Finish your tasks to prove your innocence", + "SpeedBoosterInfo": "Boost your speed", + "DoctorInfo": "Know how each player died", + "DictatorInfo": "Exile a player based on your own judgement", + "DetectiveInfo": "Gain extra info from your body reports", + "UndercoverInfo": "Impostors see you as their partner", + "KnightInfo": "You can kill 1 player", + "NiceGuesserInfo": "Guess Impostor roles in meetings to kill", + "GuessMasterInfo": "Whispers heard, every guessed word.", + "TransporterInfo": "Do tasks to swap 2 players' locations", + "TimeManagerInfo": "Increase meeting time by doing tasks", + "VeteranInfo": "Alert to kill anyone who interacts with you", + "BastionInfo": "Bomb vents", + "BodyguardInfo": "Prevent nearby kills", + "DeceiverInfo": "Try to fool the players", + "GrenadierInfo": "Reduce Impostors' vision by venting", + "MedicInfo": "Cast a shield onto a player", + "FortuneTellerInfo": "Get clues to people's roles", + "JudgeInfo": "Silence in the courtroom!", + "MorticianInfo": "Locate dead bodies", + "MediumInfo": "Talk with ghosts", + "ObserverInfo": "You can see all shield-animations", + "PacifistInfo": "Vent to reset kill cooldowns", + "MonarchInfo": "Give your crew extra voting power!", + "StealthInfo": "Killing Blinds Everyone in the Room", + "PenguinInfo": "Drag your victims", + "OverseerInfo": "Reveal roles of other players", + "CoronerInfo": "Find corpses and their killers", + "TrackerInfo": "Keep track of other players", + "PresidentInfo": "You are in charge of the meeting", + "MerchantInfo": "Sell add-ons and bribe killers", + "RetributionistInfo": "Help the crew after you die", + "HawkInfo": "Seek murdering the bad guys!", + "DeputyInfo": "Handcuff killers to increase their cooldowns", + "InvestigatorInfo": "Find potential evils", + "GuardianInfo": "Complete your tasks to become immortal", + "AddictInfo": "Vent to become invulnerable, or you'll die", + "MoleInfo": "Vanish and reappear, the Mole's game is crystal clear!", + "AlchemistInfo": "Brew potions by completing tasks", + "TracefinderInfo": "Sense the location of dead bodies", + "OracleInfo": "Vote a player to see their alignment", + "SpiritualistInfo": "Be guided by the ghostly life", + "ChameleonInfo": "Vent to disguise into your surroundings", + "InspectorInfo": "Validate the alignments of two players", + "CaptainInfo": "Sail with the Captain, lest addons be abandoned.", + "AdmirerInfo": "Choose a player to side with you", + "TimeMasterInfo": "Rewind time!", + "CrusaderInfo": "Kill a player's attacker", + "ReverieInfo": "With each kill, your cooldown decreases", + "LookoutInfo": "See through disguises", + "TelecommunicationInfo": "Track device usage", + "LighterInfo": "Catch killers with your enhanced vision", + "TaskManagerInfo": "See the total tasks completed in real time", + "WitnessInfo": "Find out if someone killed recently", + "SwapperInfo": "Swap the votes of two players", + "ChiefOfPoliceInfo": "Recruit as a Sheriff by killing players with knives", + "NiceMiniInfo": "No one can hurt you until you grow up.", + "ArsonistInfo": "Douse everyone and ignite", + "PyromaniacInfo": "Douse and kill everyone", + "HuntsmanInfo": "Kill your targets for a low cooldown", + "SpyInfo": "You know who interacts with you", + "RandomizerInfo": "You're going to be someone's burden when you die?", + "EnigmaInfo": "Get Clues about Killers", + "JesterInfo": "Get voted out", + "OpportunistInfo": "Stay alive until the end", + "TerroristInfo": "Finish your tasks, THEN die", + "ExecutionerInfo": "Get your target voted out", + "LawyerInfo": "Help your target win!", + "VectorInfo": "Jump in! Jump out!", + "JackalInfo": "Murder everyone", + "GodInfo": "Everything is under your control", + "InnocentInfo": "Get someone ejected by making them kill you", + "PelicanInfo": "Eat all players", + "RevolutionistInfo": "Recruit players to win with you", + "HaterInfo": "Kill Lovers and Neptunes", + "DemonInfo": "Consume blood volumes", + "StalkerInfo": "Descend into the darkness, release fear!", + "WorkaholicInfo": "Finish all tasks to solo win!", + "SolsticerInfo": "Speed run all your tasks!", + "CollectorInfo": "Collect votes from players", + "ProvocateurInfo": "Victory with help target", + "BloodKnightInfo": "Killing gives you a temporary shield", + "PlagueBearerInfo": "Plague everyone to turn into Pestilence", + "PestilenceInfo": "Obliterate everyone!", + "GlitchInfo": "Hack and kill everyone", + "SidekickInfo": "Help the Jackal kill everyone", + "FollowerInfo": "Follow a player and help them", + "CultistInfo": "Charm everyone", + "SerialKillerInfo": "Kill off everyone to win!", + "JuggernautInfo": "With each kill, your cooldown decreases", + "InfectiousInfo": "Infect everyone", + "VirusInfo": "Kill and infect everyone", + "PursuerInfo": "Protect yourself and live to the end!", + "PlagueDoctorInfo": "Spread the infection!", + "PhantomInfo": "Get killed and finish your tasks to win!", + "PirateInfo": "Successfully plunder players to win", + "AgitaterInfo": "Pass a Bomb onto others", + "MaverickInfo": "Kill and survive to the end", + "CursedSoulInfo": "Snatch souls and steal the win", + "PickpocketInfo": "Steal votes from your kills", + "TraitorInfo": "Eliminate the Impostors, then win", + "VultureInfo": "Eat bodies by reporting to win", + "TaskinatorInfo": "Silent tasks, deadly blasts", + "BenefactorInfo": "Task complete, shield elite!", + "MedusaInfo": "Stone bodies by reporting them", + "BakerInfo": "Feed Players Bread in order to become Famine", + "FamineInfo": "Starve Everyone", + "SpiritcallerInfo": "Turn Players into Evil Spirits", + "AmnesiacInfo": "Remember the role of a dead body", + "ImitatorInfo": "Imitate a player's role", + "BanditInfo": "Rob a player's add-on", + "DoppelgangerInfo": "Steal your target's identity", + "MasochistInfo": "Get attacked a few times to win!", + "KamikazeInfo": "Kill players with a suicidal mission", + "DoomsayerInfo": "Successfully guess players to win", + "ShroudInfo": "Shroud players to make them kill", + "WerewolfInfo": "Kill crewmates in groups", + "ShamanInfo": "Deflect all the attacks on Voodoo doll", + "SeekerInfo": "Play Hide and Seek with your target", + "PixieInfo": "Tag 'em, Bag 'em, and Eject 'em!", + "OccultistInfo": "Kill and curse your enemies", + "SoulCollectorInfo": "Predict deaths to collect souls", + "DeathInfo": "Enact Armageddon", + "SchrodingersCatInfo": "The cat is both alive and dead until observed.", + "RomanticInfo": "Protect your partner to win together", + "VengefulRomanticInfo": "Revenge your partner to win together", + "RuthlessRomanticInfo": "Kill everyone to win with your partner", + "PoisonerInfo": "Kill everyone with delayed kills", + "HexMasterInfo": "Hex players to kill them in meetings", + "WraithInfo": "Vent to temporarily go invisible", + "JinxInfo": "Reflect attacks onto your attackers", + "PotionMasterInfo": "Use your potions to your advantage", + "NecromancerInfo": "Kill your killer to defy death", + "WardenInfo": "(Ghost) Alert about danger", + "MinionInfo": "(Ghost) Blind enemies", + "LoversInfo": "Stay alive and win together", + "MadmateInfo": "Help the Impostors", + "NtrInfo": "Everyone sees you as their Lover", + "WatcherInfo": "You see all the colors of votes", + "LastImpostorInfo": "Lower kill cooldown", + "OverclockedInfo": "Lower cooldown", + "FlashInfo": "You're faster", + "TorchInfo": "You have enhanced vision!", + "SeerInfo": "You are alerted when somebody is killed", + "TiebreakerInfo": "Break tied votes", + "ObliviousInfo": "You can't report bodies", + "BewilderInfo": "A twist of vision, a web of confusion", + "WorkhorseInfo": "Be the first to complete all tasks and get more", + "FoolInfo": "You can't fix sabotages", + "AvangerInfo": "You take someone with you upon death", + "YoutuberInfo": "Get killed first to win", + "EgoistInfo": "Win on your own", + "TicketsStealerInfo": "Gain votes with kills", + "SchizophrenicInfo": "You're dead and alive simultaneously", + "MimicInfo": "Reveal killed players' roles to impostors upon death", + "GuesserInfo": "Guess roles of players in meetings to kill", + "NecroviewInfo": "See the team of the dead", + "ReachInfo": "You have a longer kill range", + "BaitInfo": "Your killer self-reports your body", + "TrapperInfo": "Freeze your killer for a few seconds", + "OnboundInfo": "You can't be guessed", + "ReboundInfo": "Guess me right, and face your plight!", + "MundaneInfo": "Tasks all done, guessing's begun.", + "UnreportableInfo": "Your body can't be reported", + "LuckyInfo": "Dodge attackers", + "DoubleShotInfo": "You have an extra life when guessing", + "RascalInfo": "You appear evil in some cases", + "SoullessInfo": "You have no soul", + "GravestoneInfo": "Your role is revealed when you die", + "LazyInfo": "You're too lazy", + "AutopsyInfo": "You see how others died", + "LoyalInfo": "You cannot be recruited", + "EvilSpiritInfo": "You are an evil Spirit", + "RecruitInfo": "Help the Jackal", + "AdmiredInfo": "The Admirer chose you as their love", + "GlowInfo": "You glow in the dark", + "DiseasedInfo": "Increase the cooldown of player who interacts with you", + "AntidoteInfo": "Decrease the cooldown of player who interacts with you", + "StubbornInfo": "Protect your role and addons", + "SwiftInfo": "Your kills don't cause a lunge", + "UnluckyInfo": "Doing things has a chance to kill you", + "VoidBallotInfo": "Your vote count is 0", + "AwareInfo": "Know who revealed your role", + "FragileInfo": "Die instantly if someone uses kill button on you", + "GhoulInfo": "Kill your killer after dying", + "BloodlustInfo": "Unleash your bloodlust and kill", + "SunglassesInfo": "You have reduced vision!", + "MareInfo": "Kill in the darkness", + "BurstInfo": "Make your killer burst!", + "SleuthInfo": "Gain info from dead bodies", + "ClumsyInfo": "You have a chance to miss your kill", + "NimbleInfo": "You can vent!", + "CircumventInfo": "You can no longer vent", + "OiiaiInfo": "OIIAIOIIIAI", + "CyberInfo": "You're popular!", + "HurriedInfo": "God I got too much stuffs!", + "InfluencedInfo": "You lack decisiveness!", + "SilentInfo": "Vote like a Ghost!", + "SusceptibleInfo": "Deathreason lotto!", + "TrickyInfo": "Tricky slays, in mysterious ways.", + "TiredInfo": "Labor makes you rest Zzz..", + "StatueInfo": "You're still as a rock nearby people", + "GMInfo": "Spectate the chaos!", + "NotAssignedInfo": "No assigned role", + "SunnyboyInfo": "Shine, shine my sunshine!", + "BardInfo": "Poem's grace, murder's trace, a rhythmic dance in dark embrace.", + "NukerInfo": "Shapeshift to nuke everyone", + "RainbowInfo": "Colorful melodies! You don't even know your own color.", + "EngineerTOHEInfoLong": "(Crewmates):\nAs the Engineer, you may access the vents while Comms Sabotaged is inactive.", + "ScientistTOHEInfoLong": "(Crewmates):\nAs the Scientist, you can see vitals at any time which shows you who is alive and who is dead.", + "ShapeshifterTOHEInfoLong": "(Impostors):\nAs the Shapeshifter, you can shapeshift into other players. It is obvious when you shapeshift or revert shifting.", + "GuardianAngelTOHEInfoLong": "(Crewmates):\nAs the Guardian Angel, you are the first crewmate to die and can give Crewmates temporary shields.", + "ImpostorTOHEInfoLong": "(Impostors):\nAs the Impostor, your goal is to simply kill off the crewmates.\nYou can sabotage and vent.", + "CrewmateTOHEInfoLong": "(Crewmates):\nAs the Crewmate, your goal is to find and exile the Impostors.\nCrewmates win by getting rid of all killers or by finishing all their tasks.", + "BountyHunterInfoLong": "(Impostors):\nAs the Bounty Hunter, if you kill your assigned Target (indicated by the arrow, if you have one), your next kill cooldown will be shortened.\nIf you kill anyone other than your target, your next kill cooldown will be increased.The Target swaps after a certain amount of time.", + "FireworkerInfoLong": "(Impostors):\nAs the Fireworker, you can Shapeshift to place Fireworker, up to the max amount set by host.\nWhen you are the last Impostor and all Fireworker have been placed, shapeshift again to detonate them and kill everyone in their radius, including you.\nIf you kill all players with your Fireworker, it's considered an Impostor victory.", + "MercenaryInfoLong": "(Impostors):\nAs the Mercenary, you must kill within your Deadline shown by your Shapeshift cooldown (which you cannot use). If you fail to kill, you die.", + "ShapeMasterInfoLong": "(Impostors):\nAs the Shapemaster, you have no Shapeshift cooldown.", + "VampireInfoLong": "(Impostors):\nAs the Vampire, your kills are delayed. If a meeting is called first, your target still dies. If you bite a Bait, you kill normally and report the body.", + "VampiressInfoLong": "(Impostors):\nAs the Vampiress, you can Bite players like a Vampire (single click) or kill normally (double click).", + "WarlockInfoLong": "(Impostors):\nAs the Warlock, you can Curse up to one other player at a time.\nWhen you Shapeshift, if you have Cursed a player, they kill the nearest person, which, depending on settings, can include you or other Impostors.\nYou can kill normally while Shapeshifted.", + "ZombieInfoLong": "(Impostors):\nZombie has a short kill cooldown, but moves very slowly and has very little vision. Zombie will not be voted out by anyone other than the Dictator, and the movement speed of Zombie will gradually slow down as they make kills or time passes.", + "NinjaInfoLong": "(Impostors):\nAs the Ninja, you can use your kill button to Mark target (single click) or kill normally (double click). You may then Shapeshift to teleport to the Marked target and kill them.", + "AnonymousInfoLong": "(Impostors):\nAs the Anonymous, you can Shapeshift to force your target to report whoever you killed this round.\nIf you killed nobody that round, the target will report their own dead body as if they had died.\nNote: This does not work on Lazy nor Lazy Guy, and this ability will work regardless of whether the body can normally be reported.", + "MinerInfoLong": "(Impostors):\nAs the Miner, you can shapeshift to teleport back to the last vent you were in.", + "KillingMachineInfoLong": "(Impostors):\nAs the Killing Machine, you have a very short kill cooldown, but cannot vent, have Crewmate vision, cannot sabotage, cannot report, and cannot call emergency meetings.\n\nNote: You will bypass any and all shield, killing bait and beartrap won't take any effect", + "EscapistInfoLong": "(Impostors):\nAs the Escapist, you can Mark a location by Shapeshifting. Shapeshift again to teleport back to the Marked spot (the Shapeshifting animation will display after you teleport, be careful).", + "WitchInfoLong": "(Impostors):\nAs the Witch, you can use your kill button to Spell (single click) or kill normally (double click).\nDuring the next meeting, the spelled target(s) will have a 「†」 next to their name visible to everyone. Unless you die by the end of that meeting, all Spelled targets will die.", + "NemesisInfoLong": "(Impostors):\nAs the Nemesis, you can only kill if you are the last Impostor.\nIf you are dead, you can use the command /rv [ID] to kill the player whose ID is typed. Use /id to show the IDs of all players, or look next to their names.", + "BloodmoonInfoLong": "(Impostors [Ghost]):\nAs the Bloodmoon attack the enemies to make them drip blood, this means they will die in a time set by host, and will be aware of it.", + "PuppeteerInfoLong": "(Impostors):\nAs the Puppeteer, you can use your kill button to Puppeteer (single click) or kill normally (double click).\nThose you Puppeteer will kill the next non-Impostor they touch. Depending on options, Puppeteered targets will also die once they kill.", + "MastermindInfoLong": "(Impostors):\nAs the Mastermind, you can use your kill button on a player once to manipulate them. This does nothing if the target doesn't have a kill button. But if the target has a kill button of any time, they'll be told after a delay that they were manipulated and they must kill someone in a limited time to survive. If the time limit expires or a meeting gets called before killing someone, they die.\nDouble click on someone to kill them normally.", + "TimeThiefInfoLong": "(Impostors):\nEvery time the Time Thief kills a player, the meeting time will be reduced by a certain amount of time. If the Time Thief dies, the meeting time will return to normal.", + "SniperInfoLong": "(Impostors):\nYou can shoot players from far away.\nYou have to shapeshift twice to make a successful snipe.\nImagine an arrow pointing from your first shapeshift location towards your unshift location.\nThat will be the direction in which the snipe will be made.\nThe snipe kills the first person in its path.\nYou cannot kill normally until you use up all of your ammo.", + "UndertakerInfoLong": "(Impostors):\nEverytime you Shapeshift into a player you mark the location. Your kills will then teleport to the marked location.\nAfter every kill and meeting your marked location will reset.\n\nAfter every teleported kill you will freeze for a configurable amount of time", + "RiftMakerInfoLong": "(Impostors):\nAs Rift Maker you can shapeshift to create a rift. You can teleport from one rift to another by touching the area where the rift was created. Trying to vent will kick you out and all the rifts will be destroyed.\n\nNote: Up to two rifts can be placed at a time, if you try to place a third, it removes the first one.", + "EvilTrackerInfoLong": "(Impostors):\nThe Evil Tracker can track other people, and the Evil Tracker can shapeshift into someone to switch the tracking target to the shapeshift target (You will immediately unshift after performing shapeshift). The arrow below the Evil Tracker's name indicates the direction of the target. When the Evil Tracker's teammate kills, the Evil Tracker will see a kill flash.", + "EvilGuesserInfoLong": "(Impostors):\nThe Evil Guesser can guess the role of a certain player during the meeting. If it is correct, the target dies, and if it is wrong, the Evil Guesser dies.\nThe guessing command is: /bt [player id] [role]\nYou can see the player's id before the player's name, or use the /id command to view the id of all players.", + "AntiAdminerInfoLong": "(Impostors):\nThe Anti Adminer can at any time find out if there are crewmates or neutrals near Cameras, Admin Table, Vitals, DoorLog and/or other devices. Note: Anti Adminer does not know for sure if the player is using the device while near it, they only know that someone is near the device.", + "ArroganceInfoLong": "(Impostors):\nThe Arrogance reduces their kill cooldown with each successful kill of theirs.", + "BomberInfoLong": "(Impostors):\nThe Bomber can use the shapeshift button to self-explode, killing players within a certain range. But as a price, the Bomber will also die. Note: All players will see a kill-flash when the Bomber explodes.", + "ScavengerInfoLong": "(Impostors):\nScavenger kills do not leave dead bodies behind. In addition, if the victim is a bait, no self-report will be made.", + "TrapsterInfoLong": "(Impostors):\nAs the Trapster, your main method of killing is by body reports.\nWhen someone tries to report a body you killed, they'll die.", + "GangsterInfoLong": "(Impostors):\nThe Gangster can attempt to recruit a player to a Madmate by pressing the kill button. If the recruitment is successful, both the Gangster and the target will see the shield animation on each other as a reminder (only visible to each other). The remaining number of available recruits is displayed next to the Gangster's name (the max is set by the host). If the Gangster tries to recruit players who cannot be recruited, such as neutrals or some special crews, they will kill the target normally instead. When the Gangster has no remaining recruitments, they can only make normal kills from that point on.", + "CleanerInfoLong": "(Impostors):\nCleaner can press the Report button to clean up any dead body they come across (including those they kill). If the cleanup is successful, the Cleaner will see a shield animation on their body as a reminder (only visible to himself). The cleaned up body cannot be reported (including bait's).", + "LightningInfoLong": "(Impostors):\nAs the Lightning, you cannot kill normally. Instead, your kill button quantizes targets, which activates after a delay, causing the next person they come into contact with to kill them. Those who are actively quantized show a「■」next to their name. Additionally, those who have been quantized die if they survive until the end of a meeting. There is a setting to quantize your killer.", + "GreedyInfoLong": "(Impostors):\nGreedy kills with odd and even kills will have different kill cooldowns. Greedy's kill cooldown is reset every meeting, and Greedy's first kill is always an odd kill.", + "CursedWolfInfoLong": "(Impostors):\nWhen the Cursed Wolf is about to be killed, the Cursed Wolf will curse the killer to death. (The max of times you can counterattack is set by the host)", + "SoulCatcherInfoLong": "(Impostors):\nAs the Soul Catcher, you can shapeshift to swap places with your target as long as they are not dead, in a vent, swallowed by pelican, or in a similar odd state.", + "QuickShooterInfoLong": "(Impostors):\nWhen the kill cooldown is over, Quick Shooter can reset the kill cooldown by shapeshift to store a bullet (when the storage is successful, a shield-animation visible only to himself will appear on their body as a reminder). If Quick Shooter has bullets he can use one to bypass kill cooldown, he will kill even if it's still on cooldown, and use a bullet. At the beginning of each meeting, the quick shooter can only keep a certain number of bullets (Number is set by the host).", + "CamouflagerInfoLong": "(Impostors):\nWhen Camouflager uses Shapeshift, all players start to look exactly the same. This state ends when Camouflager reverts its shape-shifting. Note: the skills of communication sabotage camouflage and skills of Camouflager can be superimposed.\nSkill will be invalid if a meeting is held during the skill activation of the Camouflager", + "EraserInfoLong": "(Impostors):\nEraser can vote for any crew target at the meeting to erase the target's roles, and the erasure will take effect after the meeting ends. Note: Players whose skills are erased will always be considered a vanilla role, including the game result page.\nA player can only be erased once(include Oiiai)", + "ButcherInfoLong": "(Impostors):\nButcher kills (including passive kills) have multiple dead bodies on targets, making it impossible to accurately identify other dead body when reporting. Note: Due to the principle of implementation, the killed target has to repeatedly display the animation of being killed. This animation cannot be skipped and cannot participate in the meeting normally during this period. In addition, if the Butcher kills the Avenger, the Avenger will revenge everyone in anger.", + "HangmanInfoLong": "(Impostors):\nThe killing method of the Hangman during the shapeshifting is strangling. Strangling ignores any status of the target, such as the shield of the Medic, the protection of the Bodyguard, the skills of the Super Star, etc. The strangled player will not leave a dead body, nor will it trigger any of its skills. For example, Veteran kill back (including additional roles), in addition, Seer will not be prompted.", + "SwooperInfoLong": "(Impostors):\nAs the Swooper, you can vent to temporarily Vanish. You will still appear visible on your screen. Vent again to become visible.", + "CrewpostorInfoLong": "(Team Impostor):\nYou kill the nearest player whenever you complete a task.", + "WildlingInfoLong": "(Impostors):\nAs the Wildling, you can shapeshift but lack the ability to vent.\nWhen you kill, you temporarily become immune to attacks.", + "TricksterInfoLong": "(Impostors):\nAs the Trickster, you function as a regular Impostor but with one key difference.\nYou appear crewmate to crewmate roles.\n\nThe Sheriff cannot kill you.\nPsychic does not see you as evil.\nSnitch cannot find you.", + "VindicatorInfoLong": "(Impostors):\nAs the Vindicator, you have extra votes like a Mayor.", + "StealthInfoLong": "(Impostors):\nWhen the Stealth kills, players in the same room are blinded for a short time.", + "PenguinInfoLong": "(Impostors):\nAs the Penguin, you can restrain target by pressing the kill button, and drag around.\nWhile dragging, the target dies by pressing the kill button again or after a certain period of time.\nPress the kill button twice for a direct kill.", + "ParasiteInfoLong": "(Team Impostor):\nAs the Parasite, you are an Impostor that does not know the other Impostors.\n\nYou may kill, vent, sabotage, whatever.\nJust know that you are an Impostor.", + "DisperserInfoLong": "(Impostors):\nDisperser can use Shapeshift to teleport all players to random vents.\nNote: the Disperser itself will not be teleported with shapeshift and players who are in the vent cannot be teleported.", + "InhibitorInfoLong": "(Impostors):\nAs the Inhibitor, you can only kill when there is not a critical sabotage active.\n\nIf a critical sabotage is active (eg Lights or Reactor), you cannot kill.", + "SaboteurInfoLong": "(Impostors):\nAs the Saboteur, you can only kill when there is a critical sabotage active.\n\nIf a critical sabotage is active (eg Comms or O2), then you can kill.", + "CouncillorInfoLong": "(Impostors):\nAs the Councillor, you can kill players during a meeting like a Judge.\nWhen killing in a meeting, those kills will appear as a trial from a Judge.\n\nThe kill command is /tl [player id]\nYou can see the player's id before the player's name, or use the /id command to view the id of all players.", + "DazzlerInfoLong": "(Impostors):\nAs the Dazzler, you can reduce the vision of the target of your Shapeshift permanently. When you die, their vision will turn back to normal.", + "DeathpactInfoLong": "(Impostors):\nAs the Deathpact, the targets of your shapeshifting are marked for a deathpact.\nIf enough players are marked for a death pact, the marked players must meet within a defined period of time; if they fail to do so, they die.\nIf a marked player dies before the death pact is completed, the pact is withdrawn.", + "DevourerInfoLong": "(Impostors):\nAs the Devourer, you use your shapeshift to permanently change the appearance of the target of the shapeshift. Additionally, for each player's appearance changed, your kill cooldown is reduced by a defined number of seconds. If the Devourer dies or gets voted out during a meeting, the player's appearance will change back to their normal appearance.", + "MorphlingInfoLong": "(Impostors):\nAs the Morphling, you are a Shapeshifter but cannot kill while not shapeshift.", + "TwisterInfoLong": "(Impostors):\nAs the Twister, you can use shape-shifting to randomly swap the position of all players. The swap happens twice, once when you start your shape shift and once when you return to your original appearance.\nThe Twister itself will not swap places with anyone and players who are in vents cannot be teleported.", + "LurkerInfoLong": "(Impostors):\nAs the Lurker, you can jump into a vent to reduce your cooldown by a certain number of seconds. After you kill, your cooldown is reset to its original value.", + "VisionaryInfoLong": "(Impostors):\nAs the Visionary, you see the alignments of living players during a meeting.\nThe following info will be displayed on the player.:\n- The Red name indicates the Impostors.\n- The Cyan name indicates the Crewmates.\n- The Gray name indicates the Neutrals.", + "PlagueDoctorInfoLong": "(Neutrals):\n(Plague Doctor from TOH)\nThe Plague Scientist's goal is to infect every living player.\nThey start by choosing one player to infect, after which anyone who spends a set amount of time in range of the infected player becomes infected themselves.\nInfection progress is cumulative, and does not reset with distance or after meetings.", + "RefugeeInfoLong": "(Madmates):\nAs the Refugee, you were either an Amnesiac who remembered an Impostor, or a killer who killed the Godfather's target.\n\nNow your job is to help the Impostors kill the crewmates.", + "UnderdogInfoLong": "(Impostors):\nAs the Underdog, you cannot kill until there's a certain amount of players alive.", + "ConsigliereInfoLong": "(Impostors):\nAs the Consigliere, you can reveal the roles of other players using your kill button.\n\nSingle click: Reveal role\nDouble click: Kill\n\nIf you run out of reveal uses, your kill button functions normally.", + "LudopathInfoLong": "(Impostors):\nAs the Ludopath, your kill cooldown is randomized.\n\nMinimum it can be is 1 second, while the maximum is your default kill cooldown.", + "GodfatherInfoLong": "(Impostors):\nAs the Godfather, you vote someone to make them your target.\nIn the next round if someone kills the target, the killer will turn into a Refugee.", + "ChronomancerInfoLong": "(Impostors):\nAs the Chronomancer, you can charge up your kill button. Once activated the Chronomancer can use their kill button infinitely until they run out of charge.", + "PitfallInfoLong": "(Impostors):\nAs the Pitfall, you use your shapeshift to mark the area around the shapeshift as a trap. Players who enter this area will be immobilized for a short period of time and their vision will be affected.", + "BerserkerInfoLong": "(Apocalypse):\nAs the Berserker, you level up with each kill.\nUpon reaching a certain level defined by the host, you unlock a new power.\n\nScavenged kills make your kills disappear.\nBombed kills make your kills explode. Be careful when killing, as this can kill your other Apocalypse members if they are near. \nAfter a certain level you become War.", + "WarInfoLong": "(Apocalypse):\nAs War, you are invincible, have a lower kill cooldown, and can kill anyone with your previous powers.\nYour presence is announced to everyone the meeting after you transform.", + "EvilMiniInfoLong": "(Impostors):\nAs an Evil Mini, you are unkillable until you grow up and have a very long initial kill cooldown, which is drastically shortened as you grow up.", + "BlackmailerInfoLong": "(Impostors):\nAs the Blackmailer, when you shift into a target you will blackmail that player, and the blackmailed player cannot speak.\n\nSpeaking by the blackmailed player will trigger the confusion command, please do not speak when the blackmailed player sees his icon", + "InstigatorInfoLong": "(Impostors):\nAs the Instigator, it's your job to turn the crewmates against each other. Each time a Crewmate is voted out in a meeting, as long as you are alive, an additional Crewmate who voted for the innocent player will die after the meeting. The number of additional players dying is determined by the host.", + "LazyGuyInfoLong": "(Crewmates):\nLazy Guy has only one task In addition, Impostor's abilities can't affect the Lazy Guy, such as being a scapegoat for the Anonymous, marked by a Warlock or Puppeteer, and more. Lazy Guy will not have any add-ons.", + "SuperStarInfoLong": "(Crewmates):\nThere will be a star logo next to the Super Star's name, so everyone knows who the Super Star is. The Super Star can only be killed when the Murderer is alone with the Super Star (regular kills only). In addition, the Super Star cannot be guessed by Guessers.", + "CelebrityInfoLong": "(Crewmates):\nAll Crewmates see the kill-flash when the Celebrity dies (same as the Seer sees the kill-flash) and get a notice at the next meeting. The Impostors don't know anything about this.", + "CleanserInfoLong": "(Crewmates):\nCleanser can vote for any target at the meeting to erase the target's Add-ons, and the erasure will take effect after the meeting ends. Depending on the settings cleansed player may never get add on in future", + "KeeperInfoLong": "(Crewmates):\nAs keeper you can vote someone to protect them from being ejected. You can only do this a configurable amount of times.", + "MayorInfoLong": "(Crewmates):\nAs the Mayor, you have extra votes. As a setting, these votes can be hidden, you can vent to call a meeting at any time, and you are revealed as Mayor upon tasks completion.", + "PsychicInfoLong": "(Crewmates):\nThe Psychic can see the names of several players highlighted in red during the meeting, at least one of them is evil. The Psychic will correctly see all Neutrals and Killing Crewmates displayed as red names when becoming a Madmate.", + "MechanicInfoLong": "(Crewmates):\nThe Mechanic can use the vent at any time. They can also fix Reactors, O2, Communications by using only one side. Lights can be fixed by flicking only one switch. Opening a door will open all doors in the map.", + "SheriffInfoLong": "(Crewmates):\nSheriff has no task. The Sheriff can kill the Impostor (according to the host settings, the Sheriff can also kill neutrals). If the Sheriff tries to kill a crewmate, the Sheriff will kill himself. The Sheriff can kill anyone when he becomes a madmate (also according to the host settings).", + "VigilanteInfoLong": "(Crewmates):\nThe Vigilante is tasked with eliminating potential threats to the crew, but if they mistakenly kill an innocent crew member, they become a Madmate driven by guilt and remorse.\n\n Note: Gangster can not convert Vigilante into madmate.", + "JailerInfoLong": "(Crewmates):\nAs the Jailer, use your kill button to lock a player in jail. During the next meeting, the jailed player cannot vote or be voted (vote count will be 0). The Jailer may choose to execute the prisoner by voting them. If the Jailer executes an innocent player, the Jailer loses the ability to execute for the rest of the game.\nIf the Jailer is evil, then they can execute anyone.\nThe Jailer has limited executions.\n\nNote : Jailed players cannot be guessed or judged and jailed players can only guess Jailer.", + "SnitchInfoLong": "(Crewmates):\nAfter the Snitch completes all tasks, they can see Impostors names being displayed in red on meeting. When the Snitch has only one task left, the Impostors will see a 「★」 mark next to the name of themselves and the Snitch. When a Snitch becomes a Madmate, the 「★」 mark turns red.", + "MarshallInfoLong": "(Crewmates):\nAs the Marshall, complete your tasks to reveal yourself to the rest of the crew.\nOther teams will not be able to see you.\nHowever, madmates CAN see you.", + "SpeedBoosterInfoLong": "(Crewmates):\nSpeed Booster increase their movement speed every time they complete a task. Note: due to technical limitations, the Speed Booster appears to be at a normal speed to others, so they look like glitch.", + "DoctorInfoLong": "(Crewmates):\nDoctor can see the cause of death for all players. In addition, Doctor can access vitals wherever you are while he still have battery.", + "DictatorInfoLong": "(Crewmates):\nWhen the Dictator votes someone, the meeting will end on the spot and the player they voted will be ejected. The moment the Dictator vote someone out, Dictator will also die.", + "DetectiveInfoLong": "(Crewmate):\nAfter the Detective reports the body, they will receive a clue message, which will tell the Detective what the victim's role is. According to the host's settings, the Detective may know what the murderer's role is. Note: Detective won't be Oblivious.", + "UndercoverInfoLong": "(Crewmates):\nThe Impostors knows who Undercover is and sees him as a teammate, but Undercover himself does not know who the Impostors are.", + "NiceGuesserInfoLong": "(Crewmates):\nThe Nice Guesser can guess the role of a certain player during the meeting. If it is correct, it will kill the target, and if it is wrong, Nice Guesser will suicide.\nThe guessing command is: /bt [player id] [role]\nYou can see the player's id before the player's name, or use the /id command to view the id of all players.\nNice Guesser can guess crewmate when become madmate.", + "GuessMasterInfoLong": "(Crewmates):\nAs the Guess Master, you will receive information about every attempted guess made during a meeting. You will be informed about the role guesser tried to guess, and you will also be notified in case of a misguess.", + "KnightInfoLong": "(Crewmates):\nThe Knight has no tasks. They can kill any person but they can only do it once the whole game.", + "TransporterInfoLong": "(Crewmates):\nWhenever the Transporter completes the task, two random players will switch positions, but if there are not enough players left, nothing will happen. Note: Players in the vent will not be selected.", + "TimeManagerInfoLong": "(Crewmates):\nThe more tasks the Time Manager does, the longer the meeting time will be. When the Time Manager dies, the meeting time will return to normal. When the Time Manager becomes a Madmate, the skill changes to reducing the meeting time instead of increasing it.", + "VeteranInfoLong": "(Crewmates):\nVeteran can enter the alert state by venting. If a player tries to kill the veteran in the alert state, the veteran will kill the murderer instead. Veteran will see a shield-animation on their body and a text displayed above their head as a reminder when they enter and exit the alert state.", + "BastionInfoLong": "(Crewmates):\nAs the Bastion, bomb vents to kill off impostors and neutrals.\nBe careful though, crewmates can also be killed with the bombs.", + "CopyCatInfoLong": "(Crewmate):\nAs the Copycat, you can use your kill button to copy target's role.\n\nYou can only copy some crewmate roles.\nIf you try to copy a madmate or rascal, you become the madmate variation of the target role.\nIf you target an evil that has a crewmate variant, you'll become the crewmate variant.\n\nAdditionally, Your role will be set back to copycat after every meeting", + "BodyguardInfoLong": "(Crewmates):\nIf a player is about to be killed near the Bodyguard, the Bodyguard will prevent the kill and die with the murderer. The Bodyguard's skills will affect players of any team. When the Bodyguard becomes a Madmate and the murderer is an Impostor, the Bodyguard will not activate the skill.", + "DeceiverInfoLong": "(Crewmates):\nThe Deceiver can sell the counterfeit to other players through the kill button. If the counterfeit is sold successfully, the Deceiver will see a shield animation on their body as a reminder. The counterfeit will take effect after the end of the next meeting. If the player with no kill ability holds the counterfeit, he will kill himself immediately. If the player with the kill ability has the counterfeit, he will suicide when he tries to kill someone next time.", + "GrenadierInfoLong": "(Crewmates):\nAs the Grenadier, you can vent to Flashbang players nearby, causing them to lose vision if they are an Impostor or, depending on settings, a Neutral.", + "MedicInfoLong": "(Crewmates):\nThe Medic can place a shield on the target by pressing the Kill button. The Medic can only give one shield for the whole game, when the Medic dies, the target's shield will be removed. The Medic can also see if someone is trying to break the target's shield.\nDepending on the host's settings, the Medic or the target can see if the player has a shield (shown as a green circle 「●」 next to the name).", + "FortuneTellerInfoLong": "(Crewmates):\nAs the Fortune Teller, vote for a player in a meeting to get a clue to their role.\nThe clue will relate to their actual role.\n\nWhen the Fortune Teller's tasks are complete, they will obtain the exact role rather than a clue!\n\nNote:- If the setting to give random active players as hint is on, you will not be able to check same player multiple times", + "JudgeInfoLong": "(Crewmates):\nThe Judge can judge a certain player during the meeting. If the target is evil, the target will be killed (whether it is evil or not is set by the host), and if it is wrong, the judge commits suicide.\nThe judgment command is: /tl [player id]\nYou can see the player's id before the player's name, or use the /id command to view the id of all players.\nJudges can judge all players when they become Madmate.", + "MorticianInfoLong": "(Crewmates):\nThe Mortician can see arrows pointing to all dead bodies, and if the Mortician reports a body they will know the last player the victim had contact with. Note: Mortician won't be Oblivious or Seer.", + "MediumInfoLong": "(Crewmates):\nThe Medium can establish contact with a dead player after their dead body is reported. The player who reports doesn't have to be the Medium. The dead player can answer once with a YES or a NO to the Medium's question which only the Medium will see (the dead player can use /ms yes or /ms no). Note: Medium won't be Oblivious.", + "ObserverInfoLong": "(Crewmates):\nAs the Observer, you can see all shield animations caused by other players after the first meeting. This typically indicates the use of some role ability, so look out for this.", + "MonarchInfoLong": "(Crewmates):\nAs the Monarch, you can knight players to give them an extra vote.\n\nYou cannot knight someone who already has extra votes.\n\nKnighted players appear with a golden name.\nIf a knighted player is alive, the Monarch cannot be guessed or exhiled.", + "PacifistInfoLong": "(Crewmates):\nWhen the Pacifist vents, they will reset the kill cooldown for every player with a kill button. When they become a Madmate, this ability will only work on Crewmates.", + "OverseerInfoLong": "(Crewmates):\nAs a Overseer you have very limited vision but you can use your kill button to reveal the role of a nearby player. Use the kill button to start the reveal, a 「○」 will be displayed next to the reveal target. Stay near the target for a defined time to reveal his role, if you move too far away from the target the reveal will be aborted.", + "CoronerInfoLong": "(Crewmates):\nAs a Coroner you can't report corpses, instead after trying to report the corpse you will see an arrow leading you to the killer. If a meeting is called, the arrows disappear. Depending on the setting, the body you found cannot be reported.", + "TrackerInfoLong": "(Crewmates):\nAs a Tracker, you can vote for a player in the meeting, which will mark their position for you in the game with arrows. In addition, at the beginning of a meeting you will be shown in which room the player was last, if the option is activated.", + "PresidentInfoLong": "(Crewmates):\nThe President has 2 abilities: End the meeting and Reveal identity.\n\n+ Ability 1: End the meeting - Type /finish in meetings as President to instantly end the meeting.\n+ Ability 2: Reveal identity - Type /reveal in meetings to reveal yourself. Revealing yourself will make it so every player can see that you are the President and you will become unguessable after typing the command. However, after the President has revealed themselves, whoever killed the President will have their kill CD greatly reduced on their next kill.", + "MerchantInfoLong": "(Crewmates):\\As a merchant, you sell a random add-on to a random player for each task you complete. Each add-on sold earns you money. If you have a certain amount of money, you can avert the next killing attempt against you by bribing the murderer. The bribed player won't be able to kill you, but you don't know who it is. The bribe money used is lost and is not available for additional bribes.", + "RetributionistInfoLong": "(Crewmates):\nAs the Retributionist, you can kill a limited amount of players after your death.\n\nUse /ret [playerID] to kill.", + "HawkInfoLong": "(Crewmates [Ghost]):\nAs the Hawk you can kill a limited amount of players decided by host, tough there's a chance you miss, slicing someone multiple times increases the chances.", + "DeputyInfoLong": "(Crewmates):\nAs the Deputy, use your kill button on a player to reset their kill cooldown.\n\nIf the target does not have a kill button, then the handcuff was a waste.", + "InvestigatorInfoLong": "(Crewmates):\nAs an Investigator, you can use your kill button to investigate someone. When you investigate someone, their name will appear in either red if they possess a kill button (impostor/SS basis) or light blue if they lack a kill button (crewmate/engineer/scientist basis). However, please note that the color of the names will return to normal when a meeting is called.", + "GuardianInfoLong": "(Crewmates):\nAs the Guardian, you become immortal upon tasks completion. You can't even be guessed in meetings.", + "AddictInfoLong": "(Crewmates):\nAs the Addict, you have a suicide timer. When it expires you kill yourself.\nThe timer is indicated by the vent cooldown. When the vent cooldown is at 0 seconds, you still have a short time to vent.\nIf you don't make it you die, if you make it the suicide timer is reset.\nAlso, after you are ventilated, no one can interact with you for a defined period of time.\nAfter this period is over, you are immobilized for another defined period of time and cannot report any bodies.", + "MoleInfoLong": "(Crewmates):\nAs the Mole, when you vent, you stay in the vent for 1 second. When you come out of the vent, you will spawn near a random vent in the map (Except the one you just used).", + "AlchemistInfoLong": "(Crewmates):\nAs the Alchemist, you brew potions when you complete tasks. The potion you made will show up under your role name with its corresponding description and instructions. You can get seven different potions, some with harmful or no effects. Vent to use the potion.", + "TracefinderInfoLong": "(Crewmates):\nAs the Tracefinder, you can access vitals at any time.\nIn addition, you get arrows pointing to dead bodies, with a delay set by host.", + "OracleInfoLong": "(Crewmates):\nAs the Oracle, you may vote a player during a meeting.\nYou'll see if they are a Crewmate, Neutral, or Impostor.\nDepending on settings, there can be a chance that your result will be incorrect.", + "SpiritualistInfoLong": "(Crewmates):\nAs the Spiritualist, you get an arrow pointing towards the ghost of the last meeting's victim. There is an option for the arrow to disappear and reappear in intervals. Try to notify the ghost about your ability, if you can; if they are on your side, they may lead you to an evil role so you can eject them. Be careful, as evil roles can do the same for Crewmates.", + "ChameleonInfoLong": "(Crewmates):\nAs the Chameleon, you can vent to temporarily Vanish. You will still appear visible on your screen. Vent again to become visible.", + "InspectorInfoLong": "(Crewmates):\nCheck If two players are in the same team or not. You will get an affirmation message If they are in the same team, or a denial message if they are not in the same team.\n\nAll neutrals and converted playes are counted in the same team. Trickster is counted as crew and Rascal is counted as Impostor.\nChecking command : /cmp [player id 1] [player id 2]", + "CaptainInfoLong": "(Crewmates):\nWith each completed task, the Captain gains the power to slow down a random non crew role. Crewmates can see ☆ besides captain's name.\n\nIf anyone betrays the captain's trust by voting captain out, they will lose an addon.", + "AdmirerInfoLong": "(Crewmates):\nAs the Admirer, admire a player to make them crewmate aligned.\nThey'll win with crewmates and not their original team.\n\nYou can only do this once per player.", + "TimeMasterInfoLong": "(Crewmates):\nAs the Time Master, use the vents to mark everyone's position.\nWhen using the ability again, every alive player will be rewinded back to the marked positions.\n\nDuring the ability duration, the Time Master gains a time shield, which protects them from death.", + "CrusaderInfoLong": "(Crewmates):\nAs the Crusader, use your kill button to crusade a player.\nIf that player gets attacked, you'll kill the attacker.", + "ReverieInfoLong": "(Crewmates):\nAs the Reverie, you can kill but your cooldown starts high.\n\nIt increases if you kill a crewmate and reduces otherwise.\nDepending on the host's setting you may misfire on reaching the max kill cooldown and your target dies with you. \n\nYou win with other crewmates.", + "LookoutInfoLong": "(Crewmates):\nAs the Lookout, you can see the IDs of every player at all times.\nThis allows you to see through shapeshifts and camouflages.", + "TelecommunicationInfoLong": "(Crewmates):\nAs the Telecommunication, you are notified when anyone uses cameras, vitals, doorlogs, or admin.", + "LighterInfoLong": "(Crewmate):\nAs the Lighter, you can vent to increase your vision temporarily.\nYou have increased vision both when lights are not out and when lights are out.\nUse this power to catch sneaky killers!", + "TaskManagerInfoLong": "(Crewmates):\nYou see the total number of tasks completed (by everyone all together) next to your role name, which updates in real time.", + "WitnessInfoLong": "(Crewmates):\nAs the Witness, when you use your kill button on someone, you will know if they killed in the last X seconds or not. (X depends on the settings)", + "SwapperInfoLong": "(Crewmates):\nAs the Swapper, you can swap votes in meetings.\n\nTo swap votes, use '/sw [playerID]' twice.\n\nPlayer IDs are displayed next to player names in meetings, but you can also use /id to get a list of all player IDs.\n\nNote: You cannot swap yourself", + "ChiefOfPoliceInfoLong": "(Crewmates):\nPlayers with swords can be recruited to join the sheriff's team to serve the crew, but players without swords cannot be recruited.\n note: only one recruitment opportunity", + "NiceMiniInfoLong": "(Crewmates):\nAs a Nice Mini, you can't be killed until you grow up, and if you die or are evicted from the meeting before you grow up, everyone loses.", + "SpyInfoLong": "(Crewmates):\nAs the Spy, when someone uses their kill button on you (any ability that is used through the kill button), you'll see their name in orange for a few seconds.\nNote: If a Crewmate used their ability on you, you'll also see them with an orange name!\nNote: If you have no ability uses left, you won't see orange names at all!\nNote: If the kill button interaction is blocked the player's cooldown will reset to 10s'", + "RandomizerInfoLong": "(Crewmates):\nAs this Randomizer, when you die, your killer will do one of the following:\n 1. self-report your body\n 2. stand next to your body\n 3. have their kill cooldown set to 600s\n 4. Randomly avenge a player", + "ArsonistInfoLong": "(Neutrals):\nThe Arsonist can douse by clicking the kill button on the player and following them for a few seconds. When the dousing starts and it's successful, a shield animation will be displayed as a reminder (only visible to themselves). When the Arsonist has doused all surviving players, the Arsonist can vent to start the fire and win alone.\n\nIf the player name shows 「△」, that means they are being doused;\nif the player name shows 「▲」, it means they have been completely doused.\nDepending on the setting, Arsonist may start the fire anytime. But if he failed to kill everyone, he loses.", + "EnigmaInfoLong": "(Crewmates):\nAs the Enigma, you get a random clue about the killer each meeting, depending on the setting, you may have to report the body to receive a clue. The more tasks you complete the more precise the clues get.", + "PyromaniacInfoLong": "(Neutrals):\nAs the Pyromaniac, you can douse players (single click) or kill normally (double click). Dousing players does nothing immediately, but killing a doused player will significantly shorten your kill cooldown. To win, be the last player alive.", + "KamikazeInfoLong": "(Impostors):\nAs the Kamikaze you can single click to mark people. Double click to kill normally. When you die all marked also die, with death reason Targeted.", + "HuntsmanInfoLong": "(Neutrals):\nAs the Huntsman, you have a certain amount of targets that reset every meeting. If you kill one of your targets, your kill cooldown decreases by the set amount permanately. If you kill someone else other than any of your targets, your kill cooldown permanately increases by the set amount. You see your targets with a colored name.", + "MiniInfoLong": "(Crewmate or Impostor):\nThe Mini is two roles. Either a Nice Mini or an Evil Mini is chosen.\n\nUse '/r nicemini' and '/r evilmini' respectively for more details.", + "JesterInfoLong": "(Neutrals):\nIf the Jester get voted out, the Jester wins the game alone. If the Jester is still alive at the end of the game, the Jester loses the game. Note: Jester, Executioner, and Innocent can win together.", + "TerroristInfoLong": "(Neutrals):\nIf the Terrorist dies after completing all tasks, the Terrorist wins the game alone. (They can win by either being voted out or killed).", + "ExecutionerInfoLong": "(Neutrals):\nExecutioner has an execution target, which will be indicated by a diamond 「♦」 next to their name. If the execution target is killed, the Executioner will be changed to Crewmate, Jester or Opportunist according to the settings. If the execution target is voted out in the meeting, the Executioner wins. Note: Jester, Executioner, and Innocent can win together.", + "LawyerInfoLong": "(Neutrals):\nLawyer has a target to defend, which will be indicated by a diamond 「♦」 next to their name.\nIf your target wins, you win.\nIf they lose, you lose.", + "OpportunistInfoLong": "(Neutrals):\nIf the Opportunist survives at the end of the game, the Opportunist will win with the winning player.", + "VectorInfoLong": "(Neutrals):\nVector will win alone by venting a certain number of times.", + "JackalInfoLong": "(Neutrals):\nAs the Jackal, you win if you are the last player alive. Additionally, you may recruit using the kill button. If the target is not one you can recruit, you have run out of uses, or you don't have the option to recruit, then you will kill normally (i.e. don't use kill buttons in front of others thinking it'll recruit). If the target has a kill button and the option to turn into a Sidekick is on, then they will become a Sidekick. Otherwise, they will gain the Recruit add-on if the option to give the Recruit add-on is on.", + "GodInfoLong": "(Neutrals):\nAs the God, you know everyone's role from the beginning. If you live until the end of the game, you snatch the win, i.e. everyone else loses and you win.", + "InnocentInfoLong": "(Neutrals):\nThe Innocent can use the kill button to plant any player, and the planted target will immediately kill the Innocent. If the target is voted out in the meeting, the Innocent wins. Note: Jester, Executioner, and Innocent can win together.", + "PelicanInfoLong": "(Neutrals):\nAs the Pelican, you can use the kill button to swallow a player alive, teleporting them off-bounds but not killing them yet. Those who are swallowed will only die if you're still alive at the end of the round. If you die or leave during the round, all alive swallowed players will spawn into the map where you were.", + "RevolutionistInfoLong": "(Neutrals):\nAs the Revolutionist, you can recruit players by clicking the kill button on the player and following them until the shield animation plays for you. Recruiting has a chance, set by host, to kill players (though they are still recruited). When the required number of players are recruited, (displayed next to your name) you must vent within the specified time in order to win the game immediately with all of your recruits. If you do not vent in time, you lose and die.", + "HaterInfoLong": "(Neutrals):\nAs the Hater, you have no kill cooldown. However, you can only kill Lovers, and other recruiting roles and add-ons, depending on the settings. Killing anyone else will make you suicide. You win at the end of the game with the winning team if none of the killable roles are alive. You will not be Lovers.", + "DemonInfoLong": "(Neutrals):\nAs the Demon, you kill by draining health. You see health in percentage near everyone's name, and every attack you make drains a percentage from that health without the victim knowing. Once you drain your victim's health to 0, they die. You win if you are the last one standing.", + "StalkerInfoLong": "(Neutrals):\nThe Stalker can kill anyone, and every kill will immediately cause electricity sabotage (if electricity is already sabotaged, nothing will happen). Stalker cannot vent. If the Impostor wins while the Stalker is alive or the Crewmate wins by killing the Impostors (according to the host's setting, the Stalker may also win when the Crewmate wins by killing the Neutrals), then the Stalker win alone.", + "WorkaholicInfoLong": "(Neutrals):\nAs the Workaholic, you win alone when you complete all tasks. Depending on host's settings, you can only win if alive and/or you are revealed to everyone at the beginning (these settings are almost never both on).", + "SolsticerInfoLong": "(Neutrals):\nAs the Solsticer, you won't die, and you win by finishing all your tasks in a single round. After every meeting is finished, your tasks get reset, and you need to start all over again.\nVotes on the Solsticer will be directly cancelled.\nKill attempts on the Solsticer will teleport it out of the map like Pelican until the meeting is finished.\nThe killer's kill cooldown will be reset to 10 seconds.\nSolsticer is counted as nothing in game.", + "CollectorInfoLong": "(Neutrals):\nAs the Collector, when you vote for a player, for each other player that voted for them, you gain a point. When you collect the required number of votes, the game ends and you win alone, even if you voted a Jester or Executioner's target out.", + "GlitchInfoLong": "(Neutrals):\nAs the Glitch, you can hack players (single click) or kill normally (double click).\nThose who have been hacked cannot kill, vent, or report for the hack duration.\nAdditionally, calling a sabotage other than doors will have no effect, and will instead disguise you as a random player. You cannot disguise during or after sabotages.\nTo win, be the last player alive.", + "SidekickInfoLong": "(Neutrals):\nAs the Sidekick, your job is to help the Jackal kill everyone.\n\nYou and the Jackal win together.", + "ProvocateurInfoLong": "(Neutrals):\nAs the Provocateur, you can kill any target with the kill button. If the target loses at the end of the game, the Provocateur wins with the winning team.", + "BloodKnightInfoLong": "(Neutrals):\nThe Blood Knight wins when they're the last killing role alive and the amount of crewmates is lower or equal to the amount of Blood Knights. The Blood Knight gains a temporary shield after every kill that makes them immortal for a few seconds.", + "PlagueBearerInfoLong": "(Apocalypse):\nAs the Plaguebearer, plague everyone using your kill button to turn into Pestilence.\nOnce you turn into Pestilence you will become immortal and gain the ability to kill.\nIn addition to this, after turning into Pestilence you will kill anyone who tries to kill you.\n\nTo win, turn into Pestilence and kill everyone.", + "PestilenceInfoLong": "(Apocalypse):\nAs Pestilence, you're an unstoppable machine.\nAny attack towards you will be reflected back towards them.\nIndirect kills don't even kill you.\n\nOnly way to kill Pestilence is by vote, or by it misguessing.\nYour presence is announced to everyone the meeting after you transform.", + "FollowerInfoLong": "(Neutrals):\nThe Follower can use their Kill button on someone to start following them and can use the Kill button again to switch the following target. If the Follower's target wins, the Follower will win along with them. Note: The Follower can also win after they die.", + "CultistInfoLong": "(Neutrals):\nAs the Cultist, your kill button is used to Charm others, making them win with you. To win, charm all who pose a threat and gain majority.\nDepending on settings, you may be able to charm Neutrals, and those you Charm may count as their original team, nothing, or a Cultist to determine when you win due to majority.", + "SerialKillerInfoLong": "(Neutrals):\nAs the Serial Killer, you win if you are the last player alive. Depending on settings, you will not be impacted by any harmful interactions and may have a teammate.", + "JuggernautInfoLong": "(Neutrals):\nAs the Juggernaut, your kill cooldown decreases with each kill you make.\n\nKill everyone to win.", + "InfectiousInfoLong": "(Neutrals):\nAs the Infectious, your job is to infect as many players as you can.\n\nIf you infect all the killers, you then can simply outnumber the crew and win the game.\n\nIf you die, all the players you've infected will die after the next meeting.\nIf they achieve your win condition before then, you can still win.", + "VirusInfoLong": "(Neutrals):\nThe task of the virus is to kill or infect all other players. When the virus murders a crewmate, their corpse is infected with a virus. The crewmate who reports this corpse is infected and joins the virus team or dies at the end of the meeting if the virus won't get voted out, dependent on the settings. If there are more players on the Virus team than on the Crewmate team, the Virus team wins.", + "PursuerInfoLong": "(Neutrals):\nAs the Pursuer, you can use your ability on someone to make them misfire when they try to kill.\n\nTo win, just survive to the end of the game.", + "PhantomInfoLong": "(Neutrals):\nAs the Phantom, your job is to get killed and finish your tasks.\nYou can do your tasks while alive.\nYou cannot win if you're alive.\nIf you get killed, you win with the winning team if your tasks are completed.", + "PirateInfoLong": "(Neutrals):\nAs the Pirate, use your kill button to select a target every round.\nYou will duel with your target in the next meeting. \nIf both Pirate and the target chooses same number, Pirate wins.\nAdditionally, if Pirate wins the duel or the target doesn't participate in the duel, the Pirate kills the target.\n\nDueling command:- /duel X (where X can be 0, 1 or 2)\n\nYou win after winning a certain number of duels set by the host.\n\nNote: If target did not participate in duel, the kill will not count towards pirate victory", + "AgitaterInfoLong": "(Neutrals):\nAs the Agitator, your premise is essentially Hot Potato.\n\nUse your kill button on a player to pass the bomb.\nThis can only be done once per round.\n\nThe player who receives the bomb will be notified when receiving said bomb, in which they need to pass it to another player by getting near a player.\n\nWhen a meeting is called, the player with the bomb dies.\n\nIf trying to pass to Pestilence or a Veteran on alert, the bombed player dies instead.\nOptionally, the Agitator cannot receive the bomb.", + "MaverickInfoLong": "(Neutrals):\nAs the Maverick, you can kill and, depending on options, vent and have impostor vision\nIf you survive until the end of the game, you win with the winning team.\nUse your killing ability to eliminate threats to your life, but don't get voted out.", + "CursedSoulInfoLong": "(Neutrals):\nAs the Cursed Soul, you snatch the victory if you survive to the end of the game.\n\nYou can snatch the win from a Jester or Executioner.\n\nAdditionally, you can snatch the souls of other players.\nSoulless players win with you and count as dead.", + "PickpocketInfoLong": "(Neutrals):\nAs the Pickpocket, you steal votes from your kills.\nThese votes are hidden.\n\nKill everyone to win.", + "TraitorInfoLong": "(Neutrals):\nAs the Traitor, you were an Impostor that betrayed the Impostors.\nYou know the Impostors but they don't know you.\nThe twist? They can kill you but you can't kill them.\n\nEliminate the Impostors by other means, then kill everyone else to win!", + "VultureInfoLong": "(Neutrals):\nAs the Vulture, report bodies to win!\n\nWhen you report a body, if your eat cooldown is up, you'll eat the body (makes it unreportable).\nIf your eat ability is still on cooldown, then you'll report the body normally.\n\nAdditionally, you'll report bodies normally if the maximum bodies eaten per round is reached.", + "TaskinatorInfoLong": "(Neutrals):\nAs the Taskinator, whenever you complete a task, the task will be bombed. When another player completes the bombed task, the bomb will detonate and the player will die.\n\nYou win if you survive till the end and Crew doesn't win.\n\n Note: Taskinator bombs ignore all protection.", + "BenefactorInfoLong": "(Crewmates):\nAs the Benefactor, whenever you complete a task, the task will be marked. When another player completes the marked task, they get a temporary shield.\n\n Note: Shield only protects from direct kill attacks", + "MedusaInfoLong": "(Neutrals):\nAs the Medusa, you can stone bodies much like cleaning a body.\nStoned bodies cannot be reported.\n\nKill everyone to win.", + "BakerInfoLong": "(Apocalypse): \nAs the Baker, you can use your kill button on a player per round to give them Bread. \nOnce a set amount of players are alive with bread, you become Famine.", + "FamineInfoLong": "(Apocalypse): \nIf Famine is not voted out after the meeting they become Famine, every player without bread will starve (excluding other Apocalypse members).\nYou are invincible and your presence is announced to everyone the meeting after you transform.", + "SpiritcallerInfoLong": "(Neutrals):\nAs the Spiritcaller, your victims become Evil Spirits after they die. These spirits can help you win by freezing other players for a short time and/or blocking their vision. Alternatively, the spirits can give you a shield that protects you briefly from an attempted kill.", + "AmnesiacInfoLong": "(Neutrals):\nAs the Amnesiac, use your report button to remember a role.\n\nIf the target was an Impostor, you'll become a Refugee.\nIf the target was a crewmate, you'll become the target role if compatible (otherwise you become an Engineer).\nIf the target was a passive neutral or a neutral killer not specified, you'll become the role defined in the settings.\nIf the target was a neutral killer of a select few, you'll become the role they are.", + "ImitatorInfoLong": "(Neutrals):\nAs the Imitator, use your kill button to imitate a player.\n\nYou'll either become a Sheriff, a Refugee or some Neutral", + "BanditInfoLong": "(Neutrals):\nAs the Bandit, you can click your kill button once to steal a player's addon and twice to kill. Depending on the settings, you may instantly steal the addon or after the meeting starts. After the max number of steals are reached you will kill normally. Additionally, if there are no stealable addons present on the target or the target is stubborn you will kill the target.\n\nKill everyone to win.\n\nNote:- Cleansed, Last Impostor and Lovers can not be stolen.\nNote:- If Bandit can vent is on, Nimble will become unstealable", + "DoppelgangerInfoLong": "(Neutrals):\nAs the Doppelganger, use your kill button to steal a player's identity (their name and skin) and then kill your target.\n\nKill everyone to win.\n\nNote:- You can not steal the identity of the target when Camouflage is active.", + "MasochistInfoLong": "(Neutrals):\nAs the Masochist, your goal is to get attacked a few times to win.\n\nYou cannot be guessed, as that adds to your attack count.", + "DoomsayerInfoLong": "(Neutrals):\nThe Doomsayer can guess the role of a certain player during the meeting.\nIf the Doomsayer guesses a certain number of roles (the number depends on the host settings) then he wins.\nThe guessing command is: /bt [player id] [role]\nYou can see the player's id before the player's name, or use the /id command to view the id of all players.", + "ShroudInfoLong": "(Neutrals):\nAs the Shroud, you do not kill normally.\nInstead, use your kill button to shroud a player.\nShrouded players kill others.\nIf the shrouded player doesn't make a kill, they'll kill themself after a meeting.\n\nShroud sees shrouded players with a 「◈」mark next to their name.\nShrouded players who did not make a kill will also have the 「◈」mark in meetings, where they'll die if the Shroud is alive by the end of the meeting.", + "WerewolfInfoLong": "(Neutrals):\nAs the Werewolf, you can kill much like any killer.\nHowever, when you kill, any nearby players also die.\nAny player who dies to this will have their death reason as Mauled.\n\nTo balance this, you have a higher kill cooldown than anyone else.", + "ShamanInfoLong": "(Neutrals):\nAs the Shaman, you can use your kill button to select a voodoo doll once per round. If the kill button is used on you, the effect will be deflected omto the voodoo doll.\nIf you survive until the end, you win with the winning team.", + "SeekerInfoLong": "(Neutrals):\nAs the seeker, use your kill button to tag the target. If seeker tags wrong player a point is deducted and if seeker tags correct player a point will be added.\nAdditionally, the seeker will not be able to move for 5 seconds after every meeting and after getting a new target\n\n The seeker needs to collect certain number of points set by the host to win", + "PixieInfoLong": "(Neutrals):\nAs the Pixie, Mark upto x amount of targets each round by using kill button on them. When the meeting starts, your job is to have one of the marked targets ejected. If unsuccessful you will suicide, except if you didn't mark any targets or all the targets are dead. The selected targets resets to 0 after the meeting ends. If you succeed you will gain a point. You see all your targets in colored names.\n\nYou win with the winning team when you have certain amounts of points set by the host.", + "SoulCollectorInfoLong": "(Apocalypse):\nAs a Soul Collector, you vote players to predict their death. If the prediction is correct and the target dies in the next round you collect their soul. \n\nOnce you collect the configurable amount of souls, you become Death.", + "DeathInfoLong": "(Apocalypse): \nOnce the Soul Collector has collected their needed souls, they become Death. Depending on the host's settings, a meeting may or may not be called immediately. If Death is not ejected by the end of the next meeting, Death kills everyone and wins.\nYou are invincible and your presence is announced to everyone the meeting after you transform.", + "SchrodingersCatInfoLong": "(Neutrals):\nAs Schrodingers Cat, if someone attempts to use the kill button on you, you will block the action and join their team. This blocking ability works only once. By default, you don't have a victory condition, meaning you win only after switching teams.\nIn Addition to this, you will be counted as nothing in the game.\n\nNote: If the killing machine attempts to use their kill button on you, the interaction is not blocked, and you will die.", + "RomanticInfoLong": "(Neutrals):\nThe Romantic can pick their lover partner using their kill button (this can be done at any point of the game). Once they've picked their partner, they can use their kill button to give their partner a temporary shield which protects them from attacks. If their lover partner dies, the Romantic's role will change according to the following conditions:\n1. If their partner was an Impostor, the Romantic becomes the Refugee\n2. If their partner was a Neutral Killer, then they become Ruthless Romantic.\n3. If their partner was a Crewmate or a non-killing neutral, the Romantic becomes the Vengeful Romantic. \n\nThe Romantic wins with the winning team if their partner wins.\nNote : If your role changes your win condition will be changed accordingly", + "RuthlessRomanticInfoLong": "(Neutrals):\nYou change your roles from Romantic if your partner (A neutral killer) is killed. As Ruthless Romantic, you win if you kill everyone and be the last one standing. If you win your dead partner also wins with you.", + "VengefulRomanticInfoLong": "(Neutrals):\nYou change your roles from Romantic if your partner (A crew or non neutral killer) is killed. As a Vengeful Romantic, Your goal is to avenge your partner, which means you have to kill the killer of your partner. If you succeed to do so, then both you and your partner win with the winning team at the end. If you try to kill someone other than your partner's killer, then you will die by misfire.", + "PoisonerInfoLong": "(Neutrals):\nAs the Poisoner, your kills are delayed.\nKill everyone to win.", + "HexMasterInfoLong": "(Neutrals):\nAs the Hex Master, you can hex players or kill them.\nHexing a player works the same as spelling as a Witch.", + "WraithInfoLong": "(Neutrals):\nAs the Wraith, you can vent to temporarily Vanish. You will still appear visible on your screen. Vent again to become visible. You win if you are the last player remaining.", + "JinxInfoLong": "(Neutrals):\nAs the Jinx, whenever you are attacked, you jinx them, resulting in them dying to a jinx.\nThis has limited uses.\n\nKill off everyone to win.", + "PotionMasterInfoLong": "(Neutrals):\nAs the Potion Master, you have three potions assigned to three different actions.\n\nSingle click: Reveal role\nDouble click: Kill\nMap: Sabotage\n\nThe reveal potion has a limit.\nWhen you run out, the kill buttons defaults to killing.", + "NecromancerInfoLong": "(Neutrals):\nAs the Necromancer, you win when you're the last one standing.\nAdditionally, when someone tries to kill you, the kill will be blocked and you will be teleported to a random vent. You will have a limited time to kill your killer. If you succeed to do so, you live. If the time runs out before you kill your killer, you die permanately. If you try to kill someone else other than your killer, you will die.", + "LastImpostorInfoLong": "(Add-ons):\nThis effect is given to the last surviving Impostor. Reduces their kill cooldown.", + "OverclockedInfoLong": "(Add-ons):\nAs the Overclocked, your kill cooldown is reduced by a percentage.\n\nOnly assigned to roles with a kill button.", + "LoversInfoLong": "(Add-ons),\nLovers are a combination of two players. The Lovers win when only the Lovers are left. When one of the Lovers wins, the other also wins together. Lovers can see the 「♥」 mark next to each other's name. If one of the Lovers dies, the other will die in love (may not die in love according to the host's settings). When one of the Lovers is exiled in the meeting, the other will die and become a dead body that cannot be reported.", + "MadmateInfoLong": "(Add-ons):\nOnly Crewmate can become Madmate. Madmate's task is to help the Impostors win the game, Madmate will lose if all Impostors are killed/ejected. Madmates may know who are Impostors and Impostors may know who are Madmates (host settings).\n\nLazy Guy, Celebrity can't become Madmate. Sheriff, Snitch, Nice Guesser, Mayor, and Judge may become Madmate (host settings). Skill changes when the following roles are converted into Madmates:\n\nTime Manager => Doing tasks will reduce meeting time.\nBodyguard => Skill won't activate if the killer is an Impostor.\nGrenadier => Flash bomb will work on Crewmates and Neutrals instead of the Impostors.\nSheriff => Can kill anyone including Impostors (host settings).\nNice Guesser => Can guess Crewmates and Neutrals\nPsychic => All evil Neutrals and Crewmates' names with the ability to kill will be displayed in Red.\nJudge => Can judge anyone.", + "NtrInfoLong": "(Add-ons):\nWhen there is Neptune, all players will see that they are Lovers with Neptune, and they will not die in love together and will not change the win conditions. Note: Lovers won't become Neptune, and Neptune won't become Lovers.", + "WatcherInfoLong": "(Add-ons):\nDuring the meeting, Watcher can see everyone's votes.", + "FlashInfoLong": "(Add-ons):\nThe Flash's default movement speed is faster than others. (speed depends on the setting of the host)", + "TorchInfoLong": "(Add-ons):\nTorch has max vision and is not affected by Lights sabotage.", + "SeerInfoLong": "(Add-ons):\nWhenever a player dies, the Seer will see a kill-flash (a red flash, possibly accompanied by an alarm sound like sabotage).", + "TiebreakerInfoLong": "(Add-ons):\nWhen tie vote, priority will be given to the target voted by the Tiebreaker. Note: If multiple Tiebreaker choose different tie targets at the same time, the skills of the Tiebreaker will not take effect.", + "ObliviousInfoLong": "(Add-ons):\nDetective and Cleaners won't be Oblivious. Oblivious cannot report dead bodies. Note: Bait killed by Oblivious will still be reported automatically, and Oblivious can still be used as a scapegoat for the Anonymous.", + "BewilderInfoLong": "(Add-ons):\nBewilder may have a smaller/bigger vision. When the Bewilder is killed, the murderer's vision may become the same as the Bewilder's vision depending on the settings.", + "WorkhorseInfoLong": "(Add-ons):\nThe first player to complete all the tasks will become Workhorse, Workhorse will give the player extra tasks. The amount of additional tasks are set by the host.", + "FoolInfoLong": "(Add-ons):\nSleuth and Mechanic won't be Fool. Fools can't repair any sabotage.", + "AvangerInfoLong": "(Add-ons):\nHost can set whether the Impostor can become an Avenger. When the Avenger is killed (voted out and unconventional kills are not counted), the Avenger will revenge a random player.", + "YoutuberInfoLong": "(Add-ons):\nOnly Crewmate will become YouTuber. When the YouTuber is the first player to be killed in the game, the YouTuber will win alone. If the YouTuber does not meet the win conditions, the YouTuber will follow the Crewmate to win. Note: Indirect killing methods such as being exiled, being guessed by the Guesser, etc. will not trigger the skills of the YouTuber.", + "EgoistInfoLong": "(Add-ons):\nMadmate and Neutrals won't be Egoist. If the Egoist's team wins, the Egoist wins instead of their team.", + "TicketsStealerInfoLong": "(Add-ons):\nEvery time a Stealer kills a person, he gets an additional vote (the vote number is set by the host, and the decimal is rounded down). Also, extra votes from the Stealer are hidden during meeting.", + "SchizophrenicInfoLong": "(Add-ons):\nNot assigned to Neutrals nor Madmates.\nAs the Schizophrenic, you will be considered as two players in the game for the purpose of determining when the game ends due to killers having majority. Additionally, this grants you an extra vote, depending on options.", + "MimicInfoLong": "(Add-ons):\nOnly Impostor can become Mimic. When the Mimic is dead, other Impostors will receive a message once a meeting is called, this message will include information on roles who were killed by the Mimic.", + "GuesserInfoLong": "(Add-ons):\nAs a guesser, guess roles of players in meetings to kill them.\nGuessing incorrectly kills you instead.\nThe guessing command is: /bt [player id] [role]\nYou can see the player's id before the player's name, or use the /id command to view the id of all players.", + "NecroviewInfoLong": "(Add-ons):\nThe Necroview can see the teams of dead players. The following info will be displayed on the dead player's name while in a meeting:\n- The Red name indicates the Impostors.\n- The Cyan name indicates the Crewmates.\n- The Gray name indicates the Neutrals.", + "ReachInfoLong": "(Add-on)\nOnly roles with a kill button can get this add-on. You have the longest kill range possible in the game, unlike everyone else.", + "BaitInfoLong": "(Add-ons):\nWhen the Bait is killed, the murderer who killed the Bait will be forced to self-report the Bait's body. However, this won't happen when the Bait is killed by a Scavenger, Cleaner, Swooper, Wraith, or Killing Machine. The report may have a delay according to Host's settings.", + "TrapperInfoLong": "(Add-ons):\nWhen Beartrap is killed, Beartrap immobilize killer for a configurable amount of time.", + "CharmedInfoLong": "(Betrayal Add-ons):\nThe Charmed add-on is obtained by being charmed by the Cultist.\nOnce charmed, you are now on the Cultist's team and no longer on your original team.", + "CleansedInfoLong": "(Add-ons):\nCleansed Add-on can only be obtained if cleanser erases all your Add-ons. Depending on the cleanser settings, you may not be able to obtain any more Add-ons in the future.", + "InfectedInfoLong": "(Betrayal Add-ons):\nThe Infected add-on is obtained by being infected by the Infectious.\nOnce infected, you work for the Infectious and do not win with your original team.", + "OnboundInfoLong": "(Add-ons):\nWith the Onbound add-on, you cannot be guessed in meetings.", + "ReboundInfoLong": "(Add-ons):\nWith the Rebound add-on, if a Guesser successfully guessed you or a Judge successfully judged you, they will die instead.\nIf a player with Double Shot guesses you correctly, they will die instantly.", + "MundaneInfoLong": "(Add-ons):\nAs Mundane, you can only guess after all your tasks has been finished.", + "KnightedInfoLong": "(Add-ons):\nWhen a Monarch knights someone, they get an extra vote.", + "UnreportableInfoLong": "(Add-ons):\nWith the Disregarded add-on, your corpse cannot be reported.", + "ContagiousInfoLong": "(Betrayal Add-ons):\nWhen the Virus infects you, you become contagious.\nContagious players are on the Virus team.\n\nWhether or not you die after a meeting depends on the settings for the Virus.", + "LuckyInfoLong": "(Add-ons):\nWith the Lucky add-on there is a probability for you to evade the kill, the specific probability is set by the host. When the evasion takes effect, the killer will see the shield-animation, but you not know anything.", + "DoubleShotInfoLong": "(Add-ons):\nWhen a player with Double Shot guesses a role incorrectly, they will get a second chance to guess, but the next wrong guess will result in suicide.", + "RascalInfoLong": "(Add-ons):\nAs the Rascal, you can die to the Sheriff and Snitch can find you if Snitch can find madmates.\n\nOnly assigned to Crewmates, cannot be assigned by the Merchant.", + "SoullessInfoLong": "(Add-ons):\nWhen a Cursed Soul snatches your soul, you get this add-on.\n\nYou are not counted as alive.", + "GravestoneInfoLong": "(Add-ons):\nAs the Gravestone, your role is revealed to everyone when you die.", + "LazyInfoLong": "(Add-ons):\nAs the Lazy, you are assigned a single short task and are immune to Warlocks, Puppeteers, and Gangsters.", + "AutopsyInfoLong": "(Add-ons):\nAs the Autopsy, you can see how people died.\n\nCannot be assigned to Doctor, Tracefinder, Scientist, or Sunnyboy.", + "LoyalInfoLong": "(Add-ons):\nAs the Loyal, you cannot be recruited by roles such as Jackal or Cultist.\n\nCannot be assigned to neutrals.", + "EvilSpiritInfoLong": "(Add-ons):\nAs Evil Spirit, it's your job to help the Spiritcaller to victory. You can use your Haunt button to freeze players and reduce their vision. Alternatively, you can use your Haunt button to temporarily give the Spiritcaller a shield against a kill attempt.", + "RecruitInfoLong": "(Betrayal Add-ons):\nAs a recruit, you are on the Jackal's team and help out the Jackal and their Sidekicks.\n\nYou cannot win with your original team.", + "AdmiredInfoLong": "(Betrayal Add-ons):\nAs an admired player, you win with the crew and not your original team.\n\nYou can see the Admirer.", + "GlowInfoLong": "(Add-ons):\nAs the Glow, your name is colored during a lights sabotage.", + "DiseasedInfoLong": "(Add-ons):\nWhen someone tries to use kill button on you, their cooldown will be increased by configurable amount of time", + "AntidoteInfoLong": "(Add-ons):\nWhen someone tries to use kill button on you, their cooldown will be decreased by configurable amount of time", + "StubbornInfoLong": "(Add-ons):\nWith the Stubborn add on, Eraser can’t erase your role, Cleanser can't cleanse you, Bandit can't steal from you and Monarch can't knight you.\nAdditionally, you can’t gain any new addons from the merchant", + "SwiftInfoLong": "(Add-ons):\nAs the Swift, you will not make any movement when you kill.", + "UnluckyInfoLong": "(Add-ons):\nAs the Unlucky, doing tasks, killing, or venting as a chance to kill you.", + "VoidBallotInfoLong": "(Add-ons):\nHolder of this addon will have 0 vote count", + "AwareInfoLong": "(Add-ons):\nAs the Aware, you will be notified in the next meeting if a revealing role had interacted with you", + "FragileInfoLong": "(Add-ons):\nAs Fragile, you will die instantly if someone tries to use kill button on you (even if the role can not directly kill)", + "GhoulInfoLong": "(Add-ons):\nAs the Ghoul, one of two outcomes can occur on tasks completion.\n\nIf alive: Suicide\nIf dead: You kill your killer if they're alive.\n\nOnly assigned to crewmates, and not crewmates with no tasks or are task based.", + "BloodlustInfoLong": "(Add-ons):\nAs the Bloodlust, doing tasks allows you to kill.\nWhen you complete a task, the next player you come in contact with dies.\n\nYour bloodlust remains after a meeting.\nUpon making a kill, your bloodlust clears till the next task you complete.\nBloodlusts do not stack.\n\nOnly assigned to crewmates with tasks.", + "SunglassesInfoLong": "(Add-ons):\nAs the Sunglasses, your vision is reduced.", + "MareInfoLong": "(Add-ons):\nAs the Mare, you have a low kill cooldown and have higher speed but can only kill during lights.\n\nAdditionally, your name will appear in red during lights.\n\nOnly assigned to Impostors and cannot be guessed.", + "BurstInfoLong": "(Add-ons):\nAs the Burst, your killer explodes if they aren't inside a vent after a set amount of time.", + "SleuthInfoLong": "(Add-ons):\nAs the Sleuth, you gain info from dead bodies.\n\nOptionally, you may also gain the killer's role.\n\nNot assigned to Detective or Mortician.", + "ClumsyInfoLong": "(Add-ons):\nAs the Clumsy, you have a chance to miss your kill.\n\nWhen you miss, your cooldown is reset and the target remains untouched.\n\nOnly assigned to killers.", + "CircumventInfoLong": "(Add-ons):\nAs the Circumvent, you can't vent.\n\nOnly assigned to Impostors.", + "NimbleInfoLong": "(Add-ons):\nAs the Nimble, you gain access to the vent button.\n\nOnly assigned to certain crewmates.", + "InfluencedInfoLong": "(Add-ons):\nAs the Influenced, your vote will be forced to the player with the most votes.\nInfluenced vote won't be counted while choosing the exiled player'\nNote that your vote skill still functions on the player you voted first\nIf all the alive players are Influenced,then the vote result won't shift\nCollector cannot become influenced.", + "SilentInfoLong": "(Add-ons):\nAs the Silent, your vote icon won't appear on the result screen.\nSo nobody knows who you voted for.", + "SusceptibleInfoLong": "(Add-ons):\nAs the Susceptible, your death reason will be random.", + "TrickyInfoLong": "(Add-ons):\nAs the Tricky, your kills will have a random death reason.", + "TiredInfoLong": "(Add-ons):\nWhenever Tired kills (or uses kill ability on) someone, alternatively whenever they complete a task, they will temporarily get lower vision & lower speed.", + "StatueInfoLong": "(Add-ons):\nWhenever many people are near Statue, the Statue is completely frozen or slowed down dependand on settings.", + "CyberInfoLong": "(Add-ons):\nAs the Cyber, you cannot be killed while in a group.\nAdditionally, your death will be known.", + "HurriedInfoLong": "(Add-ons):\nAs the hurried, you must finish all your tasks to win with your team! If you failed with your tasks, you lose.\nHurried hurries to his goal so it won't get madmate,charmed or so.", + "OiiaiInfoLong": "(Add-ons):\nAs the Oiiai, when you die, you will make your killer forget their role.\nAdditionally, you may pass Oiiai on to the killer, depending on settings.", + "RainbowInfoLong": "(Add-ons):\nAs the rainbow, you change your colors like crazy", + "GMInfoLong": "(None):\nThe Game Master is an observer role.\nTheir presence has no effect on the game, and all players know who the Game Master is. The Game Master role will be assigned to the host, who will automatically become a ghost at the start of the game.", + "SunnyboyInfoLong": "(Neutrals):\nAs the Sunnyboy, you win if you are dead by the end of the game. When you are alive, the game will not end due to killers gaining majority.\nAdditionally, you have access to portable vitals.", + "BardInfoLong": "(Impostors):\nWhen a bard is alive, the exile confirmation will display a sentence composed by the bard. Whenever the bard completes a creation, the bard's kill cooldown is permanently halved.", + "NukerInfoLong": "(Impostors):\nAs the Nuker, you're a stronger Bomber.\n\nShapeshift to nuke everyone.", + "WardenInfoLong": "(Crewmates [Ghost]):\nAs the Warden, alert someone of nearby danger, additionally giving them a temporary speed boost.", + "MinionInfoLong": "(Impostor [Ghost]):\nAs the Minion, you can temporarily blind non-impostors.", + "ShowTextOverlay": "Text Overlay", + "Overlay.GuesserMode": "Guesser Mode", + "Overlay.NoGameEnd": "No Game End", + "Overlay.DebugMode": "Debug Mode", + "Overlay.LowLoadMode": "Low Load Mode", + "Overlay.AllowConsole": "Console", + "DisableShieldAnimations": "Disable Unnecessary Shield Animations", + "DisableKillAnimationOnGuess": "Disable Kill Animation on Guesses", + "AbilityUseGainWithEachTaskCompleted": "Amount of Ability Use Gains With Each Task Completed", + "OutOfAbilityUsesDoMoreTasks": "Out of ability uses! Do tasks to get more!", + "AbilityUseLimit": "Initial Ability Use Limit", + "ShowArrows": "Has Arrows pointing toward bodies", + "ArrowDelayMin": "Minimum Arrow show-up delay", + "ArrowDelayMax": "Maximum Arrow show-up delay", + "SMUsesUsedWhenFixingReactorOrO2": "Uses it takes to fix Reactor/O2", + "SMUsesUsedWhenFixingLightsOrComms": "Uses it takes to fix Lights/Comms", + + "AbilityCD": "Ability Cooldown", + "GrenadierSkillMaxOfUseage": "(Initial) Max number of Grenades", + "ShowSpecificRole": "Know specific roles on Task Completion", + "TimeMasterMaxUses": "(Initial) Max Amount of Ability Uses", + "SwooperVentNormallyOnCooldown": "Swooper vents normally when swooping is on cooldown", + "WraithVentNormallyOnCooldown": "Wraith vents normally when invis is on cooldown", + "DisableMeeting": "Disable Meetings", + "DisableCloseDoor": "Disable Doors Sabotage", + "DisableSabotage": "Disable Sabotages", + "NoGameEnd": "No Game End", + "AllowConsole": "BepInEx Console", + "DebugMode": "Debug Mode", + "SyncButtonMode": "Sync Buttons Mode", + "RandomMapsMode": "Random Maps Mode", + "SyncedButtonCount": "Max Number of Emergency Meetings Allowed", + "HHSuccessKCDDecrease": "Kill cooldown decrease on killing target", + "HHFailureKCDIncrease": "Kill cooldown increase on killing others", + "HHNumOfTargets": "Number of targets", + "Targets": "Targets: ", + "HHMaxKCD": "Maximum kill cooldown", + "HHMinKCD": "Minimum kill cooldown", + "AllAliveMeeting": "Meeting When No One is Dead", + "AllAliveMeetingTime": "Meeting Time When No One is Dead", + "AdditionalEmergencyCooldown": "Additional Emergency Cooldown", + "AdditionalEmergencyCooldownThreshold": "Minimum Living Players to be Applied", + "AdditionalEmergencyCooldownTime": "Additional Cooldown", + "LadderDeath": "Fall From Ladders", + "LadderDeathChance": "Fall To Death Chance", + "DisableSwipeCardTask": "Disable Swipe Card Task", + "DisableSubmitScanTask": "Disable Submit Scan Task", + "DisableUnlockSafeTask": "Disable Unlock Safe Task", + "DisableUploadDataTask": "Disable Upload Data Task", + "DisableStartReactorTask": "Disable Start Reactor Task", + "DisableResetBreakerTask": "Disable Reset Breakers Task", + "DisableShortTasks": "Disable Short Tasks", + "DisableCleanVent": "Disable Clean Vent Task", + "DisableCalibrateDistributor": "Disable Calibrate Distributor Task", + "DisableChartCourse": "Disable Chart Course Task", + "DisableStabilizeSteering": "Disable Stabilize Steering Task", + "DisableCleanO2Filter": "Disable Clean O2 Filter Task", + "DisableUnlockManifolds": "Disable Unlock Manifolds Task", + "DisablePrimeShields": "Disable Prime Shields Task", + "DisableMeasureWeather": "Disable Measure Weather", + "DisableBuyBeverage": "Disable Buy Beverage", + "DisableAssembleArtifact": "Disable Assemble Artifact Task", + "DisableSortSamples": "Disable Sort Samples Task", + "DisableProcessData": "Disable Process Data Task", + "DisableRunDiagnostics": "Disable Run Diagnostics Task", + "DisableRepairDrill": "Disable Repair Drill Task", + "DisableAlignTelescope": "Disable Align Telescope Task", + "DisableRecordTemperature": "Disable Record Temperature Task", + "DisableFillCanisters": "Disable Fill Canisters Task", + "DisableMonitorTree": "Disable Monitor Tree Task", + "DisableStoreArtifacts": "Disable Store Artifacts Task", + "DisablePutAwayPistols": "Disable Put Away Pistols Task", + "DisablePutAwayRifles": "Disable Put Away Rifles Task", + "DisableMakeBurger": "Disable Make Burger Task", + "DisableCleanToilet": "Disable Clean Toilet Task", + "DisableDecontaminate": "Disable Decontaminate Task", + "DisableSortRecords": "Disable Sort Records Task", + "DisableFixShower": "Disable Fix Shower Task", + "DisablePickUpTowels": "Disable Pick Up Towels Task", + "DisablePolishRuby": "Disable Polish Ruby Task", + "DisableDressMannequin": "Disable Dress Mannequin Task", + "DisableCommonTasks": "Disable Common Tasks", + "DisableFixWiring": "Disable Fix Wiring Task", + "DisableEnterIdCode": "Disable Enter ID Code Task", + "DisableInsertKeys": "Disable Insert Keys Task", + "DisableScanBoardingPass": "Disable Scan Boarding Pass Task", + "DisableLongTasks": "Disable Long Tasks", + "DisableAlignEngineOutput": "Disable Align Engine Output Task", + "DisableInspectSample": "Disable Inspect Sample Task", + "DisableEmptyChute": "Disable Empty Chute Task", + "DisableClearAsteroids": "Disable Clear Asteroids Task", + "DisableWaterPlants": "Disable Water Plants Task", + "DisableOpenWaterways": "Disable Open Waterways Task", + "DisableReplaceWaterJug": "Disable Replace Water Jug Task", + "DisableRebootWifi": "Disable Reboot Wifi Task", + "DisableDevelopPhotos": "Disable Develop Photos Task", + "DisableRewindTapes": "Disable Rewind Tapes Task", + "DisableStartFans": "Disable Start Fans Task", + "DisableOtherTasks": "Disable Situational Tasks", + "DisableEmptyGarbage": "Disable Empty Garbage Task", + "DisableFuelEngines": "Disable Fuel Engines Task", + "DisableDivertPower": "Disable Divert Power Task", + "DisableActivateWeatherNodes": "Disable Weather Nodes Task", + "DisableRoastMarshmallow": "Disable Roast Marshmallow", + "DisableCollectSamples": "Disable Collect Samples", + "DisableReplaceParts": "Disable Replace Parts", + "DisableCollectVegetables": "Disable Collect Vegetables", + "DisableMineOres": "Disable Mine Ores", + "DisableExtractFuel": "Disable Extract Fuel", + "DisableCatchFish": "Disable Catch Fish", + "DisablePolishGem": "Disable Polish Gem", + "DisableHelpCritter": "Disable Help Critter", + "DisableHoistSupplies": "Disable Hoist Supplies", + "DisableFixAntenna": "Disable Fix Antenna", + "DisableBuildSandcastle": "Disable Build Sandcastle", + "DisableCrankGenerator": "Disable Crank Generator", + "DisableMonitorMushroom": "Disable Monitor Mushroom", + "DisablePlayVideoGame": "Disable Play Video Game", + "DisableFindSignal": "Disable Find Signal", + "DisableThrowFisbee": "Disable Throw Frisbee", + "DisableLiftWeights": "Disable Lift Weights", + "DisableCollectShells": "Disable Collect Shells", + "SuffixMode": "Suffix", + "SuffixMode.None": "None", + "SuffixMode.Version": "Version", + "SuffixMode.Streaming": "Streaming", + "SuffixMode.Recording": "Recording", + "SuffixMode.RoomHost": "Room Host", + "SuffixMode.OriginalName": "Original Name", + "SuffixMode.DoNotKillMe": "Don't kill me", + "SuffixMode.NoAndroidPlz": "No phones", + "SuffixMode.AutoHost": "Auto-Host", + "SuffixModeText.DoNotKillMe": "Don't kill me", + "SuffixModeText.NoAndroidPlz": "No phones please", + "SuffixModeText.AutoHost": "Auto-hosting", + "FormatNameMode": "Player Name Mode", + "FormatNameModes.None": "Disable", + "FormatNameModes.Color": "Color", + "FormatNameModes.Snacks": "Random", + "DisableEmojiName": "Disable Emoji in names", + "FixFirstKillCooldown": "Override Starting Kill Cooldown", + "FixKillCooldownValue": "Starting Kill Cooldown", + "OverclockedReduction": "Kill Cooldown Reduction", + "GhostCanSeeOtherRoles": "Ghosts Can See Other Roles", + "GhostCanSeeOtherVotes": "Ghosts Can See Vote Colors", + "GhostCanSeeDeathReason": "Ghost Can See Cause Of Death", + "GhostIgnoreTasks": "Ghosts Exempt From Tasks", + "ConvertedCanBeGhostRole": "Converted Players Can Be Any Ghost-Roles", + "MaxImpGhostRole": "Max Impostor Ghost-Roles", + "MaxCrewGhostRole": "Max Crewmate Ghost-Roles", + "DefaultAngelCooldown": "Default Ability Cooldown", + "DisableTaskWin": "Disable Task Win", + "HideGameSettings": "Hide Game Settings", + "DIYGameSettings": "Enable only custom /n messages", + "Settings:": "Settings:", + "PlayerCanSetColor": "Players can use the /color command", + "PlayerCanSetName": "Players can use the /rn command", + "PlayerCanUseQuitCommand": "Players can use the /quit command to leave the lobby forever", + "PlayerCanUseTP": "Players can use the /tpin and /tpout command", + "CanPlayMiniGames": "Players can play mini games", + "KPDCamouflageMode": "Camouflage Appearance", + "RoleOptions": "Role Options", + "DarkTheme": "Enable Dark Theme", + "AutoStart": "Auto start", + "EnableCustomButton": "Enable Custom Button Images", + "EnableCustomSoundEffect": "Enable Custom Sound Effects", + "SwitchVanilla": "Switch Vanilla", + "ModeForSmallScreen": "Small Screen Mode", + "UnlockFPS": "Unlock FPS", + "ForceOwnLanguage": "Force mod to use your language if possible", + "ForceOwnLanguageRoleName": "Force role names in your language if possible", + "VersionCheat": "Bypass version synchronization check", + "GodMode": "God Mode", + + "AutoDisplayKillLog": "Display Kill-log", + "AutoDisplayLastRoles": "Display Last Roles", + "AutoDisplayLastResult": "Auto Display Last Result", + "RevertOldKillLog": "Revert to old kill-log", + + "HideExileChat": "Hide exile (lava) chat", + "ExileSpamMsg": "Someone is trying to be a smart ass by lava chatting", + + "EnableYTPlan": "Enable Youtuber Plan", + "KickLowLevelPlayer": "Kick players whose level is lower than", + "TempBanLowLevelPlayer": "Temporarily ban low-level players", + "ApplyWhiteList": "Turn on Whitelist to bypass level kick", + "AllowOnlyWhiteList": "Allow only whitelisted players to join", + "AutoKickStart": "Kick players that say start", + "AutoKickStartTimes": "Number of warnings before kick", + "AutoKickStartAsBan": "Block a player after they're kicked", + "AutoKickStopWords": "Kick players who write banned words", + "AutoKickStopWordsTimes": "Number of warnings for banned words", + "AutoKickStopWordsAsBan": "Block a player after they're kicked", + "AutoWarnStopWords": "Warning to those who write banned words", + "TempBanPlayersWhoKeepQuitting": "Temporarily ban players who leave and join repeatedly", + "QuitTimesTillTempBan": "The quit frequency needed for temp ban", + "KickOtherPlatformPlayer": "Kick Non-PC players", + "OptKickAndroidPlayer": "Kick Android players", + "OptKickIphonePlayer": "Kick iOS players", + "OptKickXboxPlayer": "Kick Xbox players", + "OptKickPlayStationPlayer": "Kick PlayStation players", + "OptKickNintendoPlayer": "Kick Nintendo Switch players", + "ShareLobby": "Allow TOHE-Chan Shares Lobby Code To Discord", + "ShareLobbyMinPlayer": "Share Lobby Code When The Number Of Players Reaches", + "DisableVanillaRoles": "Disable Vanilla Roles", + "VoteMode": "Voting Mode", + "WhenSkipVote": "If the Player Skipped", + "WhenSkipVoteIgnoreFirstMeeting": "Ignore the First Meeting", + "WhenSkipVoteIgnoreNoDeadBody": "Ignore When No Dead Body", + "WhenSkipVoteIgnoreEmergency": "Ignore at Emergency Meetings", + "WhenNonVote": "If the Player didn't vote", + "Default": "No vote", + "Suicide": "Suicide", + "SelfVote": "Self Vote", + "Skip": "Skip", + "WhenTie": "When Tied Vote", + "TieMode.Default": "No ejects", + "TieMode.All": "Eject All", + "TieMode.Random": "Eject Random", + "DisableDevices": "Disable Devices", + "DisableSkeldDevices": "Disable Skeld Devices", + "DisableMiraHQDevices": "Disable MIRA HQ Devices", + "DisablePolusDevices": "Disable Polus Devices", + "DisableAirshipDevices": "Disable Airship Devices", + "DisableFungleDevices": "Disable Fungle Devices", + "DisableSkeldAdmin": "Disable Admin", + "DisableMiraHQAdmin": "Disable Admin", + "DisablePolusAdmin": "Disable Admin", + "DisableAirshipCockpitAdmin": "Disable Cockpit Admin", + "DisableAirshipRecordsAdmin": "Disable Records Admin", + "DisableSkeldCamera": "Disable Cameras", + "DisablePolusCamera": "Disable Cameras", + "DisableAirshipCamera": "Disable Cameras", + "DisableMiraHQDoorLog": "Disable DoorLog", + "DisablePolusVital": "Disable Vitals", + "DisableAirshipVital": "Disable Vitals", + "DisableFungleVital": "Disable Vitals", + "DisableFungleBinoculars": "Disable Binoculars (Not work for vanilla)", + "IgnoreConditions": "Ignore Conditions", + "IgnoreImpostors": "Ignore Impostors", + "IgnoreNeutrals": "Ignore Neutrals", + "IgnoreCrewmates": "Ignore Crewmates", + "IgnoreAfterAnyoneDied": "Ignore After First Death", + "LightsOutSpecialSettings": "Fix Lights Special Settings", + "BlockDisturbancesToSwitches": "Block Switches When They Are Up", + "DisableAirshipViewingDeckLightsPanel": "Disable Viewing Deck Lights Panel (Airship)", + "DisableAirshipGapRoomLightsPanel": "Disable Gap Room Lights Panel (Airship)", + "DisableAirshipCargoLightsPanel": "Disable Cargo Lights Panel (Airship)", + "RandomSpawnMode": "Random Spawns Mode", + "RandomSpawn_SpawnRandomLocation": "Random Spawns In Locations", + "RandomSpawn_AirshipAdditionalSpawn": "Additional Spawn Locations (Airship)", + "RandomSpawn_SpawnRandomVents": "Random Spawns On Vents", + "CommsCamouflage": "Camouflage during Comms Sabotage", + "DisableOnSomeMaps": "Disable comms camouflage on some maps", + "DisableOnSkeld": "Disable on The Skeld", + "DisableOnMira": "Disable on MIRA HQ", + "DisableOnPolus": "Disable on Polus", + "DisableOnDleks": "Disable on dlekS ehT", + "DisableOnAirship": "Disable on Airship", + "DisableOnFungle": "Disable on The Fungle", + "DisableReportWhenCC": "Disable body reporting while camouflaged", + "EnableDebugMode": "Enable Debug Mode", + "ChangeNameToRoleInfo": "Show Role Info to Unmodded Clients Round 1", + "SendRoleDescriptionFirstMeeting": "Show Role Descriptions to Unmodded Clients at First Meeting", + "RoleAssigningAlgorithm": "Role Assigning Algorithm", + "RoleAssigningAlgorithm.Default": "Default", + "RoleAssigningAlgorithm.NetRandom": ".NET System.Random", + "RoleAssigningAlgorithm.HashRandom": "HashRandom", + "RoleAssigningAlgorithm.Xorshift": "Xorshift", + "RoleAssigningAlgorithm.MersenneTwister": "MersenneTwister", + "MapModification": "Map Modifications", + "DisableAirshipMovingPlatform": "Disable Moving Platform (Airship)", + "AirshipVariableElectrical": "Variable Electrical (Airship)", + "DisableSporeTriggerOnFungle": "Disable Spore Trigger (Fungle)", + "DisableZiplineOnFungle": "Disable Zipline (Fungle)", + "DisableZiplineFromTop": "Disable Use From Top", + "DisableZiplineFromUnder": "Disable Use From Under", + "ResetDoorsEveryTurns": "Reset Doors After Meeting (Airship/Polus/Fungle)", + "DoorsResetMode": "Reset Doors Mode", + "AllOpen": "All Open", + "AllClosed": "All Closed", + "RandomByDoor": "Closed Random", + "ChangeDecontaminationTime": "Change Decontamination Time (MIRA HQ/Polus)", + "DecontaminationTimeOnMiraHQ": "Decontamination Time On MIRA HQ", + "DecontaminationTimeOnPolus": "Decontamination Time On Polus", + "ApplyDenyNameList": "Apply DenyName List", + "KickPlayerFriendCodeNotExist": "Kick players without a friend code", + "TempBanPlayerFriendCodeNotExist": "Temp Ban players without a friend code", + "ApplyBanList": "Apply BanList", + "EndWhenPlayerBug": "End the game when a player has a critical error", + "RemovePetsAtDeadPlayers": "Remove pets at dead players", + "KillFlashDuration": "Kill-Flash Duration", + "ConfirmEjectionsMode": "Confirm Ejections Mode", + "ConfirmEjections.None": "None", + "ConfirmEjections.Team": "Team", + "ConfirmEjections.Role": "Role", + "ShowImpRemainOnEject": "Show remaining Impostors on ejects", + "ShowNKRemainOnEject": "Show remaining Neutral Killers on ejects", + "ConfirmEgoistOnEject": "Confirm Egoists on ejection", + "ConfirmLoversOnEject": "Confirm Lovers on ejection", + "ConfirmSidekickOnEject": "Confirm Sidekicks on ejection", + "HideBittenRolesOnEject": "Hide roles of bitten players on ejection", + "ShowTeamNextToRoleNameOnEject": "Show what team the ejected player's role is on", + "Ban": "Ban", + "Kick": "Kick", + "NoticeMe": "Notify me", + "NoticeEveryone": "Notify everyone", + "TempBan": "Temporary Ban", + "OnlyCancel": "Only Cancel the cheat actions", + "CheatResponses": "When a cheating player is found", + "NeutralRoleWinTogether": "Neutrals win together", + "NeutralWinTogether": "All Neutrals win together", + "MenuTitle.Disable": "★ Disable ★", + "MenuTitle.MapsSettings": "★ Maps ★", + "MenuTitle.Sabotage": "★ Sabotage ★", + "MenuTitle.Meeting": "★ Meeting ★", + "MenuTitle.Ghost": "★ Ghost ★", + "MenuTitle.Other": "★ Different ★", + "MenuTitle.Ejections": "★ Ejection ★", + "MenuTitle.Settings": "★ Settings ★", + "MenuTitle.TaskSettings": "★ Task Management ★", + "MenuTitle.Guessers": "★ Guesser Mode ★", + "MenuTitle.GuesserModeRoles": "★ Roles and Add-ons for Guesser Mode ★", + "ShieldPersonDiedFirst": "Shield player who dead first in the last game", + "LegacyNemesis": "Use Legacy Version", + "ArsonistKeepsGameGoing": "Arsonist keeps the game going", + "ArsonistCanIgniteAnytime": "Can Ignite Anytime", + "ArsonistMinPlayersToIgnite": "Minimum doused needed for ignite", + "ArsonistMaxPlayersToIgnite": "Maximum doused needed for ignite", + "PuppeteerDoubleKills": "Puppet dies alongside victim", + "MastermindCD": "Manipulate Cooldown", + "MastermindTimeLimit": "Time limit to kill someone", + "MastermindDelay": "Manipulation notification delay", + "ManipulateNotify": "Kill someone in {0}s or die!", + "ManipulatedKilled": "{0} has killed someone", + "SurvivedManipulation": "You survived the Mastermind's manipulation!", + "Glitch_HackCooldown": "Hack Cooldown", + "Glitch_HackDuration": "Hack Duration", + "Glitch_MimicCooldown": "Mimic Cooldown", + "Glitch_MimicDuration": "Mimic Duration", + "Glitch_MimicButtonText": "Mimic", + "Glitch_MimicDur": "Mimic Duration: {0}s", + "Glitch_HackCD": "Hack Cooldown: {0}s", + "Glitch_KCD": "Kill Cooldown: {0}s", + "Glitch_MimicCD": "Mimic Cooldown: {0}s", + "HackedByGlitch": "You are hacked by the Glitch, you can't {0}.", + "GlitchKill": "kill", + "GlitchReport": "report", + "GlitchVent": "vent", + "ShowFPS": "Show FPS", + "FPSGame": "FPS: ", + "Cooldown": "Cooldown", + "KillCooldown": "Kill Cooldown", + "AbilityCooldown": "Ability Cooldown", + "VentCooldown": "Vent Cooldown", + "ControlCooldown": "Control Cooldown", + "PoisonCooldown": "Poison Cooldown", + "PoisonerKillDelay": "Poison Kill Delay", + "WardenNotifyLimit": "Max number of alerts", + "CanVent": "Can Vent", + "CanKill": "Can Kill", + "CanGuess": "Can Guess in Guesser Mode or as Guesser", + "BombCooldown": "Bomb Cooldown", + "ImpostorVision": "Has Impostor Vision", + "CanUseSabotage": "Can Sabotage", + "CanKillAllies": "Can Kill Impostors", + "CanKillSelf": "Can Kill Themself", + "CrewpostorKnowsAllies": "Knows Impostors", + "AlliesKnowCrewpostor": "Known to Impostors", + "CrewpostorLungeKill": "Crewpostor lunges on kill", + "CrewpostorKillAfterTask": "Number of tasks completed to make 1 kill", + + "NonNeutralKillingRolesMinPlayer": "Minimum amount of Non-Killing Neutrals", + "NonNeutralKillingRolesMaxPlayer": "Maximum amount of Non-Killing Neutrals", + "NeutralKillingRolesMinPlayer": "Minimum amount of Neutral Killers", + "NeutralKillingRolesMaxPlayer": "Maximum amount of Neutral Killers", + "NeutralApocalypseRolesMinPlayer": "Minimum amount of Neutral Apocalypse", + "NeutralApocalypseRolesMaxPlayer": "Maximum amount of Neutral Apocalypse", + "ImpsCanSeeEachOthersRoles": "Impostors know the roles of other Impostors", + "ImpsCanSeeEachOthersAddOns": "Impostors can see each other's Add-ons", + "ImpKnowWhosMadmate": "Impostors know Madmates", + "MadmateKnowWhosImp": "Madmates know Impostors", + "MadmateKnowWhosMadmate": "Madmates know each other", + "ImpCanKillMadmate": "Impostors can kill Madmates", + "MadmateCanKillImp": "Madmates can kill Impostors", + "MadmateHasImpostorVision": "Madmates Have Impostor Vision", + "MadmateCanFixSabotage": "Madmates Can Fix Sabotages", + "EGCanGuessImp": "Can Guess Impostor Roles", + "GGCanGuessCrew": "Can Guess Crewmate Roles", + "EGCanGuessAdt": "Can Guess Add-Ons", + "EGCanGuessTaskDoneSnitch": "Can guess Snitch with All Tasks Done", + "GGCanGuessAdt": "Can Guess Add-Ons", + "GuesserCanGuessTimes": "Maximum number of guesses", + "GuesserTryHideMsg": "Try to hide guesser's command", + "GCanGuessImp": "Impostor can guess Impostor roles", + "GCanGuessCrew": "Crewmate can guess Crewmate roles", + "GCanGuessAdt": "Can guess Add-ons", + "GCanGuessTaskDoneSnitch": "Can guess Snitch with All Tasks Done", + "BountyTargetChangeTime": "Time Until Target Swaps", + "BountySuccessKillCooldown": "Kill Cooldown After Killing Bounty", + "BountyFailureKillCooldown": "Kill Cooldown After Killing Others", + "BountyShowTargetArrow": "Show arrow pointing towards target", + "DefaultShapeshiftCooldown": "Default Shapeshift Cooldown", + "ShapeshiftDuration": "Shapeshift Duration", + "ShapeshiftCooldown": "Shapeshift Cooldown", + "VitalsDuration": "Vitals Duration", + "VitalsCooldown": "Vitals Cooldown", + "DeadImpCantSabotage": "Impostors can't sabotage after they've died", + "VampireKillDelay": "Bite Kill Delay", + + "MareAddSpeedInLightsOut": "Additional Speed During Lights Out", + "MareKillCooldownInLightsOut": "Kill Cooldown During Lights Out", + + "MechanicSkillLimit": "Initial repair use limit", + "MechanicFixesDoors": "Can open all doors in the same building", + "MechanicFixesReactors": "Can Fix Both Reactors Alone", + "MechanicFixesOxygens": "Can Fix Both O2 Alone", + "MechanicFixesCommunications": "Can Fix Both Comms Alone In MIRA HQ", + "MechanicFixesElectrical": "Can Fix Lights With One Switch", + + "SheriffShowShotLimit": "Display Shot Limit next to Role Name", + "SheriffCanKill%role%": "Can Kill %role%", + "SheriffCanKillNeutrals": "Can Kill Neutrals", + "SheriffCanKillNeutralsMode": "Neutral Configuration", + "SheriffCanKillAll": "All ON", + "SheriffCanKillSeparately": "Individual Settings", + "In%team%": "(Team %team%)", + "SheriffMisfireKillsTarget": "Misfire Kills Target", + "SheriffShotLimit": "Max number of Kills", + "SheriffCanKillAllAlive": "Can Kill When No One Is Dead", + "SheriffCanKillCharmed": "Can kill Charmed players", + "SheriffCanKillEgoist": "Can Kill Egoists", + "SheriffCanKillSidekick": "Can Kill Sidekicks", + "SheriffCanKillLovers": "Can Kill Lovers", + "SheriffCanKillMadmate": "Can Kill Madmates", + "SheriffCanKillInfected": "Can Kill Infected players", + "SheriffCanKillContagious": "Can Kill Contagious players", + "SheriffSetMadCanKill": "Non-Crew Sheriff Configuration", + "SheriffMadCanKillImp": "Can kill Impostors", + "SheriffMadCanKillNeutral": "Can kill Neutrals", + "SheriffMadCanKillCrew": "Can kill Crewmates", + + "ReverieIncreaseKillCooldown": "Increase kill cooldown", + "ReverieMaxKillCooldown": "Max kill cooldown", + "ReverieMisfireSuicide": "Misfire on reaching max kill cooldown", + "ReverieResetCooldownMeeting": "Reset kill cooldown after meeting", + "ConvertedReverieKillAll": "Converted Reverie can kill anyone without repercussions", + + "VigilanteNotify": "You have become the very thing you swore to destroy", + + "DoctorTaskCompletedBatteryCharge": "Battery Duration", + "SnitchEnableTargetArrow": "See Arrow Towards Target", + "SnitchCanGetArrowColor": "See Colored Arrows based on Team Colors", + "SnitchCanFindNeutralKiller": "Can Find Neutral Killers", + "SnitchCanFindNeutralApoc": "Can Find Neutral Apocalypse", + "SnitchCanFindMadmate": "Can Find Madmates", + "SnitchRemainingTaskFound": "Remaining tasks to be known", + "SpeedBoosterUpSpeed": "Increase Speed by", + "SpeedBoosterTimes": "Max Boosts", + "MayorAdditionalVote": "Additional Votes Count", + "MayorHasPortableButton": "Mayor has a Mobile Emergency Button", + "MayorNumOfUseButton": "Max Number of Mobile Emergency Buttons", + "MayorHideVote": "Hide additional vote(s)", + "HideJesterVote": "Hide Jester's vote", + "MeetingsNeededForWin": "Meetings needed to win", + "ExecutionerCanTargetImpostor": "Can Target Impostors", + "ExecutionerCanTargetNeutralKiller": "Can Target Neutral Killing", + "ExecutionerChangeRolesAfterTargetKilled": "When Target Dies, Executioner becomes", + "ExecutionerCanTargetNeutralBenign": "Can Target Neutral Benign", + "ExecutionerCanTargetNeutralEvil": "Can Target Neutral Evil", + "ExecutionerCanTargetNeutralChaos": "Can Target Neutral Chaos", + "ExecutionerCanTargetNeutralApocalypse": "Can Target Neutral Apocalypse", + "SidekickSheriffCanGoBerserk": "Recruited Sheriff Can Go Nuts", + "LawyerCanTargetImpostor": "Can Target Impostors", + "LawyerCanTargetNeutralKiller": "Can Target Neutral Killers", + "LawyerCanTargetCrewmate": "Can Target Crewmates", + "LawyerCanTargetJester": "Can Target Jester", + "LawyerChangeRolesAfterTargetKilled": "When Target Dies, Lawyer becomes", + "LaywerShouldChangeRoleAfterTargetKilled": "Should Lawyer Change Role when Target Dies", + "LawyerTargetDeadInMeeting": "Your target was killed while meeting./nYour role may change depending on the settings.", + + "MercenaryLimit": "Time Until Suicide", + "ArsonistDouseTime": "Douse Duration", + "CanTerroristSuicideWin": "Can Win By Suicide", + "FireworkerMaxCount": "Fireworker Count", + "FireworkerRadius": "Firework Explosion Radius", + "SniperCanKill": "Sniper can kill with bullets remaining", + "SniperBulletCount": "Ammo", + "SniperPrecisionShooting": "Precise Shooting", + "SniperAimAssist": "Aim Assist", + "SniperAimAssistOneshot": "One shot Assist", + + "PyroDouseCooldown": "Douse cooldown", + "PyroBurnCooldown": "Kill cooldown after killing a doused player", + + "UndertakerFreezeDuration": "Freeze Duration", + + "NameDisplayAddons": "Display Add-Ons next to the role name", + "YourAddon": "Your Addons:", + "NoLimitAddonsNumMax": "Max Add-ons Per Player", + "LoverSpawnChances": "Spawn Chance of Lovers", + "AdditionRolesSpawnRate": "Spawn Chance", + "TorchVision": "Torch Vision", + "TorchAffectedByLights": "Torch's vision is affected by Lights Sabotage", + "BewilderVision": "Bewilder Vision", + "JesterVision": "Jester Vision", + "LawyerVision": "Lawyer Vision", + "FlashSpeed": "Flash Speed", + "LoverSuicide": "Lovers die together", + "NumberOfLovers": "Number of Lover Pairs (x2 members)", + "LoverKnowRoles": "Lovers know the roles of each other", + "TrapperBlockMoveTime": "Freeze time", + "BecomeTrapperBlockMoveTime": "Freeze time", + "ImpCanBeTrapper": "Impostors can become Beartrap", + "CrewCanBeTrapper": "Crewmates can become Beartrap", + "NeutralCanBeTrapper": "Neutrals can become Beartrap", + "ImpCanBeGravestone": "Impostors can become Gravestone", + "CrewCanBeGravestone": "Crewmates can become Gravestone", + "NeutralCanBeGravestone": "Neutrals can become Gravestone", + "TimeThiefDecreaseMeetingTime": "Lower Meeting Time by", + "TimeThiefLowerLimitVotingTime": "Minimum Voting Time", + "TimeThiefReturnStolenTimeUponDeath": "Return Stolen Time Upon Death", + "EvilTrackerCanSeeKillFlash": "Can See Kill-Flash", + "EvilTrackerCanSeeLastRoomInMeeting": "Can See Target's Last Room In Meeting", + "EvilTrackerTargetMode": "Can Set Target", + "EvilTrackerTargetMode.Never": "Never", + "EvilTrackerTargetMode.OnceInGame": "Once in game", + "EvilTrackerTargetMode.EveryMeeting": "Every Meeting", + "EvilTrackerTargetMode.Always": "Any time", + "WitchModeSwitchAction": "Switch Action via", + "NBareRed": "Neutral Benign can be red", + "NEareRed": "Neutral Evil can be red", + "NCareRed": "Neutral Chaos can be red", + "NAareRed": "Neutral Apocalypse can be red", + "CrewKillingRed": "Crewmate Killings can be red", + "PsychicCanSeeNum": "Max number of red names", + "PsychicFresh": "New red names every meeting", + "DetectiveCanknowKiller": "Can find the killer's role", + "EveryOneKnowSuperStar": "Everyone knows the Super Star", + "HackLimit": "Ability Use Count", + "ZombieSpeedReduce": "After a certain time, decrease the speed of Zombie by", + "NemesisCanKillNum": "Max number of revenges", + "ImpKnowCelebrityDead": "Impostors know when the Celebrity dies", + "NeutralKnowCelebrityDead": "Neutrals know when the Celebrity dies", + "JesterCanUseButton": "Can call emergency meetings", + "VectorVentNumWin": "Number of Vents to win", + "CanCheckCamera": "Can track camera usage", + "Arrogance/Juggernaut___DefaultKillCooldown": "Starting kill cooldown", + "Arrogance/Juggernaut___ReduceKillCooldown": "Reduce kill cooldown by", + "Arrogance/Juggernaut___MinKillCooldown": "Minimum kill cooldown", + "BomberRadius": "Bomb radius (5x is about half a Cafeteria)", + "NotifyGodAlive": "Inform players at meetings that God is still alive", + "TransporterTeleportMax": "Max number of teleports", + "TriggerKill": "Kill", + "TriggerVent": "Vent", + "TriggerDouble": "Double Click", + "TimeManagerIncreaseMeetingTime": "Increase voting time by", + "TimeManagerLimitMeetingTime": "Maximum Length of Meetings", + "MadTimeManagerLimitMeetingTime": "Mad Time Manager - Minimum Voting Time", + "AssignOnlyToCrewmate": "Assign only to Crewmates", + "WorkhorseNumLongTasks": "Additional Long Tasks", + "WorkhorseNumShortTasks": "Additional Short Tasks", + "SnitchCanBeWorkhorse": "Snitch can become Workhorse", + "InnocentCanWinByImp": "If their target was an Impostor then they win with them", + "ImpCanBeSchizophrenic": "Impostors can become Schizophrenic", + "CrewCanBeSchizophrenic": "Crewmates can become Schizophrenic", + "DualVotes": "Duplicate votes", + "ProtectCooldown": "Protect Cooldown", + "ProtectDur": "Protection Duration", + "ProtectVisToImp": "Protect Visible To Impostors", + "VeteranSkillCooldown": "Alert Cooldown", + "VeteranSkillDuration": "Alert Duration", + "BodyguardProtectRadius": "Protect Radius", + "ImpCanBeEgoist": "An Impostor can become Egoist", + "CrewCanBeEgoist": "Crewmates can become Egoist", + "ImpEgoistVisibalToAllies": "Impostors Can See Other Egoist Impostors", + "EgoistCountAsConverted": "Egoist count as converted neutral", + "ImpCanBeSeer": "Impostors can become Seer", + "CrewCanBeSeer": "Crewmates can become Seer", + "NeutralCanBeSeer": "Neutrals can become Seer", + "ImpCanBeGuesser": "Impostors can become Guesser", + "CrewCanBeGuesser": "Crewmates can become Guesser", + "NeutralCanBeGuesser": "Neutrals can become Guesser", + "ImpCanBeWatcher": "Impostors can become Watcher", + "CrewCanBeWatcher": "Crewmates can become Watcher", + "NeutralCanBeWatcher": "Neutrals can become Watcher", + "ImpCanBeBait": "Impostors can become Bait", + "CrewCanBeBait": "Crewmates can become Bait", + "NeutralCanBeBait": "Neutrals can become Bait", + "ImpCanBeRainbow": "Impostors can become Rainbow", + "NeutralCanBeRainbow": "Neutrals can become Rainbow", + "CrewCanBeRainbow": "Crewmates can become Rainbow", + "GuessRainbow": "He seems too obvious, doesn't he?", + "RainbowColorChangeCoolDown": "The cooldown for changing colors", + "RainbowInCamouflage": "Rainbow color changes during Camouflage", + "BaitDelayMin": "Minimum Report Delay", + "BaitDelayMax": "Maximum Report Delay", + "BaitDelayNotify": "Warn the killer about the upcoming self-report", + "BecomeBaitDelayNotify": "Warn the killer about the upcoming self-report", + "BaitNotification": "Reveal Bait at the first meeting", + "BaitAdviceAlive": "{0} is the Bait. Whoever kills the Bait will commit self report.", + "BaitCanBeReportedUnderAllConditions": "Bait Can Be Reported even if meeting is disabled during comms sabotage", + "DeceiverSkillCooldown": "Ability cooldown", + "DeceiverSkillLimitTimes": "Max number of uses", + "DeceiverAbilityLost": "Deceiver looses ability if it deceives player without kill button", + "PursuerSkillCooldown": "Ability cooldown", + "PursuerSkillLimitTimes": "Max number of uses", + "AddictSuicideTimer": "Time Until Suicide", + "GrenadierSkillCooldown": "Grenade Cooldown", + "GrenadierSkillDuration": "Grenade Duration", + "GrenadierCauseVision": "Lowered vision", + "GrenadierCanAffectNeutral": "Can affect Neutrals", + "TicketsPerKill": "Votes Increase Amount Per Kill", + "GangsterRecruitCooldown": "Recruit cooldown", + "GangsterRecruitLimit": "Recruit limit", + "KamikazeMaxMarked": "Max Marked", + "RevolutionistDrawTime": "Tag Duration", + "RevolutionistCooldown": "Tag Cooldown", + "RevolutionistDrawCount": "Amount of Players needed to Tag", + "RevolutionistKillProbability": "Tagged player sacrifice probability", + "RevolutionistVentCountDown": "Time to Vent", + "PelicanKillCooldown": "Eat Cooldown", + "Pelican.TargetCannotBeEaten": "Target cannot be eaten", + "MadSnitchTasks": "Snitch Tasks", + "MedicWhoCanSeeProtect": "Who can see shield", + "MedicKnowShieldBroken": "Who sees kill attempt", + "Medic_SeeMedicAndTarget": "Medic+Shielded", + "Medic_SeeMedic": "Medic", + "Medic_SeeTarget": "Shielded", + "Medic_SeeNoOne": "Nothing", + "MedicShieldDeactivatesWhenMedicDies": "Shield deactivates when the Medic dies", + "MedicShielDeactivationIsVisible": "Shield deactivation is visible", + "MedicShieldDeactivationIsVisible_DeactivationImmediately": "Immediately", + "MedicShieldDeactivationIsVisible_DeactivationAfterMeeting": "After Meeting", + "MedicShieldDeactivationIsVisible_DeactivationIsVisibleOFF": "OFF", + "MedicResetCooldown": "On kill attempt, reset murderer's cooldown to", + "MedicShieldedCanBeGuessed": "Guessing ignores Medic shield", + "FortuneTellerSkillLimit": "Max number of ability uses", + "MadmateSpawnMode": "Madmate spawning mode", + "MadmateSpawnMode.Assign": "Assign", + "MadmateSpawnMode.FirstKill": "First Kill", + "MadmateSpawnMode.SelfVote": "Self Vote", + "MadmateCountMode": "Madmates count as", + "MadmateCountMode.None": "Nothing", + "MadmateCountMode.Imp": "Impostors", + "MadmateCountMode.Original": "Original Team", + + "SnatchesWin": "Snatches victory", + "DemonKillCooldown": "Attack Cooldown", + "DemonHealthMax": "Player max health", + "DemonDamage": "Damage ", + "DemonSelfHealthMax": "Demon max health", + "DemonSelfDamage": "Demon damage received", + "LightningConvertTime": "Duration of the transformation to Quantum Ghost", + "LightningKillCooldown": "Lightning Cooldown", + "LightningKillerConvertGhost": "Killer can transform into Quantum Ghost", + "CanCountNeutralKiller": "When Crewmates win by killing a Neutral player, they can snatch the victory", + "GreedyOddKillCooldown": "Odd-Numbered kill cooldown", + "GreedyEvenKillCooldown": "Even-Numbered kill cooldown", + "WorkaholicCannotWinAtDeath": "Can't win after they died", + "WorkaholicVisibleToEveryone": "Everyone knows who the Workaholic is", + "WorkaholicGiveAdviceAlive": "Advice at first meeting if alive, can win after death, ghost tasks ON", + "DoctorVisibleToEveryone": "Everyone knows who the Doctor is", + "CursedWolfGuardSpellTimes": "Amount of Cursed Shields", + "Jinx/CursedWolf___KillAttacker": "Kill attacker when ability is remaining", + "JinxSpellTimes": "Amount of Jinx Spells", + "CollectorCollectAmount": "Required number of votes", + "GlitchCanVote": "Can vote", + "QuickShooterShapeshiftCooldown": "Shapeshift Cooldown", + "MeetingReserved": "Max Bullets reserved for a meeting", + "AccurateCheckMode": "Can know specific role when tasks are not done", + "RandomActiveRoles": "Show random active roles in Fortune Teller hints", + "CamouflageCooldown": "Camouflage Cooldown", + "CamouflageDuration": "Camouflage Duration", + "EraseLimit": "Max Erases", + "EraserHideVote": "Hide Eraser Votes", + "NinjaMarkCooldown": "Mark Cooldown", + "NinjaAssassinateCooldown": "Ass​assinate Cooldown", + "NinjaModeDouble": "Double Click = Kill, Single Click = Mark", + "JudgeCanTrialnCrewKilling": "Can trial Crewmate Killing", + "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", + "JudgeTryHideMsg": "Hide Judge's commands", + "JudgeTrialLimitPerMeeting": "Max Trials per Meeting", + "JudgeCanTrialMadmate": "Can trial Madmates", + "JudgeCanTrialCharmed": "Can trial Charmed players", + "JudgeDead": "Sorry, you can't trial after death.", + "JudgeTrialMax": "\nNo more trials left!", + "Judge_LaughToWhoTrialSelf": "God, I didn't think the Judges would be so blind that they wouldn't even see that they had sentenced themselves.", + "Judge_TrialKill": "{0} was judged.", + "Judge_TrialKillTitle": "COURT", + "Judge_TrialHelp": "Command: /tl [player ID]\nYou can see the players' IDs before the players' names.\nOr use /id to view the list of all player IDs.", + "Judge_TrialNull": "Please choose a living player for the trial", + "VeteranSkillMaxOfUseage": "Max number of Alerts", + "SwooperCooldown": "Swoop Cooldown", + "SwooperDuration": "Swoop Duration", + "WraithCooldown": "Vanish Cooldown", + "WraithDuration": "Vanish Duration", + "BastionNotify": "A bomb was set off", + "EnteredBombedVent": "That vent was bombed!", + "BastionVentButtonText": "Bomb", + "BombsClearAfterMeeting": "Bombs clear after meetings", + "BastionMaxBombs": "(Initial) Maximum bombs", + "VentBombSuccess": "Bomb has been planted", + "LowLoadMode": "Low Load Mode", + "ShowLobbyCode": "Show lobby code in Discord status", + "BKProtectDuration": "Protection Duration", + "FollowerMaxBetTimes": "Maximum Number of Follows", + "FollowerBetCooldown": "Follow Cooldown", + "FollowerMaxBetCooldown": "Maximum Follow Cooldown", + "FollowerBetCooldownIncrese": "Increase Cooldown per 1 follow by", + "FollowerKnowTargetRole": "Follower knows their target's role", + "FollowerBetTargetKnowFollower": "Follower target knows who the Follower is", + "FortuneTellerHideVote": "Hide Fortune Teller's Votes", + "CultistCharmCooldown": "Charm Cooldown", + "CultistCharmCooldownIncrese": "Increases Charm Cooldown For Each Charm", + "CultistCharmMax": "Maximum Number Of Charm", + "CultistKnowTargetRole": "Know Charmed Player's Role", + "CultistTargetKnowOtherTarget": "Charmed players know each other", + "CultistCanCharmNeutral": "Neutral Roles can be Charmed", + "InfectiousBiteCooldown": "Infect Cooldown", + "KnowTargetRole": "Knows role of target", + "TargetKnowsLawyer": "Target knows their Lawyer", + "InfectiousBiteMax": "Maximum Infections", + "InfectiousKnowTargetRole": "Know infected player's role", + "InfectiousTargetKnowOtherTarget": "Infected players know each other", + "DoubleClickKill": "Double click to kill the target", + + "VirusInfectMax": "Maximum Number Of Spreads", + "VirusKnowTargetRole": "Know Contagious Player's Role", + "VirusTargetKnowOtherTarget": "Contagious players know each other", + "VirusKillInfectedPlayerAfterMeeting": "Contagious player dies after meeting", + "Virus_ContagiousCountMode": "Contagious players count as", + "Virus_ContagiousCountMode_None": "Nothing", + "Virus_ContagiousCountMode_Virus": "Virus", + "Virus_ContagiousCountMode_Original": "Original Team", + "VirusNoticeTitle": "[ Infected Corpse! ]", + "VirusNoticeMessage": "The body your reported was infected by the Virus! You are now part of Team Virus. Help the Virus win the game.", + "VirusNoticeMessage2": "The body your reported was infected by the Virus! Vote the Virus out this meeting or you will die.", + + "Cultist_CharmedCountMode": "Charmed players count as", + "Cultist_CharmedCountMode_None": "Nothing", + "Cultist_CharmedCountMode_Cultist": "Cultist", + "Cultist_CharmedCountMode_Original": "Original Team", + + "JackalCanWinBySabotageWhenNoImpAlive": "When all Impostors are dead, the Jackal wins by sabotage instead", + "JackalResetKillCooldownWhenPlayerGetKilled": "Reset kill cooldown if someone gets killed by another player", + "JackalResetKillCooldownOn": "Kill Cooldown On Reset", + "JackalCanRecruitSidekick": "Can recruit Sidekick", + "JackalSidekickRecruitLimit": "Maximum Number Of Recruits", + "Jackal_SidekickCountMode": "Sidekicks count as", + "Jackal_SidekickCountMode_None": "Nothing", + "Jackal_SidekickCountMode_Jackal": "Jackal", + "Jackal_SidekickCountMode_Original": "Original Team", + "Jackal_SidekickAssignMode": "Sidekick Assign Mode", + "Jackal_SidekickAssignMode_SidekickAndRecruit": "Sidekick+Recruit", + "Jackal_SidekickAssignMode_Sidekick": "Sidekick Only", + "Jackal_SidekickAssignMode_Recruit": "Recruit Only", + "JackalWinWithSidekick": "Jackal can win with Sidekick's team", + "Jackal_SidekickCanKillSidekick": "Sidekicks can kill other Sidekicks", + "Jackal_SidekickCanKillJackal": "Sidekick can kill Jackal", + "JackalCanKillSidekick": "Jackal can kill Sidekick", + + "ImpCanBeNecroview": "Impostors can become Necroview", + "CrewCanBeNecroview": "Crewmates can become Necroview", + "NeutralCanBeNecroview": "Neutrals can become Necroview", + "ImpCanBeInLove": "Impostors can be in love", + "CrewCanBeInLove": "Crewmates can be in love", + "NeutralCanBeInLove": "Neutrals can be in love", + "ImpCanBeOblivious": "Impostors can become Oblivious", + "CrewCanBeOblivious": "Crewmates can become Oblivious", + "NeutralCanBeOblivious": "Neutrals can become Oblivious", + "ImpCanBeTiebreaker": "Impostors can become Tiebreaker", + "CrewCanBeTiebreaker": "Crewmates can become Tiebreaker", + "NeutralCanBeTiebreaker": "Neutrals can become Tiebreaker", + "HexesLookLikeSpells": "Hexes appear as spells", + "HexButtonText": "Hex", + "ObliviousBaitImmune": "Immune to Bait", + "ImpCanBeOnbound": "Impostors can become Onbound", + "CrewCanBeOnbound": "Crewmates can become Onbound", + "NeutralCanBeOnbound": "Neutrals can become Onbound", + + "ImpCanBeRebound": "Impostors can become Rebound", + "CrewCanBeRebound": "Crewmates can become Rebound", + "NeutralCanBeRebound": "Neutrals can become Rebound", + + "CrewCanBeMundane": "Crewmates can become Mundane", + "NeutralCanBeMundane": "Neutrals can become Mundane", + "GuessedAsMundane": "You're Mundane.\nYou can't guess until you finish all the tasks", + + "ImpCanBeUnreportable": "Impostors can become Disregarded", + "CrewCanBeUnreportable": "Crewmates can become Disregarded", + "NeutralCanBeUnreportable": "Neutrals can become Disregarded", + "PacifistCooldown": "Ability Cooldown", + "PacifistMaxOfUseage": "Max Number of Ability Uses", + "CoronerArrowsPointingToDeadBody": "Arrows pointing to dead bodies", + "CoronerLeaveDeadBodyUnreportable": "Bodies the Coroner uses can't be reported", + "CoronerInformKillerBeingTracked": "Inform the Killer that he gets tracked", + "TrackerHideVote": "Hide Tracker Votes", + "TrackerCanGetArrowColor": "Can See Colored Arrows", + + "PresidentAbilityUses": "Max Number of Ability Uses", + "PresidentCanBeGuessedAfterRevealing": "President can be guessed after revealing", + "HidePresidentEndCommand": "Hide President's commands", + "NeutralsSeePresident": "Neutrals can see revealed President", + "MadmatesSeePresident": "Madmates can see revealed President", + "ImpsSeePresident": "Impostors can see revealed President", + "PresidentDead": "Sorry, you can't force end the meeting after death.", + "PresidentEndMax": "No more force end meeting uses left!", + "PresidentRevealMax": "You have already revealed yourself...", + "PresidentRevealed": "[{0}] has chose to reveal themselves as President!", + "GuessPresident": "President has revealed themselves, you can't guess them.", + "PresidentRevealTitle": "PRESIDENT REVEAL", + + "LuckyProbability": "Probability of surviving a kill", + "ImpCanBeLucky": "Impostors can become Lucky", + "CrewCanBeLucky": "Crewmates can become Lucky", + "NeutralCanBeLucky": "Neutrals can become Lucky", + "ImpCanBeFool": "Impostors can become Fool", + "CrewCanBeFool": "Crewmates can become Fool", + "NeutralCanBeFool": "Neutrals can become Fool", + "ImpCanBeDoubleShot": "Impostors can have Double Shot", + "CrewCanBeDoubleShot": "Crewmates can have Double Shot", + "NeutralCanBeDoubleShot": "Neutrals can have Double Shot", + "MimicCanSeeDeadRoles": "Mimic can see the roles of dead players", + "DisableReportWhenCamouflageIsActive": "Disable body reporting when сamouflage is active", + "CanUseCommsSabotage": "Can use comms sabotage", + "ModTag": "Moderator♥", + "ApplyModeratorList": "Apply Moderator List", + "VipTag": "VIP★", + "ApplyVipList": "Apply VIP List", + "AllowSayCommand": "Allow moderators to use /say command", + "KickCommandDisabled": "The kick command is currently disabled.", + "KickCommandNoAccess": "You do not have access to the kick command.", + "KickCommandInvalidID": "Invalid player ID specified.\nPlease use '/kick [playerID] [reseaon]' to kick a player.\nExample :- /kick 5 not following rules", + "KickCommandKickHost": "You are not permitted to kick the host.", + "KickCommandKickMod": "You are not permitted to kick other moderators.", + "KickCommandKicked": "was kicked from the game by ", + "KickCommandKickedRole": "Their role was", + "BanCommandDisabled": "The ban command is currently disabled.", + "BanCommandNoAccess": "You do not have access to the ban command.", + "BanCommandInvalidID": "Invalid player ID specified.\nPlease use '/ban [playerID] [reason]' to ban a player.\nExample :- /ban 5 not following rules ", + "BanCommandBanHost": "You are not permitted to ban the host.", + "BanCommandBanMod": "You are not permitted to ban other moderators.", + "BanCommandBanned": "was banned from the game by ", + "BanCommandBannedRole": "Their role was", + "BanCommandNoReason": "No reason specified.\nPlease use '/ban [playerID] [reason]\nExample :- /ban 5 not following rules", + "ColorCommandDisabled": "The modcolor command is currently disabled.", + "ColorCommandNoAccess": "You do not have access to the modcolor command.", + "ColorCommandNoLobby": "You can only use the command in Lobby when the game hasn't started.", + "ColorInvalidHexCode": "Invalid hexcode specified.\nPlease use '/modcolor [hexcode]' to change color of MODERATOR♥.\nExample :- /modcolor 33ccff", + "ColorInvalidGradientCode": "Invalid hexcode specified.\nPlease use '/modcolor [hexcode][hexcode]' to change color of MODERATOR♥.\nExample :- /modcolor 33ccff ff99cc", + "VipColorCommandDisabled": "The vipcolor command is currently disabled.", + "VipColorCommandNoAccess": "You do not have access to the vipcolor command.", + "VipColorCommandNoLobby": "You can only use the command in Lobby when the game hasn't started.", + "VipColorInvalidHexCode": "Invalid hexcode specified.\nPlease use '/vipcolor [hexcode]' to change color of VIP★.\nExample :- /vipcolor 33ccff", + "VipColorInvalidGradientCode": "Invalid hexcode specified.\nPlease use '/vipcolor [hexcode][hexcode]' to change color of VIP★.\nExample :- /vipcolor 33ccff ff99cc", + "TagColorInvalidHexCode": "Invalid hexcode specified.\nPlease use '/tagcolor [hexcode]' to change color of your tag.\nExample :- /tagcolor ff00ff", + "midCommandDisabled": "The mid command is currently disabled.", + "midCommandNoAccess": "You do not have access to the mid command.", + "DisableVoteBan": "Disable VoteKick System", + "WarnCommandDisabled": "The warn command is currently disabled.", + "WarnCommandNoAccess": "You do not have access to the warn command.", + "WarnCommandInvalidID": "Invalid player ID specified.\nPlease use '/warn [playerID] [reason]' to warn a player. \nExample :- /warn 5 lava chatting", + "WarnCommandWarnHost": "You are not permitted to warn the host.", + "WarnCommandWarnMod": "You are not permitted to warn other moderators.", + "WarnCommandWarned": "has been warned. There will be no more warnings given and appropriate action will be taken \n ", + "WarnExample": "Use /warn [id] [reason] in future. \nExample :-\n /warn 5 lava chatting", + "SayCommandDisabled": "The say command is currently disabled.", + "MessageFromModerator": "MODERATOR", + "DeathReason.Kill": "Kill", + "DeathReason.Vote": "Ejected", + "DeathReason.Suicide": "Suicide", + "DeathReason.Spell": "Spelled", + "DeathReason.Cursed": "Cursed", + "DeathReason.Hex": "Hexed", + "DeathReason.Bite": "Bitten", + "DeathReason.Poison": "Poisoned", + "DeathReason.Gambled": "Guessed", + "DeathReason.FollowingSuicide": "Heartbroken", + "DeathReason.Bombed": "Exploded", + "DeathReason.Misfire": "Misfire", + "DeathReason.Torched": "Burned", + "DeathReason.Sniped": "Sniped", + "DeathReason.Execution": "Executed", + "DeathReason.Disconnected": "Disconnected", + "DeathReason.Fall": "Fall", + "DeathReason.Revenge": "Revenge", + "DeathReason.Eaten": "Eaten", + "DeathReason.Sacrifice": "Victim", + "DeathReason.Quantization": "Quantization", + "DeathReason.Overtired": "Overtired", + "DeathReason.Ashamed": "Ashamed", + "DeathReason.PissedOff": "Destroyed", + "DeathReason.Dismembered": "Dismembered", + "DeathReason.LossOfHead": "Strangled", + "DeathReason.Trialed": "Judged", + "DeathReason.Infected": "Infected", + "DeathReason.Jinx": "Jinxed", + "DeathReason.Pirate": "Plundered", + "DeathReason.Shrouded": "Shrouded", + "DeathReason.etc": "Other", + "DeathReason.Mauled": "Mauled", + "DeathReason.Hack": "Hacked", + "DeathReason.Curse": "Cursed", + "DeathReason.Drained": "Drained", + "DeathReason.Shattered": "Shattered", + "DeathReason.Trap": "Trapped", + "DeathReason.Targeted": "Targeted", + "DeathReason.Retribution": "Retribution", + "DeathReason.Slice": "Sliced", + "DeathReason.BloodLet": "Bleed", + "DeathReason.Armageddon": "Armageddon", + "DeathReason.Starved": "Starved", + "OnlyEnabledDeathReasons": "Only Enabled Death Reasons", + "Alive": "Alive", + "Win": " Wins!", + + "Last-": "Last ", + "Madmate-": "Madmate ", + "Recruit-": "Recruit ", + "Charmed-": "Charmed ", + "Soulless-": "Soulless ", + "Infected-": "Infected ", + "Contagious-": "Contagious ", + "Admired-": "Admired ", + + "DeputyHandcuffCooldown": "Handcuff Cooldown", + "DeputyHandcuffMax": "Maximum Handcuffs", + "DeputyHandcuffedPlayer": "Handcuffed target", + "HandcuffedByDeputy": "You were handcuffed!", + "DeputyInvalidTarget": "Target cannot be handcuffed", + "DeputyHandcuffText": "Handcuff", + "DeputyHandcuffCDForTarget": "Kill Cooldown for handcuffed player", + + "RejectShapeshift.AbilityWasUsed": "Ability was used", + "ShowShapeshiftAnimations": "Show Shapeshift animations", + + "EscapisMtarkedPosition": "You marked self position", + + "InvestigateCooldown": "Investigate Cooldown", + "InvestigateMax": "Maximum Investigations", + "InvestigateRoundMax": "Maximum Investigations in one round", + + "Color.Red": "Red", + "Color.Green": "Green", + "Color.Gray": "Gray", + "InvestigatorInvestigatedPlayer": "Player Investigated", + "InvestigatorInvalidTarget": "Can not investigate", + "InvestigatorButtonText": "Check", + + "Investigator.Suspicion": "Suspicion", + "Investigator.Role": "Role", + "SabotageCooldownControl": "Sabotage Cooldown Control", + "SabotageCooldown": "Sabotage Cooldown", + "SabotageTimeControl": "Sabotage Duration Control", + "SkeldReactorTimeLimit": "The Skeld Reactor Time Limit", + "SkeldO2TimeLimit": "The Skeld O2 Time Limit", + "MiraReactorTimeLimit": "MIRA HQ Reactor Time Limit", + "MiraO2TimeLimit": "MIRA HQ O2 Time Limit", + "PolusReactorTimeLimit": "Polus Reactor Time Limit", + "AirshipReactorTimeLimit": "Airship Reactor Time Limit", + "FungleReactorTimeLimit": "The Fungle Reactor Time Limit", + "FungleMushroomMixupDuration": "The Fungle Mushroom Mixup Duration", + "CommandList": "★ Command list:", + "Command.now": "→ Display active Settings", + "Command.roles": "[RoleName] → Display Role description", + "Command.myrole": "→ Displays a description of your role", + "Command.lastresult": "→ Display match results", + "Command.winner": "→ Display winners", + "CommandOtherList": "● Other commands:", + "Command.color": "[Color] → Change your color", + "Command.rename": "[Name] → Change Host Name", + "Command.quit": "→ I don't want to enter this lobby anymore", + "CommandHostList": "▲ Host Commands:", + "Command.say": "[Content] → Send message as Host", + "Command.mw": "[Seconds] → Set the message waiting duration", + "Command.solvecover": "→ Fix an issue where role names overlap the messages", + "Command.kill": "[Player ID] → Kill assigned player", + "Command.exe": "[Player ID] → Eject assigned player", + "Command.level": "[Level] → Change your in-game level", + "Command.idlist": "→ Display a list of player IDs", + "Command.qq": "→ Lobby will be posted on QQ website (China only)", + "Command.dump": "→ Output Log to Desktop", + "Command.death": "→ Display info on how you died", + "Command.icons": "Icon Meanings\n† - This player was spelled by a Witch and will die if the Witch is not killed by the end of the meeting\n乂 - This player was hexed by a Hex Master and will die if the Hex Master is not killed by the end of the meeting\n❖ - This player was hexed by an Occultist and will die if the Occultist is not killed by the end of the meeting\n◈ - This player was shrouded by a Shroud and will die if the Shroud is not killed by the end of the meeting\n⦿ - This player is being dueled by a Pirate\n♥ - This player is a Lover\n⚠ - This player is a Snitch who has finished their tasks", + "Command.iconinfo": "→ Display info on in-meeting icons", + "Command.iconhelp": "→ Display info on in-meeting icons to everyone", + "Remaining.ImpostorCount": "Impostors left: ", + "Remaining.NeutralCount": "Neutral Killers left: ", + "EnableKillerLeftCommand": "Enable use of /kcount command", + "SeeEjectedRolesInMeeting": "See ejected roles in meetings", + + "SkillUsedLeft": "You have activated your skill to call a meeting. \nRemaining amount of uses left:", + "NemesisDeadMsg": "The death of the Nemesis means the beginning of the revenge. \nPlease use /rv + [player ID] to kill the specified player \nYou can see player IDs in front of their names. \nOr type /rv to get a list of player IDs", + "NemesisAliveKill": "Revenge for the Nemesis can only begin after their death.", + "NemesisKillDead": "Choose a living player to take revenge", + "NemesisKillSucceed": "[{0}] was killed by the Nemesis!", + "NemesisKillDisable": "Sorry, but according to Host's settings, Nemesis revenge is prohibited in this game", + + "CelebrityDead": "Shock! Celebrity[{0}]has unfortunately been mercilessly killed in the recent period of time!", + "CyberDead": "Oh no! It appears the Cyber, {0}, has died recently.", + "DetectiveNoticeVictim": "According to your investigation,\nthe victim ([{0}]) had the role [{1}]", + "DetectiveNoticeKiller": "\nThe killer's role is [{0}]", + "DetectiveNoticeKillerNotFound": "The Detective couldn't find evidence leading to a murderer, this is most likely suicide.", + "GodNoticeAlive": "During the meeting, each player felt a revelation from heaven, and it turned out that God is still alive!", + "WorkaholicAdviceAlive": "It's not recommended to kill or vote [{0}] out. Doing so will help them finish their tasks quicker.", + "GuessDead": "Sorry, but it's impossible to guess roles after your death", + "GuessSuperStar": "The Super Star can't be guessed.... you thought it would be that easy, right?", + "GuessNotifiedBait": "Bait can't be guessed because it was announced, you thought it would be that easy, right?", + "GuessGM": "Guessing the GM is impossible because they're already dead.... And why would you do that to the poor Host?", + "GuessGuardianTask": "You can't guess a Guardian who has finished their tasks.", + "GuessMarshallTask": "You can't guess a Marshall who has finished their tasks.", + "GuessObviousAddon": "Sorry, obvious add-ons cannot be guessed.", + "GuessAdtRole": "Unfortunately, the Host's settings do not allow you to guess add-ons", + "GuessImpRole": "Unfortunately, the Host's settings do not allow Impostors to guess Impostor roles.", + "GuessCrewRole": "Unfortunately, the Host's settings do not allow crewmates to guess crewmate roles.", + "GuessKill": "{0} was guessed", + "GuessNull": "Please select an ID of a living player to guess their role", + "GuessHelp": "Instructions: /bt [Player ID] [Role Name] \nExample: /bt 3 Bait \nYou can see the player IDs before everyone's names \n or use the /id command to list the player IDs", + "GGGuessMax": "You've reached the limit of maximum guesses, you can't guess anymore!", + "EGGuessMax": "You've reached the limit of maximum guesses, you can't guess anymore!", + "EGGuessSnitchTaskDone": "You thought you could guess the Snitch when all of their tasks are done? Nice try.... You're not getting out of this that easily.", + "GuessDoubleShot": "You guessed a role incorrectly, but you have Double Shot so you get another chance!", + "LaughToWhoGuessSelf": "Tried to guess, who tried to self-guess! It's you! Ahah!", + "GuessDisabled": "Sorry, the host restricted guessing for your role.", + "GuessWorkaholic": "Sorry, you can't guess a revealed Workaholic as that would be unfair.", + "GuessDoctor": "Sorry, you can't guess a revealed Doctor as that would be unfair.", + "GuessMayor": "Sorry, you can't guess a revealed Mayor as that would be unfair.", + "GuessKnighted": "Sorry, Monarchs cannot guess Knighted.", + "GuessMonarch": "There's a knighted player alive, so the Monarch cannot be guessed.", + "GuessShielded": "Sorry, you can't guess the player who is shielded by Medic", + "MayorRevealWhenDoneTasks": "Mayor is revealed to everyone on task completion", + "MimicDeadMsg": "Mimic's hint: ", + "FortuneTellerCheck": "According to your fortune...", + "FortuneTellerCheckLimit": "Reminder: You have {0} fortunes left", + "FortuneTellerCheckSelfMsg": "Wow, you found yourself... All you see is a reflection.", + "FortuneTellerCheckReachLimit": "You've run out of fortunes.", + "FortuneTellerAlreadyCheckedMsg": "You've already checked the player", + "EraserEraseNotice": "You erased {0}.\nTheir role will be deactivated after the meeting.", + "EraserEraseBaseImpostorOrNeutralRoleNotice": "Oops, your target cannot be erased!", + "EraserEraseSelf": "Unfortunately, you can't erase yourself.... Wait, why would you do that in the first place?!", + "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}.", + + "MediumContactLimit": "Max number of contacts (ability uses)", + "MediumOnlyReceiveMsgFromCrew": "Receive messages only from Crewmates (including Madmates and Charmed Players)", + "MediumTitle": "MEDIUM", + "MediumHelp": "/ms yes to agree\n/ms no to disagree", + "MediumYes": "You thought you heard a quiet voice from another world affirming the answer to your question.", + "MediumNo": "You thought you heard a quiet voice from another world denying the answer to your question.", + "MediumDone": "You successfully responded to the Medium.", + "MediumNotifyTarget": "{0}, the Medium, has established contact with you. Before the end of this meeting, you have a chance to respond to their question. Type one of the following commands to answer:\nConfirm: /ms yes\nDeny: /ms no", + "MediumNotifySelf": "You established contact with {0}, please ask questions to them and wait for them to respond.\n\nRemaining ability uses: {1}", + "MediumKnowPlayerDead": "Someone died somewhere", + + "ByBard": "by Bard", + "ByBardGetFailed": "oops, I seem to be out of inspiration.", + "GangsterSuccessfullyRecruited": "You successfully recruited a player", + "GangsterRecruitmentFailure": "Target cannot be recruited", + "BeRecruitedByGangster": "You have been recruited by the Gangster", + "KamikazeHostage": "Can't hold target hostage", + "VeteranOnGuard": "Ability in use", + "VeteranOffGuard": "Ability expired, {0} uses remain", + "VeteranMaxUsage": "Ability use limit reached", + "GrenadierSkillInUse": "Ability in use", + "GrenadierSkillStop": "Ability expired", + "TicketsStealerGetTicket": "You've got {0} votes", + "BecomeMadmateCuzMadmateMode": "You became a Madmate because you died", + "SpeedBoosterTaskDone": "Your speed is {0} now", + "SpeedBoosterSpeedLimit": "You reached your maximum speed (3x)", + "CleanerCleanBody": "The body has been cleaned", + "QuickShooterStoraging": "Bullets stored successfully", + "VampireTargetDead": "Target died", + "PoisonerTargetDead": "Target died", + "BloodlustAdded": "Your bloodlust is now active!", + "WarlockNoTarget": "Manipulation failed due to no target", + "WarlockNoTargetYet": "You haven't mark a target.", + "WarlockTargetDead": "Manipulation failed due to target dead", + "WarlockControlKill": "Target died", + "OnCelebrityDead": "Warning: Celebrity death!", + "OnCyberDead": "Warning: Cyber died!", + "TeleportedInRndVentByDisperser": "Everyone was teleported to vents", + "TeleportedByTransporter": "Swapping places with: {0}", + "ErrorTeleport": "Teleport failed", + "LostRoleByEraser": "You lost your role because of the Eraser", + "KilledByScavenger": "You were killed by the Scavenger and thus teleported off-map", + "SnitchDoneTasks": "Call a meeting to find the impostors", + "SwooperCanVent": "Vent to turn invisible", + "SwooperInvisState": "You're invisible", + "SwooperInvisStateOut": "You're now visible", + "SwooperInvisInCooldown": "Swoop cooldown isn't up yet, swooping failed", + "SwooperInvisStateCountdown": "Invisibility will expire after {0}s", + "SwooperInvisCooldownRemain": "Swoop Cooldown: {0}s", + "WraithCanVent": "Vent to turn invisible", + "WraithInvisState": "You are invisible", + "WraithInvisStateOut": "You are visible again", + "WraithInvisInCooldown": "Ability still on cooldown, vanish failed", + "WraithInvisStateCountdown": "Invisibility will expire in {0}s", + "WraithInvisCooldownRemain": "{0}s left in invisibility", + "BKInProtect": "Currently immortal", + "BKProtectOut": "Shield expired", + "BKSkillTimeRemain": "You're immune for {0} seconds", + "BKSkillNotice": "Kill a player to enter immune status", + "BKOffsetKill": "Someone tried killing you", + "MedicKillerTryBrokenShieldTargetForMedic": "Someone tried killing the player you shielded!", + "MedicKillerTryBrokenShieldTargetForTarget": "Someone tried killing you!", + "FollowerBetPlayer": "You're now following your target", + "FollowerBetOnYou": "The Follower is now following you", + "CultistCharmedPlayer": "You successfully charmed a player", + "CharmedByCultist": "You have been charmed by the Cultist", + "CultistInvalidTarget": "Target cannot be charmed", + "KillBaitNotify": "You'll self-report in {0}s", + "InfectiousInvalidTarget": "Target cannot be infected", + "BittenByInfectious": "You were infected by the Infectious!", + "InfectiousBittenPlayer": "You successfully infected a player", + "GuessNotAllowed": "Sorry, your role does not have access to guessing.", + "GuessOnbound": "This player has the Onbound add-on, so your guess on them was canceled.", + "GuessPhantom": "You can't guess a Phantom, that allows them to win!", + "PacifistOnGuard": "Ability used, {0} uses remain", + "PacifistMaxUsage": "Ability use limit reached", + "PacifistSkillNotify": "Pacifist reset your kill cooldown", + "BeRecruitedByJackal": "You have been recruited by the Jackal", + "CoronerTrackRecorded": "Track recorded", + "CoronerNoTrack": "Nothing to track", + "CoronerIsTrackingYou": "The Coroner is tracking you!", + "TrackerLastRoomMessage": "The evaluation of your tracking showed that the last room in which your target was located was:[{0}]", + "MerchantAddonDelivered": "Add-on sold", + "MerchantAddonSell": "The Merchant sold you a new Add-on", + "MerchantAddonSellFail": "Could not sell an Add-on", + "BribedByMerchant": "The Merchant bribed you, you can't kill him", + "BribedByMerchant2": "You cannot guess the Merchant after he bribed you.", + "MerchantKillAttemptBribed": "An attempted killing was averted by bribery", + "TrapTrapsterBody": "Trap Trapster's body", + "TrapConsecutiveBodies": "Trap consecutive bodies", + "HauntedByEvilSpirit": "Haunted by an Evil Spirit", + "MonarchKnightCooldown": "Knight Cooldown", + "MonarchKnightMax": "Maximum Knights", + "MonarchKnightedPlayer": "You successfully knighted a player!", + "KnightedByMonarch": "You have been knighted by a Monarch!", + "MonarchInvalidTarget": "Target cannot be knighted", + "GhostTransformTitle": "Your Role Has Transformed!", + "SpiritcallerNoticeTitle": "YOU TURNED INTO AN EVIL SPIRIT ", + "SpiritcallerNoticeMessage": "The Spiritcaller has killed you and turned you into an Evil Spirit. Your task now is to help the Spiritcaller to victory by using your spook button to hinder other players or to protect the Spiritcaller. Use /m for more information.", + "OverseerRevealCooldown": "Reveal Cooldown", + "OverseerRevealTime": "Reveal Time", + "OverseerVision": "Overseer Vision", + "MerchantMaxSell": "Max number of Add-ons to sell", + "MerchantMoneyPerSell": "Amount of money earned for selling an Add-on", + "MerchantMoneyRequiredToBribe": "Amount of money required to bribe a killer", + "MerchantNotifyBribery": "Inform Merchant when a killer gets bribed", + "MerchantTargetCrew": "Can sell to Crewmates", + "MerchantTargetImpostor": "Can sell to Impostors", + + "MerchantTargetNeutral": "Can sell to Neutrals", + "MerchantSellHelpful": "Can sell Helpful Add-ons", + "MerchantSellHarmful": "Can sell Harmful Add-ons", + "MerchantSellMixed": "Can sell Mixed Add-ons", + "MerchantSellExperimental": "Can sell experimental Add-ons", + "MerchantSellHarmfulToEvil": "Can sell Harmful Add-ons only to Evil", + "MerchantSellHelpfulToCrew": "Can sell Helpful Add-ons only to Crew", + "MerchantSellOnlyEnabledAddons": "Can sell only enabled Add-ons", + + "SpiritcallerSpiritMax": "Maximum number of Evil Spirits", + "SpiritcallerSpiritAbilityCooldown": "Evil Spirit ability cooldown", + "SpiritcallerFreezeTime": "Evil Spirit ability freeze time", + "SpiritcallerProtectTime": "Evil Spirit ability protect time", + "SpiritcallerCauseVision": "Evil Spirit ability caused vision", + "SpiritcallerCauseVisionTime": "Evil Spirit ability caused vision time", + "Message.SetToSeconds": "Set to [{0}] seconds.", + "Message.MessageWaitHelp": "Specify the first argument in seconds.", + "Message.TemplateNotFoundHost": "No templates.txt matching {0} were found", + "Message.TemplateNotFoundClient": "The Host doesn't have a template called {0}", + "Message.SyncButtonLeft": "There are {0} more emergency buttons left", + "Message.Executed": "{0} was executed", + "Message.HideGameSettings": "Game settings have been hidden by the host.", + "Message.NowOverrideText": "Please enter the root folder of the game.\\Language\\English.dat. Change this text in the dat file \nIf you don't need this feature or want to display regular /n messages. \nPlease disable [Enable only custom /n messages in the settings.]", + "Message.NoDescription": "No description", + "Message.KickedByDenyName": "{0} was kicked because its name matched {1}", + "Message.BannedByBanList": "{0} was banned because they were banned in the past.", + "Message.BannedByEACList": "{0} has been banned because he is in EAC list of Banned people.", + "Message.DumpfileSaved": "The log file was successfully saved to the desktop, filename: {0}", + "Message.DumpcmdUsed": "{0} used /dump command.", + "Message.KickedByNoFriendCode": "{0} was kicked because their friend code does not exist.", + "Message.TempBannedByNoFriendCode": "{0} was temporary banned because their friend code does not exist.", + "Message.AddedPlayerToBanList": "Added {0} to the ban list", + "Message.KickWhoSayStart": "{0} has been kicked by the system. \nThe lobby host doesn't want to see messages where the player asks to start", + "Message.WarnWhoSayStart": "{0} has been warned: {1} times \nThe lobby host doesn't want to see messages where the player asks to start", + "Message.KickStartAfterWarn": "{0} has received {1} warnings, he will be kicked. \nThe lobby host doesn't want to see messages where the player asks to start", + "Message.WarnWhoSayBanWord": "{0}, stop sending banned words!", + "Message.WarnWhoSayBanWordTimes": "{0} has been warned: {1} times \nif you continue you will be kicked", + "Message.KickWhoSayBanWordAfterWarn": "[{0}] received {1} warnings.\nHe was expelled for forbidden words", + "Message.KickedByEAC": "[{0}]Kicked by EAC, reason:{1}", + "Message.BannedByEAC": "[{0}]Banned by EAC, reason:{1}", + "Message.NoticeByEAC": "[{0}]Detected:{1}", + "Message.TempBannedByEAC": "[{0}]Temporary Banned by EAC, reason:{1}", + "Message.TempBannedForSpamQuitting": "{0} was temporary banned because of spamming quits", + "Message.KickedByWhiteList": "{0} kicked because friendcode not found in WhiteList.txt", + "Message.SetLevel": "Your game level is set to: {0}", + "Message.SetColor": "Your color is set to: {0}", + "Message.SetName": "Your name is set to: {0}", + "Message.AllowLevelRange": "The game level can be set in the range: 0-100", + "Message.AllowNameLength": "Nickname can be set length: 1-10", + "Message.OnlyCanUseInLobby": "ERROR\n\nSorry, this command can only be used in the lobby", + "Message.CanNotUseInLobby": "ERROR\n\nSorry, this command cannot be used in the lobby", + "Message.CanNotUseByHost": "ERROR\n\nSorry, Host can't use this command", + "Message.TryFixName": "An attempt was made to fix hidden message content due to roles", + "Message.CanNotFindRoleThePlayerEnter": "Could not find the role you searching\nUse command /r to show role list", + "Message.PlayerQuitForever": "{0} decided to leave voluntarily \nSorry for the bad gaming experience \nI really worked hard to make progress", + "Message.MadmateSelfVoteModeNotify": "Please note: The current Madness generation mode is [{0}]\n Voting for yourself means you want to be Madmate. If you meet the conditions to become Madmate and there are still spaces left, you will immediately become Madmate", + "Message.HostLeftGameInGame": "★Warning★ Host left the game, in next time game wouldn't start normally. Please, exit the lobby or wait until new Host opens a lobby.", + "Message.HostLeftGameInLobby": "★Warning★ Host left the game, in next time game wouldn't start normally. If new host has TOHE, you need to re-enter the lobby to play normally.", + "Message.HostLeftGameNewHostIsMod": "★Warning★ Original Host left the game and {0} become the new Host! \nThe room is still modded, just start a game and end it immediately to reset the lobby!", + "Message.HostLeftGameNewHostIsNotMod": "★Warning★ Original Host left the game and {0} become the new Host. \nBut it's not modded. Please, exit the lobby or wait until new Host opens a lobby.", + "Message.LobbyShared": "The lobby has successfully been shared!", + "Message.LobbyShareFailed": "TOHE-Chan does not seem to be online (failed to share lobby)", + "Message.YTPlanDisabled": "ERROR\n\nPlease enable {0} in the Settings", + "Message.YTPlanSelected": "In the next game, your role will be {0}", + "Message.YTPlanSelectFailed": "You cannot be assigned as {0}.\nIt may be because you don't have this role enabled, or this role does not support being assigned.", + "Message.YTPlanCanNotFindRoleThePlayerEnter": "Could not find the role you searching\nUse command /r to show role list", + "Message.YTPlanNotice": "Note: The [YouTuber Plan] is enabled in this lobby, which means the Host can specify their role in the next game to make it easier to get content. In case the Host abuses this feature, please exit the game or report it.\nCurrent Creator Credentials:", + "Message.OnlyCanBeUsedByHost": "ERROR\n\nThis command may only be used by the host.", + "Message.MaxPlayers": "Maximum players set to ", + "Message.GhostRoleInfo": "Ghost Role Info\nHiya! A little bit about ghost roles...\n\nGhost roles drastically impact the game, so not recommended for smaller lobbies, if you're unfamiliar.\n\nSpawning:\nGhost-roles only spawn after death, the first x people from (team) to die get them.\n\nPS: If your previous role didn't have tasks(e.g sheriff), your tasks as a ghost-role aren't needed for task-win", + + "EnableGadientTags": "Enable Gradient Tags (can cause disconnect issues)", + "Warning.GradientTags": "Warning:\n\nHost has enabled gradient tags. This feature is not recommended to use because it can cause disconnect issues", + "WarningTitle": "Warning!", + "Warning.BrokenVentsInDleksSendInGame": "Warning! The vents on this map are broken", + "Warning.BrokenVentsInDleksMessage": "On the «dlekS ehT» map the vents are broken, they cannot be fixed in host-only mods, this is a vanilla bug, so any roles using vent as an ability will not spawns on this map", + + "AntiBlackoutProtectionTitle": "Anti Blackout", + "Warning.AntiBlackoutProtectionMsg": "Warning:\n\rBlack screen protection has been activated, due to the low number of alive Impostors, Crewmates and Neutral Killers\nThe voting screen will show as a tied vote (only affects the visual, not the results voting)\nModded players will see voting screen normaly", + "Warning.ShowAntiBlackExiledPlayer": "Last meeting triggered Black Screen Prevention!\nFollowing is the information of the player exiled in last meeting.\n", + "DisableAntiBlackoutProtects": "Disable AntiBlackout Protects (Recommended for testing)", + + "Warning.InvalidRpc": "Kicked {0} because an invalid RPC was received.\nPlease check that no mods other than TOHE installed.", + "Warning.NoModHost": "TOHE is not installed on the host", + "Warning.MismatchedVersion": "{0} has a different version of {1}", + "Warning.AutoExitAtMismatchedVersion": "The host has no or a different version of {0}\nYou will be kicked in {1}", + "Warning.CanNotUseBepInExConsole": "The use of the console is prohibited\nso your console has been off", + "Error.MeetingException": "Error: {0}\r\nPlease use SHIFT+M+ENTER to end the meeting", + "Error.InvalidRoleAssignment": "Error: Invalid role found for a player during role assignment({0})", + "Error.InvalidColor": "Error: Only default colors are available", + "Error.InvalidColorPreventStart": "Other players are not allowed to use other colors, otherwise it will result in a serious error", + "ErrorLevel1": "Bugs may occur.", + "ErrorLevel2": "This may be a bug.", + "ErrorLevel3": "This version shouldn't have been released.", + "TerminateCommand": "Abort Command", + "ERR-000-000-0": "No Error", + "ERR-000-900-0": "Test Error Lv.0", + "ERR-000-910-1": "Test Error Lv.1", + "ERR-000-920-2": "Test Error Lv.2", + "ERR-000-930-3": "Test Error Lv.3", + "ERR-000-804-1": "TownofHost-Enhanced does not support the Vanilla HnS, so unloaded.", + "ERR-001-000-3": "Main dictionary has duplicated keys.", + "ERR-002-000-1": "Unsupported Among Us version. Please update Among Us", + "DefaultSystemMessageTitle": "SYSTEM MESSAGE", + "MessageFromTheHost": "HOST MESSAGE", + "MessageFromEAC": "EAC", + "DetectiveNoticeTitle": "INVESTIGATION", + "SleuthNoticeTitle": "SLEUTH", + "GuessKillTitle": "GUESSING INFO", + "CelebrityNewsTitle": "CELEBRITY", + "CyberNewsTitle": "CYBER", + "GodAliveTitle": "GOD ", + "WorkaholicAliveTitle": "WORKAHOLIC", + "BaitAliveTitle": "BAIT", + "MessageFromKPD": "KARPED1EM ", + "MessageFromSponsor": "SPONSOR MESSAGE ", + "MessageFromDev": "DEVELOPER MESSAGE ", + "FortuneTellerCheckMsgTitle": "FORTUNE TELLER", + "MimicMsgTitle": "MIMIC", + "EraserEraseMsgTitle": "ERASER", + "MorticianCheckTitle": "CORPSE EXAMINATION", + "NemesisRevengeTitle": "NEMESIS", + "RetributionistRevengeTitle": "RETRIBUTIONIST", + "TabGroup.SystemSettings": "System Settings", + "TabGroup.GameSettings": "Game Settings", + "TabGroup.CrewmateRoles": "Crewmate Roles", + "TabGroup.NeutralRoles": "Neutral Roles", + "TabGroup.ImpostorRoles": "Impostor Roles", + "TabGroup.Addons": "Add-Ons", + "TabGroup.OtherRoles": "Experimental Roles (Not recommended)", + "TabGroup.TaskSettings": "Game Modifiers", + "OtherRoles.CrewmateRoles": "★ Crewmate Roles", + "OtherRoles.NeutralRoles": "★ Neutral Roles", + "OtherRoles.ImpostorRoles": "★ Impostor Roles", + "OtherRoles.Addons": "★ Add-Ons", + "ActiveRolesList": "Active Roles List", + "ForExample": "Example Use", + "updateButton": "Update", + "updatePleaseWait": "Please Wait...", + "updateManually": "Update failed.\nPlease Update Manually.", + "updateRestart": "Update Finished!\nPlease restart the game.", + "CanNotJoinPublicRoomNoLatest": "You can't join public rooms without the latest version.\nPlease update.", + "ModBrokenMessage": "The MOD file is damaged.\nPlease reinstall.", + "UnsupportedVersion": "Unsupported Among Us version.\nPlease update Among Us", + "DisabledByProgram": "Public rooms have been disabled by the program", + "EnterVentToWin": "Enter Vent to Win!!", + "EatenByPelican": "You're swallowed, waiting for the Pelican to die or a meeting", + "FireworkerPutPhase": "{0} Fireworker Left", + "FireworkerWaitPhase": "Wait for it...", + "FireworkerReadyFirePhase": "Fire!", + "EnterVentWinCountDown": "Enter vent within {0} seconds to win!", + "On": "ON", + "Off": "OFF", + "ColoredOn": "ON", + "ColoredOff": "OFF", + "CurrentActiveSettingsHelp": "Current Active Settings Help", + "WitchCurrentMode": "Current Mode", + "WitchModeKill": "Kill", + "WitchModeSpell": "Spell", + "HexMasterModeHex": "Hex", + "HexMasterModeKill": "Kill", + "PoisonerPoisonButtonText": "Poison", + "WitchModeDouble": "Double Click = Kill, Single Click = Spell", + "HexMasterModeDouble": "Double Click = Kill, Single Click = Hex", + "BountyCurrentTarget": "Current Target", + "Roles": "Roles", + "Settings": "Settings", + "Addons": "Add-Ons", + "LastResult": "★ Match Results", + "LastEndReason": "★ End Reason", + "KillLog": "Kill Log", + "Maximum": "Max", + "RoleRate": "ON", + "RoleOn": "ALWAYS", + "RoleOff": "OFF", + "Chance0": "0%", + "Chance5": "5%", + "Chance10": "10%", + "Chance15": "15%", + "Chance20": "20%", + "Chance25": "25%", + "Chance30": "30%", + "Chance35": "35%", + "Chance40": "40%", + "Chance45": "45%", + "Chance50": "50%", + "Chance55": "55%", + "Chance60": "60%", + "Chance65": "65%", + "Chance70": "70%", + "Chance75": "75%", + "Chance80": "80%", + "Chance85": "85%", + "Chance90": "90%", + "Chance95": "95%", + "Chance100": "100%", + "Preset": "Preset", + "Preset_1": "Preset 1", + "Preset_2": "Preset 2", + "Preset_3": "Preset 3", + "Preset_4": "Preset 4", + "Preset_5": "Preset 5", + "Standard": "Standard", + "GameMode": "Game Mode", + "PressTabToNextPage": "Press Tab or Number for Next Page...", + "RoleSummaryText": "Role Summary:", + "doOverride": "Override %role%'s Tasks", + "assignCommonTasks": "%role% has Common Tasks", + "roleLongTasksNum": "Amount of Long Tasks for %role%", + "roleShortTasksNum": "Amount of Short Tasks for %role%", + "Format.Players": "{0}", + "Format.Seconds": "{0}s", + "Format.Percent": "{0}%", + "Format.Times": "{0}", + "Format.Multiplier": "{0}x", + "Format.Votes": "{0}", + "Format.Pieces": "{0}", + "Format.Health": "{0}", + "Format.Level": "{0}", + "KillButtonText": "Kill", + "ReportButtonText": "Report", + "VentButtonText": "Vent", + "SabotageButtonText": "Sabotage", + "SniperSnipeButtonText": "Snipe", + "FireworkerExplosionButtonText": "Detonate", + "FireworkerInstallAtionButtonText": "Install", + "MercenarySuicideButtonText": "Suicide Timer", + "WarlockCurseButtonText": "Curse", + "NinjaShapeshiftText": "Kill", + "NinjaMarkButtonText": "Mark", + "WitchSpellButtonText": "Spell", + "VampireBiteButtonText": "Bite", + "MinerTeleButtonText": "Warp", + "ArsonistDouseButtonText": "Douse", + "PuppeteerOperateButtonText": "Manipulate", + "WarlockShapeshiftButtonText": "Spell", + "BountyHunterChangeButtonText": "Swap", + "EvilTrackerChangeButtonText": "Track", + "InnocentButtonText": "Frame", + "PelicanButtonText": "Eat", + "DeceiverButtonText": "Cheat", + "PursuerButtonText": "Trick", + "GangsterButtonText": "Recruit", + "RevolutionistDrawButtonText": "Win over", + "HaterButtonText": "Hatred", + "MedicalerButtonText": "Protect", + "DemonButtonText": "Attack", + "SoulCatcherButtonText": "Teleport", + "LightningButtonText": "Evaporate", + "ProvocateurButtonText": "Greet", + "ButcherButtonText": "Dismember", + "BomberShapeshiftText": "Explode", + "QuickShooterShapeshiftText": "Keep", + "CamouflagerShapeshiftTextBeforeDisguise": "Disguise", + "CamouflagerShapeshiftTextAfterDisguise": "Duration", + "AnonymousShapeshiftText": "Hack", + "DefaultShapeshiftText": "Shift", + "CleanerReportButtonText": "Clean", + "SwooperVentButtonText": "Swoop", + "SwooperRevertVentButtonText": "Expose", + "WraithVentButtonText": "Vanish", + "WraithRevertVentButtonText": "Expose", + "VectorVentButtonText": "Hop", + "VeteranVentButtonText": "Alert", + "GrenadierVentButtonText": "Flash", + "MayorVentButtonText": "Button", + "SheriffKillButtonText": "Shoot", + "UndertakerButtonText": "Mark", + "ArsonistVentButtonText": "Ignite", + "RevolutionistVentButtonText": "Revolution", + "FollowerKillButtonText": "Follow", + "PacifistVentButtonText": "Reset", + "CultistKillButtonText": "Charm", + "InfectiousKillButtonText": "Infect", + "MonarchKillButtonText": "Knight", + "OverseerKillButtonText": "Reveal", + "DisabledBySettings": "Disabled by Settings", + "Disabled": "Disabled", + "FailToTrack": "Failed To Track", + "KillCount": "Kills: {0}", + "CantUse.lastroles": "Unable to use /lastroles during a game.", + "CantUse.killlog": "Unable to use /killlog during a game.", + "CantUse.lastresult": "Unable to use /lastresult during a game.", + "IllegalColor": "Please enter the correct color", + "DisableUseCommand": "The Host's settings do not allow this command to be used.", + "SureUse.quit": "We will kick you and block you from entering this lobby again. This setting is irreversible. If you really want it, please send the command /qt {0}", + "PlayerIdList": "List of player IDs: ", + "CancelStartCountDown": "The starting countdown was cancelled", + "RestTOHESetting": "TOHE settings have been restored to default", + "FPSSetTo": "FPS Set To: {0}", + "HostKillSelfByCommand": "The lobby Host decided to commit suicide", + "SyncCustomSettingsRPC": "Synchronized RPC", + "Mode": "Mode", + "Target": "Target", + "PlayerInfo": "Player Info", + "NoInfoExists": "No Info Exists", + "PlayerLeftByAU-Anticheat": "{0} was banned by the Innersloth anti-cheat.", + "PlayerLeftByError": "Game will auto-end to prevent black screens.", + "MsgKickOtherPlatformPlayer": "{0} was kicked due to playing on {1}", + "KickBecauseLowLevel": "{0} was kicked because their level was too low", + "TempBannedBecauseLowLevel": "{0} was temporary banned because their level was too low", + "KickBecauseDiffrentVersionOrMod": "{0} was kicked because they had a different version of the mod", + + "FFADisplayScore": "Ranking: {0} Score: {1}", + "FFATimeRemain": "Time Remaining: {0} second(s)", + + "GameOver": "Game Over", + "TOHEOptions": "TOHE Options", + "Cancel": "Cancel", + "Back": "Back", + "Yes": "Yes", + "No": "No", + "RpcAntiBlackOutIgnored": "Because of {0}, an unknown error occurred, RPC will be ignored.", + "RpcAntiBlackOutEndGame": "Because of {0}, an unknown error occurred, game will end to prevent black screen.", + "RpcAntiBlackOutNotifyInLobby": "Because of {0}, an unknown error occurred, to prevent black screen, turn off [{1}] in settings.", + "AntiBlackOutNotifyInLobby": "An unknown error occurred, to prevent black screen. Sadly, this error exists in all Town of Host versions. Automatic ending game is required, or the game will not start.", + "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.", + "NextPage": "Next Page", + "PreviousPage": "Previous Page", + "EAC.CheatDetected.EAC": "Cheating usage detected (Using AUM)", + "PressF1ShowMainRoleDes": "Press F1: Show Role Description", + "PressF2ShowAddRoleDes": "Press F2: Show Add-on Description", + "FakeTask": "Fake Tasks:", + "PVP.ATK": "Attack", + "PVP.DF": "Defend", + "PVP.RCO": "Recover", + "SettingsAreLoading": "Loading\nsettings...", + "EAC.CheatDetected.HighLevel": "Warning: EAC detected High Level of cheats.", + "EAC.CheatDetected.LowLevel": "Warning: EAC detected Low Level of cheats. One of the players is hacking.", + "ExiledJester": "You're all fools!\n{0} the {1} laughing out loud tricked you into ejecting them.\nGG!", + "JesterMeetingLoose": "\r\nBut it cannot win until meeting number {0}", + "ExiledExeTarget": "{0} was the {1}.\nBut they were also the Executioner's target!\nGG!", + "ExiledInnocentTargetAddBelow": "\nLooking back at the Innocent counts the money in their hands", + "ExiledInnocentTargetInOneLine": "{0} was the {1}.\nBut looking back, there's the Innocent counting the money in their hands....\nGG!", + "IsGood": "{0} was a good guy", + "BelongTo": "{0} belongs to {1}", + "PlayerIsRole": "{0} was The {1}", + "PlayerExiled": "{0} was ejected", + "NoImpRemain": "0 Impostors remain", + "OneImpRemain": "1 Impostor remains", + "TwoImpRemain": "2 Impostors remain", + "ThreeImpRemain": "3 Impostors remain", + "ImpRemain": "{0} Impostors remaining", + "NeutralRemain": "\n{0} Neutral Killers remain", + "OneNeutralRemain": "\n{0} Neutral Killer remains", + "GameOverReason.HumansByVote": "All Impostors and Neutral Killers were ejected or killed", + "GameOverReason.HumansByTask": "The Crewmates completed all tasks", + "GameOverReason.HumansDisconnect": "Crewmates disconnected", + "GameOverReason.ImpostorByVote": "The Crewmates were ejected", + "GameOverReason.ImpostorByKill": "The Impostors killed everyone", + "GameOverReason.ImpostorBySabotage": "Crewmates failed to fix a critical sabotage", + "GameOverReason.ImpostorDisconnect": "Impostors disconnected", + "FortuneTellerCheck.Honest": "Looks like [{0}] is being honest.", + "FortuneTellerCheck.Impulse": "Looks like [{0}] is suppressing some kind of impulse.", + "FortuneTellerCheck.Weirdo": "Looks like [{0}] is mixed in crowd.", + "FortuneTellerCheck.Blockbuster": "Looks like [{0}] got big desires to change something.", + "FortuneTellerCheck.Strong": "Looks like [{0}] has a strong power but is dim in society.", + "FortuneTellerCheck.Incomprehensible": "Looks like [{0}] is being misunderstood.", + "FortuneTellerCheck.Enthusiasm": "Looks like [{0}] speaks with everyone very enthusiastic.", + "FortuneTellerCheck.Disturbed": "Looks like [{0}] is disturbed by something.", + "FortuneTellerCheck.None": "Looks like [{0}] has just an ordinary life.", + "FortuneTellerCheck.Glitch": "TV2gPZ4wUJWYr{0}05gA6T5PKzBG17", + "FortuneTellerCheck.HideMsg": "Looks like [{0}] hides secrets.", + "FortuneTellerCheck.Love": "♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥♥", + "FortuneTellerCheck.TaskDone": "[{0}]Role -[{1}]", + "DevAndSpnTitle": "TOHE family", + "FortuneTellerCheck.Null": "{0} is a role that is not listed.\nThis message should not appear normally.", + "FortuneTellerCheck.Result": "{0} is either one of the following roles:-\n{1}", + "SunnyboyChance": "Sunnyboy Chance", + "BardChance": "Bard Chance", + "VampiressChance": "Vampiress Chance", + "NukerChance": "Nuker Chance", + "SkeldChance": "Chance that the map is The Skeld", + "MiraChance": "Chance that the map is MIRA HQ", + "PolusChance": "Chance that the map is Polus", + "DleksChance": "Chance that the map is dlekS ehT", + "AirshipChance": "Chance that the map is Airship", + "FungleChance": "Chance that the map is The Fungle", + "UseMoreRandomMapSelection": "Use a more random map selection", + "CamouflageMode.Default": "Default", + "CamouflageMode.Host": "Host", + "CamouflageMode.Random": "Random", + "CamouflageMode.OnlyRandomColor": "Only Random Color", + "CamouflageMode.Karpe": "KARPED1EM", + "CamouflageMode.Lauryn": "Lauryn", + "CamouflageMode.Moe": "Moe", + "CamouflageMode.Pyro": "Pyro", + "CamouflageMode.ryuk": "ryuk", + "CamouflageMode.Gurge44": "Gurge44", + "CamouflageMode.TommyXL": "TommyXL", + "DeathCmd.HeyPlayer": "Hey ", + "DeathCmd.YouAreRole": ", looks like you're the ", + "DeathCmd.NotDead": "You haven't died yet, this can only be used after you die\n\nCheck back again after you've been brutally murdered", + "DeathCmd.KillerName": "You were killed by ", + "DeathCmd.KillerRole": "Their role is ", + "DeathCmd.DeathReason": "Your cause of death was ", + "DeathCmd.YourName": "You are ", + "DeathCmd.YourRole": "Your role is ", + "DeathCmd.Ejected": "You were ejected during a meeting", + "DeathCmd.Misfired": "You misfired.", + "DeathCmd.Shrouded": "You were shrouded by a Shroud and didn't make a kill, so you suicided.", + "DeathCmd.Lovers": "Your lover had died.", + + "RpsCommandInfo": "This Command can only be used when in lobby or after you die.\n\ntype /rps X to play Rock Paper Scissors with the system. X can be 0 (rock), 1 (paper) or 2 (scissors). \n\nExample :- /rps 0", + "RpsDraw": "I choose {0}\n\nWow, what an intense battle of wits we just had! It's almost as if we're equally matched in this game of sheer luck and randomness.", + "RpsLose": "I choose {0}\n\nWell, well, well, looks like I've managed to outsmart a human again in this highly complex game of Rock, Paper, Scissors. I guess my unbeatable powers strike again! ", + "RpsWin": "I choose {0}\n\nOh, congratulations! You must have a crystal ball hidden behind that screen to beat me at Rock, Paper, Scissors. Or maybe I just have the world's worst luck algorithm.", + + "CoinFlipCommandInfo": "This Command can only be used when in lobby or after you die.", + "CoinFlipResult": "Drumroll, please... After an intense battle of gravity and randomness, the coin has decided to grace us with its presence! And the majestic winner is... (wait for it) ... the one and only... {0}! Who could have seen that coming?! Clearly, a momentous occasion in the history of coin flips.", + + "GNoCommandInfo": "This Command can only be used when in lobby or after you die.\n\ntype /gno X to play guess a number. X can be a number between 0 and 99 (both included). \n\nYou get maximum of 7 tries to guess the number.\n\n Example:- /gno 10", + "GNoLost": "Oh, you were so close! Just one more guess, and you might have deciphered the Da Vinci code! By the way, the secret number was... {0}! But hey, you were only off by a few billion possibilities. Better luck next time, Sherlock! ", + "GNoLow": "Oh, you're really nailing this! It's so low, I almost need a shovel to dig it up!\nYou have {0} guesses left!", + "GNoHigh": "Oh, absolutely! You're getting warmer. In fact, it's so high, I need a telescope to see it from here! \nYou have {0} guesses left!", + "GNoWon": "Oh, how did you ever figure that out? It's almost like you're a mind reader! Congratulations, you're a genius! You found the secret number with {0} guesses left!", + + "RandCommandInfo": "This Command can only be used when in lobby or after you die.\n\ntype /rand X Y to get a number between X and Y, inclusive. \nX and Y can be any number between 0 and 2147483647, including both numbers.\nX must be less than Y.\n\nExample:- /rand 0 99", + "RandResult": "Congratulations, your random number is {0}! Wasn't that fun?", + + "ChanceToMiss": "Chance to miss a kill", + + "SoulCollectorPointsToWin": "Required number of souls", + "SoulCollectorTarget": "You have predicted the death of {0}", + "SoulCollectorTitle": "SOUL COLLECTOR", + "CollectOwnSoulOpt": "Can collect their own soul", + "SoulCollectorSelfVote": "Host settings do not allow you to collect your own soul", + "SoulCollectorToDeath": "You have become Death!!!", + "SoulCollectorTransform": "Now Soul Collector has become Death, Destroyer of Worlds and Horseman of the Apocalypse!

Find them and vote them out before they bring forth Armageddon!", + "CallMeetingIfDeath": "Call a meeting immediately after Death transforms", + "GetPassiveSouls": "Gain a passive soul every round", + "PassiveSoulGained": "You have gained a passive soul from the underworld.", + "ApocalypseIsNigh": "[ The Apocalypse Is Nigh! ]", + "BakerToFamine": "You have become Famine!!!", + "BakerTransform": "The Baker has transformed into Famine, Horseman of the Apocalypse! A famine has begun!", + "BakerAlreadyBreaded": "That player already has bread!", + "BakerBreadUsedAlready": "You've already given a player bread this round!", + "BakerBreaded": "Player given bread", + "BakerBreadNeededToTransform": "Required number of bread to become Famine", + "BakerCantBreadApoc": "You cannot give other Apocalypse members bread!", + "BakerKillButtonText": "Bread", + + "ChronomancerKillCooldown": "Time to fully charge the kill button", + + "ShamanButtonText": "Voodoo", + "ShamanTargetAlreadySelected": "You have already selected a voodoo doll in this round", + "VoodooCooldown": "Voodoo Cooldown", + + "AdminWarning": "Admin Table in use!", + "VitalsWarning": "Vitals in use!", + "DoorlogWarning": "Doorlogs in use!", + "CameraWarning": "Cameras in use!", + "MinWaitAutoStart": "Minutes to wait before auto-starting", + "MaxWaitAutoStart": "Force start when Lobby Timer (in minutes) goes below", + "PlayerAutoStart": "Minimum Player Threshold to auto-start", + "AutoStartTimer": "Initial countdown for auto-starting", + "AutoPlayAgainCountdown": "Delay before re-entering lobby", + "AutoPlayAgain": "Auto Play Again", + "AutoRehost": "Auto Re-Host on Bad Disconnect", + "CountdownText": "Rejoining lobby in {0}s", + "TimeMasterSkillDuration": "Time Shield Duration", + "TimeMasterSkillCooldown": "Time Shield Cooldown", + "TimeMasterOnGuard": "Time Shield is active!", + "TimeMasterSkillStop": "Time Shield has ended!", + "TimeMasterVentButtonText": "Time Shield", + "BodyCannotBeReported": "Body could not be reported", + "BurstKillDelay": "Burst Kill Delay", + "BurstNotify": "That was a Burst! Get in a vent or die.", + "ImpCanBeBurst": "Impostors can become Burst", + "CrewCanBeBurst": "Crewmates can become Burst", + "NeutralCanBeBurst": "Neutrals can become Burst", + "BurstFailed": "Burst failed to bomb you", + "ShroudButtonText": "Shroud", + "ShroudCooldown": "Shroud Cooldown", + "Message.Shrouded": "One or more players were shrouded by a Shroud!\n\nGet rid of the Shroud or all shrouded players will suicide!", + "LudopathRandomKillCD": "Maximum kill cooldown", + "UnderdogMaximumPlayersNeededToKill": "Maximum players needed to start killing", + "GodfatherTargetCountMode": "Killer turns into", + "GodfatherCount_Refugee": "Refugee", + "GodfatherCount_Madmate": "Madmate", + "MissChance": "Chance To Miss", + "IncreaseByOneIfConvert": "Increase The KillCount +1 If a Crew Is Converted", + "HawkMissed": "Missed!", + "HawkCanKillNum": "Max Slices", + "MinimumPlayersAliveToKill": "Minimum Players Alive To Kill", + "BloodMoonCanKillNum": "Max BloodLettings", + "BloodMoonTimeTilDie": "Time Until Death", + "DeathTimer": "\nDeath In: {DeathTimer}s", + "BerserkerKillCooldown": "Berserker Kill Cooldown", + "BerserkerMax": "Max level that Berserker can reach", + "BerserkerHasImpostorVision": "Berserker Has Impostor Vision", + "WarHasImpostorVision": "War Has Impostor Vision", + "BerserkerCanVent": "Berserker Can Vent", + "WarCanVent": "War Can Vent", + "BerserkerOneCanKillCooldown": "Unlock lower kill cooldown", + "BerserkerOneKillCooldown": "Kill cooldown after unlocking", + "BerserkerTwoCanScavenger": "Unlock scavenged kills", + "BerserkerThreeCanBomber": "Unlock bombed kills", + "BerserkerFourCanNotKill": "Become War", + "BerserkerMaxReached": "Maximum level reached!", + "BerserkerLevelChanged": "Increased level to {0}", + "BerserkerLevelRequirement": "Level requirement for unlock", + "KilledByBerserker": "Killed by Berserker", + "BerserkerToWar": "You have become War!!!", + "BerserkerTransform": "The Berserker has transformed into War, Horseman of the Apocalypse! Cry 'Havoc!', and let slip the dogs of war.", + "WarKillCooldown": "War kill cooldown", + + "ImpCanBeUnlucky": "Impostors can become Unlucky", + "CrewCanBeUnlucky": "Crewmates can become Unlucky", + "NeutralCanBeUnlucky": "Neutrals can become Unlucky", + "BlackmailerSkillCooldown": "Blackmail Cooldown", + "BlackmailerMax": "Maximum times blackmailed players may speak", + "BlackmailerDead": "Warning! {0} has been blackmailed by a Blackmailer!", + "BlackmaileKillTitle": "BLACKMAILER", + "UnluckyTaskSuicideChance": "Chance to suicide from doing tasks", + "UnluckyKillSuicideChance": "Chance to suicide from killing", + "UnluckyVentSuicideChance": "Chance to suicide from venting", + "UnluckyReportSuicideChance": "Chance to suicide from reporting bodies", + "UnluckySabotageSuicideChance": "Chance to suicide from opening a door", + "ImpCanBeVoidBallot": "Impostors can become VoidBallot", + "CrewCanBeVoidBallot": "Crewmates can become VoidBallot", + "NeutralCanBeVoidBallot": "Neutrals can become VoidBallot", + "ImpCanBeAware": "Impostors can become Aware", + "NeutralCanBeAware": "Neutrals can become Aware", + "CrewCanBeAware": "Crewmates can become Aware", + "AwareKnowRole": "Knows the role of player", + "AwareInteracted": "{0} tried to reveal your role.", + "AwareTitle": "AWARE MESSAGE", + "LighterVentButtonText": "Light", + "LighterSkillCooldown": "Light Cooldown", + "LighterSkillDuration": "Light Duration", + "LighterVisionNormal": "Increased Vision", + "LighterVisionOnLightsOut": "Increased Vision During Lights Out", + "LighterSkillInUse": "Ability in use", + "LighterSkillStop": "Ability expired", + "StealthDarkened": "Darkened: {0}", + "StealthExcludeImpostors": "Ignore Impostors when Blinding", + "StealthDarkenDuration": "Blinding Duration", + "PenguinAbductTimerLimit": "Dragging Time", + "PenguinMeetingKill": "Kill the target if a meeting starts during dragging", + "PenguinKillButtonText": "Drag", + "PenguinTimerText": "Drag Timer", + "PenguinTargetOnCheckMurder": "You are grabbed, Try escape that first!", + "WitnessTime": "Max Time after killing where killer appears red", + "WitnessButtonText": "Examine", + "WitnessFoundInnocent": "✓", + "WitnessFoundKiller": "⚠", + "SwapperMax": "Maximum swaps", + "CanSwapSelfVotes": "Can exchange your own votes.", + "SwapperTrialMax": "You've reached the maximum amount of swaps!\nYou can't swap votes anymore.", + "CantSwapSelf": "Can't exchange of one's own vote", + "SwapVote": "The votes of {0} and {1} were swapped!", + "SwapDead": "Sorry, you can't swap votes after death.", + "SwapNull": "Please choose the ID of a living player to swap votes with. Use 253 to clear swaps", + "SwapHelp": "Command Format: /sw [playerID] to select the target\nYou can see the player IDs next to the player names or use /id to see the player ID list.\nUse /swap 253 to clear your previous swap", + "Swap1": "Swap target 1 selected", + "Swap2": "Swap target 2 selected", + "CancelSwap": "Cleared your previous swap!", + "CancelSwapDueToTarget": "Cleared your previous swap because one or more of your target is dead.", + "Swap1=Swap2": "The target you input is same as Swap target 1.\nPls input a different one", + "SwapTitle": "SWAPPER", + "SwapperTryHideMsg": "Try to hide Swapper's command", + "SwapperPreResult": "Currently you selected to swap votes between {0} and {1}.\nIf you feel unsure, use /swap 253 to clear your selection.", + "ImpCanBeFragile": "Impostors can become Fragile", + "NeutralCanBeFragile": "Neutrals can become Fragile", + "CrewCanBeFragile": "Crewmates can become Fragile", + "ImpCanKillFragile": "Impostors can force kill Fragile", + "NeutralCanKillFragile": "Neutrals can force kill Fragile", + "CrewCanKillFragile": "Crewmates can force kill Fragile", + "FragileKillerLunge": "Killer lunges on kill", + "CrusaderSkillLimit": "Maximum Crusades", + "CrusaderSkillCooldown": "Crusade Cooldown", + "CrusaderKillButtonText": "Crusade", + "JailorKillButtonText": "Jail", + "AgitaterKillButtonText": "Pass", + "HasSerialKillerBuddy": "Has Serial Killer buddy", + "ChanceToSpawn": "Chance to spawn", + "ChanceToSpawnAnother": "Chance to spawn another", + "BloodlustKillCD": "Bloodlust Kill Cooldown", + "BloodlustPlayerCount": "Max players alive for Bloodlust", + "ReflectHarmfulInteractions": "Reflect harmful interactions", + + "ImpCanBeDiseased": "Impostors can become Diseased", + "NeutralCanBeDiseased": "Neutrals can become Diseased", + "CrewCanBeDiseased": "Crewmates can become Diseased", + "DiseasedCDOpt": "Increase the cooldown by", + "DiseasedCDReset": "Cooldown returns to normal after a meeting", + + "ImpCanBeAntidote": "Impostors can become Antidote", + "NeutralCanBeAntidote": "Neutrals can become Antidote", + "CrewCanBeAntidote": "Crewmates can become Antidote", + "AntidoteCDOpt": "Decrease the cooldown by", + "AntidoteCDReset": "Cooldown returns to normal after a meeting", + + "ImpCanBeStubborn": "Impostors can become Stubborn", + "NeutralCanBeStubborn": "Neutrals can become Stubborn", + "CrewCanBeStubborn": "Crewmates can become Stubborn", + + "ImpCanBeAvanger": "Impostors can become Avenger", + "NeutralCanBeAvanger": "Neutrals can become Avenger", + "CrewCanBeAvanger": "Crewmates can become Avenger", + "ImpCanBeSleuth": "Impostors can become Sleuth", + "CrewCanBeSleuth": "Crewmates can become Sleuth", + "NeutralCanBeSleuth": "Neutrals can become Sleuth", + "SleuthCanKnowKillerRole": "Can find the role of the killer", + "SleuthNoticeKiller": "\nThe killer's role is {0}.", + "SleuthNoticeVictim": "{0}'s role is {1}.", + "SleuthNoticeKillerNotFound": "\nThe killer could not be identified, this was possibly a suicide.", + "BomberDiesInExplosion": "Bomber dies in their explosion", + "ImpostorsSurviveBombs": "Impostors survive bombs", + "NukeRadius": "Nuke radius (12x is very large)", + "NukeCooldown": "Nuke Cooldown", + + "MasochistKillMax": "Amount of attacks needed to win", + "GuessMasochist": "You just tried to guess a Masochist!\nThey're now one step closer to winning!\n\nDo not guess them again.", + "MasochistKill": "You were attacked!", + "SelfGuessMasochist": "You can't self guess as a Masochist, you cheater!", + "GuessMasochistBlocked": "Masochist cannot guess due to self guessing.", + + "RememberCooldown": "Imitate Cooldown", + "RefugeeKillCD": "Refugee's Kill Cooldown", + "RememberedNeutralKiller": "You remembered you were a neutral killer!", + "RememberedMaverick": "You remembered you were a Maverick!", + "RememberedPursuer": "You remembered you were a Pursuer!", + "RememberedFollower": "You remembered you were a Follower!", + "RememberedAmnesiac": "You failed to remember your role.", + "RememberedImitator": "You remmebered you were an Imitator.", + "RememberedImpostor": "You remembered you were an Impostor!", + "RememberedCrewmate": "You remembered you were a crewmate!", + "ImitatorImitated": "An Imitator imitated your role!", + "ImitatorInvalidTarget": "Imitation failed", + "RememberButtonText": "Remember", + "ImitatorKillButtonText": "Imitate", + "IncompatibleNeutralMode": "If neutral is incompatible, turn into", + "RememberedYourRole": "An Amnesiac rmembered your role!", + "YouRememberedRole": "You remembered who you were!", + + "BanditStealMode": "Steal Mode", + "BanditStealMode_OnMeeting": "On Meeting", + "BanditStealMode_Instantly": "Instantly", + "BanditMaxSteals": "Maximum Steals", + "BanditCanStealBetrayalAddon": "Can Steal Betrayal Addons", + "BanditCanStealImpOnlyAddon": "Can Steal Impostor Only Addons", + "NoStealableAddons": "Could not steal addon from the player", + "StealCooldown": "Steal cooldown", + + "DoppelMaxSteals": "Maximum Steals", + + "NecromancerRevengeTime": "Necromancy time", + "NecromancerRevenge": "You have {0}s to kill {1}", + "NecromancerSuccess": "Necromancy complete! You live to see another day.", + "NecromancerHide": "Venting is disabled, hide from the Necromancer!", + "RetributionistDeadMsg": "The death of the Retributionist means the beginning of retribution. \nPlease use /ret + [player ID] to kill the specified player \nYou can see player IDs in front of their names. \nOr type /ret to get a list of player IDs", + "RetributionistAliveKill": "Retribution for the Retributionist may only begin after their death.", + "RetributionistKillMax": "You've reached the maximum amount of kills, you can't kill anymore!", + "RetributionistKillDead": "Choose a living player to kill.", + "RetributionistKillSucceed": "{0} was killed by the Retributionist!", + "RetributionistKillDisable": "You can't retribute until your tasks are done.", + "CanOnlyRetributeWithTasksDone": "Can only retribute on task completion", + "RetributionistCanKillNum": "Max retributions", + "RetributionistKillTooManyDead": "Too many players are dead, you can't retribute.", + "MinimumPlayersAliveToRetri": "Maximum players needed to block retributions", + "MinimumNoKillerEjectsToKill": "Minimum meetings passed with no killer ejects to kill", + "BakerChangeChances": "Chance that Baker turns into Famine", + "BakerChange": "The Baker has turned into Famine!\nThe bread is now poisoned.\nIf the Famine is not exiled, all players with poisoned bread die.", + "BakerChangeNow": "A player has poisoned bread!\nExile the Famine or that player dies.", + "BakerChangeNONE": "The Famine has not given out poisoned bread.\nNobody will die of poison, but you should still exile the Famine.", + "PanAliveMessageTitle": "BAKER ", + "PanAlive": "The Baker has given out bread.", + "ImmuneToAttacksWhenTasksDone": "Immune to attacks on task completion", + + "TwisterCooldown": "Twist Cooldown", + "TwisterButtonText": "Twist", + "TwisterHideTwistedPlayerNames": "Hide who the players swap places with", + "InstigatorAbilityLimit": "Ability Use Count", + "InstigatorKillsPerAbilityUse": "Kills per Ability use", + + "CrewCanFindCaptain": "Crewmates can find Captain", + "MadmateCanFindCaptain": "Madmates can find Captain", + "ReducedSpeed": "Reduced speed", + "ReducedSpeedTime": "Time duration for reduced speed", + "CaptainCanTargetNB": "Captain can target Neutral Benign", + "CaptainCanTargetNE": "Captain can target Neutral Evil", + "CaptainCanTargetNC": "Captain can target Neutral Chaos", + "CaptainCanTargetNK": "Captain can target Neutral Killer", + "CaptainCanTargetNA": "Captain can target Neutral Apocalypse", + "CaptainSpeedReduced": "Captain reduced your speed", + "CaptainRevealTaskRequired": "Number of tasks completed after which Captain is revealed", + "CaptainSlowTaskRequired": "Number of tasks completed after which target speed is reduced", + + "InspectorTryHideMsg": "Hide Inspector's commands", + "MaxInspectCheckLimit": "Max inspections per game", + "InspectCheckLimitPerMeeting": "Max inspections per meeting", + "InspectCheckTargetKnow": "Targets know they were checked by Inspector", + "InspectCheckOtherTargetKnow": "Targets know who they were checked with", + "InspectorDead": "You can not use your power after death", + "InspectCheckMax": "Max inspections per game reached!\nYou can not use your power anymore.", + "InspectCheckRound": "Max inspections per round reached!\nYou can check again in the next round.", + "InspectCheckSelf": "HA!! you thought it would be this easy. You can not check yourself", + "InspectCheckReveal": "HA! you thought it would be this easy. You can not check a role that is revealed", + "InspectCheckTitle": "INSPECTOR ", + "InspectCheckTrue": "{0} and {1} are in the same team!", + "InspectCheckFalse": "{0} and {1} are NOT in the same team!", + "InspectCheckTargetMsg": " were checked by Inspector.", + "InspectCheckHelp": "Instructions: /cmp [Player ID 1] [Player ID 2] \nExample: /cmp 1 5 \nYou can see the player IDs before everyone's names \n or use the /id command to list the player IDs", + "InspectCheckNull": "Please select an ID of a living player to check their team", + "InspectCheckBaitCountMode": "Bait counts as revealing role if Bait reveal on first meeting is on", + "InspectCheckRevealTarget": "When tasks done, target knows team of other target", + "InspectorTargetReveal": " Looks like {0} is aligned with team {1}", + + "EgoistCountMode.Original": "Original", + "EgoistCountMode.Neutral": "Neutral", + + "JailerJailCooldown": "Jail cooldown", + "JailerMaxExecution": "Maximum executions", + "JailerNBCanBeExe": "Can execute Neutral Benign", + "JailerNCCanBeExe": "Can execute Neutral Chaos", + "JailerNECanBeExe": "Can execute Neutral Evil", + "JailerNKCanBeExe": "Can execute Neutral Killing", + "JailerNACanBeExe": "Can execute Neutral Apocalypse", + "JailerCKCanBeExe": "Can execute Crew Killing", + "JailerTargetAlreadySelected": "You have already selected a target", + "SuccessfullyJailed": "Target successfully jailed", + "CantGuessJailed": "You can not guess the target", + "JailedCanOnlyGuessJailer": "You have been jailed. You can only guess Jailer.", + "CanNotTrialJailed": "You can not trial the target.", + "notifyJailedOnMeeting": "Notify jailed player when meeting starts", + "JailedNotifyMsg": "You have been locked up in jail by the Jailer. No one can guess or judge you and you can only guess Jailer.\n\nIf Jailer votes you, you will be executed after the meeting ends.", + "JailerTitle": "Jailer", + + "CopyCatCopyCooldown": "Copy cooldown", + "CopyCatRoleChange": "Your role has been changed to {0}", + "CopyCatCanNotCopy": "You can not copy target's role", + "CopyButtonText": "Copy", + "CopyCrewVar": "Can copy evil variants of crew roles", + "CopyTeamChangingAddon": "Can copy team changing addon", + + "MaxCleanserUses": "Max cleanses", + "CleansedCanGetAddon": "Cleansed player can get Add-on", + "CleanserTitle": "CLEANSER", + "CleanserRemoveSelf": "You can not cleanse yourself", + "CleanserCantRemove": "Oops! the player can not be cleansed.", + "CleanserRemovedRole": "{0} has been cleansed. All their Addons will be removed after the meeting.", + "LostAddonByCleanser": "All your Addons were removed by the cleanser", + + "MaxProtections": "Max protections", + "KeeperHideVote": "Hide Keeper's vote", + "KeeperProtect": "You chose to protect {0}, your vote has been returned", + "KeeperTitle": "Keeper", + + "MaulRadius": "Maul Radius", + "ImpCanBeAutopsy": "Impostors can become Autopsy", + "CrewCanBeAutopsy": "Crewmates can become Autopsy", + "NeutralCanBeAutopsy": "Neutrals can become Autopsy", + "ImpCanBeCyber": "Impostors can become Cyber", + "CrewCanBeCyber": "Crewmates can become Cyber", + "NeutralCanBeCyber": "Neutrals can become Cyber", + "ImpKnowCyberDead": "Impostors know if Cyber died", + "CrewKnowCyberDead": "Crewmates know if Cyber died", + "NeutralKnowCyberDead": "Neutrals know if Cyber died", + "CyberKnown": "Everyone can see Cyber", + "ImpCanBeInfluenced": "Impostors can become Influenced", + "CrewCanBeInfluenced": "Crewmates can become Influenced", + "NeutralCanBeInfluenced": "Neutrals can become Influenced", + "ImpCanBeBewilder": "Impostors can become Bewilder", + "CrewCanBeBewilder": "Crewmates can become Bewilder", + "NeutralCanBeBewilder": "Neutrals can become Bewilder", + "KillerGetBewilderVision": "Killer gets Bewilder's vision", + "ImpCanBeOiiai": "Impostors can be OIIAI", + "CrewCanBeOiiai": "Crewmates can be OIIAI", + "NeutralCanBeOiiai": "Neutrals can be OIIAI", + "OiiaiCanPassOn": "OIIAI can pass on to the killer", + "NeutralChangeRolesForOiiai": "Neutrals turns to ", + "LostRoleByOiiai": "You got erased by OIIAI!", + "ImpCanBeSunglasses": "Impostors can become Sunglasses", + "CrewCanBeSunglasses": "Crewmates can become Sunglasses", + "NeutralCanBeSunglasses": "Neutrals can become Sunglasses", + "SunglassesVision": "Sunglasses Vision", + "ImpCanBeLoyal": "Impostors can become Loyal", + "CrewCanBeLoyal": "Crewmates can become Loyal", + "TasklessCrewCanBeLazy": "Crewmates without tasks can be Lazy", + "TaskBasedCrewCanBeLazy": "Task based crewmates can be Lazy", + "SheriffCanBeMadmate": "Sheriff can become Madmate", + "MayorCanBeMadmate": "Mayor can become Madmate", + "NGuesserCanBeMadmate": "Nice Guesser can become Madmate", + "SnitchCanBeMadmate": "Snitch can become Madmate", + "JudgeCanBeMadmate": "Judge can become Madmate", + "MarshallCanBeMadmate": "Marshall can become Madmate", + "GanRetributionistCanBeMadmate": "Retributionist can be converted", + "RetributionistCanBeMadmate": "Retributionist can become Madmate", + "OverseerCanBeMadmate": "Overseer can become Madmate", + "GanSheriffCanBeMadmate": "Sheriff can be converted", + "GanMayorCanBeMadmate": "Mayor can be converted", + "GanNGuesserCanBeMadmate": "Nice Guesser can be converted", + "GanJudgeCanBeMadmate": "Judge can be converted", + "GanMarshallCanBeMadmate": "Marshall can be converted", + "GanOverseerCanBeMadmate": "Overseer can be converted", + "RascalAppearAsMadmate": "Appear As Madmate On Ejection", + "CouncillorDead": "Sorry, you can't murder from the dead.", + "CouncillorMurderMax": "Sorry, you've reached the maximum amount of murders for the meeting.", + "Councillor_LaughToWhoMurderSelf": "Hahaha, who would've thought someone was stupid enough to murder themselves?\n\nGuess it happens to be... YOU!", + "Councillor_MurderKill": "{0} was judged.", + "Councillor_MurderHelp": "Command: /tl [player ID]\nYou can see the players' IDs before the players' names.\nOr use /id to view the list of all player IDs.", + "Councillor_MurderNull": "Please choose a living player to murder.", + "Councillor_MurderKillTitle": "COURT ", + "CouncillorMurderLimitPerMeeting": "Maximum Kills Per Meeting", + "CouncillorCanMurderMadmate": "Can Murder Madmates", + "CouncillorCanMurderImpostor": "Can Murder Impostors", + "CouncillorTryHideMsg": "Try to hide Councillor's commands", + "DazzlerDazzled": "You were dazzled by the Dazzler!", + "DazzlerCauseVision": "Reduced vision", + "DazzlerDazzleLimit": "Max number of players affected by reduced vision", + "DazzlerResetDazzledVisionOnDeath": "Reset vision of dazzled players on death/eject", + "DazzleCooldown": "Dazzle Cooldown", + "DazzleButtonText": "Dazzle", + + "MoleVentButtonText": "Dig", + "MoleVentCooldown": "Dig cooldown", + + "AddictVentButtonText": "Get Fix", + "AddictInvulnerbilityTimeAfterVent": "Invulnerability Time", + "AddictSpeedWhileInvulnerble": "Movement speed while Invulnerble", + + "AddictFreezeTimeAfterInvulnerbility": "Time the Addict gets frozen in place after Invulnerability", + "AlchemistShieldDur": "Resistance Potion Duration", + "AlchemistInvisDur": "Invisibility Potion Duration", + "AlchemistVision": "Night Vision", + "AlchemistVisionOnLightsOut": "Night Vision During Lights Sabotage", + "AlchemistVisionDur": "Night Vision Potion Duration", + "AlchemistSpeed": "Speed Potion Boost", + "AlchemistVentButtonText": "Drink", + "AlchemistGotShieldPotion": "Potion of Resistance: Grants a temporary shield", + "AlchemistGotSightPotion": "Potion of Night Vision: Gives temporary enhanced vision", + "AlchemistGotQFPotion": "Potion of Fixing: Allows you to fix one sabotage instantly", + "AlchemistGotTPPotion": "Potion of Warping: Teleports you to a random player", + "AlchemistGotSuicidePotion": "Potion of Poison: Poisons you", + "AlchemistGotSpeedPotion": "Potion Of Speed: Hastens you", + "AlchemistGotBloodlustPotion": "Potion of Harming: Kill the next player you touch", + "AlchemistGotInvisibility": "Potion of Invisibility: Become Invisible", + "NoPotion": "You have no potions", + + "StoreShield": "Potion of Resistance", + "StoreSuicide": "Potion of Poison", + "StoreTP": "Potion of Warping", + "StoreSP": "Potion Of Speed", + "StoreQF": "Potion of Fixing", + "StoreBL": "Potion of Harming", + "StoreNS": "Potion of Night Vision", + "StoreNull": "None", + "PotionStore": "Potion in store: ", + "WaitQFPotion": "\nPotion of Fixing waiting for use", + + "AlchemistShielded": "Potion of Resistance started", + "AlchemistHasVision": "Potion of Night Vision started", + "AlchemistShieldOut": "Potion of Resistance ended", + "AlchemistVisionOut": "Potion of Night Vision ended", + "AlchemistPotionBloodlust": "You gained bloodlust", + "AlchemistHasSpeed": "Potion Of Speed started", + "AlchemistSpeedOut": "Potion Of Speed ended", + + "DeathpactDuration": "Death Pact duration", + "DeathPactCooldown": "Death Pact Assign Cooldown", + "DeathpactNumberOfPlayersInPact": "Number of players in Death Pact", + "DeathpactShowArrowsToOtherPlayersInPact": "Show arrows leading to other players in Death Pact", + "DeathpactReduceVisionWhileInPact": "Reduce vision for players in Death Pact", + "DeathpactVisionWhileInPact": "Vision for players in Death Pact", + "DeathpactKillPlayersInDeathpactOnMeeting": "Kill players in Death Pact on meeting", + "DeathpactPlayersInDeathpactCanCallMeeting": "Players in active Death Pact can call meeting", + "DeathpactActiveDeathpact": "Find {0} in {1} seconds.", + "DeathpactCouldNotAddTarget": "Target can't be added to Death Pact.", + "DeathpactComplete": "Death Pact was concluded.", + "DeathpactExecuted": "Death Pact was executed.", + "DeathpactAverted": "Death Pact was averted.", + "DeathpactButtonText": "Assign", + "DevourerHideNameConsumed": "Hide the names of consumed players", + "DevourCooldown": "Devour Cooldown", + "DevourerButtonText": "Devour", + "EatenByDevourer": "Your skin was eaten by the Devourer", + "DevourerEatenSkin": "Target skin eaten", + "DevouredName": "Devoured", + "PitfallTrapCooldown": "Trap Cooldown", + "PitfallMaxTrapCount": "Number of Traps that can be set", + "PitfallTrapMaxPlayerCount": "Number of Players that can be caught per Trap", + "PitfallTrapDuration": "Time the Trap remains active", + "PitfallTrapRadius": "Trap Radius", + "PitfallTrapFreezeTime": "Trap freeze time", + "PitfallTrapCauseVision": "Trap caused vision", + "PitfallTrapCauseVisionTime": "Trap caused vision time", + "PitfallTrap": "You have fallen into a trap!", + "ConsigliereDivinationMaxCount": "Maximum Reveals", + "RitualMaxCount": "Maximum Reveals", + "CleanserHideVote": "Hide Cleanser's vote", + "OracleSkillLimit": "Maximum Uses", + "OracleHideVote": "Hide Oracle's vote", + "OracleCheckReachLimit": "You're out of uses!", + "OracleCheckSelfMsg": "You can't even trust yourself, huh?", + "OracleCheckLimit": "Reminder: You have {0} uses left", + "OracleCheckMsgTitle": "ORACLE ", + "OracleCheck.NotCrewmate": "Appears to not be a crewmate", + "OracleCheck.Crewmate": "Appears to be a crewmate", + "OracleCheck.Neutral": "Appears to be a neutral", + "OracleCheck.Impostor": "Appears to be an Impostor", + "OracleCheck": "Target Results:", + "FailChance": "Chance of showing incorrect result", + "OracleCheckAddons": "Oracle checks add-ons", + "ChameleonCanVent": "Vent to disguise", + "ChameleonInvisState": "You are disguising!", + "ChameleonInvisStateOut": "Your disguise ended", + "ChameleonInvisInCooldown": "Ability still on cooldown, disguise failed", + "ChameleonInvisStateCountdown": "Disguise will expire in {0}s", + "ChameleonInvisCooldownRemain": "Disguise Cooldown: {0}s", + "ChameleonCooldown": "Disguise Cooldown", + "ChameleonDuration": "Disguise Duration", + "ChameleonRevertDisguise": "Expose", + "ChameleonDisguise": "Disguise", + "KillCooldownAfterCleaning": "Kill Cooldown On Clean", + "KillCooldownAfterStoneGazing": "Kill Cooldown On Stone Gaze", + "MedusaStoneBody": "Body stoned", + "MedusaReportButtonText": "Stone", + + "CursedSoulCurseCooldown": "Soul Snatch Cooldown", + "CursedSoulCurseCooldownIncrese": "Soul Snatch Cooldown Increase", + "CursedSoulCurseMax": "Maximum Soul Snatches", + "CursedSoulKnowTargetRole": "Know the roles of Soulless players", + "CursedSoulCanCurseNeutral": "Neutral roles have souls", + "CursedSoulKillButtonText": "Snatch", + "SoullessByCursedSoul": "Your soul was snatched by a Cursed Soul", + "CursedSoulSoullessPlayer": "Soul snatched", + "CursedSoulInvalidTarget": "No soul found", + + "AdmireCooldown": "Admire Cooldown", + "AdmirerKnowTargetRole": "Know the roles of Admired players", + "AdmirerSkillLimit": "Skill Limit", + "AdmireButtonText": "Admire", + "AdmirerAdmired": "The Admirer admired you!", + "AdmiredPlayer": "Player admired", + "AdmirerInvalidTarget": "Target cannot be admired", + + "SpiritualistNoticeTitle": "SPIRITUALIST ", + "SpiritualistNoticeMessage": "The Spiritualist has an arrow pointing to you!\nYou can use them to a killer or frame a crewmate", + "SpiritualistShowGhostArrowForSeconds": "Ghost arrow duration", + "SpiritualistShowGhostArrowEverySeconds": "Ghost arrow interval", + "EnigmaClueStage1Tasks": "Number of Tasks to complete to see Stage 1 Clues", + "EnigmaClueStage2Tasks": "Number of Tasks to complete to see Stage 2 Clues", + "EnigmaClueStage3Tasks": "Number of Tasks to complete to see Stage 3 Clues", + "EnigmaClueStage2Probability": "Probability to see Stage 2 Clues", + "EnigmaClueStage3Probability": "Probability to see Stage 3 Clues", + "EnigmaClueGetCluesWithoutReporting": "Enigma can get Clues without reporting a dead body", + "EnigmaClueHat1": "The Killer wears a Hat!", + "EnigmaClueHat2": "The Killer does not wear a Hat!", + "EnigmaClueHat3": "The Killer wears {0} as a Hat!", + "EnigmaClueSkin1": "The Killer wears a Skin!", + "EnigmaClueSkin2": "The Killer does not wear a Skin!", + "EnigmaClueSkin3": "The Killer wears {0} as a Skin!", + "EnigmaClueVisor1": "The Killer wears a Visor!", + "EnigmaClueVisor2": "The Killer does not wear a Visor!", + "EnigmaClueVisor3": "The Killer wears {0} as a Visor!", + "EnigmaCluePet1": "The Killer does have a Pet!", + "EnigmaCluePet2": "The Killer does not have a Pet!", + "EnigmaCluePet3": "The Killer has {0} as a Pet!", + "EnigmaClueName1": "The Name of the Killer contains the letter {0} or the letter {1}!", + "EnigmaClueName2": "The Name of the Killer contains the letter {0}!", + "EnigmaClueName3": "The Name of the Killer contains the letter {0} and the letter {1}!", + "EnigmaClueNameLength1": "The Name of the Killer has a Length between {0} and {1} letters!", + "EnigmaClueNameLength2": "The Name of the Killer has a Length of {0} letters!", + "EnigmaClueColor1": "The Killer has a light color!", + "EnigmaClueColor2": "The Killer has a dark color!", + "EnigmaClueColor3": "The Killer's color is {0}!", + "EnigmaClueLocation": "The Last Room the Killer was in is {0}!", + "EnigmaClueStatus1": "The Killer is currently inside a Vent!", + "EnigmaClueStatus2": "The Killer is currently on a Ladder!", + "EnigmaClueStatus3": "The Killer is already Dead!", + "EnigmaClueStatus4": "The Killer is still Alive!", + "EnigmaClueRole1": "The Killer is an Impostor!", + "EnigmaClueRole2": "The Killer is a Neutral!", + "EnigmaClueRole3": "The Killer is a Crewmate!", + "EnigmaClueRole4": "The Killer's Role is {0}!", + "EnigmaClueLevel1": "The Killer's Level is above 50!", + "EnigmaClueLevel2": "The Killer's Level is below 50!", + "EnigmaClueLevel3": "The Killer's Level is between {0} and {1}!", + "EnigmaClueLevel4": "The Killer's Level is {0}!", + "EnigmaClueFriendCode": "The Killer's Friendcode is {0}!", + "EnigmaClueHatTitle": "Enigma Hat Clue!", + "EnigmaClueVisorTitle": "Enigma Visor Clue!", + "EnigmaClueSkinTitle": "Enigma Skin Clue!", + "EnigmaCluePetTitle": "Enigma Pet Clue!", + "EnigmaClueNameTitle": "Enigma Name Clue!", + "EnigmaClueNameLengthTitle": "Enigma Name Length Clue!", + "EnigmaClueColorTitle": "Enigma Color Clue!", + "EnigmaClueLocationTitle": "Enigma Location Clue!", + "EnigmaClueStatusTitle": "Enigma Status Clue!", + "EnigmaClueRoleTitle": "Enigma Role Clue!", + "EnigmaClueLevelTitle": "Enigma Level Clue!", + "EnigmaClueFriendCodeTitle": "Enigma Friendcode Clue!", + + "ChiefOfPoliceSkillCooldown": "Cooldown for recruiting sheriffs", + "PolicCanImpostorAndNeutarl": "You can recruit Impostor or Kill Neutral to become sheriffs", + "SheriffSuccessfullyRecruited": "You recruited a sheriff.", + "BeSheriffByPolice": "You've been recruited by the police chief! Serve the crew!", + "ChiefOfPoliceKillButtonText": "Recruitment", + "VotesPerKill": "Votes gained for each kill", + "PickpocketGetVote": "You've got {0} votes", + "VultureArrowsPointingToDeadBody": "Arrows pointing to dead bodies", + "VultureNumberOfReportsToWin": "Bodies needed to win", + "VultureReportBody": "Body eaten!", + "VultureEatButtonText": "Consume", + "VultureReportCooldown": "Eat Cooldown", + "VultureMaxEatenInOneRound": "Maximum eaten bodies possible per round", + "VultureCooldownUp": "Eat Cooldown finished", + + "TasksMarkPerRound": "Number of tasks that can be marked in one round", + "TaskinatorBombPlanted": "Bomb has been planted", + + "ShieldDuration": "Shield duration", + "ShieldIsOneTimeUse": "Shield breaks after one kill attempt", + "BenefactorTaskMarked": "Task marked successfully", + "BenefactorTargetGotShield": "You got shield by Benefactor", + + "PirateTryHideMsg": "Hide Pirate's commands", + "SuccessfulDuelsToWin": "Number of successful duels needed to win", + "PirateMeetingMsg": "Duel with your target.\n\nDuel command:\n⦿ /duel 0\n⦿ /duel 1\n⦿ /duel 2\n\nYou win the duel if you choose the same option as the target", + "PirateTargetMeetingMsg": "The Pirate chose t' duel ye!\nDuel wit' honor or die o' shame.\n\n Duel command:\n⦿ /duel 0\n⦿ /duel 1\n⦿ /duel 2\n\nIf the Pirate chooses the same option as you or you don't participate, you'll die", + "PirateTitle": "PIRATE ", + "PirateTargetAlreadyChosen": "Yarr! Ye've already chosen a target.", + "PirateDead": "Ye be dead. Ye cannot duel anymore.", + "DuelAlreadyDone": "Ye 'ave already chosen an option fer the duel.", + "DuelDone": "Ye 'ave chosen yer option fer the duel.\nWait fer the meetin' to end to see the result.", + "DuelHelp": "Duel command:\n⦿ /duel 0\n⦿ /duel 1\n⦿ /duel 2\n\nAs Pirate, try to choose the same number as the target.\nAs the target, try to choose a different number than the Pirate", + "PirateDuelButtonText": "Duel", + "DuelCooldown": "Duel Cooldown", + "Rock": "Rock", + "Paper": "Paper", + "Scissors": "Scissors", + "Heads": "Heads", + "Tails": "Tails", + "SpyRedNameDur": "Colored Name Duration", + "SpyInteractionBlocked": "Block kill button interaction", + "AgitaterBombCooldown": "Agitator bomb cooldown", + "AgitaterPassCooldown": "Bomb pass cooldown", + "BombExplodeCooldown": "Bomb explode cooldown", + "AgitaterPassNotify": "Bomb successfully passed", + "AgitaterTargetNotify": "YOU HAVE THE BOMB!! Pass it to someone else", + "AgitaterCanGetBombed": "Agitator can get bomb", + "AgitaterAutoReportBait": "Agitator Auto Report Bait", + + "SeekerPointsToWin": "Number of points required to win", + "SeekerTagCooldown": "Tag Cooldown", + "SeekerNotify": "Your target is {0}", + "SeekerTargetNotify": "You are Seekers target!! Hide before they tag you", + + "PixiePointsToWin": "Number of points required to win", + "MaxTargets": "Maximum number of targets per round", + "MarkCooldown": "Mark cooldown", + "PixieSuicide": "Pixie suicides if target is not voted out", + "PixieMaxTargetReached": "You have already selected all the targets this round", + "PixieTargetAlreadySelected": "Target is already selected", + "PixieButtonText": "Mark", + + "PlagueBearerCD": "Plague cooldown", + "PestilenceCD": "Pestilence Kill cooldown", + "PlagueBearerAlreadyPlagued": "Player has already been plagued", + "PlagueBearerToPestilence": "You have turned into Pestilence!!", + "PestilenceCanVent": "Pestilence Can Vent", + "PestilenceHasImpostorVision": "Pestilence Has Impostor Vision", + "GuessPestilence": "You just tried to guess Pestilence!\n\nSorry, Pestilence killed you.", + "PestilenceTransform": "A Plague has consumed the Crew, transforming the Plaguebearer into Pestilence, Horseman of the Apocalypse!", + "RomanticBetCooldown": "Pick Partner Cooldown", + "RomanticProtectCooldown": "Protect Cooldown", + "RomanticBetPlayer": "You picked your partner", + "RomanticBetOnYou": "The Romantic chose you as their Partner!", + "VengefulKCD": "Vengeful Romantic Kill Cooldown", + "VengefulCanVent": "Vengeful Romantic Can Vent", + "RuthlessKCD": "Ruthless Romantic Kill Cooldown", + "RuthlessCanVent": "Ruthless Romantic Can Vent", + "RomanticProtectPartner": "Your partner is under protection", + "RomanticIsProtectingYou": "The Romantic is protecting you", + "ProtectingOver": "Shield expired", + "RomanticProtectDuration": "Protect Duration", + "RomanticKnowTargetRole": "Romantic knows their target's role", + "RomanticBetTargetKnowRomantic": "Target knows who the Romantic is", + + "GuessMasterMisguess": "{0} misguessed", + "GuessMasterTargetRole": "Someone tried to guess {0}", + "GuessMasterTitle": "Guess Master ", + + "DoomsayerAmountOfGuessesToWin": "Amount of Guesses to win", + "DCanGuessImpostors": "Can Guess Impostors", + "DCanGuessCrewmates": "Can Guess Crewmates", + "DCanGuessNeutrals": "Can Guess Neutrals", + "DCanGuessAdt": "Can Guess Add-Ons", + "DoomsayerAdvancedSettings": "Advanced Settings", + "DoomsayerMaxNumberOfGuessesPerMeeting": "Max number of guesses per meeting", + "DoomsayerKillCorrectlyGuessedPlayers": "Kill correctly guessed players", + "DoomsayerDoesNotSuicideWhenMisguessing": "Doomsayer does not suicide when misguessing", + "DoomsayerMisguessRolePrevGuessRoleUntilNextMeeting": "Misguessing role prevents guessing roles until next meeting", + "DoomsayerTryHideMsg": "Hide Doomsayer's commands", + "DoomsayerCantGuess": "Sorry, you can only guess the roles in the next meeting.", + "DoomsayerCorrectlyGuessRole": "You guessed the role correctly!\nBut the player didn't die because the Host settings don't allow them to die", + "DoomsayerNotCorrectlyGuessRole": "You didn't correctly guess the role!\nBut you didn't die because the Host's settings don't allow you to die", + "DoomsayerGuessCountMsg": "You correctly guessed {0} roles", + "DoomsayerGuessCountTitle": "DOOMSAYER", + "DoomsayerGuessSameRoleAgainMsg": "You tried to guess the same role or add-on that you guessed before", + + "EveryoneCanKnowMini": "Everyone can see the Mini", + "CanBeEvil": "Mini can be an Impostor", + "EvilMiniSpawnChances": "Probability of Mini being an Impostor", + "GuessMini": "Sorry, you can't hurt a kid Mini.", + "GrowUpDuration": "Time required to grow (s)", + "MajorCooldown": "Kill Cooldown when over 18", + "UpDateAge": "Display age change in real-time", + "Cantkillkid": "You can't kill a Mini that hasn't grown up.", + "CantEat": "You can't eat a Mini that hasn't grown up", + "CantShroud": "You can't control a Mini that hasn't grown up.", + "CantBoom": "You can't blow yourself up with a Mini that hasn't grown up.", + "CantRecruit": "You can't recruit a Mini that hasn't grown up.", + "ExiledNiceMini": "You ejected a Nice Mini before they grew up.\nYou all lose", + "MiniUp": "You're a year older!", + "MiniMisGuessed": "You are supposed to misguess to death!\nHowever you are still a kid, so you are free of guilty while you can no longer guess.\nYou can guess again after you have grown up.", + "MiniGuessMax": "You have misguessed, so you are no longer allowed to guess!", + "CountMeetingTime": "Meeting time can continue to grow", + "YouKillRandomizer1": "You kill Randomizer, Self report!", + "YouKillRandomizer2": "You kill Randomizer, Cannot move!", + "YouKillRandomizer3": "You kill Randomizer, Kill CD change to 600s!", + "YouKillRandomizer4": "You kill Randomizer, Triggered Random Revenge!", + "MadmateCanBeHurried": "Madmate can be Hurried on game start", + "TaskBasedCrewCanBeHurried": "Task based Crews can be Hurried", + "HurriedCanBeConverted": "Hurried can be recruited in game (excludes madmate)", + "Developer": "Developer", + "Sponsor": "Sponsor", + "Booster": "Server Booster", + "Translator": "Translator", + "NoAccess": "Unauthorized Access!!!\n\n Please open up a ticket in the discord server to know more (discord.gg/tohe)", + "DCNotify.Hacking": "You were banned for hacking.\n\nPlease stop.", + "DCNotify.Banned": "You were banned from this lobby.\n\nContact the host if this was a mistake.", + "DCNotify.Kicked": "You were kicked from this lobby.\n\nYou may still rejoin.", + "DCNotify.DCFromServer": "You disconnected from the server.\r\nThis could be an issue with either the servers or your network.", + "DCNotify.GameNotFound": "This lobby code is invalid.\n\nCheck the code and/or server and try again.", + "DCNotify.GameStarted": "This lobby is currently in-game.\n\nWait for it to end or find a different lobby.", + "DCNotify.GameFull": "This lobby is currently full.\n\nCheck with the host to see if you may join.", + "DCNotify.IncorrectVersion": "This lobby does not support your Among Us version.", + "DCNotify.Inactivity": "The lobby closed due to inactivity.", + "DCNotify.Auth": "You are not authenticated.\n\nYou may need to restart your game.", + "DCNotify.DupeLogin": "An instance of your account is already present in this lobby.", + "DCNotify.InvalidSettings": "Game settings have been detected to be invalid.\n\nEnter local play to reset them, then try again.", + "ModeDescribe.SoloKombat": "Current mode is [Solo PVP]\nNo role assignment. Everyone has HP and can use the kill button to cause damage to other players. The player with the highest number of kills wins at the end of the game.", + "RoleType.VanillaRoles": "★ Vanilla Roles", + "RoleType.ImpKilling": "★ Impostor Killing Roles", + "RoleType.ImpSupport": "★ Impostor Support Roles", + "RoleType.ImpConcealing": "★ Impostor Concealing Roles", + "RoleType.ImpHindering": "★ Impostor Hindering Roles", + "RoleType.ImpGhost": "★ Impostor Ghost Roles /ghostinfo", + "RoleType.Madmate": "★ Madmate Roles", + "RoleType.CrewSupport": "★ Crewmate Support Roles", + "RoleType.CrewInvestigative": "★ Crewmate Investigative Roles", + "RoleType.CrewPower": "★ Crewmate Power Roles", + "RoleType.CrewKilling": "★ Crewmate Killing Roles", + "RoleType.CrewBasic": "★ Crewmate Basic Roles", + "RoleType.CrewGhost": "★ Crewmate Ghost Roles /ghostinfo", + "RoleType.NeutralEvil": "★ Neutral Evil Roles", + "RoleType.NeutralBenign": "★ Neutral Benign Roles", + "RoleType.NeutralChaos": "★ Neutral Chaos Roles", + "RoleType.NeutralKilling": "★ Neutral Killing Roles", + "RoleType.NeutralApocalypse": "★ Neutral Apocalypse Roles", + "RoleType.Harmful": "★ Harmful Add-ons", + "RoleType.Support": "★ Supportive Add-ons", + "RoleType.Helpful": "★ Helpful Add-ons", + "RoleType.Mixed": "★ Mixed Add-ons", + "RoleType.Misc": "★ Miscellaneous Add-ons", + "RoleType.Impostor": "★ Impostor Add-ons", + "RoleType.Neut": "★ Neutral Add-ons", + "SubType.Impostor": "★ Impostors", + "SubType.Shapeshifter": "★ Shapeshifters", + "SubType.SemiShapeshifter": "★ Semi-Shapeshifters", + "SubType.Madmate": "★ Madmates", + "SubType.CrewmateKilling": "★ Crewmate Killings", + "SubType.Crewmate": "★ Regular Crewmates", + "SubType.New": "★ New!", + "CrewmateRoles": "★ Crewmate Roles ★", + "ImpostorRoles": "★ Impostor Roles ★", + "NeutralRoles": "★ Neutral Roles ★", + "AddonRoles": "★ Add-ons ★", + "WinnerRoleText.Impostor": "Impostors Win!", + "WinnerRoleText.Crewmate": "Crewmates Win!", + "WinnerRoleText.Apocalypse": "Apocalypse Wins!", + "WinnerRoleText.Terrorist": "Terrorist Wins!", + "WinnerRoleText.Jester": "Jester Wins!", + "WinnerRoleText.Lovers": "Lovers Win!", + "WinnerRoleText.Executioner": "Executioner Wins!", + "WinnerRoleText.Arsonist": "Arsonist Wins!", + "WinnerRoleText.Revolutionist": "Revolutionist Wins!", + "WinnerRoleText.Jackal": "Jackals Win!", + "WinnerRoleText.God": "God Wins!", + "WinnerRoleText.Vector": "Vector Wins!", + "WinnerRoleText.Innocent": "Innocent Wins!", + "WinnerRoleText.Pelican": "Pelican Wins!", + "WinnerRoleText.Youtuber": "YouTuber Wins!", + "WinnerRoleText.Necromancer": "Necromancer Wins!", + "WinnerRoleText.Egoist": "Egoists Win!", + "WinnerRoleText.Demon": "Demon Wins!", + "WinnerRoleText.Stalker": "Stalker Wins!", + "WinnerRoleText.Workaholic": "Workaholic Wins!", + "WinnerRoleText.Collector": "Collector Wins!", + "WinnerRoleText.BloodKnight": "Blood Knight Wins!", + "WinnerRoleText.Poisoner": "Poisoner Wins!", + "WinnerRoleText.Huntsman": "Huntsman Wins!", + "WinnerRoleText.HexMaster": "Hex Master Wins!", + "WinnerRoleText.Cultist": "Cultist Wins!", + "WinnerRoleText.Wraith": "Wraith Wins!", + "WinnerRoleText.SerialKiller": "Serial Killers Win!", + "WinnerRoleText.Juggernaut": "Juggernaut Wins!", + "WinnerRoleText.Infectious": "Infectious Wins!", + "WinnerRoleText.Virus": "Virus Wins!", + "WinnerRoleText.Phantom": "Phantom Wins!", + "WinnerRoleText.Jinx": "Jinx Wins!", + "WinnerRoleText.CursedSoul": "Cursed Soul Wins!", + "WinnerRoleText.PotionMaster": "Potion Master Wins!", + "WinnerRoleText.Pickpocket": "Pickpocket Wins!", + "WinnerRoleText.Traitor": "Traitor Wins!", + "WinnerRoleText.Vulture": "Vulture Wins!", + "WinnerRoleText.Medusa": "Medusa Wins!", + "WinnerRoleText.Famine": "Famine Wins!", + "WinnerRoleText.Spiritcaller": "Spiritcaller Wins!", + "WinnerRoleText.Glitch": "Glitch Wins!", + "WinnerRoleText.Pestilence": "Pestilence Wins!", + "WinnerRoleText.PlagueBearer": "Plaguebearer Wins!", + "WinnerRoleText.Masochist": "Masochist Wins!", + "WinnerRoleText.Doomsayer": "Doomsayer Wins!", + "WinnerRoleText.Pirate": "Pirate Wins!", + "WinnerRoleText.Shroud": "Shroud Wins!", + "WinnerRoleText.Werewolf": "Werewolf Wins!", + "WinnerRoleText.Seeker": "Seeker Wins!", + "WinnerRoleText.Agitater": "Agitator Wins!", + "WinnerRoleText.Occultist": "Occultist Wins!", + "WinnerRoleText.SoulCollector": "Soul Collector Wins!", + "WinnerRoleText.NiceMini": "Nice Mini Wins!", + "WinnerRoleText.Mini": "Nice Mini was killed", + "WinnerRoleText.Bandit": "Bandit Wins!", + "WinnerRoleText.RuthlessRomantic": "Ruthless Romantic Wins!", + "WinnerRoleText.Solsticer": "Solsticer Wins!", + "WinnerRoleText.Pyromaniac": "Pyromaniac Wins!", + "WinnerRoleText.Doppelganger": "Doppelganger Wins!", + "AdditionalWinnerRoleText.Sidekick": "Sidekick", + "AdditionalWinnerRoleText.Taskinator": "Taskinator", + "AdditionalWinnerRoleText.Opportunist": "Opportunist", + "AdditionalWinnerRoleText.Lawyer": "Lawyer", + "AdditionalWinnerRoleText.Hater": "Hater", + "AdditionalWinnerRoleText.Provocateur": "Provocateur", + "AdditionalWinnerRoleText.Sunnyboy": "Sunnyboy", + "AdditionalWinnerRoleText.Follower": "Follower", + "AdditionalWinnerRoleText.Pursuer": "Pursuer", + "AdditionalWinnerRoleText.Jester": "Jester", + "AdditionalWinnerRoleText.Lovers": "Lovers", + "AdditionalWinnerRoleText.Executioner": "Executioner", + "AdditionalWinnerRoleText.Phantom": "Phantom", + "AdditionalWinnerRoleText.Maverick": "Maverick", + "AdditionalWinnerRoleText.Shaman": "Shaman", + "AdditionalWinnerRoleText.Pixie": "Pixie", + "AdditionalWinnerRoleText.NiceMini": "Nice Mini", + "AdditionalWinnerRoleText.Romantic": "Romantic", + "AdditionalWinnerRoleText.VengefulRomantic": "Vengeful Romantic", + "AdditionalWinnerRoleText.SchrodingersCat": "Schrodingers Cat", + "ErrorEndText": "An error occurred", + "ErrorEndTextDescription": "To avoid crashing, the game was forcibly ended.", + "ForceEnd": "Aborted", + "EveryoneDied": "Everyone died", + "ForceEndText": "Host has aborted the game", + "NiceMiniDied": "Nice Mini was killed", + "HaterMisFireKillTarget": "Hater kills target when misfire", + "HaterChooseConverted": "Select addons that Hater can kill", + "HaterCanKillMadmate": "Can kill madmate", + "HaterCanKillCharmed": "Can kill charmed", + "HaterCanKillLovers": "Can kill lovers", + "HaterCanKillSidekick": "Can kill jackal team", + "HaterCanKillEgoist": "Can kill egoist", + "HaterCanKillInfected": "Can kill infected team", + "HaterCanKillContagious": "Can kill virus team", + "HaterCanKillAdmired": "Can kill admirer", + "AutoMuteUs": "Enable it if you use AutoMuteUs", + "HorseMode": "Enable to become a horse", + "LongMode": "Enable to have a long neck", + "InfluencedChangeVote": "oops!You are so influenced by others!\nYou can not contain your fear that you change voted {0}!", + + + "FFA": "Free For All", + "ModeFFA": "Gamemode: FFA", + "ModeDescribe.FFA": "In the FFA (Free For All) gamemode, everyone is a killer and everyone can kill anyone. The last player alive wins!\n\nSome random events make this even more fun in the mean time!", + "FFA_GameTime": "Maximum Game Length", + "FFA_KCD": "Kill Cooldown", + "FFA_DisableVentingWhenTwoPlayersAlive": "Prevent venting when only 2 players are alive", + "FFA_EnableRandomAbilities": "Enable Random Events", + "FFA_ShieldDuration": "Shield Duration", + "FFA_IncreasedSpeed": "Increased Speed", + "FFA_DecreasedSpeed": "Decreased Speed", + "FFA_ModifiedSpeedDuration": "Modified Speed Duration", + "FFA_LowerVision": "Lowered Vision", + "FFA_ModifiedVisionDuration": "Lowered Vision Duration", + "FFA_EnableRandomTwists": "Enable Random Swaps from time to time", + "FFA-Event-GetShield": "You have a temporary shield!", + "FFA-Event-GetIncreasedSpeed": "You have a temporary speed boost!", + "FFA-Event-GetLowKCD": "You got a lower kill cooldown!", + "FFA-Event-GetHighKCD": "You got a higher kill cooldown", + "FFA-Event-GetLowVision": "You have lower vision temporarily", + "FFA-Event-GetDecreasedSpeed": "You have decreased speed temporarily", + "FFA-Event-GetTP": "You got teleported to a random vent!", + "FFA-Event-RandomTP": "Everyone was swapped with someone", + "FFA-NoVentingBecauseTwoPlayers": "There are only 2 players alive, stop hiding in vents!", + "FFA-NoVentingBecauseKCDIsUP": "Your kill cooldown is up, don't hide in vents!", + "FFA_DisableVentingWhenKCDIsUp": "Prevent players whose kill cooldown is up from venting", + "FFA_TargetIsShielded": "The player you tried to kill is shielded!", + "FFA_ShieldIsOneTimeUse": "Shields break after 1 kill attempt", + "FFA_ShieldBroken": "Someone tried to kill you, your shield is now broken!", + "Killer": "FREE FOR ALL", + "KillerInfo": "Kill Everyone to Win", + + "Hide&SeekTOHE": "Hide & Seek", + "MenuTitle.Hide&Seek": "Hide & Seek Settings", + "NumImpostorsHnS": "Num Impostors", + + "EveryOneKnowSolsticer": "Every One Know who is Solsticer", + "SolsticerKnowItsKiller": "Solsticer knows the role of whom used kill button on it", + "SolsticerSpeed": "Movement speed of Solsticer", + "SolsticerRemainingTaskWarned": "Remaining tasks to be known", + "SAddTasksPreDeadPlayer": "How many extra short tasks Solsticer gets when a player dies", + "SolsticerMurdered": "{0} attempted to murder you!", + "MurderSolsticer": "You stopped Solsticer this round!", + "SolsticerMurderMessage": "{0} used kill button on you last round! Its role is {1}!", + "SolsticerOnMeeting": "You witnessed too many deaths! Next round you will have {0} more short task!", + "SolsticerTitle": "Solsticer", + "GuessSolsticer": "Sorry, but you can not guess Solsticer!", + "VoteSolsticer": "Sorry, but you can not vote Solsticer!", + "SolsticerTasksReset": "Your tasks get reset!", + "SolsticerMisGuessed": "You just misguessed! You are no longer allowed to guess.", + "SolsticerGuessMax": "Because you already misguessed, you are no longer allowed to guess.", + + "VoteDead": "The player you voted for was exhiled before the meeting concluded. Your vote was rescinded.", + + "ImpCanBeSilent": "Impostors can become Silent", + "CrewCanBeSilent": "Crewmates can become Silent", + "NeutralCanBeSilent": "Neutrals can become Silent", + "LastMessageReplay": "Last System Message Replay", + "Contributor": "Contributor", + + "dbConnect.InitFailure": "Error while connecting to TOHE api, pls check your network connection and retry login!", + "dbConnect.nullFriendCode": "This build of TOHE is not aviliable to users with no friendcode!", + + "ImpCanBeSusceptible": "Impostors can become Susceptible", + "CrewCanBeSusceptible": "Crewmates can become Susceptible", + "NeutralCanBeSusceptible": "Neutrals can become Susceptible", + + "Quizmaster": "Quizmaster", + "QuizmasterInfo": "Quiz people to kill them in meetings", + "QuizmasterInfoLong": "(Neutrals):\nAs the Quizmaster, you can mark a player using your kill button. In the next meeting, the marked player will \"?!\" next to their name. If the player answers a question wrongly, or doesn't answer, they will die. If the Quizmaster was killed/ejected in the same meeting, the player will live.\nThe Quizmaster cannot mark multiple people in the same round", + "QuizmasterKillButtonText": "Quiz", + + "QuizmasterChat.MarkedBy": "You've been marked by the Quizmaster\nTo survive you have to answer correct to this question:\n\n{QMQUESTION}", + "QuizmasterChat.MarkedPublic": "{QMTARGET} has been marked by the Quizmaster\nTo survive {QMTARGET} have to answer correct to their question!", + "QuizmasterChat.Answers": "Answers\nA: {QMA}\nB: {QMB}\nC: {QMC}\n\nTo answer just type /answer [answer letter]\n\nIf you need to recheck the answer and questions just do /qmquiz", + "QuizmasterChat.CorrectTarget": "Correct", + "QuizmasterChat.Correct": "{QMTARGET} got the right answer!\nYou can now mark someone else!", + "QuizmasterChat.CorrectPublic": "{QMTARGET} got the Quizmaster's question answer correct and survived!\nBeware of the Quizmaster!", + "QuizmasterChat.WrongTarget": "Wrong\nYour answer was {QMWRONG}\nThe correct answer was {QMRIGHT}\n\nThe Quizmaster was {QM}", + "QuizmasterChat.Wrong": "{QMTARGET} got the wrong answer and died!\nYou can now mark someone else!", + "QuizmasterChat.WrongPublic": "{QMTARGET} got the Quizmaster's question answer wrong and died!\nBeware of the Quizmaster!", + "QuizmasterChat.Marked": "You've marked {QMTARGET}\nIf {QMTARGET} doesn't answer by the end of the meeting or answer wrong {QMTARGET} will die", + "QuizmasterChat.Title": "Quizmaster Information", + "QuizmasterChat.CantAnswer": "As the quizmaster you can't answer questions", + "QuizmasterChat.AnswerNotValid": "Your answer must be A, B or C", + "QuizmasterChat.SyntaxNotValid": "Usage:\n/answer [A/B/C]", + + "QuizmasterSettings.QuestionDifficulty": "Question Difficulty", + "QuizmasterSettings.CanVentAfterMark": "Can Vent After Marked Somebody For Quiz", + "QuizmasterSettings.CanKillAfterMark": "Can Kill After Marked Somebody For Quiz", + "QuizmasterSettings.NumOfKillAfterMark": "How Many Kills Per Round", + "QuizmasterSettings.CanGiveQuestionsAboutPastGames": "Can Give Questions About Past Games", + + "Quizmaster.None": "None", + + "QuizmasterSabotages.Lights": "Lights", + "QuizmasterSabotages.Reactor": "Reactor", + "QuizmasterSabotages.Communications": "Communications", + "QuizmasterSabotages.O2": "O2", + "QuizmasterSabotages.MushroomMixup": "Mushroom Mixup", + "QuizmasterAnswers.One": "One", + "QuizmasterAnswers.Two": "Two", + "QuizmasterAnswers.Three": "Three", + "QuizmasterAnswers.Four": "Four", + "QuizmasterAnswers.Five": "Five", + "QuizmasterAnswers.Pacifist": "Pacifist", + "QuizmasterAnswers.Vampire": "Vampire", + "QuizmasterAnswers.Snitch": "Snitch", + "QuizmasterAnswers.Vigilante": "Vigilante", + "QuizmasterAnswers.Jackal": "Jackal", + "QuizmasterAnswers.Mole": "Mole", + "QuizmasterAnswers.Sniper": "Sniper", + "QuizmasterAnswers.Coven": "Coven", + "QuizmasterAnswers.Sabotuer": "Sabotuer", + "QuizmasterAnswers.Sorcerers": "Sorcerers", + "QuizmasterAnswers.Killer": "Killer", + "QuizmasterAnswers.Edition": "Edition", + "QuizmasterAnswers.Experimental": "Experimental", + "QuizmasterAnswers.Enhanced": "Enhanced", + "QuizmasterAnswers.Edited": "Edited", + + "QuizmasterQuestions.LastSabotage": "What was the sabotage was called last?", + "QuizmasterQuestions.FirstRoundSabotage": "What was the first sabotage called this round?", + "QuizmasterQuestions.LastEjectedPlayerColor": "What was the color of the player that was last ejected?", + "QuizmasterQuestions.LastReportPlayerColor": "What was the color of the body that was last reported before this meeting?", + "QuizmasterQuestions.LastButtonPressedPlayerColor": "Who called last meeting before this meeting?", + "QuizmasterQuestions.MeetingPassed": "How many meetings have passed so far?", + "QuizmasterQuestions.HowManyFactions": "How many factions are in the game?", + "QuizmasterQuestions.BasisOfRole": "What's the basis of {QMRole}?", + "QuizmasterQuestions.FactionOfRole": "What's the faction of {QMRole}?", + "QuizmasterQuestions.FactionRemovedName": "What faction used to be in the game but was removed an update later?", + "QuizmasterQuestions.HowManyDiedFirstRound": "How many people died round one?", + "QuizmasterQuestions.ButtonPressedBefore": "How many people pressed the emergency button before this meeting?", + "QuizmasterQuestions.WhatDoesEOgMeansInName": "What did the E in TOHE originally stand for?", + "QuizmasterQuestions.PlrDieReason": "What was {PLR}'s cause of death?", + "QuizmasterQuestions.PlrDieMethod": "How did {PLR} die?", + "LastAddedRoleForKarped": "What was the last role added to TOHE before KARPED1EM stepped down?", + "QuizmasterQuestions.PlrDieFaction": "What kind of faction killed {PLR}?", + + "DeathReason.WrongAnswer": "Wrong Quiz Answer", + + "TPCooldown": "Teleport Cooldown", + "RiftsTooClose": "Location too close to the first rift", + "RiftCreated": "Rift made successfully", + "RiftsDestroyed": "All rifts Destroyed", + "RiftRadius": "Rift Radius", + + "TiredVision": "Vision When Tired", + "TiredSpeed": "Speed When Tired", + "TiredDur": "Tired Duration", + "ImpCanBeTired": "Impostors can become Tired", + "CrewCanBeTired": "Crewmates can become Tired", + "NeutralCanBeTired": "Neutrals can become Tired", + + "TiredNotify": "Zzz..", + + "PlagueDoctorInfectLimit": "Infect Limit", + "PlagueDoctorInfectWhenKilled": "Infect Killer When Killed", + "PlagueDoctorInfectTime": "Infect Time", + "PlagueDoctorInfectDistance": "Infect Distance", + "PlagueDoctorInfectInactiveTime": "Delay Infection After Start The Game And After Meetings", + "PlagueDoctorCanInfectSelf": "Can Infect Self", + "PlagueDoctorCanInfectVent": "Can Infect While In Vent", + "WinnerRoleText.PlagueDoctor": "Plague Scientist Wins!", + + "StatueSlow": "Statue Slowness", + "StatuePeopleToSlow": "People Needed To Slow", + + "ImpCanBeStatue": "Impostors can become Statue", + "CrewCanBeStatue": "Crewmates can become Statue", + "NeutralCanBeStatue": "Neutrals can become Statue", + + "WardenIncreaseSpeed": "Increase Speed By", + "WardenWarn": "DANGER! RUN!", + + "MinionAbilityTime": "Ability Duration" +>>>>>>> Stashed changes } diff --git a/Resources/roleColor.json b/Resources/roleColor.json index 013de5269..6b1d44ad0 100644 --- a/Resources/roleColor.json +++ b/Resources/roleColor.json @@ -234,6 +234,13 @@ "Rainbow": "#55FFCB", "Apocalypse": "#ff174f", "Berserker": "#FF0055", +<<<<<<< Updated upstream "War": "#2B0804" "Statue": "#7E9C8A" +======= + "War": "#2B0804", + "Statue": "#7E9C8A", + "Warden": "#588733", + "Hawk": "#606c80" +>>>>>>> Stashed changes } diff --git a/Roles/AddOns/Common/Susceptible.cs b/Roles/AddOns/Common/Susceptible.cs index c4721f6f5..8f062966a 100644 --- a/Roles/AddOns/Common/Susceptible.cs +++ b/Roles/AddOns/Common/Susceptible.cs @@ -328,6 +328,26 @@ public static void CallEnabledAndChange(PlayerControl victim) } break; + case PlayerState.DeathReason.Armageddon: + if (!CustomRoles.SoulCollector.IsEnable()) + { + Main.PlayerStates[victim.PlayerId].deathReason = PlayerState.DeathReason.Kill; + } + else + { + goto default; + } + break; + case PlayerState.DeathReason.Starved: + if (!CustomRoles.Baker.IsEnable()) + { + Main.PlayerStates[victim.PlayerId].deathReason = PlayerState.DeathReason.Kill; + } + else + { + goto default; + } + break; case PlayerState.DeathReason.Armageddon: if (!CustomRoles.SoulCollector.IsEnable()) { diff --git a/Roles/Core/AssignManager/RoleAssign.cs b/Roles/Core/AssignManager/RoleAssign.cs new file mode 100644 index 000000000..440d369f9 --- /dev/null +++ b/Roles/Core/AssignManager/RoleAssign.cs @@ -0,0 +1,801 @@ +using AmongUs.GameOptions; +using TOHE.Roles.Double; +using TOHE.Roles.Impostor; +using TOHE.Roles.Neutral; +using static TOHE.Modules.ShuffleListExtension; + +namespace TOHE.Roles.Core.AssignManager; + +public class RoleAssign +{ + public static Dictionary SetRoles = []; + public static Dictionary RoleResult; + public static CustomRoles[] AllRoles => [.. RoleResult.Values]; + + enum RoleAssignType + { + Impostor, + NeutralKilling, + NonKillingNeutral, + NeutralApocalypse, + Crewmate + } + + public class RoleAssignInfo(CustomRoles role, int spawnChance, int maxCount, int assignedCount = 0) + { + public CustomRoles Role { get => role; set => role = value; } + public int SpawnChance { get => spawnChance; set => spawnChance = value; } + public int MaxCount { get => maxCount; set => maxCount = value; } + public int AssignedCount { get => assignedCount; set => assignedCount = value; } + } + + public static void GetNeutralCounts(int NKmaxOpt, int NKminOpt, int NNKmaxOpt, int NNKminOpt, int NAmaxOpt, int NAminOpt, ref int ResultNKnum, ref int ResultNNKnum, ref int ResultNAnum) + { + var rd = IRandom.Instance; + + if (NNKmaxOpt > 0 && NNKmaxOpt >= NNKminOpt) + { + ResultNNKnum = rd.Next(NNKminOpt, NNKmaxOpt + 1); + } + + if (NKmaxOpt > 0 && NKmaxOpt >= NKminOpt) + { + ResultNKnum = rd.Next(NKminOpt, NKmaxOpt + 1); + } + + if (NAmaxOpt > 0 && NAmaxOpt >= NAminOpt) + { + ResultNAnum = rd.Next(NAminOpt, NAmaxOpt + 1); + } + } + + public static void StartSelect() + { + switch (Options.CurrentGameMode) + { + case CustomGameMode.FFA: + RoleResult = []; + foreach (PlayerControl pc in Main.AllAlivePlayerControls) + { + RoleResult.Add(pc, CustomRoles.Killer); + } + return; + } + + RoleResult = []; + var rd = IRandom.Instance; + int playerCount = Main.AllAlivePlayerControls.Length; + int optImpNum = Main.RealOptionsData.GetInt(Int32OptionNames.NumImpostors); + int optNonNeutralKillingNum = 0; + int optNeutralKillingNum = 0; + int optNeutralApocalypseNum = 0; + + GetNeutralCounts(Options.NeutralKillingRolesMaxPlayer.GetInt(), Options.NeutralKillingRolesMinPlayer.GetInt(), Options.NonNeutralKillingRolesMaxPlayer.GetInt(), Options.NonNeutralKillingRolesMinPlayer.GetInt(), Options.NeutralApocalypseRolesMaxPlayer.GetInt(), Options.NeutralApocalypseRolesMinPlayer.GetInt(), ref optNeutralKillingNum, ref optNonNeutralKillingNum, ref optNeutralApocalypseNum); + + int readyRoleNum = 0; + int readyImpNum = 0; + int readyNonNeutralKillingNum = 0; + int readyNeutralKillingNum = 0; + int readyNeutralApocalypseNum = 0; + + List FinalRolesList = []; + + Dictionary> Roles = []; + + Roles[RoleAssignType.Impostor] = []; + Roles[RoleAssignType.NeutralKilling] = []; + Roles[RoleAssignType.NonKillingNeutral] = []; + Roles[RoleAssignType.NeutralApocalypse] = []; + Roles[RoleAssignType.Crewmate] = []; + + foreach (var id in SetRoles.Keys.Where(id => Utils.GetPlayerById(id) == null).ToArray()) SetRoles.Remove(id); + + foreach (var role in EnumHelper.GetAllValues()) + { + int chance = role.GetMode(); + if (role.IsVanilla() || chance == 0 || role.IsAdditionRole() || role.IsGhostRole()) continue; + switch (role) + { + case CustomRoles.Stalker when GameStates.FungleIsActive: + case CustomRoles.VengefulRomantic: + case CustomRoles.RuthlessRomantic: + case CustomRoles.GM: + case CustomRoles.NotAssigned: + case CustomRoles.NiceMini: + case CustomRoles.EvilMini: + continue; + } + + int count = role.GetCount(); + RoleAssignInfo info = new(role, chance, count); + + if (role.IsImpostor()) Roles[RoleAssignType.Impostor].Add(info); + else if (role.IsNK()) Roles[RoleAssignType.NeutralKilling].Add(info); + else if (role.IsNA()) Roles[RoleAssignType.NeutralApocalypse].Add(info); + else if (role.IsNonNK()) Roles[RoleAssignType.NonKillingNeutral].Add(info); + else Roles[RoleAssignType.Crewmate].Add(info); + } + + if (Roles[RoleAssignType.Impostor].Count == 0 && !SetRoles.Values.Any(x => x.IsImpostor())) + { + Roles[RoleAssignType.Impostor].Add(new(CustomRoles.ImpostorTOHE, 100, optImpNum)); + Logger.Warn("Adding Vanilla Impostor", "CustomRoleSelector"); + } + + Logger.Info($"Number of NKs: {optNeutralKillingNum}, Number of NonNKs: {optNonNeutralKillingNum}", "NeutralNum"); + Logger.Msg("=====================================================", "AllActiveRoles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.Impostor].Select(x => $"{x.Role}: {x.SpawnChance}% - {x.MaxCount}")), "ImpRoles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.NeutralKilling].Select(x => $"{x.Role}: {x.SpawnChance}% - {x.MaxCount}")), "NKRoles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.NeutralApocalypse].Select(x => $"{x.Role}: {x.SpawnChance}% - {x.MaxCount}")), "NARoles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.NonKillingNeutral].Select(x => $"{x.Role}: {x.SpawnChance}% - {x.MaxCount}")), "NonNKRoles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.Crewmate].Select(x => $"{x.Role}: {x.SpawnChance}% - {x.MaxCount}")), "CrewRoles"); + Logger.Msg("=====================================================", "AllActiveRoles"); + + IEnumerable TempAlwaysImpRoles = Roles[RoleAssignType.Impostor].Where(x => x.SpawnChance == 100); + IEnumerable TempAlwaysNKRoles = Roles[RoleAssignType.NeutralKilling].Where(x => x.SpawnChance == 100); + IEnumerable TempAlwaysNARoles = Roles[RoleAssignType.NeutralApocalypse].Where(x => x.SpawnChance == 100); + IEnumerable TempAlwaysNNKRoles = Roles[RoleAssignType.NonKillingNeutral].Where(x => x.SpawnChance == 100); + IEnumerable TempAlwaysCrewRoles = Roles[RoleAssignType.Crewmate].Where(x => x.SpawnChance == 100); + + // DistinctBy - Removes duplicate roles if there are any + // Shuffle - Shuffles all roles in the list into a randomized order + // Take - Takes the first x roles of the list ... x is the maximum number of roles we could need of that team + + Roles[RoleAssignType.Impostor] = Roles[RoleAssignType.Impostor].Shuffle(rd).Take(optImpNum).ToList(); + Roles[RoleAssignType.NeutralKilling] = Roles[RoleAssignType.NeutralKilling].Shuffle(rd).Take(optNeutralKillingNum).ToList(); + Roles[RoleAssignType.NeutralApocalypse] = Roles[RoleAssignType.NeutralApocalypse].Shuffle(rd).Take(optNeutralApocalypseNum).ToList(); + Roles[RoleAssignType.NonKillingNeutral] = Roles[RoleAssignType.NonKillingNeutral].Shuffle(rd).Take(optNonNeutralKillingNum).ToList(); + Roles[RoleAssignType.Crewmate] = Roles[RoleAssignType.Crewmate].Shuffle(rd).Take(playerCount).ToList(); + + Roles[RoleAssignType.Impostor].AddRange(TempAlwaysImpRoles); + Roles[RoleAssignType.NeutralKilling].AddRange(TempAlwaysNKRoles); + Roles[RoleAssignType.NeutralApocalypse].AddRange(TempAlwaysNARoles); + Roles[RoleAssignType.NonKillingNeutral].AddRange(TempAlwaysNNKRoles); + Roles[RoleAssignType.Crewmate].AddRange(TempAlwaysCrewRoles); + + Roles[RoleAssignType.Impostor] = Roles[RoleAssignType.Impostor].DistinctBy(x => x.Role).ToList(); + Roles[RoleAssignType.NeutralKilling] = Roles[RoleAssignType.NeutralKilling].DistinctBy(x => x.Role).ToList(); + Roles[RoleAssignType.NeutralApocalypse] = Roles[RoleAssignType.NeutralApocalypse].DistinctBy(x => x.Role).ToList(); + Roles[RoleAssignType.NonKillingNeutral] = Roles[RoleAssignType.NonKillingNeutral].DistinctBy(x => x.Role).ToList(); + Roles[RoleAssignType.Crewmate] = Roles[RoleAssignType.Crewmate].DistinctBy(x => x.Role).ToList(); + + Logger.Msg("======================================================", "SelectedRoles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.Impostor].Select(x => x.Role.ToString())), "Selected-Impostor-Roles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.NeutralKilling].Select(x => x.Role.ToString())), "Selected-NK-Roles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.NeutralApocalypse].Select(x => x.Role.ToString())), "Selected-NA-Roles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.NonKillingNeutral].Select(x => x.Role.ToString())), "Selected-NonNK-Roles"); + Logger.Info(string.Join(", ", Roles[RoleAssignType.Crewmate].Select(x => x.Role.ToString())), "Selected-Crew-Roles"); + Logger.Msg("======================================================", "SelectedRoles"); + + var AllPlayers = Main.AllAlivePlayerControls.ToList(); + + // Players on the EAC banned list will be assigned as GM when opening rooms + if (BanManager.CheckEACList(PlayerControl.LocalPlayer.FriendCode, PlayerControl.LocalPlayer.GetClient().GetHashedPuid())) + { + Main.EnableGM.Value = true; + RoleResult[PlayerControl.LocalPlayer] = CustomRoles.GM; + AllPlayers.Remove(PlayerControl.LocalPlayer); + } + + // Pre-Assigned Roles By Host Are Selected First + foreach (var item in SetRoles) + { + PlayerControl pc = AllPlayers.FirstOrDefault(x => x.PlayerId == item.Key); + if (pc == null) continue; + + RoleResult[pc] = item.Value; + AllPlayers.Remove(pc); + + if (item.Value.IsImpostor()) + { + Roles[RoleAssignType.Impostor].Where(x => x.Role == item.Value).Do(x => x.AssignedCount++); + readyImpNum++; + } + else if (item.Value.IsNK()) + { + Roles[RoleAssignType.NeutralKilling].Where(x => x.Role == item.Value).Do(x => x.AssignedCount++); + readyNeutralKillingNum++; + } + else if (item.Value.IsNA()) + { + Roles[RoleAssignType.NeutralApocalypse].Where(x => x.Role == item.Value).Do(x => x.AssignedCount++); + readyNeutralApocalypseNum++; + } + else if (item.Value.IsNonNK()) + { + Roles[RoleAssignType.NonKillingNeutral].Where(x => x.Role == item.Value).Do(x => x.AssignedCount++); + readyNonNeutralKillingNum++; + } + + readyRoleNum++; + + Logger.Warn($"Pre-Set Role Assigned: {pc.GetRealName()} => {item.Value}", "RoleAssign"); + } + + RoleAssignInfo[] Imps = []; + RoleAssignInfo[] NNKs = []; + RoleAssignInfo[] NKs = []; + RoleAssignInfo[] NAs = []; + RoleAssignInfo[] Crews = []; + + // Impostor Roles + { + List AlwaysImpRoles = []; + List ChanceImpRoles = []; + for (int i = 0; i < Roles[RoleAssignType.Impostor].Count; i++) + { + RoleAssignInfo item = Roles[RoleAssignType.Impostor][i]; + + if (item.SpawnChance == 100) + { + for (int j = 0; j < item.MaxCount; j++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + AlwaysImpRoles.Add(item.Role); + } + } + else + { + // Add 'MaxCount' (1) times + for (int k = 0; k < item.MaxCount; k++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + // Make "Spawn Chance ÷ 5 = x" (Example: 65 ÷ 5 = 13) + for (int j = 0; j < item.SpawnChance / 5; j++) + { + // Add Imp roles 'x' times (13) + ChanceImpRoles.Add(item.Role); + } + } + } + } + + RoleAssignInfo[] ImpRoleCounts = AlwaysImpRoles.Distinct().Select(GetAssignInfo).ToArray().AddRangeToArray(ChanceImpRoles.Distinct().Select(GetAssignInfo).ToArray()); + Imps = ImpRoleCounts; + + // Assign roles set to 100% + if (readyImpNum < optImpNum) + { + while (AlwaysImpRoles.Any()) + { + var selected = AlwaysImpRoles[rd.Next(0, AlwaysImpRoles.Count)]; + var info = ImpRoleCounts.FirstOrDefault(x => x.Role == selected); + AlwaysImpRoles.Remove(selected); + if (info.AssignedCount >= info.MaxCount) continue; + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + readyImpNum++; + + Imps = ImpRoleCounts; + + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyImpNum >= optImpNum) break; + } + } + + // Assign other roles when needed + if (readyRoleNum < playerCount && readyImpNum < optImpNum) + { + while (ChanceImpRoles.Any()) + { + var selectesItem = rd.Next(0, ChanceImpRoles.Count); + var selected = ChanceImpRoles[selectesItem]; + var info = ImpRoleCounts.FirstOrDefault(x => x.Role == selected); + + // Remove 'x' times + for (int j = 0; j < info.SpawnChance / 5; j++) + ChanceImpRoles.Remove(selected); + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + readyImpNum++; + + Imps = ImpRoleCounts; + + if (info.AssignedCount >= info.MaxCount) + while (ChanceImpRoles.Contains(selected)) + ChanceImpRoles.Remove(selected); + + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyImpNum >= optImpNum) break; + } + } + } + + // Neutral Roles + { + // Neutral Non-Killing Roles + { + List AlwaysNonNKRoles = []; + List ChanceNonNKRoles = []; + for (int i = 0; i < Roles[RoleAssignType.NonKillingNeutral].Count; i++) + { + RoleAssignInfo item = Roles[RoleAssignType.NonKillingNeutral][i]; + + if (item.SpawnChance == 100) + { + for (int j = 0; j < item.MaxCount; j++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + AlwaysNonNKRoles.Add(item.Role); + } + } + else + { + // Add 'MaxCount' (1) times + for (int k = 0; k < item.MaxCount; k++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + // Make "Spawn Chance ÷ 5 = x" (Example: 65 ÷ 5 = 13) + for (int j = 0; j < item.SpawnChance / 5; j++) + { + // Add Non-NK roles 'x' times (13) + ChanceNonNKRoles.Add(item.Role); + } + } + } + } + + RoleAssignInfo[] NonNKRoleCounts = AlwaysNonNKRoles.Distinct().Select(GetAssignInfo).ToArray().AddRangeToArray(ChanceNonNKRoles.Distinct().Select(GetAssignInfo).ToArray()); + NNKs = NonNKRoleCounts; + + // Assign roles set to 100% + if (readyNonNeutralKillingNum < optNonNeutralKillingNum) + { + while (AlwaysNonNKRoles.Any() && optNonNeutralKillingNum > 0) + { + var selected = AlwaysNonNKRoles[rd.Next(0, AlwaysNonNKRoles.Count)]; + var info = NonNKRoleCounts.FirstOrDefault(x => x.Role == selected); + AlwaysNonNKRoles.Remove(selected); + if (info.AssignedCount >= info.MaxCount) continue; + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + readyNonNeutralKillingNum++; + + NNKs = NonNKRoleCounts; + + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyNonNeutralKillingNum >= optNonNeutralKillingNum) break; + } + } + + // Assign other roles when needed + if (readyRoleNum < playerCount && readyNonNeutralKillingNum < optNonNeutralKillingNum) + { + while (ChanceNonNKRoles.Any() && optNonNeutralKillingNum > 0) + { + var selectesItem = rd.Next(0, ChanceNonNKRoles.Count); + var selected = ChanceNonNKRoles[selectesItem]; + var info = NonNKRoleCounts.FirstOrDefault(x => x.Role == selected); + + // Remove 'x' times + for (int j = 0; j < info.SpawnChance / 5; j++) + ChanceNonNKRoles.Remove(selected); + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + readyNonNeutralKillingNum++; + + NNKs = NonNKRoleCounts; + + if (info.AssignedCount >= info.MaxCount) + while (ChanceNonNKRoles.Contains(selected)) + ChanceNonNKRoles.Remove(selected); + + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyNonNeutralKillingNum >= optNonNeutralKillingNum) break; + } + } + } + + // Neutral Killing Roles + { + List AlwaysNKRoles = []; + List ChanceNKRoles = []; + for (int i = 0; i < Roles[RoleAssignType.NeutralKilling].Count; i++) + { + RoleAssignInfo item = Roles[RoleAssignType.NeutralKilling][i]; + + if (item.SpawnChance == 100) + { + for (int j = 0; j < item.MaxCount; j++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + AlwaysNKRoles.Add(item.Role); + } + } + else + { + // Add 'MaxCount' (1) times + for (int k = 0; k < item.MaxCount; k++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + // Make "Spawn Chance ÷ 5 = x" (Example: 65 ÷ 5 = 13) + for (int j = 0; j < item.SpawnChance / 5; j++) + { + // Add NK roles 'x' times (13) + ChanceNKRoles.Add(item.Role); + } + } + } + } + + RoleAssignInfo[] NKRoleCounts = AlwaysNKRoles.Distinct().Select(GetAssignInfo).ToArray().AddRangeToArray(ChanceNKRoles.Distinct().Select(GetAssignInfo).ToArray()); + NKs = NKRoleCounts; + + // Assign roles set to 100% + if (readyNeutralKillingNum < optNeutralKillingNum) + { + while (AlwaysNKRoles.Any() && optNeutralKillingNum > 0) + { + var selected = AlwaysNKRoles[rd.Next(0, AlwaysNKRoles.Count)]; + var info = NKRoleCounts.FirstOrDefault(x => x.Role == selected); + AlwaysNKRoles.Remove(selected); + if (info.AssignedCount >= info.MaxCount) continue; + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + readyNeutralKillingNum++; + + NKs = NKRoleCounts; + + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyNeutralKillingNum >= optNeutralKillingNum) break; + } + } + + // Assign other roles when needed + if (readyRoleNum < playerCount && readyNeutralKillingNum < optNeutralKillingNum) + { + while (ChanceNKRoles.Any() && optNeutralKillingNum > 0) + { + var selectesItem = rd.Next(0, ChanceNKRoles.Count); + var selected = ChanceNKRoles[selectesItem]; + var info = NKRoleCounts.FirstOrDefault(x => x.Role == selected); + + // Remove 'x' times + for (int j = 0; j < info.SpawnChance / 5; j++) + ChanceNKRoles.Remove(selected); + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + readyNeutralKillingNum++; + + NKs = NKRoleCounts; + + if (info.AssignedCount >= info.MaxCount) while (ChanceNKRoles.Contains(selected)) ChanceNKRoles.Remove(selected); + + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyNeutralKillingNum >= optNeutralKillingNum) break; + } + } + } + // Neutral Apocalypse Roles + { + List AlwaysNARoles = []; + List ChanceNARoles = []; + for (int i = 0; i < Roles[RoleAssignType.NeutralApocalypse].Count; i++) + { + RoleAssignInfo item = Roles[RoleAssignType.NeutralApocalypse][i]; + + if (item.SpawnChance == 100) + { + for (int j = 0; j < item.MaxCount; j++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + AlwaysNARoles.Add(item.Role); + } + } + else + { + // Add 'MaxCount' (1) times + for (int k = 0; k < item.MaxCount; k++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + // Make "Spawn Chance ÷ 5 = x" (Example: 65 ÷ 5 = 13) + for (int j = 0; j < item.SpawnChance / 5; j++) + { + // Add NA roles 'x' times (13) + ChanceNARoles.Add(item.Role); + } + } + } + } + + RoleAssignInfo[] NARoleCounts = AlwaysNARoles.Distinct().Select(GetAssignInfo).ToArray().AddRangeToArray(ChanceNARoles.Distinct().Select(GetAssignInfo).ToArray()); + NAs = NARoleCounts; + + // Assign roles set to 100% + if (readyNeutralApocalypseNum < optNeutralApocalypseNum) + { + while (AlwaysNARoles.Any() && optNeutralApocalypseNum > 0) + { + var selected = AlwaysNARoles[rd.Next(0, AlwaysNARoles.Count)]; + var info = NARoleCounts.FirstOrDefault(x => x.Role == selected); + AlwaysNARoles.Remove(selected); + if (info.AssignedCount >= info.MaxCount) continue; + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + readyNeutralApocalypseNum++; + + NAs = NARoleCounts; + + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyNeutralApocalypseNum >= optNeutralApocalypseNum) break; + } + } + + // Assign other roles when needed + if (readyRoleNum < playerCount && readyNeutralApocalypseNum < optNeutralApocalypseNum) + { + while (ChanceNARoles.Any() && optNeutralApocalypseNum > 0) + { + var selectesItem = rd.Next(0, ChanceNARoles.Count); + var selected = ChanceNARoles[selectesItem]; + var info = NARoleCounts.FirstOrDefault(x => x.Role == selected); + + // Remove 'x' times + for (int j = 0; j < info.SpawnChance / 5; j++) + ChanceNARoles.Remove(selected); + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + readyNeutralApocalypseNum++; + + NAs = NARoleCounts; + + if (info.AssignedCount >= info.MaxCount) while (ChanceNARoles.Contains(selected)) ChanceNARoles.Remove(selected); + + if (readyRoleNum >= playerCount) goto EndOfAssign; + if (readyNeutralApocalypseNum >= optNeutralApocalypseNum) break; + } + } + } + } + + // Crewmate Roles + { + List AlwaysCrewRoles = []; + List ChanceCrewRoles = []; + for (int i = 0; i < Roles[RoleAssignType.Crewmate].Count; i++) + { + RoleAssignInfo item = Roles[RoleAssignType.Crewmate][i]; + + if (item.SpawnChance == 100) + { + for (int j = 0; j < item.MaxCount; j++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + AlwaysCrewRoles.Add(item.Role); + } + } + else + { + // Add 'MaxCount' (1) times + for (int k = 0; k < item.MaxCount; k++) + { + // Don't add if Host has assigned this role by using '/up' + if (SetRoles.ContainsValue(item.Role)) + { + var playerId = SetRoles.FirstOrDefault(x => x.Value == item.Role).Key; + SetRoles.Remove(playerId); + continue; + } + + // Make "Spawn Chance ÷ 5 = x" (Example: 65 ÷ 5 = 13) + for (int j = 0; j < item.SpawnChance / 5; j++) + { + // Add Crew roles 'x' times (13) + ChanceCrewRoles.Add(item.Role); + } + } + } + } + + RoleAssignInfo[] CrewRoleCounts = AlwaysCrewRoles.Distinct().Select(GetAssignInfo).ToArray().AddRangeToArray(ChanceCrewRoles.Distinct().Select(GetAssignInfo).ToArray()); + Crews = CrewRoleCounts; + + // Assign roles set to ALWAYS + if (readyRoleNum < playerCount) + { + while (AlwaysCrewRoles.Any()) + { + var selected = AlwaysCrewRoles[rd.Next(0, AlwaysCrewRoles.Count)]; + var info = CrewRoleCounts.FirstOrDefault(x => x.Role == selected); + AlwaysCrewRoles.Remove(selected); + if (info.AssignedCount >= info.MaxCount) continue; + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + + Crews = CrewRoleCounts; + + if (readyRoleNum >= playerCount) goto EndOfAssign; + } + } + + // Assign other roles when needed + if (readyRoleNum < playerCount) + { + while (ChanceCrewRoles.Any()) + { + var selectesItem = rd.Next(0, ChanceCrewRoles.Count); + var selected = ChanceCrewRoles[selectesItem]; + var info = CrewRoleCounts.FirstOrDefault(x => x.Role == selected); + + // Remove 'x' times + for (int j = 0; j < info.SpawnChance / 5; j++) + ChanceCrewRoles.Remove(selected); + + FinalRolesList.Add(selected); + info.AssignedCount++; + readyRoleNum++; + + Crews = CrewRoleCounts; + + if (info.AssignedCount >= info.MaxCount) while (ChanceCrewRoles.Contains(selected)) ChanceCrewRoles.Remove(selected); + + if (readyRoleNum >= playerCount) goto EndOfAssign; + } + } + } + + EndOfAssign: + + if (Imps.Any()) Logger.Info(string.Join(", ", Imps.Select(x => $"{x.Role} - {x.AssignedCount}/{x.MaxCount} ({x.SpawnChance}%)")), "ImpRoleResult"); + if (NNKs.Any()) Logger.Info(string.Join(", ", NNKs.Select(x => $"{x.Role} - {x.AssignedCount}/{x.MaxCount} ({x.SpawnChance}%)")), "NNKRoleResult"); + if (NKs.Any()) Logger.Info(string.Join(", ", NKs.Select(x => $"{x.Role} - {x.AssignedCount}/{x.MaxCount} ({x.SpawnChance}%)")), "NKRoleResult"); + if (Crews.Any()) Logger.Info(string.Join(", ", Crews.Select(x => $"{x.Role} - {x.AssignedCount}/{x.MaxCount} ({x.SpawnChance}%)")), "CrewRoleResult"); + + if (FinalRolesList.Contains(CustomRoles.Mini)) + { + FinalRolesList.Remove(CustomRoles.Mini); + + if (Mini.CheckSpawnEvilMini()) + { + var tempImpRole = FinalRolesList.FirstOrDefault(role => role.IsImpostor()); + FinalRolesList.Remove(tempImpRole); + FinalRolesList.Add(CustomRoles.EvilMini); + } + else + { + FinalRolesList.Add(CustomRoles.NiceMini); + } + } + + if (Bomber.CheckSpawnNuker() && FinalRolesList.Remove(CustomRoles.Bomber)) FinalRolesList.Add(CustomRoles.Nuker); + if (Vampire.CheckSpawnVampiress() && FinalRolesList.Remove(CustomRoles.Vampire)) FinalRolesList.Add(CustomRoles.Vampiress); + if (Sunnyboy.CheckSpawn() && FinalRolesList.Remove(CustomRoles.Jester)) FinalRolesList.Add(CustomRoles.Sunnyboy); + if (Bard.CheckSpawn() && FinalRolesList.Remove(CustomRoles.Arrogance)) FinalRolesList.Add(CustomRoles.Bard); + + if (Romantic.HasEnabled) + { + if (FinalRolesList.Contains(CustomRoles.Romantic) && FinalRolesList.Contains(CustomRoles.Lovers)) + FinalRolesList.Remove(CustomRoles.Lovers); + } + + Logger.Info(string.Join(", ", FinalRolesList.Select(x => x.ToString())), "RoleResults"); + + while (AllPlayers.Any() && FinalRolesList.Any()) + { + var roleId = rd.Next(0, FinalRolesList.Count); + + CustomRoles assignedRole = FinalRolesList[roleId]; + + RoleResult[AllPlayers[0]] = assignedRole; + Logger.Info($"Player:{AllPlayers[0].GetRealName()} => {assignedRole}", "RoleAssign"); + + AllPlayers.RemoveAt(0); + FinalRolesList.RemoveAt(roleId); + } + + if (AllPlayers.Any()) + Logger.Warn("Role assignment error: There are players who have not been assigned a role", "RoleAssign"); + if (FinalRolesList.Any()) + Logger.Warn("Team assignment error: There is an unassigned team", "RoleAssign"); + return; + + RoleAssignInfo GetAssignInfo(CustomRoles role) => Roles.Values.FirstOrDefault(x => x.Any(y => y.Role == role))?.FirstOrDefault(x => x.Role == role); + } + + public static int addScientistNum; + public static int addEngineerNum; + public static int addShapeshifterNum; + public static void CalculateVanillaRoleCount() + { + // Calculate the number of base roles + addEngineerNum = 0; + addScientistNum = 0; + addShapeshifterNum = 0; + foreach (var role in AllRoles) + { + switch (role.GetVNRole()) + { + case CustomRoles.Scientist: + addScientistNum++; + break; + case CustomRoles.Engineer: + addEngineerNum++; + break; + case CustomRoles.Shapeshifter: + addShapeshifterNum++; + break; + } + } + } +} diff --git a/Roles/Core/CustomRoleManager.cs b/Roles/Core/CustomRoleManager.cs new file mode 100644 index 000000000..e039d178b --- /dev/null +++ b/Roles/Core/CustomRoleManager.cs @@ -0,0 +1,389 @@ +using AmongUs.GameOptions; +using System; +using System.Text; +using TOHE.Roles.AddOns.Common; +using TOHE.Roles.AddOns.Crewmate; +using TOHE.Roles.AddOns.Impostor; +using TOHE.Roles.Crewmate; +using TOHE.Roles.Impostor; +using TOHE.Roles.Neutral; + +namespace TOHE.Roles.Core; + +public static class CustomRoleManager +{ + public static readonly Dictionary RoleClass = []; + public static RoleBase GetStaticRoleClass(this CustomRoles role) => RoleClass.TryGetValue(role, out var roleClass) & roleClass != null ? roleClass : new VanillaRole(); + public static List AllEnabledRoles => RoleClass.Values.Where(x => x.IsEnable).ToList(); + public static bool HasEnabled(this CustomRoles role) => role.GetStaticRoleClass().IsEnable; + public static RoleBase GetRoleClass(this PlayerControl player) => GetRoleClassById(player.PlayerId); + public static RoleBase GetRoleClassById(this byte playerId) => Main.PlayerStates.TryGetValue(playerId, out var statePlayer) && statePlayer != null ? statePlayer.RoleClass : new VanillaRole(); + + public static RoleBase CreateRoleClass(this CustomRoles role) + { + return (RoleBase)Activator.CreateInstance(role.GetStaticRoleClass().GetType()); // Converts this.RoleBase back to its type and creates an unique one. + } + + /// + /// If the role protect others players + /// + public static bool OnCheckMurderAsTargetOnOthers(PlayerControl killer, PlayerControl target) + { + // return true when need to cancel the kill target + // "Any()" defines a function that returns true, and converts to false to cancel the kill + return !AllEnabledRoles.Any(RoleClass => RoleClass.CheckMurderOnOthersTarget(killer, target) == true); + } + + /// + /// Builds Modified GameOptions + /// + public static void BuildCustomGameOptions(this PlayerControl player, ref IGameOptions opt, CustomRoles role) + { + if (player.IsAnySubRole(x => x is CustomRoles.EvilSpirit)) + { + AURoleOptions.GuardianAngelCooldown = Spiritcaller.SpiritAbilityCooldown.GetFloat(); + } + + player.GetRoleClass()?.ApplyGameOptions(opt, player.PlayerId); + + switch (role) + { + case CustomRoles.ShapeshifterTOHE: + AURoleOptions.ShapeshifterCooldown = Options.ShapeshiftCD.GetFloat(); + AURoleOptions.ShapeshifterDuration = Options.ShapeshiftDur.GetFloat(); + break; + case CustomRoles.ScientistTOHE: + AURoleOptions.ScientistCooldown = Options.ScientistCD.GetFloat(); + AURoleOptions.ScientistBatteryCharge = Options.ScientistDur.GetFloat(); + break; + case CustomRoles.EngineerTOHE: + AURoleOptions.EngineerCooldown = 0f; + AURoleOptions.EngineerInVentMaxTime = 0f; + break; + default: + opt.SetVision(false); + break; + } + + if (Grenadier.HasEnabled) Grenadier.ApplyGameOptionsForOthers(opt, player); + if (Dazzler.HasEnabled) Dazzler.SetDazzled(player, opt); + if (Deathpact.HasEnabled) Deathpact.SetDeathpactVision(player, opt); + if (Spiritcaller.HasEnabled) Spiritcaller.ReduceVision(opt, player); + if (Pitfall.HasEnabled) Pitfall.SetPitfallTrapVision(opt, player); + + // Add-ons + if (Bewilder.IsEnable) Bewilder.ApplyGameOptions(opt, player); + if (Ghoul.IsEnable) Ghoul.ApplyGameOptions(player); + + var playerSubRoles = player.GetCustomSubRoles(); + + if (playerSubRoles.Any()) + foreach (var subRole in playerSubRoles.ToArray()) + { + switch (subRole) + { + case CustomRoles.Watcher: + Watcher.RevealVotes(opt); + break; + case CustomRoles.Flash: + Flash.SetSpeed(player.PlayerId, false); + break; + case CustomRoles.Torch: + Torch.ApplyGameOptions(opt); + break; + case CustomRoles.Tired: + Tired.ApplyGameOptions(opt, player); + break; + case CustomRoles.Bewilder: + Bewilder.ApplyVisionOptions(opt); + break; + case CustomRoles.Reach: + Reach.ApplyGameOptions(opt); + break; + case CustomRoles.Madmate: + Madmate.ApplyGameOptions(opt); + break; + case CustomRoles.Mare: + Mare.ApplyGameOptions(player.PlayerId); + break; + } + } + } + + /// + /// Check Murder as Killer in target + /// + public static bool OnCheckMurder(PlayerControl killer, PlayerControl target) + { + if (killer == target) return true; + + var killerRoleClass = killer.GetRoleClass(); + var killerSubRoles = killer.GetCustomSubRoles(); + + // Forced check + if (killerRoleClass.ForcedCheckMurderAsKiller(killer, target) == false) + { + Logger.Info("Cancels because for killer no need kill target", "ForcedCheckMurderAsKiller"); + return false; + } + + // Check in target + if (!killer.RpcCheckAndMurder(target, true)) + { + Logger.Info("Cancels because target cancel kill", "OnCheckMurder.RpcCheckAndMurder"); + return false; + } + + if (killerSubRoles.Any()) + foreach (var killerSubRole in killerSubRoles.ToArray()) + { + switch (killerSubRole) + { + case CustomRoles.Madmate when target.Is(CustomRoleTypes.Impostor) && !Madmate.MadmateCanKillImp.GetBool(): + case CustomRoles.Infected when target.Is(CustomRoles.Infected) && !Infectious.TargetKnowOtherTargets: + case CustomRoles.Infected when target.Is(CustomRoles.Infectious): + return false; + + case CustomRoles.Mare: + if (Mare.IsLightsOut) + return false; + break; + + case CustomRoles.Unlucky: + Unlucky.SuicideRand(killer); + if (Unlucky.UnluckCheck[killer.PlayerId]) return false; + break; + + case CustomRoles.Tired: + Tired.AfterActionTasks(killer); + break; + + case CustomRoles.Clumsy: + if (!Clumsy.OnCheckMurder(killer)) + return false; + break; + + case CustomRoles.Swift: + if (!Swift.OnCheckMurder(killer, target)) + return false; + break; + } + } + + // Check murder as killer + if (!killerRoleClass.OnCheckMurderAsKiller(killer, target)) + { + Logger.Info("Cancels because for killer no need kill target", "OnCheckMurderAsKiller"); + return false; + } + + return true; + } + /// + /// Tasks after killer murder target + /// + public static void OnMurderPlayer(PlayerControl killer, PlayerControl target, bool inMeeting) + { + // ############-INFO-############## + // When using this code, keep in mind that killer and target can be equal (Suicide) + // And the player can also die during the Meeting + // ################################ + + var killerRoleClass = killer.GetRoleClass(); + var targetRoleClass = target.GetRoleClass(); + + var killerSubRoles = killer.GetCustomSubRoles(); + var targetSubRoles = target.GetCustomSubRoles(); + + // Check suicide + var isSuicide = killer.PlayerId == target.PlayerId; + + // target was murder by killer + targetRoleClass.OnMurderPlayerAsTarget(killer, target, inMeeting, isSuicide); + + // Check target add-ons + if (targetSubRoles.Any()) + foreach (var subRole in targetSubRoles.ToArray()) + { + switch (subRole) + { + case CustomRoles.Cyber: + Cyber.AfterCyberDeadTask(target, inMeeting); + break; + + case CustomRoles.Bait when !inMeeting && !isSuicide: + Bait.BaitAfterDeathTasks(killer, target); + break; + + case CustomRoles.Trapper when !inMeeting && !isSuicide && !killer.Is(CustomRoles.KillingMachine): + killer.TrapperKilled(target); + break; + + case CustomRoles.Avanger when !inMeeting && !isSuicide: + Avanger.OnMurderPlayer(target); + break; + + case CustomRoles.Burst when killer.IsAlive() && !inMeeting && !isSuicide && !killer.Is(CustomRoles.KillingMachine): + Burst.AfterBurstDeadTasks(killer, target); + break; + + case CustomRoles.Oiiai when !isSuicide: + Oiiai.OnMurderPlayer(killer, target); + break; + + case CustomRoles.EvilSpirit when !inMeeting && !isSuicide: + target.RpcSetRole(RoleTypes.GuardianAngel); + break; + + } + } + + // Killer murder target + killerRoleClass.OnMurderPlayerAsKiller(killer, target, inMeeting, isSuicide); + + // Check killer add-ons + if (killerSubRoles.Any()) + foreach (var subRole in killerSubRoles.ToArray()) + { + switch (subRole) + { + case CustomRoles.TicketsStealer when !inMeeting && !isSuicide: + killer.Notify(string.Format(Translator.GetString("TicketsStealerGetTicket"), ((Main.AllPlayerControls.Count(x => x.GetRealKiller()?.PlayerId == killer.PlayerId) + 1) * Stealer.TicketsPerKill.GetFloat()).ToString("0.0#####"))); + break; + + case CustomRoles.Tricky: + Tricky.AfterPlayerDeathTasks(target); + break; + } + } + + // Check dead body for others roles + CheckDeadBody(target, killer, inMeeting); + + // Check Lovers Suicide + FixedUpdateInNormalGamePatch.LoversSuicide(target.PlayerId, inMeeting); + } + + /// + /// Check if this task is marked by a role and do something. + /// + public static void OthersCompleteThisTask(PlayerControl player, PlayerTask task) + => Main.PlayerStates.Values.ToArray().Do(PlrState => PlrState.RoleClass.OnOthersTaskComplete(player, task)); + + + public static HashSet> CheckDeadBodyOthers = []; + /// + /// If the role need check a present dead body + /// + public static void CheckDeadBody(PlayerControl deadBody, PlayerControl killer, bool inMeeting) + { + if (!CheckDeadBodyOthers.Any()) return; + //Execute other viewpoint processing if any + foreach (var checkDeadBodyOthers in CheckDeadBodyOthers.ToArray()) + { + checkDeadBodyOthers(deadBody, killer, inMeeting); + } + } + + public static HashSet> OnFixedUpdateOthers = []; + /// + /// Function always called in a task turn + /// For interfering with other roles + /// Registered with OnFixedUpdateOthers+= at initialization + /// + public static void OnFixedUpdate(PlayerControl player) + { + player.GetRoleClass()?.OnFixedUpdate(player); + + if (!OnFixedUpdateOthers.Any()) return; + //Execute other viewpoint processing if any + foreach (var onFixedUpdate in OnFixedUpdateOthers.ToArray()) + { + onFixedUpdate(player); + } + } + public static HashSet> OnFixedUpdateLowLoadOthers = []; + public static void OnFixedUpdateLowLoad(PlayerControl player) + { + player.GetRoleClass()?.OnFixedUpdateLowLoad(player); + + if (!OnFixedUpdateLowLoadOthers.Any()) return; + //Execute other viewpoint processing if any + foreach (var onFixedUpdateLowLoad in OnFixedUpdateLowLoadOthers.ToArray()) + { + onFixedUpdateLowLoad(player); + } + } + + /// + /// When others players on entered to vent + /// + public static bool OthersCoEnterVent(PlayerPhysics physics, int ventId) + { + return AllEnabledRoles.Any(RoleClass => RoleClass.OnCoEnterVentOthers(physics, ventId)); + } + + public static HashSet> MarkOthers = []; + public static HashSet> LowerOthers = []; + public static HashSet> SuffixOthers = []; + + public static string GetMarkOthers(PlayerControl seer, PlayerControl seen = null, bool isForMeeting = false) + { + if (!MarkOthers.Any()) return string.Empty; + + var sb = new StringBuilder(100); + foreach (var marker in MarkOthers) + { + sb.Append(marker(seer, seen, isForMeeting)); + } + return sb.ToString(); + } + + public static string GetLowerTextOthers(PlayerControl seer, PlayerControl seen = null, bool isForMeeting = false, bool isForHud = false) + { + if (!LowerOthers.Any()) return string.Empty; + + var sb = new StringBuilder(100); + foreach (var lower in LowerOthers) + { + sb.Append(lower(seer, seen, isForMeeting, isForHud)); + } + return sb.ToString(); + } + + + private static readonly List SuffixWaitList = []; + public static string GetSuffixOthers(PlayerControl seer, PlayerControl seen = null, bool isForMeeting = false) + { + if (!SuffixOthers.Any()) return string.Empty; + + SuffixWaitList.Clear(); + var sb = new StringBuilder(100); + string Symbol; + foreach (var suffix in SuffixOthers) + { + Symbol = suffix(seer, seen, isForMeeting); + if (Symbol.Length == 1) + sb.Append(Symbol); + else + SuffixWaitList.Add(Symbol); + } + if (SuffixWaitList.Any()) + { + foreach (var LongText in SuffixWaitList) + sb.Append(LongText); + } + + return sb.ToString(); + } + + public static void Initialize() + { + MarkOthers.Clear(); + LowerOthers.Clear(); + SuffixOthers.Clear(); + OnFixedUpdateOthers.Clear(); + OnFixedUpdateLowLoadOthers.Clear(); + CheckDeadBodyOthers.Clear(); + } +} diff --git a/Roles/Neutral/Baker.cs b/Roles/Neutral/Baker.cs index ab08e8707..09f506f6e 100644 --- a/Roles/Neutral/Baker.cs +++ b/Roles/Neutral/Baker.cs @@ -1,3 +1,4 @@ +<<<<<<< Updated upstream using Hazel; using System.Collections.Generic; using System.Linq; @@ -41,22 +42,130 @@ public static void Add(byte playerId) public static void SendRPC(PlayerControl player, PlayerControl target) { MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetBreadedPlayer, SendOption.Reliable, -1); +======= +using AmongUs.GameOptions; +using Hazel; +using TOHE.Roles.Core; +using TOHE.Roles.Impostor; +using static TOHE.Options; +using static TOHE.Translator; +using static TOHE.Utils; + +namespace TOHE.Roles.Neutral; + +internal class Baker : RoleBase +{ + //===========================SETUP================================\\ + private static readonly int Id = 28200; + public static readonly HashSet playerIdList = []; + public static bool HasEnabled => playerIdList.Any(); + public override bool IsEnable => HasEnabled; + public override CustomRoles ThisRoleBase => CustomRoles.Impostor; + //==================================================================\\ + + private static OptionItem BreadNeededToTransform; + + private static readonly Dictionary> BreadList = []; + private static bool CanUseAbility; + + public static void SetupCustomOption() + { + Options.SetupRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.Baker); + BreadNeededToTransform = IntegerOptionItem.Create(Id + 10, "BakerBreadNeededToTransform", new(1, 5, 1), 3, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Baker]) + .SetValueFormat(OptionFormat.Times); + } + public override void Init() + { + playerIdList.Clear(); + BreadList.Clear(); + CanUseAbility = false; + } + public override void Add(byte playerId) + { + playerIdList.Add(playerId); + BreadList[playerId] = []; + CanUseAbility = true; + CustomRoleManager.CheckDeadBodyOthers.Add(OnPlayerDead); + } + + private static (int, int) BreadedPlayerCount(byte playerId) + { + int breaded = 0, all = BreadNeededToTransform.GetInt(); + foreach (var pc in Main.AllAlivePlayerControls) + { + if (pc.PlayerId == playerId) continue; + + if (HasBread(playerId, pc.PlayerId)) + breaded++; + } + return (breaded, all); + } + public static void SendRPC(PlayerControl player, PlayerControl target) + { + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncRoleSkill, SendOption.Reliable, -1); + writer.WritePacked((int)CustomRoles.Baker); +>>>>>>> Stashed changes writer.Write(player.PlayerId); writer.Write(target.PlayerId); AmongUsClient.Instance.FinishRpcImmediately(writer); } +<<<<<<< Updated upstream public static void ReceiveRPC(MessageReader reader) +======= + public override void ReceiveRPC(MessageReader reader, PlayerControl NaN) +>>>>>>> Stashed changes { byte BakerId = reader.ReadByte(); byte BreadHolderId = reader.ReadByte(); BreadList[BakerId].Add(BreadHolderId); } +<<<<<<< Updated upstream public static string GetProgressText(byte playerId) => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Baker).ShadeColor(0.25f), $"({BreadedPlayerCount(playerId).Item1}/{BreadedPlayerCount(playerId).Item2})"); public static bool HasBread(byte pc, byte target) { return BreadList[pc].Contains(target); } public static bool OnCheckMurder(PlayerControl killer, PlayerControl target) +======= + public override string GetProgressText(byte playerId, bool comms) => ColorString(GetRoleColor(CustomRoles.Baker).ShadeColor(0.25f), $"({BreadedPlayerCount(playerId).Item1}/{BreadedPlayerCount(playerId).Item2})"); + public override bool OthersKnowTargetRoleColor(PlayerControl seer, PlayerControl target) => KnowRoleTarget(seer, target); + public override bool KnowRoleTarget(PlayerControl seer, PlayerControl target) + => (target.IsNeutralApocalypse() && seer.IsNeutralApocalypse()); + public override string GetMark(PlayerControl seer, PlayerControl seen = null, bool isForMeeting = false) + => BreadList[seer.PlayerId].Contains(seen.PlayerId) ? $"●" : ""; + public override bool CanUseKillButton(PlayerControl pc) => pc.IsAlive(); + public override void SetAbilityButtonText(HudManager hud, byte playerId) + { + hud.KillButton.OverrideText(GetString("BakerKillButtonText")); + } + private static bool HasBread(byte pc, byte target) + { + return BreadList[pc].Contains(target); + } + private static bool AllHasBread(PlayerControl player) + { + if (!player.Is(CustomRoles.Baker)) return false; + + var (countItem1, countItem2) = BreadedPlayerCount(player.PlayerId); + return countItem1 >= countItem2; + } + + public override void OnReportDeadBody(PlayerControl marg, PlayerControl iscute) + { + CanUseAbility = true; + } + private void OnPlayerDead(PlayerControl killer, PlayerControl deadPlayer, bool inMeeting) + { + foreach (var playerId in BreadList.Keys.ToArray()) + { + if (deadPlayer.PlayerId == playerId) + { + BreadList[playerId].Remove(playerId); + } + } + } + public override bool ForcedCheckMurderAsKiller(PlayerControl killer, PlayerControl target) +>>>>>>> Stashed changes { if (!CanUseAbility) { @@ -78,6 +187,7 @@ public static bool OnCheckMurder(PlayerControl killer, PlayerControl target) Utils.NotifyRoles(SpecifySeer: killer); killer.Notify(GetString("BakerBreaded")); +<<<<<<< Updated upstream Logger.Info($"Bread given to "+target.GetRealName(), "Baker"); CanUseAbility = false; return false; @@ -118,6 +228,15 @@ public static bool AllHasBread(PlayerControl player) public static void OnFixedUpdate(PlayerControl player) { if (!AllHasBread(player)) return; +======= + Logger.Info($"Bread given to " + target.GetRealName(), "Baker"); + CanUseAbility = false; + return false; + } + public override void OnFixedUpdate(PlayerControl player) + { + if (!AllHasBread(player)|| player.Is(CustomRoles.Famine)) return; +>>>>>>> Stashed changes player.RpcSetCustomRole(CustomRoles.Famine); player.Notify(GetString("BakerToFamine")); @@ -150,5 +269,9 @@ public static void KillIfNotEjected(PlayerControl player) } CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Starved, [.. deathList]); } +<<<<<<< Updated upstream } +======= +} +>>>>>>> Stashed changes diff --git a/Roles/Neutral/Berserker.cs b/Roles/Neutral/Berserker.cs new file mode 100644 index 000000000..57f82ce43 --- /dev/null +++ b/Roles/Neutral/Berserker.cs @@ -0,0 +1,182 @@ +using UnityEngine; +using TOHE.Modules; +using TOHE.Roles.Impostor; +using static TOHE.Translator; +using AmongUs.GameOptions; + +namespace TOHE.Roles.Neutral; + +internal class Berserker : RoleBase +{ + //===========================SETUP================================\\ + private const int Id = 600; + + private static readonly HashSet PlayerIds = []; + public static bool HasEnabled => PlayerIds.Any(); + public override bool IsEnable => HasEnabled; + public override CustomRoles ThisRoleBase => CustomRoles.Impostor; + //==================================================================\\ + + private static OptionItem BerserkerKillCooldown; + private static OptionItem BerserkerMax; + private static OptionItem BerserkerOneCanKillCooldown; + private static OptionItem BerserkerKillCooldownLevel; + private static OptionItem BerserkerOneKillCooldown; + private static OptionItem BerserkerTwoCanScavenger; + private static OptionItem BerserkerScavengerLevel; + private static OptionItem BerserkerThreeCanBomber; + private static OptionItem BerserkerBomberLevel; + //public static OptionItem BerserkerFourCanFlash; + //public static OptionItem BerserkerSpeed; + private static OptionItem BerserkerFourCanNotKill; + private static OptionItem BerserkerImmortalLevel; + private static OptionItem WarKillCooldown; + private static OptionItem BerserkerHasImpostorVision; + private static OptionItem WarHasImpostorVision; + private static OptionItem BerserkerCanVent; + private static OptionItem WarCanVent; + + private static readonly Dictionary BerserkerKillMax = []; + + public static void SetupCustomOption() + { + Options.SetupRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.Berserker); + BerserkerKillCooldown = FloatOptionItem.Create(Id + 2, "BerserkerKillCooldown", new(25f, 250f, 2.5f), 35f, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]) + .SetValueFormat(OptionFormat.Seconds); + BerserkerMax = IntegerOptionItem.Create(Id + 3, "BerserkerMax", new(1, 10, 1), 4, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]) + .SetValueFormat(OptionFormat.Level); + BerserkerHasImpostorVision = BooleanOptionItem.Create(Id + 15, "BerserkerHasImpostorVision", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]); + WarHasImpostorVision = BooleanOptionItem.Create(Id + 16, "WarHasImpostorVision", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerCanVent = BooleanOptionItem.Create(Id + 17, "BerserkerCanVent", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]); + WarCanVent = BooleanOptionItem.Create(Id + 18, "WarCanVent", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerOneCanKillCooldown = BooleanOptionItem.Create(Id + 4, "BerserkerOneCanKillCooldown", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerOneKillCooldown = FloatOptionItem.Create(Id + 5, "BerserkerOneKillCooldown", new(10f, 45f, 2.5f), 15f, TabGroup.NeutralRoles, false).SetParent(BerserkerOneCanKillCooldown) + .SetValueFormat(OptionFormat.Seconds); + BerserkerKillCooldownLevel = IntegerOptionItem.Create(Id + 6, "BerserkerLevelRequirement", new(1, 10, 1), 1, TabGroup.NeutralRoles, false).SetParent(BerserkerOneCanKillCooldown) + .SetValueFormat(OptionFormat.Level); + BerserkerTwoCanScavenger = BooleanOptionItem.Create(Id + 7, "BerserkerTwoCanScavenger", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerScavengerLevel = IntegerOptionItem.Create(Id + 8, "BerserkerLevelRequirement", new(1, 10, 1), 2, TabGroup.NeutralRoles, false).SetParent(BerserkerTwoCanScavenger) + .SetValueFormat(OptionFormat.Level); + BerserkerThreeCanBomber = BooleanOptionItem.Create(Id + 9, "BerserkerThreeCanBomber", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerBomberLevel = IntegerOptionItem.Create(Id + 10, "BerserkerLevelRequirement", new(1, 10, 1), 3, TabGroup.NeutralRoles, false).SetParent(BerserkerThreeCanBomber) + .SetValueFormat(OptionFormat.Level); + //BerserkerFourCanFlash = BooleanOptionItem.Create(Id + 11, "BerserkerFourCanFlash", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]); + //BerserkerSpeed = FloatOptionItem.Create(611, "BerserkerSpeed", new(1.5f, 5f, 0.25f), 2.5f, TabGroup.NeutralRoles, false).SetParent(BerserkerOneCanKillCooldown) + // .SetValueFormat(OptionFormat.Multiplier); + BerserkerFourCanNotKill = BooleanOptionItem.Create(Id + 12, "BerserkerFourCanNotKill", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.Berserker]); + BerserkerImmortalLevel = IntegerOptionItem.Create(Id + 13, "BerserkerLevelRequirement", new(1, 10, 1), 4, TabGroup.NeutralRoles, false).SetParent(BerserkerFourCanNotKill) + .SetValueFormat(OptionFormat.Level); + WarKillCooldown = FloatOptionItem.Create(Id + 14, "WarKillCooldown", new(0f, 150f, 2.5f), 15f, TabGroup.NeutralRoles, false).SetParent(BerserkerFourCanNotKill) + .SetValueFormat(OptionFormat.Seconds); + } + public override void Init() + { + BerserkerKillMax.Clear(); + PlayerIds.Clear(); + } + public override void Add(byte playerId) + { + BerserkerKillMax[playerId] = 0; + PlayerIds.Add(playerId); + } + public override void Remove(byte playerId) + { + BerserkerKillMax.Remove(playerId); + } + + public override bool OthersKnowTargetRoleColor(PlayerControl seer, PlayerControl target) => KnowRoleTarget(seer, target); + public override bool KnowRoleTarget(PlayerControl seer, PlayerControl target) + => (target.IsNeutralApocalypse() && seer.IsNeutralApocalypse()); + public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = BerserkerKillCooldown.GetFloat(); + public override bool CanUseImpostorVentButton(PlayerControl pc) + { + if (pc.Is(CustomRoles.Berserker) && BerserkerCanVent.GetBool()) return true; + if (pc.Is(CustomRoles.War) && WarCanVent.GetBool()) return true; + return false; + } + + public override void ApplyGameOptions(IGameOptions opt, byte playerId) + { + if (CustomRoles.Berserker.RoleExist()) + opt.SetVision(BerserkerHasImpostorVision.GetBool()); + if (CustomRoles.War.RoleExist()) + opt.SetVision(WarHasImpostorVision.GetBool()); + } + public override bool CanUseKillButton(PlayerControl pc) => pc.IsAlive(); + public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target) + { + if (BerserkerKillMax[target.PlayerId] >= BerserkerImmortalLevel.GetInt() && BerserkerFourCanNotKill.GetBool()) + { + killer.RpcTeleport(target.GetCustomPosition()); + RPC.PlaySoundRPC(killer.PlayerId, Sounds.KillSound); + killer.SetKillCooldown(target: target, forceAnime: true); + return false; + } + return true; + } + public override bool OnCheckMurderAsKiller(PlayerControl killer, PlayerControl target) + { + if (target.IsNeutralApocalypse()) return false; + + if (BerserkerKillMax[killer.PlayerId] < BerserkerMax.GetInt()) + { + BerserkerKillMax[killer.PlayerId]++; + killer.Notify(string.Format(Translator.GetString("BerserkerLevelChanged"), BerserkerKillMax[killer.PlayerId])); + Logger.Info($"Increased the lvl to {BerserkerKillMax[killer.PlayerId]}", "CULTIVATOR"); + } + else + { + killer.Notify(Translator.GetString("BerserkerMaxReached")); + Logger.Info($"Max level reached lvl = {BerserkerKillMax[killer.PlayerId]}", "CULTIVATOR"); + + } + + if (BerserkerKillMax[killer.PlayerId] >= BerserkerKillCooldownLevel.GetInt() && BerserkerOneCanKillCooldown.GetBool()) + { + Main.AllPlayerKillCooldown[killer.PlayerId] = BerserkerOneKillCooldown.GetFloat(); + } + if (BerserkerKillMax[killer.PlayerId] == BerserkerScavengerLevel.GetInt() && BerserkerTwoCanScavenger.GetBool()) + { + killer.RpcTeleport(target.GetCustomPosition()); + RPC.PlaySoundRPC(killer.PlayerId, Sounds.KillSound); + target.RpcTeleport(ExtendedPlayerControl.GetBlackRoomPosition()); + + Main.PlayerStates[target.PlayerId].SetDead(); + target.RpcMurderPlayer(target); + target.SetRealKiller(killer); + + killer.SetKillCooldownV2(); + target.Notify(Utils.ColorString(Utils.GetRoleColor(CustomRoles.Berserker), Translator.GetString("KilledByBerserker"))); + return false; + } + + if (BerserkerKillMax[killer.PlayerId] >= BerserkerBomberLevel.GetInt() && BerserkerThreeCanBomber.GetBool()) + { + Logger.Info("炸弹爆炸了", "Boom"); + CustomSoundsManager.RPCPlayCustomSoundAll("Boom"); + foreach (var player in Main.AllAlivePlayerControls) + { + if (!player.IsModClient()) + player.KillFlash(); + + if (player == killer) continue; + if (player == target) continue; + + if (Vector2.Distance(killer.transform.position, player.transform.position) <= Bomber.BomberRadius.GetFloat()) + { + Main.PlayerStates[player.PlayerId].deathReason = PlayerState.DeathReason.Bombed; + player.SetRealKiller(killer); + player.RpcMurderPlayer(player); + } + } + } + if (BerserkerKillMax[killer.PlayerId] >= BerserkerImmortalLevel.GetInt() && BerserkerFourCanNotKill.GetBool()) + { + killer.RpcSetCustomRole(CustomRoles.War); + killer.Notify(GetString("BerserkerToWar")); + Main.AllPlayerKillCooldown[killer.PlayerId] = WarKillCooldown.GetFloat(); + } + + return true; + } +} diff --git a/Roles/Neutral/PlagueBearer.cs b/Roles/Neutral/PlagueBearer.cs index 31a4335fa..e32334efa 100644 --- a/Roles/Neutral/PlagueBearer.cs +++ b/Roles/Neutral/PlagueBearer.cs @@ -55,8 +55,22 @@ public static void Add(byte playerId) Main.ResetCamPlayerList.Add(playerId); } +<<<<<<< Updated upstream public static void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = PlagueBearerCD[id]; public static void SetKillCooldownPestilence(byte id) => Main.AllPlayerKillCooldown[id] = PestilenceCDOpt.GetFloat(); +======= + public override bool OthersKnowTargetRoleColor(PlayerControl seer, PlayerControl target) => KnowRoleTarget(seer, target); + public override bool KnowRoleTarget(PlayerControl seer, PlayerControl target) + => (target.IsNeutralApocalypse() && seer.IsNeutralApocalypse()); + + public override void SetKillCooldown(byte id) + { + if (!PestilenceList.Contains(id)) + Main.AllPlayerKillCooldown[id] = PlagueBearerCD[id]; + else + Main.AllPlayerKillCooldown[id] = PestilenceCDOpt.GetFloat(); + } +>>>>>>> Stashed changes public static bool IsPlagued(byte pc, byte target) { @@ -115,7 +129,15 @@ public static bool OnCheckMurder(PlayerControl killer, PlayerControl target) killer.SetKillCooldown(); Logger.Info($"kill cooldown {PlagueBearerCD[killer.PlayerId]}", "PlagueBearer"); +<<<<<<< Updated upstream return false; +======= + return true; + } + public override bool OnCheckMurderAsKiller(PlayerControl killer, PlayerControl target) + { + return killer.Is(CustomRoles.Pestilence) && !target.IsNeutralApocalypse(); +>>>>>>> Stashed changes } public static bool IsIndirectKill(PlayerControl killer) diff --git a/Roles/Neutral/SoulCollector.cs b/Roles/Neutral/SoulCollector.cs index 8434d4b84..b0b3b702f 100644 --- a/Roles/Neutral/SoulCollector.cs +++ b/Roles/Neutral/SoulCollector.cs @@ -15,9 +15,16 @@ public static class SoulCollector public static List playerIdList = []; public static bool IsEnable = false; +<<<<<<< Updated upstream public static Dictionary SoulCollectorTarget = []; public static Dictionary SoulCollectorPoints = []; public static Dictionary DidVote = []; +======= + private static OptionItem SoulCollectorPointsOpt; + private static OptionItem CollectOwnSoulOpt; + private static OptionItem CallMeetingIfDeath; + private static OptionItem GetPassiveSouls; +>>>>>>> Stashed changes public static OptionItem SoulCollectorPointsOpt; public static OptionItem CollectOwnSoulOpt; @@ -29,7 +36,11 @@ public static void SetupCustomOption() SetupRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.SoulCollector); SoulCollectorPointsOpt = IntegerOptionItem.Create(Id + 10, "SoulCollectorPointsToWin", new(1, 14, 1), 3, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]) .SetValueFormat(OptionFormat.Times); +<<<<<<< Updated upstream CollectOwnSoulOpt = BooleanOptionItem.Create(Id + 11, "CollectOwnSoulOpt", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]); +======= + CollectOwnSoulOpt = BooleanOptionItem.Create(Id + 11, "CollectOwnSoulOpt", true, TabGroup.NeutralRoles, false).SetParent(CustomRoleSpawnChances[CustomRoles.SoulCollector]); +>>>>>>> Stashed changes CallMeetingIfDeath = BooleanOptionItem.Create(Id + 12, "CallMeetingIfDeath", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]); GetPassiveSouls = BooleanOptionItem.Create(Id + 13, "GetPassiveSouls", true, TabGroup.NeutralRoles, false).SetParent(Options.CustomRoleSpawnChances[CustomRoles.SoulCollector]); } @@ -76,6 +87,9 @@ public static void ReceiveRPC(MessageReader reader) else SoulCollectorTarget.Add(SoulCollectorId, byte.MaxValue); } + public override bool OthersKnowTargetRoleColor(PlayerControl seer, PlayerControl target) => KnowRoleTarget(seer, target); + public override bool KnowRoleTarget(PlayerControl seer, PlayerControl target) + => (target.IsNeutralApocalypse() && seer.IsNeutralApocalypse()); public static void OnVote(PlayerControl voter, PlayerControl target) { @@ -105,11 +119,19 @@ public static void OnReportDeadBody() { SoulCollectorTarget[playerId] = byte.MaxValue; DidVote[playerId] = false; +<<<<<<< Updated upstream if (GetPassiveSouls.GetBool()) { SoulCollectorPoints[playerId]++; Utils.SendMessage(GetString("PassiveSoulGained"), playerId, title: Utils.ColorString(Utils.GetRoleColor(CustomRoles.SoulCollector), GetString("SoulCollectorTitle"))); } +======= + /*if (GetPassiveSouls.GetBool()) + { + SoulCollectorPoints[playerId]++; + Utils.SendMessage(GetString("PassiveSoulGained"), playerId, title: Utils.ColorString(Utils.GetRoleColor(CustomRoles.SoulCollector), GetString("SoulCollectorTitle"))); + }*/ +>>>>>>> Stashed changes } } @@ -128,6 +150,7 @@ public static void OnPlayerDead(PlayerControl deadPlayer) } if (SoulCollectorPoints[playerId] >= SoulCollectorPointsOpt.GetInt()) { +<<<<<<< Updated upstream SoulCollectorPoints[playerId] = SoulCollectorPointsOpt.GetInt(); } @@ -144,6 +167,41 @@ public static void BecomeDeath(PlayerControl player) if (GameStates.IsCanMove) KillIfNotEjected(player); } public static void KillIfNotEjected(PlayerControl player) + { + var deathList = new List(); + if (Main.AfterMeetingDeathPlayers.ContainsKey(player.PlayerId)) return; + foreach (var pc in Main.AllAlivePlayerControls) + { + if (pc.IsNeutralApocalypse()) continue; + if (player != null && player.IsAlive()) + { + if (!Main.AfterMeetingDeathPlayers.ContainsKey(pc.PlayerId)) + { + pc.SetRealKiller(player); + deathList.Add(pc.PlayerId); + } + else + { + Main.AfterMeetingDeathPlayers.Remove(pc.PlayerId); + } +======= + SoulCollectorPoints[playerId] = SoulCollectorPointsOpt.GetInt(); +>>>>>>> Stashed changes + } + else return; + } + CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Armageddon, [.. deathList]); + } + public override void OnFixedUpdate(PlayerControl player) + { + if (SoulCollectorPoints[player.PlayerId] < SoulCollectorPointsOpt.GetInt() || player.GetCustomRole() is CustomRoles.Death) return; + player.RpcSetCustomRole(CustomRoles.Death); + player.Notify(GetString("SoulCollectorToDeath")); + player.RpcGuardAndKill(player); + /*if (CallMeetingIfDeath.GetBool()) PlayerControl.LocalPlayer.NoCheckStartMeeting(null, force: true);*/ + KillIfNotEjected(player); + } + public static void KillIfNotEjected(PlayerControl player) { var deathList = new List(); if (Main.AfterMeetingDeathPlayers.ContainsKey(player.PlayerId)) return; @@ -166,5 +224,4 @@ public static void KillIfNotEjected(PlayerControl player) } CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Armageddon, [.. deathList]); } - } \ No newline at end of file diff --git a/main.cs b/main.cs index 3b4d5a56b..4f05beb3a 100644 --- a/main.cs +++ b/main.cs @@ -368,6 +368,41 @@ public static void LoadRoleColors() ExceptionMessageIsShown = false; } } +<<<<<<< Updated upstream +======= + public static void LoadRoleClasses() + { + TOHE.Logger.Info("Loading All RoleClasses...", "LoadRoleClasses"); + try + { + var RoleTypes = Assembly.GetAssembly(typeof(RoleBase))! + .GetTypes() + .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(RoleBase))); + foreach (var role in CustomRolesHelper.AllRoles.Where(x => x < CustomRoles.NotAssigned)) + { + Type roleType = role switch // Switch role to FatherRole (Double Classes) + { + CustomRoles.Vampiress => typeof(Vampire), + CustomRoles.Pestilence => typeof(PlagueBearer), + CustomRoles.War => typeof(Berserker), + CustomRoles.Death => typeof(SoulCollector), + CustomRoles.Famine => typeof(Baker), + CustomRoles.Nuker => typeof(Bomber), + CustomRoles.NiceMini or CustomRoles.EvilMini => typeof(Mini), + _ => RoleTypes.FirstOrDefault(x => x.Name.Equals(role.ToString(), StringComparison.OrdinalIgnoreCase)) ?? typeof(VanillaRole), + }; + + CustomRoleManager.RoleClass.Add(role, (RoleBase)Activator.CreateInstance(roleType)); + } + + TOHE.Logger.Info("RoleClasses Loaded Successfully", "LoadRoleClasses"); + } + catch (Exception err) + { + TOHE.Logger.Error($"Error at LoadRoleClasses: {err}", "LoadRoleClasses"); + } + } +>>>>>>> Stashed changes static void UpdateCustomTranslation() { string path = @$"./{LANGUAGE_FOLDER_NAME}/RoleColor.dat"; @@ -708,6 +743,7 @@ public enum CustomRoles //Neutral Agitater, Amnesiac, + Apocalypse, Arsonist, Baker, Bandit, @@ -717,12 +753,20 @@ public enum CustomRoles Succubus, //cultist CursedSoul, Death, +<<<<<<< Updated upstream Gamer, //demon +======= + Demon, +>>>>>>> Stashed changes Doomsayer, Doppelganger, Executioner, Famine, +<<<<<<< Updated upstream Totocalcio, //follower +======= + Follower, +>>>>>>> Stashed changes Glitch, God, Hater, @@ -937,15 +981,23 @@ public enum CustomWinner Medusa = CustomRoles.Medusa, Spiritcaller = CustomRoles.Spiritcaller, Glitch = CustomRoles.Glitch, +<<<<<<< Updated upstream //Plaguebearer = CustomRoles.PlagueBearer, //Pestilence = CustomRoles.Pestilence, +======= + /*Plaguebearer = CustomRoles.PlagueBearer,*/ +>>>>>>> Stashed changes PlagueDoctor = CustomRoles.PlagueDoctor, Masochist = CustomRoles.Masochist, Doomsayer = CustomRoles.Doomsayer, Shroud = CustomRoles.Shroud, Seeker = CustomRoles.Seeker, +<<<<<<< Updated upstream //SoulCollector = CustomRoles.SoulCollector, //Death = CustomRoles.Death, +======= + /*SoulCollector = CustomRoles.SoulCollector,*/ +>>>>>>> Stashed changes RuthlessRomantic = CustomRoles.RuthlessRomantic, NiceMini = CustomRoles.Mini, Doppelganger = CustomRoles.Doppelganger,