From 6397b9846bf4bf98e1cfe4aa10605dc1f3167841 Mon Sep 17 00:00:00 2001 From: HyperAtill Date: Tue, 23 Sep 2025 17:00:34 +0300 Subject: [PATCH] ban-and-rename --- CustomRoles.cs | 2 +- Main.cs | 2 +- Modules/CustomRolesHelper.cs | 8 +- Modules/GameState.cs | 6 +- Modules/RPC.cs | 6 +- Patches/IntroPatch.cs | 2 +- Patches/PlayerControlPatch.cs | 4 +- Patches/ShipStatusPatch.cs | 10 +- Resources/Config/EACList.txt | 1 + Resources/Lang/en_US.json | 26 +- .../{SabotageMaster.cs => Mechanic.cs} | 672 +++++++++--------- Roles/Crewmate/President.cs | 2 +- Roles/Impostor/Nullifier.cs | 8 +- 13 files changed, 375 insertions(+), 374 deletions(-) rename Roles/Crewmate/{SabotageMaster.cs => Mechanic.cs} (72%) diff --git a/CustomRoles.cs b/CustomRoles.cs index ac3f3f5ca..d72134182 100644 --- a/CustomRoles.cs +++ b/CustomRoles.cs @@ -218,7 +218,7 @@ public enum CustomRoles Marshall, Mathematician, Mayor, - SabotageMaster, // Mechanic + Mechanic, Medic, Mediumshiper, Merchant, diff --git a/Main.cs b/Main.cs index 0e3743d8f..85d97af6f 100644 --- a/Main.cs +++ b/Main.cs @@ -371,7 +371,7 @@ public class Main : BasePlugin { CustomRoles.DoubleAgent, "#ff1919" }, { CustomRoles.Luckey, "#b8d7a3" }, { CustomRoles.Needy, "#a4dffe" }, - { CustomRoles.SabotageMaster, "#3333ff" }, + { CustomRoles.Mechanic, "#3333ff" }, { CustomRoles.Snitch, "#b8fb4f" }, { CustomRoles.Marshall, "#5573aa" }, { CustomRoles.Mayor, "#204d42" }, diff --git a/Modules/CustomRolesHelper.cs b/Modules/CustomRolesHelper.cs index cc0ecb0d2..bab7cce05 100644 --- a/Modules/CustomRolesHelper.cs +++ b/Modules/CustomRolesHelper.cs @@ -124,7 +124,7 @@ internal static class CustomRolesHelper CustomRoles.Snitch => CustomRoles.Crewmate, CustomRoles.ParityCop => CustomRoles.Crewmate, CustomRoles.Marshall => CustomRoles.Crewmate, - CustomRoles.SabotageMaster => SabotageMaster.CanVent.GetBool() ? CustomRoles.Engineer : CustomRoles.Crewmate, + CustomRoles.Mechanic => Mechanic.CanVent.GetBool() ? CustomRoles.Engineer : CustomRoles.Crewmate, CustomRoles.Mafia => Options.LegacyMafia.GetBool() ? CustomRoles.Shapeshifter : CustomRoles.Impostor, CustomRoles.Terrorist => CustomRoles.Engineer, CustomRoles.Executioner => CustomRoles.Crewmate, @@ -1096,7 +1096,7 @@ internal static class CustomRolesHelper CustomRoles.BananaMan when pc.Is(CustomRoles.Disco) || pc.Is(CustomRoles.Doppelganger) => false, CustomRoles.Disco when pc.GetCustomRole() is CustomRoles.Chameleon or CustomRoles.Swooper or CustomRoles.Wraith or CustomRoles.Alchemist or CustomRoles.Doppelganger => false, CustomRoles.Egoist when pc.Is(CustomRoles.Gangster) => false, - CustomRoles.Nimble when pc.GetCustomRole() is CustomRoles.Oxyman or CustomRoles.EngineerEHR or CustomRoles.SabotageMaster => false, + CustomRoles.Nimble when pc.GetCustomRole() is CustomRoles.Oxyman or CustomRoles.EngineerEHR or CustomRoles.Mechanic => false, CustomRoles.Magnet when pc.Is(Team.Impostor) => false, CustomRoles.Swift when pc.Is(CustomRoles.Magnet) => false, CustomRoles.Oblivious when pc.Is(CustomRoles.Amnesiac) && Amnesiac.RememberMode.GetValue() == 0 => false, @@ -1194,7 +1194,7 @@ internal static class CustomRolesHelper CustomRoles.Unlucky when pc.Is(CustomRoles.Luckey) => false, CustomRoles.Unlucky when pc.Is(CustomRoles.Lucky) => false, CustomRoles.Lucky when pc.Is(CustomRoles.Unlucky) => false, - CustomRoles.Fool when pc.GetCustomRole() is CustomRoles.SabotageMaster or CustomRoles.GuardianAngelEHR or CustomRoles.Technician => false, + CustomRoles.Fool when pc.GetCustomRole() is CustomRoles.Mechanic or CustomRoles.GuardianAngelEHR or CustomRoles.Technician => false, CustomRoles.Bloodhound when pc.Is(CustomRoles.Oblivious) => false, CustomRoles.DoubleShot when pc.GetCustomRole() is not CustomRoles.EvilGuesser and not CustomRoles.NiceGuesser and not CustomRoles.Augur && !Options.GuesserMode.GetBool() => false, CustomRoles.DoubleShot when !pc.Is(CustomRoles.Guesser) && !pc.Is(CustomRoles.EvilGuesser) && pc.Is(CustomRoleTypes.Impostor) && !Options.ImpostorsCanGuess.GetBool() => false, @@ -1807,7 +1807,7 @@ internal static class CustomRolesHelper CustomRoles.Helper => RoleOptionType.Crewmate_Support, CustomRoles.Jailor => RoleOptionType.Crewmate_Support, CustomRoles.Mathematician => RoleOptionType.Crewmate_Support, - CustomRoles.SabotageMaster => RoleOptionType.Crewmate_Support, + CustomRoles.Mechanic => RoleOptionType.Crewmate_Support, CustomRoles.Medic => RoleOptionType.Crewmate_Support, CustomRoles.Merchant => RoleOptionType.Crewmate_Support, CustomRoles.Monarch => RoleOptionType.Crewmate_Support, diff --git a/Modules/GameState.cs b/Modules/GameState.cs index 874f4c83f..e25e08cc4 100644 --- a/Modules/GameState.cs +++ b/Modules/GameState.cs @@ -474,10 +474,10 @@ public class TaskState { switch (player.GetCustomRole()) { - case CustomRoles.SabotageMaster: - if (Main.PlayerStates[player.PlayerId].Role is not SabotageMaster sm) break; + case CustomRoles.Mechanic: + if (Main.PlayerStates[player.PlayerId].Role is not Mechanic sm) break; - sm.UsedSkillCount -= SabotageMaster.SmAbilityUseGainWithEachTaskCompleted.GetFloat(); + sm.UsedSkillCount -= Mechanic.MechanicAbilityUseGainWithEachTaskCompleted.GetFloat(); sm.SendRPC(); break; case CustomRoles.Hacker: diff --git a/Modules/RPC.cs b/Modules/RPC.cs index 4fc9dfd5d..cc3d9e73d 100644 --- a/Modules/RPC.cs +++ b/Modules/RPC.cs @@ -102,7 +102,7 @@ public enum CustomRPC SetWeaponMasterMode, SyncGlitchTimers, SyncSpy, - SetSabotageMasterLimit, + SetMechanicLimit, SetHackerLimit, SetCurrentDrawTarget, SetCpTasksDone, @@ -875,9 +875,9 @@ internal static class RPCHandlerPatch Druid.ReceiveRPCAddTrigger(reader); break; } - case CustomRPC.SetSabotageMasterLimit: + case CustomRPC.SetMechanicLimit: { - SabotageMaster.ReceiveRPC(reader); + Mechanic.ReceiveRPC(reader); break; } case CustomRPC.SetHackerLimit: diff --git a/Patches/IntroPatch.cs b/Patches/IntroPatch.cs index 73c5102e2..194dd38ea 100644 --- a/Patches/IntroPatch.cs +++ b/Patches/IntroPatch.cs @@ -634,7 +634,7 @@ internal static class BeginCrewmatePatch => FastDestroyableSingleton.Instance.TaskUpdateSound, CustomRoles.Inhibitor or - CustomRoles.SabotageMaster or + CustomRoles.Mechanic or CustomRoles.Saboteur or CustomRoles.SecurityGuard or CustomRoles.Provocateur diff --git a/Patches/PlayerControlPatch.cs b/Patches/PlayerControlPatch.cs index 5a2d94f03..bdd2bf5a1 100644 --- a/Patches/PlayerControlPatch.cs +++ b/Patches/PlayerControlPatch.cs @@ -2072,9 +2072,9 @@ internal static class FixedUpdatePatch { if (Main.HasJustStarted || !player.IsAlive()) return; - if (Main.PlayerStates[player.PlayerId].Role is SabotageMaster sm) + if (Main.PlayerStates[player.PlayerId].Role is Mechanic sm) { - sm.UsedSkillCount -= SabotageMaster.AbilityChargesWhenFinishedTasks.GetFloat(); + sm.UsedSkillCount -= Mechanic.AbilityChargesWhenFinishedTasks.GetFloat(); sm.SendRPC(); } else diff --git a/Patches/ShipStatusPatch.cs b/Patches/ShipStatusPatch.cs index 497b03810..b5f41d4fb 100644 --- a/Patches/ShipStatusPatch.cs +++ b/Patches/ShipStatusPatch.cs @@ -98,8 +98,8 @@ internal static class RepairSystemPatch switch (player.GetCustomRole()) { - case CustomRoles.SabotageMaster: - SabotageMaster.RepairSystem(player.PlayerId, systemType, amount); + case CustomRoles.Mechanic: + Mechanic.RepairSystem(player.PlayerId, systemType, amount); Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player); break; case CustomRoles.Alchemist when systemType != SystemTypes.Electrical && Main.PlayerStates[player.PlayerId].Role is Alchemist { IsEnable: true, FixNextSabo: true }: @@ -134,10 +134,10 @@ internal static class RepairSystemPatch { switch (Main.PlayerStates[player.PlayerId].Role) { - case SabotageMaster: + case Mechanic: { - Logger.Info($"{player.GetNameWithRole().RemoveHtmlTags()} instant-fix-lights", "SabotageMaster"); - SabotageMaster.SwitchSystemRepair(player.PlayerId, switchSystem, amount); + Logger.Info($"{player.GetNameWithRole().RemoveHtmlTags()} instant-fix-lights", "Mechanic"); + Mechanic.SwitchSystemRepair(player.PlayerId, switchSystem, amount); Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player); break; } diff --git a/Resources/Config/EACList.txt b/Resources/Config/EACList.txt index e8d41a9e2..2987c6be7 100644 --- a/Resources/Config/EACList.txt +++ b/Resources/Config/EACList.txt @@ -125,6 +125,7 @@ tidytrice#2024,Alisa?(either this or above one) Foamcrisp#5124, e5be77e02, Barbie (aka princess, broadbarn alt) rosefiscal#4338, Bagelz (suspected alt of broadbarn or tidytrice) driedbreak#2568, 2f9986d9f, ASHHHOLE (toxic) +markedfee#5991, 10e7b3318, (name-not-known)(toxic) ## Racist/Homophobic onwardhome#3591, MentoMan diff --git a/Resources/Lang/en_US.json b/Resources/Lang/en_US.json index 3c9b85eaf..c97bd2518 100644 --- a/Resources/Lang/en_US.json +++ b/Resources/Lang/en_US.json @@ -245,7 +245,7 @@ "Mayor": "Mayor", "Paranoia": "Paranoid", "Psychic": "Psychic", - "SabotageMaster": "Mechanic", + "Mechanic": "Mechanic", "Sheriff": "Sheriff", "CopyCat": "Copycat", "Snitch": "Snitch", @@ -524,7 +524,7 @@ "MayorInfo": "Your vote counts multiple times", "ParanoiaInfo": "You have a portable meeting button", "PsychicInfo": "One of the red names is evil", - "SabotageMasterInfo": "Vent and fix sabotages quickly", + "MechanicInfo": "Vent and fix sabotages quickly", "SheriffInfo": "Kill evil players", "CopyCatInfo": "Use kill button to copy target's role", "SnitchInfo": "Finish your tasks to find the Impostors", @@ -754,7 +754,7 @@ "MayorInfoLong": "(Crewmates):\\nMayor's vote counts as multiple votes and may have the ability to trigger an emergency meeting.", "ParanoiaInfoLong": "(Crewmates):\\nParanoid can call a meeting by venting/petting. The report animation will show a dead body with Paranoid's color, but the Paranoid is not dead.", "PsychicInfoLong": "(Crewmates):\\nThe Psychic sees red highlighted names during the meeting, indicating at least one evil player. All Neutrals and Killing Crewmates are displayed as red names when the Psychic becomes a Madmate.", - "SabotageMasterInfoLong": "(Crewmates):\\nMechanic can use vent anytime. Can fix Reactors, O2, and Comms using one side. Lights fixed with one switch. Opening one door opens all doors.", + "MechanicInfoLong": "(Crewmates):\\nMechanic can use vent anytime. Can fix Reactors, O2, and Comms using one side. Lights fixed with one switch. Opening one door opens all doors.", "SheriffInfoLong": "(Crewmates):\\nSheriff has no tasks and can kill the Impostor (maybe also neutrals). If the Sheriff tries to kill a crewmate, they will suicide. They may be able to kill anyone as a madmate.", "SnitchInfoLong": "(Crewmates):\\nAs the Snitch completes tasks, Impostors' names are displayed in red during meetings. When the Snitch has only one task left, the Snitch and Impostors are marked with a star. If the Snitch becomes a Madmate, the star turns red.", "MarshallInfoLong": "(Crewmates):\\nAs the Marshall, complete your tasks to reveal yourself to the rest of the crew. Settings determine which teams can see you.", @@ -1398,17 +1398,17 @@ "KillCooldownNormally": "Kill Cooldown Without Sabotage", "MareHasIncreasedSpeed": "Increased Speed During Lights Sabotage", "MareSpeedDuringLightsOut": "Speed During Lights Sabotage", - "SabotageMasterSkillLimit": "Max use of Abilities (Excluding Doors)", + "MechanicSkillLimit": "Max use of Abilities (Excluding Doors)", "LuckeyProbability": "Probability of surviving a kill", - "SabotageMasterFixesDoors": "Can open all doors in the same building", - "SabotageMasterFixesReactors": "Can Fix Both Reactors Alone", - "SabotageMasterFixesOxygens": "Can Fix Both O2 Alone", - "SabotageMasterFixesCommunications": "Can Fix Both Comms Alone In MIRA HQ", - "SabotageMasterFixesElectrical": "Can Fix Lights With One Switch", - "SMUsesUsedWhenFixingReactorOrO2": "Uses it takes to fix Reactor/O2", - "SMUsesUsedWhenFixingLightsOrComms": "Uses it takes to fix Lights/Comms", - "SMCanFixSabotageFromAnywhereWithPet": "Can Fix Sabotages From Anywhere If Pets Are On", - "SMMaxFixedViaPet": "Max Sabotages Fixed Via Pet", + "MechanicFixesDoors": "Can open all doors in the same building", + "MechanicFixesReactors": "Can Fix Both Reactors Alone", + "MechanicFixesOxygens": "Can Fix Both O2 Alone", + "MechanicFixesCommunications": "Can Fix Both Comms Alone In MIRA HQ", + "MechanicFixesElectrical": "Can Fix Lights With One Switch", + "MechanicUsesUsedWhenFixingReactorOrO2": "Uses it takes to fix Reactor/O2", + "MechanicUsesUsedWhenFixingLightsOrComms": "Uses it takes to fix Lights/Comms", + "MechanicCanFixSabotageFromAnywhereWithPet": "Can Fix Sabotages From Anywhere If Pets Are On", + "MechanicMaxFixedViaPet": "Max Sabotages Fixed Via Pet", "SheriffCanKill%role%": "Can Kill %role%", "SheriffCanKillNeutrals": "Can Kill Neutrals", "SheriffCanKillNeutralsMode": "Neutral Configuration", diff --git a/Roles/Crewmate/SabotageMaster.cs b/Roles/Crewmate/Mechanic.cs similarity index 72% rename from Roles/Crewmate/SabotageMaster.cs rename to Roles/Crewmate/Mechanic.cs index 3138d0aa9..1babbdebf 100644 --- a/Roles/Crewmate/SabotageMaster.cs +++ b/Roles/Crewmate/Mechanic.cs @@ -1,336 +1,336 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using AmongUs.GameOptions; -using EHR.Modules; -using Hazel; - -namespace EHR.Crewmate; - -public class SabotageMaster : RoleBase -{ - private const int Id = 7000; - private static List PlayerIdList = []; - - private static OptionItem SkillLimit; - private static OptionItem FixesDoors; - private static OptionItem FixesReactors; - private static OptionItem FixesOxygens; - private static OptionItem FixesComms; - private static OptionItem FixesElectrical; - public static OptionItem SmAbilityUseGainWithEachTaskCompleted; - public static OptionItem AbilityChargesWhenFinishedTasks; - private static OptionItem UsesUsedWhenFixingReactorOrO2; - private static OptionItem UsesUsedWhenFixingLightsOrComms; - private static OptionItem CanFixSabotageFromAnywhereWithPet; - private static OptionItem MaxFixedViaPet; - public static OptionItem CanVent; - private static OptionItem VentCooldown; - private static OptionItem MaxInVentTime; - - private static bool DoorsProgressing; - private bool fixedSabotage; - private int PetLimit; - private byte SMId; - - public float UsedSkillCount; - - public override bool IsEnable => PlayerIdList.Count > 0; - - public override void SetupCustomOption() - { - Options.SetupRoleOptions(Id, TabGroup.CrewmateRoles, CustomRoles.SabotageMaster); - - SkillLimit = new IntegerOptionItem(Id + 10, "SabotageMasterSkillLimit", new(0, 80, 1), 2, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]) - .SetValueFormat(OptionFormat.Times); - - FixesDoors = new BooleanOptionItem(Id + 11, "SabotageMasterFixesDoors", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]); - - FixesReactors = new BooleanOptionItem(Id + 12, "SabotageMasterFixesReactors", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]); - - FixesOxygens = new BooleanOptionItem(Id + 13, "SabotageMasterFixesOxygens", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]); - - FixesComms = new BooleanOptionItem(Id + 14, "SabotageMasterFixesCommunications", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]); - - FixesElectrical = new BooleanOptionItem(Id + 15, "SabotageMasterFixesElectrical", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]); - - SmAbilityUseGainWithEachTaskCompleted = new FloatOptionItem(Id + 16, "AbilityUseGainWithEachTaskCompleted", new(0f, 5f, 0.05f), 3f, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]) - .SetValueFormat(OptionFormat.Times); - - AbilityChargesWhenFinishedTasks = new FloatOptionItem(Id + 19, "AbilityChargesWhenFinishedTasks", new(0f, 5f, 0.05f), 0.2f, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]) - .SetValueFormat(OptionFormat.Times); - - UsesUsedWhenFixingReactorOrO2 = new FloatOptionItem(Id + 17, "SMUsesUsedWhenFixingReactorOrO2", new(0f, 5f, 0.1f), 4f, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]) - .SetValueFormat(OptionFormat.Times); - - UsesUsedWhenFixingLightsOrComms = new FloatOptionItem(Id + 18, "SMUsesUsedWhenFixingLightsOrComms", new(0f, 5f, 0.1f), 1f, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]) - .SetValueFormat(OptionFormat.Times); - - CanFixSabotageFromAnywhereWithPet = new BooleanOptionItem(Id + 20, "SMCanFixSabotageFromAnywhereWithPet", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]); - - MaxFixedViaPet = new IntegerOptionItem(Id + 21, "SMMaxFixedViaPet", new(1, 30, 1), 1, TabGroup.CrewmateRoles) - .SetParent(CanFixSabotageFromAnywhereWithPet); - - CanVent = new BooleanOptionItem(Id + 22, "CanVent", true, TabGroup.CrewmateRoles) - .SetParent(Options.CustomRoleSpawnChances[CustomRoles.SabotageMaster]); - - VentCooldown = new FloatOptionItem(Id + 23, "VentCooldown", new(0f, 60f, 0.5f), 0f, TabGroup.CrewmateRoles) - .SetParent(CanVent) - .SetValueFormat(OptionFormat.Seconds); - - MaxInVentTime = new FloatOptionItem(Id + 24, "MaxInVentTime", new(0f, 60f, 0.5f), 0f, TabGroup.CrewmateRoles) - .SetParent(CanVent) - .SetValueFormat(OptionFormat.Seconds); - } - - public override void Init() - { - PlayerIdList = []; - UsedSkillCount = 0; - SMId = byte.MaxValue; - } - - public override void Add(byte playerId) - { - PlayerIdList.Add(playerId); - PetLimit = MaxFixedViaPet.GetInt(); - UsedSkillCount = 0; - SMId = playerId; - } - - public override void Remove(byte playerId) - { - PlayerIdList.Remove(playerId); - } - - public override void ApplyGameOptions(IGameOptions opt, byte playerId) - { - if (!CanVent.GetBool()) return; - AURoleOptions.EngineerCooldown = VentCooldown.GetFloat(); - AURoleOptions.EngineerInVentMaxTime = MaxInVentTime.GetFloat(); - } - - public override string GetProgressText(byte playerId, bool comms) - { - double limit = Math.Round(SkillLimit.GetInt() - UsedSkillCount, 1); - string colored = Utils.ColorString(Utils.GetRoleColor(CustomRoles.SabotageMaster).ShadeColor(0.25f), limit.ToString(CultureInfo.CurrentCulture)); - return $"({colored}){base.GetProgressText(playerId, comms)}"; - } - - public override void OnPet(PlayerControl pc) - { - SystemTypes[] systemTypes = [SystemTypes.Electrical, SystemTypes.Reactor, SystemTypes.Laboratory, SystemTypes.LifeSupp, SystemTypes.HeliSabotage, SystemTypes.Comms]; - if (!CanFixSabotageFromAnywhereWithPet.GetBool() || !systemTypes.FindFirst(Utils.IsActive, out SystemTypes activeSystem) || PetLimit-- < 1) return; - - switch (activeSystem) - { - case SystemTypes.Electrical: - var switchSystem = ShipStatus.Instance.Systems[SystemTypes.Electrical].CastFast(); - if (switchSystem == null) break; - switchSystem.ActualSwitches = switchSystem.ExpectedSwitches; - switchSystem.IsDirty = true; - break; - case SystemTypes.Reactor: - case SystemTypes.Laboratory: - case SystemTypes.LifeSupp: - ShipStatus.Instance.UpdateSystem(activeSystem, pc, 16); - break; - case SystemTypes.HeliSabotage: - ShipStatus.Instance.UpdateSystem(activeSystem, pc, 17); - goto case SystemTypes.Reactor; - case SystemTypes.Comms: - if (Main.NormalOptions.MapId is 1 or 5) ShipStatus.Instance.UpdateSystem(activeSystem, pc, 17); - goto case SystemTypes.Reactor; - } - } - - public void SendRPC() - { - if (!IsEnable || !Utils.DoRPC) return; - - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetSabotageMasterLimit, SendOption.Reliable); - writer.Write(SMId); - writer.Write(UsedSkillCount); - AmongUsClient.Instance.FinishRpcImmediately(writer); - } - - public static void ReceiveRPC(MessageReader reader) - { - byte id = reader.ReadByte(); - if (Main.PlayerStates[id].Role is not SabotageMaster sm) return; - - sm.UsedSkillCount = reader.ReadSingle(); - } - - public static void RepairSystem(byte playerId, SystemTypes systemType, byte amount) - { - if (Main.PlayerStates[playerId].Role is not SabotageMaster sm) return; - - switch (systemType) - { - case SystemTypes.Reactor: - case SystemTypes.Laboratory: - { - if (!FixesReactors.GetBool()) break; - - if (SkillLimit.GetFloat() > 0 && sm.UsedSkillCount + UsesUsedWhenFixingReactorOrO2.GetFloat() - 1 >= SkillLimit.GetFloat()) break; - - if (amount.HasAnyBit(ReactorSystemType.AddUserOp)) - { - ShipStatus.Instance.UpdateSystem(Main.CurrentMap == MapNames.Polus ? SystemTypes.Laboratory : SystemTypes.Reactor, playerId.GetPlayer(), ReactorSystemType.ClearCountdown); - sm.UsedSkillCount += UsesUsedWhenFixingReactorOrO2.GetFloat(); - sm.SendRPC(); - } - - break; - } - case SystemTypes.HeliSabotage: - { - if (!FixesReactors.GetBool()) break; - - if (SkillLimit.GetFloat() > 0 && sm.UsedSkillCount + UsesUsedWhenFixingReactorOrO2.GetFloat() - 1 >= SkillLimit.GetFloat()) break; - - var tags = (HeliSabotageSystem.Tags)(amount & HeliSabotageSystem.TagMask); - if (tags == HeliSabotageSystem.Tags.ActiveBit) sm.fixedSabotage = false; - - if (!sm.fixedSabotage && tags == HeliSabotageSystem.Tags.FixBit) - { - sm.fixedSabotage = true; - int consoleId = amount & HeliSabotageSystem.IdMask; - int otherConsoleId = (consoleId + 1) % 2; - ShipStatus.Instance.UpdateSystem(SystemTypes.HeliSabotage, playerId.GetPlayer(), (byte)(otherConsoleId | (int)HeliSabotageSystem.Tags.FixBit)); - sm.UsedSkillCount += UsesUsedWhenFixingReactorOrO2.GetFloat(); - sm.SendRPC(); - } - - break; - } - case SystemTypes.LifeSupp: - { - if (!FixesOxygens.GetBool()) break; - - if (SkillLimit.GetFloat() > 0 && sm.UsedSkillCount + UsesUsedWhenFixingReactorOrO2.GetFloat() - 1 >= SkillLimit.GetFloat()) break; - - if (amount.HasAnyBit(LifeSuppSystemType.AddUserOp)) - { - ShipStatus.Instance.UpdateSystem(SystemTypes.LifeSupp, playerId.GetPlayer(), LifeSuppSystemType.ClearCountdown); - sm.UsedSkillCount += UsesUsedWhenFixingReactorOrO2.GetFloat(); - sm.SendRPC(); - } - - break; - } - case SystemTypes.Comms when Main.CurrentMap == MapNames.MiraHQ: - { - if (!FixesComms.GetBool()) break; - - if (SkillLimit.GetFloat() > 0 && sm.UsedSkillCount + UsesUsedWhenFixingLightsOrComms.GetFloat() - 1 >= SkillLimit.GetFloat()) break; - - var tags = (HqHudSystemType.Tags)(amount & HqHudSystemType.TagMask); - if (tags == HqHudSystemType.Tags.ActiveBit) sm.fixedSabotage = false; - - if (!sm.fixedSabotage && tags == HqHudSystemType.Tags.FixBit) - { - sm.fixedSabotage = true; - int consoleId = amount & HqHudSystemType.IdMask; - int otherConsoleId = (consoleId + 1) % 2; - ShipStatus.Instance.UpdateSystem(SystemTypes.Comms, playerId.GetPlayer(), (byte)(otherConsoleId | (int)HqHudSystemType.Tags.FixBit)); - sm.UsedSkillCount += UsesUsedWhenFixingLightsOrComms.GetFloat(); - sm.SendRPC(); - } - - break; - } - case SystemTypes.Doors: - { - if (!FixesDoors.GetBool()) break; - - if (DoorsProgressing) break; - - int mapId = Main.NormalOptions.MapId; - if (AmongUsClient.Instance.NetworkMode == NetworkModes.FreePlay) mapId = AmongUsClient.Instance.TutorialMapId; - - var shipStatus = ShipStatus.Instance; - - DoorsProgressing = true; - - switch (mapId) - { - case 2: // Polus - { - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 71, 72); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 67, 68); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 64, 66); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 73, 74); - break; - } - case 4: // Airship - { - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 64, 67); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 71, 73); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 74, 75); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 76, 78); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 68, 70); - RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 83, 84); - break; - } - case 5: // Fungle - { - int openedDoorId = amount & DoorsSystemType.IdMask; - OpenableDoor openedDoor = shipStatus.AllDoors.FirstOrDefault(door => door.Id == openedDoorId); - - if (openedDoor == null) - Logger.Warn($"An unknown door has been opened: {openedDoorId}", nameof(SabotageMaster)); - else - { - SystemTypes room = openedDoor.Room; - - foreach (OpenableDoor door in shipStatus.AllDoors) - { - if (door.Id != openedDoorId && door.Room == room) - door.SetDoorway(true); - } - } - - break; - } - } - - DoorsProgressing = false; - break; - } - } - } - - public static void SwitchSystemRepair(byte playerId, SwitchSystem switchSystem, byte amount) - { - if (!FixesElectrical.GetBool() || Main.PlayerStates[playerId].Role is not SabotageMaster sm) return; - - if (SkillLimit.GetFloat() > 0 && sm.UsedSkillCount + UsesUsedWhenFixingLightsOrComms.GetFloat() - 1 >= SkillLimit.GetFloat()) return; - - if (amount.HasBit(SwitchSystem.DamageSystem)) return; - - int fixbit = 1 << amount; - switchSystem.ActualSwitches = (byte)(switchSystem.ExpectedSwitches ^ fixbit); - - sm.UsedSkillCount += UsesUsedWhenFixingLightsOrComms.GetFloat(); - sm.SendRPC(); - } - - public override bool CanUseVent(PlayerControl pc, int ventId) - { - return !IsThisRole(pc) || pc.Is(CustomRoles.Nimble) || CanVent.GetBool(); - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using AmongUs.GameOptions; +using EHR.Modules; +using Hazel; + +namespace EHR.Crewmate; + +public class Mechanic : RoleBase +{ + private const int Id = 7000; + private static List PlayerIdList = []; + + private static OptionItem SkillLimit; + private static OptionItem FixesDoors; + private static OptionItem FixesReactors; + private static OptionItem FixesOxygens; + private static OptionItem FixesComms; + private static OptionItem FixesElectrical; + public static OptionItem MechanicAbilityUseGainWithEachTaskCompleted; + public static OptionItem AbilityChargesWhenFinishedTasks; + private static OptionItem UsesUsedWhenFixingReactorOrO2; + private static OptionItem UsesUsedWhenFixingLightsOrComms; + private static OptionItem CanFixSabotageFromAnywhereWithPet; + private static OptionItem MaxFixedViaPet; + public static OptionItem CanVent; + private static OptionItem VentCooldown; + private static OptionItem MaxInVentTime; + + private static bool DoorsProgressing; + private bool fixedSabotage; + private int PetLimit; + private byte MechanicId; + + public float UsedSkillCount; + + public override bool IsEnable => PlayerIdList.Count > 0; + + public override void SetupCustomOption() + { + Options.SetupRoleOptions(Id, TabGroup.CrewmateRoles, CustomRoles.Mechanic); + + SkillLimit = new IntegerOptionItem(Id + 10, "MechanicSkillLimit", new(0, 80, 1), 2, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]) + .SetValueFormat(OptionFormat.Times); + + FixesDoors = new BooleanOptionItem(Id + 11, "MechanicFixesDoors", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]); + + FixesReactors = new BooleanOptionItem(Id + 12, "MechanicFixesReactors", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]); + + FixesOxygens = new BooleanOptionItem(Id + 13, "MechanicFixesOxygens", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]); + + FixesComms = new BooleanOptionItem(Id + 14, "MechanicFixesCommunications", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]); + + FixesElectrical = new BooleanOptionItem(Id + 15, "MechanicFixesElectrical", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]); + + MechanicAbilityUseGainWithEachTaskCompleted = new FloatOptionItem(Id + 16, "AbilityUseGainWithEachTaskCompleted", new(0f, 5f, 0.05f), 3f, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]) + .SetValueFormat(OptionFormat.Times); + + AbilityChargesWhenFinishedTasks = new FloatOptionItem(Id + 19, "AbilityChargesWhenFinishedTasks", new(0f, 5f, 0.05f), 0.2f, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]) + .SetValueFormat(OptionFormat.Times); + + UsesUsedWhenFixingReactorOrO2 = new FloatOptionItem(Id + 17, "MechanicUsesUsedWhenFixingReactorOrO2", new(0f, 5f, 0.1f), 4f, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]) + .SetValueFormat(OptionFormat.Times); + + UsesUsedWhenFixingLightsOrComms = new FloatOptionItem(Id + 18, "MechanicUsesUsedWhenFixingLightsOrComms", new(0f, 5f, 0.1f), 1f, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]) + .SetValueFormat(OptionFormat.Times); + + CanFixSabotageFromAnywhereWithPet = new BooleanOptionItem(Id + 20, "MechanicCanFixSabotageFromAnywhereWithPet", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]); + + MaxFixedViaPet = new IntegerOptionItem(Id + 21, "MechanicMaxFixedViaPet", new(1, 30, 1), 1, TabGroup.CrewmateRoles) + .SetParent(CanFixSabotageFromAnywhereWithPet); + + CanVent = new BooleanOptionItem(Id + 22, "CanVent", true, TabGroup.CrewmateRoles) + .SetParent(Options.CustomRoleSpawnChances[CustomRoles.Mechanic]); + + VentCooldown = new FloatOptionItem(Id + 23, "VentCooldown", new(0f, 60f, 0.5f), 0f, TabGroup.CrewmateRoles) + .SetParent(CanVent) + .SetValueFormat(OptionFormat.Seconds); + + MaxInVentTime = new FloatOptionItem(Id + 24, "MaxInVentTime", new(0f, 60f, 0.5f), 0f, TabGroup.CrewmateRoles) + .SetParent(CanVent) + .SetValueFormat(OptionFormat.Seconds); + } + + public override void Init() + { + PlayerIdList = []; + UsedSkillCount = 0; + MechanicId = byte.MaxValue; + } + + public override void Add(byte playerId) + { + PlayerIdList.Add(playerId); + PetLimit = MaxFixedViaPet.GetInt(); + UsedSkillCount = 0; + MechanicId = playerId; + } + + public override void Remove(byte playerId) + { + PlayerIdList.Remove(playerId); + } + + public override void ApplyGameOptions(IGameOptions opt, byte playerId) + { + if (!CanVent.GetBool()) return; + AURoleOptions.EngineerCooldown = VentCooldown.GetFloat(); + AURoleOptions.EngineerInVentMaxTime = MaxInVentTime.GetFloat(); + } + + public override string GetProgressText(byte playerId, bool comms) + { + double limit = Math.Round(SkillLimit.GetInt() - UsedSkillCount, 1); + string colored = Utils.ColorString(Utils.GetRoleColor(CustomRoles.Mechanic).ShadeColor(0.25f), limit.ToString(CultureInfo.CurrentCulture)); + return $"({colored}){base.GetProgressText(playerId, comms)}"; + } + + public override void OnPet(PlayerControl pc) + { + SystemTypes[] systemTypes = [SystemTypes.Electrical, SystemTypes.Reactor, SystemTypes.Laboratory, SystemTypes.LifeSupp, SystemTypes.HeliSabotage, SystemTypes.Comms]; + if (!CanFixSabotageFromAnywhereWithPet.GetBool() || !systemTypes.FindFirst(Utils.IsActive, out SystemTypes activeSystem) || PetLimit-- < 1) return; + + switch (activeSystem) + { + case SystemTypes.Electrical: + var switchSystem = ShipStatus.Instance.Systems[SystemTypes.Electrical].CastFast(); + if (switchSystem == null) break; + switchSystem.ActualSwitches = switchSystem.ExpectedSwitches; + switchSystem.IsDirty = true; + break; + case SystemTypes.Reactor: + case SystemTypes.Laboratory: + case SystemTypes.LifeSupp: + ShipStatus.Instance.UpdateSystem(activeSystem, pc, 16); + break; + case SystemTypes.HeliSabotage: + ShipStatus.Instance.UpdateSystem(activeSystem, pc, 17); + goto case SystemTypes.Reactor; + case SystemTypes.Comms: + if (Main.NormalOptions.MapId is 1 or 5) ShipStatus.Instance.UpdateSystem(activeSystem, pc, 17); + goto case SystemTypes.Reactor; + } + } + + public void SendRPC() + { + if (!IsEnable || !Utils.DoRPC) return; + + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetMechanicLimit, SendOption.Reliable); + writer.Write(MechanicId); + writer.Write(UsedSkillCount); + AmongUsClient.Instance.FinishRpcImmediately(writer); + } + + public static void ReceiveRPC(MessageReader reader) + { + byte id = reader.ReadByte(); + if (Main.PlayerStates[id].Role is not Mechanic m) return; + + m.UsedSkillCount = reader.ReadSingle(); + } + + public static void RepairSystem(byte playerId, SystemTypes systemType, byte amount) + { + if (Main.PlayerStates[playerId].Role is not Mechanic m) return; + + switch (systemType) + { + case SystemTypes.Reactor: + case SystemTypes.Laboratory: + { + if (!FixesReactors.GetBool()) break; + + if (SkillLimit.GetFloat() > 0 && m.UsedSkillCount + UsesUsedWhenFixingReactorOrO2.GetFloat() - 1 >= SkillLimit.GetFloat()) break; + + if (amount.HasAnyBit(ReactorSystemType.AddUserOp)) + { + ShipStatus.Instance.UpdateSystem(Main.CurrentMap == MapNames.Polus ? SystemTypes.Laboratory : SystemTypes.Reactor, playerId.GetPlayer(), ReactorSystemType.ClearCountdown); + m.UsedSkillCount += UsesUsedWhenFixingReactorOrO2.GetFloat(); + m.SendRPC(); + } + + break; + } + case SystemTypes.HeliSabotage: + { + if (!FixesReactors.GetBool()) break; + + if (SkillLimit.GetFloat() > 0 && m.UsedSkillCount + UsesUsedWhenFixingReactorOrO2.GetFloat() - 1 >= SkillLimit.GetFloat()) break; + + var tags = (HeliSabotageSystem.Tags)(amount & HeliSabotageSystem.TagMask); + if (tags == HeliSabotageSystem.Tags.ActiveBit) m.fixedSabotage = false; + + if (!m.fixedSabotage && tags == HeliSabotageSystem.Tags.FixBit) + { + m.fixedSabotage = true; + int consoleId = amount & HeliSabotageSystem.IdMask; + int otherConsoleId = (consoleId + 1) % 2; + ShipStatus.Instance.UpdateSystem(SystemTypes.HeliSabotage, playerId.GetPlayer(), (byte)(otherConsoleId | (int)HeliSabotageSystem.Tags.FixBit)); + m.UsedSkillCount += UsesUsedWhenFixingReactorOrO2.GetFloat(); + m.SendRPC(); + } + + break; + } + case SystemTypes.LifeSupp: + { + if (!FixesOxygens.GetBool()) break; + + if (SkillLimit.GetFloat() > 0 && m.UsedSkillCount + UsesUsedWhenFixingReactorOrO2.GetFloat() - 1 >= SkillLimit.GetFloat()) break; + + if (amount.HasAnyBit(LifeSuppSystemType.AddUserOp)) + { + ShipStatus.Instance.UpdateSystem(SystemTypes.LifeSupp, playerId.GetPlayer(), LifeSuppSystemType.ClearCountdown); + m.UsedSkillCount += UsesUsedWhenFixingReactorOrO2.GetFloat(); + m.SendRPC(); + } + + break; + } + case SystemTypes.Comms when Main.CurrentMap == MapNames.MiraHQ: + { + if (!FixesComms.GetBool()) break; + + if (SkillLimit.GetFloat() > 0 && m.UsedSkillCount + UsesUsedWhenFixingLightsOrComms.GetFloat() - 1 >= SkillLimit.GetFloat()) break; + + var tags = (HqHudSystemType.Tags)(amount & HqHudSystemType.TagMask); + if (tags == HqHudSystemType.Tags.ActiveBit) m.fixedSabotage = false; + + if (!m.fixedSabotage && tags == HqHudSystemType.Tags.FixBit) + { + m.fixedSabotage = true; + int consoleId = amount & HqHudSystemType.IdMask; + int otherConsoleId = (consoleId + 1) % 2; + ShipStatus.Instance.UpdateSystem(SystemTypes.Comms, playerId.GetPlayer(), (byte)(otherConsoleId | (int)HqHudSystemType.Tags.FixBit)); + m.UsedSkillCount += UsesUsedWhenFixingLightsOrComms.GetFloat(); + m.SendRPC(); + } + + break; + } + case SystemTypes.Doors: + { + if (!FixesDoors.GetBool()) break; + + if (DoorsProgressing) break; + + int mapId = Main.NormalOptions.MapId; + if (AmongUsClient.Instance.NetworkMode == NetworkModes.FreePlay) mapId = AmongUsClient.Instance.TutorialMapId; + + var shipStatus = ShipStatus.Instance; + + DoorsProgressing = true; + + switch (mapId) + { + case 2: // Polus + { + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 71, 72); + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 67, 68); + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 64, 66); + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 73, 74); + break; + } + case 4: // Airship + { + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 64, 67); + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 71, 73); + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 74, 75); + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 76, 78); + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 68, 70); + RepairSystemPatch.CheckAndOpenDoorsRange(shipStatus, amount, 83, 84); + break; + } + case 5: // Fungle + { + int openedDoorId = amount & DoorsSystemType.IdMask; + OpenableDoor openedDoor = shipStatus.AllDoors.FirstOrDefault(door => door.Id == openedDoorId); + + if (openedDoor == null) + Logger.Warn($"An unknown door has been opened: {openedDoorId}", nameof(Mechanic)); + else + { + SystemTypes room = openedDoor.Room; + + foreach (OpenableDoor door in shipStatus.AllDoors) + { + if (door.Id != openedDoorId && door.Room == room) + door.SetDoorway(true); + } + } + + break; + } + } + + DoorsProgressing = false; + break; + } + } + } + + public static void SwitchSystemRepair(byte playerId, SwitchSystem switchSystem, byte amount) + { + if (!FixesElectrical.GetBool() || Main.PlayerStates[playerId].Role is not Mechanic m) return; + + if (SkillLimit.GetFloat() > 0 && m.UsedSkillCount + UsesUsedWhenFixingLightsOrComms.GetFloat() - 1 >= SkillLimit.GetFloat()) return; + + if (amount.HasBit(SwitchSystem.DamageSystem)) return; + + int fixbit = 1 << amount; + switchSystem.ActualSwitches = (byte)(switchSystem.ExpectedSwitches ^ fixbit); + + m.UsedSkillCount += UsesUsedWhenFixingLightsOrComms.GetFloat(); + m.SendRPC(); + } + + public override bool CanUseVent(PlayerControl pc, int ventId) + { + return !IsThisRole(pc) || pc.Is(CustomRoles.Nimble) || CanVent.GetBool(); + } +} diff --git a/Roles/Crewmate/President.cs b/Roles/Crewmate/President.cs index f75019171..7f69c0e97 100644 --- a/Roles/Crewmate/President.cs +++ b/Roles/Crewmate/President.cs @@ -187,7 +187,7 @@ public class President : RoleBase switch (Main.PlayerStates[player.PlayerId].Role) { - case SabotageMaster sm: + case Mechanic sm: sm.UsedSkillCount--; break; case Hacker when Hacker.UseLimit.ContainsKey(player.PlayerId): diff --git a/Roles/Impostor/Nullifier.cs b/Roles/Impostor/Nullifier.cs index 288873b1f..8bd0edae7 100644 --- a/Roles/Impostor/Nullifier.cs +++ b/Roles/Impostor/Nullifier.cs @@ -81,11 +81,11 @@ internal class Nullifier : RoleBase Hacker.UseLimit[target.PlayerId]--; break; - case CustomRoles.SabotageMaster: - if (Main.PlayerStates[target.PlayerId].Role is not SabotageMaster { IsEnable: true } sm) return; + case CustomRoles.Mechanic: + if (Main.PlayerStates[target.PlayerId].Role is not Mechanic { IsEnable: true } m) return; - sm.UsedSkillCount++; - sm.SendRPC(); + m.UsedSkillCount++; + m.SendRPC(); break; case CustomRoles.SwordsMan: SwordsMan.Killed.Add(target.PlayerId);