diff --git a/EHR.csproj b/EHR.csproj
index 6fd4fe060..3c8da82eb 100644
--- a/EHR.csproj
+++ b/EHR.csproj
@@ -7,7 +7,7 @@
EHR
Gurge44
preview
- D:\AmongUs
+ C:\Program Files\Epic Games\AmongUs
true
True
$(MSBuildProjectName)
diff --git a/Modules/AntiBlackout.cs b/Modules/AntiBlackout.cs
index e3fbb63a4..bf19807cf 100644
--- a/Modules/AntiBlackout.cs
+++ b/Modules/AntiBlackout.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Runtime.CompilerServices;
using AmongUs.GameOptions;
using EHR.Modules;
@@ -53,12 +54,12 @@ public static class AntiBlackout
if (Options.CurrentGameMode is CustomGameMode.Speedrun or CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.CaptureTheFlag or CustomGameMode.KingOfTheZones)
{
- sender.RpcSetRole(seer, RoleTypes.Impostor, seer.OwnerId, noRpcForSelf: true);
+ sender.RpcSetRole(seer, RoleTypes.Impostor, seer.OwnerId, noRpcForSelf: false);
foreach (PlayerControl target in Main.AllPlayerControls)
{
if (target.PlayerId != seer.PlayerId)
- sender.RpcSetRole(target, RoleTypes.Crewmate, seer.OwnerId, noRpcForSelf: true);
+ sender.RpcSetRole(target, RoleTypes.Crewmate, seer.OwnerId, noRpcForSelf: false);
}
hasValue = true;
@@ -72,9 +73,9 @@ public static class AntiBlackout
foreach (PlayerControl target in Main.AllPlayerControls)
{
if (seerIsAliveAndHasKillButton)
- sender.RpcSetRole(target, target.PlayerId != seer.PlayerId ? RoleTypes.Crewmate : selfRoleType, seer.GetClientId(), noRpcForSelf: true);
+ sender.RpcSetRole(target, target.PlayerId != seer.PlayerId ? RoleTypes.Crewmate : selfRoleType, seer.GetClientId(), noRpcForSelf: false);
else
- sender.RpcSetRole(target, target.PlayerId == dummyImp.PlayerId ? RoleTypes.Impostor : RoleTypes.Crewmate, seer.GetClientId(), noRpcForSelf: true);
+ sender.RpcSetRole(target, target.PlayerId == dummyImp.PlayerId ? RoleTypes.Impostor : RoleTypes.Crewmate, seer.GetClientId(), noRpcForSelf: false);
hasValue = true;
RestartMessageIfTooLong();
@@ -186,9 +187,9 @@ public static class AntiBlackout
{
if (CustomWinnerHolder.WinnerTeam != CustomWinner.Default) return;
- var hasValue = false;
-
- var sender = CustomRpcSender.Create("AntiBlackout.SetRealPlayerRoles", SendOption.Reliable);
+ byte[] keys = Main.AllPlayerControls.Select(x => x.PlayerId).Concat(Main.PlayerStates.Keys).Append(byte.MaxValue).ToArray();
+ Dictionary senders = keys.ToDictionary(x => x, _ => CustomRpcSender.Create("AntiBlackout.SetRealPlayerRoles", SendOption.Reliable));
+ Dictionary hasValue = keys.ToDictionary(x => x, _ => false);
List selfExiled = [];
@@ -230,28 +231,32 @@ public static class AntiBlackout
continue;
}
- sender.RpcSetRole(target, changedRoleType, seer.OwnerId, noRpcForSelf: true);
- hasValue = true;
- RestartMessageIfTooLong();
+ senders[seer.PlayerId].RpcSetRole(target, changedRoleType, seer.OwnerId, noRpcForSelf: false);
+ hasValue[seer.PlayerId] = true;
+ RestartMessageIfTooLong(seer.PlayerId);
}
foreach (PlayerControl pc in selfExiled)
{
+ CustomRpcSender sender = senders[byte.MaxValue];
sender.AutoStartRpc(pc.NetId, (byte)RpcCalls.Exiled);
sender.EndRpc();
- hasValue = true;
+ hasValue[byte.MaxValue] = true;
if (!pc.IsModdedClient() && pc.PlayerId == ExileControllerWrapUpPatch.LastExiled?.PlayerId)
{
+ sender = senders[pc.PlayerId];
sender.AutoStartRpc(pc.NetId, (byte)RpcCalls.MurderPlayer, pc.OwnerId);
sender.WriteNetObject(pc);
sender.Write((int)MurderResultFlags.Succeeded);
sender.EndRpc();
+
+ hasValue[pc.PlayerId] = true;
pc.ReactorFlash(0.2f);
}
- RestartMessageIfTooLong();
+ RestartMessageIfTooLong(byte.MaxValue, pc.PlayerId);
}
break;
@@ -265,25 +270,28 @@ public static class AntiBlackout
{
foreach (PlayerControl pc in Main.AllPlayerControls)
{
- sender.RpcSetRole(pc, RoleTypes.Crewmate, pc.OwnerId, noRpcForSelf: true);
- RestartMessageIfTooLong();
+ senders[pc.PlayerId].RpcSetRole(pc, RoleTypes.Crewmate, pc.OwnerId, noRpcForSelf: false);
+ hasValue[pc.PlayerId] = true;
+ RestartMessageIfTooLong(pc.PlayerId);
if (pc.IsModdedClient()) continue;
if (!pc.IsAlive())
{
+ CustomRpcSender sender = senders[pc.PlayerId];
sender.AutoStartRpc(pc.NetId, (byte)RpcCalls.MurderPlayer, pc.OwnerId);
sender.WriteNetObject(pc);
sender.Write((int)MurderResultFlags.Succeeded);
sender.EndRpc();
+
+ hasValue[pc.PlayerId] = true;
pc.ReactorFlash(0.2f);
}
- RestartMessageIfTooLong();
+ RestartMessageIfTooLong(pc.PlayerId);
}
- hasValue = true;
break;
}
case CustomGameMode.FFA:
@@ -291,13 +299,19 @@ public static class AntiBlackout
case CustomGameMode.CaptureTheFlag:
case CustomGameMode.KingOfTheZones:
{
- sender.RpcSetRole(PlayerControl.LocalPlayer, RoleTypes.Crewmate, noRpcForSelf: true);
- RestartMessageIfTooLong();
+ senders[byte.MaxValue].RpcSetRole(PlayerControl.LocalPlayer, RoleTypes.Crewmate, noRpcForSelf: false);
+ hasValue[byte.MaxValue] = true;
+ RestartMessageIfTooLong(byte.MaxValue);
foreach (PlayerControl pc in Main.AllPlayerControls)
{
+ CustomRpcSender sender = senders[pc.PlayerId];
+
if (pc.IsAlive())
- sender.RpcSetRole(pc, RoleTypes.Impostor, pc.OwnerId, noRpcForSelf: true);
+ {
+ sender.RpcSetRole(pc, RoleTypes.Impostor, pc.OwnerId, noRpcForSelf: false);
+ hasValue[pc.PlayerId] = true;
+ }
else
{
if (pc.IsModdedClient()) continue;
@@ -305,37 +319,45 @@ public static class AntiBlackout
sender.WriteNetObject(pc);
sender.Write((int)MurderResultFlags.Succeeded);
sender.EndRpc();
+
+ hasValue[pc.PlayerId] = true;
pc.ReactorFlash(0.2f);
}
- RestartMessageIfTooLong();
+ RestartMessageIfTooLong(pc.PlayerId);
}
- hasValue = true;
break;
}
}
foreach (PlayerControl pc in Main.AllPlayerControls)
{
- hasValue |= sender.RpcResetAbilityCooldown(pc);
- hasValue |= sender.SetKillCooldown(pc);
- RestartMessageIfTooLong();
+ CustomRpcSender sender = senders[pc.PlayerId];
+ hasValue[pc.PlayerId] |= sender.RpcResetAbilityCooldown(pc);
+ hasValue[pc.PlayerId] |= sender.SetKillCooldown(pc);
+ RestartMessageIfTooLong(pc.PlayerId);
}
- sender.SendMessage(!hasValue);
+ senders.Do(x => x.Value.SendMessage(dispose: !hasValue[x.Key]));
return;
- void RestartMessageIfTooLong()
+ void RestartMessageIfTooLong(params List ids)
{
- if (sender.stream.Length > 800)
+ foreach (var kvp in senders)
{
- sender.SendMessage();
- sender = CustomRpcSender.Create("AntiBlackout.SetRealPlayerRoles", SendOption.Reliable);
- hasValue = false;
+ if (!ids.Contains(kvp.Key)) continue;
+
+ if (kvp.Value.stream.Length > 800)
+ {
+ kvp.Value.SendMessage();
+ hasValue[kvp.Key] = false;
+ }
}
+
+ ids.ForEach(x => senders[x] = CustomRpcSender.Create("AntiBlackout.SetRealPlayerRoles", SendOption.Reliable));
}
}
diff --git a/Modules/Utils.cs b/Modules/Utils.cs
index ba084f294..4efca35fe 100644
--- a/Modules/Utils.cs
+++ b/Modules/Utils.cs
@@ -1053,7 +1053,9 @@ public static class Utils
switch (Options.CurrentGameMode)
{
case CustomGameMode.MoveAndStop: return GetTaskCount(playerId, comms, AmongUsClient.Instance.AmHost);
- case CustomGameMode.Speedrun: return string.Empty;
+ case CustomGameMode.Speedrun:
+ case CustomGameMode.Standard when Forger.Forges.ContainsKey(playerId) && Main.PlayerStates.TryGetValue(playerId, out var state) && state.IsDead:
+ return string.Empty;
}
StringBuilder progressText = new();
@@ -1072,7 +1074,8 @@ public static class Utils
progressText.Append($" <#00ffa5>{totalCompleted}<#ffffff>/{GameData.Instance.TotalTasks}");
}
- if (progressText.Length != 0 && !progressText.ToString().RemoveHtmlTags().StartsWith(' ')) progressText.Insert(0, ' ');
+ if (progressText.Length != 0 && !progressText.ToString().RemoveHtmlTags().StartsWith(' '))
+ progressText.Insert(0, ' ');
return progressText.ToString();
}
@@ -2345,8 +2348,6 @@ public static class Utils
if (!GameStates.IsLobby)
{
- if (AntiBlackout.SkipTasks && seer.IsAlive()) SelfSuffix.AppendLine(GetString("AntiBlackoutSkipTasks"));
-
if (!CustomGameMode.Standard.IsActiveOrIntegrated()) goto GameMode0;
SelfMark.Append(Snitch.GetWarningArrow(seer));
diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs
index 47e590e65..7029ae837 100644
--- a/Patches/ChatCommandPatch.cs
+++ b/Patches/ChatCommandPatch.cs
@@ -417,9 +417,10 @@ internal static class ChatCommands
if (!player.IsLocalPlayer()) ChatManager.SendPreviousMessagesToAll();
- Forger.Forges[targetId] = forgeRole;
-
player.RpcRemoveAbilityUse();
+
+ Forger.Forges[targetId] = forgeRole;
+ Utils.SendMessage("\n", player.PlayerId, string.Format(GetString("ForgeSuccess"), (int)Math.Round(player.GetAbilityUseLimit(), 1), targetId.ColoredPlayerName(), forgeRole.ToColoredString()));
}
private static void ChemistInfoCommand(PlayerControl player, string text, string[] args)
diff --git a/Patches/ClientPatch.cs b/Patches/ClientPatch.cs
index 0634a0609..64560a831 100644
--- a/Patches/ClientPatch.cs
+++ b/Patches/ClientPatch.cs
@@ -283,9 +283,8 @@ public static class InnerNetClientPatch
{
if (playerinfo.IsIncomplete)
{
- Logger.Info($"Disconnecting Client [{client.Id}]{client.PlayerName} {client.FriendCode} for playerinfo timeout", "DelayedNetworkedData");
- AmongUsClient.Instance.SendLateRejection(client.Id, DisconnectReasons.ClientTimeout);
- __instance.OnPlayerLeft(client, DisconnectReasons.ClientTimeout);
+ Logger.Warn($"Disconnecting Client [{client.Id}]{client.PlayerName} {client.FriendCode} for playerinfo timeout", "DelayedNetworkedData");
+ AmongUsClient.Instance.KickPlayer(client.Id, false);
}
}
}
@@ -304,8 +303,7 @@ public static class InnerNetClientPatch
if (player.Data == null || player.Data.IsIncomplete)
{
Logger.Info($"Disconnecting Client [{client.Id}]{client.PlayerName} {client.FriendCode} for playercontrol timeout", "DelayedNetworkedData");
- AmongUsClient.Instance.SendLateRejection(client.Id, DisconnectReasons.ClientTimeout);
- __instance.OnPlayerLeft(client, DisconnectReasons.ClientTimeout);
+ AmongUsClient.Instance.KickPlayer(client.Id, false);
}
}
}
diff --git a/Patches/PhantomRolePatch.cs b/Patches/PhantomRolePatch.cs
index df073ee64..e05016a52 100644
--- a/Patches/PhantomRolePatch.cs
+++ b/Patches/PhantomRolePatch.cs
@@ -96,7 +96,7 @@ public static class PhantomRolePatch
foreach (PlayerControl target in Main.AllPlayerControls)
{
- if (!target.IsAlive() || phantom == target || target.AmOwner || !target.HasDesyncRole()) continue;
+ if (!target.IsAlive() || phantom.PlayerId == target.PlayerId || target.AmOwner || !target.HasDesyncRole()) continue;
int clientId = target.GetClientId();
@@ -107,10 +107,7 @@ public static class PhantomRolePatch
}
else
{
- writer.AutoStartRpc(phantom.NetId, (byte)RpcCalls.SetRole, clientId);
- writer.Write((ushort)RoleTypes.Phantom);
- writer.Write(true);
- writer.EndRpc();
+ writer.RpcSetRole(phantom, RoleTypes.Phantom, clientId);
writer.AutoStartRpc(phantom.NetId, (byte)RpcCalls.CheckVanish, clientId);
writer.Write(0); // not used, lol
@@ -129,7 +126,7 @@ public static class PhantomRolePatch
foreach (PlayerControl target in Main.AllPlayerControls)
{
- if (GameStates.IsMeeting || phantom == null) return;
+ if (GameStates.IsMeeting || phantom == null || target.PlayerId == phantom.PlayerId) return;
int clientId = target.GetClientId();
string petId = phantom.Data.DefaultOutfit.PetId;
@@ -198,7 +195,7 @@ public static class PhantomRolePatch
foreach (PlayerControl target in Main.AllPlayerControls)
{
- if (!target.IsAlive() || phantom == target || target.AmOwner || !target.HasDesyncRole()) continue;
+ if (!target.IsAlive() || phantom.PlayerId == target.PlayerId || target.AmOwner || !target.HasDesyncRole()) continue;
sender.RpcSetRole(phantom, RoleTypes.Phantom, target.GetClientId());
hasValue = true;
}
@@ -238,16 +235,16 @@ public static class PhantomRolePatch
foreach (PlayerControl target in Main.AllPlayerControls)
{
- if (GameStates.IsMeeting || phantom == null) return;
+ if (GameStates.IsMeeting || phantom == null || target.PlayerId == phantom.PlayerId) return;
+
+ int clientId = target.GetClientId();
InvisibilityList.Remove(phantom);
- sender.RpcSetRole(phantom, RoleTypes.Scientist, target.GetClientId());
+ sender.RpcSetRole(phantom, RoleTypes.Scientist, clientId);
hasValue = true;
if (PetsList.TryGetValue(phantom.PlayerId, out string petId))
{
- int clientId = target.GetClientId();
-
if (clientId != -1)
{
if (AmongUsClient.Instance.ClientId == clientId)
diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json
index b3028fe4e..ee52e1493 100644
--- a/Resources/Lang/en_US.json
+++ b/Resources/Lang/en_US.json
@@ -2577,6 +2577,7 @@
"XORSameRole": "<#ff0000>X XOR combinations must have 2 different roles.",
"XORAdditionRole": "<#ff0000>X XOR combinations don't support add-ons. Use <#ffff00>/combo for that.",
"XORAdded": "<#00ffa5>✓ XOR combination added successfully.",
+ "ForgeSuccess": "<#00ffa5>✓ Successfully forged {1} as {2}.\\nYou have {0} ability uses left.",
"ThankYouForUsingEHR": "Thank you for using EHR!",
"GMRoles": "【 ★ Roles ★ 】",
"ImpostorRoles": "【 ★ Impostor Roles ★ 】",
diff --git a/Roles/Coven/CovenLeader.cs b/Roles/Coven/CovenLeader.cs
index 6ac5c7696..4bcefaccf 100644
--- a/Roles/Coven/CovenLeader.cs
+++ b/Roles/Coven/CovenLeader.cs
@@ -48,6 +48,16 @@ public class CovenLeader : Coven
protected override void OnReceiveNecronomicon()
{
- CovenLeaderId.GetPlayer().ResetKillCooldown();
+ CovenLeaderId.GetPlayer()?.ResetKillCooldown();
+ }
+
+ public override void AfterMeetingTasks()
+ {
+ if (!HasNecronomicon) return;
+
+ float kcd = Options.CovenLeaderKillCooldown.GetFloat() / 2f;
+
+ if (Main.KillTimers[CovenLeaderId] > kcd)
+ CovenLeaderId.GetPlayer()?.SetKillCooldown(kcd);
}
}
\ No newline at end of file