diff --git a/CTA/CustomTeamAssigner/CustomRoles.cs b/CTA/CustomTeamAssigner/CustomRoles.cs index d7b920fa2..c1d6457d1 100644 --- a/CTA/CustomTeamAssigner/CustomRoles.cs +++ b/CTA/CustomTeamAssigner/CustomRoles.cs @@ -18,7 +18,7 @@ public enum CustomRoles // Impostors Abyssbringer, - Hacker, // Anonymous + Anonymous, AntiAdminer, Sans, // Arrogance Assumer, @@ -106,6 +106,7 @@ public enum CustomRoles Twister, Undertaker, Vampire, + Venerer, Ventriloquist, Vindicator, Visionary, @@ -166,7 +167,6 @@ public enum CustomRoles Dealer, Demolitionist, Deputy, - Detective, Detour, Dictator, Doctor, @@ -181,6 +181,7 @@ public enum CustomRoles Express, Farmer, Farseer, + Forensic, Divinator, // Fortune Teller Gardener, Gaulois, @@ -189,6 +190,7 @@ public enum CustomRoles Grenadier, MeetingManager, Guardian, + Hacker, Helper, Ignitor, Imitator, @@ -218,7 +220,6 @@ public enum CustomRoles Mortician, NiceEraser, NiceGuesser, - NiceHacker, NiceSwapper, Nightmare, Negotiator, @@ -295,6 +296,7 @@ public enum CustomRoles Enderman, Evolver, Executioner, + Explosivist, Totocalcio, // Follower Gaslighter, Glitch, @@ -366,6 +368,7 @@ public enum CustomRoles Technician, Tank, Terrorist, + Thanos, Tiger, Traitor, Tremor, @@ -494,6 +497,7 @@ public enum CustomRoles Energetic, Entranced, EvilSpirit, + Facilitator, // Ghost role Finder, Flashman, Fragile, diff --git a/CustomRoles.cs b/CustomRoles.cs index 51eb836e5..367769416 100644 --- a/CustomRoles.cs +++ b/CustomRoles.cs @@ -9,11 +9,13 @@ public enum CustomRoles Impostor, Phantom, Shapeshifter, + Viper, // Vanilla Remakes ImpostorEHR, PhantomEHR, ShapeshifterEHR, + ViperEHR, // Impostors @@ -124,6 +126,7 @@ public enum CustomRoles Noisemaker, Scientist, Tracker, + Detective, // Vanilla Remakes CrewmateEHR, @@ -132,6 +135,7 @@ public enum CustomRoles NoisemakerEHR, ScientistEHR, TrackerEHR, + DetectiveEHR, // Crewmates @@ -167,7 +171,6 @@ public enum CustomRoles Dealer, Demolitionist, Deputy, - Detective, Detour, Dictator, Doctor, @@ -182,6 +185,7 @@ public enum CustomRoles Express, Farmer, Farseer, + Forensic, Divinator, // Fortune Teller Gardener, Gaulois, @@ -296,6 +300,7 @@ public enum CustomRoles Enderman, Evolver, Executioner, + Explosivist, Totocalcio, // Follower Gaslighter, Glitch, @@ -496,6 +501,7 @@ public enum CustomRoles Energetic, Entranced, EvilSpirit, + Examiner, Facilitator, // Ghost role Finder, Flashman, @@ -556,6 +562,7 @@ public enum CustomRoles Undead, Underdog, Unlucky, + Venom, Warden, // Ghost role Watcher, Workhorse, diff --git a/EHR.csproj b/EHR.csproj index df22bae4d..0834b3bfb 100644 --- a/EHR.csproj +++ b/EHR.csproj @@ -27,11 +27,11 @@ - + - + diff --git a/Main.cs b/Main.cs index d46e4784c..ae929adbe 100644 --- a/Main.cs +++ b/Main.cs @@ -58,7 +58,7 @@ public class Main : BasePlugin public const string ModColor = "#00ffff"; public const bool AllowPublicRoom = true; public const string ForkId = "EHR"; - public const string SupportedAUVersion = "2025.4.15"; + public const string SupportedAUVersion = "2025.9.9"; public static readonly string DataPath = #if ANDROID @@ -175,7 +175,7 @@ public class Main : BasePlugin private Harmony Harmony { get; } = new(PluginGuid); - public static NormalGameOptionsV09 NormalOptions => GameOptionsManager.Instance != null ? GameOptionsManager.Instance.currentNormalGameOptions : null; + public static NormalGameOptionsV10 NormalOptions => GameOptionsManager.Instance != null ? GameOptionsManager.Instance.currentNormalGameOptions : null; // Client Options public static ConfigEntry HideName { get; private set; } @@ -356,6 +356,7 @@ public class Main : BasePlugin { CustomRoles.GuardianAngel, "#77e6d1" }, { CustomRoles.Tracker, "#34ad50" }, { CustomRoles.Noisemaker, "#ff4a62" }, + { CustomRoles.Detective, "#625EEE" }, // Vanilla Remakes { CustomRoles.CrewmateEHR, "#8cffff" }, { CustomRoles.EngineerEHR, "#FF6A00" }, @@ -363,6 +364,7 @@ public class Main : BasePlugin { CustomRoles.GuardianAngelEHR, "#77e6d1" }, { CustomRoles.TrackerEHR, "#34ad50" }, { CustomRoles.NoisemakerEHR, "#ff4a62" }, + { CustomRoles.DetectiveEHR, "#625EEE" }, // Crewmates { CustomRoles.DoubleAgent, "#ff1919" }, { CustomRoles.Luckey, "#b8d7a3" }, @@ -455,7 +457,7 @@ public class Main : BasePlugin { CustomRoles.SpeedBooster, "#00ffff" }, { CustomRoles.Doctor, "#80ffdd" }, { CustomRoles.Dictator, "#df9b00" }, - { CustomRoles.Detective, "#7160e8" }, + { CustomRoles.Forensic, "#7160e8" }, { CustomRoles.NiceGuesser, "#f0e68c" }, { CustomRoles.SwordsMan, "#7a7a7a" }, { CustomRoles.Transporter, "#42D1FF" }, @@ -538,6 +540,7 @@ public class Main : BasePlugin { CustomRoles.Duality, "#8D6F64" }, { CustomRoles.Thanos, "#F9D401" }, { CustomRoles.SerialKiller, "#233fcc" }, + { CustomRoles.Explosivist, "#D20103" }, { CustomRoles.Slenderman, "#2c2e00" }, { CustomRoles.Amogus, "#ff0000" }, { CustomRoles.Weatherman, "#347deb" }, @@ -668,6 +671,8 @@ public class Main : BasePlugin { CustomRoles.Physicist, "#87e9ff" }, { CustomRoles.Finder, "#32a879" }, { CustomRoles.Noisy, "#e34fb2" }, + { CustomRoles.Examiner, "#625EEE" }, + { CustomRoles.Venom, "#ff1919" }, { CustomRoles.Torch, "#eee5be" }, { CustomRoles.Seer, "#61b26c" }, { CustomRoles.Brakar, "#1447af" }, @@ -793,9 +798,9 @@ public class Main : BasePlugin ClassInjector.RegisterTypeInIl2Cpp(); #endif - NormalGameOptionsV09.RecommendedImpostors = NormalGameOptionsV09.MaxImpostors = Enumerable.Repeat(128, 128).ToArray(); - NormalGameOptionsV09.MinPlayers = Enumerable.Repeat(4, 128).ToArray(); - HideNSeekGameOptionsV09.MinPlayers = Enumerable.Repeat(4, 128).ToArray(); + NormalGameOptionsV10.RecommendedImpostors = NormalGameOptionsV10.MaxImpostors = Enumerable.Repeat(128, 128).ToArray(); + NormalGameOptionsV10.MinPlayers = Enumerable.Repeat(4, 128).ToArray(); + HideNSeekGameOptionsV10.MinPlayers = Enumerable.Repeat(4, 128).ToArray(); PrivateTagManager.LoadTagsFromFile(); @@ -1017,6 +1022,7 @@ public enum CustomWinner Necromancer = CustomRoles.Necromancer, Wraith = CustomRoles.Wraith, SerialKiller = CustomRoles.SerialKiller, + Explosivist = CustomRoles.Explosivist, Thanos = CustomRoles.Thanos, Duality = CustomRoles.Duality, Slenderman = CustomRoles.Slenderman, diff --git a/Modules/AURoleOptions.cs b/Modules/AURoleOptions.cs index f3b85cc82..e00464255 100644 --- a/Modules/AURoleOptions.cs +++ b/Modules/AURoleOptions.cs @@ -120,6 +120,18 @@ public static class AURoleOptions set => Opt.SetFloat(FloatOptionNames.TrackerDelay, value); } + public static float ViperDissolveTime + { + get => Opt.GetFloat(FloatOptionNames.ViperDissolveTime); + set => Opt.SetFloat(FloatOptionNames.ViperDissolveTime, value); + } + + public static float DetectiveSuspectLimit + { + get => Opt.GetFloat(FloatOptionNames.DetectiveSuspectLimit); + set => Opt.SetFloat(FloatOptionNames.DetectiveSuspectLimit, value); + } + public static void SetOpt(IGameOptions opt) { Opt = opt; diff --git a/Modules/CustomNetObject.cs b/Modules/CustomNetObject.cs index c0c31ef6c..d80523140 100644 --- a/Modules/CustomNetObject.cs +++ b/Modules/CustomNetObject.cs @@ -624,6 +624,7 @@ namespace EHR protected override void OnFixedUpdate() { base.OnFixedUpdate(); + if (Options.CurrentGameMode != CustomGameMode.BedWars) return; timer -= Time.fixedDeltaTime; if (timer <= 0f) diff --git a/Modules/CustomRoleSelector.cs b/Modules/CustomRoleSelector.cs index e971de868..728c3ae99 100644 --- a/Modules/CustomRoleSelector.cs +++ b/Modules/CustomRoleSelector.cs @@ -18,6 +18,8 @@ internal static class CustomRoleSelector public static int AddNoisemakerNum; public static int AddTrackerNum; public static int AddPhantomNum; + public static int AddViperNum; + public static int AddDetectiveNum; public static readonly Dictionary GameModeRoles = new() { @@ -905,6 +907,8 @@ internal static class CustomRoleSelector AddNoisemakerNum = 0; AddTrackerNum = 0; AddPhantomNum = 0; + AddViperNum = 0; + AddDetectiveNum = 0; foreach (CustomRoles role in RoleResult.Values) { @@ -930,6 +934,12 @@ internal static class CustomRoleSelector case CustomRoles.Phantom: AddPhantomNum++; break; + case CustomRoles.Viper: + AddViperNum++; + break; + case CustomRoles.Detective: + AddDetectiveNum++; + break; } } catch (Exception e) { Utils.ThrowException(e); } @@ -955,7 +965,7 @@ internal static class CustomRoleSelector case CustomRoles.Mare or CustomRoles.Glow or CustomRoles.Sleep when Main.CurrentMap == MapNames.Fungle: case CustomRoles.Madmate when Options.MadmateSpawnMode.GetInt() != 0: case CustomRoles.Lovers or CustomRoles.LastImpostor or CustomRoles.Workhorse or CustomRoles.Undead or CustomRoles.Insane: - case CustomRoles.Nimble or CustomRoles.Physicist or CustomRoles.Bloodlust or CustomRoles.Finder or CustomRoles.Noisy: // Assigned at a different function due to role base change + case CustomRoles.Nimble or CustomRoles.Physicist or CustomRoles.Bloodlust or CustomRoles.Finder or CustomRoles.Noisy or CustomRoles.Examiner or CustomRoles.Venom: // Assigned at a different function due to role base change continue; } diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index 953e8cd0b..103624d69 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -39,6 +39,7 @@ internal static class CustomRolesHelper CustomRoles.PortalMaker, CustomRoles.Gardener, CustomRoles.Farmer, + CustomRoles.Explosivist, // Add-ons CustomRoles.Energetic, @@ -279,7 +280,7 @@ internal static class CustomRolesHelper CustomRoles.Escapee => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter, CustomRoles.NiceGuesser => CustomRoles.Crewmate, CustomRoles.EvilGuesser => CustomRoles.Impostor, - CustomRoles.Detective => CustomRoles.Crewmate, + CustomRoles.Forensic => CustomRoles.Crewmate, CustomRoles.God => CustomRoles.Crewmate, CustomRoles.Tank => CustomRoles.Engineer, CustomRoles.Technician => Technician.CanVent.GetBool() ? CustomRoles.Engineer : CustomRoles.Crewmate, @@ -339,7 +340,7 @@ internal static class CustomRolesHelper CustomRoles.Devourer => CustomRoles.Shapeshifter, CustomRoles.Deathpact => CustomRoles.Shapeshifter, CustomRoles.Bloodhound => CustomRoles.Crewmate, - CustomRoles.Tracker => CustomRoles.Crewmate, + CustomRoles.Scout => CustomRoles.Crewmate, CustomRoles.Merchant => CustomRoles.Crewmate, CustomRoles.Lookout => CustomRoles.Crewmate, CustomRoles.Guardian => CustomRoles.Crewmate, @@ -365,6 +366,8 @@ internal static class CustomRolesHelper CustomRoles.ScientistEHR => CustomRoles.Scientist, CustomRoles.TrackerEHR => CustomRoles.Tracker, CustomRoles.NoisemakerEHR => CustomRoles.Noisemaker, + CustomRoles.DetectiveEHR => CustomRoles.Detective, + CustomRoles.ViperEHR => CustomRoles.Viper, // Hide And Seek CustomRoles.Hider => CustomRoles.Crewmate, @@ -418,6 +421,8 @@ internal static class CustomRolesHelper CustomRoles.Impostor => CustomRoles.ImpostorEHR, CustomRoles.Phantom => CustomRoles.PhantomEHR, CustomRoles.Shapeshifter => CustomRoles.ShapeshifterEHR, + CustomRoles.Detective => CustomRoles.DetectiveEHR, + CustomRoles.Viper => CustomRoles.ViperEHR, _ => role.IsImpostor() ? CustomRoles.ImpostorEHR : CustomRoles.CrewmateEHR }; } @@ -482,6 +487,7 @@ internal static class CustomRolesHelper CustomRoles.Poisoner => RoleTypes.Impostor, CustomRoles.Duality => RoleTypes.Impostor, CustomRoles.SerialKiller => RoleTypes.Impostor, + CustomRoles.Explosivist => RoleTypes.Impostor, CustomRoles.Thanos => RoleTypes.Shapeshifter, CustomRoles.Slenderman => RoleTypes.Impostor, CustomRoles.Amogus => RoleTypes.Impostor, @@ -611,6 +617,7 @@ internal static class CustomRolesHelper CustomRoles.Rogue or CustomRoles.Parasite or CustomRoles.SerialKiller or + CustomRoles.Explosivist or CustomRoles.Thanos or CustomRoles.Duality or CustomRoles.Slenderman or @@ -673,6 +680,8 @@ internal static class CustomRolesHelper CustomRoles.PhantomEHR or CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR or + CustomRoles.Viper or + CustomRoles.ViperEHR or CustomRoles.LovingImpostor or CustomRoles.Godfather or CustomRoles.EvilDiviner or @@ -1029,6 +1038,7 @@ internal static class CustomRolesHelper CustomRoles.Wraith or CustomRoles.RouleteGrandeur or CustomRoles.Enderman or + CustomRoles.Explosivist or CustomRoles.Hookshot or CustomRoles.Mycologist or CustomRoles.Magician or @@ -1052,12 +1062,12 @@ 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.Commited when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => 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 or CustomRoles.Detective => false, + CustomRoles.Sleuth when pc.GetCustomRole() is CustomRoles.NecroGuesser or CustomRoles.Imitator or CustomRoles.Forensic => 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.Oblivious when pc.Is(CustomRoles.Altruist) => false, @@ -1070,7 +1080,7 @@ internal static class CustomRolesHelper 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.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter) => false, + CustomRoles.Rookie when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => 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, @@ -1088,11 +1098,11 @@ internal static class CustomRolesHelper 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.Oblivious when pc.Is(CustomRoles.Forensic) || 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.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter) => false, + CustomRoles.Damocles when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => 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, @@ -1140,9 +1150,9 @@ 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.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.Reach when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => false, + CustomRoles.Magnet when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => false, + CustomRoles.Haste when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) || !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 or CustomRoles.Ankylosaurus => false, @@ -1153,6 +1163,8 @@ internal static class CustomRolesHelper 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.Examiner when !pc.IsCrewmate() || pc.GetCustomRole().IsDesyncRole() => false, + CustomRoles.Venom when !pc.IsImpostor() => false, CustomRoles.Unreportable when pc.Is(CustomRoles.Bait) => false, CustomRoles.Oblivious when pc.Is(CustomRoles.Bloodhound) => false, CustomRoles.Oblivious when pc.Is(CustomRoles.Vulture) => false, @@ -1284,6 +1296,8 @@ internal static class CustomRolesHelper CustomRoles.Scientist or CustomRoles.GuardianAngel or CustomRoles.Impostor or + CustomRoles.Detective or + CustomRoles.Viper or CustomRoles.Phantom or CustomRoles.Shapeshifter; } @@ -1314,30 +1328,6 @@ internal static class CustomRolesHelper return Options.GetRoleSpawnMode(role); } - public static float GetChance(this CustomRoles role) - { - if (role.IsVanilla()) - { - IRoleOptionsCollection roleOpt = Main.NormalOptions.RoleOptions; - - return role switch - { - CustomRoles.Engineer => roleOpt.GetChancePerGame(RoleTypes.Engineer), - CustomRoles.Noisemaker => roleOpt.GetChancePerGame(RoleTypes.Noisemaker), - CustomRoles.Tracker => roleOpt.GetChancePerGame(RoleTypes.Tracker), - CustomRoles.Scientist => roleOpt.GetChancePerGame(RoleTypes.Scientist), - CustomRoles.Impostor => roleOpt.GetChancePerGame(RoleTypes.Impostor), - CustomRoles.Phantom => roleOpt.GetChancePerGame(RoleTypes.Phantom), - CustomRoles.Shapeshifter => roleOpt.GetChancePerGame(RoleTypes.Shapeshifter), - CustomRoles.GuardianAngel => roleOpt.GetChancePerGame(RoleTypes.GuardianAngel), - CustomRoles.Crewmate => roleOpt.GetChancePerGame(RoleTypes.Crewmate), - _ => 0 - } / 100f; - } - - return Options.GetRoleChance(role); - } - public static bool IsEnable(this CustomRoles role) { return role switch @@ -1441,7 +1431,7 @@ internal static class CustomRolesHelper RoleOptionType.Impostor_Concealing => Utils.GetRoleColor(CustomRoles.CopyCat), RoleOptionType.Impostor_Miscellaneous => Palette.ImpostorRed, RoleOptionType.Crewmate_Miscellaneous => Palette.CrewmateBlue, - RoleOptionType.Crewmate_Investigate => Utils.GetRoleColor(CustomRoles.Detective), + RoleOptionType.Crewmate_Investigate => Utils.GetRoleColor(CustomRoles.Forensic), RoleOptionType.Crewmate_Support => Utils.GetRoleColor(CustomRoles.NiceEraser), RoleOptionType.Crewmate_Killing => Utils.GetRoleColor(CustomRoles.Sheriff), RoleOptionType.Crewmate_Power => Utils.GetRoleColor(CustomRoles.Electric), @@ -1650,6 +1640,8 @@ internal static class CustomRolesHelper CustomRoles.ShapeshifterEHR => RoleOptionType.Impostor_Miscellaneous, CustomRoles.Phantom => RoleOptionType.Impostor_Miscellaneous, CustomRoles.PhantomEHR => RoleOptionType.Impostor_Miscellaneous, + CustomRoles.Viper => RoleOptionType.Impostor_Miscellaneous, + CustomRoles.ViperEHR => RoleOptionType.Impostor_Miscellaneous, CustomRoles.DoubleAgent => RoleOptionType.Crewmate_Investigate, _ => role.IsCrewmate() ? RoleOptionType.Crewmate_Miscellaneous : RoleOptionType.Neutral_Benign }; @@ -1669,6 +1661,8 @@ internal static class CustomRolesHelper CustomRoles.TrackerEHR => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.Noisemaker => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.NoisemakerEHR => RoleOptionType.Crewmate_Miscellaneous, + CustomRoles.Detective => RoleOptionType.Crewmate_Miscellaneous, + CustomRoles.DetectiveEHR => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.Addict => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.CameraMan => RoleOptionType.Crewmate_Miscellaneous, CustomRoles.Demolitionist => RoleOptionType.Crewmate_Miscellaneous, @@ -1692,7 +1686,7 @@ internal static class CustomRolesHelper CustomRoles.Chameleon => RoleOptionType.Crewmate_Investigate, CustomRoles.Clairvoyant => RoleOptionType.Crewmate_Investigate, CustomRoles.Bloodhound => RoleOptionType.Crewmate_Investigate, - CustomRoles.Detective => RoleOptionType.Crewmate_Investigate, + CustomRoles.Forensic => RoleOptionType.Crewmate_Investigate, CustomRoles.Doctor => RoleOptionType.Crewmate_Investigate, CustomRoles.DoubleAgent => RoleOptionType.Crewmate_Investigate, CustomRoles.Druid => RoleOptionType.Crewmate_Investigate, @@ -1863,6 +1857,7 @@ public enum CountTypes HexMaster, Wraith, SerialKiller, + Explosivist, Thanos, Duality, Slenderman, diff --git a/Modules/ExtendedPlayerControl.cs b/Modules/ExtendedPlayerControl.cs index 7072d4da1..46113568e 100644 --- a/Modules/ExtendedPlayerControl.cs +++ b/Modules/ExtendedPlayerControl.cs @@ -477,6 +477,8 @@ internal static class ExtendedPlayerControl CustomRoles.Physicist when newRoleType == RoleTypes.Crewmate => RoleTypes.Scientist, CustomRoles.Finder when newRoleType is RoleTypes.Crewmate or RoleTypes.Scientist or RoleTypes.Noisemaker => RoleTypes.Tracker, CustomRoles.Noisy when newRoleType == RoleTypes.Crewmate => RoleTypes.Noisemaker, + CustomRoles.Examiner when newRoleType is RoleTypes.Crewmate or RoleTypes.Scientist or RoleTypes.Noisemaker => RoleTypes.Detective, + CustomRoles.Venom when newRoleType == RoleTypes.Impostor => RoleTypes.Viper, _ => newRoleType }, _ => newRoleType @@ -1337,7 +1339,7 @@ internal static class ExtendedPlayerControl { CustomRoles role = pc.GetCustomRole(); if (pc.Data.Role.Role == RoleTypes.GuardianAngel) return false; - if (role.GetVNRole(true) is CustomRoles.Impostor or CustomRoles.ImpostorEHR or CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR or CustomRoles.Phantom or CustomRoles.PhantomEHR) return true; + if (role.GetVNRole(true) is CustomRoles.Impostor or CustomRoles.ImpostorEHR or CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR or CustomRoles.Phantom or CustomRoles.PhantomEHR or CustomRoles.Viper or CustomRoles.ViperEHR) return true; if (pc.GetRoleTypes() is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Phantom) return true; if (pc.Is(CustomRoles.Bloodlust)) return true; return !HasTasks(pc.Data, false); @@ -2112,6 +2114,8 @@ internal static class ExtendedPlayerControl { } x when x.Contains(CustomRoles.Physicist) => RoleTypes.Scientist, { } x when x.Contains(CustomRoles.Finder) => RoleTypes.Tracker, { } x when x.Contains(CustomRoles.Noisy) => RoleTypes.Noisemaker, + { } x when x.Contains(CustomRoles.Examiner) => RoleTypes.Detective, + { } x when x.Contains(CustomRoles.Venom) => RoleTypes.Viper, _ => pc.GetCustomRole().GetRoleTypes() }; } diff --git a/Modules/Extensions/IGameManagerEx.cs b/Modules/Extensions/IGameManagerEx.cs index 276413e32..380052bf2 100644 --- a/Modules/Extensions/IGameManagerEx.cs +++ b/Modules/Extensions/IGameManagerEx.cs @@ -9,13 +9,13 @@ public static class IGameManagerEx opt.SetBool(name, value); } - public static void Set(this BoolOptionNames name, bool value, NormalGameOptionsV09 opt) + public static void Set(this BoolOptionNames name, bool value, NormalGameOptionsV10 opt) { if (name is not BoolOptionNames.GhostsDoTasks and not BoolOptionNames.Roles) opt.SetBool(name, value); } - public static void Set(this BoolOptionNames name, bool value, HideNSeekGameOptionsV09 opt) + public static void Set(this BoolOptionNames name, bool value, HideNSeekGameOptionsV10 opt) { opt.SetBool(name, value); } @@ -25,12 +25,12 @@ public static class IGameManagerEx opt.SetInt(name, value); } - public static void Set(this Int32OptionNames name, int value, NormalGameOptionsV09 opt) + public static void Set(this Int32OptionNames name, int value, NormalGameOptionsV10 opt) { opt.SetInt(name, value); } - public static void Set(this Int32OptionNames name, int value, HideNSeekGameOptionsV09 opt) + public static void Set(this Int32OptionNames name, int value, HideNSeekGameOptionsV10 opt) { opt.SetInt(name, value); } @@ -40,12 +40,12 @@ public static class IGameManagerEx opt.SetFloat(name, value); } - public static void Set(this FloatOptionNames name, float value, NormalGameOptionsV09 opt) + public static void Set(this FloatOptionNames name, float value, NormalGameOptionsV10 opt) { opt.SetFloat(name, value); } - public static void Set(this FloatOptionNames name, float value, HideNSeekGameOptionsV09 opt) + public static void Set(this FloatOptionNames name, float value, HideNSeekGameOptionsV10 opt) { opt.SetFloat(name, value); } @@ -55,12 +55,12 @@ public static class IGameManagerEx opt.SetByte(name, value); } - public static void Set(this ByteOptionNames name, byte value, NormalGameOptionsV09 opt) + public static void Set(this ByteOptionNames name, byte value, NormalGameOptionsV10 opt) { opt.SetByte(name, value); } - public static void Set(this ByteOptionNames name, byte value, HideNSeekGameOptionsV09 opt) + public static void Set(this ByteOptionNames name, byte value, HideNSeekGameOptionsV10 opt) { opt.SetByte(name, value); } @@ -70,12 +70,12 @@ public static class IGameManagerEx opt.SetUInt(name, value); } - public static void Set(this UInt32OptionNames name, uint value, NormalGameOptionsV09 opt) + public static void Set(this UInt32OptionNames name, uint value, NormalGameOptionsV10 opt) { opt.SetUInt(name, value); } - public static void Set(this UInt32OptionNames name, uint value, HideNSeekGameOptionsV09 opt) + public static void Set(this UInt32OptionNames name, uint value, HideNSeekGameOptionsV10 opt) { opt.SetUInt(name, value); } diff --git a/Modules/GameOptionsSender/GameOptionsSender.cs b/Modules/GameOptionsSender/GameOptionsSender.cs index c899e4cd6..a104c62c5 100644 --- a/Modules/GameOptionsSender/GameOptionsSender.cs +++ b/Modules/GameOptionsSender/GameOptionsSender.cs @@ -24,10 +24,10 @@ public abstract class GameOptionsSender writer.StartMessage(0); writer.Write((byte)opt.GameMode); - if (opt.TryCast(out NormalGameOptionsV09 normalOpt)) - NormalGameOptionsV09.Serialize(writer, normalOpt); - else if (opt.TryCast(out HideNSeekGameOptionsV09 hnsOpt)) - HideNSeekGameOptionsV09.Serialize(writer, hnsOpt); + if (opt.TryCast(out NormalGameOptionsV10 normalOpt)) + NormalGameOptionsV10.Serialize(writer, normalOpt); + else if (opt.TryCast(out HideNSeekGameOptionsV10 hnsOpt)) + HideNSeekGameOptionsV10.Serialize(writer, hnsOpt); else { writer.Recycle(); diff --git a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs index 55eef6f89..9cfec7608 100644 --- a/Modules/GameOptionsSender/PlayerGameOptionsSender.cs +++ b/Modules/GameOptionsSender/PlayerGameOptionsSender.cs @@ -4,6 +4,7 @@ using AmongUs.GameOptions; using EHR.AddOns.Common; using EHR.AddOns.Crewmate; using EHR.AddOns.GhostRoles; +using EHR.AddOns.Impostor; using EHR.Coven; using EHR.Crewmate; using EHR.Impostor; @@ -22,7 +23,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS public PlayerControl player = player; private static IGameOptions BasedGameOptions => - Main.RealOptionsData.Restore(new NormalGameOptionsV09(new UnityLogger().CastFast()).CastFast()); + Main.RealOptionsData.Restore(new NormalGameOptionsV10(new UnityLogger().CastFast()).CastFast()); protected override bool IsDirty { get; set; } @@ -254,6 +255,12 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS AURoleOptions.TrackerDuration = CrewmateVanillaRoles.TrackerDuration.GetFloat(); AURoleOptions.TrackerDelay = CrewmateVanillaRoles.TrackerDelay.GetFloat(); break; + case CustomRoles.DetectiveEHR: + AURoleOptions.DetectiveSuspectLimit = CrewmateVanillaRoles.DetectiveSuspectLimit.GetFloat(); + break; + case CustomRoles.ViperEHR: + AURoleOptions.ViperDissolveTime = ImpostorVanillaRoles.ViperDissolveTime.GetFloat(); + break; } // When impostor alert is off, and the player is a desync crewmate, set impostor alert as true @@ -425,6 +432,12 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS AURoleOptions.NoisemakerImpostorAlert = Noisy.NoisyImpostorAlert.GetBool(); AURoleOptions.NoisemakerAlertDuration = Noisy.NoisyAlertDuration.GetFloat(); break; + case CustomRoles.Examiner when roleTypes == RoleTypes.Detective: + AURoleOptions.DetectiveSuspectLimit = Examiner.ExaminerSuspectLimit.GetFloat(); + break; + case CustomRoles.Venom when roleTypes == RoleTypes.Viper: + AURoleOptions.ViperDissolveTime = Venom.VenomDissolveTime.GetFloat(); + break; } } @@ -458,6 +471,12 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS case RoleTypes.Tracker: AURoleOptions.TrackerCooldown *= 0.75f; break; + case RoleTypes.Viper: + AURoleOptions.ViperDissolveTime *= 0.75f; + break; + case RoleTypes.Detective: + AURoleOptions.DetectiveSuspectLimit *= 1.25f; + goto default; case RoleTypes.CrewmateGhost: case RoleTypes.ImpostorGhost: break; diff --git a/Modules/OptionBackup/OptionBackupData.cs b/Modules/OptionBackup/OptionBackupData.cs index 5ce8661a9..a22c795b9 100644 --- a/Modules/OptionBackup/OptionBackupData.cs +++ b/Modules/OptionBackup/OptionBackupData.cs @@ -42,7 +42,7 @@ public class OptionBackupData // Since TryGetUInt is not implemented, get it separately AllValues.Add(new UIntOptionBackupValue(UInt32OptionNames.Keywords, (uint)option.Keywords)); - RoleTypes[] array = [RoleTypes.Scientist, RoleTypes.Engineer, RoleTypes.GuardianAngel, RoleTypes.Shapeshifter, RoleTypes.Noisemaker, RoleTypes.Phantom, RoleTypes.Tracker]; + RoleTypes[] array = [RoleTypes.Scientist, RoleTypes.Engineer, RoleTypes.GuardianAngel, RoleTypes.Shapeshifter, RoleTypes.Noisemaker, RoleTypes.Phantom, RoleTypes.Tracker, RoleTypes.Viper, RoleTypes.Detective]; foreach (RoleTypes role in array) AllValues.Add(new RoleRateBackupValue(role, option.RoleOptions.GetNumPerGame(role), option.RoleOptions.GetChancePerGame(role))); } diff --git a/Modules/Utils.cs b/Modules/Utils.cs index b57c6dfce..be898b186 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -839,6 +839,7 @@ public static class Utils case CustomRoles.Eclipse: case CustomRoles.Pyromaniac: case CustomRoles.SerialKiller: + case CustomRoles.Explosivist: case CustomRoles.Thanos: case CustomRoles.Slenderman: case CustomRoles.Amogus: @@ -3269,6 +3270,7 @@ public static class Utils CustomRoles.Tiger => Tiger.EnrageCooldown.GetInt() + (includeDuration ? Tiger.EnrageDuration.GetInt() : 0), CustomRoles.Nonplus => Nonplus.BlindCooldown.GetInt() + (includeDuration ? Nonplus.BlindDuration.GetInt() : 0), CustomRoles.Amogus => Amogus.AbilityCooldown.GetInt() + (includeDuration ? Amogus.AbilityDuration.GetInt() : 0), + CustomRoles.Explosivist => Explosivist.AbilityCooldown.GetInt() + (includeDuration ? Explosivist.ExplosionDelay.GetInt() : 0), CustomRoles.Cherokious => Cherokious.KillCooldown.GetInt(), CustomRoles.Shifter => Shifter.KillCooldown.GetInt(), CustomRoles.NoteKiller => NoteKiller.AbilityCooldown.GetInt(), @@ -4105,7 +4107,7 @@ public static class Utils { int baseColorId = deadBodyParent.Data.DefaultOutfit.ColorId; deadBodyParent.Data.DefaultOutfit.ColorId = colorId; - DeadBody deadBody = Object.Instantiate(GameManager.Instance.DeadBodyPrefab); + DeadBody deadBody = Object.Instantiate(GameManager.Instance.deadBodyPrefab[0]); deadBody.enabled = false; deadBody.ParentId = deadBodyParent.PlayerId; foreach (SpriteRenderer b in deadBody.bodyRenderers) diff --git a/Patches/ChatBubblePatch.cs b/Patches/ChatBubblePatch.cs index 73d4a9109..19553edaf 100644 --- a/Patches/ChatBubblePatch.cs +++ b/Patches/ChatBubblePatch.cs @@ -23,7 +23,8 @@ internal static class ChatBubbleSetNamePatch if (GameStates.IsInGame && !voted && seer.PlayerId == target.PlayerId) __instance.NameText.color = seer.GetRoleColor(); - if (seer.GetCustomRole().GetDYRole() is RoleTypes.Shapeshifter or RoleTypes.Phantom) __instance.NameText.color = Color.white; + if (seer.GetCustomRole().GetDYRole() is RoleTypes.Shapeshifter or RoleTypes.Phantom or RoleTypes.Viper) + __instance.NameText.color = Color.white; if (Main.DarkTheme.Value) { diff --git a/Patches/ChatCommandPatch.cs b/Patches/ChatCommandPatch.cs index fb3094d32..6ff7e380c 100644 --- a/Patches/ChatCommandPatch.cs +++ b/Patches/ChatCommandPatch.cs @@ -3238,6 +3238,30 @@ internal static class ChatCommands subArgs = args.Length < 3 ? "" : args[2]; GameOptionsManager.Instance.CurrentGameOptions.SetFloat(FloatOptionNames.TrackerDelay, float.Parse(subArgs)); break; + case "vipercount": + subArgs = args.Length < 3 ? "" : args[2]; + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.SetRoleRate(RoleTypes.Viper, int.Parse(subArgs), GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(RoleTypes.Viper)); + break; + case "viperchance": + subArgs = args.Length < 3 ? "" : args[2]; + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.SetRoleRate(RoleTypes.Viper, GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(RoleTypes.Viper), int.Parse(subArgs)); + break; + case "viperdissolvetime": + subArgs = args.Length < 3 ? "" : args[2]; + GameOptionsManager.Instance.CurrentGameOptions.SetFloat(FloatOptionNames.ViperDissolveTime, float.Parse(subArgs)); + break; + case "detectivecount": + subArgs = args.Length < 3 ? "" : args[2]; + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.SetRoleRate(RoleTypes.Detective, int.Parse(subArgs), GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(RoleTypes.Detective)); + break; + case "detectivechance": + subArgs = args.Length < 3 ? "" : args[2]; + GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.SetRoleRate(RoleTypes.Detective, GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(RoleTypes.Detective), int.Parse(subArgs)); + break; + case "detectivesuspectlimit": + subArgs = args.Length < 3 ? "" : args[2]; + GameOptionsManager.Instance.CurrentGameOptions.SetFloat(FloatOptionNames.DetectiveSuspectLimit, float.Parse(subArgs)); + break; default: Utils.SendMessage(GetString("Commands.ChangeSettingHelp"), player.PlayerId); break; @@ -3328,7 +3352,7 @@ internal static class ChatCommands "擺爛人" or "摆烂" => GetString("Needy"), "獨裁者" or "独裁" or "dict" => GetString("Dictator"), "法醫" or "doc" => GetString("Doctor"), - "偵探" or "det" => GetString("Detective"), + "偵探" or "det" => GetString("Forensic"), "幸運兒" or "幸运" => GetString("Luckey"), "大明星" or "明星" or "ss" => GetString("SuperStar"), "demo" => GetString("Demolitionist"), diff --git a/Patches/CheckGameEndPatch.cs b/Patches/CheckGameEndPatch.cs index 740520d37..6ae72f7bf 100644 --- a/Patches/CheckGameEndPatch.cs +++ b/Patches/CheckGameEndPatch.cs @@ -1101,7 +1101,7 @@ internal static class GameEndChecker if ((GameData.Instance.TotalTasks == 0 && GameData.Instance.CompletedTasks == 0) || !Main.PlayerStates.Values.Any(x => x.TaskState.HasTasks)) return false; if (Options.DisableTaskWinIfAllCrewsAreDead.GetBool() && !Main.AllAlivePlayerControls.Any(x => x.Is(CustomRoleTypes.Crewmate))) return false; - if (Options.DisableTaskWinIfAllCrewsAreConverted.GetBool() && Main.AllAlivePlayerControls.Where(x => x.Is(Team.Crewmate) && x.GetRoleTypes() is RoleTypes.Crewmate or RoleTypes.Engineer or RoleTypes.Scientist or RoleTypes.Noisemaker or RoleTypes.Tracker or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel).All(x => x.IsConverted())) return false; + if (Options.DisableTaskWinIfAllCrewsAreConverted.GetBool() && Main.AllAlivePlayerControls.Where(x => x.Is(Team.Crewmate) && x.GetRoleTypes() is RoleTypes.Crewmate or RoleTypes.Engineer or RoleTypes.Scientist or RoleTypes.Noisemaker or RoleTypes.Tracker or RoleTypes.Detective or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel).All(x => x.IsConverted())) return false; if (GameData.Instance.TotalTasks <= GameData.Instance.CompletedTasks) { diff --git a/Patches/Crowded.cs b/Patches/Crowded.cs index efcb58a63..d8b99a502 100644 --- a/Patches/Crowded.cs +++ b/Patches/Crowded.cs @@ -266,11 +266,11 @@ internal static class Crowded } } - [HarmonyPatch(typeof(NormalGameOptionsV09), nameof(NormalGameOptionsV09.AreInvalid))] + [HarmonyPatch(typeof(NormalGameOptionsV10), nameof(NormalGameOptionsV10.AreInvalid))] public static class InvalidOptionsPatches { [SuppressMessage("ReSharper", "UnusedMember.Global")] - public static bool Prefix(NormalGameOptionsV09 __instance, [HarmonyArgument(0)] int maxExpectedPlayers) + public static bool Prefix(NormalGameOptionsV10 __instance, [HarmonyArgument(0)] int maxExpectedPlayers) { return __instance.MaxPlayers > maxExpectedPlayers || __instance.NumImpostors < 1 || diff --git a/Patches/HudPatch.cs b/Patches/HudPatch.cs index a79ba7323..997e167be 100644 --- a/Patches/HudPatch.cs +++ b/Patches/HudPatch.cs @@ -219,6 +219,7 @@ internal static class HudManagerPatch RoleTypes roleTypes = player.GetRoleTypes(); __instance.AbilityButton?.OverrideText(GetString($"AbilityButtonText.{roleTypes}")); + __instance.SecondaryAbilityButton?.OverrideText(GetString($"SecondaryAbilityButtonText.{roleTypes}")); PlayerState state = Main.PlayerStates[player.PlayerId]; diff --git a/Patches/IntroPatch.cs b/Patches/IntroPatch.cs index ebf77fd39..61e50f264 100644 --- a/Patches/IntroPatch.cs +++ b/Patches/IntroPatch.cs @@ -711,6 +711,14 @@ internal static class BeginCrewmatePatch or CustomRoles.EvilTracker or CustomRoles.Scout => GetIntroSound(RoleTypes.Tracker), + + CustomRoles.Viper + or CustomRoles.ViperEHR + => GetIntroSound(RoleTypes.Viper), + + CustomRoles.Detective + or CustomRoles.DetectiveEHR + => GetIntroSound(RoleTypes.Detective), CustomRoles.Noisemaker or CustomRoles.NoisemakerEHR @@ -921,7 +929,7 @@ internal static class BeginCrewmatePatch private static AudioClip GetIntroSound(RoleTypes roleType) { - return RoleManager.Instance.AllRoles.FirstOrDefault(role => role.Role == roleType)?.IntroSound; + return RoleManager.Instance.AllRoles.Find((Il2CppSystem.Predicate)(role => role.Role == roleType))?.IntroSound; } } diff --git a/Patches/MeetingHudPatch.cs b/Patches/MeetingHudPatch.cs index 97c420e1e..a4d49ff38 100644 --- a/Patches/MeetingHudPatch.cs +++ b/Patches/MeetingHudPatch.cs @@ -776,7 +776,7 @@ internal static class MeetingHudStartPatch AddMsg(string.Format(GetString("SilencerDead"), playername, pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Silencer), GetString("SilencerKillTitle")))); } - if (Detective.DetectiveNotify.TryGetValue(pc.PlayerId, out string value1)) AddMsg(value1, pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Detective), GetString("DetectiveNoticeTitle"))); + if (Forensic.ForensicNotify.TryGetValue(pc.PlayerId, out string value1)) AddMsg(value1, pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Forensic), GetString("ForensicNoticeTitle"))); if (Main.SleuthMsgs.TryGetValue(pc.PlayerId, out string msg)) AddMsg(msg, pc.PlayerId, Utils.ColorString(Utils.GetRoleColor(CustomRoles.Sleuth), GetString("Sleuth"))); if (pc.Is(CustomRoles.Mimic) && !pc.IsAlive()) @@ -815,7 +815,7 @@ internal static class MeetingHudStartPatch Main.SuperStarDead.Clear(); Express.SpeedNormal.Clear(); Express.SpeedUp.Clear(); - Detective.DetectiveNotify.Clear(); + Forensic.ForensicNotify.Clear(); Main.SleuthMsgs.Clear(); Virus.VirusNotify.Clear(); Mortician.MsgToSend.Clear(); diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs index 83a1666ab..843e72589 100644 --- a/Patches/OnGameStartedPatch.cs +++ b/Patches/OnGameStartedPatch.cs @@ -170,7 +170,9 @@ internal static class ChangeRoleSettings RoleTypes.Shapeshifter, RoleTypes.Noisemaker, RoleTypes.Phantom, - RoleTypes.Tracker + RoleTypes.Tracker, + RoleTypes.Detective, + RoleTypes.Viper }.Do(x => Main.NormalOptions.roleOptions.SetRoleRate(x, 0, 0)); if (Main.NormalOptions.MapId > 5 && !(Main.NormalOptions.MapId == 6 && SubmergedCompatibility.Loaded)) @@ -241,7 +243,7 @@ internal static class ChangeRoleSettings Mafia.MafiaRevenged = []; Warlock.IsCurseAndKill = []; Warlock.IsCursed = false; - Detective.DetectiveNotify = []; + Forensic.ForensicNotify = []; Provocateur.Provoked = []; Crusader.ForCrusade = []; Godfather.GodfatherTarget = byte.MaxValue; @@ -472,7 +474,7 @@ internal static class StartGameHostPatch public static readonly Dictionary DataDisconnected = []; - private static RoleOptionsCollectionV09 RoleOpt => Main.NormalOptions.roleOptions; + private static RoleOptionsCollectionV10 RoleOpt => Main.NormalOptions.roleOptions; private static void UpdateRoleTypeNums() { @@ -483,7 +485,9 @@ internal static class StartGameHostPatch { RoleTypes.Shapeshifter, AddShapeshifterNum }, { RoleTypes.Noisemaker, AddNoisemakerNum }, { RoleTypes.Phantom, AddPhantomNum }, - { RoleTypes.Tracker, AddTrackerNum } + { RoleTypes.Tracker, AddTrackerNum }, + { RoleTypes.Detective, AddDetectiveNum }, + { RoleTypes.Viper, AddViperNum } }; } @@ -690,6 +694,8 @@ internal static class StartGameHostPatch bool nimbleSpawn = random.Next(100) < (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Nimble, out IntegerOptionItem option2) ? option2.GetFloat() : 0) && CustomRoles.Nimble.IsEnable(); bool finderSpawn = random.Next(100) < (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Finder, out IntegerOptionItem option3) ? option3.GetFloat() : 0) && CustomRoles.Finder.IsEnable(); bool noisySpawn = random.Next(100) < (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Noisy, out IntegerOptionItem option4) ? option4.GetFloat() : 0) && CustomRoles.Noisy.IsEnable(); + bool examinerSpawn = random.Next(100) < (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Examiner, out IntegerOptionItem option5) ? option5.GetFloat() : 0) && CustomRoles.Examiner.IsEnable(); + bool venomSpawn = random.Next(100) < (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Venom, out IntegerOptionItem option6) ? option6.GetFloat() : 0) && CustomRoles.Venom.IsEnable(); if (Options.EveryoneCanVent.GetBool()) { @@ -697,12 +703,13 @@ internal static class StartGameHostPatch physicistSpawn = false; finderSpawn = false; noisySpawn = false; + examinerSpawn = false; } - HashSet bloodlustList = [], nimbleList = [], physicistList = [], finderList = [], noisyList = []; + HashSet bloodlustList = [], nimbleList = [], physicistList = [], finderList = [], noisyList = [], examinerList = [], venomList = []; bool hasBanned = Main.NeverSpawnTogetherCombos.TryGetValue(OptionItem.CurrentPreset, out Dictionary> banned); - if (nimbleSpawn || physicistSpawn || finderSpawn || noisySpawn || bloodlustSpawn) + if (nimbleSpawn || physicistSpawn || finderSpawn || noisySpawn || bloodlustSpawn || examinerSpawn || venomSpawn) { foreach (PlayerControl player in Main.AllPlayerControls) { @@ -715,6 +722,8 @@ internal static class StartGameHostPatch bool physicistBanned = hasBanned && banned.Any(x => x.Key == kp.Value && x.Value.Contains(CustomRoles.Physicist)); bool finderBanned = hasBanned && banned.Any(x => x.Key == kp.Value && x.Value.Contains(CustomRoles.Finder)); bool noisyBanned = hasBanned && banned.Any(x => x.Key == kp.Value && x.Value.Contains(CustomRoles.Noisy)); + bool examinerBanned = hasBanned && banned.Any(x => x.Key == kp.Value && x.Value.Contains(CustomRoles.Examiner)); + bool venomBanned = hasBanned && banned.Any(x => x.Key == kp.Value && x.Value.Contains(CustomRoles.Venom)); if (kp.Value.IsCrewmate()) { @@ -726,8 +735,12 @@ internal static class StartGameHostPatch if (!physicistBanned) physicistList.Add(player.PlayerId); if (!finderBanned) finderList.Add(player.PlayerId); if (!noisyBanned) noisyList.Add(player.PlayerId); + if (!examinerBanned) examinerList.Add(player.PlayerId); } } + + if (kp.Value.IsImpostor() && kp.Value.GetRoleTypes() == RoleTypes.Impostor && !venomBanned) + venomList.Add(player.PlayerId); } } @@ -737,7 +750,9 @@ internal static class StartGameHostPatch { CustomRoles.Nimble, (nimbleSpawn, nimbleList) }, { CustomRoles.Physicist, (physicistSpawn, physicistList) }, { CustomRoles.Finder, (finderSpawn, finderList) }, - { CustomRoles.Noisy, (noisySpawn, noisyList) } + { CustomRoles.Noisy, (noisySpawn, noisyList) }, + { CustomRoles.Examiner, (examinerSpawn, examinerList) }, + { CustomRoles.Venom, (venomSpawn, venomList) } }; for (var i = 0; i < roleSpawnMapping.Count; i++) @@ -875,7 +890,7 @@ internal static class StartGameHostPatch { foreach (CustomRoles role in item.Value) { - if (role is CustomRoles.Nimble or CustomRoles.Physicist or CustomRoles.Bloodlust or CustomRoles.Finder or CustomRoles.Noisy) continue; + if (role is CustomRoles.Nimble or CustomRoles.Physicist or CustomRoles.Bloodlust or CustomRoles.Finder or CustomRoles.Noisy or CustomRoles.Examiner or CustomRoles.Venom) continue; state.SetSubRole(role); if (overrideLovers && role == CustomRoles.Lovers) Main.LoversPlayers.Add(Utils.GetPlayerById(item.Key)); @@ -1238,10 +1253,10 @@ internal static class StartGameHostPatch if (Options.CurrentGameMode == CustomGameMode.Standard) { - if (target.Is(Team.Crewmate) && roleType is not (RoleTypes.Crewmate or RoleTypes.Scientist or RoleTypes.Engineer or RoleTypes.Noisemaker or RoleTypes.Tracker or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel)) + if (target.Is(Team.Crewmate) && roleType is not (RoleTypes.Crewmate or RoleTypes.Scientist or RoleTypes.Engineer or RoleTypes.Noisemaker or RoleTypes.Tracker or RoleTypes.Detective or RoleTypes.CrewmateGhost or RoleTypes.GuardianAngel)) displayRole = RoleTypes.Crewmate; - if (target.Is(Team.Impostor) && roleType is not (RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Phantom or RoleTypes.ImpostorGhost)) + if (target.Is(Team.Impostor) && roleType is not (RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Phantom or RoleTypes.Viper or RoleTypes.ImpostorGhost)) displayRole = RoleTypes.Impostor; } @@ -1399,6 +1414,8 @@ internal static class StartGameHostPatch CustomRoles.Physicist => RoleTypes.Scientist, CustomRoles.Finder => RoleTypes.Tracker, CustomRoles.Noisy => RoleTypes.Noisemaker, + CustomRoles.Examiner => RoleTypes.Detective, + CustomRoles.Venom => RoleTypes.Viper, _ => roleType }; } @@ -1536,6 +1553,8 @@ internal static class StartGameHostPatch CustomRoles.Physicist when roleTypes == RoleTypes.Crewmate => RoleTypes.Scientist, CustomRoles.Finder when roleTypes is RoleTypes.Crewmate or RoleTypes.Scientist or RoleTypes.Noisemaker => RoleTypes.Tracker, CustomRoles.Noisy when roleTypes == RoleTypes.Crewmate => RoleTypes.Noisemaker, + CustomRoles.Examiner when roleTypes is RoleTypes.Crewmate or RoleTypes.Scientist or RoleTypes.Noisemaker => RoleTypes.Detective, + CustomRoles.Venom when roleTypes == RoleTypes.Impostor => RoleTypes.Viper, _ => roleTypes } : roleTypes; diff --git a/Patches/PhantomRolePatch.cs b/Patches/PhantomRolePatch.cs index 0c8b8cf4e..a4484fa76 100644 --- a/Patches/PhantomRolePatch.cs +++ b/Patches/PhantomRolePatch.cs @@ -326,7 +326,7 @@ public static class PhantomRoleUseAbilityPatch FastDestroyableSingleton.Instance.AbilityButton.SetSecondImage(__instance.Ability); FastDestroyableSingleton.Instance.AbilityButton.OverrideText(FastDestroyableSingleton.Instance.GetString(StringNames.PhantomAbilityUndo, new Il2CppReferenceArray(0))); - __instance.Player.CmdCheckVanish(GameManager.Instance.LogicOptions.GetPhantomDuration()); + __instance.Player.CmdCheckVanish(GameManager.Instance.LogicOptions.GetRoleFloat(FloatOptionNames.PhantomDuration)); return false; } } diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 7adc94a97..33839c21b 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -1258,8 +1258,8 @@ internal static class ReportDeadBodyPatch if (tpc != null && !tpc.IsAlive()) { - if (player.Is(CustomRoles.Detective) && player.PlayerId != target.PlayerId) - Detective.OnReportDeadBody(player, target.Object); + if (player.Is(CustomRoles.Forensic) && player.PlayerId != target.PlayerId) + Forensic.OnReportDeadBody(player, target.Object); else if (player.Is(CustomRoles.Sleuth) && player.PlayerId != target.PlayerId) { string msg = string.Format(GetString("SleuthMsg"), tpc.GetRealName(), tpc.GetDisplayRoleName()); @@ -2444,6 +2444,8 @@ internal static class PlayerControlLocalSetRolePatch RoleTypes.Noisemaker => CustomRoles.NoisemakerEHR, RoleTypes.Scientist => CustomRoles.ScientistEHR, RoleTypes.Tracker => CustomRoles.TrackerEHR, + RoleTypes.Detective => CustomRoles.DetectiveEHR, + RoleTypes.Viper => CustomRoles.ViperEHR, _ => CustomRoles.NotAssigned }; diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 2f2f711bb..9accaf76f 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -165,6 +165,8 @@ "Noisemaker": "Noisemaker", "Tracker": "Tracker", "Phantom": "Phantom", + "Detective": "Detective", + "Viper": "Viper", "EngineerEHR": "Engineer", "ScientistEHR": "Scientist", "ShapeshifterEHR": "Shapeshifter", @@ -174,6 +176,8 @@ "NoisemakerEHR": "Noisemaker", "TrackerEHR": "Tracker", "PhantomEHR": "Phantom", + "DetectiveEHR": "Detective", + "ViperEHR": "Viper", "BountyHunter": "Bounty Hunter", "Mare": "Mare", "FireWorks": "Fireworker", @@ -249,7 +253,7 @@ "SpeedBooster": "Speed Booster", "Doctor": "Doctor", "Dictator": "Dictator", - "Detective": "Detective", + "Forensic": "Forensic", "NiceGuesser": "Nice Guesser", "Transporter": "Transporter", "TimeManager": "Time Manager", @@ -528,7 +532,7 @@ "SpeedBoosterInfo": "Boost your speed", "DoctorInfo": "Know how each player died", "DictatorInfo": "Exile a player based on your own judgment", - "DetectiveInfo": "Gain extra info from your body reports", + "ForensicInfo": "Gain extra info from your body reports", "UndercoverInfo": "Impostors see you as their partner", "SwordsManInfo": "You can kill 1 player", "NiceGuesserInfo": "Guess evil roles in meetings to kill", @@ -757,7 +761,7 @@ "SpeedBoosterInfoLong": "(Crewmates):\\nSpeed Booster increases a random player's movement speed every time they complete a task.", "DoctorInfoLong": "(Crewmates):\\nThe Doctor can view players' cause of death and access their vitals as long as their battery is still operational.", "DictatorInfoLong": "(Crewmates):\\nDictator can end the meeting by voting someone out, which will also result in their death.", - "DetectiveInfoLong": "(Crewmate):\\nAfter reporting the body, the Detective receives a clue about the victim's role. The host may reveal the murderer's role to the Detective based on the setting. Note: Detective won't be Oblivious.", + "ForensicInfoLong": "(Crewmate):\\nAfter reporting the body, the Forensic receives a clue about the victim's role. The host may reveal the murderer's role to the Forensic based on the setting. Note: Forensic won't be Oblivious.", "UndercoverInfoLong": "(Crewmates):\\nThe Impostors knows who Undercover is and sees him as a teammate, but Undercover himself does not know who the Impostors are.", "NiceGuesserInfoLong": "(Crewmates):\\nNice Guesser can guess a player's role during the meeting. Correct guess results in killing the target, while a wrong one leads to suicide. Use the command /bt [player id] [role] to guess. You can see player IDs before their names or use the /id command.", "SwordsManInfoLong": "(Crewmates):\\nVigilante can kill anyone, but only once. After killing, Vigilante can do tasks. Depending on settings, Vigilante can vent.", @@ -854,7 +858,7 @@ "TorchInfoLong": "(Add-ons):\\nOnly Crewmates will get Torch, who have increased vision and may not be affected by Lights Sabotage.", "SeerInfoLong": "(Add-ons):\\nWhen a player dies, the Seer sees a red flash (with an alarm sound like sabotage).", "BrakarInfoLong": "(Add-ons):\\nIn case of a tie, the target voted by the Tiebreaker has priority. However, if multiple Tiebreakers choose different targets simultaneously, their skills will not take effect.", - "ObliviousInfoLong": "(Add-ons):\\nDetective and Cleaners won't be Oblivious. Oblivious cannot report dead bodies. Note: Bait killed by Oblivious will still be reported automatically, and Oblivious can still be used as a scapegoat for Anonymous.", + "ObliviousInfoLong": "(Add-ons):\\nForensic and Cleaners won't be Oblivious. Oblivious cannot report dead bodies. Note: Bait killed by Oblivious will still be reported automatically, and Oblivious can still be used as a scapegoat for Anonymous.", "BewilderInfoLong": "(Add-ons):\\nBewilder's vision changes killer's vision on death.", "TruantInfoLong": "(Add-ons):\\nAfter each meeting, you freeze in place for a set amount of seconds.", "WorkhorseInfoLong": "(Add-ons):\\nComplete all tasks to become Workhorse and get extra tasks set by the host.", @@ -1524,10 +1528,10 @@ "CrewKillingRed": "Crewmate Killings can be red", "PsychicCanSeeNum": "Max number of red names", "PsychicFresh": "New red names every meeting", - "DetectiveCanknowKiller": "Can find the killer's role", - "DetectiveCanknowDeathReason": "Can find the cause of death", - "DetectiveCanknowAddons": "Can find the dead player's add-ons", - "DetectiveCanknowKillTime": "Can find how long ago the player died", + "ForensicCanknowKiller": "Can find the killer's role", + "ForensicCanknowDeathReason": "Can find the cause of death", + "ForensicCanknowAddons": "Can find the dead player's add-ons", + "ForensicCanknowKillTime": "Can find how long ago the player died", "ParanoiaVentCooldown": "Vent Cooldown", "ParanoiaNumOfUseButton": "Max number of portable buttons", "EveryOneKnowSuperStar": "Everyone knows the Super Star", @@ -2113,12 +2117,12 @@ "MafiaKillSucceed": "{0} was killed by the Nemesis!", "MafiaKillDisable": "Sorry, but according to Host's settings, Nemesis revenge is prohibited in this game", "SuperStarDead": "Shock! Super Star {0} has unfortunately been mercilessly killed in the recent period of time!", - "DetectiveNoticeVictim": "According to your investigation,\\nthe victim ({0}) had the role [{1}]", - "DetectiveNoticeKiller": "\\nThe killer's role is {0}", - "DetectiveNoticeKillerNotFound": "The Detective couldn't find evidence leading to a murderer, this is most likely suicide.", - "DetectiveNoticeDeathReason": "\\nThe victim's cause of death is {0}", - "DetectiveNoticeAddons": "\\nThe victim's add-ons are: {0}", - "DetectiveNoticeKillTime": "\\nThe victim died {0} seconds ago", + "ForensicNoticeVictim": "According to your investigation,\\nthe victim ({0}) had the role [{1}]", + "ForensicNoticeKiller": "\\nThe killer's role is {0}", + "ForensicNoticeKillerNotFound": "The Forensic couldn't find evidence leading to a murderer, this is most likely suicide.", + "ForensicNoticeDeathReason": "\\nThe victim's cause of death is {0}", + "ForensicNoticeAddons": "\\nThe victim's add-ons are: {0}", + "ForensicNoticeKillTime": "\\nThe victim died {0} seconds ago", "SleuthMsg": "{0}'s role is: {1}", "GodNoticeAlive": "During the meeting, each player felt a revelation from heaven, and it turned out that God is still alive!", "WorkaholicAdviceAlive": "It's not recommended to kill or vote {0} out. Doing so will help them finish their tasks quicker.", @@ -2934,7 +2938,7 @@ "MessageFromTheHost": "★ Host message ★", "SayTitle": "★ Moderator message ★", "MessageFromEAC": "★ EAC ★", - "DetectiveNoticeTitle": "★ Message for the Detective ★", + "ForensicNoticeTitle": "★ Message for the Forensic ★", "GuessKillTitle": "★ Message about Guessing ★", "SuperStarNewsTitle": "★ Urgent news ★", "GodAliveTitle": "★ Message about God ★", @@ -3022,6 +3026,8 @@ "EnterVentWinCountDown": "Enter vent within {0} seconds to win!", "InvalidArgs": "Invalid Args", "InvalidArguments": "Invalid Arguments", + "DetectiveSuspectLimit": "Suspects Per Case", + "ViperDissolveTime": "Body Dissolve Time", "On": "Always", "Off": "OFF", "ColoredOn": "✓", @@ -3123,6 +3129,9 @@ "AbilityButtonText.Impostor": "Haunt", "AbilityButtonText.Shapeshifter": "Shift", "AbilityButtonText.Phantom": "Vanish", + "AbilityButtonText.Viper": "Acid", + "AbilityButtonText.Detective": "Notes", + "SecondaryAbilityButtonText.Detective": "Interrogate", "GauloisKillButtonText": "Speed Up", "SniperSnipeButtonText": "Snipe", "FireWorksExplosionButtonText": "Detonate", @@ -9100,6 +9109,16 @@ "AntidoteCDOpt": "Decrease the cooldown by", "AntidoteCDReset": "Cooldown returns to normal after a meeting", "HackButtonText": "Mimic", + "DetectiveEHRInfo": "Gather Info, Solve Murders", + "DetectiveEHRInfoLong": "(Crewmates):\\nAs the Detective, you can write down notes about each crime scene and interrogate suspects.", + "ViperEHRInfo": "Kill to Dissolve Bodies", + "ViperEHRInfoLong": "(Impostors):\\nAs the Viper, the bodies you leave behind slowly dissolve due to your acid, making them disappear and unreportable.", + "Explosivist": "Explosivist", + "ExplosivistInfo": "Explode Time Bombs", + "ExplosivistInfoLong": "(Neutrals):\\nAs the Explosivist, you cannot kill normally. Instead, you can pet/vanish to develop a bomb and start carrying it. Others will only see the bomb itself, and you standing in place where you've started to use your ability. After a few seconds, the bomb explodes, killing everyone nearby, and you teleport back to where you started carrying the bomb.\\nKill everyone to win!", + "Explosivist.ExplosionDelay": "Explosion Delay", + "Explosivist.ExplosionRadius": "Explosion Radius", + "ExplosivistSuffix": "Carrying the bomb\\n<#ffff00>Explodes in {0}s", "BomberCooldownsResetEachOther": "Kill Cooldown & Bomb Cooldown Reset Each Other When Used", "BomberDiesInExplosion": "Bomber dies in their explosion", "ImpostorsSurviveBombs": "Impostors survive bombs", diff --git a/Roles/AddOns/Crewmate/Examiner.cs b/Roles/AddOns/Crewmate/Examiner.cs new file mode 100644 index 000000000..3cf4e3015 --- /dev/null +++ b/Roles/AddOns/Crewmate/Examiner.cs @@ -0,0 +1,17 @@ +namespace EHR.AddOns.Crewmate; + +public class Examiner : IAddon +{ + public AddonTypes Type => AddonTypes.Helpful; + + public static OptionItem ExaminerSuspectLimit; + + public void SetupCustomOption() + { + Options.SetupAdtRoleOptions(654400, CustomRoles.Examiner, canSetNum: true); + + ExaminerSuspectLimit = new FloatOptionItem(654410, "DetectiveSuspectLimit", new(1f, 30f, 1f), 4f, TabGroup.Addons) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.DetectiveEHR]) + .SetValueFormat(OptionFormat.Players); + } +} \ No newline at end of file diff --git a/Roles/AddOns/Crewmate/Workhorse.cs b/Roles/AddOns/Crewmate/Workhorse.cs index 56c4732fe..a893d282f 100644 --- a/Roles/AddOns/Crewmate/Workhorse.cs +++ b/Roles/AddOns/Crewmate/Workhorse.cs @@ -68,7 +68,7 @@ public class Workhorse : IAddon { if (!pc.IsAlive() || IsThisRole(pc.PlayerId)) return false; if (pc.Is(CustomRoles.Needy) || pc.Is(CustomRoles.Lazy) || pc.Is(CustomRoles.Bloodlust)) return false; - if (pc.GetCustomRole().GetVNRole(true) is CustomRoles.Impostor or CustomRoles.ImpostorEHR or CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR or CustomRoles.Phantom or CustomRoles.PhantomEHR) return false; + if (pc.GetCustomRole().GetVNRole(true) is CustomRoles.Impostor or CustomRoles.ImpostorEHR or CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR or CustomRoles.Phantom or CustomRoles.PhantomEHR or CustomRoles.Viper or CustomRoles.ViperEHR) return false; TaskState taskState = pc.GetTaskState(); if (taskState.CompletedTasksCount + 1 < taskState.AllTasksCount) return false; diff --git a/Roles/AddOns/Impostor/Venom.cs b/Roles/AddOns/Impostor/Venom.cs new file mode 100644 index 000000000..a6a5eba10 --- /dev/null +++ b/Roles/AddOns/Impostor/Venom.cs @@ -0,0 +1,17 @@ +namespace EHR.AddOns.Impostor; + +public class Venom : IAddon +{ + public AddonTypes Type => AddonTypes.Helpful; + + public static OptionItem VenomDissolveTime; + + public void SetupCustomOption() + { + Options.SetupAdtRoleOptions(654500, CustomRoles.Venom, canSetNum: true); + + VenomDissolveTime = new FloatOptionItem(654510, "ViperDissolveTime", new(0f, 60f, 0.5f), 10f, TabGroup.Addons) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.ViperEHR]) + .SetValueFormat(OptionFormat.Seconds); + } +} \ No newline at end of file diff --git a/Roles/Crewmate/Analyst.cs b/Roles/Crewmate/Analyst.cs index e4cfc1694..17765dfa7 100644 --- a/Roles/Crewmate/Analyst.cs +++ b/Roles/Crewmate/Analyst.cs @@ -76,11 +76,13 @@ internal class Analyst : RoleBase CustomRoles.Impostor or CustomRoles.ImpostorEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Impostor), GetString("Impostor")), CustomRoles.Shapeshifter or CustomRoles.ShapeshifterEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Speedrunner), GetString("Shapeshifter")), CustomRoles.Phantom or CustomRoles.PhantomEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Witness), GetString("Phantom")), + CustomRoles.Viper or CustomRoles.ViperEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Poisoner), GetString("Viper")), CustomRoles.Crewmate or CustomRoles.CrewmateEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Crewmate), GetString("Crewmate")), CustomRoles.Engineer or CustomRoles.EngineerEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Autocrat), GetString("Engineer")), CustomRoles.Scientist or CustomRoles.ScientistEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Doctor), GetString("Scientist")), CustomRoles.Noisemaker or CustomRoles.NoisemakerEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Bubble), GetString("Noisemaker")), CustomRoles.Tracker or CustomRoles.TrackerEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Scout), GetString("Tracker")), + CustomRoles.Detective or CustomRoles.DetectiveEHR => Utils.ColorString(Utils.GetRoleColor(CustomRoles.Forensic), GetString("Detective")), _ => string.Empty } : string.Empty; diff --git a/Roles/Crewmate/CrewmateVanillaRoles.cs b/Roles/Crewmate/CrewmateVanillaRoles.cs index 2aeec10e5..05a72cf76 100644 --- a/Roles/Crewmate/CrewmateVanillaRoles.cs +++ b/Roles/Crewmate/CrewmateVanillaRoles.cs @@ -14,6 +14,7 @@ internal class CrewmateVanillaRoles : IVanillaSettingHolder public static OptionItem TrackerCooldown; public static OptionItem TrackerDuration; public static OptionItem TrackerDelay; + public static OptionItem DetectiveSuspectLimit; public TabGroup Tab => TabGroup.CrewmateRoles; public void SetupCustomOption() @@ -65,5 +66,11 @@ internal class CrewmateVanillaRoles : IVanillaSettingHolder TrackerDelay = new FloatOptionItem(5064, "TrackerDelay", new(1f, 250f, 1f), 5f, Tab) .SetParent(CustomRoleSpawnChances[CustomRoles.TrackerEHR]) .SetValueFormat(OptionFormat.Seconds); + + SetupRoleOptions(5080, Tab, CustomRoles.DetectiveEHR); + + DetectiveSuspectLimit = new FloatOptionItem(5082, "DetectiveSuspectLimit", new(1f, 30f, 1f), 4f, Tab) + .SetParent(CustomRoleSpawnChances[CustomRoles.DetectiveEHR]) + .SetValueFormat(OptionFormat.Players); } } \ No newline at end of file diff --git a/Roles/Crewmate/Detective.cs b/Roles/Crewmate/Detective.cs deleted file mode 100644 index 64e100236..000000000 --- a/Roles/Crewmate/Detective.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace EHR.Crewmate; - -internal class Detective : RoleBase -{ - public static OptionItem DetectiveCanknowKiller; - public static OptionItem DetectiveCanknowDeathReason; - public static OptionItem DetectiveCanknowAddons; - public static OptionItem DetectiveCanknowKillTime; - - public static Dictionary DetectiveNotify = []; - - public override bool IsEnable => false; - - public override void SetupCustomOption() - { - Options.SetupRoleOptions(6600, TabGroup.CrewmateRoles, CustomRoles.Detective); - - DetectiveCanknowKiller = new BooleanOptionItem(6610, "DetectiveCanknowKiller", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Detective]); - - DetectiveCanknowDeathReason = new BooleanOptionItem(6611, "DetectiveCanknowDeathReason", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Detective]); - - DetectiveCanknowAddons = new BooleanOptionItem(6612, "DetectiveCanknowAddons", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Detective]); - - DetectiveCanknowKillTime = new BooleanOptionItem(6613, "DetectiveCanknowKillTime", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Detective]); - } - - public override void Init() { } - - public override void Add(byte playerId) { } - - public static void OnReportDeadBody(PlayerControl player, PlayerControl tpc) - { - string msg = string.Format(Translator.GetString("DetectiveNoticeVictim"), tpc.GetRealName(), tpc.GetCustomRole().ToColoredString()); - - if (DetectiveCanknowKiller.GetBool()) - { - PlayerControl realKiller = tpc.GetRealKiller(); - - if (realKiller == null) - msg += ";" + Translator.GetString("DetectiveNoticeKillerNotFound"); - else - msg += ";" + string.Format(Translator.GetString("DetectiveNoticeKiller"), realKiller.GetCustomRole().ToColoredString()); - } - - if (DetectiveCanknowDeathReason.GetBool()) msg += ";" + string.Format(Translator.GetString("DetectiveNoticeDeathReason"), Translator.GetString($"DeathReason.{Main.PlayerStates[tpc.PlayerId].deathReason}")); - - if (DetectiveCanknowAddons.GetBool()) msg += ";" + string.Format(Translator.GetString("DetectiveNoticeAddons"), string.Join(", ", tpc.GetCustomSubRoles().Select(x => x.ToColoredString()))); - - if (DetectiveCanknowKillTime.GetBool()) - { - DateTime deathTimeStamp = Main.PlayerStates[tpc.PlayerId].RealKiller.TimeStamp; - DateTime now = DateTime.Now; - double timeSpanSeconds = (now - deathTimeStamp).TotalSeconds; - msg += ";" + string.Format(Translator.GetString("DetectiveNoticeKillTime"), (int)timeSpanSeconds); - } - - DetectiveNotify[player.PlayerId] = msg; - } -} \ No newline at end of file diff --git a/Roles/Crewmate/Farseer.cs b/Roles/Crewmate/Farseer.cs index 1ec5d844e..060b539e0 100644 --- a/Roles/Crewmate/Farseer.cs +++ b/Roles/Crewmate/Farseer.cs @@ -128,7 +128,7 @@ public class Farseer : RoleBase } else { - float range = NormalGameOptionsV09.KillDistances[Mathf.Clamp(player.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f; + float range = NormalGameOptionsV10.KillDistances[Mathf.Clamp(player.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f; float dis = Vector2.Distance(player.Pos(), arTarget.Pos()); if (dis <= range) diff --git a/Roles/Crewmate/Forensic.cs b/Roles/Crewmate/Forensic.cs new file mode 100644 index 000000000..fe527aa19 --- /dev/null +++ b/Roles/Crewmate/Forensic.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace EHR.Crewmate; + +internal class Forensic : RoleBase +{ + public static OptionItem ForensicCanknowKiller; + public static OptionItem ForensicCanknowDeathReason; + public static OptionItem ForensicCanknowAddons; + public static OptionItem ForensicCanknowKillTime; + + public static Dictionary ForensicNotify = []; + + public override bool IsEnable => false; + + public override void SetupCustomOption() + { + Options.SetupRoleOptions(6600, TabGroup.CrewmateRoles, CustomRoles.Forensic); + + ForensicCanknowKiller = new BooleanOptionItem(6610, "ForensicCanknowKiller", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Forensic]); + + ForensicCanknowDeathReason = new BooleanOptionItem(6611, "ForensicCanknowDeathReason", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Forensic]); + + ForensicCanknowAddons = new BooleanOptionItem(6612, "ForensicCanknowAddons", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Forensic]); + + ForensicCanknowKillTime = new BooleanOptionItem(6613, "ForensicCanknowKillTime", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Forensic]); + } + + public override void Init() { } + + public override void Add(byte playerId) { } + + public static void OnReportDeadBody(PlayerControl player, PlayerControl tpc) + { + string msg = string.Format(Translator.GetString("ForensicNoticeVictim"), tpc.GetRealName(), tpc.GetCustomRole().ToColoredString()); + + if (ForensicCanknowKiller.GetBool()) + { + PlayerControl realKiller = tpc.GetRealKiller(); + + if (realKiller == null) + msg += ";" + Translator.GetString("ForensicNoticeKillerNotFound"); + else + msg += ";" + string.Format(Translator.GetString("ForensicNoticeKiller"), realKiller.GetCustomRole().ToColoredString()); + } + + if (ForensicCanknowDeathReason.GetBool()) msg += ";" + string.Format(Translator.GetString("ForensicNoticeDeathReason"), Translator.GetString($"DeathReason.{Main.PlayerStates[tpc.PlayerId].deathReason}")); + + if (ForensicCanknowAddons.GetBool()) msg += ";" + string.Format(Translator.GetString("ForensicNoticeAddons"), string.Join(", ", tpc.GetCustomSubRoles().Select(x => x.ToColoredString()))); + + if (ForensicCanknowKillTime.GetBool()) + { + DateTime deathTimeStamp = Main.PlayerStates[tpc.PlayerId].RealKiller.TimeStamp; + DateTime now = DateTime.Now; + double timeSpanSeconds = (now - deathTimeStamp).TotalSeconds; + msg += ";" + string.Format(Translator.GetString("ForensicNoticeKillTime"), (int)timeSpanSeconds); + } + + ForensicNotify[player.PlayerId] = msg; + } +} \ No newline at end of file diff --git a/Roles/Crewmate/Scout.cs b/Roles/Crewmate/Scout.cs index c10402780..d311922dd 100644 --- a/Roles/Crewmate/Scout.cs +++ b/Roles/Crewmate/Scout.cs @@ -18,7 +18,7 @@ public class Scout : RoleBase private static OptionItem OptionCanSeeLastRoomInMeeting; private static OptionItem CanGetColoredArrow; public static OptionItem HideVote; - public static OptionItem TrackerAbilityUseGainWithEachTaskCompleted; + public static OptionItem ScoutAbilityUseGainWithEachTaskCompleted; public static OptionItem AbilityChargesWhenFinishedTasks; public static OptionItem CancelVote; @@ -48,7 +48,7 @@ public class Scout : RoleBase HideVote = new BooleanOptionItem(Id + 8, "TrackerHideVote", false, TabGroup.CrewmateRoles) .SetParent(CustomRoleSpawnChances[CustomRoles.Scout]); - TrackerAbilityUseGainWithEachTaskCompleted = new FloatOptionItem(Id + 9, "AbilityUseGainWithEachTaskCompleted", new(0f, 5f, 0.05f), 1f, TabGroup.CrewmateRoles) + ScoutAbilityUseGainWithEachTaskCompleted = new FloatOptionItem(Id + 9, "AbilityUseGainWithEachTaskCompleted", new(0f, 5f, 0.05f), 1f, TabGroup.CrewmateRoles) .SetParent(CustomRoleSpawnChances[CustomRoles.Scout]) .SetValueFormat(OptionFormat.Times); diff --git a/Roles/Impostor/Deathpact.cs b/Roles/Impostor/Deathpact.cs index 3557de6fe..caf730ce7 100644 --- a/Roles/Impostor/Deathpact.cs +++ b/Roles/Impostor/Deathpact.cs @@ -191,7 +191,7 @@ public class Deathpact : RoleBase foreach (PlayerControl player in dp.PlayersInDeathpact) { - float range = NormalGameOptionsV09.KillDistances[Mathf.Clamp(player.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f; + float range = NormalGameOptionsV10.KillDistances[Mathf.Clamp(player.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f; cancelDeathpact = dp.PlayersInDeathpact.Where(a => a.PlayerId != player.PlayerId).Select(otherPlayerInPact => Vector2.Distance(player.Pos(), otherPlayerInPact.Pos())).Aggregate(cancelDeathpact, (current, dis) => current && dis <= range); } diff --git a/Roles/Impostor/ImpostorVanillaRoles.cs b/Roles/Impostor/ImpostorVanillaRoles.cs index 03a18e4fe..4444d1fbf 100644 --- a/Roles/Impostor/ImpostorVanillaRoles.cs +++ b/Roles/Impostor/ImpostorVanillaRoles.cs @@ -8,6 +8,7 @@ internal class ImpostorVanillaRoles : IVanillaSettingHolder public static OptionItem PhantomDuration; public static OptionItem ShapeshiftCD; public static OptionItem ShapeshiftDur; + public static OptionItem ViperDissolveTime; public TabGroup Tab => TabGroup.ImpostorRoles; public void SetupCustomOption() @@ -32,5 +33,11 @@ internal class ImpostorVanillaRoles : IVanillaSettingHolder ShapeshiftDur = new FloatOptionItem(403, "ShapeshiftDuration", new(1f, 60f, 1f), 10f, Tab) .SetParent(CustomRoleSpawnChances[CustomRoles.ShapeshifterEHR]) .SetValueFormat(OptionFormat.Seconds); + + SetupRoleOptions(410, Tab, CustomRoles.ViperEHR); + + ViperDissolveTime = new FloatOptionItem(412, "ViperDissolveTime", new(0f, 60f, 0.5f), 10f, Tab) + .SetParent(CustomRoleSpawnChances[CustomRoles.ViperEHR]) + .SetValueFormat(OptionFormat.Seconds); } } \ No newline at end of file diff --git a/Roles/Impostor/Psychopath.cs b/Roles/Impostor/Psychopath.cs index b921e0766..5562bbd0b 100644 --- a/Roles/Impostor/Psychopath.cs +++ b/Roles/Impostor/Psychopath.cs @@ -35,7 +35,7 @@ public class Psychopath : RoleBase { if (!GameStates.IsInTask || ExileController.Instance || AntiBlackout.SkipTasks || Main.KillTimers[pc.PlayerId] > 0f || Count++ < 10) return; var pos = pc.Pos(); - var killRange = NormalGameOptionsV09.KillDistances[Mathf.Clamp(Main.NormalOptions.KillDistance, 0, 2)]; + var killRange = NormalGameOptionsV10.KillDistances[Mathf.Clamp(Main.NormalOptions.KillDistance, 0, 2)]; var closestPlayer = Main.AllAlivePlayerControls.Select(x => (pc: x, distance: Vector2.Distance(x.Pos(), pos))).Where(x => x.distance <= killRange).MinBy(x => x.distance).pc; if (closestPlayer == null) return; Count = 0; diff --git a/Roles/Impostor/Puppeteer.cs b/Roles/Impostor/Puppeteer.cs index 9fb73cd5c..dae374e48 100644 --- a/Roles/Impostor/Puppeteer.cs +++ b/Roles/Impostor/Puppeteer.cs @@ -202,7 +202,7 @@ internal class Puppeteer : RoleBase { KeyValuePair min = targetDistance.OrderBy(c => c.Value).FirstOrDefault(); PlayerControl target = Utils.GetPlayerById(min.Key); - float killRange = NormalGameOptionsV09.KillDistances[Mathf.Clamp(Main.NormalOptions.KillDistance, 0, 2)]; + float killRange = NormalGameOptionsV10.KillDistances[Mathf.Clamp(Main.NormalOptions.KillDistance, 0, 2)]; if (min.Value <= killRange && player.CanMove && target.CanMove) { diff --git a/Roles/Neutral/Agitater.cs b/Roles/Neutral/Agitater.cs index 5884da65d..96cb842a3 100644 --- a/Roles/Neutral/Agitater.cs +++ b/Roles/Neutral/Agitater.cs @@ -199,7 +199,7 @@ public class Agitater : RoleBase { KeyValuePair min = targetDistance.OrderBy(c => c.Value).FirstOrDefault(); PlayerControl target = Utils.GetPlayerById(min.Key); - float KillRange = NormalGameOptionsV09.KillDistances[Mathf.Clamp(GameOptionsManager.Instance.currentNormalGameOptions.KillDistance, 0, 2)]; + float KillRange = NormalGameOptionsV10.KillDistances[Mathf.Clamp(GameOptionsManager.Instance.currentNormalGameOptions.KillDistance, 0, 2)]; if (min.Value <= KillRange && player.CanMove && target.CanMove) PassBomb(player, target); } } diff --git a/Roles/Neutral/Arsonist.cs b/Roles/Neutral/Arsonist.cs index 6e3bf7b8b..f31f1a45b 100644 --- a/Roles/Neutral/Arsonist.cs +++ b/Roles/Neutral/Arsonist.cs @@ -201,7 +201,7 @@ internal class Arsonist : RoleBase } else { - float range = NormalGameOptionsV09.KillDistances[Mathf.Clamp(player.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f; + float range = NormalGameOptionsV10.KillDistances[Mathf.Clamp(player.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f; float dis = Vector2.Distance(player.Pos(), arTarget.Pos()); if (dis <= range) diff --git a/Roles/Neutral/Explosivist.cs b/Roles/Neutral/Explosivist.cs new file mode 100644 index 000000000..c51c1dae5 --- /dev/null +++ b/Roles/Neutral/Explosivist.cs @@ -0,0 +1,166 @@ +using System.Linq; +using AmongUs.GameOptions; +using Hazel; + +namespace EHR.Neutral; + +public class Explosivist : RoleBase +{ + public static bool On; + + public static OptionItem AbilityCooldown; + private static OptionItem ImpostorVision; + public static OptionItem ExplosionDelay; + private static OptionItem ExplosionRadius; + + public override bool IsEnable => On; + + private TNT Explosive; + private Vector2 RealPosition; + private long ExplodeTS; + private byte ExplosivistId; + + public override void SetupCustomOption() + { + StartSetup(654300) + .AutoSetupOption(ref AbilityCooldown, 22.5f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds) + .AutoSetupOption(ref ImpostorVision, true) + .AutoSetupOption(ref ExplosionDelay, 10, new IntegerValueRule(1, 60, 1), OptionFormat.Seconds) + .AutoSetupOption(ref ExplosionRadius, 4f, new FloatValueRule(0.25f, 10f, 0.25f), OptionFormat.Multiplier); + } + + public override void Init() + { + On = false; + } + + public override void Add(byte playerId) + { + On = true; + Explosive = null; + RealPosition = Vector2.zero; + ExplodeTS = 0; + ExplosivistId = playerId; + } + + public override void SetKillCooldown(byte id) + { + Main.AllPlayerKillCooldown[id] = AbilityCooldown.GetFloat(); + } + + public override void ApplyGameOptions(IGameOptions opt, byte id) + { + opt.SetVision(ImpostorVision.GetBool()); + + if (Options.UsePhantomBasis.GetBool() && Options.UsePhantomBasisForNKs.GetBool()) + { + AURoleOptions.PhantomCooldown = AbilityCooldown.GetFloat(); + AURoleOptions.PhantomDuration = 1f; + } + } + + public override bool CanUseKillButton(PlayerControl pc) + { + return false; + } + + public override bool OnVanish(PlayerControl pc) + { + UseAbility(pc); + return false; + } + + public override void OnPet(PlayerControl pc) + { + UseAbility(pc); + } + + void UseAbility(PlayerControl player) + { + if (Explosive != null) return; + + Vector2 pos = player.Pos(); + Explosive = new TNT(pos); + RealPosition = pos; + + // From https://github.com/Rabek009/MoreGamemodes/blob/master/Roles/Impostor/Concealing/Droner.cs + foreach (var pc in PlayerControl.AllPlayerControls) + { + if (pc == player || pc.AmOwner) continue; + CustomRpcSender sender = CustomRpcSender.Create("Explosivist", SendOption.Reliable); + sender.StartMessage(pc.GetClientId()); + sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo) + .WriteVector2(player.transform.position) + .Write(player.NetTransform.lastSequenceId) + .EndRpc(); + sender.StartRpc(player.NetTransform.NetId, (byte)RpcCalls.SnapTo) + .WriteVector2(player.transform.position) + .Write((ushort)(player.NetTransform.lastSequenceId + 16383)) + .EndRpc(); + sender.EndMessage(); + sender.SendMessage(); + Utils.NumSnapToCallsThisRound += 2; + } + + if (player.AmOwner) player.Visible = false; + + ExplodeTS = Utils.TimeStamp + ExplosionDelay.GetInt(); + } + + public override void OnFixedUpdate(PlayerControl pc) + { + if (!GameStates.IsInTask || ExileController.Instance || AntiBlackout.SkipTasks) return; + + Explosive?.TP(pc.transform.position); + + if (Explosive != null && ExplodeTS >= Utils.TimeStamp) + { + Utils.GetPlayersInRadius(ExplosionRadius.GetFloat(), Explosive.Position).Without(pc).Do(x => x.Suicide(PlayerState.DeathReason.Bombed, pc)); + + RevertFreeze(pc); + if (!Options.UsePets.GetBool()) pc.RpcResetAbilityCooldown(); + + Explosive.Despawn(); + Explosive = null; + RealPosition = Vector2.zero; + ExplodeTS = 0; + } + } + + public override void OnReportDeadBody() + { + if (Explosive != null) + { + PlayerControl pc = ExplosivistId.GetPlayer(); + RevertFreeze(pc); + } + } + + private void RevertFreeze(PlayerControl pc) + { + if (pc.AmOwner) pc.Visible = true; + pc.NetTransform.SnapTo(RealPosition, (ushort)(pc.NetTransform.lastSequenceId + 128)); + CustomRpcSender sender = CustomRpcSender.Create("Explosivist Revert", SendOption.Reliable); + sender.StartMessage(); + sender.StartRpc(pc.NetTransform.NetId, (byte)RpcCalls.SnapTo) + .WriteVector2(pc.transform.position) + .Write((ushort)(pc.NetTransform.lastSequenceId + 32767)) + .EndRpc(); + sender.StartRpc(pc.NetTransform.NetId, (byte)RpcCalls.SnapTo) + .WriteVector2(pc.transform.position) + .Write((ushort)(pc.NetTransform.lastSequenceId + 32767 + 16383)) + .EndRpc(); + sender.StartRpc(pc.NetTransform.NetId, (byte)RpcCalls.SnapTo) + .WriteVector2(pc.transform.position) + .Write(pc.NetTransform.lastSequenceId) + .EndRpc(); + sender.EndMessage(); + sender.SendMessage(); + } + + public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false) + { + if (seer.PlayerId != ExplosivistId || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || meeting || ExplodeTS == 0) return string.Empty; + return string.Format(Translator.GetString("ExplosivistSuffix"), ExplodeTS - Utils.TimeStamp); + } +} \ No newline at end of file diff --git a/Roles/Neutral/Revolutionist.cs b/Roles/Neutral/Revolutionist.cs index 05e36a915..2aa2f6979 100644 --- a/Roles/Neutral/Revolutionist.cs +++ b/Roles/Neutral/Revolutionist.cs @@ -176,7 +176,7 @@ internal class Revolutionist : RoleBase } else { - float range = NormalGameOptionsV09.KillDistances[Mathf.Clamp(player.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f; + float range = NormalGameOptionsV10.KillDistances[Mathf.Clamp(player.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f; float dis = Vector2.Distance(player.Pos(), rvTarget.Pos()); if (dis <= range) diff --git a/Roles/Neutral/Samurai.cs b/Roles/Neutral/Samurai.cs index e3ff0ab25..942dabac8 100644 --- a/Roles/Neutral/Samurai.cs +++ b/Roles/Neutral/Samurai.cs @@ -114,7 +114,7 @@ internal class Samurai : RoleBase PlayerControl target = Utils.GetPlayerById(Target.Id); if (target == null) return; - if (Vector2.Distance(target.Pos(), pc.Pos()) > NormalGameOptionsV09.KillDistances[Mathf.Clamp(pc.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f) + if (Vector2.Distance(target.Pos(), pc.Pos()) > NormalGameOptionsV10.KillDistances[Mathf.Clamp(pc.Is(CustomRoles.Reach) ? 2 : Main.NormalOptions.KillDistance, 0, 2)] + 0.5f) { Target = (byte.MaxValue, 0); pc.RpcCheckAndMurder(target);