From a82c1b43db6376cbdc2e8068041ee96e24af8458 Mon Sep 17 00:00:00 2001 From: HyperAtill Date: Mon, 25 Aug 2025 17:00:35 +0300 Subject: [PATCH 1/3] ban some combos, rename some roles in system --- CustomRoles.cs | 8 +- GameMode/HotPotato.cs | 2 +- GameMode/Quiz.cs | 2 +- GameMode/Speedrun.cs | 2 +- Main.cs | 8 +- Modules/CustomRolesHelper.cs | 35 +- Modules/ErrorText.cs | 2 +- Modules/RPC.cs | 8 +- Modules/Utils.cs | 10 +- Patches/ChatCommandPatch.cs | 2 +- Patches/HudPatch.cs | 6 +- Patches/HudSpritePatch.cs | 8 +- Patches/MeetingHudPatch.cs | 4 +- Patches/PlayerControlPatch.cs | 6 +- Resources/Lang/en_US.json | 38 +- Roles/Crewmate/Addict.cs | 2 +- Roles/Crewmate/CopyCat.cs | 2 +- Roles/Crewmate/DovesOfNeace.cs | 2 +- Roles/Impostor/Mastermind.cs | 2 +- .../{SerialKiller.cs => Mercenary.cs} | 234 ++++----- Roles/Impostor/{Assassin.cs => Ninja.cs} | 462 +++++++++--------- Roles/Neutral/{Gamer.cs => Demon.cs} | 412 ++++++++-------- .../{NSerialKiller.cs => SerialKiller.cs} | 92 ++-- 23 files changed, 675 insertions(+), 674 deletions(-) rename Roles/Impostor/{SerialKiller.cs => Mercenary.cs} (84%) rename Roles/Impostor/{Assassin.cs => Ninja.cs} (91%) rename Roles/Neutral/{Gamer.cs => Demon.cs} (75%) rename Roles/Neutral/{NSerialKiller.cs => SerialKiller.cs} (92%) diff --git a/CustomRoles.cs b/CustomRoles.cs index add07710e..6dcceb776 100644 --- a/CustomRoles.cs +++ b/CustomRoles.cs @@ -74,10 +74,10 @@ public enum CustomRoles Mafioso, Mastermind, Mafia, // Nemesis - SerialKiller, // Mercenary + Mercenary, Miner, Morphling, - Assassin, // Ninja + Ninja, Nuker, Nullifier, Occultist, @@ -285,7 +285,7 @@ public enum CustomRoles Collector, Curser, Deathknight, - Gamer, // Demon + Demon, Doppelganger, Doomsayer, Eclipse, @@ -347,7 +347,7 @@ public enum CustomRoles Samurai, SchrodingersCat, Seamstress, - NSerialKiller, // Serial Killer + SerialKiller, Shifter, Sidekick, Simon, diff --git a/GameMode/HotPotato.cs b/GameMode/HotPotato.cs index 2cd8345d6..c7afbb5a3 100644 --- a/GameMode/HotPotato.cs +++ b/GameMode/HotPotato.cs @@ -203,7 +203,7 @@ internal static class HotPotato if (CanPassViaKillButton) { - target.RpcChangeRoleBasis(CustomRoles.NSerialKiller); + target.RpcChangeRoleBasis(CustomRoles.SerialKiller); LateTask.New(() => target.SetKillCooldown(1f), 0.2f, log: false); } diff --git a/GameMode/Quiz.cs b/GameMode/Quiz.cs index b3da7ffa0..14c4e5335 100644 --- a/GameMode/Quiz.cs +++ b/GameMode/Quiz.cs @@ -500,7 +500,7 @@ public static class Quiz spectators.Do(x => x.ExileTemporarily()); yield return new WaitForSeconds(0.2f); var stillLiving = dyingPlayers.ToValidPlayers().FindAll(x => x.IsAlive()); - stillLiving.ForEach(x => x.RpcChangeRoleBasis(CustomRoles.NSerialKiller)); + stillLiving.ForEach(x => x.RpcChangeRoleBasis(CustomRoles.SerialKiller)); Utils.SendRPC(CustomRPC.QuizSync, AllowKills); while (Utils.TimeStamp < FFAEndTS) diff --git a/GameMode/Speedrun.cs b/GameMode/Speedrun.cs index 8d66aef01..7647cc2a2 100644 --- a/GameMode/Speedrun.cs +++ b/GameMode/Speedrun.cs @@ -76,7 +76,7 @@ public static class Speedrun Utils.SendRPC(CustomRPC.SpeedrunSync, 2, pc.PlayerId); int kcd = KillCooldown.GetInt(); Main.AllPlayerKillCooldown[pc.PlayerId] = kcd; - pc.RpcChangeRoleBasis(CustomRoles.NSerialKiller); + pc.RpcChangeRoleBasis(CustomRoles.SerialKiller); pc.Notify(Translator.GetString("Speedrun_CompletedTasks")); pc.SyncSettings(); LateTask.New(() => pc.SetKillCooldown(kcd), 0.2f, log: false); diff --git a/Main.cs b/Main.cs index 58150e208..49b2b8922 100644 --- a/Main.cs +++ b/Main.cs @@ -514,7 +514,7 @@ public class Main : BasePlugin { CustomRoles.Revolutionist, "#ba4d06" }, { CustomRoles.FFF, "#414b66" }, { CustomRoles.Konan, "#4d4dff" }, - { CustomRoles.Gamer, "#68bc71" }, + { CustomRoles.Demon, "#68bc71" }, { CustomRoles.DarkHide, "#483d8b" }, { CustomRoles.Workaholic, "#008b8b" }, { CustomRoles.Collector, "#9d8892" }, @@ -531,7 +531,7 @@ public class Main : BasePlugin { CustomRoles.Deathknight, "#361d12" }, { CustomRoles.HexMaster, "#ff00ff" }, { CustomRoles.Wraith, "#4B0082" }, - { CustomRoles.NSerialKiller, "#233fcc" }, + { CustomRoles.SerialKiller, "#233fcc" }, { CustomRoles.Slenderman, "#2c2e00" }, { CustomRoles.Amogus, "#ff0000" }, { CustomRoles.Weatherman, "#347deb" }, @@ -995,7 +995,7 @@ public enum CustomWinner Pelican = CustomRoles.Pelican, Youtuber = CustomRoles.Youtuber, Egoist = CustomRoles.Egoist, - Gamer = CustomRoles.Gamer, + Demon = CustomRoles.Demon, DarkHide = CustomRoles.DarkHide, Workaholic = CustomRoles.Workaholic, Collector = CustomRoles.Collector, @@ -1006,7 +1006,7 @@ public enum CustomWinner Succubus = CustomRoles.Succubus, Necromancer = CustomRoles.Necromancer, Wraith = CustomRoles.Wraith, - SerialKiller = CustomRoles.NSerialKiller, + SerialKiller = CustomRoles.SerialKiller, Slenderman = CustomRoles.Slenderman, Amogus = CustomRoles.Amogus, Weatherman = CustomRoles.Weatherman, diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index f04533bec..1888352da 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -77,7 +77,7 @@ internal static class CustomRolesHelper { // Roles that use the same code as another role need to be handled here CustomRoles.Nuker => new Bomber(), - CustomRoles.Undertaker => new Assassin(), + CustomRoles.Undertaker => new Ninja(), CustomRoles.Chameleon => new Swooper(), CustomRoles.BloodKnight => new Wildling(), CustomRoles.HexMaster => new Witch(), @@ -138,7 +138,7 @@ internal static class CustomRolesHelper CustomRoles.Wildling => Wildling.CanShapeshiftOpt.GetBool() ? CustomRoles.Shapeshifter : CustomRoles.Impostor, CustomRoles.Morphling => CustomRoles.Shapeshifter, CustomRoles.Warlock => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter, - CustomRoles.SerialKiller => CustomRoles.Impostor, + CustomRoles.Mercenary => CustomRoles.Impostor, CustomRoles.FireWorks => CustomRoles.Shapeshifter, CustomRoles.SpeedBooster => CustomRoles.Crewmate, CustomRoles.Dictator => CustomRoles.Crewmate, @@ -198,7 +198,7 @@ internal static class CustomRolesHelper CustomRoles.SuperStar => CustomRoles.Crewmate, CustomRoles.Hacker => CustomRoles.Shapeshifter, CustomRoles.Visionary => CustomRoles.Shapeshifter, - CustomRoles.Assassin => CustomRoles.Phantom, + CustomRoles.Ninja => CustomRoles.Phantom, CustomRoles.Undertaker => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter, CustomRoles.Luckey => CustomRoles.Crewmate, CustomRoles.Demolitionist => CustomRoles.Crewmate, @@ -465,7 +465,7 @@ internal static class CustomRolesHelper CustomRoles.Revolutionist => RoleTypes.Impostor, CustomRoles.FFF => RoleTypes.Impostor, CustomRoles.Medic => UsePets && Medic.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor, - CustomRoles.Gamer => RoleTypes.Impostor, + CustomRoles.Demon => RoleTypes.Impostor, CustomRoles.HexMaster => RoleTypes.Impostor, CustomRoles.Wraith => RoleTypes.Impostor, CustomRoles.Glitch => RoleTypes.Shapeshifter, @@ -476,7 +476,7 @@ internal static class CustomRolesHelper CustomRoles.Provocateur => RoleTypes.Impostor, CustomRoles.BloodKnight => RoleTypes.Impostor, CustomRoles.Poisoner => RoleTypes.Impostor, - CustomRoles.NSerialKiller => RoleTypes.Impostor, + CustomRoles.SerialKiller => RoleTypes.Impostor, CustomRoles.Slenderman => RoleTypes.Impostor, CustomRoles.Amogus => RoleTypes.Impostor, CustomRoles.Weatherman => RoleTypes.Impostor, @@ -583,7 +583,7 @@ internal static class CustomRolesHelper CustomRoles.HexMaster or CustomRoles.Doppelganger or CustomRoles.Succubus or - CustomRoles.Gamer or + CustomRoles.Demon or CustomRoles.Crewpostor or CustomRoles.Hypocrite or CustomRoles.Cherokious or @@ -604,7 +604,7 @@ internal static class CustomRolesHelper CustomRoles.Patroller or CustomRoles.Rogue or CustomRoles.Parasite or - CustomRoles.NSerialKiller or + CustomRoles.SerialKiller or CustomRoles.Slenderman or CustomRoles.Amogus or CustomRoles.Weatherman or @@ -677,13 +677,13 @@ internal static class CustomRolesHelper CustomRoles.Vindicator or CustomRoles.Zombie or CustomRoles.Warlock or - CustomRoles.Assassin or + CustomRoles.Ninja or CustomRoles.Undertaker or CustomRoles.Hacker or CustomRoles.Visionary or CustomRoles.Miner or CustomRoles.Escapee or - CustomRoles.SerialKiller or + CustomRoles.Mercenary or CustomRoles.Overheat or CustomRoles.Abyssbringer or CustomRoles.Echo or @@ -1003,7 +1003,7 @@ internal static class CustomRolesHelper CustomRoles.Librarian or CustomRoles.Miner or CustomRoles.RiftMaker or - CustomRoles.Assassin or + CustomRoles.Ninja or CustomRoles.QuickShooter or CustomRoles.Sapper or CustomRoles.Sniper or @@ -1042,7 +1042,7 @@ internal static class CustomRolesHelper CustomRoles.Introvert when pc.GetCustomRole() is CustomRoles.Leery or CustomRoles.Samurai or CustomRoles.Arsonist or CustomRoles.Revolutionist or CustomRoles.Farseer or CustomRoles.Scavenger or CustomRoles.Analyst => false, CustomRoles.Circumvent when pc.GetCustomRole() is CustomRoles.Swooper or CustomRoles.RiftMaker => false, CustomRoles.Oblivious when pc.Is(CustomRoles.Altruist) => false, - CustomRoles.AntiTP when pc.GetCustomRole() is CustomRoles.Transmitter or CustomRoles.Miner or CustomRoles.Escapee or CustomRoles.Tunneler => false, + CustomRoles.AntiTP when pc.GetCustomRole() is CustomRoles.Transmitter or CustomRoles.Miner or CustomRoles.Escapee or CustomRoles.Tunneler=> false, CustomRoles.Swift when pc.Is(CustomRoles.Stealth) => false, CustomRoles.BananaMan when pc.Is(CustomRoles.Disco) => false, CustomRoles.Disco when pc.GetCustomRole() is CustomRoles.Chameleon or CustomRoles.Swooper or CustomRoles.Wraith or CustomRoles.Alchemist => false, @@ -1072,7 +1072,7 @@ internal static class CustomRolesHelper CustomRoles.Oblivious when pc.Is(CustomRoles.Detective) || pc.Is(CustomRoles.Cleaner) || pc.Is(CustomRoles.Medusa) || pc.Is(CustomRoles.Mortician) || pc.Is(CustomRoles.Mediumshiper) || pc.Is(CustomRoles.GuardianAngelEHR) => false, CustomRoles.Youtuber when !pc.IsCrewmate() || pc.Is(CustomRoles.Madmate) || pc.Is(CustomRoles.Sheriff) || pc.Is(CustomRoles.GuardianAngelEHR) => false, CustomRoles.Egoist when !pc.IsImpostor() => false, - CustomRoles.Damocles when pc.GetCustomRole() is CustomRoles.Bomber or CustomRoles.Nuker or CustomRoles.SerialKiller or CustomRoles.Cantankerous => false, + CustomRoles.Damocles when pc.GetCustomRole() is CustomRoles.Bomber or CustomRoles.Nuker or CustomRoles.Mercenary or CustomRoles.Cantankerous => false, CustomRoles.Damocles when !pc.CanUseKillButton() => false, CustomRoles.Flashman when pc.Is(CustomRoles.Swiftclaw) || pc.Is(CustomRoles.Giant) || pc.Is(CustomRoles.Spurt) => false, CustomRoles.Giant when pc.Is(CustomRoles.Flashman) || pc.Is(CustomRoles.Spurt) || pc.Is(CustomRoles.RoomRusher) => false, @@ -1092,6 +1092,7 @@ internal static class CustomRolesHelper CustomRoles.Mare when pc.Is(CustomRoles.Vampire) => false, CustomRoles.Torch when pc.Is(CustomRoles.Lighter) => false, CustomRoles.Torch when pc.Is(CustomRoles.Ignitor) => false, + CustomRoles.Torch when pc.Is(CustomRoles.Decryptor) => false, CustomRoles.Bewilder when pc.Is(CustomRoles.Lighter) => false, CustomRoles.Bewilder when pc.Is(CustomRoles.Ignitor) => false, CustomRoles.Sunglasses when pc.Is(CustomRoles.Lighter) => false, @@ -1330,7 +1331,7 @@ internal static class CustomRolesHelper public static bool IsDevFavoriteRole(this CustomRoles role) { return role is CustomRoles.Adventurer or CustomRoles.Chef or CustomRoles.Detour or CustomRoles.NiceHacker or CustomRoles.NiceSwapper or CustomRoles.Sentinel or CustomRoles.Sentry or CustomRoles.Tornado or CustomRoles.Tunneler or CustomRoles.Whisperer or CustomRoles.Wizard or - CustomRoles.Abyssbringer or CustomRoles.Assumer or CustomRoles.Chronomancer or CustomRoles.Echo or CustomRoles.Escapee or CustomRoles.Hypnotist or CustomRoles.Librarian or CustomRoles.Mafioso or CustomRoles.Mastermind or CustomRoles.SerialKiller or CustomRoles.Penguin or CustomRoles.RiftMaker or CustomRoles.Sapper or CustomRoles.Wiper or + CustomRoles.Abyssbringer or CustomRoles.Assumer or CustomRoles.Chronomancer or CustomRoles.Echo or CustomRoles.Escapee or CustomRoles.Hypnotist or CustomRoles.Librarian or CustomRoles.Mafioso or CustomRoles.Mastermind or CustomRoles.Mercenary or CustomRoles.Penguin or CustomRoles.RiftMaker or CustomRoles.Sapper or CustomRoles.Wiper or CustomRoles.Bargainer or CustomRoles.Bubble or CustomRoles.Doomsayer or CustomRoles.Enderman or CustomRoles.Evolver or CustomRoles.HeadHunter or CustomRoles.Impartial or CustomRoles.Patroller or CustomRoles.Pawn or CustomRoles.PlagueDoctor or CustomRoles.Postman or CustomRoles.Pyromaniac or CustomRoles.Revolutionist or CustomRoles.Rogue or CustomRoles.Romantic or CustomRoles.RoomRusher or CustomRoles.RouleteGrandeur or CustomRoles.Simon or CustomRoles.SoulHunter or CustomRoles.Sprayer or CustomRoles.Technician or CustomRoles.Tank or CustomRoles.Tiger or CustomRoles.Tremor or CustomRoles.Vengeance or CustomRoles.Vortex or CustomRoles.Werewolf or CustomRoles.WeaponMaster or CustomRoles.Reaper or CustomRoles.VoodooMaster or CustomRoles.Dreamweaver or CustomRoles.Banshee or CustomRoles.Allergic or CustomRoles.Bloodlust or CustomRoles.Damocles or CustomRoles.Deadlined or CustomRoles.DeadlyQuota or CustomRoles.Dynamo or CustomRoles.DoubleShot or CustomRoles.Energetic or CustomRoles.Haste or CustomRoles.Introvert or CustomRoles.Messenger or CustomRoles.Nimble or CustomRoles.Reach or CustomRoles.Seer or CustomRoles.Stressed; @@ -1530,7 +1531,7 @@ internal static class CustomRolesHelper { CustomRoles.Sans => RoleOptionType.Impostor_Killing, CustomRoles.Abyssbringer => RoleOptionType.Impostor_Killing, - CustomRoles.Assassin => RoleOptionType.Impostor_Killing, + CustomRoles.Ninja => RoleOptionType.Impostor_Killing, CustomRoles.Assumer => RoleOptionType.Impostor_Killing, CustomRoles.Augmenter => RoleOptionType.Impostor_Killing, CustomRoles.Bomber => RoleOptionType.Impostor_Killing, @@ -1548,7 +1549,7 @@ internal static class CustomRolesHelper CustomRoles.Lurker => RoleOptionType.Impostor_Killing, CustomRoles.Mafioso => RoleOptionType.Impostor_Killing, CustomRoles.Mafia => RoleOptionType.Impostor_Killing, - CustomRoles.SerialKiller => RoleOptionType.Impostor_Killing, + CustomRoles.Mercenary => RoleOptionType.Impostor_Killing, CustomRoles.Nuker => RoleOptionType.Impostor_Killing, CustomRoles.Overheat => RoleOptionType.Impostor_Killing, CustomRoles.OverKiller => RoleOptionType.Impostor_Killing, @@ -1832,14 +1833,14 @@ public enum CountTypes Jackal, Doppelganger, Pelican, - Gamer, + Demon, BloodKnight, Poisoner, Succubus, Necromancer, HexMaster, Wraith, - NSerialKiller, + SerialKiller, Slenderman, Amogus, Weatherman, diff --git a/Modules/ErrorText.cs b/Modules/ErrorText.cs index 851a4ea18..c394880f2 100644 --- a/Modules/ErrorText.cs +++ b/Modules/ErrorText.cs @@ -185,7 +185,7 @@ public enum ErrorCode { //xxxyyyz: ERR-xxx-yyy-z // xxx: General type of error (HUD-related, banishment-related, etc.) - // yyy: Detailed type of error (BoutyHunter processing, SerialKiller processing, etc.) + // yyy: Detailed type of error (BoutyHunter processing, Mercenary processing, etc.) // z: Severity // 0: No action required (hide) // 1: Abandon village if not working properly (hide after a certain period of time) diff --git a/Modules/RPC.cs b/Modules/RPC.cs index b9f6eeb83..5635dd64f 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -106,7 +106,7 @@ public enum CustomRPC SetNiceHackerLimit, SetCurrentDrawTarget, SetCpTasksDone, - SetGamerHealth, + SetDemonHealth, SetPelicanEtenNum, SwordsManKill, SetGhostPlayer, @@ -989,9 +989,9 @@ internal static class RPCHandlerPatch GameStartManagerPatch.TimerStartTS = long.Parse(reader.ReadString()); break; } - case CustomRPC.SetGamerHealth: + case CustomRPC.SetDemonHealth: { - Gamer.ReceiveRPC(reader); + Demon.ReceiveRPC(reader); break; } case CustomRPC.SetPelicanEtenNum: @@ -1049,7 +1049,7 @@ internal static class RPCHandlerPatch } case CustomRPC.SetMarkedPlayer: { - Assassin.ReceiveRPC(reader); + Ninja.ReceiveRPC(reader); break; } case CustomRPC.SyncChronomancer: diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 9edc18236..e2677c6dd 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -838,7 +838,7 @@ public static class Utils case CustomRoles.Poisoner: case CustomRoles.Eclipse: case CustomRoles.Pyromaniac: - case CustomRoles.NSerialKiller: + case CustomRoles.SerialKiller: case CustomRoles.Slenderman: case CustomRoles.Amogus: case CustomRoles.Weatherman: @@ -910,7 +910,7 @@ public static class Utils case CustomRoles.Medusa: case CustomRoles.Revolutionist: case CustomRoles.FFF: - case CustomRoles.Gamer: + case CustomRoles.Demon: case CustomRoles.HexMaster: case CustomRoles.Wraith: case CustomRoles.Juggernaut: @@ -2458,7 +2458,7 @@ public static class Utils SelfMark.Append(Medic.GetMark(seer, seer)); SelfMark.Append(Gaslighter.GetMark(seer, seer, forMeeting)); - SelfMark.Append(Gamer.TargetMark(seer, seer)); + SelfMark.Append(Demon.TargetMark(seer, seer)); SelfMark.Append(Sniper.GetShotNotify(seer.PlayerId)); if (Silencer.ForSilencer.Contains(seer.PlayerId)) SelfMark.Append(ColorString(GetRoleColor(CustomRoles.Silencer), "╳")); @@ -2867,7 +2867,7 @@ public static class Utils TargetMark.Append(ColorString(GetRoleColor(CustomRoles.Marshall), "★")); TargetMark.Append(Executioner.TargetMark(seer, target)); - TargetMark.Append(Gamer.TargetMark(seer, target)); + TargetMark.Append(Demon.TargetMark(seer, target)); TargetMark.Append(Medic.GetMark(seer, target)); TargetMark.Append(Gaslighter.GetMark(seer, target, forMeeting)); TargetMark.Append(Totocalcio.TargetMark(seer, target)); @@ -3210,7 +3210,7 @@ public static class Utils CustomRoles.ToiletMaster => ToiletMaster.AbilityCooldown.GetInt(), CustomRoles.AntiAdminer => AntiAdminer.AbilityCooldown.GetInt(), CustomRoles.Sniper => Options.DefaultShapeshiftCooldown.GetInt(), - CustomRoles.Assassin => Assassin.AssassinateCooldownOpt.GetInt(), + CustomRoles.Ninja => Ninja.AssassinateCooldownOpt.GetInt(), CustomRoles.Undertaker => Undertaker.UndertakerAssassinateCooldown.GetInt(), CustomRoles.Bomber => Bomber.BombCooldown.GetInt(), CustomRoles.Nuker => Bomber.NukeCooldown.GetInt(), diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index 197f522f2..2016fc9dd 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -439,7 +439,7 @@ internal static class ChatCommands ChatManager.SendMessage(PlayerControl.LocalPlayer, text); } - if (text.Contains("666") && PlayerControl.LocalPlayer.Is(CustomRoles.Gamer)) + if (text.Contains("666") && PlayerControl.LocalPlayer.Is(CustomRoles.Demon)) Achievements.Type.WhatTheHell.Complete(); return !canceled; diff --git a/Patches/HudPatch.cs b/Patches/HudPatch.cs index 4723e71ff..f93d17aae 100644 --- a/Patches/HudPatch.cs +++ b/Patches/HudPatch.cs @@ -244,7 +244,7 @@ internal static class HudManagerPatch break; case CustomRoles.KB_Normal: case CustomRoles.BedWarsPlayer: - __instance.KillButton?.OverrideText(GetString("GamerButtonText")); + __instance.KillButton?.OverrideText(GetString("DemonButtonText")); break; case CustomRoles.Deputy: usedButton?.OverrideText(GetString("DeputyHandcuffText")); @@ -257,7 +257,7 @@ internal static class HudManagerPatch break; } - if (role.PetActivatedAbility() && Options.CurrentGameMode == CustomGameMode.Standard && player.GetRoleTypes() != RoleTypes.Engineer && !role.OnlySpawnsWithPets() && !role.AlwaysUsesPhantomBase() && !player.GetCustomSubRoles().Any(StartGameHostPatch.BasisChangingAddons.ContainsKey) && role is not CustomRoles.Changeling and not CustomRoles.Assassin && (!role.SimpleAbilityTrigger() || !Options.UsePhantomBasis.GetBool() || !(player.IsNeutralKiller() && Options.UsePhantomBasisForNKs.GetBool()))) + if (role.PetActivatedAbility() && Options.CurrentGameMode == CustomGameMode.Standard && player.GetRoleTypes() != RoleTypes.Engineer && !role.OnlySpawnsWithPets() && !role.AlwaysUsesPhantomBase() && !player.GetCustomSubRoles().Any(StartGameHostPatch.BasisChangingAddons.ContainsKey) && role is not CustomRoles.Changeling and not CustomRoles.Ninja && (!role.SimpleAbilityTrigger() || !Options.UsePhantomBasis.GetBool() || !(player.IsNeutralKiller() && Options.UsePhantomBasisForNKs.GetBool()))) __instance.AbilityButton?.Hide(); if (LowerInfoText == null) @@ -590,7 +590,7 @@ internal static class SetHudActivePatch case CustomRoles.Pelican: case CustomRoles.FFF: case CustomRoles.Medic: - case CustomRoles.Gamer: + case CustomRoles.Demon: case CustomRoles.DarkHide: case CustomRoles.Farseer: case CustomRoles.Crusader: diff --git a/Patches/HudSpritePatch.cs b/Patches/HudSpritePatch.cs index 0fd005163..6b10a5c7a 100644 --- a/Patches/HudSpritePatch.cs +++ b/Patches/HudSpritePatch.cs @@ -167,22 +167,22 @@ public static class HudSpritePatch break; } - case CustomRoles.Assassin: + case CustomRoles.Ninja: case CustomRoles.Undertaker: { - if (Main.PlayerStates[player.PlayerId].Role is not Assassin assassin) break; + if (Main.PlayerStates[player.PlayerId].Role is not Ninja ninja) break; if (Options.UsePets.GetBool()) { newKillButton = CustomButton.Get("Mark"); - if (assassin.MarkedPlayer != byte.MaxValue) newPetButton = CustomButton.Get("Assassinate"); + if (ninja.MarkedPlayer != byte.MaxValue) newPetButton = CustomButton.Get("Assassinate"); } else { if (!shapeshifting) { newKillButton = CustomButton.Get("Mark"); - if (assassin.MarkedPlayer != byte.MaxValue) newAbilityButton = CustomButton.Get("Assassinate"); + if (ninja.MarkedPlayer != byte.MaxValue) newAbilityButton = CustomButton.Get("Assassinate"); } } diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index 50e4fa9f7..e28e89770 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -1044,8 +1044,8 @@ internal static class MeetingHudStartPatch case CustomRoles.Psychic when Psychic.IsRedForPsy(target, seer) && !seer.Data.IsDead: pva.NameText.text = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Impostor), pva.NameText.text); break; - case CustomRoles.Gamer: - sb.Append(Gamer.TargetMark(seer, target)); + case CustomRoles.Demon: + sb.Append(Demon.TargetMark(seer, target)); sb.Append(Snitch.GetWarningMark(seer, target)); break; case CustomRoles.Scout: diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 3c8a563d9..0a5962290 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -1577,7 +1577,7 @@ internal static class FixedUpdatePatch { foreach (PlayerControl pc in Main.AllPlayerControls) { - if (pc.Is(CustomRoles.Vampire) || pc.Is(CustomRoles.Warlock) || pc.Is(CustomRoles.Assassin) || pc.Is(CustomRoles.Undertaker) || pc.Is(CustomRoles.Poisoner)) + if (pc.Is(CustomRoles.Vampire) || pc.Is(CustomRoles.Warlock) || pc.Is(CustomRoles.Ninja) || pc.Is(CustomRoles.Undertaker) || pc.Is(CustomRoles.Poisoner)) Main.AllPlayerKillCooldown[pc.PlayerId] = Options.AdjustedDefaultKillCooldown * 2; } } @@ -1807,8 +1807,8 @@ internal static class FixedUpdatePatch case CustomRoles.Executioner: Mark.Append(Executioner.TargetMark(seer, target)); break; - case CustomRoles.Gamer: - Mark.Append(Gamer.TargetMark(seer, target)); + case CustomRoles.Demon: + Mark.Append(Demon.TargetMark(seer, target)); break; } diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 5290c865e..dedeb4ff8 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -177,7 +177,7 @@ "BountyHunter": "Bounty Hunter", "Mare": "Mare", "FireWorks": "Fireworker", - "SerialKiller": "Mercenary", + "Mercenary": "Mercenary", "Vampire": "Vampire", "Warlock": "Warlock", "Assassin": "Ninja", @@ -295,7 +295,7 @@ "Pelican": "Pelican", "Revolutionist": "Revolutionist", "FFF": "Hater", - "Gamer": "Demon", + "Demon": "Demon", "DarkHide": "Stalker", "Workaholic": "Workaholic", "Collector": "Collector", @@ -309,7 +309,7 @@ "Succubus": "Cultist", "HexMaster": "Hex Master", "Wraith": "Wraith", - "NSerialKiller": "Serial Killer", + "SerialKiller": "Serial Killer", "Juggernaut": "Juggernaut", "Infectious": "Infectious", "Virus": "Virus", @@ -456,7 +456,7 @@ "BountyHunterInfo": "Eliminate your target", "MareInfo": "Kill in the dark", "FireWorksInfo": "Go out with a BANG", - "SerialKillerInfo": "Keep killing, else you suicide", + "MercenaryInfo": "Keep killing, else you suicide", "VampireInfo": "Your kills are delayed", "WarlockInfo": "Curse crewmates & shift to make them kill", "AssassinInfo": "Mark a target, then shift to kill them", @@ -575,7 +575,7 @@ "PelicanInfo": "Eat all players", "RevolutionistInfo": "Recruit players to win with you", "FFFInfo": "Kill Lovers and Converted players", - "GamerInfo": "Consume blood volumes", + "DemonInfo": "Consume blood volumes", "DarkHideInfo": "Your kills cause power outages", "WorkaholicInfo": "Finish all tasks to solo win!", "CollectorInfo": "Collect votes from players", @@ -589,7 +589,7 @@ "SuccubusInfo": "Charm everyone", "HexMasterInfo": "Hex players to kill them in meetings", "WraithInfo": "Vent to go invisible temporarily", - "NSerialKillerInfo": "Kill everyone to win!", + "SerialKillerInfo": "Kill everyone to win!", "JuggernautInfo": "Your cooldown decreases with each kill you get", "InfectiousInfo": "Infect everyone", "VirusInfo": "Kill and infect everyone", @@ -685,7 +685,7 @@ "PhantomEHRInfoLong": "(Impostors):\\nAs the Phantom, you can go invisible for a short period. During this time, you can't be seen by Crewmates.", "BountyHunterInfoLong": "(Impostors):\\nIf you successfully eliminate your assigned target, your next kill cooldown will decrease, but if you eliminate anyone else, your next kill cooldown will increase; the target will switch after a predetermined time.", "FireWorksInfoLong": "(Impostors):\\nAs a Fireworker, you can plant up to three firework bombs using vanish/by petting. If you're the last impostor standing, you can use vanish/pet to detonate the bombs and cause area damage. But be careful, the bombs can also harm you. If the bombs eliminate all surviving players, it's a victory for the impostors.", - "SerialKillerInfoLong": "(Impostors):\\nYou have a shorter kill cooldown than other impostors, but you will die if you don't kill before the end of your shapeshift cooldown.", + "MercenaryInfoLong": "(Impostors):\\nYou have a shorter kill cooldown than other impostors, but you will die if you don't kill before the end of your shapeshift cooldown.", "MareInfoLong": "(Add-ons):\\nAs the Mare, you have a low kill cooldown but can only kill during lights. Additionally, your name will appear in red during lights. Only assigned to Impostors and cannot be guessed.", "VampireInfoLong": "(Impostors):\\nVampire bites delay death. Meeting kills bitten. Biting Bait = normal kill, self-report.", "WarlockInfoLong": "(Impostors):\\nWarlock can curse a player while not Shapeshifted. If they vanish/pet after cursing, the closest player to the cursed one is killed, regardless of distance. Only one player can be cursed at a time.\\n\\nKilling, shapeshifting/petting, and cursing all have separate cooldowns. The Warlock may freeze in place for a few seconds after killing via cursed player.", @@ -805,7 +805,7 @@ "PelicanInfoLong": "(Neutrals):\\nAs the Pelican, you can use the kill button and swallow a player alive. Swallowed players are teleported out of the map and cannot interact with the game. In case of an emergency meeting or reporting a dead body, all swallowed players die instantly. This is not a standard way to kill, and role skills such as the Bodyguard and Veteran are ineffective. If you die or disconnect, all previously swallowed players are returned to your location.", "RevolutionistInfoLong": "(Neutrals):\\nThe Revolutionist can recruit players by clicking the kill button on them and following them for a few seconds. If recruiting is successful, a shield animation is shown as a reminder. Players recruited are likely to be immediately killed, a probability set by the host. The Revolutionist and all recruited players win if the specified number of players vent within the set time, otherwise, the recruited player dies. The Revolutionist dies if killed during a countdown or at a meeting. Players who died after recruitment can still win with the Revolutionist.", "FFFInfoLong": "(Neutrals):\\nAs the Hater, you can kill as often as you want with no cooldown and have impostor vision. You can only kill Lovers, recruited players, and recruiting roles, or you will die. Winning is possible if you kill at least one player during the game.", - "GamerInfoLong": "(Neutrals):\\nIf the number of Demons is greater than Crewmates, Demons win. Demons have a unique health-based killing system, and killing takes effect when one's health is exhausted. If a Demon's health is consumed but they are not killed, the murderer will see a shield animation as a reminder.", + "DemonInfoLong": "(Neutrals):\\nDemon has a unique health-based killing system, and killing takes effect when one's health is exhausted. If a Demon's health is consumed but they are not killed, the murderer will see a shield animation as a reminder.", "DarkHideInfoLong": "(Neutrals):\\nThe Stalker can kill anyone and cause electricity sabotage. They cannot vent. If the Stalker is alive and the Impostor or Crewmate wins, the Stalker wins alone.", "WorkaholicInfoLong": "(Neutrals):\\nWorkaholics win by completing more tasks, maybe even after their death.", "CollectorInfoLong": "(Neutrals):\\nCollectors can gather all the votes cast by a target in a meeting. When they collect the specified number of votes, they win, even over exiled players.", @@ -819,7 +819,7 @@ "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 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.", + "SerialKillerInfoLong": "(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.", "PursuerInfoLong": "(Neutrals):\\nAs the Pursuer, you can use your ability on someone to make them shoot a blank when they try to kill (= reset their kill cooldown).\\n\\nTo win, survive to the end of the game.", @@ -1456,7 +1456,7 @@ "LawyerCanTargetJester": "Can Target Jester", "LawyerChangeRolesAfterTargetKilled": "When Target Dies, Lawyer becomes", "LawyerChangeRole": "Your client is dead", - "SerialKillerLimit": "Time Until Suicide", + "MercenaryLimit": "Time Until Suicide", "ArsonistDouseTime": "Douse Duration", "CanTerroristSuicideWin": "Can Win By Suicide", "FireWorksMaxCount": "Fireworks Count", @@ -1659,11 +1659,11 @@ "ContagiousCountMode.Virus": "Virus", "ContagiousCountMode.Original": "Original Team", "SnatchesWin": "Snatches victory", - "GamerKillCooldown": "Attack Cooldown", - "GamerHealthMax": "Player max health", - "GamerDamage": "Damage ", - "GamerSelfHealthMax": "Demon max health", - "GamerSelfDamage": "Demon damage received", + "DemonKillCooldown": "Attack Cooldown", + "DemonHealthMax": "Player max health", + "DemonDamage": "Damage ", + "DemonSelfHealthMax": "Demon max health", + "DemonSelfDamage": "Demon damage received", "BallLightningConvertTime": "Duration of the transformation to Quantum Ghost", "BallLightningKillCooldown": "Lightning Cooldown", "BallLightningKillerConvertGhost": "Killer can transform into Quantum Ghost", @@ -3116,7 +3116,7 @@ "SniperSnipeButtonText": "Snipe", "FireWorksExplosionButtonText": "Detonate", "FireWorksInstallAtionButtonText": "Install", - "SerialKillerSuicideButtonText": "Suicide Timer", + "MercenarySuicideButtonText": "Suicide Timer", "WarlockCurseButtonText": "Curse", "AssassinMarkButtonText": "Mark", "UndertakerMarkButtonText": "Mark", @@ -3137,7 +3137,7 @@ "RevolutionistDrawButtonText": "Win over", "FFFButtonText": "Hatred", "MedicalerButtonText": "Protect", - "GamerButtonText": "Attack", + "DemonButtonText": "Attack", "ImperiusCurseButtonText": "Teleport", "BallLightningButtonText": "Evaporate", "ProvocateurButtonText": "Greet", @@ -9638,7 +9638,7 @@ "WinnerRoleText.Pelican": "Pelican Wins!", "WinnerRoleText.Youtuber": "YouTuber Wins!", "WinnerRoleText.Egoist": "Egoist Wins!", - "WinnerRoleText.Gamer": "Demon Wins!", + "WinnerRoleText.Demon": "Demon Wins!", "WinnerRoleText.DarkHide": "Stalker Wins!", "WinnerRoleText.Workaholic": "Workaholic Wins!", "WinnerRoleText.Agitater": "Agitator Wins!", @@ -9649,7 +9649,7 @@ "WinnerRoleText.Succubus": "Cultist Wins!", "WinnerRoleText.Necromancer": "Undead Faction Wins!", "WinnerRoleText.Wraith": "Wraith Wins!", - "WinnerRoleText.NSerialKiller": "Serial Killer Wins!", + "WinnerRoleText.SerialKiller": "Serial Killer Wins!", "WinnerRoleText.Juggernaut": "Juggernaut Wins!", "WinnerRoleText.Infectious": "Infectious Wins!", "WinnerRoleText.Virus": "Virus Wins!", diff --git a/Roles/Crewmate/Addict.cs b/Roles/Crewmate/Addict.cs index dad16989a..e2e21d240 100644 --- a/Roles/Crewmate/Addict.cs +++ b/Roles/Crewmate/Addict.cs @@ -35,7 +35,7 @@ public class Addict : RoleBase VentCooldown = new FloatOptionItem(Id + 11, "VentCooldown", new(5f, 70f, 1f), 40f, TabGroup.CrewmateRoles).SetParent(CustomRoleSpawnChances[CustomRoles.Addict]) .SetValueFormat(OptionFormat.Seconds); - TimeLimit = new FloatOptionItem(Id + 12, "SerialKillerLimit", new(5f, 75f, 1f), 45f, TabGroup.CrewmateRoles).SetParent(CustomRoleSpawnChances[CustomRoles.Addict]) + TimeLimit = new FloatOptionItem(Id + 12, "MercenaryLimit", new(5f, 75f, 1f), 45f, TabGroup.CrewmateRoles).SetParent(CustomRoleSpawnChances[CustomRoles.Addict]) .SetValueFormat(OptionFormat.Seconds); ImmortalTimeAfterVent = new FloatOptionItem(Id + 13, "AddictInvulnerbilityTimeAfterVent", new(0f, 30f, 1f), 10f, TabGroup.CrewmateRoles).SetParent(CustomRoleSpawnChances[CustomRoles.Addict]) diff --git a/Roles/Crewmate/CopyCat.cs b/Roles/Crewmate/CopyCat.cs index 8817c975c..48c608049 100644 --- a/Roles/Crewmate/CopyCat.cs +++ b/Roles/Crewmate/CopyCat.cs @@ -103,7 +103,7 @@ public class CopyCat : RoleBase CustomRoles.AntiAdminer => CustomRoles.Monitor, CustomRoles.CursedWolf or CustomRoles.Jinx => CustomRoles.Veteran, CustomRoles.EvilTracker => CustomRoles.TrackerEHR, - CustomRoles.SerialKiller => CustomRoles.Addict, + CustomRoles.Mercenary => CustomRoles.Addict, CustomRoles.Miner => CustomRoles.Mole, CustomRoles.Escapee => CustomRoles.Tunneler, CustomRoles.Twister => CustomRoles.TimeMaster, diff --git a/Roles/Crewmate/DovesOfNeace.cs b/Roles/Crewmate/DovesOfNeace.cs index 0bf3e52fe..fb7cb25b0 100644 --- a/Roles/Crewmate/DovesOfNeace.cs +++ b/Roles/Crewmate/DovesOfNeace.cs @@ -89,7 +89,7 @@ internal class DovesOfNeace : RoleBase x.RPCPlayCustomSound("Dove"); x.ResetKillCooldown(); x.SetKillCooldown(); - if (Main.PlayerStates[x.PlayerId].Role is SerialKiller sk) sk.OnReportDeadBody(); + if (Main.PlayerStates[x.PlayerId].Role is Mercenary m) m.OnReportDeadBody(); x.Notify(Utils.ColorString(Utils.GetRoleColor(CustomRoles.DovesOfNeace), Translator.GetString("DovesOfNeaceSkillNotify"))); }); diff --git a/Roles/Impostor/Mastermind.cs b/Roles/Impostor/Mastermind.cs index 519afde82..dc9b39ed7 100644 --- a/Roles/Impostor/Mastermind.cs +++ b/Roles/Impostor/Mastermind.cs @@ -111,7 +111,7 @@ public class Mastermind : RoleBase pc.SetKillCooldown(1f); } else - pc.RpcChangeRoleBasis(CustomRoles.NSerialKiller); + pc.RpcChangeRoleBasis(CustomRoles.SerialKiller); NotifyRoles(SpecifySeer: MastermindPC, SpecifyTarget: pc); } diff --git a/Roles/Impostor/SerialKiller.cs b/Roles/Impostor/Mercenary.cs similarity index 84% rename from Roles/Impostor/SerialKiller.cs rename to Roles/Impostor/Mercenary.cs index 9f96cc3a2..1b32a3007 100644 --- a/Roles/Impostor/SerialKiller.cs +++ b/Roles/Impostor/Mercenary.cs @@ -1,118 +1,118 @@ -using System.Collections.Generic; -using EHR.Modules; -using UnityEngine; - -namespace EHR.Impostor; - -public class SerialKiller : RoleBase -{ - private const int Id = 1700; - public static List PlayerIdList = []; - - private static OptionItem KillCooldown; - private static OptionItem TimeLimit; - private static OptionItem WaitFor1Kill; - - private float SuicideTimer; - private int Timer; - - public override bool IsEnable => PlayerIdList.Count > 0; - - public override void SetupCustomOption() - { - Options.SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.SerialKiller); - - KillCooldown = new FloatOptionItem(Id + 10, "KillCooldown", new(0f, 180f, 0.5f), 22.5f, TabGroup.ImpostorRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SerialKiller]) - .SetValueFormat(OptionFormat.Seconds); - - TimeLimit = new FloatOptionItem(Id + 11, "SerialKillerLimit", new(5f, 180f, 5f), 40f, TabGroup.ImpostorRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SerialKiller]) - .SetValueFormat(OptionFormat.Seconds); - - WaitFor1Kill = new BooleanOptionItem(Id + 12, "WaitFor1Kill", true, TabGroup.ImpostorRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SerialKiller]); - } - - public override void Init() - { - PlayerIdList = []; - SuicideTimer = TimeLimit.GetFloat(); - Timer = TimeLimit.GetInt(); - } - - public override void Add(byte serial) - { - PlayerIdList.Add(serial); - Timer = TimeLimit.GetInt(); - SuicideTimer = TimeLimit.GetFloat(); - } - - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - - public override void SetKillCooldown(byte id) - { - Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); - } - - private static bool HasKilled(PlayerControl pc) - { - return pc != null && pc.Is(CustomRoles.SerialKiller) && pc.IsAlive() && (Main.PlayerStates[pc.PlayerId].GetKillCount(true) > 0 || !WaitFor1Kill.GetBool()); - } - - public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) - { - if (!killer.Is(CustomRoles.SerialKiller)) return true; - - SuicideTimer = float.NaN; - Timer = TimeLimit.GetInt(); - killer.MarkDirtySettings(); - return true; - } - - public override void OnReportDeadBody() - { - SuicideTimer = float.NaN; - Timer = TimeLimit.GetInt(); - } - - public override void OnFixedUpdate(PlayerControl player) - { - if (!GameStates.IsInTask) return; - - if (!HasKilled(player)) - { - SuicideTimer = float.NaN; - Timer = TimeLimit.GetInt(); - return; - } - - if (float.IsNaN(SuicideTimer)) return; - - if (SuicideTimer >= TimeLimit.GetFloat()) - { - player.Suicide(); - SuicideTimer = float.NaN; - Timer = TimeLimit.GetInt(); - - if (player.IsLocalPlayer()) - Achievements.Type.OutOfTime.Complete(); - } - else - { - SuicideTimer += Time.fixedDeltaTime; - int tempTimer = Timer; - Timer = TimeLimit.GetInt() - (int)SuicideTimer; - if (Timer != tempTimer && Timer <= 20 && !player.IsModdedClient()) Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player); - } - } - - public override void AfterMeetingTasks() - { - SuicideTimer = 0f; - Timer = TimeLimit.GetInt(); - } +using System.Collections.Generic; +using EHR.Modules; +using UnityEngine; + +namespace EHR.Impostor; + +public class Mercenary : RoleBase +{ + private const int Id = 1700; + public static List PlayerIdList = []; + + private static OptionItem KillCooldown; + private static OptionItem TimeLimit; + private static OptionItem WaitFor1Kill; + + private float SuicideTimer; + private int Timer; + + public override bool IsEnable => PlayerIdList.Count > 0; + + public override void SetupCustomOption() + { + Options.SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Mercenary); + + KillCooldown = new FloatOptionItem(Id + 10, "KillCooldown", new(0f, 180f, 0.5f), 22.5f, TabGroup.ImpostorRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mercenary]) + .SetValueFormat(OptionFormat.Seconds); + + TimeLimit = new FloatOptionItem(Id + 11, "MercenaryLimit", new(5f, 180f, 5f), 40f, TabGroup.ImpostorRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mercenary]) + .SetValueFormat(OptionFormat.Seconds); + + WaitFor1Kill = new BooleanOptionItem(Id + 12, "WaitFor1Kill", true, TabGroup.ImpostorRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mercenary]); + } + + public override void Init() + { + PlayerIdList = []; + SuicideTimer = TimeLimit.GetFloat(); + Timer = TimeLimit.GetInt(); + } + + public override void Add(byte serial) + { + PlayerIdList.Add(serial); + Timer = TimeLimit.GetInt(); + SuicideTimer = TimeLimit.GetFloat(); + } + + public override void Remove(byte playerId) + { + PlayerIdList.Remove(playerId); + } + + public override void SetKillCooldown(byte id) + { + Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); + } + + private static bool HasKilled(PlayerControl pc) + { + return pc != null && pc.Is(CustomRoles.Mercenary) && pc.IsAlive() && (Main.PlayerStates[pc.PlayerId].GetKillCount(true) > 0 || !WaitFor1Kill.GetBool()); + } + + public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) + { + if (!killer.Is(CustomRoles.Mercenary)) return true; + + SuicideTimer = float.NaN; + Timer = TimeLimit.GetInt(); + killer.MarkDirtySettings(); + return true; + } + + public override void OnReportDeadBody() + { + SuicideTimer = float.NaN; + Timer = TimeLimit.GetInt(); + } + + public override void OnFixedUpdate(PlayerControl player) + { + if (!GameStates.IsInTask) return; + + if (!HasKilled(player)) + { + SuicideTimer = float.NaN; + Timer = TimeLimit.GetInt(); + return; + } + + if (float.IsNaN(SuicideTimer)) return; + + if (SuicideTimer >= TimeLimit.GetFloat()) + { + player.Suicide(); + SuicideTimer = float.NaN; + Timer = TimeLimit.GetInt(); + + if (player.IsLocalPlayer()) + Achievements.Type.OutOfTime.Complete(); + } + else + { + SuicideTimer += Time.fixedDeltaTime; + int tempTimer = Timer; + Timer = TimeLimit.GetInt() - (int)SuicideTimer; + if (Timer != tempTimer && Timer <= 20 && !player.IsModdedClient()) Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player); + } + } + + public override void AfterMeetingTasks() + { + SuicideTimer = 0f; + Timer = TimeLimit.GetInt(); + } } \ No newline at end of file diff --git a/Roles/Impostor/Assassin.cs b/Roles/Impostor/Ninja.cs similarity index 91% rename from Roles/Impostor/Assassin.cs rename to Roles/Impostor/Ninja.cs index d1c339c41..ee59c59b3 100644 --- a/Roles/Impostor/Assassin.cs +++ b/Roles/Impostor/Ninja.cs @@ -1,232 +1,232 @@ -using System.Collections.Generic; -using AmongUs.GameOptions; -using EHR.Modules; -using EHR.Neutral; -using Hazel; -using static EHR.Options; -using static EHR.Translator; - -namespace EHR.Impostor; - -internal class Assassin : RoleBase -{ - private const int Id = 700; - public static List PlayerIdList = []; - - private static OptionItem MarkCooldownOpt; - public static OptionItem AssassinateCooldownOpt; - private static OptionItem CanKillAfterAssassinateOpt; - private static OptionItem InvisibilityTimeAfterAssassinateOpt; - - private float AssassinateCooldown; - private bool CanKillAfterAssassinate; - private bool IsUndertaker; - private float MarkCooldown; - public byte MarkedPlayer; - - public override bool IsEnable => PlayerIdList.Count > 0; - - public override void SetupCustomOption() - { - SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Assassin); - - MarkCooldownOpt = new FloatOptionItem(Id + 10, "AssassinMarkCooldown", new(0f, 180f, 0.5f), 1f, TabGroup.ImpostorRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Assassin]) - .SetValueFormat(OptionFormat.Seconds); - - AssassinateCooldownOpt = new FloatOptionItem(Id + 11, "AssassinAssassinateCooldown", new(0f, 180f, 0.5f), 18.5f, TabGroup.ImpostorRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Assassin]) - .SetValueFormat(OptionFormat.Seconds); - - CanKillAfterAssassinateOpt = new BooleanOptionItem(Id + 12, "AssassinCanKillAfterAssassinate", true, TabGroup.ImpostorRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Assassin]); - - InvisibilityTimeAfterAssassinateOpt = new FloatOptionItem(Id + 13, "AssassinInvisibilityTimeAfterAssassinate", new(0f, 30f, 0.5f), 5f, TabGroup.ImpostorRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Assassin]) - .SetValueFormat(OptionFormat.Seconds); - } - - public override void Init() - { - PlayerIdList = []; - MarkedPlayer = byte.MaxValue; - IsUndertaker = false; - } - - public override void Add(byte playerId) - { - PlayerIdList.Add(playerId); - MarkedPlayer = byte.MaxValue; - IsUndertaker = Main.PlayerStates[playerId].MainRole == CustomRoles.Undertaker; - - if (IsUndertaker) - { - MarkCooldown = Undertaker.UndertakerMarkCooldown.GetFloat(); - AssassinateCooldown = Undertaker.UndertakerAssassinateCooldown.GetFloat(); - CanKillAfterAssassinate = Undertaker.UndertakerCanKillAfterAssassinate.GetBool(); - } - else - { - MarkCooldown = MarkCooldownOpt.GetFloat(); - AssassinateCooldown = AssassinateCooldownOpt.GetFloat(); - CanKillAfterAssassinate = CanKillAfterAssassinateOpt.GetBool(); - } - } - - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - - private void SendRPC(byte playerId) - { - if (!IsEnable || !Utils.DoRPC) return; - - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetMarkedPlayer, SendOption.Reliable); - writer.Write(playerId); - writer.Write(MarkedPlayer); - AmongUsClient.Instance.FinishRpcImmediately(writer); - } - - public static void ReceiveRPC(MessageReader reader) - { - byte playerId = reader.ReadByte(); - byte targetId = reader.ReadByte(); - - if (Main.PlayerStates[playerId].Role is not Assassin assassin) return; - - assassin.MarkedPlayer = targetId; - } - - public override void SetKillCooldown(byte id) - { - Main.AllPlayerKillCooldown[id] = id.IsPlayerShifted() ? AdjustedDefaultKillCooldown : MarkCooldown; - } - - public override void ApplyGameOptions(IGameOptions opt, byte id) - { - if (UsePhantomBasis.GetBool()) - AURoleOptions.PhantomCooldown = AssassinateCooldown; - else - { - if (UsePets.GetBool()) return; - - AURoleOptions.ShapeshifterCooldown = AssassinateCooldown; - } - } - - public override bool CanUseKillButton(PlayerControl pc) - { - if (pc == null || !pc.IsAlive()) return false; - return CanKillAfterAssassinate || (!pc.IsShifted() && (pc.Data.Role as PhantomRole) is null or { IsInvisible: false }); - } - - public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) - { - if (killer.IsShifted()) - { - 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; - SendRPC(killer.PlayerId); - killer.ResetKillCooldown(); - killer.SetKillCooldown(); - if (killer.IsModdedClient()) killer.RpcResetAbilityCooldown(); - - killer.SyncSettings(); - killer.RPCPlayCustomSound("Clothe"); - return false; - } - - public override void OnPet(PlayerControl pc) - { - Take(pc); - } - - public override bool OnShapeshift(PlayerControl pc, PlayerControl t, bool shapeshifting) - { - if (!pc.IsAlive() || Pelican.IsEaten(pc.PlayerId)) return false; - - if (!shapeshifting) return true; - - Take(pc); - - return false; - } - - public override bool OnVanish(PlayerControl pc) - { - if (pc == null || !pc.IsAlive() || Pelican.IsEaten(pc.PlayerId)) return false; - - if (!IsUndertaker) - { - float time = InvisibilityTimeAfterAssassinateOpt.GetFloat(); - - if (time >= 1f) - { - pc.RpcMakeInvisible(); - - LateTask.New(() => - { - if (!GameStates.IsInTask || ExileController.Instance || AntiBlackout.SkipTasks) return; - pc.RpcMakeVisible(); - }, time, log: false); - } - } - - Take(pc); - return false; - } - - private void Take(PlayerControl pc) - { - if (MarkedPlayer != byte.MaxValue) - { - PlayerControl target = Utils.GetPlayerById(MarkedPlayer); - bool tpSuccess = IsUndertaker ? target.TP(pc) : pc.TP(target); - - if (!(target == null || !target.IsAlive() || Pelican.IsEaten(target.PlayerId) || target.inVent || !GameStates.IsInTask) && tpSuccess && pc.RpcCheckAndMurder(target)) - { - MarkedPlayer = byte.MaxValue; - SendRPC(pc.PlayerId); - pc.ResetKillCooldown(); - pc.SyncSettings(); - pc.SetKillCooldown(AdjustedDefaultKillCooldown); - } - else if (!tpSuccess) pc.Notify(GetString("TargetCannotBeTeleported")); - } - } - - public override void SetButtonTexts(HudManager __instance, byte playerId) - { - bool shifted = playerId.IsPlayerShifted(); - __instance.KillButton.OverrideText(!shifted ? GetString("AssassinMarkButtonText") : GetString("KillButtonText")); - - if (MarkedPlayer != byte.MaxValue && !shifted) - { - if (!UsePets.GetBool()) - __instance.AbilityButton.OverrideText(GetString("AssassinShapeshiftText")); - else - __instance.PetButton.OverrideText(GetString("AssassinShapeshiftText")); - } - } +using System.Collections.Generic; +using AmongUs.GameOptions; +using EHR.Modules; +using EHR.Neutral; +using Hazel; +using static EHR.Options; +using static EHR.Translator; + +namespace EHR.Impostor; + +internal class Ninja : RoleBase +{ + private const int Id = 700; + public static List PlayerIdList = []; + + private static OptionItem MarkCooldownOpt; + public static OptionItem AssassinateCooldownOpt; + private static OptionItem CanKillAfterAssassinateOpt; + private static OptionItem InvisibilityTimeAfterAssassinateOpt; + + private float AssassinateCooldown; + private bool CanKillAfterAssassinate; + private bool IsUndertaker; + private float MarkCooldown; + public byte MarkedPlayer; + + public override bool IsEnable => PlayerIdList.Count > 0; + + public override void SetupCustomOption() + { + SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Ninja); + + MarkCooldownOpt = new FloatOptionItem(Id + 10, "AssassinMarkCooldown", new(0f, 180f, 0.5f), 1f, TabGroup.ImpostorRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Ninja]) + .SetValueFormat(OptionFormat.Seconds); + + AssassinateCooldownOpt = new FloatOptionItem(Id + 11, "AssassinAssassinateCooldown", new(0f, 180f, 0.5f), 18.5f, TabGroup.ImpostorRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Ninja]) + .SetValueFormat(OptionFormat.Seconds); + + CanKillAfterAssassinateOpt = new BooleanOptionItem(Id + 12, "AssassinCanKillAfterAssassinate", true, TabGroup.ImpostorRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Ninja]); + + InvisibilityTimeAfterAssassinateOpt = new FloatOptionItem(Id + 13, "AssassinInvisibilityTimeAfterAssassinate", new(0f, 30f, 0.5f), 5f, TabGroup.ImpostorRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Ninja]) + .SetValueFormat(OptionFormat.Seconds); + } + + public override void Init() + { + PlayerIdList = []; + MarkedPlayer = byte.MaxValue; + IsUndertaker = false; + } + + public override void Add(byte playerId) + { + PlayerIdList.Add(playerId); + MarkedPlayer = byte.MaxValue; + IsUndertaker = Main.PlayerStates[playerId].MainRole == CustomRoles.Undertaker; + + if (IsUndertaker) + { + MarkCooldown = Undertaker.UndertakerMarkCooldown.GetFloat(); + AssassinateCooldown = Undertaker.UndertakerAssassinateCooldown.GetFloat(); + CanKillAfterAssassinate = Undertaker.UndertakerCanKillAfterAssassinate.GetBool(); + } + else + { + MarkCooldown = MarkCooldownOpt.GetFloat(); + AssassinateCooldown = AssassinateCooldownOpt.GetFloat(); + CanKillAfterAssassinate = CanKillAfterAssassinateOpt.GetBool(); + } + } + + public override void Remove(byte playerId) + { + PlayerIdList.Remove(playerId); + } + + private void SendRPC(byte playerId) + { + if (!IsEnable || !Utils.DoRPC) return; + + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetMarkedPlayer, SendOption.Reliable); + writer.Write(playerId); + writer.Write(MarkedPlayer); + AmongUsClient.Instance.FinishRpcImmediately(writer); + } + + public static void ReceiveRPC(MessageReader reader) + { + byte playerId = reader.ReadByte(); + byte targetId = reader.ReadByte(); + + if (Main.PlayerStates[playerId].Role is not Ninja ninja) return; + + ninja.MarkedPlayer = targetId; + } + + public override void SetKillCooldown(byte id) + { + Main.AllPlayerKillCooldown[id] = id.IsPlayerShifted() ? AdjustedDefaultKillCooldown : MarkCooldown; + } + + public override void ApplyGameOptions(IGameOptions opt, byte id) + { + if (UsePhantomBasis.GetBool()) + AURoleOptions.PhantomCooldown = AssassinateCooldown; + else + { + if (UsePets.GetBool()) return; + + AURoleOptions.ShapeshifterCooldown = AssassinateCooldown; + } + } + + public override bool CanUseKillButton(PlayerControl pc) + { + if (pc == null || !pc.IsAlive()) return false; + return CanKillAfterAssassinate || (!pc.IsShifted() && (pc.Data.Role as PhantomRole) is null or { IsInvisible: false }); + } + + public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) + { + if (killer.IsShifted()) + { + 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; + SendRPC(killer.PlayerId); + killer.ResetKillCooldown(); + killer.SetKillCooldown(); + if (killer.IsModdedClient()) killer.RpcResetAbilityCooldown(); + + killer.SyncSettings(); + killer.RPCPlayCustomSound("Clothe"); + return false; + } + + public override void OnPet(PlayerControl pc) + { + Take(pc); + } + + public override bool OnShapeshift(PlayerControl pc, PlayerControl t, bool shapeshifting) + { + if (!pc.IsAlive() || Pelican.IsEaten(pc.PlayerId)) return false; + + if (!shapeshifting) return true; + + Take(pc); + + return false; + } + + public override bool OnVanish(PlayerControl pc) + { + if (pc == null || !pc.IsAlive() || Pelican.IsEaten(pc.PlayerId)) return false; + + if (!IsUndertaker) + { + float time = InvisibilityTimeAfterAssassinateOpt.GetFloat(); + + if (time >= 1f) + { + pc.RpcMakeInvisible(); + + LateTask.New(() => + { + if (!GameStates.IsInTask || ExileController.Instance || AntiBlackout.SkipTasks) return; + pc.RpcMakeVisible(); + }, time, log: false); + } + } + + Take(pc); + return false; + } + + private void Take(PlayerControl pc) + { + if (MarkedPlayer != byte.MaxValue) + { + PlayerControl target = Utils.GetPlayerById(MarkedPlayer); + bool tpSuccess = IsUndertaker ? target.TP(pc) : pc.TP(target); + + if (!(target == null || !target.IsAlive() || Pelican.IsEaten(target.PlayerId) || target.inVent || !GameStates.IsInTask) && tpSuccess && pc.RpcCheckAndMurder(target)) + { + MarkedPlayer = byte.MaxValue; + SendRPC(pc.PlayerId); + pc.ResetKillCooldown(); + pc.SyncSettings(); + pc.SetKillCooldown(AdjustedDefaultKillCooldown); + } + else if (!tpSuccess) pc.Notify(GetString("TargetCannotBeTeleported")); + } + } + + public override void SetButtonTexts(HudManager __instance, byte playerId) + { + bool shifted = playerId.IsPlayerShifted(); + __instance.KillButton.OverrideText(!shifted ? GetString("AssassinMarkButtonText") : GetString("KillButtonText")); + + if (MarkedPlayer != byte.MaxValue && !shifted) + { + if (!UsePets.GetBool()) + __instance.AbilityButton.OverrideText(GetString("AssassinShapeshiftText")); + else + __instance.PetButton.OverrideText(GetString("AssassinShapeshiftText")); + } + } } \ No newline at end of file diff --git a/Roles/Neutral/Gamer.cs b/Roles/Neutral/Demon.cs similarity index 75% rename from Roles/Neutral/Gamer.cs rename to Roles/Neutral/Demon.cs index 0e1641441..94568f969 100644 --- a/Roles/Neutral/Gamer.cs +++ b/Roles/Neutral/Demon.cs @@ -1,207 +1,207 @@ -using System.Collections.Generic; -using AmongUs.GameOptions; -using EHR.Modules; -using Hazel; -using UnityEngine; -using static EHR.Options; - -namespace EHR.Neutral; - -public class Gamer : RoleBase -{ - private const int Id = 10600; - public static List PlayerIdList = []; - - private static Dictionary PlayerHealth = []; - private static Dictionary GamerHealth = []; - - private static OptionItem KillCooldown; - private static OptionItem CanVent; - private static OptionItem HasImpostorVision; - private static OptionItem HealthMax; - private static OptionItem Damage; - private static OptionItem SelfHealthMax; - private static OptionItem SelfDamage; - - public override bool IsEnable => PlayerIdList.Count > 0; - - public override void SetupCustomOption() - { - SetupRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.Gamer); - - KillCooldown = new FloatOptionItem(Id + 10, "GamerKillCooldown", new(1f, 180f, 1f), 2f, TabGroup.NeutralRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Gamer]) - .SetValueFormat(OptionFormat.Seconds); - - CanVent = new BooleanOptionItem(Id + 11, "CanVent", true, TabGroup.NeutralRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Gamer]); - - HasImpostorVision = new BooleanOptionItem(Id + 13, "ImpostorVision", false, TabGroup.NeutralRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Gamer]); - - HealthMax = new IntegerOptionItem(Id + 15, "GamerHealthMax", new(5, 300, 5), 100, TabGroup.NeutralRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Gamer]) - .SetValueFormat(OptionFormat.Health); - - Damage = new IntegerOptionItem(Id + 16, "GamerDamage", new(1, 100, 1), 15, TabGroup.NeutralRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Gamer]) - .SetValueFormat(OptionFormat.Health); - - SelfHealthMax = new IntegerOptionItem(Id + 17, "GamerSelfHealthMax", new(100, 100, 5), 100, TabGroup.NeutralRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Gamer]) - .SetValueFormat(OptionFormat.Health); - - SelfDamage = new IntegerOptionItem(Id + 18, "GamerSelfDamage", new(1, 100, 1), 35, TabGroup.NeutralRoles) - .SetParent(CustomRoleSpawnChances[CustomRoles.Gamer]) - .SetValueFormat(OptionFormat.Health); - } - - public override void Init() - { - PlayerIdList = []; - GamerHealth = []; - PlayerHealth = []; - } - - public override void Add(byte playerId) - { - PlayerIdList.Add(playerId); - GamerHealth[playerId] = SelfHealthMax.GetInt(); - - foreach (PlayerControl pc in Main.AllAlivePlayerControls) - PlayerHealth[pc.PlayerId] = HealthMax.GetInt(); - } - - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - - public override void SetKillCooldown(byte id) - { - Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); - } - - public override void ApplyGameOptions(IGameOptions opt, byte id) - { - opt.SetVision(HasImpostorVision.GetBool()); - } - - public override bool CanUseImpostorVentButton(PlayerControl pc) - { - return CanVent.GetBool(); - } - - private void SendRPC(byte playerId) - { - if (!IsEnable || !Utils.DoRPC) return; - - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetGamerHealth, SendOption.Reliable); - writer.Write(playerId); - writer.Write(GamerHealth.TryGetValue(playerId, out int value) ? value : PlayerHealth[playerId]); - AmongUsClient.Instance.FinishRpcImmediately(writer); - } - - public static void ReceiveRPC(MessageReader reader) - { - byte playerId = reader.ReadByte(); - int health = reader.ReadInt32(); - - if (GamerHealth.ContainsKey(playerId)) - GamerHealth[playerId] = health; - else - PlayerHealth[playerId] = health; - } - - public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) - { - if (killer == null || target == null || target.Is(CustomRoles.Gamer) || !PlayerHealth.TryGetValue(target.PlayerId, out var targetHealth)) return false; - - killer.SetKillCooldown(); - - if (targetHealth - Damage.GetInt() < 1) - { - if (target.Is(CustomRoles.Pestilence)) - { - target.Kill(killer); - - if (target.IsLocalPlayer()) - Achievements.Type.YoureTooLate.Complete(); - - return false; - } - - PlayerHealth.Remove(target.PlayerId); - killer.RpcCheckAndMurder(target); - Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); - return false; - } - - PlayerHealth[target.PlayerId] -= Damage.GetInt(); - SendRPC(target.PlayerId); - RPC.PlaySoundRPC(killer.PlayerId, Sounds.KillSound); - Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); - - Logger.Info($"{killer.GetNameWithRole().RemoveHtmlTags()} attacked {target.GetNameWithRole().RemoveHtmlTags()}, did {Damage.GetInt()} damage", "Gamer"); - return false; - } - - public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target) - { - if (killer == null || target == null || killer.Is(CustomRoles.Gamer)) return true; - - if (GamerHealth[target.PlayerId] - SelfDamage.GetInt() < 1) - { - GamerHealth.Remove(target.PlayerId); - Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: killer); - return true; - } - - killer.SetKillCooldown(); - - GamerHealth[target.PlayerId] -= SelfDamage.GetInt(); - SendRPC(target.PlayerId); - RPC.PlaySoundRPC(target.PlayerId, Sounds.KillSound); - Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: killer); - - Logger.Info($"{killer.GetNameWithRole().RemoveHtmlTags()} attacked {target.GetNameWithRole().RemoveHtmlTags()}, did {SelfDamage.GetInt()} damage", "Gamer"); - return false; - } - - public static string TargetMark(PlayerControl seer, PlayerControl target) - { - if (!seer.IsAlive() || !PlayerIdList.Contains(seer.PlayerId)) return string.Empty; - - if (seer.PlayerId == target.PlayerId) - { - return GamerHealth.TryGetValue(target.PlayerId, out int value) && value > 0 - ? Utils.ColorString(GetColor(value, true), $"【{value}/{SelfHealthMax.GetInt()}】") - : string.Empty; - } - else - { - return PlayerHealth.TryGetValue(target.PlayerId, out int value) && value > 0 - ? Utils.ColorString(GetColor(value), $"【{value}/{HealthMax.GetInt()}】") - : string.Empty; - } - } - - private static Color32 GetColor(float health, bool self = false) - { - var x = (int)(health / (self ? SelfHealthMax.GetInt() : HealthMax.GetInt()) * 10 * 50); - var r = 255; - var g = 255; - - if (x > 255) - r -= x - 255; - else - g = x; - - return new((byte)r, (byte)g, 0, byte.MaxValue); - } - - public override void SetButtonTexts(HudManager hud, byte id) - { - hud.KillButton?.OverrideText(Translator.GetString("GamerButtonText")); - } +using System.Collections.Generic; +using AmongUs.GameOptions; +using EHR.Modules; +using Hazel; +using UnityEngine; +using static EHR.Options; + +namespace EHR.Neutral; + +public class Demon : RoleBase +{ + private const int Id = 10600; + public static List PlayerIdList = []; + + private static Dictionary PlayerHealth = []; + private static Dictionary DemonHealth = []; + + private static OptionItem KillCooldown; + private static OptionItem CanVent; + private static OptionItem HasImpostorVision; + private static OptionItem HealthMax; + private static OptionItem Damage; + private static OptionItem SelfHealthMax; + private static OptionItem SelfDamage; + + public override bool IsEnable => PlayerIdList.Count > 0; + + public override void SetupCustomOption() + { + SetupRoleOptions(Id, TabGroup.NeutralRoles, CustomRoles.Demon); + + KillCooldown = new FloatOptionItem(Id + 10, "DemonKillCooldown", new(1f, 180f, 1f), 2f, TabGroup.NeutralRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Demon]) + .SetValueFormat(OptionFormat.Seconds); + + CanVent = new BooleanOptionItem(Id + 11, "CanVent", true, TabGroup.NeutralRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Demon]); + + HasImpostorVision = new BooleanOptionItem(Id + 13, "ImpostorVision", false, TabGroup.NeutralRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Demon]); + + HealthMax = new IntegerOptionItem(Id + 15, "DemonHealthMax", new(5, 300, 5), 100, TabGroup.NeutralRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Demon]) + .SetValueFormat(OptionFormat.Health); + + Damage = new IntegerOptionItem(Id + 16, "DemonDamage", new(1, 100, 1), 15, TabGroup.NeutralRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Demon]) + .SetValueFormat(OptionFormat.Health); + + SelfHealthMax = new IntegerOptionItem(Id + 17, "DemonSelfHealthMax", new(100, 100, 5), 100, TabGroup.NeutralRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Demon]) + .SetValueFormat(OptionFormat.Health); + + SelfDamage = new IntegerOptionItem(Id + 18, "DemonSelfDamage", new(1, 100, 1), 35, TabGroup.NeutralRoles) + .SetParent(CustomRoleSpawnChances[CustomRoles.Demon]) + .SetValueFormat(OptionFormat.Health); + } + + public override void Init() + { + PlayerIdList = []; + DemonHealth = []; + PlayerHealth = []; + } + + public override void Add(byte playerId) + { + PlayerIdList.Add(playerId); + DemonHealth[playerId] = SelfHealthMax.GetInt(); + + foreach (PlayerControl pc in Main.AllAlivePlayerControls) + PlayerHealth[pc.PlayerId] = HealthMax.GetInt(); + } + + public override void Remove(byte playerId) + { + PlayerIdList.Remove(playerId); + } + + public override void SetKillCooldown(byte id) + { + Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); + } + + public override void ApplyGameOptions(IGameOptions opt, byte id) + { + opt.SetVision(HasImpostorVision.GetBool()); + } + + public override bool CanUseImpostorVentButton(PlayerControl pc) + { + return CanVent.GetBool(); + } + + private void SendRPC(byte playerId) + { + if (!IsEnable || !Utils.DoRPC) return; + + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetDemonHealth, SendOption.Reliable); + writer.Write(playerId); + writer.Write(DemonHealth.TryGetValue(playerId, out int value) ? value : PlayerHealth[playerId]); + AmongUsClient.Instance.FinishRpcImmediately(writer); + } + + public static void ReceiveRPC(MessageReader reader) + { + byte playerId = reader.ReadByte(); + int health = reader.ReadInt32(); + + if (DemonHealth.ContainsKey(playerId)) + DemonHealth[playerId] = health; + else + PlayerHealth[playerId] = health; + } + + public override bool OnCheckMurder(PlayerControl killer, PlayerControl target) + { + if (killer == null || target == null || target.Is(CustomRoles.Demon) || !PlayerHealth.TryGetValue(target.PlayerId, out var targetHealth)) return false; + + killer.SetKillCooldown(); + + if (targetHealth - Damage.GetInt() < 1) + { + if (target.Is(CustomRoles.Pestilence)) + { + target.Kill(killer); + + if (target.IsLocalPlayer()) + Achievements.Type.YoureTooLate.Complete(); + + return false; + } + + PlayerHealth.Remove(target.PlayerId); + killer.RpcCheckAndMurder(target); + Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); + return false; + } + + PlayerHealth[target.PlayerId] -= Damage.GetInt(); + SendRPC(target.PlayerId); + RPC.PlaySoundRPC(killer.PlayerId, Sounds.KillSound); + Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target); + + Logger.Info($"{killer.GetNameWithRole().RemoveHtmlTags()} attacked {target.GetNameWithRole().RemoveHtmlTags()}, did {Damage.GetInt()} damage", "Demon"); + return false; + } + + public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target) + { + if (killer == null || target == null || killer.Is(CustomRoles.Demon)) return true; + + if (DemonHealth[target.PlayerId] - SelfDamage.GetInt() < 1) + { + DemonHealth.Remove(target.PlayerId); + Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: killer); + return true; + } + + killer.SetKillCooldown(); + + DemonHealth[target.PlayerId] -= SelfDamage.GetInt(); + SendRPC(target.PlayerId); + RPC.PlaySoundRPC(target.PlayerId, Sounds.KillSound); + Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: killer); + + Logger.Info($"{killer.GetNameWithRole().RemoveHtmlTags()} attacked {target.GetNameWithRole().RemoveHtmlTags()}, did {SelfDamage.GetInt()} damage", "Demon"); + return false; + } + + public static string TargetMark(PlayerControl seer, PlayerControl target) + { + if (!seer.IsAlive() || !PlayerIdList.Contains(seer.PlayerId)) return string.Empty; + + if (seer.PlayerId == target.PlayerId) + { + return DemonHealth.TryGetValue(target.PlayerId, out int value) && value > 0 + ? Utils.ColorString(GetColor(value, true), $"【{value}/{SelfHealthMax.GetInt()}】") + : string.Empty; + } + else + { + return PlayerHealth.TryGetValue(target.PlayerId, out int value) && value > 0 + ? Utils.ColorString(GetColor(value), $"【{value}/{HealthMax.GetInt()}】") + : string.Empty; + } + } + + private static Color32 GetColor(float health, bool self = false) + { + var x = (int)(health / (self ? SelfHealthMax.GetInt() : HealthMax.GetInt()) * 10 * 50); + var r = 255; + var g = 255; + + if (x > 255) + r -= x - 255; + else + g = x; + + return new((byte)r, (byte)g, 0, byte.MaxValue); + } + + public override void SetButtonTexts(HudManager hud, byte id) + { + hud.KillButton?.OverrideText(Translator.GetString("DemonButtonText")); + } } \ No newline at end of file diff --git a/Roles/Neutral/NSerialKiller.cs b/Roles/Neutral/SerialKiller.cs similarity index 92% rename from Roles/Neutral/NSerialKiller.cs rename to Roles/Neutral/SerialKiller.cs index 38e8b6aba..4e978673c 100644 --- a/Roles/Neutral/NSerialKiller.cs +++ b/Roles/Neutral/SerialKiller.cs @@ -1,47 +1,47 @@ -using AmongUs.GameOptions; - -namespace EHR.Neutral; - -public class NSerialKiller : RoleBase -{ - public static bool On; - - private static OptionItem KillCooldown; - private static OptionItem CanVent; - private static OptionItem ImpostorVision; - - public override bool IsEnable => On; - - public override void SetupCustomOption() - { - StartSetup(12800) - .AutoSetupOption(ref KillCooldown, 22.5f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds) - .AutoSetupOption(ref CanVent, true) - .AutoSetupOption(ref ImpostorVision, true); - } - - public override void Init() - { - On = false; - } - - public override void Add(byte playerId) - { - On = true; - } - - public override void SetKillCooldown(byte id) - { - Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); - } - - public override void ApplyGameOptions(IGameOptions opt, byte id) - { - opt.SetVision(ImpostorVision.GetBool()); - } - - public override bool CanUseImpostorVentButton(PlayerControl pc) - { - return CanVent.GetBool(); - } +using AmongUs.GameOptions; + +namespace EHR.Neutral; + +public class SerialKiller : RoleBase +{ + public static bool On; + + private static OptionItem KillCooldown; + private static OptionItem CanVent; + private static OptionItem ImpostorVision; + + public override bool IsEnable => On; + + public override void SetupCustomOption() + { + StartSetup(12800) + .AutoSetupOption(ref KillCooldown, 22.5f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds) + .AutoSetupOption(ref CanVent, true) + .AutoSetupOption(ref ImpostorVision, true); + } + + public override void Init() + { + On = false; + } + + public override void Add(byte playerId) + { + On = true; + } + + public override void SetKillCooldown(byte id) + { + Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat(); + } + + public override void ApplyGameOptions(IGameOptions opt, byte id) + { + opt.SetVision(ImpostorVision.GetBool()); + } + + public override bool CanUseImpostorVentButton(PlayerControl pc) + { + return CanVent.GetBool(); + } } \ No newline at end of file From 0874d8157036a6c866b95c7fb7fb6adaa637f045 Mon Sep 17 00:00:00 2001 From: HyperAtill Date: Mon, 25 Aug 2025 17:59:12 +0300 Subject: [PATCH 2/3] Undo some automatic deletions --- GameMode/Speedrun.cs | 3 +- Modules/CustomRolesHelper.cs | 55 +++++++++++++++++------------------- Resources/Lang/en_US.json | 40 ++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 35 deletions(-) diff --git a/GameMode/Speedrun.cs b/GameMode/Speedrun.cs index 7647cc2a2..56b1ac3d9 100644 --- a/GameMode/Speedrun.cs +++ b/GameMode/Speedrun.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using EHR.Modules; +using Hazel; using UnityEngine; namespace EHR; @@ -77,7 +78,7 @@ public static class Speedrun int kcd = KillCooldown.GetInt(); Main.AllPlayerKillCooldown[pc.PlayerId] = kcd; pc.RpcChangeRoleBasis(CustomRoles.SerialKiller); - pc.Notify(Translator.GetString("Speedrun_CompletedTasks")); + pc.Notify(Translator.GetString("Speedrun_CompletedTasks"), sendOption: SendOption.None); pc.SyncSettings(); LateTask.New(() => pc.SetKillCooldown(kcd), 0.2f, log: false); } diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 1888352da..29da61250 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -865,7 +865,7 @@ internal static class CustomRolesHelper CustomRoles.Godfather when Options.GodfatherCancelVote.GetBool() => true, CustomRoles.Socialite when Socialite.CancelVote.GetBool() => true, CustomRoles.Negotiator when Negotiator.CancelVote.GetBool() => true, - + CustomRoles.Retributionist when Retributionist.CancelVote.GetBool() => true, CustomRoles.President => true, _ => false @@ -1034,24 +1034,25 @@ internal static class CustomRolesHelper { return role.IsAdditionRole() && (!Main.NeverSpawnTogetherCombos.TryGetValue(OptionItem.CurrentPreset, out Dictionary> neverList) || !neverList.TryGetValue(pc.GetCustomRole(), out List bannedAddonList) || !bannedAddonList.Contains(role)) && pc.GetCustomRole() is not CustomRoles.GuardianAngelEHR and not CustomRoles.God && !pc.Is(CustomRoles.Madmate) && !pc.Is(CustomRoles.GM) && role is not CustomRoles.Lovers && !pc.Is(CustomRoles.Needy) && (!pc.HasSubRole() || pc.GetCustomSubRoles().Count < Options.NoLimitAddonsNumMax.GetInt()) && (!Options.AddonCanBeSettings.TryGetValue(role, out (OptionItem Imp, OptionItem Neutral, OptionItem Crew, OptionItem Coven) o) || ((o.Imp.GetBool() || !pc.GetCustomRole().IsImpostor()) && (o.Neutral.GetBool() || !pc.GetCustomRole().IsNeutral()) && (o.Crew.GetBool() || !pc.IsCrewmate()) && (o.Coven.GetBool() || !pc.Is(Team.Coven)))) && (!role.IsImpOnlyAddon() || (pc.IsImpostor() && !pc.Is(CustomRoles.DoubleAgent)) || (pc.Is(CustomRoles.Traitor) && Traitor.CanGetImpostorOnlyAddons.GetBool())) && role switch { + CustomRoles.Commited when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter) => false, CustomRoles.Underdog when pc.Is(CustomRoles.Mare) => false, CustomRoles.Shy when Options.DisableWhisperCommand.GetBool() => false, CustomRoles.Blocked when !pc.CanUseVent() => false, CustomRoles.Aide when pc.IsMadmate() || pc.Is(CustomRoles.Saboteur) => false, - CustomRoles.Sleuth when pc.GetCustomRole() is CustomRoles.NecroGuesser or CustomRoles.Imitator => false, + CustomRoles.Sleuth when pc.GetCustomRole() is CustomRoles.NecroGuesser or CustomRoles.Imitator or CustomRoles.Detective => false, CustomRoles.Introvert when pc.GetCustomRole() is CustomRoles.Leery or CustomRoles.Samurai or CustomRoles.Arsonist or CustomRoles.Revolutionist or CustomRoles.Farseer or CustomRoles.Scavenger or CustomRoles.Analyst => false, - CustomRoles.Circumvent when pc.GetCustomRole() is CustomRoles.Swooper or CustomRoles.RiftMaker => false, + CustomRoles.Circumvent when pc.GetCustomRole() is CustomRoles.Swooper or CustomRoles.RiftMaker=> false, CustomRoles.Oblivious when pc.Is(CustomRoles.Altruist) => false, - CustomRoles.AntiTP when pc.GetCustomRole() is CustomRoles.Transmitter or CustomRoles.Miner or CustomRoles.Escapee or CustomRoles.Tunneler=> false, + CustomRoles.AntiTP when pc.GetCustomRole() is CustomRoles.Transmitter or CustomRoles.Miner or CustomRoles.Escapee or CustomRoles.Tunneler or CustomRoles.Ninja => false, CustomRoles.Swift when pc.Is(CustomRoles.Stealth) => false, - CustomRoles.BananaMan when pc.Is(CustomRoles.Disco) => false, - CustomRoles.Disco when pc.GetCustomRole() is CustomRoles.Chameleon or CustomRoles.Swooper or CustomRoles.Wraith or CustomRoles.Alchemist => false, + CustomRoles.BananaMan when pc.Is(CustomRoles.Disco) || pc.Is(CustomRoles.Doppelganger) => false, + CustomRoles.Disco when pc.GetCustomRole() is CustomRoles.Chameleon or CustomRoles.Swooper or CustomRoles.Wraith or CustomRoles.Alchemist or CustomRoles.Doppelganger => false, CustomRoles.Egoist when pc.Is(CustomRoles.Gangster) => false, - CustomRoles.Nimble when pc.Is(CustomRoles.Oxyman) => false, + CustomRoles.Nimble when pc.GetCustomRole() is CustomRoles.Oxyman or CustomRoles.EngineerEHR or CustomRoles.SabotageMaster => false, CustomRoles.Magnet when pc.Is(Team.Impostor) => false, CustomRoles.Swift when pc.Is(CustomRoles.Magnet) => false, CustomRoles.Oblivious when pc.Is(CustomRoles.Amnesiac) && Amnesiac.RememberMode.GetValue() == 0 => false, - CustomRoles.Rookie when !pc.CanUseKillButton() => false, + CustomRoles.Rookie when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter) => false, CustomRoles.Madmate when pc.Is(CustomRoles.Sidekick) => false, CustomRoles.Autopsy when pc.Is(CustomRoles.Doctor) || pc.Is(CustomRoles.Tracefinder) || pc.Is(CustomRoles.Scientist) || pc.Is(CustomRoles.ScientistEHR) || pc.Is(CustomRoles.Sunnyboy) => false, CustomRoles.Necroview when pc.Is(CustomRoles.Doctor) => false, @@ -1064,17 +1065,17 @@ internal static class CustomRolesHelper CustomRoles.TicketsStealer or CustomRoles.Swift or CustomRoles.DeadlyQuota or CustomRoles.Damocles or CustomRoles.Mare when pc.Is(CustomRoles.Bomber) || pc.Is(CustomRoles.Nuker) || pc.Is(CustomRoles.BoobyTrap) || pc.Is(CustomRoles.Capitalism) => false, CustomRoles.Torch when !pc.IsCrewmate() || pc.Is(CustomRoles.Bewilder) || pc.Is(CustomRoles.Sunglasses) || pc.Is(CustomRoles.GuardianAngelEHR) => false, CustomRoles.Bewilder when pc.Is(CustomRoles.Torch) || pc.Is(CustomRoles.GuardianAngelEHR) || pc.Is(CustomRoles.Sunglasses) => false, - CustomRoles.Dynamo when pc.Is(CustomRoles.Spurt) => false, - CustomRoles.Spurt when pc.GetCustomSubRoles().Any(x => x is CustomRoles.Dynamo or CustomRoles.Swiftclaw or CustomRoles.Giant or CustomRoles.Flashman) => false, + CustomRoles.Dynamo when pc.Is(CustomRoles.Spurt) || pc.GetCustomRole() is CustomRoles.Tank or CustomRoles.Ankylosaurus => false, + CustomRoles.Spurt when pc.GetCustomSubRoles().Any(x => x is CustomRoles.Dynamo or CustomRoles.Swiftclaw or CustomRoles.Giant or CustomRoles.Flashman) || pc.Is(CustomRoles.Ankylosaurus) => false, CustomRoles.Sunglasses when pc.Is(CustomRoles.Torch) || pc.Is(CustomRoles.GuardianAngelEHR) || pc.Is(CustomRoles.Bewilder) => false, CustomRoles.Guesser when pc.GetCustomRole() is CustomRoles.EvilGuesser or CustomRoles.NiceGuesser or CustomRoles.Doomsayer or CustomRoles.CopyCat => false, CustomRoles.Madmate when !pc.CanBeMadmate() || pc.Is(CustomRoles.Egoist) || pc.Is(CustomRoles.Rascal) => false, CustomRoles.Oblivious when pc.Is(CustomRoles.Detective) || pc.Is(CustomRoles.Cleaner) || pc.Is(CustomRoles.Medusa) || pc.Is(CustomRoles.Mortician) || pc.Is(CustomRoles.Mediumshiper) || pc.Is(CustomRoles.GuardianAngelEHR) => false, CustomRoles.Youtuber when !pc.IsCrewmate() || pc.Is(CustomRoles.Madmate) || pc.Is(CustomRoles.Sheriff) || pc.Is(CustomRoles.GuardianAngelEHR) => false, CustomRoles.Egoist when !pc.IsImpostor() => false, - CustomRoles.Damocles when pc.GetCustomRole() is CustomRoles.Bomber or CustomRoles.Nuker or CustomRoles.Mercenary or CustomRoles.Cantankerous => false, - CustomRoles.Damocles when !pc.CanUseKillButton() => false, - CustomRoles.Flashman when pc.Is(CustomRoles.Swiftclaw) || pc.Is(CustomRoles.Giant) || pc.Is(CustomRoles.Spurt) => false, + CustomRoles.Damocles when pc.GetCustomRole() is CustomRoles.Bomber or CustomRoles.Nuker or CustomRoles.SerialKiller or CustomRoles.Cantankerous => false, + CustomRoles.Damocles when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter) => false, + CustomRoles.Flashman when pc.Is(CustomRoles.Swiftclaw) || pc.Is(CustomRoles.Giant) || pc.Is(CustomRoles.Spurt) || pc.GetCustomRole() is CustomRoles.Tank or CustomRoles.Zombie => false, CustomRoles.Giant when pc.Is(CustomRoles.Flashman) || pc.Is(CustomRoles.Spurt) || pc.Is(CustomRoles.RoomRusher) => false, CustomRoles.Necroview when pc.Is(CustomRoles.Visionary) => false, CustomRoles.Mimic when pc.Is(CustomRoles.Mafia) => false, @@ -1090,13 +1091,9 @@ internal static class CustomRolesHelper CustomRoles.Mare when pc.Is(CustomRoles.FireWorks) => false, CustomRoles.Mare when pc.Is(CustomRoles.Swooper) => false, CustomRoles.Mare when pc.Is(CustomRoles.Vampire) => false, - CustomRoles.Torch when pc.Is(CustomRoles.Lighter) => false, - CustomRoles.Torch when pc.Is(CustomRoles.Ignitor) => false, - CustomRoles.Torch when pc.Is(CustomRoles.Decryptor) => false, - CustomRoles.Bewilder when pc.Is(CustomRoles.Lighter) => false, - CustomRoles.Bewilder when pc.Is(CustomRoles.Ignitor) => false, - CustomRoles.Sunglasses when pc.Is(CustomRoles.Lighter) => false, - CustomRoles.Sunglasses when pc.Is(CustomRoles.Ignitor) => false, + CustomRoles.Torch when pc.GetCustomRole() is CustomRoles.Lighter or CustomRoles.Ignitor or CustomRoles.Farseer or CustomRoles.Eclipse or CustomRoles.Decryptor => false, + CustomRoles.Bewilder when pc.GetCustomRole() is CustomRoles.Lighter or CustomRoles.Ignitor or CustomRoles.Farseer or CustomRoles.Eclipse or CustomRoles.Decryptor => false, + CustomRoles.Sunglasses when pc.GetCustomRole() is CustomRoles.Lighter or CustomRoles.Ignitor or CustomRoles.Farseer or CustomRoles.Eclipse or CustomRoles.Decryptor => false, CustomRoles.Bait when pc.Is(CustomRoles.Demolitionist) => false, CustomRoles.Trapper when pc.Is(CustomRoles.Demolitionist) => false, CustomRoles.Lovers when pc.Is(CustomRoles.Romantic) => false, @@ -1105,11 +1102,11 @@ internal static class CustomRolesHelper CustomRoles.DualPersonality when (!pc.IsImpostor() && !pc.IsCrewmate()) || pc.Is(CustomRoles.Madmate) => false, CustomRoles.Loyal when (!pc.IsImpostor() && !pc.IsCrewmate()) || pc.Is(CustomRoles.Madmate) => false, CustomRoles.Loyal when pc.IsImpostor() && !Options.ImpCanBeLoyal.GetBool() => false, - CustomRoles.Seer when pc.Is(CustomRoles.Mortician) => false, - CustomRoles.Onbound when pc.Is(CustomRoles.SuperStar) => false, + CustomRoles.Seer when pc.GetCustomRole() is CustomRoles.Mortician or CustomRoles.TimeMaster => false, + CustomRoles.Onbound when pc.GetCustomRole() is CustomRoles.SuperStar or CustomRoles.Ankylosaurus or CustomRoles.Car => false, CustomRoles.Rascal when pc.Is(CustomRoles.SuperStar) || pc.Is(CustomRoles.Madmate) => false, CustomRoles.Madmate when pc.Is(CustomRoles.SuperStar) => false, - CustomRoles.Gravestone when pc.Is(CustomRoles.SuperStar) => false, + CustomRoles.Gravestone when pc.GetCustomRole() is CustomRoles.SuperStar or CustomRoles.Innocent => false, CustomRoles.Lucky when pc.Is(CustomRoles.Guardian) => false, CustomRoles.Bait when pc.Is(CustomRoles.GuardianAngelEHR) => false, CustomRoles.Bait when pc.Is(CustomRoles.Trapper) => false, @@ -1125,17 +1122,17 @@ internal static class CustomRolesHelper CustomRoles.Swift when pc.Is(CustomRoles.Mafia) => false, CustomRoles.Reach when pc.GetCustomRole() is CustomRoles.Mafioso or CustomRoles.Evolver => false, CustomRoles.Trapper when pc.Is(CustomRoles.GuardianAngelEHR) => false, - CustomRoles.Reach when !pc.CanUseKillButton() => false, - CustomRoles.Magnet when !pc.CanUseKillButton() => false, - CustomRoles.Haste when !pc.CanUseKillButton() || !pc.CanUseImpostorVentButton() => false, + CustomRoles.Reach when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter) => false, + CustomRoles.Magnet when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter) => false, + CustomRoles.Haste when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter) || !pc.CanUseImpostorVentButton() => false, CustomRoles.Diseased when pc.Is(CustomRoles.Antidote) => false, CustomRoles.Antidote when pc.Is(CustomRoles.Diseased) => false, - CustomRoles.Flashman or CustomRoles.Giant when pc.GetCustomRole() is CustomRoles.Swooper or CustomRoles.Wraith or CustomRoles.Chameleon or CustomRoles.Alchemist => false, + CustomRoles.Flashman or CustomRoles.Giant when pc.GetCustomRole() is CustomRoles.Swooper or CustomRoles.Wraith or CustomRoles.Chameleon or CustomRoles.Alchemist or CustomRoles.Ankylosaurus => false, CustomRoles.Bait when pc.Is(CustomRoles.Unreportable) => false, CustomRoles.Busy when !pc.GetTaskState().HasTasks => false, CustomRoles.Truant when pc.Is(CustomRoles.SoulHunter) => false, CustomRoles.Nimble when !pc.IsCrewmate() => false, - CustomRoles.Physicist when !pc.IsCrewmate() || pc.GetCustomRole().IsDesyncRole() => false, + CustomRoles.Physicist when !pc.IsCrewmate() || pc.GetCustomRole().IsDesyncRole() || (pc.Is(CustomRoles.TimeMaster) && !TimeMaster.TimeMasterCanUseVitals.GetBool()) => false, CustomRoles.Finder when !pc.IsCrewmate() || pc.GetCustomRole().IsDesyncRole() => false, CustomRoles.Noisy when !pc.IsCrewmate() || pc.GetCustomRole().IsDesyncRole() => false, CustomRoles.Unreportable when pc.Is(CustomRoles.Bait) => false, @@ -1147,7 +1144,7 @@ internal static class CustomRolesHelper CustomRoles.Unlucky when pc.Is(CustomRoles.Luckey) => false, CustomRoles.Unlucky when pc.Is(CustomRoles.Lucky) => false, CustomRoles.Lucky when pc.Is(CustomRoles.Unlucky) => false, - CustomRoles.Fool when pc.Is(CustomRoles.SabotageMaster) || pc.Is(CustomRoles.GuardianAngelEHR) => false, + CustomRoles.Fool when pc.GetCustomRole() is CustomRoles.SabotageMaster or CustomRoles.GuardianAngelEHR or CustomRoles.Technician => false, CustomRoles.Bloodhound when pc.Is(CustomRoles.Oblivious) => false, CustomRoles.DoubleShot when pc.GetCustomRole() is not CustomRoles.EvilGuesser and not CustomRoles.NiceGuesser and not CustomRoles.Augur && !Options.GuesserMode.GetBool() => false, CustomRoles.DoubleShot when !pc.Is(CustomRoles.Guesser) && !pc.Is(CustomRoles.EvilGuesser) && pc.Is(CustomRoleTypes.Impostor) && !Options.ImpostorsCanGuess.GetBool() => false, diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index dedeb4ff8..73c27fc15 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -1456,6 +1456,8 @@ "LawyerCanTargetJester": "Can Target Jester", "LawyerChangeRolesAfterTargetKilled": "When Target Dies, Lawyer becomes", "LawyerChangeRole": "Your client is dead", + "BountyHunterSwapTimer": "<#00ffa5>New target in <#ffffff>{0}s", + "SerialKillerTimeLeft": "<#ffff00>Must kill within <#ffa500>{0}s", "MercenaryLimit": "Time Until Suicide", "ArsonistDouseTime": "Douse Duration", "CanTerroristSuicideWin": "Can Win By Suicide", @@ -1826,6 +1828,7 @@ "TargetDisregarded": "The player whose body you tried to report is <#FF6347>Disregarded", "AmOblivious": "You are Oblivious, meaning you cannot report bodies", "ShipHasNoMoreMeetingsLeft": "The ship has no more emergency meetings left", + "NoMoreEmergencyMeetingsLeft": "You have no more emergency meetings left", "UnreportableBody": "This body is unreportable due to a role-specific action", "CheckMurderFail": "<#ff0000>The murder was canceled due to the following reason:\\n", "SameCTATeam": "The player you attacked is in the same custom team as you", @@ -2476,6 +2479,7 @@ "CommandDescription.VoteStart": "Vote to start the game", "CommandDescription.Imitate": "Imitate a dead player's role as the Imitator", "CommandDescription.Retribute": "Guess who killed the player you camped as the Retributionist", + "CommandDescription.ConfirmAuth": "Command required to register a new EHR account", "CommandArgs.ChangeSetting.Name": "The setting's name to change (leave blank to view all settings)", "CommandArgs.ChangeSetting.UnknownValue": "(leave blank to view what to put here)", "CommandArgs.Rename.Name": "Your new name", @@ -2566,6 +2570,7 @@ "CommandArgs.DeleteAdmin.Id": "The player's ID to remove from the list of admins", "CommandArgs.Imitate.Id": "The dead player's ID to imitate the role of", "CommandArgs.Retribute.Id": "The potential killer's ID to guess", + "CommandArgs.ConfirmAuth.UUID": "The unique token provided by the server", "Commands.ChangeSettingHelp": "Arguments:\\n- map (theskeld/mirahq/polus/dlekseht/airship/thefungle/custom)\\n- impostors (number)\\n- players (number)\\n- recommended (on/off)\\n- confirmejects (on/off)\\n- emergencymeetings (number)\\n- anonymousvotes (on/off)\\n- emergencycooldown (number)\\n- discussiontime (number)\\n- votingtime (number)\\n- playerspeed (float)\\n- crewmatevision (float)\\n- impostorvision (float)\\n- killcooldown (float)\\n- killdistance (short/medium/long/custom)\\n- taskbarupdates (always/meetings/never)\\n- visualtasks (on/off)\\n- commontasks (number)\\n- longtasks (number)\\n- shorttasks (number)\\n- scientistcount (number)\\n- scientistchance (number)\\n- vitalsdisplaycooldown (float)\\n- batteryduration (float)\\n- engineercount (number)\\n- engineerchance (number)\\n- ventusecooldown (float)\\n- maxtimeinvents (float)\\n- guardianangelcount (number)\\n- guardianangelchance (number)\\n- protectcooldown (float)\\n- protectduration (float)\\n- protectvisibletoimpostors (on/off)\\n- shapeshiftercount (number)\\n- shapeshifterchance (number)\\n- shapeshiftduration (float)\\n- shapeshiftcooldown (float)\\n- leaveshapeshiftevidence (on/off)\\n- phantomcount (number)\\n- phantomchance (number)\\n- inviscooldown (float)\\n- invisduration (float)\\n- noisemakercount (number)\\n- noisemakerchance (number)\\n- noisemakerimpostoralert (on/off)\\n- noisemakeralertduration (number)\\n- trackercount (number)\\n- trackerchance (number)\\n- trackduration (float)\\n- trackcooldown (float)\\n- trackdelay (float)\\n- ghostdotasks (on/off)\\n\\nNotes:\\n1. Upper and lowercase matters\\n2. Where it says custom, you obviously put a number argument after it\\n3. A float is a single precision floating number, like 1.7\\n4. \"ghostdotasks\" causes issues if you set it\\n5. Example uses:\\n - \"/cs map polus\"\\n - \"/cs killdistance custom 4\"\\n - \"/cs trackdelay 1,7\"\\n - \"/cs confirmejects on\"", "AddTagSuccess": "<#00ffa5>✓ Suceessfully created the tag {0} for {1}. If you want to change it, just use this command again and it will override the current tag. To delete it, simply type <#00a5ff>/deletetag {2}!", "DeleteTagSuccess": "<#00ffa5>✓ Successfully deleted {0}'s tag.", @@ -2590,6 +2595,10 @@ "8BallResponse.17": "My sources say no.", "8BallResponse.18": "Outlook not so good.", "8BallResponse.19": "Very doubtful.", + "ConfirmAuth.ErrorNotVanilla": "<#ff5555>X <#bb0000>You must be in an Online game on a Vanilla server (Europe, North America, Asia)", + "ConfirmAuth.ErrorLobbyUnlisted": "<#ff5555>X <#bb0000>This lobby is not listed on the EHR server.\\n<#ffffff>Please enable <#ffff44>{0} in System Settings, otherwise the server will reject your request.", + "ConfirmAuth.Error": "<#ff5555>X <#bb0000>Network error\\n<#ffffff>{0}", + "ConfirmAuth.Success": "<#00ff00>✓ <#00ffa5>Successfully sent the verification request to the server.", "GameModePoll.Question": "Which game mode should we play next?", "PollResultTitle": "Poll Results", "Poll.Title": "<#ff0000>➡ POLL ⬅", @@ -3339,8 +3348,9 @@ "CamouflageMode.Host": "Host", "CamouflageMode.Karpe": "KARPED1EM", "CamouflageMode.Gurge44": "Gurge44", - "CamouflageMode.TommyXL": "TommyXL", + "CamouflageMode.TommyXL": "TommyXL","pet_Goose": "A Goose", "pet_Bedcrab": "Bedcrab", + "pet_DancingSkeletonPet": "Bone Shaker", "pet_BredPet": "Breb", "pet_YuleGoatPet": "Bränt", "pet_Bush": "Bushfriend", @@ -3348,9 +3358,12 @@ "pet_ChewiePet": "Chewie", "pet_clank": "Clank", "pet_coaltonpet": "Coalton", + "pet_Creb": "Creb", "pet_Cube": "Deitied Guy", + "pet_lny_dragon": "Desolation of Skeld", "pet_Doggy": "Doggy", "pet_Ellie": "E. Rose", + "pet_Strawb": "Flying Strawberry", "pet_frankendog": "Frankendog", "pet_D2GhostPet": "Ghost", "pet_test": "Glitch Pet", @@ -3360,13 +3373,23 @@ "pet_Alien": "Headslug", "pet_poro": "Heimerdinger's Poro", "pet_HamPet": "Holiday Hammy", + "pet_Crow": "Mad Crow", "pet_Lava": "Magmate", "pet_Crewmate": "Mini Crewmate", + "pet_Mister": "Mister", + "pet_nancy": "Nancy", + "pet_napstamate": "Napstamate", + "pet_Pip": "Pip", + "pet_pocketCircuitCar": "Pocket Circuit", "pet_D2PoukaPet": "Pouka", "pet_Pusheen": "Pusheen", + "pet_Pate": "Pâté", + "pet_Rammy": "Rammy", "pet_Robot": "Ro-Bot", "pet_Snow": "Snowball", + "pet_spaceCat": "Space Cat", "pet_Squig": "Squig", + "pet_Stormy": "Stormy", "pet_nuggetPet": "The Nug", "pet_Charles_Red": "Toppat Chopper", "pet_UFO": "UFO", @@ -3511,6 +3534,12 @@ "JailorTitle": "Jailor Message", "JailedSuffix": "JAILED", "Car": "Car", + "Commited": "Commited", + "CommitedInfo": "Vote Equivalently for Low Cooldown", + "CommitedInfoLong": "(Add-ons):\\nAs the Commited, you get a random target at the start of every meeting. They will have a ⌆ symbol next to their name. If you vote for the same player as your target, your kill cooldown will be reduced. Skips may be ignored.", + "Commited.PermanentReduction": "Cooldown Reduction is Permanent", + "Commited.Reduction": "Cooldown is Reduced by", + "Commited.IgnoreSkips": "Ignore Skips", "CarInfo": "Drive around the map", "CarInfoLong": "(Crewmates):\\nAs the Car, you can drive around the map. Everyone sees you as a car, including yourself. When you touch someone, you propel them far away in the direction you were driving. This role has absolutely no purpose other than to have fun.", "Car.PropelDistance": "Propel Distance", @@ -3818,9 +3847,9 @@ "ConvenerInfoLong": "(Crewmates):\\nAs the Convener, you can vent/pet to teleport everyone to you.", "Mathematician": "Mathematician", "MathematicianInfo": "Ask Math Questions", - "MathematicianInfoLong": "(Crewmates):\\nAs the Mathematician, you can use '/ask [number] [number]' to ask a math question (What is [number] + [number]?). The first player to answer correctly will get shielded for the next round. You can only ask once per meeting.", + "MathematicianInfoLong": "(Crewmates):\\nAs the Mathematician, you can use '/ask [number] [number]' to ask a math question (What is [number] + [number]?). The first player to answer correctly will get +1 ability use and shielded for the next round. You can only ask once per meeting.", "MathematicianQuestionString": "What is {0} + {1}?\n\nType '/answer [answer]' to answer.\nFirst to answer correctly gets shielded for the next round!", - "MathematicianAnsweredString": "{0} has answered correctly! ({1})\nThey will be shielded for the next round.", + "MathematicianAnsweredString": "{0} has answered correctly! ({1})\nThey got +1 ability use and will be shielded for the next round.", "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)", @@ -5106,7 +5135,7 @@ "TMGPlayerInfoLong": "THINK WITH OTHERS' BRAINS TO WIN", "BedWars": "BED WARS", "ModeBedWars": "Gamemode: Bed Wars", - "ModeDescribe.BedWars": "In the Bed Wars gamemode, players are split into teams. Each team has a bed. If a team's bed is destroyed, all players in that team can no longer respawn. The goal is to destroy all other teams' beds (via Pet button) and be the last team alive.\\n\\nYou can buy items (blocks, weapons, tool, etc.) and team upgrades (heal pool, haste, trap, etc.) in shops from the resources (iron, gold, diamond, emerald) you can collect from the generators on the map. These are useful for protecting the bed with various layers to make it harder to break it, increase your attack damage, increase your defense, or give you or your team various abilities.\\n\\nIn the beginning, there's a grace period, where no attacks are allowed, and players can't leave their own base.\\n\\nEach player has an inventory with all of their items. Cycle through the slots using the Vanish button or by typing the number of the slot in the chat (ex. '5'). Use weapons through the kill button, other items via Pet. To start breaking someone's bed, Pet. Make sure to always have the correct item selected in your inventory (swords when you're fighting, blocks when you protect the bed, tools when you break beds, etc.).\\nYou can break beds with anything, but using the correct tool for each layer will make the process significantly faster.\\n\\nIf you die, you will respawn after a few seconds, unless your bed is broken. If your bed is broken, you will not respawn anymore.", + "ModeDescribe.BedWars": "In the Bed Wars gamemode, players are split into teams. Each team has a bed. If a team's bed is destroyed, all players in that team can no longer respawn. The goal is to destroy all other teams' beds (via Pet button) and be the last team alive.\\n\\nYou can buy items (blocks, weapons, tool, etc.) and team upgrades (heal pool, haste, trap, etc.) in shops from the resources (iron, gold, diamond, emerald) you can collect from the generators on the map. These are useful for protecting the bed with various layers to make it harder to break it, increase your attack damage, increase your defense, or give you or your team various abilities.\\n\\nIn the beginning, there's a grace period, where no attacks are allowed, and players can't leave their own base.\\n\\nEach player has an inventory with all of their items. Cycle through the slots using the Vanish button or by typing the number of the slot in the chat (ex. '5'). Use weapons through the kill button, other items via Pet. To start breaking someone's bed, Pet. Make sure to always have the correct item selected in your inventory (blocks when you protect the bed, tools when you break beds, etc.).\\nYou can break beds with anything, but using the correct tool for each layer will make the process significantly faster.\\n\\nIf you die, you will respawn after a few seconds, unless your bed is broken. If your bed is broken, you will not respawn anymore.", "Bedwars.Shop.ItemShop": "ITEM SHOP", "Bedwars.Shop.UpgradeShop": "UPGRADE SHOP", "Bedwars.Rooms": "Teams are located in the following rooms:", @@ -8994,7 +9023,7 @@ "Tank.Suffix": "<#00ffa5>A random vent you haven't entered yet: {0}", "Retributionist": "Retributionist", "RetributionistInfo": "Find & Kill Your Target's Killer", - "RetributionistInfoLong": "(Crewmates):\\nAs the Retributionist, you may mark a player using your kill button to camp them. If they die, you will be notified. In the meeting after that, you may use /retribute {id} to kill the player who killed your target. If you guess wrong, nothing happens. After the meeting, you may camp another player.", + "RetributionistInfoLong": "(Crewmates):\\nAs the Retributionist, you may mark a player using your kill button to camp them. If they die, you will be notified. In the meeting after that, you may use /retribute {id} to kill the player who killed your target. (If the vote cancelling setting is enabled, you can vote for your target instead of using this command.) If you guess wrong, nothing happens. After the meeting, you may camp another player.", "Retributionist.ResetCampedPlayerAfterEveryMeeting": "Can Camp a New Player After Every Meeting", "Retributionist.TargetDead": "<#ffff00>Your target is dead!\\nYou can use /retribute {id} to kill the player who killed your target in the next meeting.", "Retributionist.Fail": "<#ff0000>X <#bb0000>Wrong!", @@ -9032,6 +9061,7 @@ "CleanserInfo": "Erase All Addons of your vote target", "CleanserInfoLong": "(Crewmates):\\nCleanser can vote for any target at the meeting to erase the target's Add-ons, and the erasure will take effect after the meeting ends. Depending on the settings cleansed player may never get add on in future", "CleansedInfoLong": "(Add-ons):\\nCleansed Add-on can only be obtained if cleanser erases all your Add-ons. Depending on the cleanser settings, you may not be able to obtain any more Add-ons in the future.", + "TimeMasterCanUseVitals": "Can Use Vitals", "TimeMasterRewindTimeLength": "Rewind Time Length", "TimeMasterOnGuard": "Time Shield is active!", "TimeMasterSkillStop": "Time Shield has ended!", From 1158973961a43499850cd52563b93652e48986da Mon Sep 17 00:00:00 2001 From: HyperAtill Date: Mon, 25 Aug 2025 18:00:56 +0300 Subject: [PATCH 3/3] re-check myself --- Resources/Lang/en_US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 73c27fc15..0fbd7f8c8 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -3533,13 +3533,13 @@ "JailedNotifyMsg": "You have been locked up in Jail by the Jailor. No one can guess or judge you and you can only guess Jailor.", "JailorTitle": "Jailor Message", "JailedSuffix": "JAILED", - "Car": "Car", "Commited": "Commited", "CommitedInfo": "Vote Equivalently for Low Cooldown", "CommitedInfoLong": "(Add-ons):\\nAs the Commited, you get a random target at the start of every meeting. They will have a ⌆ symbol next to their name. If you vote for the same player as your target, your kill cooldown will be reduced. Skips may be ignored.", "Commited.PermanentReduction": "Cooldown Reduction is Permanent", "Commited.Reduction": "Cooldown is Reduced by", "Commited.IgnoreSkips": "Ignore Skips", + "Car": "Car", "CarInfo": "Drive around the map", "CarInfoLong": "(Crewmates):\\nAs the Car, you can drive around the map. Everyone sees you as a car, including yourself. When you touch someone, you propel them far away in the direction you were driving. This role has absolutely no purpose other than to have fun.", "Car.PropelDistance": "Propel Distance",