Add Ambusher

This commit is contained in:
Gurge44 committed 2025-09-17 10:02:02 +02:00
1 parent 5cda61610c
commit 0995d7c966
15 files changed
+358 -84

No files matched your search

+1
View File
@@ -20,6 +20,7 @@ public enum CustomRoles
// Impostors
Abyssbringer,
Ambusher,
Anonymous,
AntiAdminer,
Sans, // Arrogance
+15 -2
View File
@@ -735,7 +735,7 @@ public static class BedWars
if (Health <= 0 && pc != null && pc.IsAlive())
{
killer.KillFlash();
if (killer != null) killer.KillFlash();
if (Main.GM.Value && AmongUsClient.Instance.AmHost) PlayerControl.LocalPlayer.KillFlash();
ChatCommands.Spectators.ToValidPlayers().Do(x => x.KillFlash());
@@ -1833,7 +1833,20 @@ public static class BedWars
break;
}
if (didDamage) bed.UpdateStatus();
if (didDamage)
{
foreach (byte id in bed.Breaking)
{
var pc = id.GetPlayer();
if (pc == null) continue;
NameNotifyManager.Notifies.Remove(pc.PlayerId);
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}
bed.Breaking.Clear();
bed.UpdateStatus();
}
}
}
}
+5 -3
View File
@@ -210,7 +210,7 @@ internal static class SoloPVP
if (!target.SoloAlive())
{
OnPlayerDead(target);
Main.Instance.StartCoroutine(OnPlayerDead(target));
OnPlayerKill(killer);
}
@@ -238,10 +238,12 @@ internal static class SoloPVP
Utils.NotifyRoles(SpecifyTarget: pc, SendOption: SendOption.None);
}
private static void OnPlayerDead(PlayerControl target)
private static System.Collections.IEnumerator OnPlayerDead(PlayerControl target)
{
target.ExileTemporarily();
BackCountdown.TryAdd(target.PlayerId, KB_ResurrectionWaitingTime.GetInt());
if (target.inVent || target.MyPhysics.Animations.IsPlayingEnterVentAnimation()) LateTask.New(() => target.MyPhysics.RpcExitVent(target.GetClosestVent().Id), 0.6f, log: false);
while (target.inVent || target.inMovingPlat || target.onLadder || target.MyPhysics.Animations.IsPlayingAnyLadderAnimation() || target.MyPhysics.Animations.IsPlayingEnterVentAnimation()) yield return null;
if (BackCountdown.ContainsKey(target.PlayerId)) target.ExileTemporarily();
}
private static void OnPlayerKill(PlayerControl killer)
+1 -24
View File
@@ -692,34 +692,11 @@ namespace EHR
internal sealed class ShapeshiftMenuElement : CustomNetObject
{
private readonly byte VisibleTo;
public ShapeshiftMenuElement(string display, string namePlate, byte visibleTo)
public ShapeshiftMenuElement(byte visibleTo)
{
VisibleTo = visibleTo;
CreateNetObject(string.Empty, new Vector2(0f, 0f));
LateTask.New(() => SetData(display, namePlate), 1f, log: false);
Main.AllPlayerControls.DoIf(x => x.PlayerId != visibleTo, Hide);
}
public void SetData(string display, string namePlate)
{
Logger.Warn(playerControl.Data == null ? "playerControl.Data is null" : "playerControl.Data is not null", "ShapeshiftMenuElement");
playerControl.Data.DefaultOutfit.NamePlateId = namePlate;
playerControl.Data.PlayerName = $"{Translator.GetString(display).ToUpper()}<size=0>{display}";
MessageWriter writer = MessageWriter.Get(SendOption.Reliable);
writer.StartMessage(6);
writer.Write(AmongUsClient.Instance.GameId);
writer.WritePacked(VisibleTo.GetPlayer().OwnerId);
writer.StartMessage(1);
writer.WritePacked(playerControl.Data.NetId);
playerControl.Data.Serialize(writer, false);
writer.EndMessage();
writer.EndMessage();
AmongUsClient.Instance.SendOrDisconnect(writer);
writer.Recycle();
}
}
}
+47 -6
View File
@@ -108,9 +108,9 @@ internal static class CustomRolesHelper
if (role.IsVanilla()) return role;
if (role is CustomRoles.GM) return CustomRoles.Crewmate;
if (checkDesyncRole && role.IsDesyncRole()) return Enum.Parse<CustomRoles>(role.GetDYRole() + "EHR");
if ((Options.UsePhantomBasis.GetBool() || role.AlwaysUsesPhantomBase()) && role.SimpleAbilityTrigger()) return CustomRoles.Phantom;
bool UsePets = Options.UsePets.GetBool();
if ((Options.UsePhantomBasis.GetBool() || role.AlwaysUsesPhantomBase()) && role.SimpleAbilityTrigger() && (!UsePets || role is CustomRoles.Swooper or CustomRoles.Wraith)) return CustomRoles.Phantom;
return role switch
{
@@ -153,8 +153,9 @@ internal static class CustomRolesHelper
CustomRoles.ClockBlocker => CustomRoles.Impostor,
CustomRoles.Psychopath => CustomRoles.Impostor,
CustomRoles.Postponer => CustomRoles.Impostor,
CustomRoles.Venerer => CustomRoles.Shapeshifter,
CustomRoles.Venerer => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
CustomRoles.Kidnapper => CustomRoles.Shapeshifter,
CustomRoles.Ambusher => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
CustomRoles.Stasis => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
CustomRoles.Wasp => CustomRoles.Impostor,
CustomRoles.Assumer => CustomRoles.Impostor,
@@ -433,8 +434,9 @@ internal static class CustomRolesHelper
public static RoleTypes GetDYRole(this CustomRoles role, bool load = false)
{
if (!load && ((Options.UsePhantomBasis.GetBool() && Options.UsePhantomBasisForNKs.GetBool()) || role.AlwaysUsesPhantomBase()) && !role.IsImpostor() && role.SimpleAbilityTrigger()) return RoleTypes.Phantom;
bool UsePets = !load && Options.UsePets.GetBool();
if (!load && ((Options.UsePhantomBasis.GetBool() && Options.UsePhantomBasisForNKs.GetBool()) || role.AlwaysUsesPhantomBase()) && !role.IsImpostor() && role.SimpleAbilityTrigger() && (!UsePets || role is CustomRoles.Swooper or CustomRoles.Wraith)) return RoleTypes.Phantom;
return role switch
{
@@ -713,6 +715,7 @@ internal static class CustomRolesHelper
CustomRoles.Inhibitor or
CustomRoles.Wiper or
CustomRoles.Psychopath or
CustomRoles.Ambusher or
CustomRoles.Kidnapper or
CustomRoles.Postponer or
CustomRoles.Venerer or
@@ -848,6 +851,8 @@ internal static class CustomRolesHelper
if (role.UsesPetInsteadOfKill()) return true;
if (Options.UsePhantomBasis.GetBool() && (!role.IsNK() || Options.UsePhantomBasisForNKs.GetBool()) && role.SimpleAbilityTrigger()) return false;
Type type = role.GetRoleClass().GetType();
return type.GetMethod("OnPet")?.DeclaringType == type;
}
@@ -1024,6 +1029,7 @@ internal static class CustomRolesHelper
CustomRoles.Swiftclaw or
CustomRoles.Undertaker or
CustomRoles.Abyssbringer or
CustomRoles.Ambusher or
CustomRoles.Bomber or
CustomRoles.Nuker or
CustomRoles.Camouflager or
@@ -1039,9 +1045,9 @@ internal static class CustomRolesHelper
CustomRoles.Sapper or
CustomRoles.Sniper or
CustomRoles.Twister or
CustomRoles.Swooper or //!
CustomRoles.Swooper or
CustomRoles.Venerer or
CustomRoles.Wraith or //!
CustomRoles.Wraith or
CustomRoles.RouleteGrandeur or
CustomRoles.Enderman or
CustomRoles.Explosivist or
@@ -1244,7 +1250,41 @@ internal static class CustomRolesHelper
CustomRoles.Eraser when Eraser.EraseMethod.GetValue() == 0 => true,
CustomRoles.Silencer when Silencer.SilenceMode.GetValue() == 0 => true,
CustomRoles.Sniper when !Sniper.CanKillWithBullets.GetBool() => true,
CustomRoles.Augmenter or CustomRoles.BallLightning or CustomRoles.Blackmailer or CustomRoles.Cantankerous or CustomRoles.Capitalism or CustomRoles.Consort or CustomRoles.Echo or CustomRoles.EvilDiviner or CustomRoles.FireWorks or CustomRoles.Framer or CustomRoles.Gangster or CustomRoles.Generator or CustomRoles.Hangman or CustomRoles.Inhibitor or CustomRoles.Kamikaze or CustomRoles.Mastermind or CustomRoles.Morphling or CustomRoles.Ninja or CustomRoles.Nullifier or CustomRoles.Penguin or CustomRoles.Puppeteer or CustomRoles.Saboteur or CustomRoles.Sapper or CustomRoles.Scavenger or CustomRoles.Swooper or CustomRoles.Undertaker or CustomRoles.Vampire or CustomRoles.Warlock or CustomRoles.Wasp or CustomRoles.Wiper or CustomRoles.Witch or CustomRoles.YinYanger => true,
CustomRoles.Augmenter or
CustomRoles.BallLightning or
CustomRoles.Blackmailer or
CustomRoles.Cantankerous or
CustomRoles.Capitalism or
CustomRoles.Consort or
CustomRoles.Echo or
CustomRoles.EvilDiviner or
CustomRoles.FireWorks or
CustomRoles.Framer or
CustomRoles.Gangster or
CustomRoles.Generator or
CustomRoles.Hangman or
CustomRoles.Inhibitor or
CustomRoles.Kamikaze or
CustomRoles.Mastermind or
CustomRoles.Morphling or
CustomRoles.Ninja or
CustomRoles.Nullifier or
CustomRoles.Penguin or
CustomRoles.Postponer or
CustomRoles.Puppeteer or
CustomRoles.Saboteur or
CustomRoles.Sapper or
CustomRoles.Scavenger or
CustomRoles.Swooper or
CustomRoles.Undertaker or
CustomRoles.Vampire or
CustomRoles.Warlock or
CustomRoles.Wasp or
CustomRoles.Wiper or
CustomRoles.Witch or
CustomRoles.YinYanger => true,
_ => false
};
}
@@ -1621,6 +1661,7 @@ internal static class CustomRolesHelper
CustomRoles.Ventriloquist => RoleOptionType.Impostor_Support,
CustomRoles.Vindicator => RoleOptionType.Impostor_Support,
CustomRoles.YinYanger => RoleOptionType.Impostor_Support,
CustomRoles.Ambusher => RoleOptionType.Impostor_Concealing,
CustomRoles.Anonymous => RoleOptionType.Impostor_Concealing,
CustomRoles.Duellist => RoleOptionType.Impostor_Concealing,
CustomRoles.Echo => RoleOptionType.Impostor_Concealing,
+1 -1
View File
@@ -733,7 +733,7 @@ internal static class ExtendedPlayerControl
public static void KillFlash(this PlayerControl player)
{
if (GameStates.IsLobby) return;
if (GameStates.IsLobby || player == null) return;
// Kill flash (blackout + reactor flash) processing
+19 -26
View File
@@ -1267,7 +1267,7 @@ public static class GuessManager
bool TryGetDisplay(out string display)
{
if (!PlayerIdToRawDisplay.TryGetValue(target.PlayerId, out display)) //TODO: ANY WAY TO FIND CNOS FROM PLAYERCONTROL?
display = ExistingCNOs.FindFirst(x => x.playerControl.name == target.name, out ShapeshiftMenuElement cno) ? cno.playerControl.tag : string.Empty;
display = ExistingCNOs.FindFirst(x => x.playerControl.NetId == target.NetId, out ShapeshiftMenuElement cno) ? cno.playerControl.tag : string.Empty;
if (string.IsNullOrEmpty(display))
{
@@ -1315,6 +1315,8 @@ public static class GuessManager
// First, use living players to show choices by changing their names
// The local player can't be used to show a choice (-1)
StringBuilder sb = new();
var skipped = false;
PlayerControl guesser = GuesserId.GetPlayer();
MessageWriter writer = MessageWriter.Get(SendOption.Reliable);
@@ -1338,9 +1340,11 @@ public static class GuessManager
string originalNamePlateId = pc.Data.DefaultOutfit.NamePlateId;
string originalPlayerName = pc.Data.DefaultOutfit.PlayerName;
pc.Data.DefaultOutfit.NamePlateSequenceId += 10;
pc.Data.DefaultOutfit.NamePlateId = namePlateId;
pc.SetNamePlate(namePlateId);
pc.Data.DefaultOutfit.NamePlateSequenceId += 10;
pc.Data.DefaultOutfit.PlayerName = GetString(choice).ToUpper();
sb.Append(pc.Data.DefaultOutfit.PlayerName.PadRightV2(10));
if (writer.Length > 500)
{
@@ -1357,6 +1361,13 @@ public static class GuessManager
pc.Data.Serialize(writer, false);
writer.EndMessage();
writer.StartMessage(2);
writer.WritePacked(pc.NetId);
writer.Write((byte)RpcCalls.SetNamePlateStr);
writer.Write(namePlateId);
writer.Write(pc.GetNextRpcSequenceId(RpcCalls.SetNamePlateStr));
writer.EndMessage();
pc.Data.DefaultOutfit.NamePlateId = originalNamePlateId;
pc.Data.DefaultOutfit.PlayerName = originalPlayerName;
}
@@ -1369,40 +1380,22 @@ public static class GuessManager
ExistingCNOs.Clear();
// If there aren't enough living players, spawn new CNOs to show the rest of choices
// Since CNOs use the local player's NetworkedPlayerInfo, add AU reads the player's name directly from it,
// it's impossible to show vanilla players all choices accurately with CNOs.
// No workaround found yet....
// So we send the remaining choices in chat so the player can identify them
if (data.Length >= alivePlayerControlsLength)
{
string originalNamePlateId = guesser.Data.DefaultOutfit.NamePlateId;
string originalPlayerName = guesser.Data.DefaultOutfit.PlayerName;
for (int i = alivePlayerControlsLength; i < data.Length; i++)
{
string choice = data[i].choice;
string namePlateId = data[i].namePlateId;
guesser.Data.DefaultOutfit.NamePlateSequenceId += 10;
guesser.Data.DefaultOutfit.NamePlateId = namePlateId;
guesser.Data.DefaultOutfit.PlayerName = $"{GetString(choice).ToUpper()}<size=0>{choice}";
writer = MessageWriter.Get(SendOption.Reliable);
writer.StartMessage(6);
writer.Write(AmongUsClient.Instance.GameId);
writer.WritePacked(guesser.OwnerId);
writer.StartMessage(1);
writer.WritePacked(guesser.Data.NetId);
guesser.Data.Serialize(writer, false);
writer.EndMessage();
writer.EndMessage();
AmongUsClient.Instance.SendOrDisconnect(writer);
writer.Recycle();
var cno = new ShapeshiftMenuElement(choice, namePlateId, GuesserId);
var cno = new ShapeshiftMenuElement(GuesserId);
ExistingCNOs.Add(cno);
}
guesser.Data.DefaultOutfit.NamePlateSequenceId += 10;
guesser.Data.DefaultOutfit.NamePlateId = originalNamePlateId;
guesser.Data.DefaultOutfit.PlayerName = originalPlayerName;
}
Logger.Info($"Spawned {ExistingCNOs.Count} CNOs, Used {alivePlayerControlsLength} Living Players, Showing {data.Length} Choices", "Meeting Shapeshift For Guessing");
+1
View File
@@ -3258,6 +3258,7 @@ public static class Utils
CustomRoles.Catcher => Catcher.AbilityCooldown.GetInt(),
CustomRoles.Sentry => Crewmate.Sentry.ShowInfoCooldown.GetInt(),
CustomRoles.ToiletMaster => ToiletMaster.AbilityCooldown.GetInt(),
CustomRoles.Ambusher => Ambusher.AbilityCooldown.GetInt(),
CustomRoles.AntiAdminer => AntiAdminer.AbilityCooldown.GetInt(),
CustomRoles.Sniper => Options.DefaultShapeshiftCooldown.GetInt(),
CustomRoles.Ninja => Ninja.AssassinateCooldownOpt.GetInt(),
+15 -15
View File
@@ -351,7 +351,7 @@ internal static class CheckMurderPatch
return false;
}
bool CheckMurder() => Main.PlayerStates[killer.PlayerId].Role.OnCheckMurder(killer, target) || target.Is(CustomRoles.Fragile);
bool CheckMurder() => Main.PlayerStates[killer.PlayerId].Role.OnCheckMurder(killer, target) || target.Is(CustomRoles.Fragile) || Ambusher.FragilePlayers.ContainsKey(target.PlayerId);
}
if (!killer.RpcCheckAndMurder(target, true)) return false;
@@ -1300,6 +1300,20 @@ internal static class ReportDeadBodyPatch
}
}
foreach (PlayerControl pc in Main.AllPlayerControls)
{
if (Main.CheckShapeshift.ContainsKey(pc.PlayerId) && !Doppelganger.DoppelVictim.ContainsKey(pc.PlayerId))
Camouflage.RpcSetSkin(pc, revertToDefault: true);
if (Main.CurrentMap == MapNames.Fungle && (pc.IsMushroomMixupActive() || IsActive(SystemTypes.MushroomMixupSabotage)))
pc.FixMixedUpOutfit();
if (Main.Invisible.Contains(pc.PlayerId))
pc.RpcMakeVisible();
PhantomRolePatch.OnReportDeadBody(pc);
}
EAC.TimeSinceLastTaskCompletion.Clear();
Enigma.OnReportDeadBody(player, target);
@@ -1342,20 +1356,6 @@ internal static class ReportDeadBodyPatch
Stressed.OnMeetingStart();
foreach (PlayerControl pc in Main.AllPlayerControls)
{
if (Main.CheckShapeshift.ContainsKey(pc.PlayerId) && !Doppelganger.DoppelVictim.ContainsKey(pc.PlayerId))
Camouflage.RpcSetSkin(pc, revertToDefault: true);
if (Main.CurrentMap == MapNames.Fungle && (pc.IsMushroomMixupActive() || IsActive(SystemTypes.MushroomMixupSabotage)))
pc.FixMixedUpOutfit();
if (Main.Invisible.Contains(pc.PlayerId))
pc.RpcMakeVisible();
PhantomRolePatch.OnReportDeadBody(pc);
}
MeetingTimeManager.OnReportDeadBody();
NameNotifyManager.Reset();
+9
View File
@@ -9262,6 +9262,15 @@
"Listener.FullMessageHearChance": "Full Message Hear Chance",
"Listener.FullMessage": "{0}\\n{1} whispers to {2}:\\n{3}",
"Listener.FromTo": "{0}\\n{1} whispers to {2}",
"Ambusher": "Ambusher",
"AmbusherInfo": "Ambush Players to Make Them Fragile",
"AmbusherInfoLong": "(Impostors):\\nAs the Ambusher, use your ability to become invisible. Follow someone for a few seconds to ambush them. If you succeed, the player gets Fragile effects. Otherwise, you become visible once your invisibility duration runs out and nothing happens.",
"Ambusher.FollowDuration": "Time Needed to Follow Someone For",
"Ambusher.FollowRadius": "Following Radius",
"Ambusher.InvisDurAfterSuccessfulAmbush": "Extra Invisibility Duration After Successful Ambushes",
"Ambusher.FragileDuration": "Fragile Effect Duration",
"Ambusher.Success": "<#00ff00>Ambush successful</color>",
"Ambusher.InProgress": "Ambushing {0}.... {1}s left",
"Psychopath": "Psychopath",
"PsychopathInfo": "You're Forced to Kill Off Cooldown",
"PsychopathInfoLong": "(Impostors):\\nAs the Psychopath, as soon as your kill cooldown is up, you'll kill instantly if someone is inside your kill-range.",
+2
View File
@@ -123,6 +123,8 @@ public class Siren : Coven
{
if (!Stages.TryGetValue(player.PlayerId, out var stage))
Stages[player.PlayerId] = stage = HasNecronomicon ? 2 : 1;
else
Stages[player.PlayerId]++;
switch (stage)
{
+205
View File
@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using EHR.Modules;
using Hazel;
using UnityEngine;
namespace EHR.Impostor;
public class Ambusher : RoleBase
{
public static bool On;
public override bool IsEnable => On;
public static OptionItem AbilityCooldown;
private static OptionItem AbilityDuration;
private static OptionItem KillCooldown;
private static OptionItem FollowDuration;
private static OptionItem FollowRadius;
private static OptionItem InvisDurAfterSuccessfulAmbush;
private static OptionItem FragileDuration;
private float AbilityEndTimer;
private byte TargetId;
private float TargetTimer;
private bool DontCheck;
private int Count;
private long LastRPCTS;
private byte AmbusherId;
public static Dictionary<byte, long> FragilePlayers = [];
public override void SetupCustomOption()
{
StartSetup(655200)
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0.5f, 120f, 0.5f), OptionFormat.Seconds)
.AutoSetupOption(ref AbilityDuration, 20f, new FloatValueRule(0.5f, 60f, 0.5f), OptionFormat.Seconds)
.AutoSetupOption(ref KillCooldown, 30f, new FloatValueRule(0.5f, 120f, 0.5f), OptionFormat.Seconds)
.AutoSetupOption(ref FollowDuration, 5f, new FloatValueRule(0.5f, 30f, 0.5f), OptionFormat.Seconds)
.AutoSetupOption(ref FollowRadius, 1.5f, new FloatValueRule(0.1f, 10f, 0.1f), OptionFormat.Multiplier)
.AutoSetupOption(ref InvisDurAfterSuccessfulAmbush, 5f, new FloatValueRule(0.5f, 60f, 0.5f), OptionFormat.Seconds)
.AutoSetupOption(ref FragileDuration, 40, new IntegerValueRule(1, 300, 1), OptionFormat.Seconds);
}
public override void Init()
{
On = false;
FragilePlayers = [];
}
public override void Add(byte playerId)
{
On = true;
AbilityEndTimer = AbilityDuration.GetFloat();
TargetId = byte.MaxValue;
TargetTimer = FollowDuration.GetFloat();
DontCheck = false;
Count = 0;
LastRPCTS = 0;
AmbusherId = playerId;
}
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
{
if (Options.UsePhantomBasis.GetBool())
{
AURoleOptions.PhantomCooldown = AbilityCooldown.GetFloat();
AURoleOptions.PhantomDuration = 1f;
}
else
{
AURoleOptions.ShapeshifterCooldown = AbilityCooldown.GetFloat();
AURoleOptions.ShapeshifterDuration = 1f;
}
}
public override bool CanUseKillButton(PlayerControl pc)
{
return !Main.Invisible.Contains(pc.PlayerId);
}
public override void SetKillCooldown(byte id)
{
Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
}
public override void OnMurder(PlayerControl killer, PlayerControl target)
{
killer.RpcResetAbilityCooldown();
}
public override bool OnVanish(PlayerControl pc)
{
pc.RpcMakeInvisible(true);
return false;
}
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
{
if (!shapeshifting) return true;
shapeshifter.RpcMakeInvisible(true);
return false;
}
public override void OnPet(PlayerControl pc)
{
pc.RpcMakeInvisible(true);
}
public override void OnFixedUpdate(PlayerControl pc)
{
if (!GameStates.IsInTask || !Main.IntroDestroyed || ExileController.Instance || AntiBlackout.SkipTasks || !Main.Invisible.Contains(pc.PlayerId)) return;
if (!DontCheck)
{
var pos = pc.Pos();
var radius = FollowRadius.GetFloat();
var closestPlayer = Main.AllAlivePlayerControls.Without(pc).Select(x => (pc: x, distance: Vector2.Distance(x.Pos(), pos))).Where(x => x.distance <= radius).MinBy(x => x.distance).pc;
if (closestPlayer == null)
{
TargetId = byte.MaxValue;
TargetTimer = FollowDuration.GetFloat();
}
else if (closestPlayer.PlayerId != TargetId)
{
TargetId = closestPlayer.PlayerId;
TargetTimer = FollowDuration.GetFloat();
}
else
{
TargetTimer -= Time.fixedDeltaTime;
if (TargetTimer <= 0)
{
DontCheck = true;
AbilityEndTimer = InvisDurAfterSuccessfulAmbush.GetFloat();
FragilePlayers[TargetId] = Utils.TimeStamp + FragileDuration.GetInt();
TargetId = byte.MaxValue;
TargetTimer = FollowDuration.GetFloat();
}
}
}
AbilityEndTimer -= Time.fixedDeltaTime;
if (AbilityEndTimer <= 0)
{
pc.SetKillCooldown(KillCooldown.GetFloat());
pc.RpcMakeVisible(true);
AbilityEndTimer = AbilityDuration.GetFloat();
LastRPCTS = 0;
Count = 30;
DontCheck = false;
}
if (LastRPCTS != Utils.TimeStamp)
{
pc.RpcResetAbilityCooldown();
Utils.SendRPC(CustomRPC.SyncRoleData, AmbusherId, TargetId, TargetTimer, AbilityEndTimer, DontCheck, AmbusherId, Main.Invisible.Contains(AmbusherId));
LastRPCTS = Utils.TimeStamp;
}
if (Count++ < (DontCheck ? 30 : 10)) return;
Count = 0;
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
if (pc.AmOwner) Utils.DirtyName.Add(pc.PlayerId);
}
public override void OnGlobalFixedUpdate(PlayerControl pc, bool lowLoad)
{
if (!lowLoad && FragilePlayers.TryGetValue(pc.PlayerId, out var endTS) && Utils.TimeStamp >= endTS)
FragilePlayers.Remove(pc.PlayerId);
}
public override void OnReportDeadBody()
{
TargetId = byte.MaxValue;
TargetTimer = FollowDuration.GetFloat();
AbilityEndTimer = AbilityDuration.GetFloat();
DontCheck = false;
Utils.SendRPC(CustomRPC.SyncRoleData, AmbusherId, TargetId, TargetTimer, AbilityEndTimer, DontCheck, AmbusherId, Main.Invisible.Contains(AmbusherId));
}
public void ReceiveRPC(MessageReader reader)
{
TargetId = reader.ReadByte();
TargetTimer = reader.ReadSingle();
AbilityEndTimer = reader.ReadSingle();
DontCheck = reader.ReadBoolean();
byte ambusherId = reader.ReadByte();
if (reader.ReadBoolean()) Main.Invisible.Add(ambusherId);
else Main.Invisible.Remove(ambusherId);
}
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
if (seer.PlayerId != AmbusherId || seer.PlayerId != target.PlayerId || hud || meeting || !Main.Invisible.Contains(AmbusherId)) return string.Empty;
string str = DontCheck ? Translator.GetString("Ambusher.Success") : string.Format(Translator.GetString("Ambusher.InProgress"), TargetId.ColoredPlayerName(), Math.Round(TargetTimer, 1));
return $"{str}\n{string.Format(Translator.GetString("Ambusher.InvisTimeLeft"), (int)Math.Round(AbilityEndTimer))}";
}
}
+1 -1
View File
@@ -125,7 +125,7 @@ public class BountyHunter : RoleBase
public override void OnFixedUpdate(PlayerControl player)
{
if (!GameStates.IsInTask || float.IsNaN(ChangeTimer)) return;
if (!GameStates.IsInTask || float.IsNaN(ChangeTimer) || !Main.IntroDestroyed || ExileController.Instance || AntiBlackout.SkipTasks) return;
if (!player.IsAlive())
ChangeTimer = float.NaN;
+24 -5
View File
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using EHR.Modules;
using Hazel;
using UnityEngine;
namespace EHR.Impostor;
@@ -15,6 +16,7 @@ public class Mercenary : RoleBase
private float SuicideTimer;
private int Timer;
private byte MercenaryId;
public override bool IsEnable => PlayerIdList.Count > 0;
@@ -37,7 +39,7 @@ public class Mercenary : RoleBase
public override void Init()
{
PlayerIdList = [];
SuicideTimer = TimeLimit.GetFloat();
SuicideTimer = 0f;
Timer = TimeLimit.GetInt();
}
@@ -45,7 +47,8 @@ public class Mercenary : RoleBase
{
PlayerIdList.Add(serial);
Timer = TimeLimit.GetInt();
SuicideTimer = TimeLimit.GetFloat();
SuicideTimer = 0f;
MercenaryId = serial;
}
public override void Remove(byte playerId)
@@ -67,7 +70,7 @@ public class Mercenary : RoleBase
{
if (!killer.Is(CustomRoles.Mercenary)) return true;
SuicideTimer = float.NaN;
SuicideTimer = 0f;
Timer = TimeLimit.GetInt();
killer.MarkDirtySettings();
return true;
@@ -81,7 +84,7 @@ public class Mercenary : RoleBase
public override void OnFixedUpdate(PlayerControl player)
{
if (!GameStates.IsInTask) return;
if (!GameStates.IsInTask || !Main.IntroDestroyed || ExileController.Instance || AntiBlackout.SkipTasks) return;
if (!HasKilled(player))
{
@@ -106,7 +109,12 @@ public class Mercenary : RoleBase
SuicideTimer += Time.fixedDeltaTime;
int tempTimer = Timer;
Timer = TimeLimit.GetInt() - (int)SuicideTimer;
if (Timer != tempTimer && Timer <= 20 && !player.IsModdedClient()) Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
if (Timer != tempTimer && Timer <= 20 && !player.IsModdedClient())
{
Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
Utils.SendRPC(CustomRPC.SyncRoleData, MercenaryId, Timer);
}
}
}
@@ -115,4 +123,15 @@ public class Mercenary : RoleBase
SuicideTimer = 0f;
Timer = TimeLimit.GetInt();
}
public void ReceiveRPC(MessageReader reader)
{
Timer = reader.ReadPackedInt32();
}
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
if (seer.PlayerId != MercenaryId || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || meeting || Timer - 1 > 20) return string.Empty;
return string.Format(Translator.GetString("SerialKillerTimeLeft"), Timer - 1);
}
}
+12 -1
View File
@@ -74,7 +74,13 @@ public class Slenderman : RoleBase
public override void OnCheckPlayerPosition(PlayerControl pc)
{
if (MeetingCooldownEndTS > Utils.TimeStamp || pc.Is(CustomRoles.Slenderman) || SlendermanPC == null || !SlendermanPC.IsAlive()) return;
if (pc.Is(CustomRoles.Slenderman)) return;
if (MeetingCooldownEndTS > Utils.TimeStamp || SlendermanPC == null || !SlendermanPC.IsAlive())
{
Blinded.Clear();
return;
}
bool inRange = Vector2.Distance(pc.Pos(), SlendermanPC.Pos()) <= BlindRange.GetFloat();
@@ -105,6 +111,11 @@ public class Slenderman : RoleBase
MeetingCooldownEndTS = Utils.TimeStamp + AfterMeetingBlindCooldown.GetInt();
}
public override void OnReportDeadBody()
{
Blinded.Clear();
}
public override string GetProgressText(byte playerId, bool comms)
{
return base.GetProgressText(playerId, comms) + $" ({Blinded.Count})";