diff --git a/CTA/CustomTeamAssigner/CustomRoles.cs b/CTA/CustomTeamAssigner/CustomRoles.cs
index 1951b8db6..5f811525b 100644
--- a/CTA/CustomTeamAssigner/CustomRoles.cs
+++ b/CTA/CustomTeamAssigner/CustomRoles.cs
@@ -273,6 +273,7 @@ public enum CustomRoles
Evolver,
Executioner,
Totocalcio, // Follower
+ Gaslighter,
Glitch,
God,
FFF, // Hater
@@ -329,6 +330,7 @@ public enum CustomRoles
DarkHide, // Stalker
Succubus,
Sunnyboy,
+ Technician,
Tank,
Terrorist,
Tiger,
diff --git a/EHR.sln.DotSettings b/EHR.sln.DotSettings
index 189c52d74..326108f5a 100644
--- a/EHR.sln.DotSettings
+++ b/EHR.sln.DotSettings
@@ -7,6 +7,7 @@
AU
CD
CK
+ CNO
CTF
DLL
DY
diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs
index 8c68882b9..0f58bb8e0 100644
--- a/Modules/ExtendedPlayerControl.cs
+++ b/Modules/ExtendedPlayerControl.cs
@@ -343,10 +343,24 @@ static class ExtendedPlayerControl
AmongUsClient.Instance.FinishRpcImmediately(msg);
}
+ public static bool HasAbilityCD(this PlayerControl pc) => Main.AbilityCD.ContainsKey(pc.PlayerId);
public static void AddKCDAsAbilityCD(this PlayerControl pc) => AddAbilityCD(pc, (int)Math.Round(Main.AllPlayerKillCooldown.TryGetValue(pc.PlayerId, out var KCD) ? KCD : Options.DefaultKillCooldown));
public static void AddAbilityCD(this PlayerControl pc, bool includeDuration = true) => Utils.AddAbilityCD(pc.GetCustomRole(), pc.PlayerId, includeDuration);
- public static void AddAbilityCD(this PlayerControl pc, int CD) => Main.AbilityCD[pc.PlayerId] = (TimeStamp, CD);
- public static bool HasAbilityCD(this PlayerControl pc) => Main.AbilityCD.ContainsKey(pc.PlayerId);
+
+ public static void AddAbilityCD(this PlayerControl pc, int CD)
+ {
+ Main.AbilityCD[pc.PlayerId] = (TimeStamp, CD);
+ SendRPC(CustomRPC.SyncAbilityCD, 1, pc.PlayerId, CD);
+ }
+
+ public static void RemoveAbilityCD(this PlayerControl pc)
+ {
+ if (Main.AbilityCD.ContainsKey(pc.PlayerId))
+ {
+ Main.AbilityCD.Remove(pc.PlayerId);
+ SendRPC(CustomRPC.SyncAbilityCD, 3, pc.PlayerId);
+ }
+ }
public static float GetAbilityUseLimit(this PlayerControl pc) => Main.AbilityUseLimit.GetValueOrDefault(pc.PlayerId, float.NaN);
public static float GetAbilityUseLimit(this byte playerId) => Main.AbilityUseLimit.GetValueOrDefault(playerId, float.NaN);
diff --git a/Modules/RPC.cs b/Modules/RPC.cs
index 5f6f49b07..d66344ed3 100644
--- a/Modules/RPC.cs
+++ b/Modules/RPC.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
@@ -56,6 +57,7 @@ public enum CustomRPC
RemoveSubRole,
Arrow,
FixModdedClientCNO,
+ SyncAbilityCD,
// Roles
SyncRoleData,
@@ -365,6 +367,33 @@ internal class RPCHandlerPatch
if (reader.ReadBoolean()) TargetArrow.ReceiveRPC(reader);
else LocateArrow.ReceiveRPC(reader);
break;
+ case CustomRPC.SyncAbilityCD:
+ {
+ switch (reader.ReadPackedInt32())
+ {
+ case 1:
+ {
+ byte id = reader.ReadByte();
+ int cd = reader.ReadPackedInt32();
+ long ts = Utils.TimeStamp;
+ Main.AbilityCD[id] = (ts, cd);
+ break;
+ }
+ case 2:
+ {
+ Main.AbilityCD.Clear();
+ break;
+ }
+ case 3:
+ {
+ byte id = reader.ReadByte();
+ Main.AbilityCD.Remove(id);
+ break;
+ }
+ }
+
+ break;
+ }
case CustomRPC.SyncRoleData:
{
byte id = reader.ReadByte();
@@ -399,7 +428,7 @@ internal class RPCHandlerPatch
case CustomRPC.SyncSentry:
{
byte id = reader.ReadByte();
- if (Main.PlayerStates[id].Role is not EHR.Impostor.Sentry sentry) break;
+ if (Main.PlayerStates[id].Role is not Impostor.Sentry sentry) break;
sentry.MonitoredRoom = Utils.GetPlayerById(id).GetPlainShipRoom();
break;
}
@@ -912,7 +941,7 @@ internal static class RPC
Main.Instance.StartCoroutine(VersionCheck());
return;
- static System.Collections.IEnumerator VersionCheck()
+ static IEnumerator VersionCheck()
{
while (PlayerControl.LocalPlayer == null) yield return null;
MessageWriter writer = AmongUsClient.Instance.StartRpc(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.VersionCheck);
diff --git a/Modules/Utils.cs b/Modules/Utils.cs
index 5da817f4f..952fbd1ab 100644
--- a/Modules/Utils.cs
+++ b/Modules/Utils.cs
@@ -2450,7 +2450,9 @@ public static class Utils
{
if (role.UsesPetInsteadOfKill())
{
- Main.AbilityCD[playerId] = (TimeStamp, (int)Math.Round(Main.AllPlayerKillCooldown.TryGetValue(playerId, out var KCD) ? KCD : Options.DefaultKillCooldown));
+ int kcd = (int)Math.Round(Main.AllPlayerKillCooldown.TryGetValue(playerId, out var KCD) ? KCD : Options.DefaultKillCooldown);
+ Main.AbilityCD[playerId] = (TimeStamp, kcd);
+ SendRPC(CustomRPC.SyncAbilityCD, 1, playerId, kcd);
return;
}
@@ -2507,6 +2509,7 @@ public static class Utils
CD = (int)Math.Round(CD * 0.75f);
Main.AbilityCD[playerId] = (TimeStamp, CD);
+ SendRPC(CustomRPC.SyncAbilityCD, 1, playerId, CD);
if (Options.UseUnshiftTrigger.GetBool() && role.SimpleAbilityTrigger() && (!role.IsNeutral() || Options.UseUnshiftTriggerForNKs.GetBool()))
GetPlayerById(playerId)?.RpcResetAbilityCooldown();
diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs
index db09afce5..85554cb05 100644
--- a/Patches/PlayerControlPatch.cs
+++ b/Patches/PlayerControlPatch.cs
@@ -589,8 +589,15 @@ class MurderPlayerPatch
}
if (killer.Is(CustomRoles.Sniper))
+ {
if (!Options.UsePets.GetBool()) killer.RpcResetAbilityCooldown();
- else Main.AbilityCD[killer.PlayerId] = (TimeStamp, Options.DefaultShapeshiftCooldown.GetInt());
+ else
+ {
+ int cd = Options.DefaultShapeshiftCooldown.GetInt();
+ Main.AbilityCD[killer.PlayerId] = (TimeStamp, cd);
+ SendRPC(CustomRPC.SyncAbilityCD, 1, killer.PlayerId, cd);
+ }
+ }
if (killer != __instance)
{
@@ -1089,6 +1096,7 @@ class ReportDeadBodyPatch
}
Main.AbilityCD.Clear();
+ SendRPC(CustomRPC.SyncAbilityCD, 2);
if (player.Is(CustomRoles.Damocles))
{
@@ -1234,7 +1242,7 @@ static class FixedUpdatePatch
var player = __instance;
var playerId = player.PlayerId;
var lpId = PlayerControl.LocalPlayer.PlayerId;
- var localPlayer = player.PlayerId == lpId; // Updates that are independent of the player are only executed for the local player.
+ var localPlayer = playerId == lpId; // Updates that are independent of the player are only executed for the local player.
bool lowLoad = false;
if (Options.LowLoadMode.GetBool())
@@ -1297,9 +1305,9 @@ static class FixedUpdatePatch
if (!GameStates.IsLobby)
{
- if (player.Is(CustomRoles.Spurt) && !Mathf.Approximately(Main.AllPlayerSpeed[player.PlayerId], Spurt.StartingSpeed[player.PlayerId]) && !inTask && !GameStates.IsMeeting) // fix ludicrous bug
+ if (player.Is(CustomRoles.Spurt) && !Mathf.Approximately(Main.AllPlayerSpeed[playerId], Spurt.StartingSpeed[playerId]) && !inTask && !GameStates.IsMeeting) // fix ludicrous bug
{
- Main.AllPlayerSpeed[player.PlayerId] = Spurt.StartingSpeed[player.PlayerId];
+ Main.AllPlayerSpeed[playerId] = Spurt.StartingSpeed[playerId];
player.MarkDirtySettings();
}
@@ -1336,7 +1344,7 @@ static class FixedUpdatePatch
PlagueBearer.playerIdList.Remove(playerId);
}
- bool checkPos = inTask && player != null && alive && !Pelican.IsEaten(player.PlayerId);
+ bool checkPos = inTask && player != null && alive && !Pelican.IsEaten(playerId);
if (checkPos) Asthmatic.OnCheckPlayerPosition(player);
foreach (var state in Main.PlayerStates.Values)
{
@@ -1372,6 +1380,7 @@ static class FixedUpdatePatch
if (timer.START_TIMESTAMP + timer.TOTALCD < TimeStamp || !alive)
{
Main.AbilityCD.Remove(playerId);
+ SendRPC(CustomRPC.SyncAbilityCD, 3, playerId);
}
if (!player.IsModClient() && timer.TOTALCD - (now - timer.START_TIMESTAMP) <= 60) NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
@@ -1434,7 +1443,7 @@ static class FixedUpdatePatch
{
if (GameStates.IsLobby)
{
- if (Main.PlayerVersion.TryGetValue(player.PlayerId, out var ver))
+ if (Main.PlayerVersion.TryGetValue(playerId, out var ver))
{
if (Main.ForkId != ver.forkId)
__instance.cosmetics.nameText.text = $"{ver.forkId}\n{__instance?.name}";
diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json
index e6b565ae1..ef95a7415 100644
--- a/Resources/Lang/en_US.json
+++ b/Resources/Lang/en_US.json
@@ -2356,7 +2356,7 @@
"LoadingHint.25": "Meeting not ending when everyone voted?\nPress F6 to end the meeting while also counting the votes!",
"LoadingHint.26": "Do you have a friend who thinks they understand everything, and you want to prove them wrong?\nTurn on the Chemist role and see their reaction!",
"LoadingHint.27": "The Dev's favorite roles are:\nEcho, Penguin, Rift Maker, Bubble, Evolver, Rogue, Vengeance....\nand basically all crewmate roles :D",
- "LoadingHint.28": "Did you know?\nEHR has 7 different game modes! Try them out!\nSwitch game modes using the ⇄ button next to the huge title in the top left of the settings menu!",
+ "LoadingHint.28": "Did you know?\nEHR has 9 different game modes! Try them out!\nSwitch game modes using the ⇄ button next to the huge title in the top left of the settings menu!",
"LoadingHint.29": "Did you know?\nEHR has a unique game mode for expert gamers called Stop And Go!\nGive it a try after this game!",
"LoadingHint.30": "Getting Crewmate over and over again? Didn't get to kill for the last half an hour?\nUnleash your bloodthirst in our Free For All game mode!",
"LoadingHint.31": "Did you know?\nEHR has a custom Hide And Seek game mode with 12 special roles dedicated for this playstyle!",
diff --git a/Roles/Crewmate/Grappler.cs b/Roles/Crewmate/Grappler.cs
index 7f3f591c9..76b6703de 100644
--- a/Roles/Crewmate/Grappler.cs
+++ b/Roles/Crewmate/Grappler.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using EHR.Modules;
+using Hazel;
namespace EHR.Crewmate
{
@@ -19,7 +20,7 @@ namespace EHR.Crewmate
public override void SetupCustomOption()
{
- StartSetup(647250, TabGroup.CrewmateRoles, CustomRoles.Grappler)
+ StartSetup(647250)
.AutoSetupOption(ref AbilityUseLimit, 0, new IntegerValueRule(0, 20, 1), OptionFormat.Times)
.AutoSetupOption(ref AbilityUseGainWithEachTaskCompleted, 0.3f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times)
.AutoSetupOption(ref AbilityChargesWhenFinishedTasks, 0.2f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times);
@@ -56,7 +57,7 @@ namespace EHR.Crewmate
return base.GetProgressText(playerId, comms) + (InUse ? "<#00ff00>\u271a" : string.Empty);
}
- public void ReceiveRPC(Hazel.MessageReader reader)
+ public void ReceiveRPC(MessageReader reader)
{
InUse = reader.ReadBoolean();
}
diff --git a/Roles/Crewmate/Journalist.cs b/Roles/Crewmate/Journalist.cs
index 074a16640..52ffef43d 100644
--- a/Roles/Crewmate/Journalist.cs
+++ b/Roles/Crewmate/Journalist.cs
@@ -15,7 +15,7 @@ namespace EHR.Crewmate
public override void SetupCustomOption()
{
- StartSetup(647450, TabGroup.CrewmateRoles, CustomRoles.Journalist);
+ StartSetup(647450);
}
public override void Init()
diff --git a/Roles/Crewmate/Negotiator.cs b/Roles/Crewmate/Negotiator.cs
index 7705fa302..7e44d5ac6 100644
--- a/Roles/Crewmate/Negotiator.cs
+++ b/Roles/Crewmate/Negotiator.cs
@@ -24,7 +24,7 @@ namespace EHR.Crewmate
public override void SetupCustomOption()
{
- StartSetup(647950, TabGroup.CrewmateRoles, CustomRoles.Negotiator)
+ StartSetup(647950)
.AutoSetupOption(ref LowVision, 0.4f, new FloatValueRule(0f, 1f, 0.05f), OptionFormat.Multiplier)
.AutoSetupOption(ref LowSpeed, 0.9f, new FloatValueRule(0.1f, 2f, 0.1f), OptionFormat.Multiplier)
.AutoSetupOption(ref AbilityUseLimit, 0, new IntegerValueRule(0, 20, 1), OptionFormat.Times)
diff --git a/Roles/Impostor/Assumer.cs b/Roles/Impostor/Assumer.cs
index ac2adee20..6b5964f3c 100644
--- a/Roles/Impostor/Assumer.cs
+++ b/Roles/Impostor/Assumer.cs
@@ -21,7 +21,7 @@ namespace EHR.Impostor
public override void SetupCustomOption()
{
- StartSetup(649250, TabGroup.ImpostorRoles, CustomRoles.Assumer)
+ StartSetup(649250)
.AutoSetupOption(ref VoteReceiverDies, false)
.AutoSetupOption(ref MinPlayersToAssume, 6, new IntegerValueRule(1, 15, 1), OptionFormat.Players)
.AutoSetupOption(ref CanKill, true)
diff --git a/Roles/Impostor/Hypnotist.cs b/Roles/Impostor/Hypnotist.cs
index 15ba83a45..f1070682c 100644
--- a/Roles/Impostor/Hypnotist.cs
+++ b/Roles/Impostor/Hypnotist.cs
@@ -25,7 +25,7 @@ namespace EHR.Impostor
public override void SetupCustomOption()
{
- StartSetup(647550, TabGroup.ImpostorRoles, CustomRoles.Hypnotist)
+ StartSetup(647550)
.AutoSetupOption(ref AbilityCooldown, 30, new IntegerValueRule(0, 60, 1), OptionFormat.Seconds)
.AutoSetupOption(ref AbilityDuration, 15, new IntegerValueRule(0, 30, 1), OptionFormat.Seconds)
.AutoSetupOption(ref AbilityUseLimit, 1, new IntegerValueRule(0, 5, 1), OptionFormat.Times)
diff --git a/Roles/Neutral/Gaslighter.cs b/Roles/Neutral/Gaslighter.cs
index 9a890c011..ba5f77a5a 100644
--- a/Roles/Neutral/Gaslighter.cs
+++ b/Roles/Neutral/Gaslighter.cs
@@ -32,7 +32,7 @@ namespace EHR.Neutral
public override void SetupCustomOption()
{
- StartSetup(648350, TabGroup.NeutralRoles, CustomRoles.Gaslighter)
+ StartSetup(648350)
.AutoSetupOption(ref KillCooldown, 22.5f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
.AutoSetupOption(ref WinCondition, 0, WinConditionOptions)
.AutoSetupOption(ref CycleRepeats, false);
@@ -55,6 +55,8 @@ namespace EHR.Neutral
CycleFinished = false;
}
+ public override bool CanUseKillButton(PlayerControl pc) => pc.IsAlive();
+
public override void SetKillCooldown(byte id)
{
Main.AllPlayerKillCooldown[id] = CurrentRound switch
@@ -112,15 +114,16 @@ namespace EHR.Neutral
CurrentRound++;
}
+ var pc = GaslighterId.GetPlayer();
float limit = CurrentRound switch
{
Round.Knight => Monarch.KnightMax.GetFloat(),
Round.Shield => Medic.SkillLimit,
- _ => 0
+ _ => float.NaN
};
- GaslighterId.SetAbilityUseLimit(limit);
+ if (!float.IsNaN(limit)) GaslighterId.SetAbilityUseLimit(limit);
+ else pc?.RemoveAbilityCD();
- var pc = GaslighterId.GetPlayer();
pc?.ResetKillCooldown();
pc?.Notify(Translator.GetString($"Gaslighter.{CurrentRound}"));
}
diff --git a/Roles/Neutral/Shifter.cs b/Roles/Neutral/Shifter.cs
index 7d9d45f9d..bdc695737 100644
--- a/Roles/Neutral/Shifter.cs
+++ b/Roles/Neutral/Shifter.cs
@@ -111,6 +111,7 @@ namespace EHR.Neutral
}
Main.AbilityCD.Remove(killer.PlayerId);
+ Utils.SendRPC(CustomRPC.SyncAbilityCD, 3, killer.PlayerId);
killer.SyncSettings();
// ------------------------------------------------------------------------------------------
diff --git a/Roles/Neutral/Simon.cs b/Roles/Neutral/Simon.cs
index d3faa42c3..893221243 100644
--- a/Roles/Neutral/Simon.cs
+++ b/Roles/Neutral/Simon.cs
@@ -187,7 +187,7 @@ namespace EHR.Neutral
if (seer.IsModClient() && !hud && self) return string.Empty;
if (self) return Translator.GetString(simon.DoMode ? "SimonDoMode" : "SimonDontMode");
- else if (simon.MarkedPlayers.TryGetValue(target.PlayerId, out var value)) return Translator.GetString(GetNotify(value.Instruction, value.DoAction, true));
+ if (simon.MarkedPlayers.TryGetValue(target.PlayerId, out var value)) return Translator.GetString(GetNotify(value.Instruction, value.DoAction, true));
return string.Empty;
}
diff --git a/Roles/Neutral/Tank.cs b/Roles/Neutral/Tank.cs
index 2679e050d..7279523ce 100644
--- a/Roles/Neutral/Tank.cs
+++ b/Roles/Neutral/Tank.cs
@@ -22,7 +22,7 @@ namespace EHR.Neutral
public override void SetupCustomOption()
{
- StartSetup(646950, TabGroup.NeutralRoles, CustomRoles.Tank)
+ StartSetup(646950)
.AutoSetupOption(ref Speed, 0.7f, new FloatValueRule(0.1f, 3f, 0.1f), OptionFormat.Multiplier)
.AutoSetupOption(ref CanBeGuessed, false)
.AutoSetupOption(ref CanBeKilled, true)
diff --git a/Roles/Neutral/Technician.cs b/Roles/Neutral/Technician.cs
index c753c231e..97f0f75cd 100644
--- a/Roles/Neutral/Technician.cs
+++ b/Roles/Neutral/Technician.cs
@@ -13,17 +13,16 @@ namespace EHR.Neutral
private static OptionItem VentCooldown;
private static OptionItem MaxInVentTime;
private static readonly Dictionary PointGains = [];
+
private bool fixedSabotage;
-
public bool IsWon;
-
private PlayerControl TechnicianPC;
public override bool IsEnable => On;
public override void SetupCustomOption()
{
- StartSetup(646950, TabGroup.NeutralRoles, CustomRoles.Technician)
+ StartSetup(646450)
.AutoSetupOption(ref WinsAlone, false)
.AutoSetupOption(ref RequiredPoints, 5, new IntegerValueRule(1, 50, 1))
.AutoSetupOption(ref CanVent, false)
diff --git a/Roles/RoleBase.cs b/Roles/RoleBase.cs
index 10e2287aa..08d232076 100644
--- a/Roles/RoleBase.cs
+++ b/Roles/RoleBase.cs
@@ -194,8 +194,14 @@ namespace EHR
}
// Option setup simplifier
- protected static OptionSetupHandler StartSetup(int id, TabGroup tab, CustomRoles role)
+ protected OptionSetupHandler StartSetup(int id)
{
+ var role = Enum.Parse(this.GetType().Name, true);
+ var tab = TabGroup.OtherRoles;
+ if (role.IsImpostor()) tab = TabGroup.ImpostorRoles;
+ else if (role.IsNeutral(check: true)) tab = TabGroup.NeutralRoles;
+ else if (role.IsCrewmate()) tab = TabGroup.CrewmateRoles;
+
Options.SetupRoleOptions(id++, tab, role);
return new(++id, tab, role);
}