Add Frightener

This commit is contained in:
Gurge44 committed 2026-04-20 16:28:18 +02:00
1 parent 4379ca8409
commit 65094c0b75
8 files changed
+155 -3

No files matched your search

+1
View File
@@ -63,6 +63,7 @@ public enum CustomRoles
Forger,
Framer,
Freezer,
Frightener,
Gambler,
Gangster,
Generator,
+3
View File
@@ -164,6 +164,7 @@ internal static class CustomRolesHelper
CustomRoles.Centralizer => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
CustomRoles.Venerer => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
CustomRoles.Kidnapper => CustomRoles.Shapeshifter,
CustomRoles.Frightener => CustomRoles.Shapeshifter,
CustomRoles.Obstructer => CustomRoles.Impostor,
CustomRoles.Ambusher => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
CustomRoles.Stasis => UsePets ? CustomRoles.Impostor : CustomRoles.Shapeshifter,
@@ -764,6 +765,7 @@ internal static class CustomRolesHelper
CustomRoles.Psychopath or
CustomRoles.Ambusher or
CustomRoles.Kidnapper or
CustomRoles.Frightener or
CustomRoles.Obstructer or
CustomRoles.Exclusionary or
CustomRoles.Catalyst or
@@ -1296,6 +1298,7 @@ internal static class CustomRolesHelper
CustomRoles.Consigliere => RoleOptionType.Impostor_Miscellaneous,
CustomRoles.CursedWolf => RoleOptionType.Impostor_Miscellaneous,
CustomRoles.EvilTracker => RoleOptionType.Impostor_Miscellaneous,
CustomRoles.Frightener => RoleOptionType.Impostor_Miscellaneous,
CustomRoles.Gambler => RoleOptionType.Impostor_Miscellaneous,
CustomRoles.Generator => RoleOptionType.Impostor_Miscellaneous,
CustomRoles.Loner => RoleOptionType.Impostor_Miscellaneous,
+1
View File
@@ -71,6 +71,7 @@ public class PlayerState(byte playerId)
Patrolled,
Misguess,
LossOfBlood,
Frightened,
// Natural Disasters
Meteor,
+1
View File
@@ -162,6 +162,7 @@ public enum CustomRPC
Blessed,
Necronomicon,
Stained,
Entombed,
// Game Modes
RoomRushDataSync,
+2
View File
@@ -1088,6 +1088,8 @@ internal static class ReportDeadBodyPatch
if (!Wyrd.CheckPlayerAction(__instance, Wyrd.Action.Button)) return false; // Player dies, no notify needed
}
if (!Frightener.OnAnyoneReportDeadBody(__instance, !target)) return false; // Player dies, no notify needed
if (target)
{
if (Coroner.UnreportablePlayers.Contains(target.PlayerId)
+6 -1
View File
@@ -1888,6 +1888,10 @@
"Freezer": "Freezer",
"FreezerInfo": "Freeze Players",
"FreezerInfoLong": "(Impostors):\\nAs the Freezer, you can shapeshift into a player to freeze them. The frozen player cannot move for a set amount of time.",
"Frightener": "Frightener",
"FrightenerInfo": "Prevent Players From Reporting",
"FrightenerInfoLong": "(Impostors):\\nAs the Frightener, you can shapeshift into a player to alarm them for some time. Alarmed players can't report bodies (or even call meetings), else they die.",
"Frightener.KillOnEmergencyMeetingCall": "Alarmed Players Die When Calling an Emergency Meeting",
"Gambler": "Gambler",
"GamblerInfo": "Each Kill gets you an Effect",
"GamblerInfoLong": "(Impostors):\\nAs the Gambler, a random event happens after each kill you get. These effects can be anywhere between a self-report and an extra sniper shot.",
@@ -3642,7 +3646,7 @@
"EnergeticInfoLong": "(Add-ons):\\nAs the Energetic, the cooldown of your ability is reduced by 25%.",
"Entombed": "Entombed",
"EntombedInfo": "Can't Enter a Specific Room",
"EntombedInfoLong": "(Add-ons):\\nAs the Entombed, you can't enter a specific room. Randomized every round.",
"EntombedInfoLong": "(Add-ons):\\nAs the Entombed, you can't enter a specific room. Randomized every round. If you enter it regardless, you suicide.",
"Entombed.SuffixGracePeriod": "This round, you can't enter <#bb0000><b>{0}</b></color>\\nTakes effect in <#ffff44><b>{1}</b>s</color>",
"Entombed.SuffixActive": "<size=80%><#ff5555>X</color> <#bb0000>{0}</color></size>",
"Entranced": "Entranced",
@@ -9515,6 +9519,7 @@
"DeathReason.Execution": "Executed",
"DeathReason.Fall": "Fall",
"DeathReason.FollowingSuicide": "Heartbroken",
"DeathReason.Frightened": "Frightened",
"DeathReason.Gambled": "Guessed",
"DeathReason.Hack": "Hacked",
"DeathReason.Hex": "Hexed",
+41 -2
View File
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using AmongUs.GameOptions;
using EHR.Modules;
using Hazel;
namespace EHR.Roles;
@@ -8,6 +10,7 @@ public class Entombed : IAddon
public AddonTypes Type => AddonTypes.Harmful;
public static Dictionary<byte, SystemTypes> BlockedRoom = [];
private static long LastNotifyTS;
private static long MeetingEndTS;
private static float GracePeriodLength;
@@ -24,12 +27,48 @@ public class Entombed : IAddon
foreach (PlayerControl pc in Main.EnumeratePlayerControls())
if (pc.Is(CustomRoles.Entombed))
BlockedRoom[pc.PlayerId] = ShipStatus.Instance.AllRooms.RandomElement().RoomId;
if (Utils.DoRPC)
{
MessageWriter writer = Utils.CreateRPC(CustomRPC.Entombed);
writer.Write(MeetingEndTS.ToString());
writer.Write(GracePeriodLength);
writer.Write(BlockedRoom.Count);
foreach ((byte key, SystemTypes value) in BlockedRoom)
{
writer.Write(key);
writer.Write((byte)value);
}
Utils.EndRPC(writer);
}
}
public static void ReceiveRPC(MessageReader reader)
{
MeetingEndTS = long.Parse(reader.ReadString());
GracePeriodLength = reader.ReadSingle();
Loop.Times(reader.ReadInt32(), _ => BlockedRoom[reader.ReadByte()] = (SystemTypes)reader.ReadByte());
}
public static void OnFixedUpdate(PlayerControl pc)
{
if (Utils.TimeStamp - MeetingEndTS <= GracePeriodLength || !BlockedRoom.TryGetValue(pc.PlayerId, out SystemTypes blockedRoom)) return;
if (!BlockedRoom.TryGetValue(pc.PlayerId, out SystemTypes blockedRoom)) return;
long now = Utils.TimeStamp;
if (now - MeetingEndTS <= GracePeriodLength)
{
if (LastNotifyTS != now)
{
LastNotifyTS = now;
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}
return;
}
if (pc.IsInRoom(blockedRoom))
pc.Suicide();
}
@@ -38,6 +77,6 @@ public class Entombed : IAddon
{
if (!BlockedRoom.TryGetValue(seer.PlayerId, out SystemTypes blockedRoom) || !seer.IsAlive()) return string.Empty;
long elapsed = Utils.TimeStamp - MeetingEndTS;
return string.Format(Translator.GetString(elapsed <= GracePeriodLength ? "Entombed.SuffixGracePeriod" : "Entombed.SuffixActive"), Translator.GetString(blockedRoom.ToString()), GracePeriodLength - elapsed);
return string.Format(Translator.GetString(elapsed < GracePeriodLength ? "Entombed.SuffixGracePeriod" : "Entombed.SuffixActive"), Translator.GetString(blockedRoom.ToString()), GracePeriodLength - elapsed);
}
}
@@ -0,0 +1,100 @@
using System.Collections.Generic;
using System.Linq;
using EHR.Modules;
using Hazel;
namespace EHR.Roles;
public class Frightener : RoleBase
{
public static bool On;
private static List<Frightener> Instances = [];
private static OptionItem KillOnEmergencyMeetingCall;
private static OptionItem AbilityCooldown;
private static OptionItem AbilityDuration;
private static OptionItem AbilityUseLimit;
private static OptionItem AbilityUseGainWithEachKill;
public override bool IsEnable => On;
private byte FrightenerId;
private HashSet<byte> AlarmedPlayers;
public override void SetupCustomOption()
{
StartSetup(658400)
.AutoSetupOption(ref KillOnEmergencyMeetingCall, true)
.AutoSetupOption(ref AbilityCooldown, 5f, new FloatValueRule(1, 120, 1), OptionFormat.Seconds)
.AutoSetupOption(ref AbilityDuration, 15f, new FloatValueRule(1, 120, 1), OptionFormat.Seconds)
.AutoSetupOption(ref AbilityUseLimit, 1f, new FloatValueRule(0, 20, 0.05f), OptionFormat.Times)
.AutoSetupOption(ref AbilityUseGainWithEachKill, 1.5f, new FloatValueRule(0f, 5f, 0.25f), OptionFormat.Times);
}
public override void Init()
{
On = false;
Instances = [];
}
public override void Add(byte playerId)
{
On = true;
Instances.Add(this);
FrightenerId = playerId;
AlarmedPlayers = [];
playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat());
}
public override void Remove(byte playerId)
{
Instances.Remove(this);
}
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
{
if (shapeshifting && AlarmedPlayers.Add(target.PlayerId))
{
LateTask.New(() =>
{
AlarmedPlayers.Remove(target.PlayerId);
if (ReportDeadBodyPatch.MeetingStarted || GameStates.IsEnded || !GameStates.IsInTask) return;
Utils.NotifyRoles(SpecifySeer: shapeshifter, SpecifyTarget: shapeshifter);
}, AbilityDuration.GetFloat(), "Frightener Ability");
Utils.NotifyRoles(SpecifySeer: shapeshifter, SpecifyTarget: shapeshifter);
Utils.SendRPC(CustomRPC.SyncRoleData, FrightenerId, target.PlayerId);
}
return false;
}
public static bool OnAnyoneReportDeadBody(PlayerControl reporter, bool emergencyMeeting)
{
if (emergencyMeeting && !KillOnEmergencyMeetingCall.GetBool()) return true;
foreach (Frightener instance in Instances)
{
if (instance.AlarmedPlayers.Contains(reporter.PlayerId))
{
reporter.Suicide(PlayerState.DeathReason.Frightened, instance.FrightenerId.GetPlayer());
return false;
}
}
return true;
}
public void ReceiveRPC(MessageReader reader)
{
byte id = reader.ReadByte();
AlarmedPlayers.Add(id);
LateTask.New(() => AlarmedPlayers.Remove(id), AbilityDuration.GetFloat() - 0.5f, "Frightener Ability NHMC");
}
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
if (seer.PlayerId != FrightenerId || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || meeting || AlarmedPlayers.Count == 0) return string.Empty;
return string.Format(Translator.GetString("Frightener.Suffix"), string.Join(", ", AlarmedPlayers.Select(x => x.ColoredPlayerName())));
}
}