Add Wasp, Trainee, Vortex

This commit is contained in:
Gurge44 committed 2024-10-19 12:22:02 +02:00
1 parent 9019361870
commit 372177f2c4
14 files changed
+362 -2

No files matched your search

+4 -1
View File
@@ -105,6 +105,7 @@ public enum CustomRoles
Vindicator,
Visionary,
Warlock,
Wasp,
Wildling,
Witch,
YinYanger,
@@ -341,6 +342,7 @@ public enum CustomRoles
Vengeance,
VengefulRomantic,
Virus,
Vortex,
Vulture,
Wraith,
Werewolf,
@@ -381,7 +383,7 @@ public enum CustomRoles
// Natural Disasters
NDPlayer,
// Room Rush
RRPlayer,
@@ -457,6 +459,7 @@ public enum CustomRoles
Reach,
Recruit,
Rookie,
Trainee,
DualPersonality, // Schizophrenic
Seer,
Sleuth,
+6
View File
@@ -121,6 +121,7 @@ internal static class CustomRolesHelper
CustomRoles.Dictator => CustomRoles.Crewmate,
CustomRoles.Inhibitor => CustomRoles.Impostor,
CustomRoles.Kidnapper => CustomRoles.Shapeshifter,
CustomRoles.Wasp => CustomRoles.Impostor,
CustomRoles.Assumer => CustomRoles.Impostor,
CustomRoles.Augmenter => CustomRoles.Shapeshifter,
CustomRoles.Ventriloquist => CustomRoles.Impostor,
@@ -419,6 +420,7 @@ internal static class CustomRolesHelper
CustomRoles.BloodKnight => RoleTypes.Impostor,
CustomRoles.Poisoner => RoleTypes.Impostor,
CustomRoles.NSerialKiller => RoleTypes.Impostor,
CustomRoles.Vortex => RoleTypes.Impostor,
CustomRoles.Beehive => RoleTypes.Impostor,
CustomRoles.RouleteGrandeur => RoleTypes.Impostor,
CustomRoles.Nonplus => RoleTypes.Impostor,
@@ -510,6 +512,7 @@ internal static class CustomRolesHelper
CustomRoles.Rogue or
CustomRoles.Parasite or
CustomRoles.NSerialKiller or
CustomRoles.Vortex or
CustomRoles.Beehive or
CustomRoles.RouleteGrandeur or
CustomRoles.Nonplus or
@@ -583,6 +586,7 @@ internal static class CustomRolesHelper
CustomRoles.Augmenter or
CustomRoles.Inhibitor or
CustomRoles.Kidnapper or
CustomRoles.Wasp or
CustomRoles.Hypnotist or
CustomRoles.Assumer or
CustomRoles.Generator or
@@ -1237,6 +1241,7 @@ internal static class CustomRolesHelper
CustomRoles.Sniper => RoleOptionType.Impostor_Killing,
CustomRoles.BoobyTrap => RoleOptionType.Impostor_Killing,
CustomRoles.Underdog => RoleOptionType.Impostor_Killing,
CustomRoles.Wasp => RoleOptionType.Impostor_Killing,
CustomRoles.Witch => RoleOptionType.Impostor_Killing,
CustomRoles.Zombie => RoleOptionType.Impostor_Killing,
CustomRoles.Bard => RoleOptionType.Impostor_Support,
@@ -1493,6 +1498,7 @@ public enum CountTypes
HexMaster,
Wraith,
NSerialKiller,
Vortex,
Beehive,
RouleteGrandeur,
Nonplus,
+7
View File
@@ -67,6 +67,7 @@ static class ExtendedPlayerControl
public static bool CanUseVent(this PlayerControl player, int ventId)
{
if (player.Is(CustomRoles.Trainee) && MeetingStates.FirstMeeting) return false;
return GameStates.IsInTask && ((player.inVent && player.GetClosestVent()?.Id == ventId) || (player.CanUseImpostorVentButton() || player.GetRoleTypes() == RoleTypes.Engineer) && Main.PlayerStates.Values.All(x => x.Role.CanUseVent(player, ventId)));
}
@@ -1169,6 +1170,12 @@ static class ExtendedPlayerControl
public static bool CanUseSabotage(this PlayerControl pc)
{
if (!pc.IsAlive() || pc.Data.Role.Role == RoleTypes.GuardianAngel) return false;
if (pc.Is(CustomRoles.Trainee) && MeetingStates.FirstMeeting)
{
pc.Notify(GetString("TraineeNotify"));
return false;
}
return Main.PlayerStates.TryGetValue(pc.PlayerId, out var state) && state.Role.CanUseSabotage(pc);
}
+1
View File
@@ -57,6 +57,7 @@ public class PlayerState(byte playerId)
Assumed,
Negotiation,
Trapped,
Stung,
// Natural Disasters
Meteor,
+1
View File
@@ -160,6 +160,7 @@ public static class NameColorManager
CustomRoles.Socialite when ((Socialite)seerRoleClass).MarkedPlayerId == target.PlayerId => Main.RoleColors[seerRole],
CustomRoles.Beehive when ((Beehive)seerRoleClass).StungPlayers.ContainsKey(target.PlayerId) => "000000",
CustomRoles.Dad when ((Dad)seerRoleClass).DrunkPlayers.Contains(target.PlayerId) => "000000",
CustomRoles.Wasp when seerRoleClass is Wasp wasp && (wasp.DelayedKills.ContainsKey(target.PlayerId) || wasp.MeetingKills.Contains(target.PlayerId)) => "000000",
CustomRoles.God when God.KnowInfo.GetValue() == 1 => target.GetTeam() switch
{
Team.Impostor => Main.ImpostorColor,
+3
View File
@@ -745,6 +745,7 @@ public static class Utils
case CustomRoles.Eclipse:
case CustomRoles.Pyromaniac:
case CustomRoles.NSerialKiller:
case CustomRoles.Vortex:
case CustomRoles.Beehive:
case CustomRoles.RouleteGrandeur:
case CustomRoles.Nonplus:
@@ -2020,6 +2021,7 @@ public static class Utils
else
{
SelfMark.Append(Witch.GetSpelledMark(seer.PlayerId, isForMeeting));
if (isForMeeting) SelfMark.Append(Wasp.GetStungMark(seer.PlayerId));
}
GameMode:
@@ -2176,6 +2178,7 @@ public static class Utils
if (Options.CurrentGameMode != CustomGameMode.Standard || GameStates.IsLobby) goto BeforeEnd2;
TargetMark.Append(Witch.GetSpelledMark(target.PlayerId, isForMeeting));
if (isForMeeting) TargetMark.Append(Wasp.GetStungMark(target.PlayerId));
if (target.Is(CustomRoles.SuperStar) && Options.EveryOneKnowSuperStar.GetBool())
TargetMark.Append(ColorString(GetRoleColor(CustomRoles.SuperStar), "★"));
+2
View File
@@ -511,6 +511,7 @@ static class CheckForEndVotingPatch
if (Witch.PlayerIdList.Count > 0) Witch.OnCheckForEndVoting(deathReason, playerIds);
if (Virus.PlayerIdList.Count > 0) Virus.OnCheckForEndVoting(deathReason, playerIds);
if (deathReason == PlayerState.DeathReason.Vote) Gaslighter.OnExile(playerIds);
if (Wasp.On && deathReason == PlayerState.DeathReason.Vote) Wasp.OnExile(playerIds);
foreach (var playerId in playerIds)
{
try
@@ -960,6 +961,7 @@ static class MeetingHudStartPatch
}
sb.Append(Witch.GetSpelledMark(target.PlayerId, true));
sb.Append(Wasp.GetStungMark(target.PlayerId));
if (target.Is(CustomRoles.SuperStar) && Options.EveryOneKnowSuperStar.GetBool())
sb.Append(Utils.ColorString(Utils.GetRoleColor(CustomRoles.SuperStar), "★"));
+6
View File
@@ -122,6 +122,12 @@ static class ExternalRpcPetPatch
return;
}
if (pc.Is(CustomRoles.Trainee) && MeetingStates.FirstMeeting)
{
pc.Notify(Translator.GetString("TraineeNotify"));
return;
}
if (pc.HasAbilityCD())
{
if (!pc.IsHost()) pc.Notify(Translator.GetString("AbilityOnCooldown"));
+9 -1
View File
@@ -760,7 +760,15 @@ static class ShapeshiftPatch
var shapeshifting = shapeshifter.PlayerId != target.PlayerId;
if (AmongUsClient.Instance.AmHost && shapeshifting && !Rhapsode.CheckAbilityUse(shapeshifter)) return false;
if (AmongUsClient.Instance.AmHost && shapeshifting)
{
if (!Rhapsode.CheckAbilityUse(shapeshifter)) return false;
if (shapeshifter.Is(CustomRoles.Trainee) && MeetingStates.FirstMeeting)
{
shapeshifter.Notify(GetString("TraineeNotify"));
return false;
}
}
Main.CheckShapeshift[shapeshifter.PlayerId] = shapeshifting;
Main.ShapeshiftTarget[shapeshifter.PlayerId] = target.PlayerId;
+24
View File
@@ -1780,6 +1780,7 @@
"DeathReason.Assumed": "Assumed",
"DeathReason.Negotiation": "Negotiation",
"DeathReason.Trapped": "Trapped",
"DeathReason.Stung": "Stung",
"DeathReason.Meteor": "Meteor Strike",
"DeathReason.Lava": "Lava",
"DeathReason.Tornado": "Tornado",
@@ -2854,6 +2855,18 @@
"pet_D2WormPet": "Worm",
"pet_RANDOM_FOR_EVERYONE": "<color=#ffff00>Random</color>",
"PlayerExemptedFromAFK": "{0} was exempted from AFK detection for 1 game",
"Wasp": "Wasp",
"WaspInfo": "Sting Players",
"WaspInfoLong": "(Impostors):\\nAs the Wasp, you can sting players by using your kill button on them. If the player has a kill button they will die after a few seconds. Otherwise, they will have a ◀ next to their name in the next meeting. They will die after the meeting if the wasp isn't ejected.\\n\\nWhen someone tries to kill you, you may evade the kill based on the host's settings. If so, 2 things can happen:\\n1. You become aggressive and go into swarm mode with no kill cooldown for some time.\\n2. You get pest controlled. This will daze you, Stunting your vision, speed and ability to sting for some time.\\nNote: If you evade a kill, you cannot evade kills, sabotage or vent for the rest of that round.",
"Wasp.StingCooldown": "Sting Cooldown",
"Wasp.KillDelay": "Sting Kill Delay",
"Wasp.EvadeKills": "Evade Kills",
"Wasp.SwarmModeDuration": "Swarm Mode Duration",
"Wasp.WaspDiesAfterSwarmEnd": "Suicide After Swarm Mode Ends",
"Wasp.PestControlDuration": "Pest Control Duration",
"Wasp.PestControlSpeed": "Speed During Pest Control",
"Wasp.PestControlVision": "Vision During Pest Control",
"Wasp.SwarmModeSuffix": "<#ffff00>You've been attacked and entered swarm mode for {0} seconds!</color>",
"Swapster": "Swapster",
"SwapsterInfo": "Swap Players",
"SwapsterInfoLong": "(Impostors):\\nAs the Swapster, you can select 2 players using your shapeshift menu to swap them.",
@@ -3290,6 +3303,12 @@
"VentguardNotifyOnBlockedVentUse": "Notify When Someone Tries to Use a Blocked Vent",
"VentguardBlocksResetOnMeeting": "Reset Blocked Vents Every Meeting",
"VentBlockSuccess": "This vent is now blocked",
"Vortex": "Vortex",
"VortexInfo": "Your victims' bodies teleport to a random vent",
"VortexInfoLong": "(Neutrals):\\nAs the Vortex, when you kill someone, their body teleports to a random vent. The body will be reported in the vent.",
"Vortex.IfTargetCannotBeTeleported": "If the target cannot be teleported to a random vent",
"Vortex.NoTPMode.Block": "Cancel Kill",
"Vortex.NoTPMode.DoWithoutTP": "Kill Without TP",
"Enigma": "Enigma",
"EnigmaInfo": "Get Clues about Killers",
"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 preciser the clues get.\\n\\n<size=70%><u><b>Light colors:</b></u> Red, Pink, Orange, Yellow, White, Cyan, Lime, Rose, Banana, Coral\\n<u><b>Dark colors:</b></u> Blue, Green, Black, Purple, Brown, Maroon, Gray, Tan</size>",
@@ -4438,6 +4457,11 @@
"RookieInfo": "You Can't Kill on the First Round",
"RookieInfoLong": "(Add-ons):\\nYou can't kill on the first round.",
"Prefix.Rookie": "Rookie",
"Trainee": "Trainee",
"TraineeInfo": "You Can't Vent/Sabotage/SS/Pet Round 1",
"TraineeInfoLong": "(Add-ons):\\nYou can't vent, sabotage, shapeshift, or pet on the first round.",
"Prefix.Trainee": "Trainee",
"TraineeNotify": "<#ffff00>As the <#4287f5>Trainee</color>, you can't vent, sabotage, shapeshift, or pet on the first round!</color>",
"Soothsayer": "Soothsayer",
"SoothsayerInfo": "Predict Who Kills You",
"SoothsayerInfoLong": "(Crewmates):\\nAs the Soothsayer, you can predict who kills you. If you're correct, when you die, everyone will be notified of the killer's identity.",
+12
View File
@@ -0,0 +1,12 @@
namespace EHR.AddOns.Common
{
public class Trainee : IAddon
{
public AddonTypes Type => AddonTypes.Harmful;
public void SetupCustomOption()
{
Options.SetupAdtRoleOptions(647844, CustomRoles.Trainee, canSetNum: true, teamSpawnOptions: true);
}
}
}
+226
View File
@@ -0,0 +1,226 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using EHR.Modules;
using EHR.Patches;
using Hazel;
namespace EHR.Impostor
{
public class Wasp : RoleBase
{
public static bool On;
private static List<Wasp> Instances = [];
private static OptionItem StingCooldown;
private static OptionItem KillDelay;
private static OptionItem EvadeKills;
private static OptionItem SwarmModeDuration;
private static OptionItem WaspDiesAfterSwarmEnd;
private static OptionItem PestControlDuration;
private static OptionItem PestControlSpeed;
private static OptionItem PestControlVision;
public Dictionary<byte, long> DelayedKills;
private bool EvadedKillThisRound;
private long LastUpdate;
public HashSet<byte> MeetingKills;
private long PestControlEnd;
private long SwarmModeEnd;
private PlayerControl WaspPC;
public override bool IsEnable => On;
public override void SetupCustomOption()
{
StartSetup(645350)
.AutoSetupOption(ref StingCooldown, 30, new IntegerValueRule(1, 120, 1), OptionFormat.Seconds)
.AutoSetupOption(ref KillDelay, 5, new IntegerValueRule(1, 30, 1), OptionFormat.Seconds)
.AutoSetupOption(ref EvadeKills, true)
.AutoSetupOption(ref SwarmModeDuration, 15, new IntegerValueRule(1, 60, 1), OptionFormat.Seconds, overrideParent: EvadeKills)
.AutoSetupOption(ref WaspDiesAfterSwarmEnd, true, overrideParent: EvadeKills)
.AutoSetupOption(ref PestControlDuration, 10, new IntegerValueRule(1, 60, 1), OptionFormat.Seconds, overrideParent: EvadeKills)
.AutoSetupOption(ref PestControlSpeed, 0.5f, new FloatValueRule(0.05f, 2f, 0.05f), OptionFormat.Multiplier, overrideParent: EvadeKills)
.AutoSetupOption(ref PestControlVision, 0.2f, new FloatValueRule(0f, 1f, 0.05f), OptionFormat.Multiplier, overrideParent: EvadeKills);
}
public override void Init()
{
On = false;
Instances = [];
}
public override void Add(byte playerId)
{
On = true;
Instances.Add(this);
WaspPC = playerId.GetPlayer();
DelayedKills = [];
MeetingKills = [];
SwarmModeEnd = 0;
PestControlEnd = 0;
EvadedKillThisRound = false;
}
public override void SetKillCooldown(byte id)
{
Main.AllPlayerKillCooldown[id] = SwarmModeEnd == 0 ? StingCooldown.GetInt() : 0.01f;
}
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
{
if (PestControlEnd == 0) return;
opt.SetVision(false);
opt.SetFloat(FloatOptionNames.ImpostorLightMod, PestControlVision.GetFloat());
opt.SetFloat(FloatOptionNames.CrewLightMod, PestControlVision.GetFloat());
}
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (DelayedKills.ContainsKey(target.PlayerId)) return false;
if (target.HasKillButton()) DelayedKills[target.PlayerId] = Utils.TimeStamp + KillDelay.GetInt();
else MeetingKills.Add(target.PlayerId);
killer.SetKillCooldown(StingCooldown.GetInt());
return false;
}
public override void OnGlobalFixedUpdate(PlayerControl pc, bool lowLoad)
{
if (lowLoad || !GameStates.IsInTask || ExileController.Instance || !DelayedKills.TryGetValue(pc.PlayerId, out var ts) || ts > Utils.TimeStamp) return;
pc.Suicide(PlayerState.DeathReason.Stung, WaspPC);
}
public override void OnFixedUpdate(PlayerControl pc)
{
if (SwarmModeEnd == 0 && PestControlEnd == 0) return;
var now = Utils.TimeStamp;
if (LastUpdate == now) return;
LastUpdate = now;
if (SwarmModeEnd != 0)
{
if (SwarmModeEnd <= now)
{
SwarmModeEnd = 0;
Utils.SendRPC(CustomRPC.SyncRoleData, WaspPC.PlayerId, SwarmModeEnd);
if (WaspDiesAfterSwarmEnd.GetBool())
{
pc.Suicide();
}
else
{
pc.SyncSettings();
pc.SetKillCooldown(StingCooldown.GetInt());
}
}
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}
if (PestControlEnd != 0 && PestControlEnd <= now)
{
PestControlEnd = 0;
Main.AllPlayerSpeed[pc.PlayerId] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
pc.MarkDirtySettings();
}
}
public override void OnReportDeadBody()
{
foreach (var id in DelayedKills.Keys)
{
var player = id.GetPlayer();
if (player == null || !player.IsAlive()) continue;
player.Suicide(PlayerState.DeathReason.Stung, WaspPC);
}
DelayedKills.Clear();
}
public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target)
{
if (!EvadeKills.GetBool() || EvadedKillThisRound) return true;
if (IRandom.Instance.Next(2) == 0)
{
SwarmModeEnd = Utils.TimeStamp + SwarmModeDuration.GetInt();
Utils.SendRPC(CustomRPC.SyncRoleData, WaspPC.PlayerId, SwarmModeEnd);
target.SyncSettings();
target.SetKillCooldown(0.01f);
}
else
{
PestControlEnd = Utils.TimeStamp + PestControlDuration.GetInt();
Main.AllPlayerSpeed[target.PlayerId] = PestControlSpeed.GetFloat();
target.MarkDirtySettings();
}
EvadedKillThisRound = true;
return false;
}
public override bool CanUseKillButton(PlayerControl pc) => !EvadedKillThisRound || SwarmModeEnd != 0;
public override bool CanUseSabotage(PlayerControl pc) => !EvadedKillThisRound;
public override bool CanUseImpostorVentButton(PlayerControl pc) => !EvadedKillThisRound;
public override void AfterMeetingTasks()
{
EvadedKillThisRound = false;
MeetingKills.Clear();
}
public static void OnExile(byte[] exileIds)
{
try
{
foreach (Wasp instance in Instances)
{
foreach (byte id in exileIds)
{
if (id == instance.WaspPC.PlayerId)
instance.MeetingKills.Clear();
}
}
List<byte> waspDeathList = [];
foreach (PlayerControl pc in Main.AllAlivePlayerControls)
{
foreach (Wasp instance in Instances)
{
if (Main.AfterMeetingDeathPlayers.ContainsKey(pc.PlayerId)) continue;
if (instance.MeetingKills.Contains(pc.PlayerId) && instance.WaspPC != null && instance.WaspPC.IsAlive())
{
pc.SetRealKiller(instance.WaspPC);
waspDeathList.Add(pc.PlayerId);
}
}
}
CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Stung, [.. waspDeathList]);
}
catch (Exception e)
{
Utils.ThrowException(e);
}
}
public static string GetStungMark(byte target)
{
return Instances.Any(x => x.MeetingKills.Contains(target)) ? Utils.ColorString(Palette.ImpostorRed, "\u25c0") : string.Empty;
}
public void ReceiveRPC(MessageReader reader) => SwarmModeEnd = long.Parse(reader.ReadString());
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
if (seer.PlayerId != WaspPC.PlayerId || seer.PlayerId != target.PlayerId || (seer.IsModClient() && !hud) || meeting || SwarmModeEnd == 0) return string.Empty;
return string.Format(Translator.GetString("Wasp.SwarmModeSuffix"), SwarmModeEnd - Utils.TimeStamp);
}
}
}
+58
View File
@@ -0,0 +1,58 @@
using AmongUs.GameOptions;
namespace EHR.Neutral
{
public class Vortex : RoleBase
{
private const int Id = 645650;
public static bool On;
private static OptionItem KillCooldown;
private static OptionItem CanVent;
private static OptionItem HasImpostorVision;
private static OptionItem IfTargetCannotBeTeleported;
public override bool IsEnable => On;
public override void SetupCustomOption()
{
Options.SetupRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.Vortex);
KillCooldown = new FloatOptionItem(Id + 2, "KillCooldown", new(0f, 180f, 0.5f), 22.5f, TabGroup.NeutralRoles)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Vortex])
.SetValueFormat(OptionFormat.Seconds);
CanVent = new BooleanOptionItem(Id + 3, "CanVent", true, TabGroup.NeutralRoles)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Vortex]);
HasImpostorVision = new BooleanOptionItem(Id + 4, "ImpostorVision", true, TabGroup.NeutralRoles)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Vortex]);
IfTargetCannotBeTeleported = new StringOptionItem(Id + 5, "Vortex.IfTargetCannotBeTeleported", ["Vortex.NoTPMode.Block", "Vortex.NoTPMode.DoWithoutTP"], 0, TabGroup.NeutralRoles)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Vortex]);
}
public override void Init()
{
On = false;
}
public override void Add(byte playerId)
{
On = true;
}
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
public override void ApplyGameOptions(IGameOptions opt, byte id) => opt.SetVision(HasImpostorVision.GetBool());
public override bool CanUseImpostorVentButton(PlayerControl pc) => CanVent.GetBool();
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (!killer.RpcCheckAndMurder(target, check: true)) return false;
if (!target.TPtoRndVent())
{
killer.Notify(Translator.GetString("TargetCannotBeTeleported"));
return IfTargetCannotBeTeleported.GetValue() == 1;
}
LateTask.New(() => target.Suicide(PlayerState.DeathReason.Kill, killer), 0.2f, log: false);
return false;
}
}
}
+3
View File
@@ -475,6 +475,7 @@ public class Main : BasePlugin
{ CustomRoles.HexMaster, "#ff00ff" },
{ CustomRoles.Wraith, "#4B0082" },
{ CustomRoles.NSerialKiller, "#233fcc" },
{ CustomRoles.Vortex, "#a83293" },
{ CustomRoles.Beehive, "#ffff00" },
{ CustomRoles.RouleteGrandeur, "#a88332" },
{ CustomRoles.Nonplus, "#09632f" },
@@ -559,6 +560,7 @@ public class Main : BasePlugin
{ CustomRoles.AntiTP, "#fcba03" },
{ CustomRoles.Deadlined, "#ffa500" },
{ CustomRoles.Rookie, "#bf671f" },
{ CustomRoles.Trainee, "#4287f5" },
{ CustomRoles.Taskcounter, "#ff1919" },
{ CustomRoles.Stained, "#e6bf91" },
{ CustomRoles.Clumsy, "#b8b8b8" },
@@ -804,6 +806,7 @@ public enum CustomWinner
Necromancer = CustomRoles.Necromancer,
Wraith = CustomRoles.Wraith,
SerialKiller = CustomRoles.NSerialKiller,
Vortex = CustomRoles.Vortex,
Beehive = CustomRoles.Beehive,
RouleteGrandeur = CustomRoles.RouleteGrandeur,
Nonplus = CustomRoles.Nonplus,