just some casual bug fixing

This commit is contained in:
Gurge44 committed 2026-06-01 20:44:35 +02:00
1 parent 83b74f66fc
commit b2ea6d4745
34 files changed
+462 -255

No files matched your search

+1
View File
@@ -36,6 +36,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FPS/@EntryIndexedValue">FPS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GA/@EntryIndexedValue">GA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GM/@EntryIndexedValue">GM</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HNS/@EntryIndexedValue">HNS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HP/@EntryIndexedValue">HP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HQ/@EntryIndexedValue">HQ</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HUD/@EntryIndexedValue">HUD</s:String>
+1 -1
View File
@@ -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("</color>");
}
+4 -2
View File
@@ -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<PlayerControl> 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<PlayerControl> CachedAllPlayerControls()
{
+2 -1
View File
@@ -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)
+1 -1
View File
@@ -248,7 +248,7 @@ internal static class CustomRoleSelector
Dictionary<RoleAssignType, List<RoleAssignInfo>> 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");
+64 -31
View File
@@ -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)
+12 -2
View File
@@ -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);
+114 -17
View File
@@ -14,6 +14,7 @@ internal static class CustomTeamManager
public static CustomTeam WinnerTeam;
public static Dictionary<CustomTeam, HashSet<byte>> CustomTeamPlayerIds = [];
public static List<CustomTeamOptionGroup> CustomTeamOptions = [];
public static Dictionary<CustomTeam, List<bool>> 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<byte> 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<bool> 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<byte> TakeAsManyAsSet(IEnumerable<byte> 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<CustomTeam, HashSet<byte>> 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<byte> 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<bool> 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<CustomTeam> AliveTeamPlayers = [];
private static readonly List<byte> ToRemove = [];
public static bool CheckCustomTeamGameEnd()
{
@@ -210,27 +295,20 @@ internal static class CustomTeamManager
AliveTeamPlayers.Clear();
foreach ((CustomTeam teamKey, HashSet<byte> originalSet) in CustomTeamPlayerIds)
{
HashSet<byte> 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<bool> 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
+11 -4
View File
@@ -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)
+7 -2
View File
@@ -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!
/// <summary>
/// Sets the value for all existing keys in a dictionary to a specific value
@@ -41,7 +46,7 @@ public static class CollectionExtensions
/// <param name="value"></param>
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
/// <param name="adjust">The function to adjust the values with</param>
public void AdjustAllValues(Func<TValue, TValue> adjust)
{
foreach (TKey key in dictionary.Keys.ToArray())
foreach (TKey key in dictionary.Keys)
dictionary[key] = adjust(dictionary[key]);
}
@@ -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);
+1
View File
@@ -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();
+16 -6
View File
@@ -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;
+37 -38
View File
@@ -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 = $"<size=450%>{iconTextLeft} <font=\"VCR SDF\" material=\"VCR Black Outline\">{ColorString(seerTeam.GetColor(), $"{seerTeam}")}</font> {iconTextRight}</size><size=500%>\n \n</size>";
selfName = $"{selfTeamName}\r\n<size=150%>{seerRole.ToColoredString()}</size>{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("</size>")) selfName = selfName.Remove(selfName.Length - 7);
if (selfName.EndsWith("</color>")) 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, "<size=0%>", seer);
CustomRpcSenderExtensions.RpcSetName(ref sender, target, "<size=0%>", 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("</size>")) targetName = targetName.Remove(targetName.Length - 7);
if (targetName.EndsWith("</color>")) 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;
+9 -8
View File
@@ -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");
+1
View File
@@ -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"))));
}
+14
View File
@@ -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();
+5 -7
View File
@@ -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);
+22 -12
View File
@@ -1037,8 +1037,12 @@ internal static class ReportDeadBodyPatch
Logger.Info($"{__instance.GetNameWithRole().RemoveHtmlTags()} => {target?.Object?.GetNameWithRole() ?? "null"}", "ReportDeadBody");
foreach (KeyValuePair<byte, PlayerState> kvp in Main.PlayerStates)
kvp.Value.LastRoom = GetPlayerById(kvp.Key).GetPlainShipRoom();
try
{
foreach (KeyValuePair<byte, PlayerState> 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<PlayerTask>)(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<PlayerTask>)(x => taskId == x.Id));
Hider.OnSpecificTaskComplete(pc, task);
}
if (pc.IsAlive())
{
var task = pc.myTasks.Find((Il2CppSystem.Predicate<PlayerTask>)(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();
}
+2 -2
View File
@@ -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)
{
+23 -24
View File
@@ -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); }
}
}
@@ -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 ? $"<size=200%>{suffix}</size>" : suffix;
@@ -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;
@@ -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<DonutDelivery> Instances = [];
private static Dictionary<byte, float> 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);
+26 -14
View File
@@ -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<byte, float> 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)
+2 -2
View File
@@ -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()
+23 -38
View File
@@ -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";
}
}
+17 -2
View File
@@ -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();
}
}
@@ -82,7 +82,7 @@ public class BountyHunter : RoleBase
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
public void ReceiveRPC(byte targetId)
public void ReceiveRPCTarget(byte targetId)
{
Target = targetId;
}
+3 -6
View File
@@ -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;
}
}
@@ -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); }
}
}
+10 -3
View File
@@ -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();
}
}
+5 -2
View File
@@ -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);
+7 -10
View File
@@ -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(")</color>");
}
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();