Add Operative

This commit is contained in:
Gurge44 committed 2026-04-23 10:19:57 +02:00
1 parent 6049baca00
commit 4fef350486
11 files changed
+87 -14

No files matched your search

+1
View File
@@ -243,6 +243,7 @@ public enum CustomRoles
Nightmare,
Negotiator,
Observer,
Operative,
Oracle,
Oxyman,
Pacifist,
+1
View File
@@ -439,6 +439,7 @@ public class Main : BasePlugin
{ CustomRoles.Vacuum, "#E44CD6" },
{ CustomRoles.Carrier, "#5DE2E7" },
{ CustomRoles.Transmitter, "#c9a11e" },
{ CustomRoles.Operative, "#47f5d2" },
{ CustomRoles.Tar, "#8C796B" },
{ CustomRoles.Sensor, "#a3f7ff" },
{ CustomRoles.Doorjammer, "#FFECA1" },
+2
View File
@@ -239,6 +239,7 @@ internal static class CustomRolesHelper
CustomRoles.Tornado => CustomRoles.Crewmate,
CustomRoles.Druid => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
CustomRoles.Catcher => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
CustomRoles.Operative => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
CustomRoles.Insight => CustomRoles.Crewmate,
CustomRoles.Tunneler => CustomRoles.Crewmate,
CustomRoles.Detour => CustomRoles.Crewmate,
@@ -1445,6 +1446,7 @@ internal static class CustomRolesHelper
CustomRoles.Merchant => RoleOptionType.Crewmate_Support,
CustomRoles.Monarch => RoleOptionType.Crewmate_Support,
CustomRoles.Negotiator => RoleOptionType.Crewmate_Support,
CustomRoles.Operative => RoleOptionType.Crewmate_Support,
CustomRoles.Rhapsode => RoleOptionType.Crewmate_Support,
CustomRoles.Scanner => RoleOptionType.Crewmate_Support,
CustomRoles.SecurityGuard => RoleOptionType.Crewmate_Support,
+4 -2
View File
@@ -20,7 +20,7 @@ public abstract class CustomSabotage
protected virtual void Fix()
{
Instances.Remove(this);
SabotageSystemTypeRepairDamagePatch.Instance.IsDirty = true;
ShipStatus.Instance.Systems[SystemTypes.Sabotage].CastFast<SabotageSystemType>().IsDirty = true;
}
protected virtual string GetSuffix(PlayerControl seer, PlayerControl target, bool hud, bool meeting)
@@ -93,9 +93,11 @@ public abstract class CustomSabotage
if (Instances.Count > 0)
{
SabotageSystemTypeRepairDamagePatch.Instance.Timer = SabotageSystemTypeRepairDamagePatch.IsCooldownModificationEnabled
SabotageSystemType sabotageSystemType = ShipStatus.Instance.Systems[SystemTypes.Sabotage].CastFast<SabotageSystemType>();
sabotageSystemType.Timer = SabotageSystemTypeRepairDamagePatch.IsCooldownModificationEnabled
? SabotageSystemTypeRepairDamagePatch.ModifiedCooldownSec
: 30f;
sabotageSystemType.IsDirty = true;
}
}
+2
View File
@@ -1405,11 +1405,13 @@ internal static class ExtendedPlayerControl
};
}
/*
public bool CanUseSabotage()
{
if (!player.IsAlive() || player.Data.Role.Role == RoleTypes.GuardianAngel) return false;
return Main.PlayerStates.TryGetValue(player.PlayerId, out PlayerState state) && state.Role.CanUseSabotage(player);
}
*/
public RoleTypes GetGhostRoleBasis()
{
+1
View File
@@ -3679,6 +3679,7 @@ public static class Utils
CustomRoles.Thanos => 5,
CustomRoles.Blockade => 5,
CustomRoles.Mole => Mole.CD.GetInt(),
CustomRoles.Operative => Operative.AbilityCooldown.GetInt(),
CustomRoles.PortalMaker => PortalMaker.AbilityCooldown.GetInt(),
CustomRoles.Telecommunication => Telecommunication.VentCooldown.GetInt(),
CustomRoles.Tether => Tether.VentCooldown.GetInt(),
+3 -4
View File
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using AmongUs.GameOptions;
using EHR.Modules;
using EHR.Roles;
@@ -405,7 +404,7 @@ public static class SabotageSystemTypeRepairDamagePatch
public static bool CheckSabotage(SabotageSystemType __instance, PlayerControl player, SystemTypes systemTypes)
{
if (__instance is { Timer: > 0f }) return false;
if (__instance.Timer > 0f) return false;
if (Options.DisableSabotage.GetBool())
{
@@ -450,7 +449,7 @@ public static class SabotageSystemTypeRepairDamagePatch
if (Pelican.IsEaten(player.PlayerId)) return false;
if (__instance != null && systemTypes == SystemTypes.Electrical && Main.PlayerStates.Values.FindFirst(x => !x.IsDead && x.MainRole == CustomRoles.Battery && x.Player != null && x.Player.GetAbilityUseLimit() >= 1f, out var batteryState))
if (systemTypes == SystemTypes.Electrical && Main.PlayerStates.Values.FindFirst(x => !x.IsDead && x.MainRole == CustomRoles.Battery && x.Player != null && x.Player.GetAbilityUseLimit() >= 1f, out var batteryState))
{
batteryState.Player.RpcRemoveAbilityUse();
player.Notify(string.Format(Translator.GetString("BatteryNotify"), CustomRoles.Battery.ToColoredString()), 10f);
@@ -465,7 +464,7 @@ public static class SabotageSystemTypeRepairDamagePatch
{
var room = player.GetPlainShipRoom();
if (room != null && Doorjammer.JammedRooms.Contains(room.RoomId))
if (room && Doorjammer.JammedRooms.Contains(room.RoomId))
{
player.Notify(Translator.GetString("DoorjammerBlockSabo"));
return false;
+6 -6
View File
@@ -18,7 +18,7 @@ namespace EHR;
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.UpdateSystem), typeof(SystemTypes), typeof(PlayerControl), typeof(MessageReader))]
public static class MessageReaderUpdateSystemPatch
{
public static bool Prefix([HarmonyArgument(0)] SystemTypes systemType, [HarmonyArgument(1)] PlayerControl player, [HarmonyArgument(2)] MessageReader reader)
public static bool Prefix(ShipStatus __instance, [HarmonyArgument(0)] SystemTypes systemType, [HarmonyArgument(1)] PlayerControl player, [HarmonyArgument(2)] MessageReader reader)
{
try
{
@@ -38,7 +38,7 @@ public static class MessageReaderUpdateSystemPatch
return false;
}
return RepairSystemPatch.Prefix(systemType, player, amount);
return RepairSystemPatch.Prefix(__instance, systemType, player, amount);
}
catch { }
@@ -60,7 +60,7 @@ public static class MessageReaderUpdateSystemPatch
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.UpdateSystem), typeof(SystemTypes), typeof(PlayerControl), typeof(byte))]
internal static class RepairSystemPatch
{
public static bool Prefix( /*ShipStatus __instance,*/
public static bool Prefix(ShipStatus __instance,
[HarmonyArgument(0)] SystemTypes systemType,
[HarmonyArgument(1)] PlayerControl player,
[HarmonyArgument(2)] byte amount)
@@ -156,7 +156,7 @@ internal static class RepairSystemPatch
break;
}
case SystemTypes.Sabotage when AmongUsClient.Instance.NetworkMode != NetworkModes.FreePlay:
return SabotageSystemTypeRepairDamagePatch.CheckSabotage(null, player, systemType);
return SabotageSystemTypeRepairDamagePatch.CheckSabotage(__instance.Systems[SystemTypes.Sabotage].CastFast<SabotageSystemType>(), player, systemType);
case SystemTypes.Security when amount == 1:
{
bool camerasDisabled = Main.CurrentMap switch
@@ -233,14 +233,14 @@ internal static class RepairSystemPatch
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CloseDoorsOfType))]
internal static class CloseDoorsPatch
{
public static bool Prefix([HarmonyArgument(0)] SystemTypes room)
public static bool Prefix(ShipStatus __instance, [HarmonyArgument(0)] SystemTypes room)
{
bool allow = !Options.DisableSabotage.GetBool() && !AntiBlackout.SkipTasks && Options.CurrentGameMode is not CustomGameMode.SoloPVP and not CustomGameMode.FFA and not CustomGameMode.StopAndGo and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle and not CustomGameMode.Snowdown;
if (Doorjammer.JammedRooms.Contains(room)) allow = false;
if (SecurityGuard.BlockSabo.Count > 0) allow = false;
if (Options.DisableCloseDoor.GetBool()) allow = false;
if (Main.CurrentMap != MapNames.Polus && SabotageSystemTypeRepairDamagePatch.Instance != null && SabotageSystemTypeRepairDamagePatch.Instance.AnyActive) allow = false;
if (Main.CurrentMap != MapNames.Polus && __instance.Systems[SystemTypes.Sabotage].CastFast<SabotageSystemType>().AnyActive) allow = false;
Logger.Info($"({room}) => {(allow ? "Allowed" : "Blocked")}", "DoorClose");
return allow;
+3
View File
@@ -1191,6 +1191,9 @@
"Observer": "Observer",
"ObserverInfo": "You can see all shield-animations",
"ObserverInfoLong": "(Crewmates):\\nThe Observer can see all shield animations caused by other players after the first meeting.",
"Operative": "Operative",
"OperativeInfo": "Reset Sabotage Cooldowns",
"OperativeInfoLong": "(Crewmates):\\nAs the Operative, you can vent/pet to reset all critical sabotage cooldowns to 30s.",
"Oracle": "Oracle",
"OracleInfo": "Vote a player to see their alignment",
"OracleInfoLong": "(Crewmates):\\nAs the Oracle, you may vote for a player during a meeting. You'll see if they are a crewmate, neutral, or Impostor.\\n\\n<color=#ff0000><b>But be careful!\\nThis has a chance of giving you an incorrect result!</b></color>\\n(The host sets the chance)",
+2 -2
View File
@@ -116,12 +116,12 @@ public abstract class RoleBase : IComparable<RoleBase>
public virtual bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
return target != null && killer != null;
return target && killer;
}
public virtual bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target)
{
return target != null && killer != null;
return target && killer;
}
public virtual void OnMurder(PlayerControl killer, PlayerControl target) { }
@@ -0,0 +1,62 @@
using AmongUs.GameOptions;
namespace EHR.Roles;
public class Operative : RoleBase
{
public static bool On;
public static OptionItem AbilityCooldown;
private static OptionItem AbilityUseLimit;
private static OptionItem AbilityUseGainWithEachTaskCompleted;
private static OptionItem AbilityChargesWhenFinishedTasks;
public override bool IsEnable => On;
public override void SetupCustomOption()
{
StartSetup(658800)
.AutoSetupOption(ref AbilityCooldown, 15, new IntegerValueRule(0, 120, 1), OptionFormat.Seconds)
.AutoSetupOption(ref AbilityUseLimit, 1f, new FloatValueRule(0, 20, 0.05f), OptionFormat.Times)
.AutoSetupOption(ref AbilityUseGainWithEachTaskCompleted, 0.4f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times)
.AutoSetupOption(ref AbilityChargesWhenFinishedTasks, 0.2f, new FloatValueRule(0f, 5f, 0.05f), OptionFormat.Times);
}
public override void Init()
{
On = false;
}
public override void Add(byte playerId)
{
On = true;
playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat());
}
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
{
if (!Options.UsePets.GetBool())
{
AURoleOptions.EngineerInVentMaxTime = 1f;
AURoleOptions.EngineerCooldown = AbilityCooldown.GetFloat();
}
}
public override void OnEnterVent(PlayerControl pc, Vent vent)
{
if (Options.UsePets.GetBool()) return;
UseAbility();
}
public override void OnPet(PlayerControl pc)
{
UseAbility();
}
static void UseAbility()
{
SabotageSystemType sabotageSystemType = ShipStatus.Instance.Systems[SystemTypes.Sabotage].CastFast<SabotageSystemType>();
sabotageSystemType.Timer = 30f;
sabotageSystemType.IsDirty = true;
}
}