Merge remote-tracking branch 'origin/main'

This commit is contained in:
Gurge44 committed 2024-10-16 19:50:27 +02:00
commit 13dcaaa0f1
22 files changed
+221 -130

No files matched your search

+2 -2
View File
@@ -170,7 +170,6 @@ namespace EHR
int ventLimit = VentTimes.GetInt();
VentLimit = Main.AllPlayerControls.ToDictionary(x => x.PlayerId, _ => ventLimit);
Utils.SendRPC(CustomRPC.RoomRushDataSync, 1);
AllRooms = ShipStatus.Instance.AllRooms.Select(x => x.RoomId).ToHashSet();
AllRooms.Remove(SystemTypes.Hallway);
@@ -222,6 +221,7 @@ namespace EHR
}
if (ventLimit > 0) aapc.Without(PlayerControl.LocalPlayer).Do(x => x.RpcChangeRoleBasis(CustomRoles.EngineerEHR));
Utils.SendRPC(CustomRPC.RoomRushDataSync, 1);
NameNotifyManager.Reset();
StartNewRound(true);
@@ -287,7 +287,7 @@ namespace EHR
{
Main.AllPlayerSpeed[PlayerControl.LocalPlayer.PlayerId] = speed;
PlayerControl.LocalPlayer.SyncSettings();
}, (AmongUsClient.Instance.Ping / 1000f) * 2);
}, (AmongUsClient.Instance.Ping / 1000f) * 2f);
}
private static PlainShipRoom GetRoomClass(this SystemTypes systemTypes) => ShipStatus.Instance.AllRooms.First(x => x.RoomId == systemTypes);
+43 -43
View File
@@ -21,7 +21,7 @@ internal static class SoloKombatManager
public static Dictionary<byte, int> KBScore = [];
public static int RoundTime;
private static readonly Dictionary<byte, (string TEXT, long TIMESTAMP)> NameNotify = [];
private static readonly Dictionary<byte, (string Text, long TimeStamp)> NameNotify = [];
private static Dictionary<byte, int> BackCountdown = [];
private static Dictionary<byte, long> LastHurt = [];
@@ -122,9 +122,9 @@ internal static class SoloKombatManager
return;
}
if (NameNotify.TryGetValue(player.PlayerId, out (string TEXT, long TIMESTAMP) value1))
if (NameNotify.TryGetValue(player.PlayerId, out (string Text, long TimeStamp) value1))
{
name = value1.TEXT;
name = value1.Text;
}
}
@@ -164,7 +164,9 @@ internal static class SoloKombatManager
LastHurt[target.PlayerId] = Utils.TimeStamp;
killer.SetKillCooldown(KB_ATKCooldown.GetFloat(), target);
var kcd = KB_ATKCooldown.GetFloat();
if (killer.IsHost()) kcd += (AmongUsClient.Instance.Ping / 1000f) * 2f;
killer.SetKillCooldown(kcd, target);
RPC.PlaySoundRPC(killer.PlayerId, Sounds.KillSound);
RPC.PlaySoundRPC(target.PlayerId, Sounds.KillSound);
if (!target.IsModClient() && !target.AmOwner)
@@ -268,58 +270,56 @@ internal static class SoloKombatManager
public static void Postfix( /*PlayerControl __instance*/)
{
if (!GameStates.IsInTask || Options.CurrentGameMode != CustomGameMode.SoloKombat) return;
if (!GameStates.IsInTask || Options.CurrentGameMode != CustomGameMode.SoloKombat || !AmongUsClient.Instance.AmHost) return;
if (AmongUsClient.Instance.AmHost)
foreach (var pc in Main.AllPlayerControls)
{
foreach (var pc in Main.AllPlayerControls.Where(pc => !pc.SoloAlive()).ToArray())
if (!pc.SoloAlive())
{
if (pc.inVent && KB_BootVentWhenDead.GetBool()) pc.MyPhysics.RpcExitVent(2);
var pos = Pelican.GetBlackRoomPS();
var dis = Vector2.Distance(pos, pc.Pos());
if (dis > 1f) pc.TP(pos);
}
}
if (LastFixedUpdate == Utils.TimeStamp) return;
LastFixedUpdate = Utils.TimeStamp;
if (LastFixedUpdate == Utils.TimeStamp) return;
LastFixedUpdate = Utils.TimeStamp;
RoundTime--;
RoundTime--;
if (!AmongUsClient.Instance.AmHost) return;
foreach (var pc in Main.AllPlayerControls)
foreach (var pc in Main.AllPlayerControls)
{
bool notifyRoles = false;
if (LastHurt[pc.PlayerId] + KB_RecoverAfterSecond.GetInt() < Utils.TimeStamp && PlayerHP[pc.PlayerId] < PlayerHPMax[pc.PlayerId] && pc.SoloAlive() && !pc.inVent)
{
bool notifyRoles = false;
if (LastHurt[pc.PlayerId] + KB_RecoverAfterSecond.GetInt() < Utils.TimeStamp && PlayerHP[pc.PlayerId] < PlayerHPMax[pc.PlayerId] && pc.SoloAlive() && !pc.inVent)
{
PlayerHP[pc.PlayerId] += PlayerHPReco[pc.PlayerId];
PlayerHP[pc.PlayerId] = Math.Min(PlayerHPMax[pc.PlayerId], PlayerHP[pc.PlayerId]);
notifyRoles = true;
}
if (pc.SoloAlive() && !pc.inVent)
{
var pos = Pelican.GetBlackRoomPS();
var dis = Vector2.Distance(pos, pc.Pos());
if (dis < 1.1f) PlayerRandomSpwan(pc);
}
if (BackCountdown.ContainsKey(pc.PlayerId))
{
BackCountdown[pc.PlayerId]--;
if (BackCountdown[pc.PlayerId] <= 0)
OnPlayerBack(pc);
notifyRoles = true;
}
if (NameNotify.ContainsKey(pc.PlayerId) && NameNotify[pc.PlayerId].TIMESTAMP < Utils.TimeStamp)
{
NameNotify.Remove(pc.PlayerId);
notifyRoles = true;
}
if (notifyRoles) Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
PlayerHP[pc.PlayerId] += PlayerHPReco[pc.PlayerId];
PlayerHP[pc.PlayerId] = Math.Min(PlayerHPMax[pc.PlayerId], PlayerHP[pc.PlayerId]);
notifyRoles = true;
}
if (pc.SoloAlive() && !pc.inVent)
{
var pos = Pelican.GetBlackRoomPS();
var dis = Vector2.Distance(pos, pc.Pos());
if (dis < 1.1f) PlayerRandomSpwan(pc);
}
if (BackCountdown.ContainsKey(pc.PlayerId))
{
BackCountdown[pc.PlayerId]--;
if (BackCountdown[pc.PlayerId] <= 0)
OnPlayerBack(pc);
notifyRoles = true;
}
if (NameNotify.ContainsKey(pc.PlayerId) && NameNotify[pc.PlayerId].TimeStamp < Utils.TimeStamp)
{
NameNotify.Remove(pc.PlayerId);
notifyRoles = true;
}
if (notifyRoles) Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}
}
}
+13 -21
View File
@@ -1146,29 +1146,21 @@ static class ExtendedPlayerControl
{
if (!pc.IsAlive() || pc.Data.Role.Role == RoleTypes.GuardianAngel || Penguin.IsVictim(pc)) return false;
if (pc.GetRoleTypes() == RoleTypes.Engineer) return false;
if (CopyCat.Instances.Any(x => x.CopyCatPC.PlayerId == pc.PlayerId)) return true;
if (pc.Is(CustomRoles.Nimble) || Options.EveryoneCanVent.GetBool()) return true;
if (pc.Is(CustomRoles.Bloodlust) || pc.Is(CustomRoles.Refugee)) return true;
return pc.GetCustomRole() switch
return Options.CurrentGameMode switch
{
// SoloKombat
CustomRoles.KB_Normal => true,
// FFA
CustomRoles.Killer => true,
// Move And Stop
CustomRoles.Tasker => false,
// Hot Potato
CustomRoles.Potato => false,
// Speedrun
CustomRoles.Runner => false,
// Capture The Flag
CustomRoles.CTFPlayer => false,
// Natural Disasters
CustomRoles.NDPlayer => false,
// Room Rush
CustomRoles.RRPlayer => RoomRush.VentLimit[pc.PlayerId] > 0,
CustomGameMode.SoloKombat => true,
CustomGameMode.FFA => true,
CustomGameMode.MoveAndStop => false,
CustomGameMode.HotPotato => false,
CustomGameMode.Speedrun => false,
CustomGameMode.CaptureTheFlag => false,
CustomGameMode.NaturalDisasters => false,
CustomGameMode.RoomRush => RoomRush.VentLimit[pc.PlayerId] > 0,
CustomGameMode.Standard when (CopyCat.Instances.Any(x => x.CopyCatPC.PlayerId == pc.PlayerId)) => true,
CustomGameMode.Standard when (pc.Is(CustomRoles.Nimble) || Options.EveryoneCanVent.GetBool()) => true,
CustomGameMode.Standard when (pc.Is(CustomRoles.Bloodlust) || pc.Is(CustomRoles.Refugee)) => true,
_ => Main.PlayerStates.TryGetValue(pc.PlayerId, out var state) && state.Role.CanUseImpostorVentButton(pc)
};
+21 -3
View File
@@ -140,7 +140,8 @@ public enum CustomRPC
SyncOverheat,
// Game Modes
RoomRushDataSync
RoomRushDataSync,
FFAKill
}
public enum Sounds
@@ -156,7 +157,7 @@ static class RPCHandlerPatch
{
public static readonly Dictionary<byte, int> ReportDeadBodyRPCs = [];
private static bool TrustedRpc(byte id) => (CustomRPC)id is CustomRPC.VersionCheck or CustomRPC.RequestRetryVersionCheck or CustomRPC.AntiBlackout or CustomRPC.SyncNameNotify or CustomRPC.RequestSendMessage or CustomRPC.Judge or CustomRPC.SetNiceSwapperVotes or CustomRPC.MeetingKill or CustomRPC.Guess or CustomRPC.MafiaRevenge or CustomRPC.BAU;
private static bool TrustedRpc(byte id) => (CustomRPC)id is CustomRPC.VersionCheck or CustomRPC.RequestRetryVersionCheck or CustomRPC.AntiBlackout or CustomRPC.SyncNameNotify or CustomRPC.RequestSendMessage or CustomRPC.Judge or CustomRPC.SetNiceSwapperVotes or CustomRPC.MeetingKill or CustomRPC.Guess or CustomRPC.MafiaRevenge or CustomRPC.BAU or CustomRPC.FFAKill;
public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] byte callId, [HarmonyArgument(1)] MessageReader reader)
{
@@ -1042,6 +1043,23 @@ static class RPCHandlerPatch
RoomRush.ReceiveRPC(reader);
break;
}
case CustomRPC.FFAKill:
{
if (Options.CurrentGameMode != CustomGameMode.FFA)
{
EAC.WarnHost();
EAC.Report(__instance, "FFA RPC when game mode is not FFA");
break;
}
PlayerControl killer = reader.ReadNetObject<PlayerControl>();
PlayerControl target = reader.ReadNetObject<PlayerControl>();
if (!killer.IsAlive() || !target.IsAlive() || AntiBlackout.SkipTasks || target.inMovingPlat || target.onLadder || target.inVent || MeetingHud.Instance) break;
FFAManager.OnPlayerAttack(killer, target);
break;
}
}
}
}
@@ -1176,7 +1194,7 @@ internal static class RPC
while (PlayerControl.LocalPlayer == null) yield return null;
if (AmongUsClient.Instance.AmHost)
{
Utils.SendRPC(CustomRPC.VersionCheck, Main.PlayerVersion, $"{ThisAssembly.Git.Commit}({ThisAssembly.Git.Branch})", Main.ForkId);
Utils.SendRPC(CustomRPC.VersionCheck, Main.PluginVersion, $"{ThisAssembly.Git.Commit}({ThisAssembly.Git.Branch})", Main.ForkId);
}
else
{
+4 -4
View File
@@ -169,14 +169,14 @@ internal static class ChatCommands
new(["deletevip", "удвип", "убрвип", "удалитьвип", "убратьвип"], "{id}", GetString("CommandDescription.DeleteVIP"), Command.UsageLevels.Host, Command.UsageTimes.Always, DeleteVIPCommand, true, [GetString("CommandArgs.DeleteVIP.Id")]),
new(["assume", "предположить"], "{id} {number}", GetString("CommandDescription.Assume"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, AssumeCommand, true, [GetString("CommandArgs.Assume.Id"), GetString("CommandArgs.Assume.Number")]),
new(["note", "заметка"], "{action} [?]", GetString("CommandDescription.Note"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, NoteCommand, true, [GetString("CommandArgs.Note.Action"), GetString("CommandArgs.Note.UnknownValue")]),
new(["os", "optionset", "шансроли", "опция"], "{chance} {role}", GetString("CommandDescription.OS"), Command.UsageLevels.Host, Command.UsageTimes.InLobby, OSCommand, true, [GetString("CommandArgs.OS.Chance"), GetString("CommandArgs.OS.Role")]),
new(["os", "optionset", "шансроли"], "{chance} {role}", GetString("CommandDescription.OS"), Command.UsageLevels.Host, Command.UsageTimes.InLobby, OSCommand, true, [GetString("CommandArgs.OS.Chance"), GetString("CommandArgs.OS.Role")]),
new(["negotiation", "neg", "наказание"], "{number}", GetString("CommandDescription.Negotiation"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, NegotiationCommand, true, [GetString("CommandArgs.Negotiation.Number")]),
new(["mute", "мут"], "{id} [duration]", GetString("CommandDescription.Mute"), Command.UsageLevels.HostOrModerator, Command.UsageTimes.AfterDeathOrLobby, MuteCommand, true, [GetString("CommandArgs.Mute.Id"), GetString("CommandArgs.Mute.Duration")]),
new(["unmute", "размут"], "{id}", GetString("CommandDescription.Unmute"), Command.UsageLevels.Host, Command.UsageTimes.Always, UnmuteCommand, true, [GetString("CommandArgs.Unmute.Id")]),
new(["draftstart", "ds", "драфтстарт"], "", GetString("CommandDescription.DraftStart"), Command.UsageLevels.Host, Command.UsageTimes.InLobby, DraftStartCommand, true),
new(["draft", "драфт"], "{number}", GetString("CommandDescription.Draft"), Command.UsageLevels.Everyone, Command.UsageTimes.InLobby, DraftCommand, true, [GetString("CommandArgs.Draft.Number")]),
new(["readycheck", "rc", "готов"], "", GetString("CommandDescription.ReadyCheck"), Command.UsageLevels.Host, Command.UsageTimes.InLobby, ReadyCheckCommand, true),
new(["ready", "готов", "г"], "", GetString("CommandDescription.Ready"), Command.UsageLevels.Everyone, Command.UsageTimes.InLobby, ReadyCommand, true),
new(["readycheck", "rc", "проверитьготовность"], "", GetString("CommandDescription.ReadyCheck"), Command.UsageLevels.Host, Command.UsageTimes.InLobby, ReadyCheckCommand, true),
new(["ready", "готов"], "", GetString("CommandDescription.Ready"), Command.UsageLevels.Everyone, Command.UsageTimes.InLobby, ReadyCommand, true),
// Commands with action handled elsewhere
new(["shoot", "guess", "bet", "bt", "st", "угадать", "бт"], "{id} {role}", GetString("CommandDescription.Guess"), Command.UsageLevels.Everyone, Command.UsageTimes.InMeeting, (_, _, _, _) => { }, true, [GetString("CommandArgs.Guess.Id"), GetString("CommandArgs.Guess.Role")]),
@@ -2208,4 +2208,4 @@ internal class RpcSendChatPatch
__result = true;
return false;
}
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ static class GameEndChecker
{
if (!AmongUsClient.Instance.AmHost) return true;
if (Predicate == null || ShouldNotCheck) return false;
if (Predicate == null || ShouldNotCheck || Main.HasJustStarted) return false;
if (Options.NoGameEnd.GetBool() && WinnerTeam is not CustomWinner.Draw and not CustomWinner.Error) return false;
+22 -7
View File
@@ -782,12 +782,14 @@ static class IntroCutsceneDestroyPatch
// Set roleAssigned as false for overriding roles for modded players
// for vanilla clients we use "Data.Disconnected"
Main.AllPlayerControls.Do(x => x.roleAssigned = false);
var aapc = Main.AllAlivePlayerControls;
if (AmongUsClient.Instance.AmHost)
{
if (Main.NormalOptions.MapId != 4)
{
foreach (var pc in Main.AllAlivePlayerControls)
foreach (var pc in aapc)
{
pc.SyncSettings();
pc.RpcResetAbilityCooldown();
@@ -839,7 +841,7 @@ static class IntroCutsceneDestroyPatch
LateTask.New(() =>
{
foreach (var pc in Main.AllAlivePlayerControls)
foreach (var pc in aapc)
{
if (pc.Is(CustomRoles.GM)) continue;
string petId = pet == "pet_RANDOM_FOR_EVERYONE" ? pets[r.Next(0, pets.Length - 1)] : pet;
@@ -856,7 +858,7 @@ static class IntroCutsceneDestroyPatch
try
{
lp.Notify(GetString("GLHF"), 2f);
foreach (PlayerControl pc in Main.AllAlivePlayerControls)
foreach (PlayerControl pc in aapc)
{
if (pc.IsHost()) continue; // Skip the host
try
@@ -885,7 +887,7 @@ static class IntroCutsceneDestroyPatch
if (Options.UseUnshiftTrigger.GetBool() || Main.PlayerStates.Values.Any(x => x.MainRole.AlwaysUsesUnshift()))
{
LateTask.New(() => Main.AllAlivePlayerControls.Do(x => x.CheckAndSetUnshiftState()), 2f, "UnshiftTrigger SS");
LateTask.New(() => aapc.Do(x => x.CheckAndSetUnshiftState()), 2f, "UnshiftTrigger SS");
}
if (Main.GM.Value)
@@ -906,7 +908,7 @@ static class IntroCutsceneDestroyPatch
5 => new RandomSpawn.FungleSpawnMap(),
_ => null
};
if (map != null && AmongUsClient.Instance.AmHost) Main.AllAlivePlayerControls.Do(map.RandomTeleport);
if (map != null && AmongUsClient.Instance.AmHost) aapc.Do(map.RandomTeleport);
}
if (lp.HasDesyncRole())
@@ -927,10 +929,10 @@ static class IntroCutsceneDestroyPatch
if (AFKDetector.ActivateOnStart.GetBool())
{
LateTask.New(() => Main.AllAlivePlayerControls.Do(AFKDetector.RecordPosition), 1f, log: false);
LateTask.New(() => aapc.Do(AFKDetector.RecordPosition), 1f, log: false);
}
LateTask.New(() => Utils.NotifyRoles(NoCache: true), 3f, log: false);
LateTask.New(() => Main.Instance.StartCoroutine(NotifyEveryone()), 3f, log: false);
}
else
{
@@ -941,5 +943,18 @@ static class IntroCutsceneDestroyPatch
}
Logger.Info("OnDestroy", "IntroCutscene");
System.Collections.IEnumerator NotifyEveryone()
{
int count = 0;
foreach (var seer in aapc)
{
foreach (var target in aapc)
{
Utils.NotifyRoles(SpecifySeer: seer, SpecifyTarget: target);
if (count++ % 2 == 0) yield return null;
}
}
}
}
}
+4 -1
View File
@@ -1,4 +1,5 @@
using System.Linq;
using System.Threading.Tasks;
using HarmonyLib;
using InnerNet;
using TMPro;
@@ -37,9 +38,11 @@ public static class HostInfoPanelSetUpPatch
{
if (HostText == null) HostText = __instance.content.transform.FindChild("Name").GetComponent<TextMeshPro>();
var icon = Translator.GetString("Icon");
var name = GameData.Instance?.GetHost()?.PlayerName?.RemoveHtmlTags()?.Split('\n').FirstOrDefault(x => x.Contains(icon))?.Split(icon)[^1] ?? string.Empty;
if (name == string.Empty) return;
var text = AmongUsClient.Instance.AmHost
? Translator.GetString("YouAreHostSuffix")
: GameData.Instance.GetHost().PlayerName.RemoveHtmlTags().Split('\n').First(x => x.Contains(icon)).Split(icon)[^1];
: name;
HostText.text = Utils.ColorString(Palette.PlayerColors[__instance.player.ColorId], text);
}
}
+6 -3
View File
@@ -12,6 +12,7 @@ using HarmonyLib;
using TMPro;
using UnityEngine;
using static EHR.Translator;
// ReSharper disable AccessToModifiedClosure
namespace EHR.Patches;
@@ -733,7 +734,7 @@ static class MeetingHudStartPatch
if (msgToSend.Count > 0)
{
LateTask.New(() => { msgToSend.Do(x => Utils.SendMessage(x.Message, x.TargetID, x.Title)); }, 6f, "Meeting Start Notify");
LateTask.New(() => msgToSend.Do(x => Utils.SendMessage(x.Message, x.TargetID, x.Title)), 6f, "Meeting Start Notify");
}
Main.CyberStarDead.Clear();
@@ -829,7 +830,9 @@ static class MeetingHudStartPatch
var suffix = Main.PlayerStates[seer.PlayerId].Role.GetSuffix(seer, target, meeting: true);
if (suffix.Length > 0)
{
if (roleTextMeeting.text.Length > 0) roleTextMeeting.text += "\n";
if (roleTextMeeting.enabled) roleTextMeeting.text += "\n";
else roleTextMeeting.text = string.Empty;
roleTextMeeting.text += suffix;
roleTextMeeting.enabled = true;
}
@@ -838,7 +841,7 @@ static class MeetingHudStartPatch
var playerLevel = pva.transform.Find("PlayerLevel");
var levelDisplay = Object.Instantiate(playerLevel, pva.transform);
levelDisplay.localPosition = new(-1.21f, -0.15f, playerLevel.transform.localPosition.z);
levelDisplay.transform.SetSiblingIndex(pva.transform.Find("PlayerLevel").GetSiblingIndex() + 1);
levelDisplay.transform.SetSiblingIndex(playerLevel.GetSiblingIndex() + 1);
levelDisplay.gameObject.name = "PlayerId";
levelDisplay.GetComponent<SpriteRenderer>().color = Palette.Purple;
var idLabel = levelDisplay.transform.Find("LevelLabel");
+1 -1
View File
@@ -131,7 +131,7 @@ static class EndGamePatch
}
[HarmonyPatch(typeof(EndGameManager), nameof(EndGameManager.SetEverythingUp))]
class SetEverythingUpPatch
static class SetEverythingUpPatch
{
public static string LastWinsText = string.Empty;
public static string LastWinsReason = string.Empty;
+8 -1
View File
@@ -91,12 +91,19 @@ static class CmdCheckMurderPatch
{
__instance.CheckMurder(target);
}
else
else if (Options.CurrentGameMode != CustomGameMode.FFA)
{
MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(__instance.NetId, (byte)RpcCalls.CheckMurder, SendOption.Reliable);
messageWriter.WriteNetObject(target);
AmongUsClient.Instance.FinishRpcImmediately(messageWriter);
}
else
{
MessageWriter writer = AmongUsClient.Instance.StartRpc(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.FFAKill);
writer.WriteNetObject(__instance);
writer.WriteNetObject(target);
writer.EndMessage();
}
return false;
}
+10 -7
View File
@@ -32,7 +32,8 @@
# Installation
Follow the manual guide on our website: [EHR](https://sites.google.com/view/ehr-au)
- Follow the manual guide on our website: [EHR](https://sites.google.com/view/ehr-au)
- Or watch the tutorial by AnonWorks: [YouTube](https://youtu.be/AOLqWCbbrHM?si=nRRsNgz5-Z8XvFrs)
<br>
@@ -58,7 +59,7 @@ You can also report bugs and suggest features in the #bug-reports and #suggestio
### Visit this website to find role descriptions: [Role description finder](https://gurge44.pythonanywhere.com/)
#### Over 400 roles and add-ons, 9 custom game modes, and a custom team assigner application, making EHR the most extensive mod for Among Us. And it's all free, and only the host needs to have the mod installed for the entire lobby to play!
#### Over 400 roles and add-ons, 10 custom game modes, and a custom team assigner application, making EHR the most extensive mod for Among Us. And it's all free, and only the host needs to have the mod installed for the entire lobby to play!
## List of Roles <span style="font-size: 12px;">(excluding Hide and Seek roles and vanilla roles)</span>
@@ -212,11 +213,13 @@ Source language: English
#### Translation progress
![es-ES translation](https://img.shields.io/badge/dynamic/json?color=blue&label=es-ES&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27es-ES%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)
![ko translation](https://img.shields.io/badge/dynamic/json?color=blue&label=ko&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27ko%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)
![pt-BR translation](https://img.shields.io/badge/dynamic/json?color=blue&label=pt-BR&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27pt-BR%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)
![ru translation](https://img.shields.io/badge/dynamic/json?color=blue&label=ru&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27ru%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)
![zh-CN translation](https://img.shields.io/badge/dynamic/json?color=blue&label=zh-CN&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27zh-CN%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)
[![es-ES translation](https://img.shields.io/badge/dynamic/json?color=blue&label=es-ES&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27es-ES%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)](https://crowdin.com/project/ehr)
[![fr translation](https://img.shields.io/badge/dynamic/json?color=blue&label=fr&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27fr%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)](https://crowdin.com/project/ehr)
[![ko translation](https://img.shields.io/badge/dynamic/json?color=blue&label=ko&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27ko%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)](https://crowdin.com/project/ehr)
[![pt-BR translation](https://img.shields.io/badge/dynamic/json?color=blue&label=pt-BR&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27pt-BR%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)](https://crowdin.com/project/ehr)
[![ru translation](https://img.shields.io/badge/dynamic/json?color=blue&label=ru&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27ru%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)](https://crowdin.com/project/ehr)
[![zh-CN translation](https://img.shields.io/badge/dynamic/json?color=blue&label=zh-CN&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27zh-CN%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)](https://crowdin.com/project/ehr)
[![zh-TW translation](https://img.shields.io/badge/dynamic/json?color=blue&label=zh-TW&style=for-the-badge&logo=crowdin&query=%24.progress[?(@.data.languageId==%27zh-TW%27)].data.translationProgress&url=https%3A%2F%2Fbadges.awesome-crowdin.com%2Fstats-15788979-676754.json)](https://crowdin.com/project/ehr)
[![Graph](https://badges.awesome-crowdin.com/translation-15788979-676754.png)](https://crowdin.com/project/ehr)
+3 -1
View File
@@ -2125,7 +2125,7 @@
"killAttacker": "On Kill Attempt, Kill The Attacker",
"CrewpostorLungeKill": "Lunge on Kills",
"CrewpostorKillAfterTask": "Amount of Task Completions Required to make 1 Kill",
"SpeedrunnerHasXTasksLeft": "\n<color=#800080>{0}</color> is the <color=#800080>Speedrunner</color>!\\nThey have {1} task(s) left.",
"SpeedrunnerHasXTasksLeft": "<size=80%><#ffff44>{0} is the <color=#800080>Speedrunner</color>! They have <color=#800080>{1}</color> task(s) left.</color></size>",
"SpeedrunnerSpeed": "<color=#800080>Speedrunner</color> Speed",
"Message.SetToSeconds": "Set to <b>{0}</b> seconds.",
"Message.MessageWaitHelp": "Specify the first argument in seconds.",
@@ -3456,6 +3456,7 @@
"EchoInfo": "Make Players Die Where You Want",
"EchoInfoLong": "(Impostors):\\nAs the Echo, you can shapeshift into a player to swap locations and skins with them. During the shapeshift, your target cannot move. When you unshift, the swap reverts and the target dies exactly where you were.\\n\\nYou cannot kill normally.",
"EchoShapeshiftButtonText": "Control",
"Echo.DisableVentingWhileControlling": "Disable Venting While Controlling",
"NiceHacker": "Hacker",
"NiceHackerInfo": "Watch Admin Table from anywhere",
"NiceHackerInfoLong": "(Crewmates):\\nThis role is different for modded clients and vanilla clients.\\n\\nAs a modded client, whenever you open the map (and have ability uses left), the admin map will show up. You may be able to move while having the map open depending on settings.\\n\\nAs a vanilla client, you may enter a vent/pet to get a notification saying how many people are in different rooms on the ship (essentially admin map).",
@@ -4436,6 +4437,7 @@
"Augmenter": "Augmenter",
"AugmenterInfo": "Choose Who You Want to Kill",
"AugmenterInfoLong": "(Impostors):\\nAs the Augmenter, you can shapeshift into someone to make them your target. The next time you attempt to kill someone, they will be swapped with your target and you will kill your target instead.",
"AugmenterFail": "<#ffff44>Your attempt of murdering your target failed,\\nso you've murdered your original target instead.</color>",
"Inquirer": "Inquirer",
"InquirerInfo": "Check Players' Roles",
"InquirerInfoLong": "(Crewmates):\\nAs the Inquirer, you can check if a player has a specific role by typing /check {player id} {role}. There's a fail chance.",
+11 -9
View File
@@ -6,6 +6,8 @@ namespace EHR.Crewmate
{
internal class Beacon : RoleBase
{
private static List<PlayerControl> Beacons = [];
private static OptionItem VisionIncrease;
private static OptionItem Radius;
private static List<byte> AffectedPlayers = [];
@@ -29,6 +31,7 @@ namespace EHR.Crewmate
public override void Init()
{
Beacons = [];
AffectedPlayers = [];
LastChange = [];
On = false;
@@ -37,34 +40,33 @@ namespace EHR.Crewmate
public override void Add(byte playerId)
{
On = true;
Beacons.Add(playerId.GetPlayer());
}
public static bool IsAffectedPlayer(byte id) => Utils.IsActive(SystemTypes.Electrical) && AffectedPlayers.Contains(id);
private int Count;
public override void OnCheckPlayerPosition(PlayerControl pc)
{
if (!GameStates.IsInTask || pc == null) return;
if (Count++ < 10) return;
Count = 0;
long now = Utils.TimeStamp;
if (LastChange.TryGetValue(pc.PlayerId, out var ts) && ts == now) return;
bool isBeaconNearby = Main.AllAlivePlayerControls.Any(x => x.Is(CustomRoles.Beacon) && Vector2.Distance(x.Pos(), pc.Pos()) <= Radius.GetFloat());
bool isAffectedPlayer = AffectedPlayers.Contains(pc.PlayerId);
var pos = pc.Pos();
var radius = Radius.GetFloat();
bool beaconNearby = Beacons.Any(x => Vector2.Distance(x.Pos(), pos) <= radius);
bool affectedPlayer = AffectedPlayers.Contains(pc.PlayerId);
switch (isAffectedPlayer)
switch (affectedPlayer)
{
case true when !isBeaconNearby:
case true when !beaconNearby:
{
AffectedPlayers.Remove(pc.PlayerId);
if (Utils.IsActive(SystemTypes.Electrical)) pc.MarkDirtySettings();
LastChange[pc.PlayerId] = now;
break;
}
case false when isBeaconNearby:
case false when beaconNearby:
{
AffectedPlayers.Add(pc.PlayerId);
if (Utils.IsActive(SystemTypes.Electrical)) pc.MarkDirtySettings();
+24 -8
View File
@@ -13,19 +13,23 @@ namespace EHR.Crewmate
public static OptionItem SpeedrunnerSpeed;
public override bool IsEnable => On;
private static PlayerControl SpeedrunnerPC;
public override void Add(byte playerId)
{
On = true;
SpeedrunnerPC = playerId.GetPlayer();
}
public override void Init()
{
On = false;
SpeedrunnerPC = null;
}
public override void SetupCustomOption()
{
SetupRoleOptions(9170, TabGroup.CrewmateRoles, CustomRoles.Speedrunner);
SetupSingleRoleOptions(9170, TabGroup.CrewmateRoles, CustomRoles.Speedrunner, zeroOne: true);
SpeedrunnerNotifyKillers = new BooleanOptionItem(9178, "SpeedrunnerNotifyKillers", true, TabGroup.CrewmateRoles)
.SetParent(CustomRoleSpawnChances[CustomRoles.Speedrunner]);
SpeedrunnerNotifyAtXTasksLeft = new IntegerOptionItem(9179, "SpeedrunnerNotifyAtXTasksLeft", new(0, 90, 1), 3, TabGroup.CrewmateRoles)
@@ -46,7 +50,6 @@ namespace EHR.Crewmate
if (!player.IsAlive()) return;
var completedTasks = CompletedTasksCount + 1;
int remainingTasks = AllTasksCount - completedTasks;
if (completedTasks >= AllTasksCount)
{
Logger.Info("Speedrunner finished tasks", "Speedrunner");
@@ -60,16 +63,29 @@ namespace EHR.Crewmate
}
else if (completedTasks >= SpeedrunnerNotifyAtXTasksLeft.GetInt() && SpeedrunnerNotifyKillers.GetBool())
{
string speedrunnerName = player.GetRealName().RemoveHtmlTags();
string notifyString = Translator.GetString("SpeedrunnerHasXTasksLeft");
foreach (var pc in Main.AllAlivePlayerControls)
LateTask.New(() =>
{
if (!pc.Is(Team.Crewmate))
foreach (var pc in Main.AllAlivePlayerControls)
{
pc.Notify(string.Format(notifyString, speedrunnerName, remainingTasks));
if (!pc.Is(Team.Crewmate))
{
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}
}
}
}, 0.1f, log: false);
}
}
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
if (seer.PlayerId != target.PlayerId || seer.Is(Team.Crewmate) || meeting || SpeedrunnerPC == null) return string.Empty;
TaskState ts = SpeedrunnerPC.GetTaskState();
if (ts.CompletedTasksCount < SpeedrunnerNotifyAtXTasksLeft.GetInt() || !SpeedrunnerNotifyKillers.GetBool()) return string.Empty;
string speedrunnerName = SpeedrunnerPC.PlayerId.ColoredPlayerName();
string notifyString = Translator.GetString("SpeedrunnerHasXTasksLeft");
return string.Format(notifyString, speedrunnerName, ts.RemainingTasksCount);
}
}
}
+5
View File
@@ -108,8 +108,13 @@ namespace EHR.Impostor
}
}
private int Count;
public override void OnFixedUpdate(PlayerControl pc)
{
if (Count++ < 3) return;
Count = 0;
var abyssbringer = AbyssbringerId.GetPlayer();
int count = BlackHoles.Count;
for (int i = 0; i < count; i++)
+7 -7
View File
@@ -279,26 +279,26 @@ internal class AntiAdminer : RoleBase
}
}
var isChange = false;
var change = false;
isChange |= IsAdminWatch != Admin;
change |= IsAdminWatch != Admin;
IsAdminWatch = Admin;
isChange |= IsVitalWatch != Vital;
change |= IsVitalWatch != Vital;
IsVitalWatch = Vital;
isChange |= IsDoorLogWatch != DoorLog;
change |= IsDoorLogWatch != DoorLog;
IsDoorLogWatch = DoorLog;
if (IsMonitor ? Monitor.CanCheckCamera.GetBool() : CanCheckCamera.GetBool())
{
isChange |= IsCameraWatch != Camera;
change |= IsCameraWatch != Camera;
IsCameraWatch = Camera;
}
if (notify || isChange)
if (notify || change)
{
Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
}
if (isChange)
if (change)
{
FixedUpdatePatch.DoPostfix(player);
}
+5 -1
View File
@@ -33,7 +33,11 @@
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
var newTarget = Utils.GetPlayerById(Target);
if (newTarget == null || !newTarget.IsAlive() || !killer.RpcCheckAndMurder(newTarget, check: true)) return true;
if (newTarget == null || !newTarget.IsAlive() || !killer.RpcCheckAndMurder(newTarget, check: true))
{
killer.Notify(string.Format(Translator.GetString("AugmenterFail"), Target.ColoredPlayerName()));
return true;
}
var pos = newTarget.Pos();
newTarget.TP(target);
+24
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using AmongUs.GameOptions;
namespace EHR.Impostor
{
@@ -9,6 +10,7 @@ namespace EHR.Impostor
private static OptionItem ShapeshiftCooldown;
private static OptionItem ShapeshiftDuration;
private static OptionItem DisableVentingWhileControlling;
public PlayerControl EchoPC;
public override bool IsEnable => On;
@@ -26,6 +28,8 @@ namespace EHR.Impostor
ShapeshiftDuration = new FloatOptionItem(++id, "ShapeshiftDuration", new(0f, 180f, 0.5f), 10f, tab)
.SetParent(parent)
.SetValueFormat(OptionFormat.Seconds);
DisableVentingWhileControlling = new BooleanOptionItem(++id, "Echo.DisableVentingWhileControlling", true, tab)
.SetParent(parent);
}
public override void Init()
@@ -50,6 +54,18 @@ namespace EHR.Impostor
public override bool CanUseKillButton(PlayerControl pc) => false;
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
{
AURoleOptions.ShapeshifterCooldown = ShapeshiftCooldown.GetFloat();
AURoleOptions.ShapeshifterDuration = ShapeshiftDuration.GetFloat();
AURoleOptions.ShapeshifterLeaveSkin = false;
}
public override bool CanUseImpostorVentButton(PlayerControl pc)
{
return !DisableVentingWhileControlling.GetBool() || !pc.IsShifted();
}
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
{
if (shapeshifting)
@@ -100,5 +116,13 @@ namespace EHR.Impostor
target.Suicide(PlayerState.DeathReason.Kill, EchoPC);
}, 0.2f, log: false);
}
public override void OnReportDeadBody()
{
if (!EchoPC.IsShifted()) return;
var ssTarget = ((byte)EchoPC.shapeshiftTargetPlayerId).GetPlayer();
if (ssTarget == null || !ssTarget.IsAlive()) return;
ssTarget.Suicide(PlayerState.DeathReason.Kill, EchoPC);
}
}
}
+3 -6
View File
@@ -365,13 +365,10 @@ namespace EHR.Impostor
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
if (seer == null || seer.PlayerId != target.PlayerId) return string.Empty;
if (Main.PlayerStates.TryGetValue(seer.PlayerId, out var state) && state.Role is Penguin pg && pg.AbductVictim != null)
{
return $"\u21b9 {(int)(pg.AbductTimer + 1f)}s";
}
if (seer == null || seer.PlayerId != target.PlayerId || (seer.IsModClient() && !hud)) return string.Empty;
if (!Main.PlayerStates.TryGetValue(seer.PlayerId, out var state) || state.Role is not Penguin pg || pg.AbductVictim == null) return string.Empty;
return string.Empty;
return $"\u21b9 {(int)(pg.AbductTimer + 1f)}s";
}
}
}
+2 -2
View File
@@ -164,7 +164,7 @@ public class Swooper : RoleBase
if (!player.IsModClient())
{
var cooldown = lastTime + (long)Cooldown - now;
if ((int)cooldown != CD) player.Notify(string.Format(GetString("CDPT"), cooldown + 1), 1.1f);
if ((int)cooldown != CD) player.Notify(string.Format(GetString("CDPT"), cooldown + 1), 1.1f, overrideAll: true);
CD = (int)cooldown;
}
@@ -195,7 +195,7 @@ public class Swooper : RoleBase
LateTask.New(() => { player.TP(pos); }, 0.5f, log: false);
break;
case <= 10 when !player.IsModClient():
player.Notify(string.Format(GetString("SwooperInvisStateCountdown"), remainTime + 1));
player.Notify(string.Format(GetString("SwooperInvisStateCountdown"), remainTime + 1), overrideAll: true);
break;
}
+2 -2
View File
@@ -118,7 +118,7 @@ public class Werewolf : RoleBase
if (!player.IsModClient())
{
var cooldown = lastTime + (long)RampageCD.GetFloat() - now;
if ((int)cooldown != CD) player.Notify(string.Format(GetString("CDPT"), cooldown + 1), 1.1f);
if ((int)cooldown != CD) player.Notify(string.Format(GetString("CDPT"), cooldown + 1), 1.1f, overrideAll: true);
CD = (int)cooldown;
}
@@ -149,7 +149,7 @@ public class Werewolf : RoleBase
refresh = true;
break;
case <= 10 when !player.IsModClient():
player.Notify(string.Format(GetString("WWRampageCountdown"), remainTime + 1));
player.Notify(string.Format(GetString("WWRampageCountdown"), remainTime + 1), overrideAll: true);
break;
}