diff --git a/CTA/CustomTeamAssigner/CustomRoles.cs b/CTA/CustomTeamAssigner/CustomRoles.cs index 27da0e092..aa661d76a 100644 --- a/CTA/CustomTeamAssigner/CustomRoles.cs +++ b/CTA/CustomTeamAssigner/CustomRoles.cs @@ -566,8 +566,9 @@ public enum CustomRoles Facilitator, // Ghost role Finder, Flash, - Fragile, + Focused, Fool, + Fragile, Giant, Glow, Gravestone, diff --git a/CustomRoles.cs b/CustomRoles.cs index 27da0e092..aa661d76a 100644 --- a/CustomRoles.cs +++ b/CustomRoles.cs @@ -566,8 +566,9 @@ public enum CustomRoles Facilitator, // Ghost role Finder, Flash, - Fragile, + Focused, Fool, + Fragile, Giant, Glow, Gravestone, diff --git a/Gamemodes/RoomRush.cs b/Gamemodes/RoomRush.cs index b077d6b34..55ebec004 100644 --- a/Gamemodes/RoomRush.cs +++ b/Gamemodes/RoomRush.cs @@ -888,9 +888,9 @@ public static class RoomRush try { - Main.CachedAllPlayerControls().Do(x => LocateArrow.RemoveAllTarget(x.PlayerId)); + Main.CachedAllPlayerControls().ForEach(x => LocateArrow.RemoveAllTarget(x.PlayerId)); Vector2 goalPos = RoomGoal.GetRoomClass().transform.position; - if (DisplayArrowToRoom.GetBool()) Main.CachedAllPlayerControls().Do(x => LocateArrow.Add(x.PlayerId, goalPos)); + if (DisplayArrowToRoom.GetBool()) Main.CachedAllPlayerControls().ForEach(x => LocateArrow.Add(x.PlayerId, goalPos)); } catch (Exception e) { Utils.ThrowException(e); } diff --git a/Main.cs b/Main.cs index abaa91353..8367882ea 100644 --- a/Main.cs +++ b/Main.cs @@ -762,6 +762,7 @@ public class Main : BasePlugin { CustomRoles.Listener, "#060270" }, { CustomRoles.Unbound, "#DFC57B" }, { CustomRoles.AntiTP, "#fcba03" }, + { CustomRoles.Focused, "#A10E49" }, { CustomRoles.Dizzy, "#de97a7" }, { CustomRoles.Entombed, "#8c71de" }, { CustomRoles.Urgent, "#D49255" }, diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 875845050..f1eca1d58 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -1958,6 +1958,7 @@ internal static class CustomRolesHelper CustomRoles.Reach when (pc.GetCustomRole() is CustomRoles.Mafioso or CustomRoles.Evolver or CustomRoles.Berserker) || pc.Is(CustomRoles.Constricted) => false, CustomRoles.Constricted when (pc.GetCustomRole() is CustomRoles.Mafioso or CustomRoles.Evolver or CustomRoles.Berserker) || pc.Is(CustomRoles.Reach) => false, CustomRoles.Beartrap when pc.Is(CustomRoles.GuardianAngelEHR) => false, + CustomRoles.Focused when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => false, CustomRoles.Dizzy when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => false, CustomRoles.Reach when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => false, CustomRoles.Constricted when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => false, diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 89f7d7d1f..4184f9ab4 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -1826,6 +1826,8 @@ internal static class ExtendedPlayerControl public void BeartrapKilled(PlayerControl target) { + if (player.Is(CustomRoles.Focused)) return; + Logger.Info($"{target?.Data?.PlayerName} was Beartrap", "Beartrap"); float tmpSpeed = Main.AllPlayerSpeed[player.PlayerId]; Main.AllPlayerSpeed[player.PlayerId] = Main.MinSpeed; diff --git a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs index 40ff641c0..367ac726e 100644 --- a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs +++ b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs @@ -659,7 +659,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS if (player.Is(CustomRoles.Bloodlust) && Bloodlust.HasImpVision.GetBool()) opt.SetVision(true); - if (Main.AllAlivePlayerControlsToList.Any(x => x.Is(CustomRoles.Bewilder) && !x.IsAlive() && x.GetRealKiller()?.PlayerId == player.PlayerId && !x.Is(CustomRoles.Hangman))) + if (!player.Is(CustomRoles.Focused) && Main.CachedAllPlayerControls().Exists(x => x.Is(CustomRoles.Bewilder) && !x.IsAlive() && x.GetRealKiller()?.PlayerId == player.PlayerId && !x.Is(CustomRoles.Hangman))) { opt.SetVision(false); opt.SetFloat(FloatOptionNames.CrewLightMod, Options.BewilderVision.GetFloat()); diff --git a/Patches/ClientPatch.cs b/Patches/ClientPatch.cs index d1f78a199..d2bdd300f 100644 --- a/Patches/ClientPatch.cs +++ b/Patches/ClientPatch.cs @@ -39,7 +39,7 @@ static class StartRpcImmediatelyPatch public static void Postfix(uint targetNetId, byte callId, SendOption option, int targetClientId = -1) { if (callId is 21 or 44 or 45 or 104) return; - Logger.Info($"Starting RPC: {callId} ({RPC.GetRpcName(callId)}) as {Main.CachedAllPlayerControls().FirstOrDefault(x => x.NetId == targetNetId)?.GetRealName() ?? targetNetId.ToString()} with SendOption {option} to {Utils.GetClientById(targetClientId)?.Character?.GetRealName() ?? targetClientId.ToString()}", "StartRpcImmediately"); + Logger.Info($"Starting RPC: {callId} ({RPC.GetRpcName(callId)}) as {Main.CachedAllPlayerControls().Find(x => x.NetId == targetNetId)?.GetRealName() ?? targetNetId.ToString()} with SendOption {option} to {Utils.GetClientById(targetClientId)?.Character?.GetRealName() ?? targetClientId.ToString()}", "StartRpcImmediately"); } } diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index d3b2b465f..0fb6cfeb3 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -194,16 +194,19 @@ internal static class CheckMurderPatch TimeSinceLastKill[killer.PlayerId] = 0f; - if (target.Is(CustomRoles.Diseased)) + if (!killer.Is(CustomRoles.Focused)) { - if (!Main.KilledDiseased.TryAdd(killer.PlayerId, 1)) - Main.KilledDiseased[killer.PlayerId] += 1; - } + if (target.Is(CustomRoles.Diseased)) + { + if (!Main.KilledDiseased.TryAdd(killer.PlayerId, 1)) + Main.KilledDiseased[killer.PlayerId] += 1; + } - if (target.Is(CustomRoles.Antidote)) - { - if (!Main.KilledAntidote.TryAdd(killer.PlayerId, 1)) - Main.KilledAntidote[killer.PlayerId] += 1; + if (target.Is(CustomRoles.Antidote)) + { + if (!Main.KilledAntidote.TryAdd(killer.PlayerId, 1)) + Main.KilledAntidote[killer.PlayerId] += 1; + } } killer.ResetKillCooldown(false); @@ -761,7 +764,7 @@ internal static class MurderPlayerPatch if (killer.AmOwner && Main.PlayerStates.TryGetValue(killer.PlayerId, out var ks) && ks.GetKillCount() <= 1) Achievements.Type.OhNo.CompleteAfterGameEnd(); - if (target != killer && !killer.Is(CustomRoles.KillingMachine) && (killer.PlayerId != target.PlayerId || target.GetRealKiller()?.GetCustomRole() is CustomRoles.Swooper or CustomRoles.Wraith || !killer.Is(CustomRoles.Oblivious) || !Options.ObliviousBaitImmune.GetBool())) + if (target != killer && !killer.Is(CustomRoles.KillingMachine) && !killer.Is(CustomRoles.Focused) && (killer.PlayerId != target.PlayerId || target.GetRealKiller()?.GetCustomRole() is CustomRoles.Swooper or CustomRoles.Wraith || !killer.Is(CustomRoles.Oblivious) || !Options.ObliviousBaitImmune.GetBool())) { killer.RPCPlayCustomSound("Congrats"); target.RPCPlayCustomSound("Congrats"); diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index fa4eddc75..3867cc912 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -149,6 +149,7 @@ "Prefix.Facilitator": "Facilitator", "Prefix.Finder": "Finder", "Prefix.Flash": "Flash", + "Prefix.Focused": "Focused", "Prefix.Fool": "Fool", "Prefix.Fragile": "Fragile", "Prefix.GA": "Guardian Angel", @@ -3737,6 +3738,9 @@ "FlashInfo": "You're faster", "FlashInfoLong": "(Add-ons):\\nThe Flash is faster by default.", "FlashSpeed": "Flash Speed", + "Focused": "Focused", + "FocusedInfo": "You Bypass On-Death Effects", + "FocusedInfoLong": "(Add-ons):\\nAs a Focused player, you aren't affected by on-death effects, such as Bane, Bewilder, Beartrap, etc.", "Fool": "Fool", "FoolInfo": "You can't fix lights & comms", "FoolInfoLong": "(Add-ons):\\nYou can't fix Lights and Comms sabotages. Note: Technician can't be Fool.", diff --git a/Roles/Standard/Addons/Common/Focused.cs b/Roles/Standard/Addons/Common/Focused.cs new file mode 100644 index 000000000..65bf4196b --- /dev/null +++ b/Roles/Standard/Addons/Common/Focused.cs @@ -0,0 +1,11 @@ +namespace EHR.Roles; + +public class Focused : IAddon +{ + public AddonTypes Type => AddonTypes.Mixed; + + public void SetupCustomOption() + { + Options.SetupAdtRoleOptions(659400, CustomRoles.Focused, canSetNum: true, teamSpawnOptions: true); + } +} \ No newline at end of file diff --git a/Roles/Standard/Addons/Common/Stained.cs b/Roles/Standard/Addons/Common/Stained.cs index 93025e42b..acafba61f 100644 --- a/Roles/Standard/Addons/Common/Stained.cs +++ b/Roles/Standard/Addons/Common/Stained.cs @@ -15,7 +15,7 @@ internal class Stained : IAddon public static void OnDeath(PlayerControl pc, PlayerControl killer) { - if (!killer || !pc || pc.PlayerId == killer.PlayerId || !killer.IsAlive() || !GameStates.IsInTask) return; + if (!killer || !pc || pc.PlayerId == killer.PlayerId || !killer.IsAlive() || killer.Is(CustomRoles.Focused) || !GameStates.IsInTask) return; VioletNameList ??= []; VioletNameList.Add(killer.PlayerId); diff --git a/Roles/Standard/Crewmate/Miscellaneous/Demolitionist.cs b/Roles/Standard/Crewmate/Miscellaneous/Demolitionist.cs index 124946188..363e50ca7 100644 --- a/Roles/Standard/Crewmate/Miscellaneous/Demolitionist.cs +++ b/Roles/Standard/Crewmate/Miscellaneous/Demolitionist.cs @@ -26,7 +26,7 @@ internal class Demolitionist : RoleBase public static void OnDeath(PlayerControl killer, PlayerControl target) { - if (killer == null || target == null || killer.PlayerId == target.PlayerId || !killer.IsAlive() || !GameStates.IsInTask) return; + if (!killer || !target || killer.PlayerId == target.PlayerId || !killer.IsAlive() || killer.Is(CustomRoles.Focused) || !GameStates.IsInTask) return; string warningMark = Utils.ColorString(Color.yellow, "\u26a0"); killer.Notify($"{warningMark} {CustomRoles.Demolitionist.ColoredTextByRole(Translator.GetString("OnDemolitionistDead"))} {warningMark}"); diff --git a/Roles/Standard/Crewmate/Support/Bane.cs b/Roles/Standard/Crewmate/Support/Bane.cs index 28abf0e9d..fe770f88f 100644 --- a/Roles/Standard/Crewmate/Support/Bane.cs +++ b/Roles/Standard/Crewmate/Support/Bane.cs @@ -22,7 +22,7 @@ public class Bane : RoleBase public static void OnKilled(PlayerControl killer) { - if (killer == null || killer.Is(CustomRoles.Bloodlust)) return; + if (!killer || killer.Is(CustomRoles.Bloodlust) || killer.Is(CustomRoles.Focused)) return; CustomRoles erasedRole = killer.IsImpostor() ? CustomRoles.ImpostorEHR : killer.IsCrewmate() ? CustomRoles.CrewmateEHR : killer.Is(Team.Coven) ? CustomRoles.CovenMember : CustomRoles.Amnesiac; killer.RpcSetCustomRole(erasedRole); diff --git a/Roles/Standard/Crewmate/Support/Medic.cs b/Roles/Standard/Crewmate/Support/Medic.cs index e70c80e25..f3681a9f3 100644 --- a/Roles/Standard/Crewmate/Support/Medic.cs +++ b/Roles/Standard/Crewmate/Support/Medic.cs @@ -225,18 +225,18 @@ public class Medic : RoleBase killer.SetKillCooldown(ResetCooldown.GetFloat()); Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: killer); - var medics = Main.CachedAllPlayerControls().Where(x => PlayerIdList.Contains(x.PlayerId) && x.IsAlive()).ToArray(); + var medics = Main.CachedAllPlayerControls().FindAll(x => PlayerIdList.Contains(x.PlayerId) && x.IsAlive()); switch (KnowShieldBroken.GetInt()) { case 0: target.RpcGuardAndKill(); - medics.Do(x => x.KillFlash()); + medics.ForEach(x => x.KillFlash()); medics.NotifyPlayers(Translator.GetString("MedicKillerTryBrokenShieldTargetForMedic")); Main.CachedAllPlayerControls().Where(x => ProtectList.Contains(x.PlayerId)).NotifyPlayers(Translator.GetString("MedicKillerTryBrokenShieldTargetForTarget")); break; case 1: - medics.Do(x => x.KillFlash()); + medics.ForEach(x => x.KillFlash()); medics.NotifyPlayers(Translator.GetString("MedicKillerTryBrokenShieldTargetForMedic")); break; case 2: diff --git a/Roles/Standard/Impostor/Concealing/Fakeshifter.cs b/Roles/Standard/Impostor/Concealing/Fakeshifter.cs index daa6826e9..fd7656431 100644 --- a/Roles/Standard/Impostor/Concealing/Fakeshifter.cs +++ b/Roles/Standard/Impostor/Concealing/Fakeshifter.cs @@ -49,7 +49,7 @@ public class Fakeshifter : RoleBase if (MarkedId == byte.MaxValue && target.IsAlive()) { MarkedId = target.PlayerId; - PlayerControl randomPlayer = Main.CachedAlivePlayerControls().Without(target).RandomElement(); + PlayerControl randomPlayer = Main.CachedAlivePlayerControls().FindAll(x => x.PlayerId != target.PlayerId).RandomElement(); bool hasValue = false; CustomRpcSender sender = CustomRpcSender.Create("Fakeshifter", SendOption.Reliable); sender.StartPackedMessage();