even more improvements & fixes

This commit is contained in:
Gurge44 committed 2025-07-02 13:08:52 +02:00
1 parent e92042bf54
commit 69768032eb
12 files changed
+607 -107

No files matched your search

+11 -5
View File
@@ -130,12 +130,10 @@ public static class BedWars
.SetGameMode(gameMode);
TNTDamageOption = new IntegerOptionItem(id++, "BedWars.TNTDamageOption", new(1, 100, 1), 20, tab)
.SetValueFormat(OptionFormat.Multiplier)
.SetColor(color)
.SetGameMode(gameMode);
TNTBedDamageOption = new IntegerOptionItem(id++, "BedWars.TNTBedDamageOption", new(1, 100, 1), 10, tab)
.SetValueFormat(OptionFormat.Multiplier)
.SetColor(color)
.SetGameMode(gameMode);
@@ -392,6 +390,7 @@ public static class BedWars
pc.RpcSetColor(team.GetColorId());
pc.TP(data.Base.SpawnPosition);
pc.RpcResetAbilityCooldown();
Data[pc.PlayerId] = data;
@@ -502,7 +501,7 @@ public static class BedWars
(MapNames.MiraHQ, SystemTypes.Balcony) => pos.y < 2f,
(MapNames.Polus, SystemTypes.LifeSupp) => room != null && room.RoomId == SystemTypes.BoilerRoom,
(MapNames.Airship, SystemTypes.CargoBay) => room != null && room.RoomId == SystemTypes.Ventilation,
(MapNames.Airship, SystemTypes.MeetingRoom) => room != null && room.RoomId == SystemTypes.GapRoom,
(MapNames.Airship, SystemTypes.MeetingRoom) => (room != null && room.RoomId == SystemTypes.GapRoom) || __instance.inMovingPlat || __instance.onLadder || __instance.MyPhysics.Animations.IsPlayingAnyLadderAnimation(),
(MapNames.Fungle, SystemTypes.Kitchen) => room != null && room.RoomId == SystemTypes.FishingDock,
(MapNames.Fungle, SystemTypes.Comms) => pos is { y: > 8f, x: > 19f },
(MapNames.Fungle, SystemTypes.Jungle) => pos is { x: > 10f, y: < -11f },
@@ -1394,6 +1393,13 @@ public static class BedWars
Vector2 pos = pc.Pos();
bool nextToBed = AllNetObjects.FindFirst(x => !x.Value.Bed.IsBroken && Vector2.Distance(x.Value.Bed.Position, pos) <= BedBreakAndProtectRange, out KeyValuePair<BedWarsTeam, NetObjectCollection> bed);
if (SelectedSlot < 0 || SelectedSlot >= Items.Count)
{
if (nextToBed) bed.Value.Bed.TryBreak(pc);
return;
}
KeyValuePair<Item, int> selected = Items.ElementAt(SelectedSlot);
Logger.Info($"Use selected item {selected.Key} with count {selected.Value} at position {pos} by player {pc.GetRealName()} (next to bed: {nextToBed})", "BedWars");
@@ -1696,6 +1702,8 @@ public static class BedWars
public static void ApplyGameOptions(IGameOptions opt, byte playerId)
{
AURoleOptions.PhantomCooldown = 0.1f;
try { AURoleOptions.GuardianAngelCooldown = 900f; }
catch (Exception e) { Utils.ThrowException(e); }
@@ -1705,8 +1713,6 @@ public static class BedWars
opt.SetFloat(FloatOptionNames.CrewLightMod, TrappedVision);
opt.SetFloat(FloatOptionNames.ImpostorLightMod, TrappedVision);
}
AURoleOptions.PhantomCooldown = 0.1f;
}
public static bool CanUseImpostorVentButton(PlayerControl pc)
+448 -23
View File
@@ -14,6 +14,7 @@ internal static class EAC
{
public static int DeNum;
public static readonly HashSet<string> InvalidReports = [];
public static readonly Dictionary<byte, float> TimeSinceLastTaskCompletion = [];
public static void WarnHost(int denum = 1)
{
@@ -37,6 +38,7 @@ internal static class EAC
if (pc == null || reader == null) return false;
MessageReader sr = MessageReader.Get(reader);
bool gameStarted = AmongUsClient.Instance.GameState is InnerNetClient.GameStates.Started or InnerNetClient.GameStates.Ended;
try
{
@@ -67,7 +69,8 @@ internal static class EAC
text.Contains('█') ||
text.Contains('▌') ||
text.Contains('▒') ||
text.Contains("习近平"))
text.Contains("习近平") ||
(!pc.IsModdedClient() && text.Length > 100))
{
Report(pc, "Illegal messages");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent an illegal message, which has been rejected", "EAC");
@@ -95,8 +98,6 @@ internal static class EAC
{
WarnHost();
Report(pc, "Try to Report body out of game B");
HandleCheat(pc, "Try to Report body out of game B");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] attempted to report a body that may have been illegally killed, but was rejected", "EAC");
sr.Recycle();
return true;
}
@@ -193,10 +194,23 @@ internal static class EAC
return true;
}
byte colorId = sr.ReadByte();
if (colorId > 17)
{
WarnHost();
Report(pc, "Invalid color");
HandleCheat(pc, "Invalid color");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent invalid color {colorId}, rejected", "EAC");
sr.Recycle();
return true;
}
break;
}
case RpcCalls.SetColor when !pc.IsModdedClient() && (!Options.PlayerCanSetColor.GetBool() || !GameStates.IsLobby):
{
WarnHost();
Report(pc, "Directly SetColor");
HandleCheat(pc, "Directly SetColor");
Logger.Fatal($"Directly SetColor【{pc.OwnerId}:{pc.GetRealName()}】has been rejected", "EAC");
@@ -220,6 +234,7 @@ internal static class EAC
case RpcCalls.MurderPlayer:
{
var target = sr.ReadNetObject<PlayerControl>();
var resultFlags = (MurderResultFlags)sr.ReadInt32();
if (GameStates.IsLobby)
{
@@ -230,32 +245,57 @@ internal static class EAC
return true;
}
if (!resultFlags.HasFlag(MurderResultFlags.FailedError) && !resultFlags.HasFlag(MurderResultFlags.FailedProtected) && target != null)
LateTask.New(() => target.RpcRevive(), 0.1f, log: false);
Report(pc, "Directly Murder Player");
HandleCheat(pc, "Directly Murder Player");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] directly killed, rejected", "EAC");
sr.Recycle();
LateTask.New(() =>
{
foreach (PlayerControl player in new[] { pc, target })
{
if (player.IsAlive() && player.Data.IsDead)
player.RpcChangeRoleBasis(player.GetRoleMap().CustomRole);
}
}, 1f, "force revive after hack");
return true;
}
case RpcCalls.CheckShapeshift:
{
if (GameStates.IsLobby)
var target = sr.ReadNetObject<PlayerControl>();
bool animate = sr.ReadBoolean();
if (!gameStarted)
{
Report(pc, "Lobby Check Shapeshift");
HandleCheat(pc, "Lobby Check Shapeshift");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] directly transformed in lobby, rejected", "EAC");
WarnHost();
Report(pc, "Using shift button in lobby");
HandleCheat(pc, "Using shift button in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent CheckShapeshift in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if (target != null && target != pc && !animate)
{
WarnHost();
Report(pc, "No shapeshift animation");
HandleCheat(pc, "No shapeshift animation");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent CheckShapeshift with no animation, rejected", "EAC");
sr.Recycle();
return true;
}
if (pc.shapeshiftTargetPlayerId != -1 && target != null && target != pc)
{
WarnHost();
Report(pc, "Shapeshifting while shapeshifted");
HandleCheat(pc, "Shapeshifting while shapeshifted");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent CheckShapeshift while shapeshifted, rejected", "EAC");
sr.Recycle();
return true;
}
if (((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance) && target != pc)
{
WarnHost();
Report(pc, "Trying to shift during meeting");
sr.Recycle();
return true;
}
break;
}
case RpcCalls.Shapeshift when !pc.IsHost():
@@ -283,13 +323,38 @@ internal static class EAC
sr.Recycle();
return true;
}
case RpcCalls.CompleteTask when GameStates.IsMeeting && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating && !pc.IsHost() && !(Main.CurrentMap == MapNames.Airship && ExileController.Instance):
case RpcCalls.CompleteTask:
{
Report(pc, "Complete Task in Meeting");
HandleCheat(pc, "Complete Task in Meeting");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] completed a task in a meeting, which has been rejected", "EAC");
sr.Recycle();
return true;
if (!gameStarted)
{
WarnHost();
Report(pc, "CompleteTask Rpc in lobby");
HandleCheat(pc, "CompleteTask Rpc in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent CompleteTask RPC in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if (TimeSinceLastTaskCompletion.TryGetValue(pc.PlayerId, out float time) && time < 0.1f)
{
WarnHost();
Report(pc, "Auto complete tasks");
HandleCheat(pc, "Auto complete tasks");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent CompleteTask RPC too fast, rejected", "EAC");
sr.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(pc, "Doing task during meeting");
sr.Recycle();
return true;
}
TimeSinceLastTaskCompletion[pc.PlayerId] = 0f;
break;
}
case RpcCalls.SetStartCounter:
{
@@ -318,6 +383,292 @@ internal static class EAC
break;
}
case RpcCalls.CheckVanish:
{
if (!gameStarted)
{
WarnHost();
Report(pc, "Using vanish button in lobby");
HandleCheat(pc, "Using vanish button in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent CheckVanish in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(pc, "Trying to vanish during meeting");
sr.Recycle();
return true;
}
break;
}
case RpcCalls.CheckAppear:
{
if (!gameStarted)
{
WarnHost();
Report(pc, "Using appear button in lobby");
HandleCheat(pc, "Using appear button in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] tried to appear in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(pc, "Trying to appear during meeting");
sr.Recycle();
return true;
}
break;
}
case RpcCalls.PlayAnimation:
{
if (!gameStarted)
{
WarnHost();
Report(pc, "PlayAnimation Rpc in lobby");
HandleCheat(pc, "PlayAnimation Rpc in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent PlayAnimation RPC in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if (!GameManager.Instance.LogicOptions.GetVisualTasks())
{
WarnHost();
Report(pc, "PlayAnimation Rpc with visuals off");
HandleCheat(pc, "PlayAnimation Rpc with visuals off");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent PlayAnimation RPC with visuals off, rejected", "EAC");
sr.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(pc, "PlayAnimation Rpc during meeting");
sr.Recycle();
return true;
}
break;
}
case RpcCalls.Exiled:
case RpcCalls.SetName:
case RpcCalls.StartMeeting:
case RpcCalls.SendChatNote:
case RpcCalls.SetRole:
case RpcCalls.ProtectPlayer:
case RpcCalls.UseZipline:
case RpcCalls.TriggerSpores:
case RpcCalls.RejectShapeshift:
{
if (!pc.IsModdedClient())
{
WarnHost();
Report(pc, "Invalid Rpc");
HandleCheat(pc, "Invalid Rpc");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent invalid RPC {rpc}, rejected", "EAC");
}
sr.Recycle();
return true;
}
case RpcCalls.SetScanner:
{
if (!GameManager.Instance.LogicOptions.GetVisualTasks())
{
WarnHost();
Report(pc, "SetScanner Rpc with visuals off");
HandleCheat(pc, "SetScanner Rpc with visuals off");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent SetScanner RPC with visuals off, rejected", "EAC");
sr.Recycle();
return true;
}
if (!sr.ReadBoolean()) break;
if (!gameStarted)
{
WarnHost();
Report(pc, "SetScanner Rpc in lobby");
HandleCheat(pc, "SetScanner Rpc in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] sent SetScanner RPC in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(pc, "SetScanner Rpc during meeting");
sr.Recycle();
return true;
}
if (pc.IsImpostor())
{
WarnHost();
Report(pc, "SetScanner Rpc as impostor");
sr.Recycle();
return true;
}
if (pc.myTasks.ToArray().All(x => x.TaskType != TaskTypes.SubmitScan))
{
WarnHost();
Report(pc, "SetScanner Rpc without Submit Scan task");
sr.Recycle();
return true;
}
break;
}
case RpcCalls.UsePlatform:
{
if (!gameStarted)
{
WarnHost();
Report(pc, "Using platform in lobby");
HandleCheat(pc, "Using platform in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] tried to use platform in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if (GameManager.Instance.LogicOptions.MapId != 4)
{
WarnHost();
Report(pc, "Using platform on wrong map");
HandleCheat(pc, "Using platform on wrong map");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] tried to use platform on a map that does not have platforms, rejected", "EAC");
sr.Recycle();
return true;
}
if (GameManager.Instance.TryCast<HideAndSeekManager>())
{
WarnHost();
Report(pc, "Using platform in hide n seek");
HandleCheat(pc, "Using platform in hide n seek");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] tried to use platform in hide n seek, rejected", "EAC");
sr.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(pc, "Using platform during meeting");
sr.Recycle();
return true;
}
break;
}
case RpcCalls.CheckProtect:
{
var target = sr.ReadNetObject<PlayerControl>();
if (!gameStarted)
{
WarnHost();
Report(pc, "Using protect button in lobby");
HandleCheat(pc, "Using protect button in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] tried to protect in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if (target != null && target == pc)
{
WarnHost();
Report(pc, "Using protect button on self");
sr.Recycle();
return true;
}
if (target == null) break;
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(pc, "Trying to protect during meeting");
sr.Recycle();
return true;
}
break;
}
case RpcCalls.CheckZipline:
{
if (!gameStarted)
{
WarnHost();
Report(pc, "Using zipline in lobby");
HandleCheat(pc, "Using zipline in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] tried to use zipline in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if (GameManager.Instance.LogicOptions.MapId < 5)
{
WarnHost();
Report(pc, "Using zipline on wrong map");
HandleCheat(pc, "Using zipline on wrong map");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] tried to use zipline on a map that does not have ziplines, rejected", "EAC");
sr.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(pc, "Using zipline during meeting");
sr.Recycle();
return true;
}
break;
}
case RpcCalls.CheckSpore:
{
if (!gameStarted)
{
WarnHost();
Report(pc, "Triggering spore in lobby");
HandleCheat(pc, "Triggering spore in lobby");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] tried to trigger spore in lobby, rejected", "EAC");
sr.Recycle();
return true;
}
if (GameManager.Instance.LogicOptions.MapId < 5)
{
WarnHost();
Report(pc, "Triggering spore on wrong map");
HandleCheat(pc, "Triggering spore on wrong map");
Logger.Fatal($"Player [{pc.OwnerId}:{pc.GetRealName()}] tried to trigger spore on a map that does not have spores, rejected", "EAC");
sr.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(pc, "Triggering spore during meeting");
sr.Recycle();
return true;
}
break;
}
}
switch (callId)
@@ -494,6 +845,7 @@ internal static class EAC
var rpcType = (RpcCalls)callId;
MessageReader subReader = MessageReader.Get(reader);
bool gameStarted = AmongUsClient.Instance.GameState is InnerNetClient.GameStates.Started or InnerNetClient.GameStates.Ended;
try
{
@@ -521,6 +873,26 @@ internal static class EAC
case RpcCalls.EnterVent:
case RpcCalls.ExitVent:
{
if (!gameStarted)
{
WarnHost();
Report(player, "Venting in lobby");
HandleCheat(player, "Venting in lobby");
Logger.Fatal($"【{player.OwnerId}:{player.GetRealName()}】 attempted to vent in lobby.", "EAC_physics");
subReader.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(player, "Venting during meeting");
HandleCheat(player, "Venting during meeting");
Logger.Fatal($"【{player.OwnerId}:{player.GetRealName()}】 attempted to vent during a meeting.", "EAC_physics");
subReader.Recycle();
return true;
}
int ventid = subReader.ReadPackedInt32();
if (!HasVent(ventid))
@@ -565,6 +937,34 @@ internal static class EAC
case RpcCalls.ClimbLadder:
{
if (!gameStarted)
{
WarnHost();
Report(player, "Climbing ladder in lobby");
HandleCheat(player, "Climbing ladder in lobby");
Logger.Fatal($"【{player.OwnerId}:{player.GetRealName()}】 attempted to climb a ladder in lobby.", "EAC_physics");
subReader.Recycle();
return true;
}
if (GameManager.Instance.LogicOptions.MapId < 4)
{
WarnHost();
Report(player, "Climbing ladder on wrong map");
HandleCheat(player, "Climbing ladder on wrong map");
Logger.Fatal($"【{player.OwnerId}:{player.GetRealName()}】 attempted to climb a ladder on a map that does not have ladders.", "EAC_physics");
subReader.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(player, "Climbing ladder during meeting");
subReader.Recycle();
return true;
}
int ladderId = subReader.ReadPackedInt32();
if (!HasLadder(ladderId))
@@ -600,6 +1000,27 @@ internal static class EAC
return true;
}
goto case RpcCalls.CancelPet;
}
case RpcCalls.CancelPet:
{
if (player.inVent)
{
WarnHost();
Report(player, "Petting in vent");
subReader.Recycle();
return true;
}
if ((MeetingHud.Instance && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
{
WarnHost();
Report(player, "Petting during meeting");
subReader.Recycle();
return true;
}
break;
}
}
@@ -645,6 +1066,7 @@ internal static class EAC
{
case 0:
{
if (pc.IsTrusted()) break;
AmongUsClient.Instance.KickPlayer(pc.OwnerId, true);
string msg0 = string.Format(GetString("Message.BannedByEAC"), pc.Data?.PlayerName, text);
Logger.Warn(msg0, "EAC");
@@ -653,6 +1075,7 @@ internal static class EAC
}
case 1:
{
if (pc.IsTrusted()) break;
AmongUsClient.Instance.KickPlayer(pc.OwnerId, false);
string msg1 = string.Format(GetString("Message.KickedByEAC"), pc.Data?.PlayerName, text);
Logger.Warn(msg1, "EAC");
@@ -677,6 +1100,8 @@ internal static class EAC
}
case 5:
{
if (pc.IsTrusted()) break;
string hashedPuid = pc.GetClient().GetHashedPuid();
if (!BanManager.TempBanWhiteList.Contains(hashedPuid)) BanManager.TempBanWhiteList.Add(hashedPuid);
@@ -949,7 +1374,7 @@ internal static class CheckInvalidMovementPatch
public static void Postfix(PlayerControl __instance)
{
if (!GameStates.IsInTask || ExileController.Instance || !Options.EnableMovementChecking.GetBool() || Main.HasJustStarted || MeetingStates.FirstMeeting || Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod) >= 1.9f || AmongUsClient.Instance.Ping >= 300 || Options.CurrentGameMode == CustomGameMode.NaturalDisasters || Utils.GetRegionName() is not ("EU" or "NA" or "AS") || __instance == null || __instance.PlayerId >= 254 || !__instance.IsAlive() || __instance.inVent) return;
if (!GameStates.IsInTask || ExileController.Instance || !Options.EnableMovementChecking.GetBool() || Main.HasJustStarted || !Main.IntroDestroyed || MeetingStates.FirstMeeting || Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod) >= 1.9f || AmongUsClient.Instance.Ping >= 300 || Options.CurrentGameMode == CustomGameMode.NaturalDisasters || Utils.GetRegionName() is not ("EU" or "NA" or "AS") || __instance == null || __instance.PlayerId >= 254 || !__instance.IsAlive() || __instance.inVent) return;
Vector2 pos = __instance.Pos();
long now = Utils.TimeStamp;
+53 -46
View File
@@ -87,7 +87,7 @@ internal static class ExtendedPlayerControl
if (player.Is(CustomRoles.Trainee) && MeetingStates.FirstMeeting) return false;
if (player.Is(CustomRoles.Blocked) && player.GetClosestVent()?.Id != ventId) return false;
if (!GameStates.IsInTask || ExileController.Instance || AntiBlackout.SkipTasks) return false;
if (!GameStates.IsInTask || ExileController.Instance || AntiBlackout.SkipTasks || Main.Invisible.Contains(player.PlayerId)) return false;
if (player.inVent && player.GetClosestVent()?.Id == ventId) return true;
return (player.CanUseImpostorVentButton() || player.GetRoleTypes() == RoleTypes.Engineer) && Main.PlayerStates.Values.All(x => x.Role.CanUseVent(player, ventId));
}
@@ -1321,7 +1321,7 @@ internal static class ExtendedPlayerControl
return Main.PlayerStates.TryGetValue(pc.PlayerId, out PlayerState state) && state.Role.CanUseSabotage(pc);
}
// Next 8: From MoreGamemodes by Rabek009 (https://github.com/Rabek009/MoreGamemodes)
// Next 6: From MoreGamemodes by Rabek009 (https://github.com/Rabek009/MoreGamemodes)
public static Vector2 Pos(this PlayerControl pc)
{
@@ -1361,44 +1361,38 @@ internal static class ExtendedPlayerControl
}
}
public static void RpcMakeInvisibleModded(this PlayerControl player)
{
player.MakeInvisible();
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)CustomRPC.Invisibility, SendOption.Reliable);
writer.Write(true);
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
public static void RpcMakeVisibleModded(this PlayerControl player)
{
player.MakeVisible();
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)CustomRPC.Invisibility, SendOption.Reliable);
writer.Write(false);
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
public static void RpcMakeInvisible(this PlayerControl player, bool phantom = false)
{
if (!Main.Invisible.Add(player.PlayerId)) return;
if (phantom && Options.CurrentGameMode != CustomGameMode.Standard) return;
player.RpcSetPet("");
if (!phantom) player.RpcMakeInvisibleModded();
if (!phantom)
{
player.MakeInvisible();
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)CustomRPC.Invisibility, SendOption.Reliable);
writer.Write(true);
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
foreach (PlayerControl pc in PlayerControl.AllPlayerControls)
{
if (pc.AmOwner || pc == player || (!phantom && pc.IsModdedClient()) || (phantom && pc.IsImpostor())) continue;
var sender = CustomRpcSender.Create("RpcMakeInvisible", SendOption.Reliable);
sender.StartMessage(pc.GetClientId());
sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo)
sender.StartRpc(player.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write(player.NetTransform.lastSequenceId)
.EndRpc();
sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo)
sender.StartRpc(player.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write((ushort)(player.NetTransform.lastSequenceId + 16383))
.EndRpc();
sender.EndMessage();
sender.SendMessage();
NumSnapToCallsThisRound += 2;
}
Logger.Info($"Made {player.GetNameWithRole()} invisible", "RpcMakeInvisible");
@@ -1409,27 +1403,37 @@ internal static class ExtendedPlayerControl
if (!Main.Invisible.Remove(player.PlayerId)) return;
if (phantom && Options.CurrentGameMode != CustomGameMode.Standard) return;
if (Options.UsePets.GetBool()) PetsHelper.SetPet(player, PetsHelper.GetPetId());
if (!phantom) player.RpcMakeVisibleModded();
if (!phantom)
{
player.MakeVisible();
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)CustomRPC.Invisibility, SendOption.Reliable);
writer.Write(false);
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
foreach (PlayerControl pc in PlayerControl.AllPlayerControls)
{
if (pc.AmOwner || pc == player || (!phantom && pc.IsModdedClient()) || (phantom && pc.IsImpostor())) continue;
var sender = CustomRpcSender.Create("RpcMakeVisible", SendOption.Reliable);
sender.StartMessage(pc.GetClientId());
sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo)
sender.StartRpc(player.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write((ushort)(player.NetTransform.lastSequenceId + 32767))
.EndRpc();
sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo)
sender.StartRpc(player.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write((ushort)(player.NetTransform.lastSequenceId + 32767 + 16383))
.EndRpc();
sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo)
sender.StartRpc(player.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(player.transform.position)
.Write(player.NetTransform.lastSequenceId)
.EndRpc();
sender.EndMessage();
sender.SendMessage();
NumSnapToCallsThisRound += 3;
}
Logger.Info($"Made {player.GetNameWithRole()} visible", "RpcMakeVisible");
@@ -1443,35 +1447,38 @@ internal static class ExtendedPlayerControl
foreach (PlayerControl pc in PlayerControl.AllPlayerControls)
{
if (pc.AmOwner || pc == player || (!phantom && pc.IsModdedClient()) || (phantom && pc.IsImpostor())) continue;
var sender = CustomRpcSender.Create("RpcResetInvisibility", SendOption.Reliable);
sender.StartMessage(pc.GetClientId());
sender.StartRpc(player.NetId, (byte)RpcCalls.Exiled)
sender.StartRpc(player.NetId, RpcCalls.Exiled)
.EndRpc();
RoleTypes role = Utils.GetRoleMap(pc.PlayerId, player.PlayerId).RoleType;
sender.StartRpc(player.NetId, (byte)RpcCalls.SetRole)
sender.StartRpc(player.NetId, RpcCalls.SetRole)
.Write((ushort)role)
.Write(true)
.EndRpc();
sender.StartRpc(player.NetId, (byte)RpcCalls.CancelPet)
sender.StartRpc(player.NetId, RpcCalls.CancelPet)
.EndRpc();
sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo)
sender.StartRpc(player.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write((ushort)(player.NetTransform.lastSequenceId + 32767))
.EndRpc();
sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo)
sender.StartRpc(player.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write((ushort)(player.NetTransform.lastSequenceId + 32767 + 16383))
.EndRpc();
sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo)
sender.StartRpc(player.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write(player.NetTransform.lastSequenceId)
.EndRpc();
sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo)
sender.StartRpc(player.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write((ushort)(player.NetTransform.lastSequenceId + 16383))
.EndRpc();
sender.EndMessage();
sender.SendMessage();
NumSnapToCallsThisRound += 4;
}
Logger.Info($"Reset invisibility for {player.GetNameWithRole()}", "RpcResetInvisibility");
@@ -2053,21 +2060,21 @@ internal static class ExtendedPlayerControl
return self.protectedByGuardianId > -1;
}
public static bool IsTrusted(this PlayerControl pc)
{
if (pc.FriendCode.GetDevUser().up) return true;
if (ChatCommands.IsPlayerModerator(pc.FriendCode)) return true;
if (ChatCommands.IsPlayerVIP(pc.FriendCode)) return true;
if (PrivateTagManager.Tags.ContainsKey(pc.FriendCode)) return true;
ClientData client = pc.GetClient();
return client != null && FastDestroyableSingleton<FriendsListManager>.Instance.IsPlayerFriend(client.ProductUserId);
}
public static bool IsBeginner(this PlayerControl pc)
{
if (pc.IsModdedClient()) return false;
DevManager.TagInfo devUser = pc.FriendCode.GetDevUser();
if (devUser.HasTag() || devUser.up) return false;
if (ChatCommands.IsPlayerModerator(pc.FriendCode)) return false;
if (ChatCommands.IsPlayerVIP(pc.FriendCode)) return false;
if (Main.GamesPlayed.TryGetValue(pc.FriendCode, out int gamesPlayed) && gamesPlayed >= 4) return false;
ClientData client = pc.GetClient();
if (client == null) return true;
return !FastDestroyableSingleton<FriendsListManager>.Instance.IsPlayerFriend(client.ProductUserId);
if (pc.IsModdedClient() || pc.IsTrusted() || pc.FriendCode.GetDevUser().HasTag()) return false;
return !Main.GamesPlayed.TryGetValue(pc.FriendCode, out int gamesPlayed) || gamesPlayed < 4;
}
}
@@ -177,9 +177,11 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
case CustomGameMode.Speedrun:
case CustomGameMode.HotPotato:
case CustomGameMode.KingOfTheZones:
case CustomGameMode.BedWars:
SetMaxVision();
break;
case CustomGameMode.BedWars:
AURoleOptions.PhantomCooldown = 0.1f;
goto case CustomGameMode.RoomRush;
case CustomGameMode.Quiz:
try
{
+18 -8
View File
@@ -230,6 +230,23 @@ internal static class RPCHandlerPatch
return (CustomRPC)id is CustomRPC.VersionCheck or CustomRPC.RequestRetryVersionCheck or CustomRPC.AntiBlackout or CustomRPC.SyncNameNotify or CustomRPC.RequestSendMessage or CustomRPC.RequestCommandProcessing or CustomRPC.Judge or CustomRPC.SetNiceSwapperVotes or CustomRPC.MeetingKill or CustomRPC.Guess or CustomRPC.MafiaRevenge or CustomRPC.BAU or CustomRPC.FFAKill or CustomRPC.TMGSync or CustomRPC.ParityCopCommand;
}
private static bool CheckRateLimit(PlayerControl __instance, RpcCalls rpcType)
{
if (NumRPCsThisSecond.TryAdd(__instance.PlayerId, [])) LateTask.New(() => NumRPCsThisSecond.Remove(__instance.PlayerId), 1f, log: false);
Dictionary<RpcCalls, int> calls = NumRPCsThisSecond[__instance.PlayerId];
if (!calls.TryAdd(rpcType, 1)) calls[rpcType]++;
if (AmongUsClient.Instance.AmHost && !__instance.IsHost() && !(__instance.IsModdedClient() && rpcType == RpcCalls.SendChat) && (!RateLimitWhiteList.TryGetValue(__instance.PlayerId, out long expireTS) || expireTS < Utils.TimeStamp) && RpcRateLimit.TryGetValue(rpcType, out int limit) && calls[rpcType] > limit)
{
AmongUsClient.Instance.KickPlayer(__instance.OwnerId, false);
Logger.SendInGame(string.Format(GetString("Warning.TooManyRPCs"), __instance.Data?.PlayerName));
Logger.Warn($"Sent {calls[rpcType]} RPCs of type {rpcType} ({(byte)rpcType}), which exceeds the limit of {limit}. Kicking player.", "Kick");
return false;
}
return true;
}
public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] byte callId, [HarmonyArgument(1)] MessageReader reader)
{
var rpcType = (RpcCalls)callId;
@@ -247,15 +264,8 @@ internal static class RPCHandlerPatch
if (__instance != null)
{
if (NumRPCsThisSecond.TryAdd(__instance.PlayerId, [])) LateTask.New(() => NumRPCsThisSecond.Remove(__instance.PlayerId), 1f, log: false);
Dictionary<RpcCalls, int> calls = NumRPCsThisSecond[__instance.PlayerId];
if (!calls.TryAdd(rpcType, 1)) calls[rpcType]++;
if (AmongUsClient.Instance.AmHost && !__instance.IsHost() && !(__instance.IsModdedClient() && rpcType == RpcCalls.SendChat) && (!RateLimitWhiteList.TryGetValue(__instance.PlayerId, out long expireTS) || expireTS < Utils.TimeStamp) && RpcRateLimit.TryGetValue(rpcType, out int limit) && calls[rpcType] > limit)
if (!__instance.IsTrusted() && !CheckRateLimit(__instance, rpcType))
{
AmongUsClient.Instance.KickPlayer(__instance.OwnerId, false);
Logger.SendInGame(string.Format(GetString("Warning.TooManyRPCs"), __instance.Data?.PlayerName));
Logger.Warn($"Sent {calls[rpcType]} RPCs of type {rpcType} ({callId}), which exceeds the limit of {limit}. Kicking player.", "Kick");
subReader.Recycle();
return false;
}
+2 -2
View File
@@ -1135,13 +1135,13 @@ public static class GameSettingMenuPatch
var gmButton = Object.Instantiate(gMinus, gameSettingsLabel.transform, true);
gmButton.transform.localPosition = new Vector3((((index / 7) - ((totalCols - 1) / 2f)) * 1.4f) + 0.86f, gameSettingsLabelPos.y - 1.9f - (0.22f * (index % 7)), -1f);
gmButton.transform.localScale = new(russian ? 0.5f : 0.4f, russian ? 0.37f : 0.3f, 1f);
gmButton.transform.localScale = new(0.4f, 0.3f, 1f);
var gmButtonTmp = gmButton.transform.Find("FontPlacer/Text_TMP").GetComponent<TextMeshPro>();
gmButtonTmp.alignment = TextAlignmentOptions.Center;
gmButtonTmp.DestroyTranslator();
gmButtonTmp.text = Translator.GetString(gm.ToString()).ToUpper();
gmButtonTmp.color = Main.GameModeColors[gm];
gmButtonTmp.transform.localPosition = new(gameSettingsLabelPos.x + (!russian ? 3.35f : 3.65f), gameSettingsLabelPos.y - 1.62f, gameSettingsLabelPos.z);
gmButtonTmp.transform.localPosition = new(gameSettingsLabelPos.x + 3.35f, gameSettingsLabelPos.y - 1.62f, gameSettingsLabelPos.z);
gmButtonTmp.transform.localScale = new(1f, 1f, 1f);
var gmPassiveButton = gmButton.GetComponent<PassiveButton>();
+1 -2
View File
@@ -1037,9 +1037,7 @@ internal static class IntroCutsceneDestroyPatch
});
}, 2f, "FixKillCooldownTask");
}
else Utils.SyncAllSettings();
}
else Utils.SyncAllSettings();
}
switch (Options.CurrentGameMode)
@@ -1191,6 +1189,7 @@ internal static class IntroCutsceneDestroyPatch
if (AFKDetector.ActivateOnStart.GetBool()) LateTask.New(() => aapc.Do(AFKDetector.RecordPosition), 1f, log: false);
LateTask.New(() => Main.Instance.StartCoroutine(Utils.NotifyEveryoneAsync()), 3f, "NotifyEveryoneAsync On Game Start");
LateTask.New(Utils.SyncAllSettings, 0.5f, "SyncAllSettings On Game Start");
}
else
{
+1
View File
@@ -126,6 +126,7 @@ internal static class EndGamePatch
Main.GamesPlayed.AddRange(Main.AllPlayerControls.ToDictionary(x => x.FriendCode, _ => 0), false);
Main.GamesPlayed.AdjustAllValues(x => ++x);
Main.GotShieldAnimationInfoThisGame.Clear();
if (Main.GM.Value) Main.PlayerStates[PlayerControl.LocalPlayer.PlayerId].IsDead = false;
break;
case CustomGameMode.MoveAndStop:
Main.AllPlayerControls.Do(x => MoveAndStop.HasPlayed.Add(x.FriendCode));
+33 -5
View File
@@ -85,11 +85,34 @@ internal static class RpcMurderPlayerPatch
if (AmongUsClient.Instance.AmClient)
__instance.MurderPlayer(target, murderResultFlags);
MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(__instance.NetId, (byte)RpcCalls.MurderPlayer, SendOption.Reliable);
messageWriter.WriteNetObject(target);
messageWriter.Write((int)murderResultFlags);
AmongUsClient.Instance.FinishRpcImmediately(messageWriter);
var sender = CustomRpcSender.Create("RpcMurderPlayer", SendOption.Reliable);
if (Main.Invisible.Contains(target.PlayerId) && murderResultFlags == MurderResultFlags.Succeeded)
{
sender.AutoStartRpc(target.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write((ushort)(target.NetTransform.lastSequenceId + 16383))
.EndRpc();
sender.AutoStartRpc(target.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write((ushort)(target.NetTransform.lastSequenceId + 32767))
.EndRpc();
sender.AutoStartRpc(target.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(new Vector2(50f, 50f))
.Write((ushort)(target.NetTransform.lastSequenceId + 32767 + 16383))
.EndRpc();
sender.AutoStartRpc(target.NetTransform.NetId, RpcCalls.SnapTo)
.WriteVector2(target.transform.position)
.Write(target.NetTransform.lastSequenceId)
.EndRpc();
}
sender.AutoStartRpc(__instance.NetId, RpcCalls.MurderPlayer)
.WriteNetObject(target)
.Write((int)murderResultFlags)
.EndRpc();
sender.SendMessage();
return false;
}
}
@@ -1176,6 +1199,8 @@ internal static class ReportDeadBodyPatch
}
}
EAC.TimeSinceLastTaskCompletion.Clear();
Enigma.OnReportDeadBody(player, target);
Mediumshiper.OnReportDeadBody(target);
Mortician.OnReportDeadBody(player, target);
@@ -1274,7 +1299,10 @@ internal static class FixedUpdatePatch
CheckMurderPatch.Update(__instance.PlayerId);
if (AmongUsClient.Instance.AmHost && __instance.AmOwner)
{
CustomNetObject.FixedUpdate();
EAC.TimeSinceLastTaskCompletion.AdjustAllValues(f => f + Time.fixedDeltaTime);
}
byte id = __instance.PlayerId;
@@ -1316,7 +1344,7 @@ internal static class FixedUpdatePatch
GhostRolesManager.AssignGhostRole(__instance);
}
if (GameStates.InGame && Options.DontUpdateDeadPlayers.GetBool() && !__instance.IsAlive() && !__instance.GetCustomRole().NeedsUpdateAfterDeath() && Options.CurrentGameMode is not CustomGameMode.RoomRush and not CustomGameMode.Quiz)
if (GameStates.InGame && Options.DontUpdateDeadPlayers.GetBool() && !(__instance.IsHost() && __instance.IsLocalPlayer()) && !__instance.IsAlive() && !__instance.GetCustomRole().NeedsUpdateAfterDeath() && Options.CurrentGameMode is not CustomGameMode.RoomRush and not CustomGameMode.Quiz)
{
int buffer = Options.DeepLowLoad.GetBool() ? 150 : 60;
DeadBufferTime.TryAdd(id, buffer);
+2 -2
View File
@@ -721,7 +721,7 @@
"EraserInfoLong": "(Impostors):\\nDuring the meeting, Eraser can vote for any crew member as a target. The selected crew member's roles will be erased after the meeting concludes. It is important to note that players whose roles have been erased will always be considered as vanilla roles, including the game result page.",
"OverKillerInfoLong": "(Impostors):\\nIt can be difficult to identify other dead bodies when reporting a Butcher kill, as there may be multiple dead bodies on the targets. This is due to the implementation principle, which requires the killed target to repeatedly display the animation of being killed and prevents them from participating in the meeting normally during this period. Additionally, if the Butcher kills the Avenger, the Avenger will seek revenge on everyone in anger.",
"HangmanInfoLong": "(Impostors):\\nThe Hangman kills by strangling during shapeshifting, ignoring all target abilities. The strangled player doesn't leave a body or trigger abilities, including Veteran, Lucky, and Seer. Additional roles are also affected.",
"SwooperInfoLong": "(Impostors):\\nThe Swooper can Vent to temporarily turn invisible and vent again to cancel their invisibility phase. This means the Swooper can never vent normally, even when swooping is on cooldown. (Note: The game will forcibly make you turn visible when the swoop duration ends, no matter if you vent or not.)",
"SwooperInfoLong": "(Impostors):\\nThe Swooper can Vent/Vanish to temporarily turn invisible. You can kill while being invisible, unlike vanilla Phantom.",
"CrewpostorInfoLong": "(Team Impostor):\\nYou kill the nearest player whenever you complete a task.",
"WildlingInfoLong": "(Impostors):\\nAs the Wildling, you can shapeshift but cannot vent. When you kill, you temporarily become immune to attacks.",
"TricksterInfoLong": "(Impostors):\\nAs the Trickster, you function as a regular Impostor but with one key difference. You appear crewmate to crewmate roles.\\n\\nThe Sheriff cannot kill you.\\nPsychic does not see you as evil.\\nSnitch cannot find you.",
@@ -819,7 +819,7 @@
"TotocalcioInfoLong": "(Neutrals):\\nThe Follower can start following someone by using their Kill button and switch targets by using it again. If the Follower's target wins, the Follower will win, too. They can even win after they die.",
"SuccubusInfoLong": "(Neutrals):\\nIf there are more Cultists than Crewmates and only these two teams remain, the Cultist team wins. The Cultist can charm a non-neutral player by pressing the kill button, making them a part of the Cultist team with new victory conditions.",
"HexMasterInfoLong": "(Neutrals):\\nAs the Hex Master, you can hex players or kill them. Hexing a player works the same as spelling as a Witch.",
"WraithInfoLong": "(Neutrals):\\nAs the Wraith, you can use vents to go invisible temporarily. Trying to vent while invisible will cause you to become visible again.\\n\\nKill; everyone to win.",
"WraithInfoLong": "(Neutrals):\\nAs the Wraith, you can Vent/Vanish to go invisible temporarily. You can kill while being invisible.\\n\\nKill; everyone to win.",
"NSerialKillerInfoLong": "(Neutrals):\\nAs the Serial Killer, your job is to murder everyone to win simply.",
"JuggernautInfoLong": "(Neutrals):\\nAs the Juggernaut, your kill cooldown decreases with each kill you make.\\n\\nKill everyone to win.",
"VirusInfoLong": "(Neutrals):\\nThe virus' goal is to eliminate other players. When a crewmate is killed, their corpse is infected, and the reporting crewmate joins the virus team or dies if the Virus isn't voted out. The Virus team wins if they outnumber the Crewmate team.",
+19 -3
View File
@@ -125,9 +125,25 @@ internal class Assassin : RoleBase
{
if (killer.IsShifted())
{
killer.ResetKillCooldown();
killer.SyncSettings();
return CanUseKillButton(killer);
bool canUseKillButton = CanUseKillButton(killer);
if (canUseKillButton)
{
killer.ResetKillCooldown();
killer.SyncSettings();
if (Main.Invisible.Contains(killer.PlayerId) && !target.Is(CustomRoles.Bait))
{
if (!killer.RpcCheckAndMurder(target, true)) return false;
RPC.PlaySoundRPC(killer.PlayerId, Sounds.KillSound);
target.Suicide(PlayerState.DeathReason.Swooped, killer);
killer.SetKillCooldown();
return false;
}
}
return canUseKillButton;
}
MarkedPlayer = target.PlayerId;
+16 -10
View File
@@ -177,7 +177,9 @@ public class Swooper : RoleBase
if (lastTime + (long)Cooldown < now)
{
lastTime = -10;
if (!player.IsModdedClient()) player.Notify(GetString("SwooperCanVent"), 10f);
if (!player.IsModdedClient() && !(UsedRole != CustomRoles.Chameleon && UsePhantomBasis.GetBool() && (UsedRole != CustomRoles.Wraith || UsePhantomBasisForNKs.GetBool())))
player.Notify(GetString("SwooperCanVent"), 10f);
SendRPC();
CD = 0;
@@ -193,8 +195,15 @@ public class Swooper : RoleBase
{
case < 0:
lastTime = now;
int ventId = ventedId == -10 ? Main.LastEnteredVent[player.PlayerId].Id : ventedId;
Main.AllPlayerControls.Without(player).Do(x => player.MyPhysics.RpcExitVentDesync(ventId, x));
if (UsedRole == CustomRoles.Chameleon)
{
int ventId = ventedId == -10 ? Main.LastEnteredVent[player.PlayerId].Id : ventedId;
Main.AllPlayerControls.Without(player).Do(x => player.MyPhysics.RpcExitVentDesync(ventId, x));
}
else
player.RpcMakeVisible();
player.Notify(GetString("SwooperInvisStateOut"));
player.RpcResetAbilityCooldown();
InvisTime = -10;
@@ -209,7 +218,7 @@ public class Swooper : RoleBase
bool OnCoEnterVent(PlayerPhysics __instance, int ventId)
{
if (!AmongUsClient.Instance.AmHost || IsInvis) return false;
if (!AmongUsClient.Instance.AmHost || IsInvis || (UsedRole != CustomRoles.Chameleon && UsePhantomBasis.GetBool() && (UsedRole != CustomRoles.Wraith || UsePhantomBasisForNKs.GetBool()))) return false;
PlayerControl pc = __instance.myPlayer;
@@ -262,10 +271,8 @@ public class Swooper : RoleBase
if (CanGoInvis && (wraith || limit >= 1))
{
int? ventId = pc.GetClosestVent()?.Id;
Main.AllPlayerControls.Without(pc).Do(x => pc.MyPhysics.RpcEnterVentDesync(ventId ?? 2, x));
ventedId = ventId ?? -10;
pc.RpcMakeInvisible();
InvisTime = Utils.TimeStamp;
if (!wraith) pc.RpcRemoveAbilityUse();
@@ -292,7 +299,7 @@ public class Swooper : RoleBase
long cooldown = lastTime + (long)Cooldown - Utils.TimeStamp;
str.Append(string.Format(GetString("SwooperInvisCooldownRemain"), cooldown + 1));
}
else
else if (!(UsedRole != CustomRoles.Chameleon && UsePhantomBasis.GetBool() && (UsedRole != CustomRoles.Wraith || UsePhantomBasisForNKs.GetBool())))
str.Append(GetString("SwooperCanVent"));
return str.ToString();
@@ -300,7 +307,6 @@ public class Swooper : RoleBase
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (Medic.ProtectList.Contains(target.PlayerId)) return false;
if (target.Is(CustomRoles.Bait)) return true;
if (!IsInvis) return true;
if (!killer.RpcCheckAndMurder(target, true)) return false;