diff --git a/GameMode/CustomHideAndSeekManager.cs b/GameMode/CustomHideAndSeekManager.cs index b398b5efe..496151f5c 100644 --- a/GameMode/CustomHideAndSeekManager.cs +++ b/GameMode/CustomHideAndSeekManager.cs @@ -88,8 +88,6 @@ namespace EHR .Where(x => x.Interface.Team == Team.Impostor) .Do(x => x.pc.MarkDirtySettings()); }, Seeker.BlindTime.GetFloat() + 8f, "Blind Time Expire"); - - AssignRoles(); } public static List GetAllHnsRoles(IEnumerable types) @@ -109,7 +107,7 @@ namespace EHR .ToArray(); } - private static void AssignRoles() + public static void AssignRoles() { Dictionary result = []; List allPlayers = [.. Main.AllAlivePlayerControls]; @@ -199,6 +197,8 @@ namespace EHR .Where(x => x != null) .ToDictionary(x => x.GetType().Name, x => x); PlayerRoles = result.ToDictionary(x => x.Key.PlayerId, x => (roleInterfaces[x.Value.ToString()], x.Value)); + + result.IntersectBy(Main.PlayerStates.Keys, x => x.Key.PlayerId).Do(x => x.Key.RpcSetCustomRole(x.Value)); } public static void ApplyGameOptions(IGameOptions opt, PlayerControl pc) @@ -343,11 +343,18 @@ namespace EHR name = Utils.ColorString(Main.PlayerColors.GetValueOrDefault(state.Key, Color.white), name); bool isSeeker = PlayerRoles[state.Key].Interface.Team == Team.Impostor; bool alive = !state.Value.IsDead; - string stateText; - if (isSeeker) stateText = $" ({Utils.ColorString(Utils.GetRoleColor(CustomRoles.Seeker), Translator.GetString("Seeker"))})"; - else stateText = alive ? string.Empty : " <#ff1313>DEAD"; + + TaskState ts = state.Value.TaskState; + string stateText = string.Empty; + if (PlayersSeeRoles.GetBool()) stateText = $" ({GetRole()}){GetTaskCount()}"; + else if (isSeeker) stateText = $" ({CustomRoles.Seeker.ToString()})"; + if (!alive) stateText += $" {Translator.GetString("Dead")}"; + stateText = $"{name}{stateText}"; return stateText; + + CustomRoles GetRole() => state.Value.MainRole == CustomRoles.Agent ? CustomRoles.Hider : state.Value.MainRole; + string GetTaskCount() => CustomRoles.Agent.IsEnable() || !ts.hasTasks ? string.Empty : $" ({ts.CompletedTasksCount}/{ts.AllTasksCount})"; } } @@ -375,7 +382,7 @@ namespace EHR { public static void Postfix(PlayerControl __instance) { - if (!AmongUsClient.Instance.AmHost || Options.CurrentGameMode != CustomGameMode.HideAndSeek) return; + if (!AmongUsClient.Instance.AmHost || !GameStates.IsInTask || Options.CurrentGameMode != CustomGameMode.HideAndSeek) return; long now = Utils.TimeStamp; if (LastUpdate == now) return; diff --git a/Modules/AntiBlackout.cs b/Modules/AntiBlackout.cs index 9a658de46..0f02dac0d 100644 --- a/Modules/AntiBlackout.cs +++ b/Modules/AntiBlackout.cs @@ -9,6 +9,9 @@ namespace EHR; public static class AntiBlackout { + private static Dictionary IsDeadCache = []; + private static readonly LogHandler Logger = EHR.Logger.Handler("AntiBlackout"); + /// ///Whether to override the ejection process /// @@ -44,9 +47,7 @@ public static class AntiBlackout } } - public static bool IsCached { get; private set; } - private static Dictionary isDeadCache = []; - private static readonly LogHandler Logger = EHR.Logger.Handler("AntiBlackout"); + private static bool IsCached { get; set; } public static void SetIsDead(bool doSend = true, [CallerMemberName] string callerMethodName = "") { @@ -57,11 +58,11 @@ public static class AntiBlackout return; } - isDeadCache.Clear(); + IsDeadCache.Clear(); foreach (var info in GameData.Instance.AllPlayers) { if (info == null) continue; - isDeadCache[info.PlayerId] = (info.IsDead, info.Disconnected); + IsDeadCache[info.PlayerId] = (info.IsDead, info.Disconnected); info.IsDead = false; info.Disconnected = false; } @@ -76,14 +77,14 @@ public static class AntiBlackout foreach (var info in GameData.Instance.AllPlayers) { if (info == null) continue; - if (isDeadCache.TryGetValue(info.PlayerId, out var val)) + if (IsDeadCache.TryGetValue(info.PlayerId, out var val)) { info.IsDead = val.isDead; info.Disconnected = val.Disconnected; } } - isDeadCache.Clear(); + IsDeadCache.Clear(); IsCached = false; if (doSend) SendGameData(); } @@ -113,7 +114,7 @@ public static class AntiBlackout { // Execution conditions: client is host, IsDead is overwritten, player is disconnected if (!AmongUsClient.Instance.AmHost || !IsCached || !player.Disconnected) return; - isDeadCache[player.PlayerId] = (true, true); + IsDeadCache[player.PlayerId] = (true, true); player.IsDead = player.Disconnected = false; SendGameData(); } @@ -149,8 +150,8 @@ public static class AntiBlackout public static void Reset() { Logger.Info("==Reset=="); - isDeadCache ??= []; - isDeadCache.Clear(); + IsDeadCache ??= []; + IsDeadCache.Clear(); IsCached = false; } } \ No newline at end of file diff --git a/Modules/CustomRoleSelector.cs b/Modules/CustomRoleSelector.cs index a7a9c0830..368f661e1 100644 --- a/Modules/CustomRoleSelector.cs +++ b/Modules/CustomRoleSelector.cs @@ -78,6 +78,7 @@ internal static class CustomRoleSelector AssignRoleToEveryone(CustomRoles.Potato); return; case CustomGameMode.HideAndSeek: + CustomHideAndSeekManager.AssignRoles(); RoleResult = CustomHideAndSeekManager.PlayerRoles.ToDictionary(x => Utils.GetPlayerById(x.Key), x => x.Value.Role); return; } @@ -611,7 +612,7 @@ internal static class CustomRoleSelector public static void SelectAddonRoles() { - if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop) return; + if (Options.CurrentGameMode is CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HideAndSeek) return; foreach (var id in Main.SetAddOns.Keys.Where(id => Utils.GetPlayerById(id) == null).ToArray()) Main.SetAddOns.Remove(id); @@ -640,25 +641,13 @@ internal static class CustomRoleSelector Crewmate } - public class RoleAssignInfo(CustomRoles role, int spawnChance, int maxCount, int assignedCount = 0) + private class RoleAssignInfo(CustomRoles role, int spawnChance, int maxCount, int assignedCount = 0) { - public CustomRoles Role - { - get => role; - set => role = value; - } + public CustomRoles Role => role; - public int SpawnChance - { - get => spawnChance; - set => spawnChance = value; - } + public int SpawnChance => spawnChance; - public int MaxCount - { - get => maxCount; - set => maxCount = value; - } + public int MaxCount => maxCount; public int AssignedCount { diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 222a62aa3..94cfad3ad 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -82,7 +82,7 @@ internal static class CustomRolesHelper CustomRoles.Lawyer => CustomRoles.Crewmate, CustomRoles.NiceSwapper => CustomRoles.Crewmate, CustomRoles.Ignitor => CustomRoles.Crewmate, - CustomRoles.Jailor => CustomRoles.Impostor, + CustomRoles.Jailor => UsePets && Jailor.UsePet.GetBool() ? CustomRoles.Crewmate : CustomRoles.Impostor, CustomRoles.Vampire => CustomRoles.Impostor, CustomRoles.BountyHunter => CustomRoles.Impostor, CustomRoles.Trickster => CustomRoles.Impostor, @@ -258,9 +258,20 @@ internal static class CustomRolesHelper CustomRoles.Silencer => Silencer.SilenceMode.GetValue() == 1 ? CustomRoles.Shapeshifter : CustomRoles.Impostor, // Hide And Seek + CustomRoles.Hider => CustomRoles.Crewmate, + CustomRoles.Seeker => CustomRoles.Impostor, + CustomRoles.Fox => CustomRoles.Crewmate, + CustomRoles.Troll => CustomRoles.Crewmate, CustomRoles.Jumper => CustomRoles.Engineer, + CustomRoles.Detector => CustomRoles.Crewmate, + CustomRoles.Jet => CustomRoles.Crewmate, + CustomRoles.Dasher => CustomRoles.Impostor, + CustomRoles.Locator => CustomRoles.Impostor, + CustomRoles.Venter => CustomRoles.Impostor, + CustomRoles.Agent => CustomRoles.Impostor, + CustomRoles.Taskinator => CustomRoles.Crewmate, - _ => role.IsImpostor() ? CustomRoles.Impostor : CustomRoles.Crewmate, + _ => role.IsImpostor() ? CustomRoles.Impostor : CustomRoles.Crewmate }; } @@ -294,18 +305,6 @@ internal static class CustomRolesHelper CustomRoles.Tasker => RoleTypes.Crewmate, // Hot Potato CustomRoles.Potato => RoleTypes.Crewmate, - // Hide And Seek - CustomRoles.Hider => RoleTypes.Crewmate, - CustomRoles.Seeker => RoleTypes.Impostor, - CustomRoles.Fox => RoleTypes.Crewmate, - CustomRoles.Troll => RoleTypes.Crewmate, - CustomRoles.Detector => RoleTypes.Crewmate, - CustomRoles.Jet => RoleTypes.Crewmate, - CustomRoles.Dasher => RoleTypes.Impostor, - CustomRoles.Locator => RoleTypes.Impostor, - CustomRoles.Venter => RoleTypes.Impostor, - CustomRoles.Agent => RoleTypes.Impostor, - CustomRoles.Taskinator => RoleTypes.Crewmate, // Standard CustomRoles.Executioner => Executioner.CRoleChangeRoles[Executioner.ChangeRolesAfterTargetKilled.GetValue()].GetDYRole(), CustomRoles.Sheriff => UsePets && Sheriff.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor, diff --git a/Modules/EAC.cs b/Modules/EAC.cs index ff6f81d26..b54680805 100644 --- a/Modules/EAC.cs +++ b/Modules/EAC.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using AmongUs.GameOptions; @@ -10,11 +9,11 @@ using static EHR.Translator; namespace EHR; -internal class EAC +internal static class EAC { public static int DeNum; - public static void WarnHost(int denum = 1) + private static void WarnHost(int denum = 1) { DeNum += denum; if (ErrorText.Instance != null) @@ -120,9 +119,7 @@ internal class EAC return true; } - if (pc.Data.DefaultOutfit.ColorId != -1 && - (Main.AllPlayerControls.Count(x => x.Data.DefaultOutfit.ColorId == color) >= 5 - || color < 0 || color > 18)) + if (pc.Data.DefaultOutfit.ColorId != -1 && (Main.AllPlayerControls.Count(x => x.Data.DefaultOutfit.ColorId == color) >= 5 || color > 18)) { WarnHost(); Report(pc, "Illegal color setting"); @@ -180,12 +177,56 @@ internal class EAC switch (callId) { - case 101: - var AUMChat = sr.ReadString(); - WarnHost(); - Report(pc, "AUM"); - HandleCheat(pc, GetString("EAC.CheatDetected.EAC")); - return true; + case 101: // Aum Chat + try + { + var firstString = reader.ReadString(); + var secondString = reader.ReadString(); + reader.ReadInt32(); + + var flag = string.IsNullOrEmpty(firstString) && string.IsNullOrEmpty(secondString); + + if (!flag) + { + Report(pc, "Aum Chat RPC"); + HandleCheat(pc, "Aum Chat RPC"); + Logger.Fatal($"Player [{pc.GetClientId()}:{pc.GetRealName()}] sent an AUM chat, which was rejected", "EAC"); + return true; + } + } + catch + { + } + + break; + case unchecked((byte)42069): // 85 AUM + try + { + var aumid = reader.ReadByte(); + + if (aumid == pc.PlayerId) + { + Report(pc, "Aum RPC"); + HandleCheat(pc, "Aum RPC"); + Logger.Fatal($"Player [{pc.GetClientId()}:{pc.GetRealName()}] sent AUM RPC, which was rejected", "EAC"); + return true; + } + } + catch + { + } + + break; + case unchecked((byte)420): // 164 Sicko + if (reader.BytesRemaining == 0) + { + Report(pc, "Sicko RPC"); + HandleCheat(pc, "Sicko RPC"); + Logger.Fatal($"Player [{pc.GetClientId()}:{pc.GetRealName()}] sent Sicko RPC, which was rejected", "EAC"); + return true; + } + + break; case 7: case 8: if (!GameStates.IsLobby) @@ -198,7 +239,7 @@ internal class EAC break; case 5: - string name = sr.ReadString(); + sr.ReadString(); if (GameStates.IsInGame) { WarnHost(); @@ -282,32 +323,14 @@ internal class EAC return false; } - public static Dictionary OriginalRoles = []; - - public static void LogAllRoles() - { - foreach (var pc in Main.AllPlayerControls.ToArray()) - { - try - { - OriginalRoles.Add(pc.PlayerId, pc.GetCustomRole()); - } - catch (Exception error) - { - Logger.Fatal(error.ToString(), "EAC.LogAllRoles"); - } - } - } - public static bool RpcUpdateSystemCheck(PlayerControl player, SystemTypes systemType, byte amount) { - //Update system rpc can not get received by playercontrol.handlerpc - var Mapid = Main.NormalOptions.MapId; - Logger.Info("Check sabotage RPC" + ", PlayerName: " + player.GetNameWithRole() + ", SabotageType: " + systemType + ", amount: " + amount, "EAC"); + // UpdateSystem RPC cannot be received by PlayerControl.HandleRpc if (!AmongUsClient.Instance.AmHost) return false; - switch (systemType) //Normal sabotage using buttons + var Mapid = Main.NormalOptions.MapId; + switch (systemType) { - case SystemTypes.Sabotage: + case SystemTypes.Sabotage: // Normal sabotage using buttons if (!player.HasKillButton() && !player.CanUseSabotage()) { WarnHost(); @@ -342,7 +365,7 @@ internal class EAC break; case SystemTypes.Electrical: if (Mapid == 5) goto YesCheat; - if (amount >= 5) //0 - 4 normal lights. other sabotage, Should never be sent by client + if (amount >= 5) // 0 - 4 normal lights. Other sabotage should never be sent by client { goto YesCheat; } @@ -350,21 +373,22 @@ internal class EAC break; case SystemTypes.Laboratory: if (Mapid != 2) goto YesCheat; - if (!(amount is 64 or 65 or 32 or 33)) goto YesCheat; + if (amount is not (64 or 65 or 32 or 33)) goto YesCheat; break; case SystemTypes.Reactor: if (Mapid is 2 or 4) goto YesCheat; - if (!(amount is 64 or 65 or 32 or 33)) goto YesCheat; - //Airship use heli sabotage /Other use 64,65 | 32,33 + if (amount is not (64 or 65 or 32 or 33)) goto YesCheat; + // Airship uses heli sabotage / Other maps use 64,65 | 32,33 break; case SystemTypes.HeliSabotage: if (Mapid != 4) goto YesCheat; - if (!(amount is 64 or 65 or 16 or 17 or 32 or 33)) goto YesCheat; + if (amount is not (64 or 65 or 16 or 17 or 32 or 33)) goto YesCheat; break; case SystemTypes.MushroomMixupSabotage: goto YesCheat; } + // There may be cases where a player is fixing reactor and a meeting starts, triggering EAC check in a meeting if ((GameStates.IsMeeting && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance != null) { WarnHost(); @@ -372,7 +396,6 @@ internal class EAC Logger.Fatal($"Player【{player.GetClientId()}:{player.GetRealName()}】Bad Sabotage D, rejected", "EAC"); return true; } - //There may be cases where a player is fixing reactor and a meeting start, triggering EAC check in meeting return false; @@ -386,12 +409,12 @@ internal class EAC } } - public static void Report(PlayerControl pc, string reason) + private static void Report(PlayerControl pc, string reason) { string msg = $"{pc.GetClientId()}|{pc.FriendCode}|{pc.Data.PlayerName}|{pc.GetClient().GetHashedPuid()}|{reason}"; //Cloud.SendData(msg); Logger.Fatal($"EAC report: {msg}", "EAC Cloud"); - if (Options.CheatResponses.GetInt() != 5) Logger.SendInGame(string.Format(GetString("Message.NoticeByEAC"), $"{pc?.Data?.PlayerName} | {pc.GetClient().GetHashedPuid()}", reason)); + if (Options.CheatResponses.GetInt() != 5) Logger.SendInGame(string.Format(GetString("Message.NoticeByEAC"), $"{pc.Data?.PlayerName} | {pc.GetClient().GetHashedPuid()}", reason)); } public static bool ReceiveInvalidRpc(PlayerControl pc, byte callId) @@ -407,7 +430,7 @@ internal class EAC return true; } - public static void HandleCheat(PlayerControl pc, string text) + private static void HandleCheat(PlayerControl pc, string text) { switch (Options.CheatResponses.GetInt()) { @@ -427,8 +450,16 @@ internal class EAC Utils.SendMessage(string.Format(GetString("Message.NoticeByEAC"), pc?.Data?.PlayerName, text), PlayerControl.LocalPlayer.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Impostor), GetString("MessageFromEAC"))); break; case 3: - foreach (var apc in Main.AllPlayerControls.Where(x => x.PlayerId != pc?.Data?.PlayerId).ToArray()) - Utils.SendMessage(string.Format(GetString("Message.NoticeByEAC"), pc?.Data?.PlayerName, text), pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Impostor), GetString("MessageFromEAC"))); + foreach (var player in Main.AllPlayerControls) + { + if (player.PlayerId != pc?.Data?.PlayerId) + { + var message = string.Format(GetString("Message.NoticeByEAC"), pc?.Data?.PlayerName, text); + var title = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Impostor), GetString("MessageFromEAC")); + Utils.SendMessage(message, player.PlayerId, title); + } + } + break; case 4: if (!BanManager.TempBanWhiteList.Contains(pc.GetClient().GetHashedPuid())) diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 3f0cf0e32..ccac1ba93 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -606,6 +606,10 @@ static class ExtendedPlayerControl } } + public static bool IsRoleBlocked(this PlayerControl pc) => RoleBlockManager.RoleBlockedPlayers.ContainsKey(pc.PlayerId); + public static bool IsPlayerRoleBlocked(this byte id) => RoleBlockManager.RoleBlockedPlayers.ContainsKey(id); + public static void BlockRole(this PlayerControl pc, float duration) => RoleBlockManager.AddRoleBlock(pc, duration); + public static bool HasKillButton(this PlayerControl pc) { CustomRoles role = pc.GetCustomRole(); diff --git a/Modules/LocateArrow.cs b/Modules/LocateArrow.cs index 7f82df04c..60ec93f1b 100644 --- a/Modules/LocateArrow.cs +++ b/Modules/LocateArrow.cs @@ -1,28 +1,17 @@ using System.Collections.Generic; using System.Linq; +using EHR.Modules; +using Hazel; using UnityEngine; namespace EHR; static class LocateArrow { - class ArrowInfo(byte from, Vector3 to) - { - public byte From = from; - public Vector3 To = to; - - public bool Equals(ArrowInfo obj) - { - return From == obj.From && To == obj.To; - } - public override string ToString() - { - return $"(From:{From} To:{To})"; - } - } - static readonly Dictionary LocateArrows = []; - static readonly string[] Arrows = [ + + static readonly string[] Arrows = + [ "↑", "↗", "→", @@ -38,23 +27,43 @@ static class LocateArrow { LocateArrows.Clear(); } + + public static void ReceiveRPC(MessageReader reader) + { + switch (reader.ReadPackedInt32()) + { + case 1: + Add(reader.ReadByte(), Utils.ReadVector3(reader)); + break; + case 2: + Remove(reader.ReadByte(), Utils.ReadVector3(reader)); + break; + case 3: + RemoveAllTarget(reader.ReadByte()); + break; + } + } + /// /// Register a new target arrow object /// /// - /// - /// + /// public static void Add(byte seer, Vector3 locate) { var arrowInfo = new ArrowInfo(seer, locate); if (!LocateArrows.Any(a => a.Key.Equals(arrowInfo))) + { LocateArrows[arrowInfo] = "・"; + Utils.SendRPC(CustomRPC.Arrow, false, 1, seer, locate); + } } + /// /// Delete target /// /// - /// + /// public static void Remove(byte seer, Vector3 locate) { var arrowInfo = new ArrowInfo(seer, locate); @@ -63,7 +72,10 @@ static class LocateArrow { LocateArrows.Remove(a); } + + Utils.SendRPC(CustomRPC.Arrow, false, 2, seer, locate); } + /// /// Delete all targets for the specified seer /// @@ -75,7 +87,10 @@ static class LocateArrow { LocateArrows.Remove(arrowInfo); } + + Utils.SendRPC(CustomRPC.Arrow, false, 3, seer); } + /// /// Get all visible target arrows /// @@ -85,6 +100,7 @@ static class LocateArrow { return LocateArrows.Keys.Where(ai => ai.From == seer.PlayerId).Aggregate(string.Empty, (current, arrowInfo) => current + LocateArrows[arrowInfo]); } + /// /// Check target arrow every FixedUpdate /// Issue NotifyRoles when there are updates @@ -110,6 +126,7 @@ static class LocateArrow update = true; continue; } + // Take the direction vector of the target var dir = loc - seer.transform.position; int index; @@ -127,6 +144,7 @@ static class LocateArrow var angle = Vector3.SignedAngle(Vector3.down, dir, Vector3.back) + 180 + 22.5; index = ((int)(angle / 45)) % 8; } + var arrow = Arrows[index]; if (LocateArrows[arrowInfo] != arrow) { @@ -134,9 +152,26 @@ static class LocateArrow update = true; } } + if (update) { Utils.NotifyRoles(SpecifySeer: seer, ForceLoop: false, SpecifyTarget: seer); } } -} + + class ArrowInfo(byte from, Vector3 to) + { + public byte From = from; + public Vector3 To = to; + + public bool Equals(ArrowInfo obj) + { + return From == obj.From && To == obj.To; + } + + public override string ToString() + { + return $"(From:{From} To:{To})"; + } + } +} \ No newline at end of file diff --git a/Modules/NameColorManager.cs b/Modules/NameColorManager.cs index 0e52bd5b8..a7e30aa52 100644 --- a/Modules/NameColorManager.cs +++ b/Modules/NameColorManager.cs @@ -132,9 +132,7 @@ public static class NameColorManager CustomRoles.HeadHunter when ((HeadHunter)Main.PlayerStates[seer.PlayerId].Role).Targets.Contains(target.PlayerId) => "000000", CustomRoles.BountyHunter when (Main.PlayerStates[seer.PlayerId].Role as BountyHunter)?.GetTarget(seer) == target.PlayerId => "000000", CustomRoles.Pyromaniac when ((Pyromaniac)Main.PlayerStates[seer.PlayerId].Role).DousedList.Contains(target.PlayerId) => "#BA4A00", - CustomRoles.Glitch when Glitch.hackedIdList.ContainsKey(target.PlayerId) => Main.RoleColors[CustomRoles.Glitch], - CustomRoles.Escort when Glitch.hackedIdList.ContainsKey(target.PlayerId) => Main.RoleColors[CustomRoles.Escort], - CustomRoles.Consort when Glitch.hackedIdList.ContainsKey(target.PlayerId) => Main.RoleColors[CustomRoles.Glitch], + CustomRoles.Glitch when target.IsRoleBlocked() => Main.RoleColors[seerRole], CustomRoles.Aid when Aid.ShieldedPlayers.ContainsKey(target.PlayerId) => Main.RoleColors[CustomRoles.Aid], CustomRoles.Spy when Spy.SpyRedNameList.ContainsKey(target.PlayerId) => "#BA4A00", CustomRoles.Mastermind when Mastermind.ManipulateDelays.ContainsKey(target.PlayerId) => "#00ffa5", diff --git a/Modules/RPC.cs b/Modules/RPC.cs index 3bc1e2f75..4e1887842 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -51,6 +51,7 @@ public enum CustomRPC KillFlash, SyncAbilityUseLimit, RemoveSubRole, + Arrow, // Roles SetDrawPlayer, @@ -97,8 +98,6 @@ public enum CustomRPC SetMarkedPlayer, SetMedicalerProtectList, SyncPsychicRedList, - SetMorticianArrow, - SetTracefinderArrow, SetCleanserCleanLimit, SetJailorTarget, SetDoppelgangerStealLimit, @@ -118,8 +117,6 @@ public enum CustomRPC SyncVengefulRomanticTarget, SetRevealedPlayer, SetCurrentRevealTarget, - SetBloodhoundArrow, - SetVultureArrow, SetDoomsayerProgress, SetTrackerTarget, RpcPassBomb, @@ -340,6 +337,10 @@ internal class RPCHandlerPatch case CustomRPC.RemoveSubRole: Main.PlayerStates[reader.ReadByte()].RemoveSubRole((CustomRoles)reader.ReadPackedInt32()); break; + case CustomRPC.Arrow: + if (reader.ReadBoolean()) TargetArrow.ReceiveRPC(reader); + else LocateArrow.ReceiveRPC(reader); + break; case CustomRPC.SyncPostman: { byte id = reader.ReadByte(); @@ -613,12 +614,6 @@ internal class RPCHandlerPatch (Main.PlayerStates[id].Role as Mafioso)?.ReceiveRPCSyncPistolCD(reader); } break; - case CustomRPC.SetMorticianArrow: - Mortician.ReceiveRPC(reader); - break; - case CustomRPC.SetTracefinderArrow: - Tracefinder.ReceiveRPC(reader); - break; case CustomRPC.SyncNameNotify: NameNotifyManager.ReceiveRPC(reader); break; @@ -634,9 +629,6 @@ internal class RPCHandlerPatch case CustomRPC.MafiaRevenge: Mafia.ReceiveRPC(reader, __instance); break; - //case CustomRPC.RetributionistRevenge: - // RetributionistRevengeManager.ReceiveRPC(reader, __instance); - // break; case CustomRPC.SetSwooperTimer: { byte id = reader.ReadByte(); @@ -687,15 +679,6 @@ internal class RPCHandlerPatch Utils.FlashColor(new(1f, 0f, 0f, 0.3f)); if (Constants.ShouldPlaySfx()) RPC.PlaySound(PlayerControl.LocalPlayer.PlayerId, Sounds.KillSound); break; - case CustomRPC.SetBloodhoundArrow: - Bloodhound.ReceiveRPC(reader); - break; - case CustomRPC.SetVultureArrow: - Vulture.ReceiveRPC(reader); - break; - //case CustomRPC.DoPoison: - // Baker.ReceiveRPC(reader); - // break; case CustomRPC.SetCleanserCleanLimit: Cleanser.ReceiveRPC(reader); break; diff --git a/Modules/RoleBlockManager.cs b/Modules/RoleBlockManager.cs new file mode 100644 index 000000000..faaa21613 --- /dev/null +++ b/Modules/RoleBlockManager.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace EHR.Modules +{ + public static class RoleBlockManager + { + public static Dictionary RoleBlockedPlayers = []; + + public static void Reset() => RoleBlockedPlayers = []; + + public static void AddRoleBlock(PlayerControl pc, float duration) + { + long now = Utils.TimeStamp; + if (RoleBlockedPlayers.TryGetValue(pc.PlayerId, out var data) && (data.Duration - (now - data.StartTimeStamp) + 1) > duration) + { + Logger.Info($"{pc.GetNameWithRole()} got role blocked, but the duration is less than the previous one", "RoleBlockManager"); + return; + } + + RoleBlockedPlayers[pc.PlayerId] = (now, duration); + Logger.Info($"{pc.GetNameWithRole()} is now role blocked for {duration} seconds", "RoleBlockManager"); + } + + public static void OnFixedUpdate(PlayerControl pc) + { + if (RoleBlockedPlayers.TryGetValue(pc.PlayerId, out var data)) + { + long now = Utils.TimeStamp; + if (now - data.StartTimeStamp >= data.Duration) + { + RoleBlockedPlayers.Remove(pc.PlayerId); + Logger.Info($"{pc.GetNameWithRole()} is no longer role blocked", "RoleBlockManager"); + } + } + } + + public static string GetBlockNotify(this BlockedAction blockedAction) + { + string blockedActionString = Translator.GetString($"{blockedAction}"); + string notify = string.Format(Translator.GetString("RoleBlockNotify"), blockedActionString); + return Utils.ColorString(Color.yellow, notify); + } + } + + public enum BlockedAction + { + Kill, + Report, + Sabotage, + Vent + } +} \ No newline at end of file diff --git a/Modules/TargetArrow.cs b/Modules/TargetArrow.cs index 33342d130..c663fd2a4 100644 --- a/Modules/TargetArrow.cs +++ b/Modules/TargetArrow.cs @@ -1,28 +1,17 @@ using System.Collections.Generic; using System.Linq; +using EHR.Modules; +using Hazel; using UnityEngine; namespace EHR; static class TargetArrow { - class ArrowInfo(byte from, byte to) - { - public readonly byte From = from; - public readonly byte To = to; - - public bool Equals(ArrowInfo obj) - { - return From == obj.From && To == obj.To; - } - public override string ToString() - { - return $"(From:{From} To:{To})"; - } - } - static readonly Dictionary TargetArrows = []; - static readonly string[] Arrows = [ + + static readonly string[] Arrows = + [ "↑", "↗", "→", @@ -39,6 +28,22 @@ static class TargetArrow TargetArrows.Clear(); } + public static void ReceiveRPC(MessageReader reader) + { + switch (reader.ReadPackedInt32()) + { + case 1: + Add(reader.ReadByte(), reader.ReadByte()); + break; + case 2: + Remove(reader.ReadByte(), reader.ReadByte()); + break; + case 3: + RemoveAllTarget(reader.ReadByte()); + break; + } + } + /// /// Register a new target arrow object /// @@ -50,8 +55,10 @@ static class TargetArrow if (!TargetArrows.Any(a => a.Key.Equals(arrowInfo))) { TargetArrows[arrowInfo] = "・"; + Utils.SendRPC(CustomRPC.Arrow, true, 1, seer, target); } } + /// /// Delete target /// @@ -65,7 +72,10 @@ static class TargetArrow { TargetArrows.Remove(a); } + + Utils.SendRPC(CustomRPC.Arrow, true, 2, seer, target); } + /// /// Delete all targets for the specified seer /// @@ -77,6 +87,8 @@ static class TargetArrow { TargetArrows.Remove(arrowInfo); } + + Utils.SendRPC(CustomRPC.Arrow, true, 3, seer); } /// @@ -115,6 +127,7 @@ static class TargetArrow update = true; continue; } + // Take the direction vector of the target var dir = target.transform.position - seer.transform.position; int index; @@ -132,6 +145,7 @@ static class TargetArrow var angle = Vector3.SignedAngle(Vector3.down, dir, Vector3.back) + 180 + 22.5; index = ((int)(angle / 45)) % 8; } + var arrow = Arrows[index]; if (TargetArrows[arrowInfo] != arrow) { @@ -139,9 +153,26 @@ static class TargetArrow update = true; } } + if (update) { Utils.NotifyRoles(SpecifySeer: seer, ForceLoop: false, SpecifyTarget: seer); } } -} + + class ArrowInfo(byte from, byte to) + { + public readonly byte From = from; + public readonly byte To = to; + + public bool Equals(ArrowInfo obj) + { + return From == obj.From && To == obj.To; + } + + public override string ToString() + { + return $"(From:{From} To:{To})"; + } + } +} \ No newline at end of file diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 4ad88b278..c4679ef9f 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -448,6 +448,8 @@ public static class Utils public static void SendRPC(CustomRPC rpc, params object[] data) { + if (!AmongUsClient.Instance.AmHost) return; + var w = CreateRPC(rpc); foreach (var o in data) { @@ -471,12 +473,25 @@ public static class Utils case Vector2 v: NetHelpers.WriteVector2(v, w); break; + case Vector3 v: + w.Write(v.x); + w.Write(v.y); + w.Write(v.z); + break; } } EndRPC(w); } + public static Vector3 ReadVector3(MessageReader reader) + { + float x = reader.ReadSingle(); + float y = reader.ReadSingle(); + float z = reader.ReadSingle(); + return new(x, y, z); + } + public static void IncreaseAbilityUseLimitOnKill(PlayerControl killer) { if (Main.PlayerStates[killer.PlayerId].Role is Mafioso { IsEnable: true } mo) mo.OnMurder(killer, null); @@ -2422,6 +2437,7 @@ public static class Utils Main.DontCancelVoteList.Clear(); DoorsReset.ResetDoors(); + RoleBlockManager.Reset(); if ((MapNames)Main.NormalOptions.MapId == MapNames.Airship && AmongUsClient.Instance.AmHost && PlayerControl.LocalPlayer.Is(CustomRoles.GM)) { diff --git a/Patches/CredentialsPatch.cs b/Patches/CredentialsPatch.cs index 046bd0813..b18eff67d 100644 --- a/Patches/CredentialsPatch.cs +++ b/Patches/CredentialsPatch.cs @@ -96,17 +96,31 @@ internal class TitleLogoPatch //private static readonly Color themeColor10 = new(0.18f, 0.2f, 0.59f); //private static readonly Color themeColor11 = new(0.96f, 0.88f, 0.86f); - private static readonly Color ThemeColor1 = new(0.98f, 0.7f, 0.44f); - private static readonly Color ThemeColor2 = new(0.98f, 0.61f, 0.42f); - private static readonly Color ThemeColor3 = new(0.04f, 0.41f, 0.75f); - private static readonly Color ThemeColor4 = new(0.12f, 0.17f, 0.47f); - private static readonly Color ThemeColor5 = new(0.38f, 0.44f, 0.66f); - private static readonly Color ThemeColor6 = new(0.72f, 0.68f, 0.79f); - private static readonly Color ThemeColor7 = ThemeColor1.ShadeColor(0.1f); - private static readonly Color ThemeColor8 = new(0.19f, 0.24f, 0.37f); - private static readonly Color ThemeColor9 = ThemeColor5.ShadeColor(0.1f); - private static readonly Color ThemeColor10 = ThemeColor4.ShadeColor(0.1f); - private static readonly Color ThemeColor11 = ThemeColor6.ShadeColor(0.1f); + // Winter BG + // private static readonly Color ThemeColor1 = new(0.98f, 0.7f, 0.44f); + // private static readonly Color ThemeColor2 = new(0.98f, 0.61f, 0.42f); + // private static readonly Color ThemeColor3 = new(0.04f, 0.41f, 0.75f); + // private static readonly Color ThemeColor4 = new(0.12f, 0.17f, 0.47f); + // private static readonly Color ThemeColor5 = new(0.38f, 0.44f, 0.66f); + // private static readonly Color ThemeColor6 = new(0.72f, 0.68f, 0.79f); + // private static readonly Color ThemeColor7 = ThemeColor1.ShadeColor(0.1f); + // private static readonly Color ThemeColor8 = new(0.19f, 0.24f, 0.37f); + // private static readonly Color ThemeColor9 = ThemeColor5.ShadeColor(0.1f); + // private static readonly Color ThemeColor10 = ThemeColor4.ShadeColor(0.1f); + // private static readonly Color ThemeColor11 = ThemeColor6.ShadeColor(0.1f); + + // Summer BG + private static readonly Color ThemeColor1 = new(0.706f, 0.588f, 0.455f); + private static readonly Color ThemeColor2 = new(0.827f, 0.827f, 0.643f); + private static readonly Color ThemeColor3 = new(0.196f, 0.196f, 0.067f); + private static readonly Color ThemeColor4 = new(0.145f, 0.655f, 0.859f); + private static readonly Color ThemeColor5 = new(0.580f, 0.780f, 0.820f); + private static readonly Color ThemeColor6 = new(0.435f, 0.776f, 0.925f); + private static readonly Color ThemeColor7 = new(0.161f, 0.439f, 0.588f); + private static readonly Color ThemeColor8 = new(0.388f, 0.565f, 0.537f); + private static readonly Color ThemeColor9 = new(0.463f, 0.471f, 0.188f); + private static readonly Color ThemeColor10 = Color.black; + private static readonly Color ThemeColor11 = ThemeColor4.ShadeColor(0.1f); private static void Postfix(MainMenuManager __instance) { @@ -199,7 +213,7 @@ internal class TitleLogoPatch } }; var bgRenderer = customBg.AddComponent(); - bgRenderer.sprite = Utils.LoadSprite("EHR.Resources.Images.WinterBG.jpg", 180f); + bgRenderer.sprite = Utils.LoadSprite("EHR.Resources.Images.SummerBG.jpg", 180f); if (__instance.screenTint != null) { diff --git a/Patches/ExilePatch.cs b/Patches/ExilePatch.cs index 8c51e981f..1c69d3754 100644 --- a/Patches/ExilePatch.cs +++ b/Patches/ExilePatch.cs @@ -159,7 +159,6 @@ class ExileControllerWrapUpPatch static void WrapUpFinalizer(GameData.PlayerInfo exiled) { // Even if an exception occurs in WrapUpPostfix, this part will be executed reliably. - bool overrideExiledPlayer = false; if (AmongUsClient.Instance.AmHost) { _ = new LateTask(() => @@ -171,7 +170,6 @@ class ExileControllerWrapUpPatch exiled.Object != null) { exiled.Object.RpcExileV2(); - overrideExiledPlayer = true; } }, 0.8f, "Restore IsDead Task"); _ = new LateTask(() => @@ -199,11 +197,14 @@ class ExileControllerWrapUpPatch SoundManager.Instance.ChangeAmbienceVolume(DataManager.Settings.Audio.AmbienceVolume); Logger.Info("Start task phase", "Phase"); - if (Options.EnableKillerLeftCommand.GetBool() && Options.CurrentGameMode == CustomGameMode.Standard && Options.ShowImpRemainOnEject.GetBool()) + bool showRemainingKillers = Options.EnableKillerLeftCommand.GetBool() && Options.ShowImpRemainOnEject.GetBool(); + bool appendEjectionNotify = CheckForEndVotingPatch.EjectionText != string.Empty; + Logger.Warn($"Ejection Text: {CheckForEndVotingPatch.EjectionText}", "debug"); + if ((showRemainingKillers || appendEjectionNotify) && Options.CurrentGameMode == CustomGameMode.Standard) { _ = new LateTask(() => { - var text = Utils.GetRemainingKillers(notify: true); + var text = showRemainingKillers ? Utils.GetRemainingKillers(notify: true) : string.Empty; text = $"<#ffffff>{text}"; var r = IRandom.Instance; foreach (var pc in Main.AllAlivePlayerControls) @@ -214,11 +215,13 @@ class ExileControllerWrapUpPatch finalText = $"\n{notify.TEXT}\n{finalText}"; } - if (overrideExiledPlayer && CheckForEndVotingPatch.EjectionText != string.Empty) + if (appendEjectionNotify) { - finalText = $"\n{CheckForEndVotingPatch.EjectionText}\n{finalText}"; + finalText = $"\n<#ffffff>{CheckForEndVotingPatch.EjectionText}\n{finalText}"; } + if (!showRemainingKillers) finalText = finalText.TrimStart(); + pc.Notify(finalText, r.Next(7, 13)); } }, 0.5f, log: false); diff --git a/Patches/HudPatch.cs b/Patches/HudPatch.cs index c8d4b4fbf..618353717 100644 --- a/Patches/HudPatch.cs +++ b/Patches/HudPatch.cs @@ -75,15 +75,14 @@ class HudManagerPatch { if (OverriddenRolesText == null) { - OverriddenRolesText = Object.Instantiate(__instance.KillButton.cooldownTimerText); + OverriddenRolesText = Object.Instantiate(__instance.KillButton.cooldownTimerText, __instance.transform, true); OverriddenRolesText.alignment = TextAlignmentOptions.Right; OverriddenRolesText.verticalAlignment = VerticalAlignmentOptions.Top; - OverriddenRolesText.transform.parent = __instance.transform; OverriddenRolesText.transform.localPosition = new(4.9f, 0.8f, 0); OverriddenRolesText.overflowMode = TextOverflowModes.Overflow; OverriddenRolesText.enableWordWrapping = false; OverriddenRolesText.color = Color.white; - OverriddenRolesText.fontSize = OverriddenRolesText.fontSizeMax = OverriddenRolesText.fontSizeMin = 2f; + OverriddenRolesText.fontSize = OverriddenRolesText.fontSizeMax = OverriddenRolesText.fontSizeMin = 2.5f; } if (Main.SetRoles.Count > 0 || Main.SetAddOns.Count > 0) @@ -120,24 +119,17 @@ class HudManagerPatch } } - bool stop = false; - foreach (var roles in Main.SetRoles) + foreach (var role in Main.SetRoles) { - if (!Main.SetAddOns.ContainsKey(roles.Key)) continue; - foreach (var addons in Main.SetAddOns) - { - if (!Main.SetRoles.ContainsKey(addons.Key)) continue; - foreach (var addon in addons.Value) - { - if (!CustomRolesHelper.CheckAddonConflictV2(addon, roles.Value)) - { - resultText[roles.Key] += " <#ff0000>(!)"; - stop = true; - break; - } - } + if (!Main.SetAddOns.TryGetValue(role.Key, out var addons)) continue; - if (stop) break; + foreach (var addon in addons) + { + if (!CustomRolesHelper.CheckAddonConflictV2(addon, role.Value)) + { + resultText[role.Key] += " <#ff0000>(!)"; + break; + } } } @@ -359,14 +351,13 @@ class HudManagerPatch if (LowerInfoText == null) { - LowerInfoText = Object.Instantiate(__instance.KillButton.cooldownTimerText); + LowerInfoText = Object.Instantiate(__instance.KillButton.cooldownTimerText, __instance.transform, true); LowerInfoText.alignment = TextAlignmentOptions.Center; - LowerInfoText.transform.parent = __instance.transform; LowerInfoText.transform.localPosition = new(0, -2f, 0); LowerInfoText.overflowMode = TextOverflowModes.Overflow; LowerInfoText.enableWordWrapping = false; LowerInfoText.color = Color.white; - LowerInfoText.fontSize = LowerInfoText.fontSizeMax = LowerInfoText.fontSizeMin = 2f; + LowerInfoText.fontSize = LowerInfoText.fontSizeMax = LowerInfoText.fontSizeMin = 2.7f; } var state = Main.PlayerStates[player.PlayerId]; @@ -476,6 +467,8 @@ class HudManagerPatch [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.ToggleHighlight))] class ToggleHighlightPatch { + private static readonly int OutlineColor = Shader.PropertyToID("_OutlineColor"); + public static void Postfix(PlayerControl __instance /*[HarmonyArgument(0)] bool active,*/ /*[HarmonyArgument(1)] RoleTeamTypes team*/) { if (!GameStates.IsInTask) return; @@ -483,7 +476,7 @@ class ToggleHighlightPatch if (player.CanUseKillButton()) { - __instance.cosmetics.currentBodySprite.BodySprite.material.SetColor("_OutlineColor", Utils.GetRoleColor(player.GetCustomRole())); + __instance.cosmetics.currentBodySprite.BodySprite.material.SetColor(OutlineColor, Utils.GetRoleColor(player.GetCustomRole())); } } } @@ -491,11 +484,14 @@ class ToggleHighlightPatch [HarmonyPatch(typeof(Vent), nameof(Vent.SetOutline))] class SetVentOutlinePatch { + private static readonly int OutlineColor = Shader.PropertyToID("_OutlineColor"); + private static readonly int AddColor = Shader.PropertyToID("_AddColor"); + public static void Postfix(Vent __instance, [HarmonyArgument(1)] ref bool mainTarget) { Color color = PlayerControl.LocalPlayer.GetRoleColor(); - __instance.myRend.material.SetColor("_OutlineColor", color); - __instance.myRend.material.SetColor("_AddColor", mainTarget ? color : Color.clear); + __instance.myRend.material.SetColor(OutlineColor, color); + __instance.myRend.material.SetColor(AddColor, mainTarget ? color : Color.clear); } } @@ -623,10 +619,49 @@ class MapBehaviourShowPatch opts.Mode = MapOptions.Modes.Normal; } + if (Main.GodMode.Value) opts.ShowLivePlayerPosition = true; + return true; } } +[HarmonyPatch(typeof(InfectedOverlay), nameof(InfectedOverlay.Update))] +class SabotageMapPatch +{ + public static Dictionary TimerTexts = []; + + public static void Postfix(InfectedOverlay __instance) + { + float perc = __instance.sabSystem.PercentCool; + int total = __instance.sabSystem.initialCooldown ? 10 : 30; + if (SabotageSystemTypeRepairDamagePatch.IsCooldownModificationEnabled) total = (int)SabotageSystemTypeRepairDamagePatch.ModifiedCooldownSec; + int remaining = Math.Clamp(total - (int)Math.Ceiling((1f - perc) * total) + 1, 0, total); + + foreach (var mr in __instance.rooms) + { + if (mr.special == null || mr.special.transform == null) continue; + var room = mr.room; + if (!TimerTexts.ContainsKey(room)) + { + TimerTexts[room] = Object.Instantiate(HudManager.Instance.KillButton.cooldownTimerText, mr.special.transform, true); + TimerTexts[room].alignment = TextAlignmentOptions.Center; + TimerTexts[room].transform.localPosition = mr.special.transform.localPosition; + TimerTexts[room].transform.localPosition = new(0, -0.4f, 0f); + TimerTexts[room].overflowMode = TextOverflowModes.Overflow; + TimerTexts[room].enableWordWrapping = false; + TimerTexts[room].color = Color.white; + TimerTexts[room].fontSize = TimerTexts[room].fontSizeMax = TimerTexts[room].fontSizeMin = 2.5f; + TimerTexts[room].sortingOrder = 100; + } + + bool isActive = Utils.IsActive(room); + bool isOtherActive = TimerTexts.Keys.Any(Utils.IsActive); + TimerTexts[room].text = $"<#ff{(isActive || isOtherActive ? "00" : "ff")}00>{(isActive ? remaining : GetString(isOtherActive ? "SabotageDisabledIndicator" : "SabotageActiveIndicator"))}"; + TimerTexts[room].enabled = remaining > 0 || isActive; + } + } +} + [HarmonyPatch(typeof(TaskPanelBehaviour), nameof(TaskPanelBehaviour.SetTaskText))] class TaskPanelBehaviourPatch { diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index f0c880304..b479b17f7 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -880,20 +880,28 @@ class MeetingHudStartPatch var seerRole = seer.GetCustomRole(); // Guesser Mode // - if (Options.GuesserMode.GetBool()) + if (((Options.GuesserMode.GetBool() && !seer.Data.IsDead && !target.Data.IsDead && + ((Options.CrewmatesCanGuess.GetBool() && seer.IsCrewmate() && !seer.Is(CustomRoles.Judge) && !seer.Is(CustomRoles.NiceSwapper) && !seer.Is(CustomRoles.Lookout) && !seer.Is(CustomRoles.ParityCop)) || + (Options.ImpostorsCanGuess.GetBool() && seerRole.IsImpostor() && !seer.Is(CustomRoles.Councillor)) || + (Options.NeutralKillersCanGuess.GetBool() && seer.IsNeutralKiller()) || + (Options.PassiveNeutralsCanGuess.GetBool() && seerRole.IsNonNK() && !seer.Is(CustomRoles.Doomsayer)))) || + + // Other Roles and Add-ons that can see player IDs // + (seer.Data.IsDead && !target.Data.IsDead && seerRole is CustomRoles.Mafia) || + (!seer.Data.IsDead && !target.Data.IsDead && + (seer.Is(CustomRoles.Guesser) || + seerRole is + CustomRoles.Judge or + CustomRoles.NiceSwapper or + CustomRoles.Lookout or + CustomRoles.ParityCop or + CustomRoles.Councillor or + CustomRoles.Doomsayer or + CustomRoles.EvilGuesser or + CustomRoles.NiceGuesser + )))) { - if (Options.CrewmatesCanGuess.GetBool() && seer.IsCrewmate() && !seer.Is(CustomRoles.Judge) && !seer.Is(CustomRoles.NiceSwapper) && !seer.Is(CustomRoles.Lookout) && !seer.Is(CustomRoles.ParityCop)) - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(seerRole), target.PlayerId.ToString()) + " " + pva.NameText.text; - if (Options.ImpostorsCanGuess.GetBool() && seerRole.IsImpostor() && !seer.Is(CustomRoles.Councillor)) - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(seerRole), target.PlayerId.ToString()) + " " + pva.NameText.text; - if (Options.NeutralKillersCanGuess.GetBool() && seer.IsNeutralKiller()) - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(seerRole), target.PlayerId.ToString()) + " " + pva.NameText.text; - if (Options.PassiveNeutralsCanGuess.GetBool() && seerRole.IsNonNK() && !seer.Is(CustomRoles.Doomsayer)) - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(seerRole), target.PlayerId.ToString()) + " " + pva.NameText.text; + pva.NameText.text = $"{Utils.ColorString(Utils.GetRoleColor(seerRole), target.PlayerId.ToString())} {pva.NameText.text}"; } if (seer.KnowDeathReason(target)) @@ -940,46 +948,6 @@ class MeetingHudStartPatch if (Psychic.IsRedForPsy(target, seer) && !seer.Data.IsDead) pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Impostor), pva.NameText.text); break; - case CustomRoles.Mafia: - if (seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Mafia), target.PlayerId.ToString()) + " " + pva.NameText.text; - break; - case CustomRoles.NiceGuesser: - case CustomRoles.EvilGuesser: - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(seer.Is(CustomRoles.NiceGuesser) ? CustomRoles.NiceGuesser : CustomRoles.EvilGuesser), target.PlayerId.ToString()) + " " + pva.NameText.text; - break; - case CustomRoles.Guesser: - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Guesser), target.PlayerId.ToString()) + " " + pva.NameText.text; - break; - case CustomRoles.Judge: - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Judge), target.PlayerId.ToString()) + " " + pva.NameText.text; - break; - case CustomRoles.NiceSwapper: - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.NiceSwapper), target.PlayerId.ToString()) + " " + pva.NameText.text; - break; - case CustomRoles.Lookout: - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Lookout), target.PlayerId.ToString()) + " " + pva.NameText.text; - break; - case CustomRoles.Doomsayer: - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Doomsayer), target.PlayerId.ToString()) + " " + pva.NameText.text; - break; - case CustomRoles.ParityCop: - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.ParityCop), target.PlayerId.ToString()) + " " + pva.NameText.text; - break; - - case CustomRoles.Councillor: - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Councillor), target.PlayerId.ToString()) + " " + pva.NameText.text; - - break; - case CustomRoles.Gamer: sb.Append(Gamer.TargetMark(seer, target)); sb.Append(Snitch.GetWarningMark(seer, target)); @@ -992,31 +960,9 @@ class MeetingHudStartPatch if (Silencer.ForSilencer.Contains(target.PlayerId)) sb.Append(Utils.ColorString(Utils.GetRoleColor(CustomRoles.Silencer), "╳")); - List list = seer.GetCustomSubRoles(); - foreach (var SeerSubRole in list) + if (target.GetCustomSubRoles().Contains(CustomRoles.Lovers) && (seer.Is(CustomRoles.Lovers) || seer.Data.IsDead)) { - switch (SeerSubRole) - { - case CustomRoles.Guesser: - if (!seer.Data.IsDead && !target.Data.IsDead) - pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Guesser), target.PlayerId.ToString()) + " " + pva.NameText.text; - break; - } - } - - List list1 = target.GetCustomSubRoles(); - foreach (var subRole in list1) - { - switch (subRole) - { - case CustomRoles.Lovers: - if (seer.Is(CustomRoles.Lovers) || seer.Data.IsDead) - { - sb.Append(Utils.ColorString(Utils.GetRoleColor(CustomRoles.Lovers), "♥")); - } - - break; - } + sb.Append(Utils.ColorString(Utils.GetRoleColor(CustomRoles.Lovers), "♥")); } sb.Append(Witch.GetSpelledMark(target.PlayerId, true)); diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs index 187d029fb..58578fe83 100644 --- a/Patches/OnGameStartedPatch.cs +++ b/Patches/OnGameStartedPatch.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using AmongUs.GameOptions; using EHR.Modules; +using EHR.Patches; using EHR.Roles.AddOns.Common; using EHR.Roles.AddOns.Crewmate; using EHR.Roles.AddOns.Impostor; @@ -50,6 +51,37 @@ internal class ChangeRoleSettings Main.AllPlayerKillCooldown = []; Main.AllPlayerSpeed = []; Main.KillTimers = []; + Main.SleuthMsgs = []; + Main.CyberStarDead = []; + Main.KilledDiseased = []; + Main.KilledAntidote = []; + Main.BaitAlive = []; + Main.DontCancelVoteList = []; + Main.LastEnteredVent = []; + Main.LastEnteredVentLocation = []; + Main.AfterMeetingDeathPlayers = []; + Main.ResetCamPlayerList = []; + Main.ClientIdList = []; + Main.CheckShapeshift = []; + Main.ShapeshiftTarget = []; + Main.ShieldPlayer = Options.ShieldPersonDiedFirst.GetBool() ? Main.FirstDied : byte.MaxValue; + Main.FirstDied = byte.MaxValue; + Main.MadmateNum = 0; + + Mayor.MayorUsedButtonCount = []; + Paranoia.ParaUsedButtonCount = []; + Mario.MarioVentCount = []; + Cleaner.CleanerBodies = []; + Virus.InfectedBodies = []; + Workaholic.WorkaholicAlive = []; + Virus.VirusNotify = []; + Veteran.VeteranInProtect = []; + Witness.AllKillers = []; + Grenadier.GrenadierBlinding = []; + SecurityGuard.BlockSabo = []; + Ventguard.BlockedVents = []; + Grenadier.MadGrenadierBlinding = []; + OverKiller.OverDeadPlayerList = []; Warlock.WarlockTimer = []; Arsonist.IsDoused = []; Revolutionist.IsDraw = []; @@ -67,45 +99,13 @@ internal class ChangeRoleSettings Warlock.IsCurseAndKill = []; Warlock.IsCursed = false; Detective.DetectiveNotify = []; - Main.SleuthMsgs = []; - Crusader.ForCrusade = []; - Main.CyberStarDead = []; - Main.KilledDiseased = []; - Main.KilledAntidote = []; - Workaholic.WorkaholicAlive = []; - Main.BaitAlive = []; - Cleaner.CleanerBodies = []; - Virus.InfectedBodies = []; - Virus.VirusNotify = []; - Main.DontCancelVoteList = []; - - Main.LastEnteredVent = []; - Main.LastEnteredVentLocation = []; - - Main.AfterMeetingDeathPlayers = []; - Main.ResetCamPlayerList = []; - Main.ClientIdList = []; - - Main.CheckShapeshift = []; - Main.ShapeshiftTarget = []; - Mayor.MayorUsedButtonCount = []; - Paranoia.ParaUsedButtonCount = []; - Mario.MarioVentCount = []; - Veteran.VeteranInProtect = []; - Witness.AllKillers = []; - Grenadier.GrenadierBlinding = []; - SecurityGuard.BlockSabo = []; - Ventguard.BlockedVents = []; - Grenadier.MadGrenadierBlinding = []; - OverKiller.OverDeadPlayerList = []; Provocateur.Provoked = []; - Main.ShieldPlayer = Options.ShieldPersonDiedFirst.GetBool() ? Main.FirstDied : byte.MaxValue; - Main.FirstDied = byte.MaxValue; - Main.MadmateNum = 0; + Crusader.ForCrusade = []; Godfather.GodfatherTarget = byte.MaxValue; ChatManager.ResetHistory(); ReportDeadBodyPatch.CanReport = []; + SabotageMapPatch.TimerTexts = []; Options.UsedButtonCount = 0; @@ -213,6 +213,7 @@ internal class ChangeRoleSettings DoorsReset.Initialize(); GhostRolesManager.Initialize(); CustomTeamManager.LoadCustomTeams(); + RoleBlockManager.Reset(); IRandom.SetInstanceById(Options.RoleAssigningAlgorithm.GetValue()); diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 8004c6d9c..c2233ee29 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -238,9 +238,9 @@ class CheckMurderPatch if (Pelican.IsEaten(target.PlayerId)) return false; - if (Glitch.hackedIdList.ContainsKey(killer.PlayerId)) + if (killer.IsRoleBlocked()) { - killer.Notify(string.Format(GetString("HackedByGlitch"), "Kill")); + killer.Notify(BlockedAction.Kill.GetBlockNotify()); return false; } @@ -341,9 +341,9 @@ class CheckMurderPatch return false; } - if (Glitch.hackedIdList.ContainsKey(killer.PlayerId)) + if (killer.IsRoleBlocked()) { - killer.Notify(string.Format(GetString("HackedByGlitch"), "Kill")); + killer.Notify(BlockedAction.Kill.GetBlockNotify()); return false; } @@ -1030,9 +1030,9 @@ class FixedUpdatePatch byte id = __instance.PlayerId; if (AmongUsClient.Instance.AmHost && GameStates.IsInTask && ReportDeadBodyPatch.CanReport[id] && ReportDeadBodyPatch.WaitReport[id].Count > 0) { - if (Glitch.hackedIdList.ContainsKey(id)) + if (id.IsPlayerRoleBlocked()) { - __instance.Notify(string.Format(GetString("HackedByGlitch"), "Report")); + __instance.Notify(BlockedAction.Report.GetBlockNotify()); Logger.Info("Dead Body Report Blocked (player is hacked by Glitch)", "FixedUpdate.ReportDeadBody"); ReportDeadBodyPatch.WaitReport[id].Clear(); } @@ -1222,6 +1222,8 @@ class FixedUpdatePatch } if (!lowLoad) Randomizer.OnFixedUpdateForPlayers(player); + + RoleBlockManager.OnFixedUpdate(player); } if (!lowLoad) @@ -1294,7 +1296,7 @@ class FixedUpdatePatch RoleText.text = RoleTextData.Item1; RoleText.color = RoleTextData.Item2; - if (Options.CurrentGameMode != CustomGameMode.Standard) RoleText.text = string.Empty; + if (Options.CurrentGameMode is not CustomGameMode.Standard and not CustomGameMode.HideAndSeek) RoleText.text = string.Empty; RoleText.enabled = IsRoleTextEnabled(__instance); @@ -1623,7 +1625,7 @@ class PlayerStartPatch { var roleText = Object.Instantiate(__instance.cosmetics.nameText, __instance.cosmetics.nameText.transform, true); roleText.transform.localPosition = new(0f, 0.2f, 0f); - roleText.fontSize -= 0.2f; + roleText.fontSize -= 0.9f; roleText.text = "RoleText"; roleText.gameObject.name = "RoleText"; roleText.enabled = false; @@ -1756,14 +1758,14 @@ class CoEnterVentPatch return true; case CustomGameMode.HideAndSeek: CustomHideAndSeekManager.OnCoEnterVent(__instance, id); - return true; + break; } - if (Glitch.hackedIdList.ContainsKey(__instance.myPlayer.PlayerId)) + if (__instance.myPlayer.IsRoleBlocked()) { _ = new LateTask(() => { - __instance.myPlayer?.Notify(string.Format(GetString("HackedByGlitch"), "Vent")); + __instance.myPlayer?.Notify(BlockedAction.Vent.GetBlockNotify()); __instance.myPlayer?.MyPhysics?.RpcBootFromVent(id); }, 0.5f); return true; diff --git a/Patches/SabotageSystemPatch.cs b/Patches/SabotageSystemPatch.cs index b734d6326..53000f98f 100644 --- a/Patches/SabotageSystemPatch.cs +++ b/Patches/SabotageSystemPatch.cs @@ -1,4 +1,4 @@ -using System.Linq; +using EHR.Modules; using EHR.Roles.Crewmate; using EHR.Roles.Neutral; using HarmonyLib; @@ -244,27 +244,26 @@ public static class ElectricTaskCompletePatch [HarmonyPatch(typeof(SabotageSystemType), nameof(SabotageSystemType.UpdateSystem))] public static class SabotageSystemTypeRepairDamagePatch { - private static bool isCooldownModificationEnabled; - private static float modifiedCooldownSec; + public static bool IsCooldownModificationEnabled; + public static float ModifiedCooldownSec; public static void Initialize() { - isCooldownModificationEnabled = Options.SabotageCooldownControl.GetBool(); - modifiedCooldownSec = Options.SabotageCooldown.GetFloat(); + IsCooldownModificationEnabled = Options.SabotageCooldownControl.GetBool(); + ModifiedCooldownSec = Options.SabotageCooldown.GetFloat(); } public static bool Prefix([HarmonyArgument(0)] PlayerControl player) { if (Options.DisableSabotage.GetBool() || Options.CurrentGameMode != CustomGameMode.Standard) return false; if (SecurityGuard.BlockSabo.Count > 0) return false; - if (Glitch.hackedIdList.ContainsKey(player.PlayerId)) + if (player.IsRoleBlocked()) { - player.Notify(string.Format(Translator.GetString("HackedByGlitch"), "Sabotage")); + player.Notify(BlockedAction.Sabotage.GetBlockNotify()); return false; } if (player.Is(CustomRoleTypes.Impostor) && !player.IsAlive() && Options.DeadImpCantSabotage.GetBool()) return false; - if (player.Is(CustomRoleTypes.Impostor) && (player.IsAlive() || !Options.DeadImpCantSabotage.GetBool()) && player.GetCustomRole() is not CustomRoles.Minimalism and not CustomRoles.Mafioso and not CustomRoles.Generator) return true; bool allow = player.GetCustomRole() switch { CustomRoles.Jackal when Jackal.CanSabotage.GetBool() => true, @@ -282,12 +281,12 @@ public static class SabotageSystemTypeRepairDamagePatch public static void Postfix(SabotageSystemType __instance) { - if (!isCooldownModificationEnabled || !AmongUsClient.Instance.AmHost) + if (!IsCooldownModificationEnabled || !AmongUsClient.Instance.AmHost) { return; } - __instance.Timer = modifiedCooldownSec; + __instance.Timer = ModifiedCooldownSec; __instance.IsDirty = true; } } diff --git a/Patches/ShipStatusPatch.cs b/Patches/ShipStatusPatch.cs index d559a9438..f5c3d85d2 100644 --- a/Patches/ShipStatusPatch.cs +++ b/Patches/ShipStatusPatch.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using BepInEx; +using EHR.Modules; using EHR.Patches; using EHR.Roles.AddOns.Crewmate; using EHR.Roles.AddOns.Impostor; @@ -34,15 +35,19 @@ class ShipFixedUpdatePatch [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.UpdateSystem), typeof(SystemTypes), typeof(PlayerControl), typeof(MessageReader))] public static class MessageReaderUpdateSystemPatch { - public static void Prefix(ShipStatus __instance, [HarmonyArgument(0)] SystemTypes systemType, [HarmonyArgument(1)] PlayerControl player, [HarmonyArgument(2)] MessageReader reader) + public static bool Prefix(ShipStatus __instance, [HarmonyArgument(0)] SystemTypes systemType, [HarmonyArgument(1)] PlayerControl player, [HarmonyArgument(2)] MessageReader reader) { try { - RepairSystemPatch.Prefix(__instance, systemType, player, MessageReader.Get(reader).ReadByte()); + byte amount = MessageReader.Get(reader).ReadByte(); + if (EAC.RpcUpdateSystemCheck(player, systemType, amount)) return false; + RepairSystemPatch.Prefix(__instance, systemType, player, amount); } catch { } + + return true; } public static void Postfix( /*ShipStatus __instance,*/ [HarmonyArgument(0)] SystemTypes systemType, [HarmonyArgument(1)] PlayerControl player, [HarmonyArgument(2)] MessageReader reader) @@ -115,14 +120,13 @@ class RepairSystemPatch case SystemTypes.Sabotage when AmongUsClient.Instance.NetworkMode != NetworkModes.FreePlay: if (Options.CurrentGameMode != CustomGameMode.Standard) return false; if (SecurityGuard.BlockSabo.Count > 0) return false; - if (Glitch.hackedIdList.ContainsKey(player.PlayerId)) + if (player.IsRoleBlocked()) { - player.Notify(string.Format(Translator.GetString("HackedByGlitch"), "Sabotage")); + player.Notify(BlockedAction.Sabotage.GetBlockNotify()); return false; } if (player.Is(CustomRoleTypes.Impostor) && !player.IsAlive() && Options.DeadImpCantSabotage.GetBool()) return false; - if (player.Is(CustomRoleTypes.Impostor) && (player.IsAlive() || !Options.DeadImpCantSabotage.GetBool()) && !player.Is(CustomRoles.Minimalism) && !player.Is(CustomRoles.Mafioso)) return true; return player.GetCustomRole() switch { CustomRoles.Jackal when Jackal.CanSabotage.GetBool() => true, diff --git a/Resources/Images/DleksButton.png b/Resources/Images/DleksButton.png deleted file mode 100644 index 40edb2275..000000000 Binary files a/Resources/Images/DleksButton.png and /dev/null differ diff --git a/Resources/Images/EHR-BG.jpg b/Resources/Images/EHR-BG.jpg deleted file mode 100644 index 9ff32963a..000000000 Binary files a/Resources/Images/EHR-BG.jpg and /dev/null differ diff --git a/Resources/Images/SummerBG.jpg b/Resources/Images/SummerBG.jpg new file mode 100644 index 000000000..62bccddf7 Binary files /dev/null and b/Resources/Images/SummerBG.jpg differ diff --git a/Resources/Images/TOHE-BG.jpg b/Resources/Images/TOHE-BG.jpg deleted file mode 100644 index 93bd866d1..000000000 Binary files a/Resources/Images/TOHE-BG.jpg and /dev/null differ diff --git a/Resources/Images/TabIcon_CovenRoles.png b/Resources/Images/TabIcon_CovenRoles.png deleted file mode 100644 index e3885ebfa..000000000 Binary files a/Resources/Images/TabIcon_CovenRoles.png and /dev/null differ diff --git a/Resources/Images/TabIcon_VanillaRoles.png b/Resources/Images/TabIcon_VanillaRoles.png deleted file mode 100644 index 832b2978f..000000000 Binary files a/Resources/Images/TabIcon_VanillaRoles.png and /dev/null differ diff --git a/Resources/Images/TownOfHost-Logo.png b/Resources/Images/TownOfHost-Logo.png deleted file mode 100644 index 5cfc67a03..000000000 Binary files a/Resources/Images/TownOfHost-Logo.png and /dev/null differ diff --git a/Resources/Images/WinterBG.jpg b/Resources/Images/WinterBG.jpg deleted file mode 100644 index b43cc703c..000000000 Binary files a/Resources/Images/WinterBG.jpg and /dev/null differ diff --git a/Resources/Images/bg.png b/Resources/Images/bg.png deleted file mode 100644 index 441525353..000000000 Binary files a/Resources/Images/bg.png and /dev/null differ diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index a95beed32..83ddce61d 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -1047,6 +1047,11 @@ "TieMode.Default": "No ejects", "TieMode.All": "Eject All", "TieMode.Random": "Eject Random", + "Kill": "Kill", + "Report": "Report", + "Sabotage": "Sabotage", + "Vent": "Vent", + "Dead": "DEAD", "DisableDevices": "Disable Devices", "DisableSkeldDevices": "Disable Skeld Devices", "DisableMiraHQDevices": "Disable MiraHQ Devices", @@ -1573,7 +1578,9 @@ "HackDuration": "Hack Duration", "MimicCooldown": "Mimic Cooldown", "MimicDuration": "Mimic Duration", - "HackedByGlitch": "You are hacked, you can't {0}.", + "RoleBlockNotify": "You are role blocked, you can't {0}", + "RoleBlockDuration": "Role Block Duration", + "RoleBlockCooldown": "Role Block Cooldown", "BKProtectDuration": "Protection Duration", "TotocalcioMaxBetTimes": "Maximum Number of Follows", "TotocalcioBetCooldown": "Follow Cooldown", @@ -2124,7 +2131,6 @@ "TabGroup.Addons": "Add-Ons", "TabGroup.OtherRoles": "Ghost Roles", "TabGroup.TaskSettings": "Game Modifiers", - "TabGroup.CovenRoles": "Coven Roles", "OtherRoles.CrewmateRoles": "★ Crewmate Roles", "OtherRoles.NeutralRoles": "★ Neutral Roles", "OtherRoles.ImpostorRoles": "★ Impostor Roles", @@ -2309,6 +2315,8 @@ "FarseerKillButtonText": "Reveal", "PoisonerKillButtonText": "Poison", "SentryPetButtonText": "Monitor", + "SabotageActiveIndicator": "▶", + "SabotageDisabledIndicator": "⊘", "DisabledBySettings": "Disabled by Settings", "Disabled": "Disabled", "FailToTrack": "Failed To Track", @@ -2797,7 +2805,6 @@ "Escort": "Escort", "EscortInfo": "Role-Block Players to Help the Crew!", "EscortInfoLong": "(Crewmates):\\nAs the Escort, you can role-block a player for a specified time using your kill button. A role-blocked player cannot kill, vent, sabotage or report.\\n(Role-Block duration is the same as the hack duration for The Glitch)", - "EscortCD": "Role-Block Cooldown", "EscortBlockDuration": "Role-Block Duration", "EscortTargetHacked": "Target successfully role-blocked", "EscortKillButtonText": "Role-Block", diff --git a/Resources/Lang/ru_RU.json b/Resources/Lang/ru_RU.json index 2ad0ff869..79749da16 100644 --- a/Resources/Lang/ru_RU.json +++ b/Resources/Lang/ru_RU.json @@ -2308,7 +2308,7 @@ "MonarchKillButtonText": "Посвятить", "FarseerKillButtonText": "Раскрыть", "SentryPetButtonText": "Сенсор", - "PoisonerKillButtonText": "Отравить" + "PoisonerKillButtonText": "Отравить", "DisabledBySettings": "Выключено настройками", "Disabled": "Выключено", "FailToTrack": "Не удалось отследить", diff --git a/Roles/AddOns/Common/Flash.cs b/Roles/AddOns/Common/Flash.cs index 1db9a30cc..24c74b24f 100644 --- a/Roles/AddOns/Common/Flash.cs +++ b/Roles/AddOns/Common/Flash.cs @@ -1,6 +1,6 @@ namespace EHR.Roles.AddOns.Common { - internal class Flash : IAddon + internal class Flashman : IAddon { public AddonTypes Type => AddonTypes.Helpful; diff --git a/Roles/Crewmate/Bloodhound.cs b/Roles/Crewmate/Bloodhound.cs index 5584279a6..af2c33321 100644 --- a/Roles/Crewmate/Bloodhound.cs +++ b/Roles/Crewmate/Bloodhound.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using EHR.Modules; using Hazel; using UnityEngine; @@ -88,7 +87,6 @@ namespace EHR.Roles.Crewmate { TargetArrow.RemoveAllTarget(id); LocateArrow.RemoveAllTarget(id); - Utils.SendRPC(CustomRPC.SetBloodhoundArrow, id, 1); } BloodhoundTargets.Clear(); @@ -105,7 +103,6 @@ namespace EHR.Roles.Crewmate var pos = target.Pos(); LocateArrow.Add(id, pos); - Utils.SendRPC(CustomRPC.SetBloodhoundArrow, id, 2, pos); } } @@ -120,13 +117,11 @@ namespace EHR.Roles.Crewmate var pos = target.Object.Pos(); LocateArrow.Remove(pc.PlayerId, pos); - Utils.SendRPC(CustomRPC.SetBloodhoundArrow, pc.PlayerId, 3, pos); if (pc.GetAbilityUseLimit() >= 1) { BloodhoundTargets.Add(killer.PlayerId); TargetArrow.Add(pc.PlayerId, killer.PlayerId); - Utils.SendRPC(CustomRPC.SetBloodhoundArrow, pc.PlayerId, 4, killer.PlayerId); pc.Notify(GetString("BloodhoundTrackRecorded")); pc.RpcRemoveAbilityUse(); diff --git a/Roles/Crewmate/Escort.cs b/Roles/Crewmate/Escort.cs index f82557cc6..8c942fb0d 100644 --- a/Roles/Crewmate/Escort.cs +++ b/Roles/Crewmate/Escort.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using AmongUs.GameOptions; -using EHR.Roles.Neutral; using static EHR.Translator; namespace EHR.Roles.Crewmate @@ -12,18 +11,24 @@ namespace EHR.Roles.Crewmate private static OptionItem CD; private static OptionItem UseLimit; + private static OptionItem Duration; public static OptionItem UsePet; + public override bool IsEnable => playerIdList.Count > 0; + public static void SetupCustomOption() { Options.SetupSingleRoleOptions(Id, TabGroup.CrewmateRoles, CustomRoles.Escort); - CD = FloatOptionItem.Create(Id + 10, "EscortCD", new(2.5f, 60f, 2.5f), 30f, TabGroup.CrewmateRoles) + CD = FloatOptionItem.Create(Id + 10, "RoleBlockCooldown", new(2.5f, 60f, 2.5f), 30f, TabGroup.CrewmateRoles) .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Escort]) .SetValueFormat(OptionFormat.Seconds); UseLimit = IntegerOptionItem.Create(Id + 11, "AbilityUseLimit", new(1, 20, 1), 3, TabGroup.CrewmateRoles) .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Escort]) .SetValueFormat(OptionFormat.Times); UsePet = Options.CreatePetUseSetting(Id + 12, CustomRoles.Escort); + Duration = FloatOptionItem.Create(Id + 13, "RoleBlockDuration", new(1f, 60f, 1f), 15f, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Escort]) + .SetValueFormat(OptionFormat.Seconds); } public override void Init() @@ -42,8 +47,6 @@ namespace EHR.Roles.Crewmate Main.ResetCamPlayerList.Add(playerId); } - public override bool IsEnable => playerIdList.Count > 0; - public override void SetKillCooldown(byte playerId) { Main.AllPlayerKillCooldown[playerId] = playerId.GetAbilityUseLimit() > 0 ? CD.GetFloat() : 300f; @@ -58,7 +61,7 @@ namespace EHR.Roles.Crewmate killer.RpcRemoveAbilityUse(); killer.SetKillCooldown(); - Glitch.hackedIdList.TryAdd(target.PlayerId, Utils.TimeStamp); + target.BlockRole(Duration.GetFloat()); killer.Notify(GetString("EscortTargetHacked")); return false; diff --git a/Roles/Crewmate/Mortician.cs b/Roles/Crewmate/Mortician.cs index 9abca27b1..a2a384dde 100644 --- a/Roles/Crewmate/Mortician.cs +++ b/Roles/Crewmate/Mortician.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using EHR.Modules; -using Hazel; using UnityEngine; using static EHR.Options; @@ -36,32 +34,6 @@ public class Mortician : RoleBase playerIdList.Add(playerId); } - private static void SendRPC(byte playerId, bool add, Vector3 loc = new()) - { - if (!Utils.DoRPC) return; - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetMorticianArrow, SendOption.Reliable); - writer.Write(playerId); - writer.Write(add); - if (add) - { - writer.Write(loc.x); - writer.Write(loc.y); - writer.Write(loc.z); - } - - AmongUsClient.Instance.FinishRpcImmediately(writer); - } - - public static void ReceiveRPC(MessageReader reader) - { - byte playerId = reader.ReadByte(); - bool add = reader.ReadBoolean(); - if (add) - LocateArrow.Add(playerId, new(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); - else - LocateArrow.RemoveAllTarget(playerId); - } - public static void OnPlayerDead(PlayerControl target) { var pos = target.Pos(); @@ -84,7 +56,6 @@ public class Mortician : RoleBase var player = Utils.GetPlayerById(pc); if (player == null || !player.IsAlive()) continue; LocateArrow.Add(pc, target.transform.position); - SendRPC(pc, true, target.transform.position); } } @@ -93,7 +64,6 @@ public class Mortician : RoleBase foreach (byte apc in playerIdList) { LocateArrow.RemoveAllTarget(apc); - SendRPC(apc, false); } if (!pc.Is(CustomRoles.Mortician) || target == null || pc.PlayerId == target.PlayerId) return; diff --git a/Roles/Crewmate/Randomizer.cs b/Roles/Crewmate/Randomizer.cs index cd8c49256..35128e7a7 100644 --- a/Roles/Crewmate/Randomizer.cs +++ b/Roles/Crewmate/Randomizer.cs @@ -271,7 +271,7 @@ namespace EHR.Roles.Crewmate { var pc = PickRandomPlayer(); AddEffectForPlayer(pc, effect); - Glitch.hackedIdList.TryAdd(pc.PlayerId, Utils.TimeStamp); + pc.BlockRole(IRandom.Instance.Next(MinimumEffectDuration, MaximumEffectDuration)); NotifyAboutRNG(pc); } break; @@ -281,7 +281,7 @@ namespace EHR.Roles.Crewmate foreach (var pc in Main.AllAlivePlayerControls) { AddEffectForPlayer(pc, effect); - Glitch.hackedIdList.TryAdd(pc.PlayerId, now); + pc.BlockRole(IRandom.Instance.Next(MinimumEffectDuration, MaximumEffectDuration)); NotifyAboutRNG(pc); } } diff --git a/Roles/Crewmate/Tracefinder.cs b/Roles/Crewmate/Tracefinder.cs index e8d2fa64b..4a9278433 100644 --- a/Roles/Crewmate/Tracefinder.cs +++ b/Roles/Crewmate/Tracefinder.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using AmongUs.GameOptions; -using EHR.Modules; -using Hazel; using UnityEngine; using static EHR.Options; @@ -50,44 +48,17 @@ public class Tracefinder : RoleBase On = true; } - private static void SendRPC(byte playerId, bool add, Vector3 loc = new()) - { - if (!Utils.DoRPC) return; - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetTracefinderArrow, SendOption.Reliable); - writer.Write(playerId); - writer.Write(add); - if (add) - { - writer.Write(loc.x); - writer.Write(loc.y); - writer.Write(loc.z); - } - - AmongUsClient.Instance.FinishRpcImmediately(writer); - } - public override void ApplyGameOptions(IGameOptions opt, byte id) { AURoleOptions.ScientistCooldown = VitalsCooldown.GetFloat(); AURoleOptions.ScientistBatteryCharge = VitalsDuration.GetFloat(); } - public static void ReceiveRPC(MessageReader reader) - { - byte playerId = reader.ReadByte(); - bool add = reader.ReadBoolean(); - if (add) - LocateArrow.Add(playerId, new(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); - else - LocateArrow.RemoveAllTarget(playerId); - } - public override void OnReportDeadBody() { foreach (byte apc in playerIdList) { LocateArrow.RemoveAllTarget(apc); - SendRPC(apc, false); } } @@ -109,7 +80,6 @@ public class Tracefinder : RoleBase PlayerControl pc = Utils.GetPlayerById(id); if (pc == null || !pc.IsAlive()) continue; LocateArrow.Add(id, target.transform.position); - SendRPC(id, true, target.transform.position); Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc); } } diff --git a/Roles/Crewmate/Tracker.cs b/Roles/Crewmate/Tracker.cs index 1f8bee5d7..c8eb2f3bb 100644 --- a/Roles/Crewmate/Tracker.cs +++ b/Roles/Crewmate/Tracker.cs @@ -77,7 +77,6 @@ namespace EHR.Roles.Crewmate Utils.GetPlayerById(trackerId).RpcRemoveAbilityUse(); TrackerTarget[trackerId].Add(targetId); - TargetArrow.Add(trackerId, targetId); } public static string GetTargetMark(PlayerControl seer, PlayerControl target) => !(seer == null || target == null) && TrackerTarget.ContainsKey(seer.PlayerId) && TrackerTarget[seer.PlayerId].Contains(target.PlayerId) ? Utils.ColorString(seer.GetRoleColor(), "◀") : string.Empty; diff --git a/Roles/Impostor/BountyHunter.cs b/Roles/Impostor/BountyHunter.cs index 7037af85d..926414ef9 100644 --- a/Roles/Impostor/BountyHunter.cs +++ b/Roles/Impostor/BountyHunter.cs @@ -82,7 +82,6 @@ public class BountyHunter : RoleBase public void ReceiveRPC(byte bountyId, byte targetId) { Target = targetId; - if (ShowTargetArrow) TargetArrow.Add(bountyId, targetId); } public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) diff --git a/Roles/Impostor/Consort.cs b/Roles/Impostor/Consort.cs index f6d630513..28c197392 100644 --- a/Roles/Impostor/Consort.cs +++ b/Roles/Impostor/Consort.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using EHR.Roles.Neutral; using static EHR.Translator; namespace EHR.Roles.Impostor @@ -11,16 +10,22 @@ namespace EHR.Roles.Impostor private static OptionItem CD; private static OptionItem UseLimit; + private static OptionItem Duration; + + public override bool IsEnable => playerIdList.Count > 0; public static void SetupCustomOption() { Options.SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Consort); - CD = FloatOptionItem.Create(Id + 10, "EscortCD", new(2.5f, 60f, 2.5f), 30f, TabGroup.ImpostorRoles) + CD = FloatOptionItem.Create(Id + 10, "RoleBlockCooldown", new(2.5f, 60f, 2.5f), 30f, TabGroup.ImpostorRoles) .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Consort]) .SetValueFormat(OptionFormat.Seconds); UseLimit = IntegerOptionItem.Create(Id + 11, "AbilityUseLimit", new(1, 20, 1), 3, TabGroup.ImpostorRoles) .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Consort]) .SetValueFormat(OptionFormat.Times); + Duration = FloatOptionItem.Create(Id + 12, "RoleBlockDuration", new(1f, 60f, 1f), 15f, TabGroup.ImpostorRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Consort]) + .SetValueFormat(OptionFormat.Seconds); } public override void Init() @@ -34,8 +39,6 @@ namespace EHR.Roles.Impostor playerId.SetAbilityUseLimit(UseLimit.GetInt()); } - public override bool IsEnable => playerIdList.Count > 0; - public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) { if (!IsEnable || killer == null || target == null) return false; @@ -44,7 +47,7 @@ namespace EHR.Roles.Impostor return killer.CheckDoubleTrigger(target, () => { killer.RpcRemoveAbilityUse(); - Glitch.hackedIdList.TryAdd(target.PlayerId, Utils.TimeStamp); + target.BlockRole(Duration.GetFloat()); killer.Notify(GetString("EscortTargetHacked")); killer.SetKillCooldown(CD.GetFloat()); }); diff --git a/Roles/Impostor/Mafioso.cs b/Roles/Impostor/Mafioso.cs index 0f09a82b6..fb62412a1 100644 --- a/Roles/Impostor/Mafioso.cs +++ b/Roles/Impostor/Mafioso.cs @@ -237,7 +237,7 @@ namespace EHR.Roles.Impostor SendRPCSyncPistolCD(); } - public override string GetProgressText(byte id, bool comms) => string.Format(GetString("MafiosoProgressText"), Tier, XP); + public override string GetProgressText(byte id, bool comms) => id.IsPlayerModClient() ? string.Empty : string.Format(GetString("MafiosoProgressText"), Tier, XP); public override string GetSuffix(PlayerControl pc, PlayerControl _, bool hud = false, bool m = false) { diff --git a/Roles/Impostor/YinYanger.cs b/Roles/Impostor/YinYanger.cs index e91437e22..bbef37052 100644 --- a/Roles/Impostor/YinYanger.cs +++ b/Roles/Impostor/YinYanger.cs @@ -136,6 +136,7 @@ namespace EHR.Roles.Impostor public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool m = false) { + if (seer.IsModClient() && !hud) return string.Empty; if (Main.PlayerStates[seer.PlayerId].Role is YinYanger { IsEnable: true } yy && seer.PlayerId == target.PlayerId) { return yy.YinYangedPlayers.Count == 2 ? $"{Translator.GetString("Mode")}: {Translator.GetString("YinYangModeNormal")}" : $"{Translator.GetString("Mode")}: {Translator.GetString("YinYangMode")} ({yy.YinYangedPlayers.Count}/2)"; diff --git a/Roles/Neutral/Glitch.cs b/Roles/Neutral/Glitch.cs index 91224bbef..c6a87c076 100644 --- a/Roles/Neutral/Glitch.cs +++ b/Roles/Neutral/Glitch.cs @@ -14,8 +14,6 @@ public class Glitch : RoleBase private const int Id = 18125; public static List playerIdList = []; - public static Dictionary hackedIdList = []; - public static OptionItem KillCooldown; public static OptionItem HackCooldown; public static OptionItem HackDuration; @@ -62,7 +60,6 @@ public class Glitch : RoleBase public override void Init() { playerIdList = []; - hackedIdList = []; GlitchId = byte.MaxValue; } @@ -168,7 +165,7 @@ public class Glitch : RoleBase { Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); HackCDTimer = HackCooldown.GetInt(); - hackedIdList.TryAdd(target.PlayerId, Utils.TimeStamp); + target.BlockRole(HackDuration.GetFloat()); LastHack = Utils.TimeStamp; SendRPCSyncTimers(); } @@ -195,23 +192,8 @@ public class Glitch : RoleBase if (MimicCDTimer is > 180 or < 0) MimicCDTimer = 0; if (MimicDurTimer is > 180 or < 0) MimicDurTimer = 0; - bool change = false; - foreach (var pc in hackedIdList) - { - if (pc.Value + HackDuration.GetInt() < now) - { - hackedIdList.Remove(pc.Key); - change = true; - } - } - if (player == null) return; - if (change) - { - Utils.NotifyRoles(SpecifySeer: player); - } - if (!player.IsAlive()) { HackCDTimer = 0; diff --git a/Roles/Neutral/Vulture.cs b/Roles/Neutral/Vulture.cs index 79d7a43f2..1cc702172 100644 --- a/Roles/Neutral/Vulture.cs +++ b/Roles/Neutral/Vulture.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.Linq; using AmongUs.GameOptions; -using EHR.Modules; -using Hazel; using UnityEngine; using static EHR.Options; using static EHR.Translator; @@ -91,38 +89,11 @@ public class Vulture : RoleBase AURoleOptions.EngineerInVentMaxTime = 0f; } - private static void SendRPC(byte playerId, bool add, Vector3 loc = new()) - { - if (!Utils.DoRPC) return; - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetVultureArrow, SendOption.Reliable); - writer.Write(playerId); - writer.Write(add); - if (add) - { - writer.Write(loc.x); - writer.Write(loc.y); - writer.Write(loc.z); - } - - AmongUsClient.Instance.FinishRpcImmediately(writer); - } - - public static void ReceiveRPC(MessageReader reader) - { - byte playerId = reader.ReadByte(); - bool add = reader.ReadBoolean(); - if (add) - LocateArrow.Add(playerId, new(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())); - else - LocateArrow.RemoveAllTarget(playerId); - } - public static void Clear() { foreach (byte apc in playerIdList) { LocateArrow.RemoveAllTarget(apc); - SendRPC(apc, false); } } @@ -144,7 +115,6 @@ public class Vulture : RoleBase Utils.GetPlayerById(apc).Notify(GetString("VultureCooldownUp")); } }, VultureReportCD.GetFloat(), "Vulture CD"); - SendRPC(apc, false); Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player); } } @@ -159,7 +129,6 @@ public class Vulture : RoleBase var player = Utils.GetPlayerById(pc); if (player == null || !player.IsAlive()) continue; LocateArrow.Add(pc, target.transform.position); - SendRPC(pc, true, target.transform.position); Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player); } } @@ -177,7 +146,6 @@ public class Vulture : RoleBase foreach (byte apc in playerIdList) { LocateArrow.Remove(apc, target.Object.transform.position); - SendRPC(apc, false); } } diff --git a/main.cs b/main.cs index f9b3c1ad8..6a6df4567 100644 --- a/main.cs +++ b/main.cs @@ -28,8 +28,8 @@ public class Main : BasePlugin public const string DebugKeyHash = "c0fd562955ba56af3ae20d7ec9e64c664f0facecef4b3e366e109306adeae29d"; public const string DebugKeySalt = "59687b"; public const string PluginGuid = "com.gurge44.endlesshostroles"; - public const string PluginVersion = "3.3.0"; - public const string PluginDisplayVersion = "3.3.0"; + public const string PluginVersion = "3.4.0"; + public const string PluginDisplayVersion = "3.4.0"; public const string NeutralColor = "#ffab1b"; public const string ImpostorColor = "#ff1919"; public const string CrewmateColor = "#8cffff";