diff --git a/EHR.sln.DotSettings b/EHR.sln.DotSettings
index 24140ddf2..7e7dcc2e4 100644
--- a/EHR.sln.DotSettings
+++ b/EHR.sln.DotSettings
@@ -36,6 +36,7 @@
FPS
GA
GM
+ HNS
HP
HQ
HUD
diff --git a/Gamemodes/BedWars.cs b/Gamemodes/BedWars.cs
index ee200ed61..7847ced0b 100644
--- a/Gamemodes/BedWars.cs
+++ b/Gamemodes/BedWars.cs
@@ -1734,8 +1734,8 @@ public static class BedWars
if (!vanillaServer)
{
if (lastSelected) finalSb.Append(baseColor);
- finalSb.Append("▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔");
finalSb.AppendLine();
+ finalSb.AppendLine("▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔");
finalSb.Append(Utils.ColorString(Color.white, bottomText));
finalSb.AppendLine("");
}
diff --git a/Main.cs b/Main.cs
index c9d2dde25..a8fda79bd 100644
--- a/Main.cs
+++ b/Main.cs
@@ -260,13 +260,14 @@ public class Main : BasePlugin
// ################# - WARNING!!! - #####################
// Don't use Enumerate(Alive)PlayerControls if it updates every frame or every second
- // Better use CachedAll/AlivePlayerControls, but in Coroutines (Async) functions need use "for (...)" loop
+ // Better use CachedAll/AlivePlayerControls, but in Coroutines (Async) functions, use "for (...)" loop
public static IEnumerable EnumeratePlayerControls()
{
// foreach can throw System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
// if the code waits frames between iterations, so the safest way is to use a for loop backwards
for (int index = PlayerControl.AllPlayerControls.Count - 1; index >= 0; index--)
{
+ if (PlayerControl.AllPlayerControls.Count <= index) continue;
PlayerControl pc = PlayerControl.AllPlayerControls[index];
if (!pc || pc.PlayerId >= 254) continue;
yield return pc;
@@ -276,6 +277,7 @@ public class Main : BasePlugin
{
for (int index = PlayerControl.AllPlayerControls.Count - 1; index >= 0; index--)
{
+ if (PlayerControl.AllPlayerControls.Count <= index) continue;
PlayerControl pc = PlayerControl.AllPlayerControls[index];
if (!pc.IsAliveWithConditions() || pc.PlayerId >= 254) continue;
yield return pc;
@@ -319,7 +321,7 @@ public class Main : BasePlugin
}
// ################# - WARNING!!! - #####################
// Don't use CachedAll/AlivePlayerControls witch "foreach (...)" loop in Coroutines (Async) functions
- // In async functions use a "for (...)" loop or Enumerate(Alive)PlayerControls or CachedAll/AlivePlayerControls witch LINQ functions (Like: ToList() or ToArray())
+ // In async functions use a "for (...)" loop or Enumerate(Alive)PlayerControls or CachedAll(Alive)PlayerControls with LINQ functions (Like: ToList() or ToArray())
// ######################################
public static List CachedAllPlayerControls()
{
diff --git a/Modules/CustomNetObject.cs b/Modules/CustomNetObject.cs
index a39513ecf..1fb2a94fe 100644
--- a/Modules/CustomNetObject.cs
+++ b/Modules/CustomNetObject.cs
@@ -241,7 +241,8 @@ namespace EHR
{
if (!AmongUsClient.Instance.AmHost) return;
- if (SnapToSendFrameCount++ < 5) return;
+ // max 30 calls per second in total
+ if (++SnapToSendFrameCount < Math.Max(10, AllObjects.Count)) return;
SnapToSendFrameCount = 0;
if (AmongUsClient.Instance.AmClient)
diff --git a/Modules/CustomRoleSelector.cs b/Modules/CustomRoleSelector.cs
index 98702794d..cc9e57797 100644
--- a/Modules/CustomRoleSelector.cs
+++ b/Modules/CustomRoleSelector.cs
@@ -248,7 +248,7 @@ internal static class CustomRoleSelector
Dictionary> allRoles = roles.ToDictionary(x => x.Key, x => x.Value.ToList());
- roles.Keys.ToArray().Do(type => ApplySubCategoryLimits(type, subCategoryLimits));
+ roles.Keys.Do(type => ApplySubCategoryLimits(type, subCategoryLimits));
Logger.Msg("===================================================", "PreSelectedRoles");
Logger.Info(string.Join(", ", roles[RoleAssignType.Impostor].Select(x => x.Role.ToString())), "PreSelectedImpostorRoles");
diff --git a/Modules/CustomRpcSender.cs b/Modules/CustomRpcSender.cs
index 1230069d3..4404fad63 100644
--- a/Modules/CustomRpcSender.cs
+++ b/Modules/CustomRpcSender.cs
@@ -36,12 +36,14 @@ public class CustomRpcSender
private readonly OnSendDelegateType onSendDelegate;
public readonly SendOption sendOption;
+ public bool checkLength;
+
// 0~: targetClientId (GameDataTo)
// -1: All players (GameData)
// -2: Not set
private int currentRpcTarget;
- private bool packed;
+ public bool packed;
private State currentState/* = State.BeforeInit*/;
public int messages;
@@ -57,6 +59,7 @@ public class CustomRpcSender
this.log = log;
currentRpcTarget = -2;
packed = false;
+ checkLength = true;
onSendDelegate = () => { };
messages = 0;
@@ -177,7 +180,7 @@ public class CustomRpcSender
return count;
}
- if (1 + GetPackedIntSize(AmongUsClient.Instance.GameId) >= stream.Length)
+ if (3 + GetPackedIntSize(AmongUsClient.Instance.GameId) >= stream.Length)
dispose = true;
else
EndMessage();
@@ -271,7 +274,7 @@ public class CustomRpcSender
throw new InvalidOperationException(errorMsg);
}
- if (stream.Length > 500)
+ if (checkLength && stream.Length > 500)
{
if (currentState == State.InRootPackedMessage)
{
@@ -320,7 +323,7 @@ public class CustomRpcSender
throw new InvalidOperationException(errorMsg);
}
- if (stream.Length > 500)
+ if (checkLength && stream.Length > 500)
{
doneStreams.Add(stream);
stream = MessageWriter.Get(sendOption);
@@ -529,6 +532,63 @@ public class CustomRpcSender
public static class CustomRpcSenderExtensions
{
+ public static void RpcSetName(ref CustomRpcSender sender, PlayerControl player, string name, PlayerControl seer = null)
+ {
+ bool seerIsNull = !seer;
+ int targetClientId = seerIsNull ? -1 : seer.OwnerId;
+
+ name = name.Replace("color=", string.Empty);
+
+ switch (seerIsNull)
+ {
+ case true when Main.LastNotifyNames.Where(x => x.Key.Item1 == player.PlayerId).All(x => x.Value == name):
+ case false when Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] == name:
+ return;
+ case true:
+ Main.EnumeratePlayerControls().Do(x => Main.LastNotifyNames[(player.PlayerId, x.PlayerId)] = name);
+ break;
+ default:
+ Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] = name;
+ break;
+ }
+
+ sender.checkLength = false;
+
+ if (sender.stream.Length + GetSetNameRpcSize(player.NetId, name) > 1100)
+ {
+ bool packed = sender.packed;
+ sender.SendMessage();
+ sender = CustomRpcSender.Create(sender.name, sender.sendOption);
+ if (packed) sender.StartPackedMessage();
+ }
+
+ sender.AutoStartRpc(player.NetId, RpcCalls.SetName, targetClientId)
+ .Write(player.Data.NetId)
+ .Write(name)
+ .Write(false)
+ .EndRpc();
+
+ return;
+
+ static int GetSetNameRpcSize(uint netId, string playerName)
+ {
+ int byteCount = System.Text.Encoding.UTF8.GetByteCount(playerName);
+ return 3 + PackedUIntSize(netId) + 1 + 4 + PackedUIntSize((uint)byteCount) + byteCount + 1;
+ }
+
+ static int PackedUIntSize(uint value)
+ {
+ return value switch
+ {
+ < 0x80 => 1,
+ < 0x4000 => 2,
+ < 0x20_0000 => 3,
+ < 0x1000_0000 => 4,
+ _ => 5
+ };
+ }
+ }
+
extension(CustomRpcSender sender)
{
public bool RpcSetRole(PlayerControl player, RoleTypes role, int targetClientId = -1, bool noRpcForSelf = true, bool changeRoleMap = false)
@@ -568,33 +628,6 @@ public static class CustomRpcSenderExtensions
return true;
}
- public void RpcSetName(PlayerControl player, string name, PlayerControl seer = null)
- {
- bool seerIsNull = !seer;
- int targetClientId = seerIsNull ? -1 : seer.OwnerId;
-
- name = name.Replace("color=", string.Empty);
-
- switch (seerIsNull)
- {
- case true when Main.LastNotifyNames.Where(x => x.Key.Item1 == player.PlayerId).All(x => x.Value == name):
- case false when Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] == name:
- return;
- case true:
- Main.EnumeratePlayerControls().Do(x => Main.LastNotifyNames[(player.PlayerId, x.PlayerId)] = name);
- break;
- default:
- Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] = name;
- break;
- }
-
- sender.AutoStartRpc(player.NetId, RpcCalls.SetName, targetClientId)
- .Write(player.Data.NetId)
- .Write(name)
- .Write(false)
- .EndRpc();
- }
-
public bool RpcGuardAndKill(PlayerControl killer, PlayerControl target = null, bool forObserver = false, bool fromSetKCD = false)
{
if (!AmongUsClient.Instance.AmHost)
diff --git a/Modules/CustomSounds.cs b/Modules/CustomSounds.cs
index 6047ad4a1..5f6af1bcd 100644
--- a/Modules/CustomSounds.cs
+++ b/Modules/CustomSounds.cs
@@ -12,18 +12,24 @@ public static class CustomSoundsManager
{
private static readonly string SoundsPath = $"{Environment.CurrentDirectory.Replace(@"\", "/")}/BepInEx/resources/";
+ public static long LastSoundRPCTS;
+
public static void RPCPlayCustomSound(this PlayerControl pc, string sound, float volume = 1f, float pitch = 1f, bool force = false)
{
try
{
- if (!force && (!AmongUsClient.Instance.AmHost || !pc.IsModdedClient())) return;
-
if (!pc || pc.AmOwner)
{
Play(sound, volume, pitch);
return;
}
+ if (!force && (!AmongUsClient.Instance.AmHost || !pc.IsModdedClient())) return;
+
+ long now = Utils.TimeStamp;
+ if (now == LastSoundRPCTS) return;
+ LastSoundRPCTS = now;
+
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.PlayCustomSound, SendOption.None, pc.OwnerId);
writer.Write(sound);
writer.Write(volume);
@@ -40,6 +46,10 @@ public static class CustomSoundsManager
if (!AmongUsClient.Instance.AmHost) return;
Play(sound, volume, pitch);
+
+ long now = Utils.TimeStamp;
+ if (now == LastSoundRPCTS) return;
+ LastSoundRPCTS = now;
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.PlayCustomSound, SendOption.None);
writer.Write(sound);
diff --git a/Modules/CustomTeamManager.cs b/Modules/CustomTeamManager.cs
index 3f6699163..5a5105d2c 100644
--- a/Modules/CustomTeamManager.cs
+++ b/Modules/CustomTeamManager.cs
@@ -14,6 +14,7 @@ internal static class CustomTeamManager
public static CustomTeam WinnerTeam;
public static Dictionary> CustomTeamPlayerIds = [];
public static List CustomTeamOptions = [];
+ public static Dictionary> CustomTeamOptionsCache = [];
public static void LoadCustomTeams()
{
@@ -72,7 +73,16 @@ internal static class CustomTeamManager
private static void UpdateEnabledTeams()
{
- EnabledCustomTeams = CustomTeamOptions.Where(x => x.Enabled.GetBool()).Select(x => x.Team).ToHashSet();
+ EnabledCustomTeams.Clear();
+ CustomTeamOptionsCache.Clear();
+
+ foreach (CustomTeamOptionGroup customTeamOptionGroup in CustomTeamOptions)
+ {
+ if (customTeamOptionGroup.Enabled.GetBool())
+ EnabledCustomTeams.Add(customTeamOptionGroup.Team);
+
+ CustomTeamOptionsCache[customTeamOptionGroup.Team] = customTeamOptionGroup.AllOptions.ConvertAll(x => x.GetBool());
+ }
}
public static void InitializeCustomTeamPlayers()
@@ -127,6 +137,44 @@ internal static class CustomTeamManager
});
}
+ if (Utils.DoRPC)
+ {
+ var msg = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.CTA, SendOption.Reliable);
+ msg.WritePacked(1);
+ msg.WritePacked(EnabledCustomTeams.Count);
+ EnabledCustomTeams.Do(x => x.Serialize(msg));
+ AmongUsClient.Instance.FinishRpcImmediately(msg);
+ msg.Recycle();
+
+ msg = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.CTA, SendOption.Reliable);
+ msg.WritePacked(2);
+ msg.WritePacked(CustomTeamPlayerIds.Count);
+
+ foreach ((CustomTeam customTeam, HashSet ids) in CustomTeamPlayerIds)
+ {
+ msg.WritePacked(ids.Count);
+ ids.Do(x => msg.Write(x));
+ msg.Write(customTeam.TeamName);
+ }
+
+ AmongUsClient.Instance.FinishRpcImmediately(msg);
+ msg.Recycle();
+
+ msg = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.CTA, SendOption.Reliable);
+ msg.WritePacked(3);
+ msg.WritePacked(CustomTeamOptionsCache.Count);
+
+ foreach ((CustomTeam customTeam, List bools) in CustomTeamOptionsCache)
+ {
+ msg.WritePacked(bools.Count);
+ bools.ForEach(x => msg.Write(x));
+ msg.Write(customTeam.TeamName);
+ }
+
+ AmongUsClient.Instance.FinishRpcImmediately(msg);
+ msg.Recycle();
+ }
+
return;
static HashSet TakeAsManyAsSet(IEnumerable playerIds, CustomTeam customTeam)
@@ -169,7 +217,44 @@ internal static class CustomTeamManager
return CustomTeamPlayerIds[GetCustomTeam(seer.PlayerId)].Aggregate(string.Empty, (s, id) => s + Utils.ColorString(Main.PlayerColors.GetValueOrDefault(id, Color.white), TargetArrow.GetArrows(seer, id)));
}
- private static readonly Dictionary> AliveTeamPlayers = [];
+ public static void ReceiveRPC(MessageReader reader)
+ {
+ switch (reader.ReadPackedInt32())
+ {
+ case 1:
+ {
+ EnabledCustomTeams.Clear();
+ Loop.Times(reader.ReadPackedInt32(), _ => EnabledCustomTeams.Add(new CustomTeam(reader.ReadString())));
+ break;
+ }
+ case 2:
+ {
+ CustomTeamPlayerIds.Clear();
+ Loop.Times(reader.ReadPackedInt32(), _ =>
+ {
+ HashSet ids = [];
+ Loop.Times(reader.ReadPackedInt32(), _ => ids.Add(reader.ReadByte()));
+ string teamName = reader.ReadString();
+ CustomTeamPlayerIds[EnabledCustomTeams.First(x => x.TeamName == teamName)] = ids;
+ });
+ break;
+ }
+ case 3:
+ {
+ CustomTeamOptionsCache.Clear();
+ Loop.Times(reader.ReadPackedInt32(), _ =>
+ {
+ List bools = [];
+ Loop.Times(reader.ReadPackedInt32(), _ => bools.Add(reader.ReadBoolean()));
+ string teamName = reader.ReadString();
+ CustomTeamOptionsCache[EnabledCustomTeams.First(x => x.TeamName == teamName)] = bools;
+ });
+ break;
+ }
+ }
+ }
+
+ private static readonly List AliveTeamPlayers = [];
private static readonly List ToRemove = [];
public static bool CheckCustomTeamGameEnd()
{
@@ -210,27 +295,20 @@ internal static class CustomTeamManager
AliveTeamPlayers.Clear();
foreach ((CustomTeam teamKey, HashSet originalSet) in CustomTeamPlayerIds)
{
- HashSet aliveSet = null;
foreach (var id in originalSet)
{
var pc = Utils.GetPlayerById(id);
- if (pc.IsAlive())
+ if (pc && pc.IsAlive())
{
- aliveSet ??= [];
- aliveSet.Add(id);
+ AliveTeamPlayers.Add(teamKey);
+ break;
}
}
- if (aliveSet != null)
- AliveTeamPlayers[teamKey] = aliveSet;
}
if (AliveTeamPlayers.Count == 1)
{
- var aliveTeam = AliveTeamPlayers.GetEnumerator();
- aliveTeam.MoveNext();
-
- var onlyTeam = aliveTeam.Current.Key;
- var winners = aliveTeam.Current.Value;
+ var onlyTeam = AliveTeamPlayers[0];
for (int i = 0; i < aapc.Count; i++)
{
@@ -243,7 +321,7 @@ internal static class CustomTeamManager
WinnerTeam = onlyTeam;
CustomWinnerHolder.SetWinnerOrAdditonalWinner(CustomWinner.CustomTeam);
- CustomWinnerHolder.WinnerIds = winners;
+ CustomWinnerHolder.WinnerIds = CustomTeamPlayerIds[onlyTeam];
return true;
}
}
@@ -287,9 +365,7 @@ internal static class CustomTeamManager
public static bool IsSettingEnabledForTeam(CustomTeam team, CTAOption setting)
{
- CustomTeamOptionGroup optionsGroup = CustomTeamOptions.First(x => x.Team.Equals(team));
- List values = optionsGroup.AllOptions.ConvertAll(x => x.GetBool());
- return values[(int)setting];
+ return CustomTeamOptionsCache[team][(int)setting];
}
internal class CustomTeam
@@ -330,6 +406,27 @@ internal static class CustomTeamManager
{
return TeamName.GetHashCode();
}
+
+ public void Serialize(MessageWriter writer)
+ {
+ StringBuilder sb = new();
+ sb.Append(TeamName);
+ sb.Append(';');
+ sb.Append(RoleRevealScreenTitle);
+ sb.Append(';');
+ sb.Append(RoleRevealScreenSubtitle);
+ sb.Append(';');
+ sb.Append(RoleRevealScreenBackgroundColor);
+ sb.Append(';');
+
+ for (var index = 0; index < TeamMembers.Count; index++)
+ {
+ sb.Append(TeamMembers[index].ToString());
+ sb.Append(',');
+ }
+
+ writer.Write(sb.ToString());
+ }
}
internal class CustomTeamOptionGroup
diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs
index 5b6bb920f..f697ecea0 100644
--- a/Modules/ExtendedPlayerControl.cs
+++ b/Modules/ExtendedPlayerControl.cs
@@ -10,6 +10,7 @@ using EHR.Modules;
using EHR.Modules.Extensions;
using EHR.Roles;
using Hazel;
+using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using InnerNet;
using UnityEngine;
@@ -509,9 +510,11 @@ internal static class ExtendedPlayerControl
public void RpcChangeRoleBasis(CustomRoles newCustomRole, bool loggerRoleMap = false, bool forced = false)
{
+ if (!AmongUsClient.Instance.AmHost) return;
+
if (!forced)
{
- if (!AmongUsClient.Instance.AmHost || !GameStates.IsInGame || !player || !player.IsAlive()) return;
+ if (!GameStates.IsInGame || !player || !player.IsAlive()) return;
if (AntiBlackout.SkipTasks || ExileController.Instance)
{
@@ -1299,12 +1302,16 @@ internal static class ExtendedPlayerControl
{
try
{
- string name = isMeeting ? player.Data.PlayerName : player.name;
- return name.RemoveHtmlTags();
+ return (isMeeting ? player.Data.PlayerName : player.name).RemoveHtmlTags();
}
catch (NullReferenceException nullReferenceException)
{
- Logger.Error($"{nullReferenceException.Message} - player is null? {!player}", "GetRealName");
+ Logger.Warn($"{nullReferenceException.Message} - player is null? {!player}", "GetRealName");
+ return string.Empty;
+ }
+ catch (Il2CppException il2CppException)
+ {
+ Logger.Warn($"{il2CppException.Message} - player is null? {!player}", "GetRealName");
return string.Empty;
}
catch (Exception exception)
diff --git a/Modules/Extensions/CollectionExtensions.cs b/Modules/Extensions/CollectionExtensions.cs
index 85db2d463..1be5243f6 100644
--- a/Modules/Extensions/CollectionExtensions.cs
+++ b/Modules/Extensions/CollectionExtensions.cs
@@ -34,6 +34,11 @@ public static class CollectionExtensions
return default(TKey);
}
+
+ // foreach will only throw System.InvalidOperationException: collection was modified; enumeration operation may not execute.
+ // if the number of elements inside the dictionary changes.
+ // If Count stays the same for the entire iteration, it can be safely enumerated without ToList or ToArray.
+ // Changing values does NOT modify the collection!
///
/// Sets the value for all existing keys in a dictionary to a specific value
@@ -41,7 +46,7 @@ public static class CollectionExtensions
///
public void SetAllValues(TValue value)
{
- foreach (TKey key in dictionary.Keys.ToArray())
+ foreach (TKey key in dictionary.Keys)
dictionary[key] = value;
}
@@ -51,7 +56,7 @@ public static class CollectionExtensions
/// The function to adjust the values with
public void AdjustAllValues(Func adjust)
{
- foreach (TKey key in dictionary.Keys.ToArray())
+ foreach (TKey key in dictionary.Keys)
dictionary[key] = adjust(dictionary[key]);
}
diff --git a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs
index f32bbebac..b6028773f 100644
--- a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs
+++ b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs
@@ -53,7 +53,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
}
}
- public static void SendAllImmediately()
+ public static DataFlagRateLimiter.QueuedAction SendAllImmediately()
{
ForceWaitFrame = true;
@@ -83,20 +83,22 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
sender.IsDirty = false;
}
}
+
+ DataFlagRateLimiter.QueuedAction qa = null;
if (PackedWriter != null)
{
if (PackedWriterMessages > 0)
{
PackedWriter.EndMessage();
+ Logger.Info($"PackedWriter flush queued - Length: {PackedWriter.Length}, Messages: {PackedWriterMessages}", "SendAllImmediately");
var capturedWriter = PackedWriter;
- DataFlagRateLimiter.Enqueue(() =>
+ qa = DataFlagRateLimiter.Enqueue(() =>
{
AmongUsClient.Instance.SendOrDisconnect(capturedWriter);
capturedWriter.Recycle();
Logger.Info("PackedWriter flush queue finished and sent", "SendAllImmediately");
}, cleanup: capturedWriter.Recycle);
- Logger.Info($"PackedWriter flush queued - Length: {PackedWriter.Length}, Messages: {PackedWriterMessages}", "SendAllImmediately");
}
else
{
@@ -106,6 +108,8 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
PackedWriter = null;
PackedWriterMessages = 0;
+
+ return qa;
}
public static void SetDirtyToAll()
@@ -292,6 +296,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
if (PackedWriter.Length > 1000 || PackedWriterMessages >= AmongUsClient.Instance.GetMaxMessagePackingLimit())
{
PackedWriter.EndMessage();
+ Logger.Info($"PackedWriter flush queued - Length: {PackedWriter.Length}, Messages: {PackedWriterMessages}", "SendOptionsArray");
var capturedWriter = PackedWriter;
DataFlagRateLimiter.Enqueue(() =>
{
@@ -299,7 +304,6 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
capturedWriter.Recycle();
Logger.Info("PackedWriter flush queue finished and sent", "SendOptionsArray");
}, cleanup: capturedWriter.Recycle);
- Logger.Info($"PackedWriter flush queued - Length: {PackedWriter.Length}, Messages: {PackedWriterMessages}", "SendOptionsArray");
PackedWriterMessages = 0;
PackedWriter = MessageWriter.Get(SendOption.Reliable);
PackedWriter.StartMessage(26);
diff --git a/Modules/LobbySharingAPI.cs b/Modules/LobbySharingAPI.cs
index 408e9a1b7..3b32b9783 100644
--- a/Modules/LobbySharingAPI.cs
+++ b/Modules/LobbySharingAPI.cs
@@ -161,6 +161,7 @@ internal static class ExitGamePatch
Logger.Msg($"Exiting game - reason: {reason}", "ExitGamePatch.Prefix");
GameStates.InGame = false;
+ GameEndChecker.LoadingEndScreen = false;
Main.RealOptionsData?.Restore(GameOptionsManager.Instance.CurrentGameOptions);
DataFlagRateLimiter.DropQueue();
diff --git a/Modules/RPC.cs b/Modules/RPC.cs
index 05b350cb6..4af9e413e 100644
--- a/Modules/RPC.cs
+++ b/Modules/RPC.cs
@@ -163,6 +163,7 @@ public enum CustomRPC
Necronomicon,
Stained,
Entombed,
+ CTA,
// Game Modes
RoomRushDataSync,
@@ -713,7 +714,7 @@ internal static class RPCHandlerPatch
{
byte bountyId = reader.ReadByte();
byte targetId = reader.ReadByte();
- (Main.PlayerStates[bountyId].Role as BountyHunter)?.ReceiveRPC(targetId);
+ (Main.PlayerStates[bountyId].Role as BountyHunter)?.ReceiveRPCTarget(targetId);
break;
}
case CustomRPC.SyncBargainer:
@@ -824,7 +825,7 @@ internal static class RPCHandlerPatch
}
case CustomRPC.SyncRabbit:
{
- Rabbit.ReceiveRPC(reader);
+ Rabbit.ReceiveRPCStatic(reader);
break;
}
case CustomRPC.SyncYinYanger:
@@ -961,12 +962,12 @@ internal static class RPCHandlerPatch
if (operate == 1)
{
byte victim = reader.ReadByte();
- (Main.PlayerStates[id].Role as Penguin)?.ReceiveRPC(victim);
+ (Main.PlayerStates[id].Role as Penguin)?.ReceiveRPCVictim(victim);
}
else
{
- float timer = reader.ReadSingle();
- (Main.PlayerStates[id].Role as Penguin)?.ReceiveRPC(timer);
+ bool reset = reader.ReadBoolean();
+ (Main.PlayerStates[id].Role as Penguin)?.ReceiveRPCTimer(reset);
}
break;
@@ -1374,6 +1375,11 @@ internal static class RPCHandlerPatch
LateTask.New(() => Stained.VioletNameList.Remove(id), 3f);
break;
}
+ case CustomRPC.CTA:
+ {
+ CustomTeamManager.ReceiveRPC(reader);
+ break;
+ }
}
}
catch (Exception e) { Utils.ThrowException(e); }
@@ -1390,7 +1396,7 @@ internal static class RPC
if (targetId != -1)
{
ClientData client = Utils.GetClientById(targetId);
- if (client == null || client.Character == null || !Main.PlayerVersion.ContainsKey(client.Character.PlayerId)) return;
+ if (client == null || !client.Character || !Main.PlayerVersion.ContainsKey(client.Character.PlayerId)) return;
}
if (!AmongUsClient.Instance.AmHost || PlayerControl.AllPlayerControls.Count <= 1) return;
@@ -1417,6 +1423,10 @@ internal static class RPC
public static void PlaySoundRPC(byte playerID, Sounds sound)
{
if (AmongUsClient.Instance.AmHost) PlaySound(playerID, sound);
+
+ long now = Utils.TimeStamp;
+ if (now == CustomSoundsManager.LastSoundRPCTS) return;
+ CustomSoundsManager.LastSoundRPCTS = now;
SendOption sendOption = SendOption.Reliable;
diff --git a/Modules/Utils.cs b/Modules/Utils.cs
index 65d140f39..bb35c1ea5 100644
--- a/Modules/Utils.cs
+++ b/Modules/Utils.cs
@@ -169,11 +169,11 @@ public static class Utils
switch (Vector2.Distance(pc.Pos(), location))
{
- // case < 0.3f:
- // {
- // if (log) Logger.Warn($"Target ({pc.GetNameWithRole().RemoveHtmlTags()}) is too close to the destination - Teleporting canceled", "TP");
- // return false;
- // }
+ case < 0.3f:
+ {
+ if (log) Logger.Warn($"Target ({pc.GetNameWithRole().RemoveHtmlTags()}) is too close to the destination - Teleporting canceled", "TP");
+ return false;
+ }
case < 1.5f when !GameStates.IsLobby:
{
if (log) Logger.Msg($"Target ({pc.GetNameWithRole().RemoveHtmlTags()}) is too close to the destination - Changed to SendOption.None", "TP");
@@ -191,12 +191,18 @@ public static class Utils
nt.SnapTo(location, (ushort)(nt.lastSequenceId + 328));
nt.SetDirtyBit(uint.MaxValue);
- if (NumSnapToCallsThisRound > 80)
+ if (NumSnapToCallsThisRound >= 80)
{
- if (log) Logger.Warn($"Too many SnapTo calls this round ({NumSnapToCallsThisRound}) - Changed to SendOption.None", "TP");
+ if (log) Logger.Warn($"Too many Reliable SnapTo calls this round ({NumSnapToCallsThisRound}) - Changed to SendOption.None", "TP");
sendOption = SendOption.None;
}
+ if (NumSnapToCallsThisRound >= 100)
+ {
+ if (log) Logger.Warn($"Too many Total SnapTo calls this round and this second ({NumSnapToCallsThisRound}) - Canceled", "TP");
+ return false;
+ }
+
if (GameStates.CurrentServerType != GameStates.ServerType.Vanilla)
sendOption = SendOption.Reliable;
@@ -214,7 +220,7 @@ public static class Utils
SubmergedCompatibility.CheckOutOfBoundsElevator(pc);
}
- if (sendOption == SendOption.Reliable) NumSnapToCallsThisRound++;
+ NumSnapToCallsThisRound++;
return true;
}
@@ -2748,13 +2754,17 @@ public static class Utils
if (!AmongUsClient.Instance.AmHost || GameStates.IsMeeting) yield break;
const int frameBudget = 3; // milliseconds per frame
- var stopwatch = new Stopwatch();
+ var stopwatch = Stopwatch.StartNew();
var aapc = Main.CachedAlivePlayerControls();
+ var hasValue = false;
+ var sender = CustomRpcSender.Create("NotifyRoles", sendOption, log: false);
+ sender.StartPackedMessage();
for (int seerIndex = 0; seerIndex < aapc.Count; seerIndex++)
{
PlayerControl seer = aapc[seerIndex];
- NotifyRoles(SpecifySeer: seer, NoCache: noCache, SendOption: sendOption);
+ hasValue |= WriteSetNameRpcsToSender(ref sender, false, noCache, false, false, false, false, seer, [seer], aapc, out bool senderWasCleared, sendOption);
+ if (senderWasCleared) hasValue = false;
if (stopwatch.ElapsedMilliseconds >= frameBudget)
{
@@ -2764,6 +2774,8 @@ public static class Utils
stopwatch.Start();
}
}
+
+ sender.SendMessage(!hasValue || sender.stream.Length <= 11);
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
@@ -2779,24 +2791,18 @@ public static class Utils
SeerList = SpecifySeer ? [SpecifySeer] : apc;
TargetList = SpecifyTarget ? [SpecifyTarget] : apc;
- var sender = CustomRpcSender.Create("NotifyRoles", SendOption, log: false);
var hasValue = false;
+ var sender = CustomRpcSender.Create("NotifyRoles", SendOption, log: false);
+ sender.StartPackedMessage();
for (byte seerIndex = 0; seerIndex < SeerList.Count; seerIndex++)
{
PlayerControl seer = SeerList[seerIndex];
hasValue |= WriteSetNameRpcsToSender(ref sender, ForMeeting, NoCache, ForceLoop, CamouflageIsForMeeting, GuesserIsForMeeting, MushroomMixup, seer, SeerList, TargetList, out bool senderWasCleared, SendOption);
if (senderWasCleared) hasValue = false;
-
- if (sender.stream.Length > 500)
- {
- sender.SendMessage();
- sender = CustomRpcSender.Create("NotifyRoles", SendOption, log: false);
- hasValue = false;
- }
}
- sender.SendMessage(!hasValue || sender.stream.Length <= 3);
+ sender.SendMessage(!hasValue || sender.stream.Length <= 11);
if (Options.CurrentGameMode != CustomGameMode.Standard) return;
@@ -2838,26 +2844,26 @@ public static class Utils
var selfTeamName = $"{iconTextLeft} {ColorString(seerTeam.GetColor(), $"{seerTeam}")} {iconTextRight}\n \n";
selfName = $"{selfTeamName}\r\n{seerRole.ToColoredString()}{roleNameUp}";
- sender.RpcSetName(seer, selfName, seer);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, seer, selfName, seer);
return true;
}
if (seer.Is(CustomRoles.Car) && !forMeeting && !GameStates.IsEnded)
{
- sender.RpcSetName(seer, Car.Name);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, seer, Car.Name);
return true;
}
if (Main.PlayerStates.TryGetValue(seer.PlayerId, out var seerState) && seerState.Role is Tree { TreeSpriteActive: true } && !forMeeting && !GameStates.IsEnded)
{
- sender.RpcSetName(seer, Tree.Sprite);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, seer, Tree.Sprite);
return true;
}
if (forMeeting && Magistrate.CallCourtNextMeeting)
{
selfName = seer.Is(CustomRoles.Magistrate) ? GetString("Magistrate.CourtName") : GetString("Magistrate.JuryName");
- sender.RpcSetName(seer, selfName);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, seer, selfName);
return true;
}
@@ -3183,7 +3189,7 @@ public static class Utils
if (selfName.EndsWith("")) selfName = selfName.Remove(selfName.Length - 7);
if (selfName.EndsWith("")) selfName = selfName.Remove(selfName.Length - 8);
- sender.RpcSetName(seer, selfName, seer);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, seer, selfName, seer);
hasValue = true;
bool onlySelfNameUpdateRequired = Options.CurrentGameMode switch
@@ -3214,24 +3220,24 @@ public static class Utils
if (target.PlayerId == seer.PlayerId) continue;
if ((IsActive(SystemTypes.MushroomMixupSabotage) || mushroomMixup) && !forMeeting && target.IsAlive() && !seer.Is(CustomRoleTypes.Impostor) && seer.HasDesyncRole())
- sender.RpcSetName(target, "", seer);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, target, "", seer);
else
{
if (target.Is(CustomRoles.Car) && !forMeeting && !GameStates.IsEnded)
{
- sender.RpcSetName(target, Car.Name, seer);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, target, Car.Name, seer);
continue;
}
if (Main.PlayerStates.TryGetValue(target.PlayerId, out var targetState) && targetState.Role is Tree { TreeSpriteActive: true } && !forMeeting && !GameStates.IsEnded)
{
- sender.RpcSetName(target, Tree.Sprite, seer);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, target, Tree.Sprite, seer);
continue;
}
if (forMeeting && Magistrate.CallCourtNextMeeting)
{
- sender.RpcSetName(target, GetString(target.Is(CustomRoles.Magistrate) ? "Magistrate.CourtName" : "Magistrate.JuryName"), seer);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, target, GetString(target.Is(CustomRoles.Magistrate) ? "Magistrate.CourtName" : "Magistrate.JuryName"), seer);
return true;
}
@@ -3500,17 +3506,9 @@ public static class Utils
if (targetName.EndsWith("")) targetName = targetName.Remove(targetName.Length - 7);
if (targetName.EndsWith("")) targetName = targetName.Remove(targetName.Length - 8);
- sender.RpcSetName(target, targetName, seer);
+ CustomRpcSenderExtensions.RpcSetName(ref sender, target, targetName, seer);
hasValue = true;
senderWasCleared = false;
-
- if (sender.stream.Length > 500)
- {
- sender.SendMessage();
- sender = CustomRpcSender.Create(sender.name, sender.sendOption);
- hasValue = false;
- senderWasCleared = true;
- }
}
}
catch (Exception ex)
@@ -4153,7 +4151,7 @@ public static class Utils
CustomRpcSender sender = CustomRpcSender.Create("Bloodlust fix", SendOption.Reliable);
bool hasValue = false;
hasValue |= sender.RpcSetRole(pc, RoleTypes.Impostor, pc.OwnerId);
- Main.EnumeratePlayerControls().DoIf(x => x.IsImpostor(), x => hasValue |= sender.RpcSetRole(x, RoleTypes.Crewmate, pc.OwnerId));
+ Main.EnumerateAlivePlayerControls().DoIf(x => x.IsImpostor(), x => hasValue |= sender.RpcSetRole(x, RoleTypes.Crewmate, pc.OwnerId));
sender.SendMessage(dispose: !hasValue);
LateTask.New(() => pc.SetKillCooldown(), 0.2f, log: false);
}
@@ -5171,6 +5169,7 @@ public static class Utils
DataFlagRateLimiter.Enqueue(() =>
{
+ if (!deadBodyParent) return;
CreateDeadBody(position, colorId, deadBodyParent);
PlayerControl playerControl = Object.Instantiate(AmongUsClient.Instance.PlayerPrefab, Vector2.zero, Quaternion.identity);
playerControl.PlayerId = deadBodyParent.PlayerId;
diff --git a/Patches/IntroPatch.cs b/Patches/IntroPatch.cs
index bb7d1229a..721bfb4cf 100644
--- a/Patches/IntroPatch.cs
+++ b/Patches/IntroPatch.cs
@@ -1252,28 +1252,29 @@ internal static class IntroCutsceneDestroyPatch
{
lp.Notify(GetString("GLHF"), 2f);
+ bool hasValue = false;
+ var sender = CustomRpcSender.Create("Shapeshift After Pet Assign On Game Start", SendOption.Reliable);
+ sender.StartPackedMessage();
+
foreach (PlayerControl pc in aapc)
{
- if (pc.IsHost()) continue; // Skip the host
+ if (pc.AmOwner) continue; // Skip the host
try
{
- var sender = CustomRpcSender.Create("Shapeshift After Pet Assign On Game Start", SendOption.Reliable);
-
- if (AmongUsClient.Instance.AmClient)
- pc.Shapeshift(pc, false);
-
- sender.AutoStartRpc(pc.NetId, 46);
+ sender.AutoStartRpc(pc.NetId, 46, pc.OwnerId);
sender.WriteNetObject(pc);
sender.Write(false);
sender.EndRpc();
sender.Notify(pc, GetString("GLHF"), 2f);
- sender.SendMessage();
+ hasValue = true;
}
catch (Exception ex) { Logger.Fatal(ex.ToString(), "IntroPatch.RpcShapeshift"); }
}
+
+ sender.SendMessage(dispose: !hasValue);
}, 4f, "Show Pet For Everyone");
LateTask.New(() => Main.ProcessShapeshifts = true, 2f, "Enable SS Processing");
diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs
index 2b8d6bed6..d77243b4f 100644
--- a/Patches/MeetingHudPatch.cs
+++ b/Patches/MeetingHudPatch.cs
@@ -818,6 +818,7 @@ internal static class MeetingHudStartPatch
{
string playername = pc.GetRealName();
if (Doppelganger.DoppelVictim.TryGetValue(pc.PlayerId, out string value)) playername = value;
+ playername = Main.AllPlayerNames.GetValueOrDefault(pc.PlayerId, playername);
AddMsg(string.Format(GetString("SilencerDead"), playername, pc.PlayerId, CustomRoles.Silencer.ColoredTextByRole(GetString("SilencerKillTitle"))));
}
diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs
index 01d1ddcdd..383801d0a 100644
--- a/Patches/OnGameStartedPatch.cs
+++ b/Patches/OnGameStartedPatch.cs
@@ -377,6 +377,20 @@ internal static class ChangeRoleSettings
{
RPC.SyncCustomSettingsRPC();
}
+ else
+ {
+ CustomTeamManager.CustomTeams.Clear();
+ CustomTeamManager.EnabledCustomTeams.Clear();
+ CustomTeamManager.CustomTeamPlayerIds.Clear();
+ CustomTeamManager.CustomTeamOptionsCache.Clear();
+ StartGameHostPatch.BasisChangingAddons[CustomRoles.Bloodlust] = [];
+ StartGameHostPatch.BasisChangingAddons[CustomRoles.Nimble] = [];
+ StartGameHostPatch.BasisChangingAddons[CustomRoles.Examiner] = [];
+ StartGameHostPatch.BasisChangingAddons[CustomRoles.Finder] = [];
+ StartGameHostPatch.BasisChangingAddons[CustomRoles.Physicist] = [];
+ StartGameHostPatch.BasisChangingAddons[CustomRoles.Noisy] = [];
+ StartGameHostPatch.BasisChangingAddons[CustomRoles.Venom] = [];
+ }
FallFromLadder.Reset();
CustomSabotage.Reset();
diff --git a/Patches/PetActionsPatch.cs b/Patches/PetActionsPatch.cs
index 45bd760fd..fc224ecb7 100644
--- a/Patches/PetActionsPatch.cs
+++ b/Patches/PetActionsPatch.cs
@@ -32,12 +32,11 @@ internal static class LocalPetPatch
AFKDetector.SetNotAFK(__instance.PlayerId);
- if (!LastProcess.ContainsKey(__instance.PlayerId)) LastProcess.TryAdd(__instance.PlayerId, Utils.TimeStamp - 2);
+ LastProcess.TryAdd(__instance.PlayerId, Utils.TimeStamp - 2);
if (LastProcess[__instance.PlayerId] + 1 >= Utils.TimeStamp) return true;
ExternalRpcPetPatch.Prefix(__instance.MyPhysics, (byte)RpcCalls.Pet);
- LastProcess[__instance.PlayerId] = Utils.TimeStamp;
return !Main.CancelPetAnimation.Value || !__instance.GetCustomRole().PetActivatedAbility();
}
@@ -68,6 +67,10 @@ internal static class ExternalRpcPetPatch
AFKDetector.SetNotAFK(pc.PlayerId);
+ LastProcess.TryAdd(pc.PlayerId, Utils.TimeStamp - 2);
+ if (LastProcess[pc.PlayerId] + 1 >= Utils.TimeStamp) return;
+ LastProcess[pc.PlayerId] = Utils.TimeStamp;
+
if (!pc.inVent
&& !pc.inMovingPlat
&& !pc.walkingToVent
@@ -89,11 +92,6 @@ internal static class ExternalRpcPetPatch
}
}
- if (!LastProcess.ContainsKey(pc.PlayerId)) LastProcess.TryAdd(pc.PlayerId, Utils.TimeStamp - 2);
- if (LastProcess[pc.PlayerId] + 1 >= Utils.TimeStamp) return;
-
- LastProcess[pc.PlayerId] = Utils.TimeStamp;
-
Logger.Info($"Player {pc.GetNameWithRole().RemoveHtmlTags()} petted their pet", "PetActionTrigger");
LateTask.New(() => OnPetUse(pc), 0.2f, $"OnPetUse: {pc.GetNameWithRole().RemoveHtmlTags()}", false);
diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs
index b17b22670..09959c184 100644
--- a/Patches/PlayerControlPatch.cs
+++ b/Patches/PlayerControlPatch.cs
@@ -1037,8 +1037,12 @@ internal static class ReportDeadBodyPatch
Logger.Info($"{__instance.GetNameWithRole().RemoveHtmlTags()} => {target?.Object?.GetNameWithRole() ?? "null"}", "ReportDeadBody");
- foreach (KeyValuePair kvp in Main.PlayerStates)
- kvp.Value.LastRoom = GetPlayerById(kvp.Key).GetPlainShipRoom();
+ try
+ {
+ foreach (KeyValuePair kvp in Main.PlayerStates)
+ kvp.Value.LastRoom = GetPlayerById(kvp.Key)?.GetPlainShipRoom();
+ }
+ catch (Exception e) { ThrowException(e); }
if (!AmongUsClient.Instance.AmHost) return true;
@@ -1639,6 +1643,9 @@ internal static class FixedUpdatePatch
if (self)
{
+ if (NumSnapToCallsThisRound > 80)
+ NumSnapToCallsThisRound = 80;
+
Zoom.OnFixedUpdate();
TextBoxPatch.CheckChatOpen();
@@ -2391,26 +2398,29 @@ internal static class GameDataCompleteTaskPatch
public static void Postfix(PlayerControl pc, uint taskId)
{
- if (MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) return;
-
- if (Options.CurrentGameMode == CustomGameMode.HideAndSeek && CustomHnS.PlayerRoles[pc.PlayerId].Interface.Team == Team.Crewmate && pc.IsAlive())
- {
- var task = pc.myTasks.Find((Il2CppSystem.Predicate)(x => taskId == x.Id));
- Hider.OnSpecificTaskComplete(pc, task);
- }
-
try
{
+ if (MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) return;
+
+ if (Options.CurrentGameMode == CustomGameMode.HideAndSeek && CustomHnS.PlayerRoles[pc.PlayerId].Interface.Team == Team.Crewmate && pc.IsAlive())
+ {
+ var task = pc.myTasks.Find((Il2CppSystem.Predicate)(x => taskId == x.Id));
+ Hider.OnSpecificTaskComplete(pc, task);
+ }
+
+
if (pc.IsAlive())
{
var task = pc.myTasks.Find((Il2CppSystem.Predicate)(x => taskId == x.Id));
Benefactor.OnTaskComplete(pc, task);
}
+
+ Logger.Info($"TaskComplete: {pc.GetNameWithRole().RemoveHtmlTags()}", "CompleteTask");
}
catch (Exception e) { ThrowException(e); }
-
- Logger.Info($"TaskComplete: {pc.GetNameWithRole().RemoveHtmlTags()}", "CompleteTask");
+
Main.PlayerStates[pc.PlayerId].UpdateTask(pc);
+
NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
GameEndChecker.SetDirtyCheckEnd();
}
diff --git a/Patches/SabotageSystemPatch.cs b/Patches/SabotageSystemPatch.cs
index 17a5e15d0..9376e6fc1 100644
--- a/Patches/SabotageSystemPatch.cs
+++ b/Patches/SabotageSystemPatch.cs
@@ -287,7 +287,7 @@ public static class ElectricTaskInitializePatch
foreach (PlayerControl pc in Main.CachedAlivePlayerControls())
{
if (pc.GetCustomRole().NeedUpdateOnLights() || pc.Is(CustomRoles.Torch) || pc.Is(CustomRoles.Sleep) || Beacon.IsAffectedPlayer(pc.PlayerId))
- Utils.NotifyRoles(SpecifyTarget: pc, ForceLoop: true, SendOption: SendOption.None);
+ Utils.NotifyRoles(SpecifyTarget: pc, SendOption: SendOption.None);
}
}
@@ -313,7 +313,7 @@ public static class ElectricTaskCompletePatch
CustomRoles role = pc.GetCustomRole();
if (role.NeedUpdateOnLights() || pc.Is(CustomRoles.Torch) || pc.Is(CustomRoles.Sleep) || Beacon.IsAffectedPlayer(pc.PlayerId))
- Utils.NotifyRoles(SpecifyTarget: pc, ForceLoop: true, SendOption: SendOption.None);
+ Utils.NotifyRoles(SpecifyTarget: pc, SendOption: SendOption.None);
if (role == CustomRoles.Wiper)
{
diff --git a/Roles/Standard/Addons/Crewmate/Workhorse.cs b/Roles/Standard/Addons/Crewmate/Workhorse.cs
index c65b95e35..c8a77e2d3 100644
--- a/Roles/Standard/Addons/Crewmate/Workhorse.cs
+++ b/Roles/Standard/Addons/Crewmate/Workhorse.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using UnityEngine;
using static EHR.Options;
@@ -79,32 +80,30 @@ public class Workhorse : IAddon
return canBeTarget;
}
- public static bool OnCompleteTask(PlayerControl pc)
+ public static void OnCompleteTask(PlayerControl pc)
{
- if (!CustomRoles.Workhorse.IsEnable() || PlayerIdList.Count >= CustomRoles.Workhorse.GetCount()) return false;
-
- if (CurrentGameMode != CustomGameMode.Standard) return false;
-
- if (pc.Is(CustomRoles.Snitch) && !OptionSnitchCanBeWorkhorse.GetBool()) return false;
-
- if (!IsAssignTarget(pc)) return false;
-
- if (IRandom.Instance.Next(100) >= SpawnChance.GetInt()) return false;
-
- pc.RpcSetCustomRole(CustomRoles.Workhorse);
- TaskState taskState = pc.GetTaskState();
- taskState.AllTasksCount += NumLongTasks + NumShortTasks;
- taskState.CompletedTasksCount++;
-
- if (AmongUsClient.Instance.AmHost)
+ try
{
- Add(pc.PlayerId);
- pc.RpcResetTasks(false);
- pc.SyncSettings();
- Main.PlayerStates[pc.PlayerId].TaskState.AllTasksCount = pc.Data.Tasks.Count;
- Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
- }
+ if (!CustomRoles.Workhorse.IsEnable() || PlayerIdList.Count >= CustomRoles.Workhorse.GetCount()) return;
+ if (CurrentGameMode != CustomGameMode.Standard) return;
+ if (pc.Is(CustomRoles.Snitch) && !OptionSnitchCanBeWorkhorse.GetBool()) return;
+ if (!IsAssignTarget(pc)) return;
+ if (IRandom.Instance.Next(100) >= SpawnChance.GetInt()) return;
- return true;
+ pc.RpcSetCustomRole(CustomRoles.Workhorse);
+ TaskState taskState = pc.GetTaskState();
+ taskState.AllTasksCount += NumLongTasks + NumShortTasks;
+ taskState.CompletedTasksCount++;
+
+ if (AmongUsClient.Instance.AmHost)
+ {
+ Add(pc.PlayerId);
+ pc.RpcResetTasks(false);
+ pc.SyncSettings();
+ Main.PlayerStates[pc.PlayerId].TaskState.AllTasksCount = pc.Data.Tasks.Count;
+ Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
+ }
+ }
+ catch (System.Exception e) { Utils.ThrowException(e); }
}
}
\ No newline at end of file
diff --git a/Roles/Standard/Crewmate/Investigate/Rabbit.cs b/Roles/Standard/Crewmate/Investigate/Rabbit.cs
index 54d6f7c88..c3c82ae26 100644
--- a/Roles/Standard/Crewmate/Investigate/Rabbit.cs
+++ b/Roles/Standard/Crewmate/Investigate/Rabbit.cs
@@ -40,7 +40,7 @@ internal class Rabbit : RoleBase
RabbitStates.Remove(playerId);
}
- public static void ReceiveRPC(MessageReader reader)
+ public static void ReceiveRPCStatic(MessageReader reader)
{
byte id = reader.ReadByte();
bool hasArrow = reader.ReadBoolean();
@@ -49,14 +49,14 @@ internal class Rabbit : RoleBase
public override void OnTaskComplete(PlayerControl pc, int completedTaskCount, int totalTaskCount)
{
- if (pc == null || !RabbitStates.TryGetValue(pc.PlayerId, out RabbitState state)) return;
+ if (!RabbitStates.TryGetValue(pc.PlayerId, out RabbitState state)) return;
state.OnTaskComplete();
}
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
- if (seer == null || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || !RabbitStates.TryGetValue(seer.PlayerId, out RabbitState state)) return string.Empty;
+ if (seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || !RabbitStates.TryGetValue(seer.PlayerId, out RabbitState state)) return string.Empty;
string suffix = state.Suffix;
return hud ? $"{suffix}" : suffix;
diff --git a/Roles/Standard/Crewmate/Miscellaneous/Carrier.cs b/Roles/Standard/Crewmate/Miscellaneous/Carrier.cs
index 1bc6ce394..0eade9303 100644
--- a/Roles/Standard/Crewmate/Miscellaneous/Carrier.cs
+++ b/Roles/Standard/Crewmate/Miscellaneous/Carrier.cs
@@ -58,7 +58,8 @@ public class Carrier : RoleBase
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
{
- if (!shapeshifting || !Location.HasValue) return true;
+ if (!shapeshifting) return true;
+ if (!Location.HasValue) return false;
target.TP(Location.Value);
target.Notify(Translator.GetString("Carrier.TargetNotify"));
return false;
diff --git a/Roles/Standard/Crewmate/Support/DonutDelivery.cs b/Roles/Standard/Crewmate/Support/DonutDelivery.cs
index a988b5da8..e8ca67216 100644
--- a/Roles/Standard/Crewmate/Support/DonutDelivery.cs
+++ b/Roles/Standard/Crewmate/Support/DonutDelivery.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.Linq;
using AmongUs.GameOptions;
using EHR.Modules;
using static EHR.Options;
@@ -11,7 +10,6 @@ public class DonutDelivery : RoleBase
{
private const int Id = 642700;
private static List Instances = [];
- private static Dictionary StartingSpeed = [];
private static OptionItem CD;
private static OptionItem UseLimit;
@@ -66,7 +64,6 @@ public class DonutDelivery : RoleBase
public override void Init()
{
Instances = [];
- StartingSpeed = Main.AllPlayerSpeed.ToDictionary(x => x.Key, x => x.Value);
}
public override void Add(byte playerId)
@@ -99,7 +96,7 @@ public class DonutDelivery : RoleBase
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
- if (!IsEnable || killer == null || target == null || killer.GetAbilityUseLimit() <= 0) return false;
+ if (!IsEnable) return false;
killer.RpcRemoveAbilityUse();
@@ -123,7 +120,7 @@ public class DonutDelivery : RoleBase
LateTask.New(() =>
{
- Main.AllPlayerSpeed[target.PlayerId] = StartingSpeed[target.PlayerId];
+ Main.AllPlayerSpeed[target.PlayerId] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
target.MarkDirtySettings();
}, SEDuration.GetFloat(), log: false);
}, SEDelay.GetFloat(), log: false);
diff --git a/Roles/Standard/Crewmate/Support/TimeMaster.cs b/Roles/Standard/Crewmate/Support/TimeMaster.cs
index 999a26996..af7a3c686 100644
--- a/Roles/Standard/Crewmate/Support/TimeMaster.cs
+++ b/Roles/Standard/Crewmate/Support/TimeMaster.cs
@@ -131,27 +131,31 @@ internal class TimeMaster : RoleBase
private static IEnumerator Rewind()
{
- const float delay = 0.3f;
- long now = Utils.TimeStamp;
- int length = TimeMasterRewindTimeLength.GetInt();
-
Dictionary originalSpeeds = Main.AllPlayerSpeed.ToDictionary(x => x.Key, x => x.Value);
- Main.AllPlayerSpeed.SetAllValues(Main.MinSpeed);
- ReportDeadBodyPatch.CanReport.SetAllValues(false);
-
+
try
{
Rewinding = true;
+ const float delay = 0.3f;
+ long now = Utils.TimeStamp;
+ int length = TimeMasterRewindTimeLength.GetInt();
+ float duration = length * delay + 0.55f;
+
+ Main.AllPlayerSpeed.SetAllValues(Main.MinSpeed);
+ ReportDeadBodyPatch.CanReport.SetAllValues(false);
+
string notify = Utils.ColorString(Color.yellow, string.Format(Translator.GetString("TimeMasterRewindStart"), CustomRoles.TimeMaster.ToColoredString()));
foreach (PlayerControl player in Main.EnumeratePlayerControls())
{
if (player.inVent || player.MyPhysics?.Animations?.IsPlayingEnterVentAnimation() == true) player.MyPhysics?.RpcExitVent(player.GetClosestVent().Id);
- player.ReactorFlash(flashDuration: length * delay + 0.55f);
- player.Notify(notify, Math.Max((length * delay) + 0.55f, 4f));
+ player.ReactorFlash(flashDuration: duration);
+ player.Notify(notify, Math.Max(duration, 4f));
player.MarkDirtySettings();
}
+
+ yield return PlayerGameOptionsSender.SendAllImmediately().Wait();
yield return new WaitForSecondsRealtime(0.55f);
@@ -196,21 +200,29 @@ internal class TimeMaster : RoleBase
}
}
}
-
-
}
finally
{
Rewinding = false;
- Main.AllPlayerSpeed = originalSpeeds;
- ReportDeadBodyPatch.CanReport.SetAllValues(true);
+ Main.AllPlayerSpeed.AddRange(originalSpeeds);
Utils.MarkEveryoneDirtySettings();
+ ReportDeadBodyPatch.CanReport.SetAllValues(true);
}
}
+ public override void OnReportDeadBody()
+ {
+ AfterMeetingTasks();
+ }
+
public override void AfterMeetingTasks()
{
- Rewinding = false;
+ if (Rewinding)
+ {
+ Rewinding = false;
+ Main.AllPlayerSpeed.SetAllValues(Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod));
+ ReportDeadBodyPatch.CanReport.SetAllValues(true);
+ }
}
public override void OnFixedUpdate(PlayerControl player)
diff --git a/Roles/Standard/Ghost/Bloodmoon.cs b/Roles/Standard/Ghost/Bloodmoon.cs
index c3418913c..a2e37f73f 100644
--- a/Roles/Standard/Ghost/Bloodmoon.cs
+++ b/Roles/Standard/Ghost/Bloodmoon.cs
@@ -82,7 +82,7 @@ internal class Bloodmoon : IGhostRole
if (pc.RpcCheckAndMurder(player, true)) player.Suicide(PlayerState.DeathReason.LossOfBlood, pc);
}
- var alivePlayers = Main.CachedAlivePlayerControls();
+ /*var alivePlayers = Main.CachedAlivePlayerControls();
for (int index = 0; index < alivePlayers.Count; index++)
{
PlayerControl target = alivePlayers[index];
@@ -90,7 +90,7 @@ internal class Bloodmoon : IGhostRole
if (!FastVector2.DistanceWithinRange(target.Pos(), pc.Pos(), 4f)) continue;
target.Notify(string.Format(Translator.GetString("BloodmoonNearYou"), CustomRoles.Bloodmoon.ToColoredString()), sendOption: SendOption.None);
- }
+ }*/
}
public static void OnMeetingStart()
diff --git a/Roles/Standard/Impostor/Concealing/Penguin.cs b/Roles/Standard/Impostor/Concealing/Penguin.cs
index b92e6ae10..44d7af0ce 100644
--- a/Roles/Standard/Impostor/Concealing/Penguin.cs
+++ b/Roles/Standard/Impostor/Concealing/Penguin.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Diagnostics;
using AmongUs.GameOptions;
using EHR.Modules;
using Hazel;
@@ -16,13 +17,12 @@ public class Penguin : RoleBase
private static OptionItem OptionSpeedDuringDrag;
private static OptionItem OptionVictimCanUseAbilities;
- private float AbductTimer;
+ private Stopwatch AbductTimer;
private float AbductTimerLimit;
private PlayerControl AbductVictim;
private float Cooldown;
- private int Count;
private float DefaultSpeed;
private bool IsGoose;
@@ -33,7 +33,6 @@ public class Penguin : RoleBase
private byte PenguinId = byte.MaxValue;
private float SpeedDuringDrag;
- private bool stopCount;
private bool VictimCanUseAbilities;
public override bool IsEnable => PenguinId != byte.MaxValue;
@@ -101,8 +100,7 @@ public class Penguin : RoleBase
PenguinId = playerId;
Penguin_ = Utils.GetPlayerById(playerId);
- AbductTimer = 255f;
- stopCount = false;
+ AbductTimer = null;
LastNotify = 0;
}
@@ -142,23 +140,25 @@ public class Penguin : RoleBase
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
- public void ReceiveRPC(byte victim)
+ public void ReceiveRPCVictim(byte victim)
{
if (victim == 255)
{
AbductVictim = null;
- AbductTimer = 255f;
+ AbductTimer = null;
+ Utils.SendRPC(CustomRPC.PenguinSync, PenguinId, 2, true);
}
else
{
AbductVictim = Utils.GetPlayerById(victim);
- AbductTimer = AbductTimerLimit;
+ AbductTimer = Stopwatch.StartNew();
+ Utils.SendRPC(CustomRPC.PenguinSync, PenguinId, 2, false);
}
}
- public void ReceiveRPC(float timer)
+ public void ReceiveRPCTimer(bool reset)
{
- AbductTimer = timer;
+ AbductTimer = reset ? null : Stopwatch.StartNew();
}
private void AddVictim(PlayerControl target)
@@ -168,7 +168,8 @@ public class Penguin : RoleBase
if (Thanos.IsImmune(target)) return;
AbductVictim = target;
- AbductTimer = AbductTimerLimit;
+ AbductTimer = Stopwatch.StartNew();
+ Utils.SendRPC(CustomRPC.PenguinSync, PenguinId, 2, false);
Main.AllPlayerSpeed[PenguinId] = SpeedDuringDrag;
Penguin_.MarkDirtySettings();
LogSpeed();
@@ -209,7 +210,8 @@ public class Penguin : RoleBase
}, 1f, log: false);
}
- AbductTimer = 255f;
+ AbductTimer = null;
+ Utils.SendRPC(CustomRPC.PenguinSync, PenguinId, 2, true);
Main.AllPlayerSpeed[PenguinId] = DefaultSpeed;
Penguin_.MarkDirtySettings();
LogSpeed();
@@ -268,8 +270,6 @@ public class Penguin : RoleBase
{
if (!IsEnable) return;
- stopCount = true;
-
if (!AmongUsClient.Instance.AmHost) return;
if (!AbductVictim) return;
@@ -301,31 +301,12 @@ public class Penguin : RoleBase
if (!IsEnable) return;
if (AbductVictim) Penguin_.MarkDirtySettings();
-
- stopCount = false;
}
public override void OnFixedUpdate(PlayerControl pc)
{
if (!IsEnable) return;
- if (!AmongUsClient.Instance.AmHost) return;
-
- if (!GameStates.IsInTask) return;
-
- if (!stopCount)
- {
- AbductTimer -= Time.fixedDeltaTime;
-
- Count++;
-
- if (Count >= 30)
- {
- Count = 0;
- Utils.SendRPC(CustomRPC.PenguinSync, PenguinId, 2, AbductTimer);
- }
- }
-
if (AbductVictim)
{
if (!Penguin_.IsAlive() || Pelican.IsEaten(PenguinId) || !AbductVictim.IsAlive())
@@ -340,7 +321,7 @@ public class Penguin : RoleBase
LastNotify = Utils.TimeStamp;
}
- if (AbductTimer <= 0f && !Penguin_.MyPhysics.Animations.IsPlayingAnyLadderAnimation() && !Penguin_.inMovingPlat && !Penguin_.onLadder)
+ if (AbductTimer.Elapsed.TotalSeconds >= AbductTimerLimit && !Penguin_.MyPhysics.Animations.IsPlayingAnyLadderAnimation() && !Penguin_.inMovingPlat && !Penguin_.onLadder)
{
// Set IsDead to true first (prevents ladder chase)
if (!IsGoose)
@@ -395,17 +376,21 @@ public class Penguin : RoleBase
{
LateTask.New(() =>
{
- if (AbductVictim != null) Utils.TP(AbductVictim.NetTransform, position, log: false);
+ if (AbductVictim) Utils.TP(AbductVictim.NetTransform, position, log: false);
}, 0.25f, log: false);
}
}
}
- else if (AbductTimer <= 100f) AbductTimer = 255f;
+ else
+ {
+ AbductTimer = null;
+ Utils.SendRPC(CustomRPC.PenguinSync, PenguinId, 2, true);
+ }
}
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
- if (seer == null || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || !Main.PlayerStates.TryGetValue(seer.PlayerId, out PlayerState state) || state.Role is not Penguin pg || pg.AbductVictim == null) return string.Empty;
- return $"\u21b9 {(int)(pg.AbductTimer + 1f)}s";
+ if (seer.PlayerId != PenguinId || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || AbductTimer == null) return string.Empty;
+ return $"\u21b9 {(int)(AbductTimerLimit - AbductTimer.Elapsed.TotalSeconds + 1f)}s";
}
}
\ No newline at end of file
diff --git a/Roles/Standard/Impostor/Concealing/Stasis.cs b/Roles/Standard/Impostor/Concealing/Stasis.cs
index e2147af9c..3b2d51976 100644
--- a/Roles/Standard/Impostor/Concealing/Stasis.cs
+++ b/Roles/Standard/Impostor/Concealing/Stasis.cs
@@ -128,7 +128,7 @@ public class Stasis : RoleBase
}
}
- Utils.MarkEveryoneDirtySettings();
+ Utils.SyncAllSettings();
Main.Instance.StartCoroutine(Countdown());
return;
@@ -150,11 +150,26 @@ public class Stasis : RoleBase
catch (Exception e) { Utils.ThrowException(e); }
ReportDeadBodyPatch.CanReport.SetAllValues(true);
- Main.AllPlayerSpeed = originalSpeeds;
+ Main.AllPlayerSpeed.AddRange(originalSpeeds);
Main.PlayerStates.Values.Do(x => x.IsBlackOut = false);
Utils.MarkEveryoneDirtySettings();
pc.RpcResetAbilityCooldown();
}
}
+
+ public override void OnReportDeadBody()
+ {
+ if (UsingAbility)
+ {
+ UsingAbility = false;
+ ReportDeadBodyPatch.CanReport.SetAllValues(true);
+ Main.AllPlayerSpeed.SetAllValues(Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod));
+ }
+ }
+
+ public override void AfterMeetingTasks()
+ {
+ OnReportDeadBody();
+ }
}
diff --git a/Roles/Standard/Impostor/Killing/BountyHunter.cs b/Roles/Standard/Impostor/Killing/BountyHunter.cs
index 2c6c7ef2d..99ea08950 100644
--- a/Roles/Standard/Impostor/Killing/BountyHunter.cs
+++ b/Roles/Standard/Impostor/Killing/BountyHunter.cs
@@ -82,7 +82,7 @@ public class BountyHunter : RoleBase
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
- public void ReceiveRPC(byte targetId)
+ public void ReceiveRPCTarget(byte targetId)
{
Target = targetId;
}
diff --git a/Roles/Standard/Impostor/Killing/Butcher.cs b/Roles/Standard/Impostor/Killing/Butcher.cs
index da333e43a..80be40afe 100644
--- a/Roles/Standard/Impostor/Killing/Butcher.cs
+++ b/Roles/Standard/Impostor/Killing/Butcher.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using EHR.Modules;
using Hazel;
+using UnityEngine;
namespace EHR.Roles;
@@ -70,7 +71,6 @@ internal class Butcher : RoleBase
RPCHandlerPatch.WhiteListFromRateLimitUntil(target.PlayerId, Utils.TimeStamp + 5);
Vector2 ops = target.Pos();
- Vector2 originPos = killer.Pos();
var rd = IRandom.Instance;
Main.Instance.StartCoroutine(SpawnFakeDeadBodies());
@@ -85,15 +85,12 @@ internal class Butcher : RoleBase
Utils.RpcCreateDeadBody(location, (byte)target.CurrentOutfit.ColorId, target, SendOption.None);
- if (i % 4 == 0) yield return null;
+ yield return new WaitForSecondsRealtime(0.2f);
}
-
- yield return null;
- killer.TP(originPos);
}
}, 0.05f, "Butcher Murder");
}
- return base.OnCheckMurder(killer, target);
+ return true;
}
}
\ No newline at end of file
diff --git a/Roles/Standard/Impostor/Support/Capitalist.cs b/Roles/Standard/Impostor/Support/Capitalist.cs
index 8122fd10b..6709cf05d 100644
--- a/Roles/Standard/Impostor/Support/Capitalist.cs
+++ b/Roles/Standard/Impostor/Support/Capitalist.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using static EHR.Options;
namespace EHR.Roles;
@@ -51,10 +52,11 @@ internal class Capitalist : RoleBase
});
}
- public static bool AddTaskForPlayer(PlayerControl player)
+ public static void AddTaskForPlayer(PlayerControl player)
{
- if (CapitalistAddTask.TryGetValue(player.PlayerId, out int amount))
+ try
{
+ if (!CapitalistAddTask.TryGetValue(player.PlayerId, out int amount)) return;
TaskState taskState = player.GetTaskState();
taskState.AllTasksCount += amount;
CapitalistAddTask.Remove(player.PlayerId);
@@ -63,9 +65,7 @@ internal class Capitalist : RoleBase
player.SyncSettings();
Main.PlayerStates[player.PlayerId].TaskState.AllTasksCount = player.Data.Tasks.Count;
Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
- return false;
}
-
- return true;
+ catch (Exception e) { Utils.ThrowException(e); }
}
}
\ No newline at end of file
diff --git a/Roles/Standard/Neutral/Evil/Vector.cs b/Roles/Standard/Neutral/Evil/Vector.cs
index 25aef829b..57e170150 100644
--- a/Roles/Standard/Neutral/Evil/Vector.cs
+++ b/Roles/Standard/Neutral/Evil/Vector.cs
@@ -2,6 +2,7 @@
using System.Linq;
using AmongUs.GameOptions;
using EHR.Modules;
+using Hazel;
using UnityEngine;
using static EHR.Options;
@@ -36,8 +37,9 @@ internal class Vector : RoleBase
public override void Add(byte playerId)
{
On = true;
- VectorVentCount[playerId] = 0;
- VectorVentNumWin = MapWinCounts[SubmergedCompatibility.IsSubmerged() ? MapNames.Airship : Main.CurrentMap].GetInt();
+ if (!AmongUsClient.Instance.AmHost) return;
+ VectorVentNumWin = MapWinCounts[SubmergedCompatibility.IsSubmerged() || Main.LIMap ? MapNames.Airship : Main.CurrentMap].GetInt();
+ LateTask.New(() => Utils.SendRPC(CustomRPC.SyncRoleData, playerId, VectorVentNumWin), 8f);
}
public override void Init()
@@ -71,7 +73,7 @@ internal class Vector : RoleBase
{
VectorVentCount.TryAdd(pc.PlayerId, 0);
VectorVentCount[pc.PlayerId]++;
- if (!pc.IsModdedClient()) Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
+ if (!pc.IsModdedClient()) Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc, SendOption: SendOption.None);
if (pc.AmOwner) CustomSoundsManager.Play("MarioJump");
if (AmongUsClient.Instance.AmHost && VectorVentCount[pc.PlayerId] >= VectorVentNumWin)
@@ -81,4 +83,9 @@ internal class Vector : RoleBase
CustomWinnerHolder.WinnerIds.Add(pc.PlayerId);
}
}
+
+ public void ReceiveRPC(MessageReader reader)
+ {
+ VectorVentNumWin = reader.ReadPackedInt32();
+ }
}
diff --git a/Roles/Standard/Neutral/Evil/Workaholic.cs b/Roles/Standard/Neutral/Evil/Workaholic.cs
index 8e61175f3..33ace292e 100644
--- a/Roles/Standard/Neutral/Evil/Workaholic.cs
+++ b/Roles/Standard/Neutral/Evil/Workaholic.cs
@@ -69,13 +69,16 @@ internal class Workaholic : RoleBase
Main.AllPlayerSpeed[playerId] = WorkaholicSpeed.GetFloat();
}
- public override void OnTaskComplete(PlayerControl player, int CompletedTasksCount, int AllTasksCount)
+ public override void OnTaskComplete(PlayerControl player, int completedTasksCount, int allTasksCount)
{
- if (CompletedTasksCount + 1 >= AllTasksCount && (!WorkaholicCannotWinAtDeath.GetBool() || player.IsAlive()))
+ if (completedTasksCount + 1 >= allTasksCount && (!WorkaholicCannotWinAtDeath.GetBool() || player.IsAlive()))
{
Logger.Info("Workaholic Tasks Finished", "Workaholic");
RPC.PlaySoundRPC(player.PlayerId, Sounds.KillSound);
+ GameEndChecker.ShouldNotCheck = true;
+ LateTask.New(() => GameEndChecker.ShouldNotCheck = false, 0.2f);
+
foreach (PlayerControl pc in Main.EnumerateAlivePlayerControls())
pc.Suicide(pc.PlayerId == player.PlayerId ? PlayerState.DeathReason.Overtired : PlayerState.DeathReason.Ashamed, player);
diff --git a/Roles/Standard/Neutral/Killing/Pelican.cs b/Roles/Standard/Neutral/Killing/Pelican.cs
index 9216853ab..4d07920a3 100644
--- a/Roles/Standard/Neutral/Killing/Pelican.cs
+++ b/Roles/Standard/Neutral/Killing/Pelican.cs
@@ -121,12 +121,9 @@ public class Pelican : RoleBase
return false;
}
- public static bool CanEat(PlayerControl pc, byte id)
+ public static bool CanEat(PlayerControl target)
{
- if (!pc.Is(CustomRoles.Pelican) || GameStates.IsMeeting) return false;
-
- PlayerControl target = Utils.GetPlayerById(id);
- return target.IsAlive() && !target.inVent && !Medic.ProtectList.Contains(target.PlayerId) && !target.Is(CustomRoles.GM) && !target.Is(CustomRoles.Pestilence) && !IsEaten(pc, id) && !IsEaten(id);
+ return !Medic.ProtectList.Contains(target.PlayerId) && !target.Is(CustomRoles.GM) && !target.Is(CustomRoles.Pestilence) && (!target.Is(CustomRoles.Guardian) || !target.AllTasksCompleted()) && !IsEaten(target.PlayerId);
}
public static Vector2 GetBlackRoomPS()
@@ -147,7 +144,7 @@ public class Pelican : RoleBase
public override void GetProgressText(byte playerId, bool comms, StringBuilder resultText)
{
PlayerControl player = Utils.GetPlayerById(playerId);
- if (player == null)
+ if (!player)
{
resultText.Append("Invalid");
return;
@@ -163,9 +160,9 @@ public class Pelican : RoleBase
.Append(")");
}
- public static void EatPlayer(PlayerControl pc, PlayerControl target)
+ private static void EatPlayer(PlayerControl pc, PlayerControl target)
{
- if (pc == null || target == null || !CanEat(pc, target.PlayerId)) return;
+ if (!pc || !target) return;
if (!EatenList.ContainsKey(pc.PlayerId)) EatenList.Add(pc.PlayerId, []);
@@ -217,7 +214,7 @@ public class Pelican : RoleBase
{
PlayerControl target = Utils.GetPlayerById(tar);
PlayerControl player = Utils.GetPlayerById(pc);
- if (player == null || target == null) continue;
+ if (!player || !target) continue;
target.TP(player);
Main.AllPlayerSpeed[tar] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
@@ -259,7 +256,7 @@ public class Pelican : RoleBase
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
- if (CanEat(killer, target.PlayerId))
+ if (CanEat(target))
{
EatPlayer(killer, target);
killer.SetKillCooldown();