diff --git a/CustomRoles.cs b/CustomRoles.cs
index ad5da108b..3bb18f8e0 100644
--- a/CustomRoles.cs
+++ b/CustomRoles.cs
@@ -3,10 +3,10 @@ namespace EHR;
public enum CustomRoles
{
- //Default
+ // Default
Crewmate = 0,
- //Impostors (Vanilla)
+ // Impostors (Vanilla)
Impostor,
Shapeshifter,
@@ -14,7 +14,7 @@ public enum CustomRoles
ImpostorEHR,
ShapeshifterEHR,
- //Impostors
+ // Impostors
Hacker, // Anonymous
AntiAdminer,
@@ -101,7 +101,7 @@ public enum CustomRoles
YinYanger,
Zombie,
- //Crewmates (Vanilla)
+ // Crewmates (Vanilla)
Engineer,
GuardianAngel,
Scientist,
@@ -112,7 +112,7 @@ public enum CustomRoles
GuardianAngelEHR,
ScientistEHR,
- // Crewmate
+ // Crewmates
Addict,
Adventurer,
@@ -214,7 +214,7 @@ public enum CustomRoles
SwordsMan, // Vigilante
Witness,
- //Neutrals
+ // Neutrals
Agitater,
Amnesiac,
@@ -222,6 +222,7 @@ public enum CustomRoles
Bandit,
BloodKnight,
Bubble,
+ Cherokious,
Collector,
Deathknight,
Gamer, // Demon
@@ -270,6 +271,7 @@ public enum CustomRoles
Ritualist,
Romantic,
RuthlessRomantic,
+ SchrodingersCat,
NSerialKiller, // Serial Killer
Sidekick,
SoulHunter,
@@ -290,25 +292,25 @@ public enum CustomRoles
WeaponMaster,
Workaholic,
- //SoloKombat
+ // Solo Kombat
KB_Normal,
- //FFA
+ // FFA
Killer,
- //MoveAndStop
+ // Move And Stop
Tasker,
- //HotPotato
+ // Hot Potato
Potato,
- //H&S
+ // H&S
Hider,
Seeker,
Fox,
Troll,
- //GM
+ // GM
GM,
// ????
diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs
index 7e1a83aec..ab99b1de3 100644
--- a/Modules/CustomRolesHelper.cs
+++ b/Modules/CustomRolesHelper.cs
@@ -232,6 +232,7 @@ internal static class CustomRolesHelper
CustomRoles.Bard => CustomRoles.Impostor,
CustomRoles.Swooper => CustomRoles.Impostor,
CustomRoles.Crewpostor => CustomRoles.Engineer,
+ CustomRoles.Cherokious => CustomRoles.Engineer,
CustomRoles.Observer => CustomRoles.Crewmate,
CustomRoles.DovesOfNeace => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
CustomRoles.Disperser => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
@@ -341,6 +342,7 @@ internal static class CustomRolesHelper
CustomRoles.Sprayer => RoleTypes.Impostor,
CustomRoles.PlagueDoctor => RoleTypes.Impostor,
CustomRoles.Postman => RoleTypes.Impostor,
+ CustomRoles.SchrodingersCat => RoleTypes.Impostor,
CustomRoles.Impartial => RoleTypes.Impostor,
CustomRoles.Predator => RoleTypes.Impostor,
CustomRoles.Reckless => RoleTypes.Impostor,
@@ -379,6 +381,7 @@ internal static class CustomRolesHelper
public static bool IsNonNK(this CustomRoles role) => role is
CustomRoles.Jester or
CustomRoles.Postman or
+ CustomRoles.SchrodingersCat or
CustomRoles.Impartial or
CustomRoles.Predator or
CustomRoles.SoulHunter or
@@ -415,6 +418,7 @@ internal static class CustomRolesHelper
CustomRoles.Succubus or
CustomRoles.Gamer or
CustomRoles.Crewpostor or
+ CustomRoles.Cherokious or
CustomRoles.Necromancer or
CustomRoles.Agitater or
CustomRoles.Wraith or
@@ -479,6 +483,7 @@ internal static class CustomRolesHelper
CustomRoles.Amnesiac or
CustomRoles.God or
CustomRoles.Postman or
+ CustomRoles.SchrodingersCat or
CustomRoles.Predator or
CustomRoles.Pursuer or
CustomRoles.FFF or
@@ -682,7 +687,8 @@ internal static class CustomRolesHelper
CustomRoles.Tornado,
CustomRoles.Swiftclaw,
CustomRoles.Adventurer,
- CustomRoles.Sentry
+ CustomRoles.Sentry,
+ CustomRoles.Cherokious
];
public static bool OnlySpawnsWithPets(this CustomRoles role) => OnlySpawnsWithPetsRoleList.Contains(role);
@@ -1179,8 +1185,10 @@ internal static class CustomRolesHelper
CustomRoles.Refugee => CountTypes.Impostor,
CustomRoles.Glitch => CountTypes.Glitch,
CustomRoles.Spiritcaller => CountTypes.Spiritcaller,
+ CustomRoles.Cherokious => CountTypes.Cherokious,
CustomRoles.DarkHide => DarkHide.SnatchesWin.GetBool() ? CountTypes.Crew : CountTypes.DarkHide,
CustomRoles.Arsonist => !Options.ArsonistCanIgniteAnytime.GetBool() ? CountTypes.Crew : CountTypes.Arsonist,
+ CustomRoles.SchrodingersCat => SchrodingersCat.WinsWithCrewIfNotAttacked.GetBool() ? CountTypes.Crew : CountTypes.OutOfGame,
_ => role.Is(Team.Impostor) ? CountTypes.Impostor : CountTypes.Crew
};
@@ -1329,5 +1337,6 @@ public enum CountTypes
Pestilence,
PlagueBearer,
Glitch,
- Arsonist
+ Arsonist,
+ Cherokious
}
\ No newline at end of file
diff --git a/Modules/CustomTeamManager.cs b/Modules/CustomTeamManager.cs
index d4c39bc1a..1bdc4da26 100644
--- a/Modules/CustomTeamManager.cs
+++ b/Modules/CustomTeamManager.cs
@@ -86,26 +86,33 @@ namespace EHR.Modules
{
if (CustomTeams.Count == 0 || CustomTeamPlayerIds.Count == 0) return false;
- CustomTeamPlayerIds.Do(x => x.Value.RemoveWhere(p =>
+ try
{
- var pc = Utils.GetPlayerById(p);
- return pc == null || pc.Data.Disconnected;
- }));
-
- var aliveTeamPlayers = CustomTeamPlayerIds.ToDictionary(x => x.Key, x => x.Value);
- aliveTeamPlayers.Do(x => x.Value.RemoveWhere(p => !Utils.GetPlayerById(p).IsAlive()));
-
- var team = aliveTeamPlayers.Keys.First();
- if (aliveTeamPlayers.Count == 1 && Main.AllAlivePlayerControls.All(x =>
+ CustomTeamPlayerIds.Do(x => x.Value.RemoveWhere(p =>
{
- var customTeam = GetCustomTeam(x.PlayerId);
- return customTeam != null && customTeam.Equals(team);
- }))
+ var pc = Utils.GetPlayerById(p);
+ return pc == null || pc.Data.Disconnected;
+ }));
+
+ var aliveTeamPlayers = CustomTeamPlayerIds.ToDictionary(x => x.Key, x => x.Value);
+ aliveTeamPlayers.Do(x => x.Value.RemoveWhere(p => !Utils.GetPlayerById(p).IsAlive()));
+
+ var team = aliveTeamPlayers.Keys.First();
+ if (aliveTeamPlayers.Count == 1 && Main.AllAlivePlayerControls.All(x =>
+ {
+ var customTeam = GetCustomTeam(x.PlayerId);
+ return customTeam != null && customTeam.Equals(team);
+ }))
+ {
+ WinnerTeam = team;
+ CustomWinnerHolder.ResetAndSetWinner(CustomWinner.CustomTeam);
+ CustomWinnerHolder.WinnerIds = aliveTeamPlayers.Values.First();
+ return true;
+ }
+ }
+ catch
{
- WinnerTeam = team;
- CustomWinnerHolder.ResetAndSetWinner(CustomWinner.CustomTeam);
- CustomWinnerHolder.WinnerIds = aliveTeamPlayers.Values.First();
- return true;
+ return false;
}
return false;
diff --git a/Modules/Utils.cs b/Modules/Utils.cs
index c8c58e6ea..10ca2d753 100644
--- a/Modules/Utils.cs
+++ b/Modules/Utils.cs
@@ -528,6 +528,7 @@ public static class Utils
case CustomRoles.Doppelganger:
case CustomRoles.PlagueDoctor:
case CustomRoles.Postman:
+ case CustomRoles.SchrodingersCat:
case CustomRoles.Impartial:
case CustomRoles.Predator:
case CustomRoles.Reckless:
@@ -606,6 +607,7 @@ public static class Utils
if (ForRecompute)
hasTasks = false;
break;
+ case CustomRoles.Cherokious:
case CustomRoles.Crewpostor:
if (ForRecompute && !p.IsDead)
hasTasks = false;
@@ -805,8 +807,15 @@ public static class Utils
NonCompleteColor = Workhorse.RoleColor;
var NormalColor = taskState.IsTaskFinished ? TaskCompleteColor : NonCompleteColor;
- if (Main.PlayerStates.TryGetValue(playerId, out var ps) && ps.MainRole == CustomRoles.Crewpostor)
- NormalColor = Color.red;
+ if (Main.PlayerStates.TryGetValue(playerId, out var ps))
+ {
+ NormalColor = ps.MainRole switch
+ {
+ CustomRoles.Crewpostor => Color.red,
+ CustomRoles.Cherokious => GetRoleColor(CustomRoles.Cherokious),
+ _ => NormalColor
+ };
+ }
Color TextColor = comms ? Color.gray : NormalColor;
string Completed = comms ? "?" : $"{taskState.CompletedTasksCount}";
@@ -2407,6 +2416,7 @@ public static class Utils
CustomRoles.Warlock => Warlock.IsCursed ? -1 : (int)Options.DefaultKillCooldown,
CustomRoles.Swiftclaw => Swiftclaw.DashCD.GetInt() + (includeDuration ? Swiftclaw.DashDuration.GetInt() : 0),
CustomRoles.Tiger => Tiger.EnrageCooldown.GetInt() + (includeDuration ? Tiger.EnrageDuration.GetInt() : 0),
+ CustomRoles.Cherokious => Cherokious.KillCooldown.GetInt(),
_ => -1,
};
if (CD == -1) return;
@@ -2693,8 +2703,15 @@ public static class Utils
NonCompleteColor = Workhorse.RoleColor;
var NormalColor = taskState.IsTaskFinished ? TaskCompleteColor : NonCompleteColor;
- if (Main.PlayerStates.TryGetValue(id, out var ps) && ps.MainRole == CustomRoles.Crewpostor)
- NormalColor = Color.red;
+ if (Main.PlayerStates.TryGetValue(id, out var ps))
+ {
+ NormalColor = ps.MainRole switch
+ {
+ CustomRoles.Crewpostor => Color.red,
+ CustomRoles.Cherokious => GetRoleColor(CustomRoles.Cherokious),
+ _ => NormalColor
+ };
+ }
Color TextColor = NormalColor;
string Completed = $"{taskState.CompletedTasksCount}";
diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs
index cd1606b28..e69c9e5d5 100644
--- a/Patches/CheckGameEndPatch.cs
+++ b/Patches/CheckGameEndPatch.cs
@@ -183,6 +183,10 @@ class GameEndChecker
WinnerIds.Add(pc.PlayerId);
AdditionalWinnerTeams.Add(AdditionalWinners.SoulHunter);
break;
+ case CustomRoles.SchrodingersCat when SchrodingersCat.WinsWithCrewIfNotAttacked.GetBool():
+ WinnerIds.Add(pc.PlayerId);
+ AdditionalWinnerTeams.Add(AdditionalWinners.SchrodingersCat);
+ break;
}
}
diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json
index ce3e24b21..b4aa55b74 100644
--- a/Resources/Lang/en_US.json
+++ b/Resources/Lang/en_US.json
@@ -2620,6 +2620,14 @@
"SentryInfoLong": "(Crewmates):\\nAs the Sentry, you can monitor a room by petting your pet. When you pet your pet in the future, you get information from that room, including:\\n- The players currently in that room\\n- Who is dead in that room (if any)\\n- The players that used a vent in that room\\n- Whether someone shapeshifted in that room\\n\\nDepending on the settings, players may know if a room is monitored, and you may not be able to view this info during Comms Sabotages.",
"Sentry.Suffix.MonitoredRoom": "This room is monitored by the {0}",
"Sentry.Notify.Info": "Info about your monitored room:\\nPlayers: {0}\\nDead players: {1}\\nPlayers that used a vent: {2}\\nShapeshifters: {3}",
+ "SchrodingersCat": "Schrödinger's Cat",
+ "SchrodingersCatInfo": "Get Attacked to Get a Role",
+ "SchrodingersCatInfoLong": "(Neutrals):\\nAs Schrödinger's Cat, if you get attacked, you turn into the same role as your killer.\\nIf you don't get attacked, you lose, or you can win with crewmates, depending on the settings.",
+ "SchrodingersCat.WinsWithCrewIfNotAttacked": "Wins with crewmates if not attacked",
+ "SchrodingersCat.Notify.KillerRecruited": "You have recruited the {1}! {0} now has the same role as you.",
+ "Cherokious": "Cherokious",
+ "CherokiousInfo": "Do tasks to get Bullets to Kill",
+ "CherokiousInfoLong": "(Neutrals):\\nAs the Cherokious, you can get bullets by completing tasks. You can kill by petting your pet near someone. You can only kill if you have bullets.",
"Autocrat": "Autocrat",
"AutocratInfo": "TP nearest player to you by doing a task",
"AutocratInfoLong": "(Crewmates):\\nAs the Autocrat, when you complete a task, you teleport the nearest player to you.",
diff --git a/Roles/Crewmate/Analyzer.cs b/Roles/Crewmate/Analyst.cs
similarity index 100%
rename from Roles/Crewmate/Analyzer.cs
rename to Roles/Crewmate/Analyst.cs
diff --git a/Roles/Crewmate/Sentry.cs b/Roles/Crewmate/Sentry.cs
index b42ccda3f..5992c874b 100644
--- a/Roles/Crewmate/Sentry.cs
+++ b/Roles/Crewmate/Sentry.cs
@@ -1,9 +1,6 @@
-using System;
+using HarmonyLib;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using HarmonyLib;
namespace EHR.Roles.Impostor
{
@@ -16,8 +13,8 @@ namespace EHR.Roles.Impostor
private static OptionItem ShowInfoDuration;
private static OptionItem PlayersKnowAboutCamera;
private static OptionItem AbilityUseLimit;
- private static OptionItem AbilityUseGainWithEachTaskCompleted;
- private static OptionItem AbilityChargesWhenFinishedTasks;
+ public static OptionItem AbilityUseGainWithEachTaskCompleted;
+ public static OptionItem AbilityChargesWhenFinishedTasks;
private PlainShipRoom MonitoredRoom;
@@ -72,11 +69,14 @@ namespace EHR.Roles.Impostor
pc.RpcRemoveAbilityUse();
var players = Main.AllAlivePlayerControls.Where(IsInMonitoredRoom).Select(x => Utils.ColorString(Main.PlayerColors[x.PlayerId], x.GetRealName())).Join();
- var bodies = DeadBodiesInRoom.Where(x => Utils.GetPlayerById(x) != null).Select(x => Utils.ColorString(Main.PlayerColors[x], Utils.GetPlayerById(x).GetRealName())).Join();
- var vented = PlayersVentedInRoom.Where(x => Utils.GetPlayerById(x) != null).Select(x => Utils.ColorString(Main.PlayerColors[x], Utils.GetPlayerById(x).GetRealName())).Join();
- var shifted = PlayersShiftedInRoom.Where(x => Utils.GetPlayerById(x) != null).Select(x => Utils.ColorString(Main.PlayerColors[x], Utils.GetPlayerById(x).GetRealName())).Join();
+ var bodies = GetColoredNames(DeadBodiesInRoom);
+ var vented = GetColoredNames(PlayersVentedInRoom);
+ var shifted = GetColoredNames(PlayersShiftedInRoom);
pc.Notify(string.Format(Translator.GetString("Sentry.Notify.Info"), players, bodies, vented, shifted), ShowInfoDuration.GetInt());
+ return;
+
+ static string GetColoredNames(IEnumerable ids) => ids.Where(x => Utils.GetPlayerById(x) != null).Select(x => Utils.ColorString(Main.PlayerColors[x], Utils.GetPlayerById(x).GetRealName())).Join();
}
bool IsInMonitoredRoom(PlayerControl pc) => pc.GetPlainShipRoom().name == MonitoredRoom.name;
diff --git a/Roles/Neutral/Cherokious.cs b/Roles/Neutral/Cherokious.cs
new file mode 100644
index 000000000..816082bb8
--- /dev/null
+++ b/Roles/Neutral/Cherokious.cs
@@ -0,0 +1,46 @@
+using EHR.Patches;
+
+namespace EHR.Roles.Impostor
+{
+ internal class Cherokious : RoleBase
+ {
+ public static bool On;
+ public override bool IsEnable => On;
+
+ public static OptionItem KillCooldown;
+ public static Options.OverrideTasksData Tasks;
+
+ public static void SetupCustomOption()
+ {
+ const int id = 13860;
+ Options.SetupRoleOptions(id, TabGroup.NeutralRoles, CustomRoles.Cherokious);
+ KillCooldown = IntegerOptionItem.Create(id + 2, "KillCooldown", new(0, 60, 1), 15, TabGroup.NeutralRoles, false)
+ .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Cherokious])
+ .SetValueFormat(OptionFormat.Seconds);
+ Tasks = Options.OverrideTasksData.Create(id + 3, TabGroup.NeutralRoles, CustomRoles.Cherokious);
+ }
+
+ public override void Add(byte playerId)
+ {
+ On = true;
+ playerId.SetAbilityUseLimit(0);
+ }
+
+ public override void Init()
+ {
+ On = false;
+ }
+
+ public override void OnTaskComplete(PlayerControl pc, int completedTaskCount, int totalTaskCount)
+ {
+ pc.RpcIncreaseAbilityUseLimitBy(1);
+ }
+
+ public override void OnPet(PlayerControl pc)
+ {
+ if (pc.GetAbilityUseLimit() < 1) return;
+ var target = ExternalRpcPetPatch.SelectKillButtonTarget(pc);
+ if (target != null && pc.RpcCheckAndMurder(target)) pc.RpcRemoveAbilityUse();
+ }
+ }
+}
diff --git a/Roles/Neutral/SchrodingersCat.cs b/Roles/Neutral/SchrodingersCat.cs
new file mode 100644
index 000000000..86e5ac650
--- /dev/null
+++ b/Roles/Neutral/SchrodingersCat.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EHR.Roles.Impostor
+{
+ internal class SchrodingersCat : RoleBase
+ {
+ public static bool On;
+ public override bool IsEnable => On;
+
+ public static OptionItem WinsWithCrewIfNotAttacked;
+
+ public static void SetupCustomOption()
+ {
+ const int id = 13900;
+ Options.SetupRoleOptions(id, TabGroup.NeutralRoles, CustomRoles.SchrodingersCat);
+ WinsWithCrewIfNotAttacked = BooleanOptionItem.Create(id + 2, "SchrodingersCat.WinsWithCrewIfNotAttacked", true, TabGroup.NeutralRoles, false)
+ .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SchrodingersCat]);
+ }
+
+ public override void Add(byte playerId)
+ {
+ On = true;
+ }
+
+ public override void Init()
+ {
+ On = false;
+ }
+
+ public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target)
+ {
+ var killerRole = killer.GetCustomRole();
+
+ target.RpcSetCustomRole(killerRole);
+
+ killer.SetKillCooldown(5f);
+
+ killer.Notify(string.Format(Translator.GetString("SchrodingersCat.Notify.KillerRecruited"), target.GetRealName(), CustomRoles.SchrodingersCat.ToColoredString()));
+ target.Notify(string.Format(Translator.GetString("SchrodingersCat.Notify.RecruitedByKiller"), killer.GetRealName(), killerRole.ToColoredString()));
+
+ Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
+ Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: killer);
+
+ return false;
+ }
+ }
+}
diff --git a/main.cs b/main.cs
index 46598b618..d306cda4c 100644
--- a/main.cs
+++ b/main.cs
@@ -433,6 +433,7 @@ public class Main : BasePlugin
{ CustomRoles.Sprayer, "#ffc038" },
{ CustomRoles.PlagueDoctor, "#ff6633" },
{ CustomRoles.Postman, "#00b893" },
+ { CustomRoles.SchrodingersCat, "#616161" },
{ CustomRoles.Impartial, "#4287f5" },
{ CustomRoles.Predator, "#c73906" },
{ CustomRoles.Reckless, "#6e000d" },
@@ -447,6 +448,7 @@ public class Main : BasePlugin
{ CustomRoles.Agitater, "#F4A460" },
{ CustomRoles.BloodKnight, "#630000" },
{ CustomRoles.Juggernaut, "#A41342" },
+ { CustomRoles.Cherokious, "#de4b9e" },
{ CustomRoles.Parasite, "#ff1919" },
{ CustomRoles.Crewpostor, "#ff1919" },
{ CustomRoles.Refugee, "#ff1919" },
@@ -714,6 +716,7 @@ public enum CustomWinner
RuthlessRomantic = CustomRoles.RuthlessRomantic,
Doppelganger = CustomRoles.Doppelganger,
Imitator = CustomRoles.Imitator,
+ Cherokious = CustomRoles.Cherokious,
Bloodlust = CustomRoles.Bloodlust
}
@@ -744,7 +747,8 @@ public enum AdditionalWinners
Postman = CustomRoles.Postman,
Impartial = CustomRoles.Impartial,
Predator = CustomRoles.Predator,
- SoulHunter = CustomRoles.SoulHunter
+ SoulHunter = CustomRoles.SoulHunter,
+ SchrodingersCat = CustomRoles.SchrodingersCat
}
public enum SuffixModes