From a82c1b43db6376cbdc2e8068041ee96e24af8458 Mon Sep 17 00:00:00 2001 From: HyperAtill Date: Mon, 25 Aug 2025 17:00:35 +0300 Subject: [PATCH] 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