a ton of fixes

This commit is contained in:
Gurge44 committed 2025-12-22 19:08:38 +01:00
1 parent 5eb5cb31fe
commit 333d00d22b
32 files changed
+170 -109

No files matched your search

+1 -1
View File
@@ -379,7 +379,7 @@ public static class CaptureTheFlag
try
{
var pc1 = id1.GetPlayer();
if (pc1 == null) continue;
if (pc1 == null || pc1.AmOwner) continue;
var sender = CustomRpcSender.Create("CTF Set Teams");
sender.StartMessage(pc1.OwnerId);
+2
View File
@@ -196,6 +196,7 @@ public class Main : BasePlugin
public static ConfigEntry<bool> SwitchVanilla { get; private set; }
public static ConfigEntry<bool> GodMode { get; private set; }
public static ConfigEntry<bool> DarkTheme { get; private set; }
public static ConfigEntry<bool> DarkThemeForMeetingUI { get; private set; }
public static ConfigEntry<bool> HorseMode { get; private set; }
public static ConfigEntry<bool> LongMode { get; private set; }
public static ConfigEntry<bool> ShowPlayerInfoInLobby { get; private set; }
@@ -298,6 +299,7 @@ public class Main : BasePlugin
SwitchVanilla = Config.Bind("Client Options", "SwitchVanilla", false);
GodMode = Config.Bind("Client Options", "GodMode", false);
DarkTheme = Config.Bind("Client Options", "DarkTheme", true);
DarkThemeForMeetingUI = Config.Bind("Client Options", "DarkThemeForMeetingUI", false);
HorseMode = Config.Bind("Client Options", "HorseMode", false);
LongMode = Config.Bind("Client Options", "LongMode", false);
ShowPlayerInfoInLobby = Config.Bind("Client Options", "ShowPlayerInfoInLobby", false);
+2 -2
View File
@@ -950,7 +950,7 @@ internal static class CustomRolesHelper
public static bool NeedUpdateOnLights(this CustomRoles role)
{
return !role.UsesPetInsteadOfKill() && role is CustomRoles.Lighter or CustomRoles.Wiper;
return role is CustomRoles.Wiper;
}
public static bool IsBetrayalAddon(this CustomRoles role)
@@ -1213,7 +1213,7 @@ internal static class CustomRolesHelper
CustomRoles.Beartrap when pc.Is(CustomRoles.GuardianAngelEHR) => false,
CustomRoles.Reach when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => false,
CustomRoles.Magnet when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) => false,
CustomRoles.Haste when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) || !pc.CanUseImpostorVentButton() => false,
CustomRoles.Haste when pc.GetRoleTypes() is not (RoleTypes.Impostor or RoleTypes.Phantom or RoleTypes.Shapeshifter or RoleTypes.Viper) || !pc.CanUseImpostorVentButton() || pc.Is(CustomRoles.Glitch) => false,
CustomRoles.Diseased when pc.Is(CustomRoles.Antidote) => false,
CustomRoles.Antidote when pc.Is(CustomRoles.Diseased) => false,
CustomRoles.Flashman or CustomRoles.Giant when pc.GetCustomRole() is CustomRoles.Swooper or CustomRoles.Wraith or CustomRoles.Chameleon or CustomRoles.Alchemist or CustomRoles.Ankylosaurus => false,
+18 -1
View File
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using Hazel;
using UnityEngine;
namespace EHR.Modules;
@@ -78,8 +80,10 @@ internal static class CustomTeamManager
UpdateEnabledTeams();
if (EnabledCustomTeams.Count == 0) return;
PlayerControl[] aapc = Main.AllAlivePlayerControls;
CustomTeamPlayerIds = Main.PlayerStates
.IntersectBy(Main.AllAlivePlayerControls.Select(x => x.PlayerId), x => x.Key)
.IntersectBy(aapc.Select(x => x.PlayerId), x => x.Key)
.GroupBy(x => EnabledCustomTeams.FirstOrDefault(t => t.TeamMembers.Contains(x.Value.MainRole)), x => x.Key)
.Where(x => x.Key != null)
.ToDictionary(x => x.Key, x => TakeAsManyAsSet(x, x.Key));
@@ -107,6 +111,19 @@ internal static class CustomTeamManager
}
}
var imps = aapc.Where(x => x.IsImpostor() && !CustomTeamPlayerIds.Values.Any(l => l.Contains(x.PlayerId))).ToList();
if (imps.Count > 0)
{
CustomTeamPlayerIds.Values.Flatten().ToValidPlayers().DoIf(x => x != null && x.IsImpostor() && !IsSettingEnabledForPlayerTeam(x.PlayerId, CTAOption.WinWithOriginalTeam), ctp =>
{
imps.ForEach(imp => ctp.RpcSetRoleDesync(RoleTypes.Crewmate, imp.OwnerId, setRoleMap: true));
var sender = CustomRpcSender.Create("CustomTeamManager_SetImpostorDesync", SendOption.Reliable);
imps.ForEach(imp => sender.RpcSetRole(imp, RoleTypes.Crewmate, ctp.OwnerId, changeRoleMap: true));
sender.SendMessage();
});
}
return;
static HashSet<byte> TakeAsManyAsSet(IEnumerable<byte> playerIds, CustomTeam customTeam)
+14 -1
View File
@@ -377,7 +377,7 @@ internal static class ExtendedPlayerControl
}
// If you use vanilla RpcSetRole, it will block further SetRole calls until the next game starts.
public static void RpcSetRoleGlobal(this PlayerControl player, RoleTypes roleTypes)
public static void RpcSetRoleGlobal(this PlayerControl player, RoleTypes roleTypes, bool setRoleMap = false)
{
if (!AmongUsClient.Instance.AmHost) return;
if (AmongUsClient.Instance.AmClient) player.StartCoroutine(player.CoSetRole(roleTypes, true));
@@ -386,6 +386,19 @@ internal static class ExtendedPlayerControl
writer.Write(true);
AmongUsClient.Instance.FinishRpcImmediately(writer);
Logger.Info($" {player.GetNameWithRole()} => {roleTypes}", "RpcSetRoleGlobal");
if (setRoleMap)
{
foreach ((byte seerID, byte targetID) in StartGameHostPatch.RpcSetRoleReplacer.RoleMap.Keys.ToArray())
{
if (targetID == player.PlayerId)
{
var value = StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(seerID, targetID)];
value.RoleType = roleTypes;
StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(seerID, targetID)] = value;
}
}
}
}
public static void RpcSetRoleDesync(this PlayerControl player, RoleTypes role, int clientId, bool setRoleMap = false)
+7
View File
@@ -127,6 +127,13 @@ public static class FixedUpdateCaller
}
catch { }
try
{
if (amongUsClient.AmHost && GameStates.InGame && !GameStates.IsEnded)
FixedUpdatePatch.LoversSuicide();
}
catch (Exception e) { Utils.ThrowException(e); }
bool lobby = GameStates.IsLobby;
if (lobby || (Main.IntroDestroyed && GameStates.InGame && !GameStates.IsMeeting && !ExileController.Instance && !AntiBlackout.SkipTasks))
@@ -59,7 +59,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
{
GameOptionsSender allSender = AllSenders[index];
if (allSender is PlayerGameOptionsSender { IsDirty: false } sender && sender.player.IsAlive() && (sender.player.HasDesyncRole() || sender.player.GetCustomRole() == CustomRoles.Transporter || sender.player.Is(CustomRoles.Torch) || sender.player.Is(CustomRoles.Mare) || sender.player.Is(CustomRoles.Sleep) || Beacon.IsAffectedPlayer(sender.player.PlayerId)))
if (allSender is PlayerGameOptionsSender { IsDirty: false } sender && sender.player.IsAlive() && (sender.player.HasDesyncRole() || sender.player.GetCustomRole() is CustomRoles.Transporter or CustomRoles.Lighter || sender.player.Is(CustomRoles.Torch) || sender.player.Is(CustomRoles.Mare) || sender.player.Is(CustomRoles.Sleep) || Beacon.IsAffectedPlayer(sender.player.PlayerId)))
sender.SetDirty();
}
}
+1 -1
View File
@@ -114,7 +114,7 @@ public class PlayerState(byte playerId)
public void SetMainRole(CustomRoles role)
{
try { Utils.RemovePlayerFromPreviousRoleData(Player); }
try { Role.Remove(PlayerId); }
catch (Exception e) { Utils.ThrowException(e); }
if (Main.IntroDestroyed && (RoleHistory.Count == 0 || RoleHistory[^1] != MainRole))
+1 -1
View File
@@ -999,7 +999,7 @@ public static class GuessManager
if (selectedButton != button)
{
selectedButton = button;
buttons.ForEach(x => x.GetComponent<SpriteRenderer>().color = x == selectedButton ? Utils.GetRoleColor(PlayerControl.LocalPlayer.GetCustomRole()) : Main.DarkTheme.Value ? new Color(0.1f, 0.1f, 0.1f) : Color.white);
buttons.ForEach(x => x.GetComponent<SpriteRenderer>().color = x == selectedButton ? Utils.GetRoleColor(PlayerControl.LocalPlayer.GetCustomRole()) : Main.DarkThemeForMeetingUI.Value ? new Color(0.1f, 0.1f, 0.1f) : Color.white);
}
else
{
+7 -3
View File
@@ -146,11 +146,13 @@ public enum LobbyStatus
Closed
}
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.ExitGame))]
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.DisconnectInternal))]
internal static class ExitGamePatch
{
public static void Prefix()
public static void Prefix(InnerNetClient __instance)
{
if (__instance is not AmongUsClient) return;
Logger.Msg("Exiting game", "ExitGamePatch.Prefix");
Main.RealOptionsData?.Restore(GameOptionsManager.Instance.CurrentGameOptions);
@@ -162,8 +164,10 @@ internal static class ExitGamePatch
}
}
public static void Postfix()
public static void Postfix(InnerNetClient __instance)
{
if (__instance is not AmongUsClient) return;
LobbySharingAPI.NotifyLobbyStatusChanged(LobbyStatus.Closed);
GameEndChecker.LoadingEndScreen = false;
+1 -3
View File
@@ -1481,7 +1481,6 @@ internal static class RPC
int amountAllOptions = OptionItem.AllOptions.Count;
Logger.Msg(" Starting", "SyncCustomSettings");
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncCustomSettings, SendOption.Reliable, targetId);
writer.Write(startAmount);
writer.Write(lastAmount);
@@ -1492,13 +1491,12 @@ internal static class RPC
for (int option = startAmount; option < amountAllOptions && option <= lastAmount; option++) listOptions.Add(OptionItem.AllOptions[option]);
int countListOptions = listOptions.Count;
Logger.Msg($"StartAmount: {startAmount} - LastAmount: {lastAmount} ({startAmount}/{lastAmount}) :--: ListOptionsCount: {countListOptions} - AllOptions: {amountAllOptions} ({countListOptions}/{amountAllOptions})", "SyncCustomSettings");
Logger.Info($"StartAmount: {startAmount} - LastAmount: {lastAmount} ({startAmount}/{lastAmount}) :--: ListOptionsCount: {countListOptions} - AllOptions: {amountAllOptions} ({countListOptions}/{amountAllOptions})", "SyncCustomSettings");
// Sync Settings
foreach (OptionItem option in listOptions) writer.WritePacked(option.GetValue());
AmongUsClient.Instance.FinishRpcImmediately(writer);
Logger.Msg(" Finished", "SyncCustomSettings");
}
public static void PlaySoundRPC(byte playerID, Sounds sound)
+1 -1
View File
@@ -165,7 +165,7 @@ public static class Translator
try
{
if (TranslateMaps.TryGetValue(str, out Dictionary<int, string> dic) && (!dic.TryGetValue((int)langId, out res) || string.IsNullOrWhiteSpace(res) || (langId is not SupportedLangs.SChinese and not SupportedLangs.TChinese && Regex.IsMatch(res, @"[\u4e00-\u9fa5]") && res == GetString(str, SupportedLangs.SChinese))))
if (TranslateMaps.TryGetValue(str, out Dictionary<int, string> dic) && (!dic.TryGetValue((int)langId, out res) || string.IsNullOrEmpty(res) || (langId is not SupportedLangs.SChinese and not SupportedLangs.TChinese && Regex.IsMatch(res, @"[\u4e00-\u9fa5]") && res == GetString(str, SupportedLangs.SChinese))))
res = langId == SupportedLangs.English ? $"*{str}" : GetString(str, SupportedLangs.English);
if (!TranslateMaps.ContainsKey(str) && Enum.GetValues<StringNames>().FindFirst(x => x.ToString() == str, out StringNames stringNames))
+1 -11
View File
@@ -460,12 +460,6 @@ public static class Utils
}
}
public static void RemovePlayerFromPreviousRoleData(PlayerControl target)
{
if (!Main.PlayerStates.TryGetValue(target.PlayerId, out PlayerState state)) return;
state.Role.Remove(target.PlayerId);
}
public static string GetDisplayRoleName(byte playerId, bool pure = false, bool seeTargetBetrayalAddons = false)
{
(string, Color) textData = GetRoleText(playerId, playerId, pure, seeTargetBetrayalAddons);
@@ -1177,7 +1171,7 @@ public static class Utils
}
TaskState taskState = state.TaskState;
if (!taskState.HasTasks) return string.Empty;
if (!taskState.HasTasks || taskState.AllTasksCount == 0) return string.Empty;
NetworkedPlayerInfo info = GetPlayerInfoById(playerId);
bool hasTasks = HasTasks(info);
@@ -3455,10 +3449,6 @@ public static class Utils
Main.AbilityCD[playerId] = (TimeStamp, cd);
SendRPC(CustomRPC.SyncAbilityCD, 1, playerId, cd);
if (role.SimpleAbilityTrigger() &&
(Options.UsePhantomBasis.GetBool() && (!role.IsNeutral() || Options.UsePhantomBasisForNKs.GetBool())) && !role.AlwaysUsesPhantomBase())
GetPlayerById(playerId)?.RpcResetAbilityCooldown();
}
public static (RoleTypes RoleType, CustomRoles CustomRole) GetRoleMap(byte seerId, byte targetId = byte.MaxValue)
+4 -4
View File
@@ -291,17 +291,17 @@ internal static class GameEndChecker
if (WinnerTeam != CustomWinner.CustomTeam && CustomTeamManager.EnabledCustomTeams.Count > 0)
{
Dictionary<CustomTeamManager.CustomTeam, IEnumerable<byte>> teams = Main.AllPlayerControls
Dictionary<CustomTeamManager.CustomTeam, byte[]> teams = Main.AllPlayerControls
.Select(x => new { Team = CustomTeamManager.GetCustomTeam(x.PlayerId), Player = x })
.Where(x => x.Team != null)
.GroupBy(x => x.Team)
.ToDictionary(x => x.Key, x => x.Select(y => y.Player.PlayerId));
.ToDictionary(x => x.Key, x => x.Select(y => y.Player.PlayerId).ToArray());
foreach ((CustomTeamManager.CustomTeam team, IEnumerable<byte> playerIds) in teams)
foreach ((CustomTeamManager.CustomTeam team, byte[] playerIds) in teams)
{
bool winWithOriginalTeam = CustomTeamManager.IsSettingEnabledForTeam(team, CTAOption.WinWithOriginalTeam);
if (CustomTeamManager.IsSettingEnabledForTeam(team, CTAOption.OriginalWinCondition) && (winWithOriginalTeam || WinnerTeam is not (CustomWinner.Impostor or CustomWinner.Crewmate or CustomWinner.Coven or CustomWinner.Neutrals)))
if (CustomTeamManager.IsSettingEnabledForTeam(team, CTAOption.OriginalWinCondition) && playerIds.Any(WinnerIds.Contains) && (winWithOriginalTeam || WinnerTeam is not (CustomWinner.Impostor or CustomWinner.Crewmate or CustomWinner.Coven or CustomWinner.Neutrals)))
WinnerIds.UnionWith(playerIds);
else if (!winWithOriginalTeam)
WinnerIds.ExceptWith(playerIds);
+4
View File
@@ -21,6 +21,7 @@ public static class OptionsMenuBehaviourStartPatch
private static ClientOptionItem EnableCustomSoundEffect;
private static ClientOptionItem SwitchVanilla;
private static ClientOptionItem DarkTheme;
private static ClientOptionItem DarkThemeForMeetingUI;
private static ClientOptionItem HorseMode;
private static ClientOptionItem LongMode;
private static ClientOptionItem ShowPlayerInfoInLobby;
@@ -130,6 +131,9 @@ public static class OptionsMenuBehaviourStartPatch
if (DarkTheme == null || DarkTheme.ToggleButton == null)
DarkTheme = ClientOptionItem.Create("EnableDarkTheme", Main.DarkTheme, __instance);
if (DarkThemeForMeetingUI == null || DarkThemeForMeetingUI.ToggleButton == null)
DarkThemeForMeetingUI = ClientOptionItem.Create("DarkThemeForMeetingUI", Main.DarkThemeForMeetingUI, __instance);
if (HorseMode == null || HorseMode.ToggleButton == null)
{
+1 -1
View File
@@ -89,7 +89,7 @@ internal static class ExileControllerWrapUpPatch
}
pc.ResetKillCooldown(false);
pc.RpcResetAbilityCooldown();
if (!Utils.ShouldNotApplyAbilityCooldownAfterMeeting(pc)) pc.RpcResetAbilityCooldown();
PetsHelper.RpcRemovePet(pc);
}
+2 -2
View File
@@ -1418,7 +1418,7 @@ static class NamePlateDarkThemePatch
{
public static void Postfix(PlayerVoteArea __instance)
{
if (Main.DarkTheme.Value)
if (Main.DarkThemeForMeetingUI.Value)
__instance.Background.color = new Color(0.1f, 0.1f, 0.1f);
}
}
@@ -1428,7 +1428,7 @@ static class MeetingHud_Start
{
public static void Postfix(MeetingHud __instance)
{
if (!Main.DarkTheme.Value) return;
if (!Main.DarkThemeForMeetingUI.Value) return;
__instance.meetingContents.transform.FindChild("PhoneUI").FindChild("baseColor").GetComponent<SpriteRenderer>().color = new Color(0.01f, 0.01f, 0.01f);
__instance.Glass.color = new Color(0.7f, 0.7f, 0.7f, 0.3f);
__instance.SkipVoteButton.GetComponent<SpriteRenderer>().color = new Color(0.4f, 0.4f, 0.4f);
+1 -21
View File
@@ -869,26 +869,6 @@ internal static class StartGameHostPatch
}
catch (Exception e) { Utils.ThrowException(e); }
try
{
if (CustomTeamManager.EnabledCustomTeams.Count > 0)
{
var imps = RoleResult.Where(x => x.Value.IsImpostor() && !CustomTeamManager.CustomTeamPlayerIds.Values.Any(l => l.Contains(x.Key))).Select(x => x.Key).ToValidPlayers().ToHashSet();
if (imps.Count > 0)
{
CustomTeamManager.CustomTeamPlayerIds.Values.Flatten().ToValidPlayers().DoIf(x => x != null && x.IsImpostor() && !CustomTeamManager.IsSettingEnabledForPlayerTeam(x.PlayerId, CTAOption.WinWithOriginalTeam), ctp =>
{
imps.Do(imp => ctp.RpcSetRoleDesync(RoleTypes.Scientist, imp.OwnerId, setRoleMap: true));
var sender = CustomRpcSender.Create("CustomTeamManager_SetImpostorDesync", SendOption.Reliable);
imps.Do(imp => sender.RpcSetRole(imp, RoleTypes.Scientist, ctp.OwnerId, changeRoleMap: true));
sender.SendMessage();
});
}
}
}
catch (Exception e) { Utils.ThrowException(e); }
try
{
foreach (PlayerControl pc in Main.AllPlayerControls)
@@ -1043,7 +1023,7 @@ internal static class StartGameHostPatch
}
catch (Exception e) { Utils.ThrowException(e); }
LateTask.New(CustomTeamManager.InitializeCustomTeamPlayers, 7f, log: false);
LateTask.New(CustomTeamManager.InitializeCustomTeamPlayers, 4f, log: false);
if (overrideLovers) Logger.Msg(Main.LoversPlayers.Join(x => x?.GetRealName()), "Lovers");
+6 -10
View File
@@ -66,7 +66,7 @@ public static class PhantomRolePatch
{
HudManager.Instance.AbilityButton.SetFromSettings(phantom.Data.Role.Ability);
if (Utils.ShouldNotApplyAbilityCooldown(roleBase)) return false;
phantom.Data.Role.SetCooldown();
phantom.RpcResetAbilityCooldown();
return false;
}
@@ -78,17 +78,13 @@ public static class PhantomRolePatch
.Write(true)
.EndRpc();
if (Utils.ShouldNotApplyAbilityCooldown(roleBase))
if (!Utils.ShouldNotApplyAbilityCooldown(roleBase))
{
sender.EndMessage();
sender.SendMessage();
return false;
sender.StartRpc(phantom.NetId, RpcCalls.ProtectPlayer)
.WriteNetObject(phantom)
.Write(0)
.EndRpc();
}
sender.StartRpc(phantom.NetId, RpcCalls.ProtectPlayer)
.WriteNetObject(phantom)
.Write(0)
.EndRpc();
sender.EndMessage();
sender.SendMessage();
+3 -5
View File
@@ -648,7 +648,7 @@ internal static class MurderPlayerPatch
if (target.AmOwner) RemoveDisableDevicesPatch.UpdateDisableDevices();
if (!target.Data.IsDead || !AmongUsClient.Instance.AmHost) return;
if (Butcher.ButcherDeadPlayerList.Contains(target.PlayerId)) return;
if (Butcher.ButcherDeadPlayerList.Contains(target.PlayerId) || (__instance.AmOwner && __instance.Is(CustomRoles.SoulCollector))) return;
PlayerControl killer = __instance; // Alternative variable
@@ -1377,7 +1377,8 @@ internal static class ReportDeadBodyPatch
RpcChangeSkin(pc, new NetworkedPlayerInfo.PlayerOutfit().Set(name, 15, "", "", "", "", ""));
}
if (pc.IsShifted()) pc.RpcShapeshift(pc, false);
if (Main.CheckShapeshift.ContainsKey(pc.PlayerId))
pc.RpcShapeshift(pc, false);
}
else if (!pc.Data.IsDead)
pc.RpcExileV2();
@@ -1671,9 +1672,6 @@ internal static class FixedUpdatePatch
if (AmongUsClient.Instance.AmHost && inTask && alive && Options.LadderDeath.GetBool())
FallFromLadder.FixedUpdate(player);
if (self && GameStates.IsInGame)
LoversSuicide();
if (inTask && self && Options.DisableDevices.GetBool())
DisableDevice.FixedUpdate();
+1 -1
View File
@@ -181,7 +181,7 @@ public static class MushroomMixupSabotageSystemPatch
LateTask.New(() =>
{
// After MushroomMixup sabotage, shapeshift cooldown sets to 0
Main.AllAlivePlayerControls.DoIf(x => x.GetRoleTypes() != RoleTypes.Engineer, x => x.RpcResetAbilityCooldown());
Main.AllAlivePlayerControls.DoIf(x => x.GetRoleTypes() == RoleTypes.Shapeshifter, x => x.RpcResetAbilityCooldown());
ReportDeadBodyPatch.CanReport.SetAllValues(true);
}, 1.2f, "Reset Ability Cooldown Arter Mushroom Mixup");
+4 -1
View File
@@ -1064,7 +1064,8 @@
"ForceOwnLanguage": "Force mod to use your language if possible",
"ForceOwnLanguageRoleName": "Force role names in your language if possible",
"GodMode": "God Mode",
"EnableDarkTheme": "Enable Dark Theme",
"EnableDarkTheme": "Dark Theme for Chat UI",
"DarkThemeForMeetingUI": "Dark Theme for Meeting UI",
"AutoDisplayKillLog": "Display Kill-log",
"AutoDisplayLastRoles": "Display Last Roles",
"AutoDisplayLastAddOns": "Display Last Add-ons",
@@ -1332,6 +1333,7 @@
"CanKill": "Can Kill",
"CanShapeshift": "Can <color=#ff1919>Shapeshift</color>",
"CanGuess": "Can Guess in Guesser Mode",
"CooldownsResetEachOther": "Ability Cooldown and Kill Cooldown Reset Each Other",
"BombCooldown": "Bomb Cooldown",
"AbilityUseGainWithEachTaskCompleted": "<color=#00ffff>Ability <color=#7FFFD2>Use Gains</color> With Each Task Completed</color>",
"AbilityUseGainWithEachKill": "<color=#00ffff>Ability <color=#7FFFD2>Use Gains</color> With Each Kill</color>",
@@ -9896,6 +9898,7 @@
"ThiefInfo": "Steal a Killing Role",
"ThiefInfoLong": "(Neutrals):\\nAs the Thief, you must kill someone to become their role:\\n<size=80%>- Impostor / Neutral Killing roles => become the same role as them\\n- Trickster => become Double Agent\\n- Maverick => become Serial Killer\\n- Madmate roles => become Renegade\\n- Coven roles (if enabled) => become Coven Member</size>\\nIf you kill any other role, you suicide.",
"Thief.CanStealCovenRoles": "Can Steal <#7b3fbb>Coven</color> Roles",
"Thief.CanKillRound1": "Can Kill on the First Round",
"Sensor": "Sensor",
"SensorInfo": "Whenever Someone Sabotages, You See an Arrow Towards Them",
"SensorInfoLong": "(Crewmates):\\nAs the Sensor, whenever someone sabotages, you get a temporary arrow pointing at them.",
+3
View File
@@ -164,6 +164,9 @@ public class Dad : RoleBase
public override void Remove(byte playerId)
{
Instances.Remove(this);
if (!AmongUsClient.Instance.AmHost) return;
Main.AllPlayerSpeed[playerId] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
PlayerGameOptionsSender.SetDirty(playerId);
}
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
+9 -6
View File
@@ -76,15 +76,18 @@ public class Imitator : RoleBase
{
var command = $"/imitate {playerId}";
ChatCommands.ImitateCommand(PlayerControl.LocalPlayer, command, command.Split(' '));
if (ImitatingRole.ContainsKey(PlayerControl.LocalPlayer.PlayerId))
{
foreach (PlayerVoteArea pva in MeetingHud.Instance.playerStates)
{
Transform button = pva.transform.FindChild("ImitatorButton");
if (button != null) Object.Destroy(button.gameObject);
}
}
}
else
SendRPC(playerId);
foreach (PlayerVoteArea pva in MeetingHud.Instance.playerStates)
{
Transform button = pva.transform.FindChild("ImitatorButton");
if (button != null) Object.Destroy(button.gameObject);
}
}
private static void CreateImitatorButton(MeetingHud __instance)
+1 -1
View File
@@ -439,7 +439,7 @@ public class Inspector : RoleBase
if (FirstPick.TryGetValue(lpcId, out byte firstPick))
{
InspectorCheckMsg(PlayerControl.LocalPlayer, $"/cp {playerId} {firstPick}");
InspectorCheckMsg(lpcId.GetPlayer(), $"/cp {playerId} {firstPick}", lpcId.IsPlayerModdedClient());
FirstPick.Remove(lpcId);
}
else
+20 -1
View File
@@ -18,6 +18,8 @@ public class Sapper : RoleBase
private static OptionItem Delay;
private static OptionItem Radius;
private static OptionItem CanSabotage;
private static OptionItem CanKill;
private static OptionItem CooldownsResetEachOther;
private static Dictionary<Vector2, long> Bombs = [];
@@ -43,6 +45,12 @@ public class Sapper : RoleBase
CanSabotage = new BooleanOptionItem(Id + 14, "CanSabotage", false, TabGroup.ImpostorRoles)
.SetParent(CustomRoleSpawnChances[CustomRoles.Sapper]);
CanKill = new BooleanOptionItem(Id + 15, "CanKill", true, TabGroup.ImpostorRoles)
.SetParent(CustomRoleSpawnChances[CustomRoles.Sapper]);
CooldownsResetEachOther = new BooleanOptionItem(Id + 16, "CooldownsResetEachOther", true, TabGroup.ImpostorRoles)
.SetParent(CanKill);
}
public override void Init()
@@ -90,7 +98,17 @@ public class Sapper : RoleBase
public override bool CanUseKillButton(PlayerControl pc)
{
return false;
return CanKill.GetBool();
}
public override void OnMurder(PlayerControl killer, PlayerControl target)
{
if (!CooldownsResetEachOther.GetBool()) return;
if (UsePets.GetBool() && !UsePhantomBasis.GetBool())
killer.AddAbilityCD();
else
killer.RpcResetAbilityCooldown();
}
public override void OnPet(PlayerControl pc)
@@ -108,6 +126,7 @@ public class Sapper : RoleBase
if (pc == null) return false;
if (!pc.IsAlive() || Pelican.IsEaten(pc.PlayerId)) return false;
Bombs.TryAdd(pc.Pos(), TimeStamp);
if (CanKill.GetBool() && CooldownsResetEachOther.GetBool()) pc.SetKillCooldown();
return false;
}
+26 -10
View File
@@ -34,7 +34,7 @@ public class Silencer : RoleBase
public override void SetupCustomOption()
{
SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Silencer);
SetupRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Silencer, zeroOne: true);
SkillCooldown = new FloatOptionItem(Id + 5, "AbilityCooldown", new(2.5f, 60f, 0.5f), 30f, TabGroup.ImpostorRoles)
.SetParent(CustomRoleSpawnChances[CustomRoles.Silencer])
@@ -83,12 +83,13 @@ public class Silencer : RoleBase
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (SilenceMode.GetValue() >= 1 || ForSilencer.Count >= 1) return true;
if (SilenceMode.GetValue() >= 1) return true;
return killer.CheckDoubleTrigger(target, () =>
{
ForSilencer.Add(target.PlayerId);
ForSilencer = [target.PlayerId];
Utils.SendRPC(CustomRPC.SyncRoleData, killer.PlayerId, 1, target.PlayerId);
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
killer.SetKillCooldown(3f);
if (killer.AmOwner)
@@ -104,9 +105,9 @@ public class Silencer : RoleBase
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
{
if (SilenceMode.GetValue() == 1 && ForSilencer.Count == 0 && shapeshifter.PlayerId != target.PlayerId)
if (SilenceMode.GetValue() == 1&& shapeshifter.PlayerId != target.PlayerId)
{
ForSilencer.Add(target.PlayerId);
ForSilencer = [target.PlayerId];
Utils.SendRPC(CustomRPC.SyncRoleData, shapeshifter.PlayerId, 1, target.PlayerId);
if (shapeshifter.AmOwner)
@@ -124,7 +125,7 @@ public class Silencer : RoleBase
public override bool OnVanish(PlayerControl pc)
{
if (SilenceMode.GetValue() == 2 && ForSilencer.Count == 0)
if (SilenceMode.GetValue() == 2)
{
var pos = pc.Pos();
var killRange = NormalGameOptionsV10.KillDistances[Mathf.Clamp(Main.NormalOptions.KillDistance, 0, 2)];
@@ -132,8 +133,9 @@ public class Silencer : RoleBase
PlayerControl target = nearPlayers.Length == 0 ? null : nearPlayers.MinBy(x => x.distance).pc;
if (target == null) return false;
ForSilencer.Add(target.PlayerId);
ForSilencer = [target.PlayerId];
Utils.SendRPC(CustomRPC.SyncRoleData, pc.PlayerId, 1, target.PlayerId);
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: target);
if (pc.AmOwner)
{
@@ -153,7 +155,7 @@ public class Silencer : RoleBase
switch (reader.ReadPackedInt32())
{
case 1:
ForSilencer.Add(reader.ReadByte());
ForSilencer = [reader.ReadByte()];
break;
case 2:
ForSilencer = [];
@@ -163,8 +165,22 @@ public class Silencer : RoleBase
public override void AfterMeetingTasks()
{
if (ForSilencer.Count == 0) return;
ForSilencer.Clear();
if (PlayerIdList.Count == 0) return;
Utils.SendRPC(CustomRPC.SyncRoleData, PlayerIdList[0], 2);
PlayerIdList.ForEach(x => Utils.SendRPC(CustomRPC.SyncRoleData, x, 2));
}
public override void OnReportDeadBody()
{
if (ForSilencer.Count == 0) return;
Main.Instance.StartCoroutine(WaitForVote());
return;
System.Collections.IEnumerator WaitForVote()
{
while (MeetingHud.Instance && MeetingHud.Instance.state is not (MeetingHud.VoteStates.NotVoted or MeetingHud.VoteStates.Voted) && !GameStates.IsEnded && GameStates.InGame && !ExileController.Instance) yield return null;
if (!MeetingHud.Instance || GameStates.IsEnded || !GameStates.InGame || ExileController.Instance) yield break;
ForSilencer.ForEach(x => MeetingHud.Instance.CastVote(x, 253)); // 253 is skip vote
}
}
}
+1 -1
View File
@@ -97,7 +97,7 @@ public class Venerer : RoleBase
Utils.NotifyRoles(SpecifyTarget: pc);
LateTask.New(() =>
{
if (!ChangedSkin || pc == null || !pc.IsAlive()) return;
if (!ChangedSkin || pc == null || !pc.IsAlive() || Camouflage.IsCamouflage) return;
Utils.RpcChangeSkin(pc, outfit);
ChangedSkin = false;
Utils.NotifyRoles(SpecifyTarget: pc);
+4 -1
View File
@@ -95,14 +95,17 @@ internal class Arsonist : RoleBase
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
{
float cooldown = ArsonistCooldown.GetFloat();
if (ArsonistCanIgniteAnytime.GetBool() && IsDoused[(killer.PlayerId, target.PlayerId)] && Utils.GetDousedPlayerCount(killer.PlayerId).Doused >= ArsonistMinPlayersToIgnite.GetInt())
{
killer.SetKillCooldown(cooldown);
Ignite(killer.MyPhysics);
return false;
}
float douseTime = ArsonistDouseTime.GetFloat();
killer.SetKillCooldown(Mathf.Approximately(douseTime, 0f) ? ArsonistCooldown.GetFloat() : douseTime);
killer.SetKillCooldown(Mathf.Approximately(douseTime, 0f) ? cooldown : douseTime);
if (!IsDoused[(killer.PlayerId, target.PlayerId)] && !ArsonistTimer.ContainsKey(killer.PlayerId) && killer.RpcCheckAndMurder(target, check: true))
{
+19 -14
View File
@@ -1,4 +1,5 @@
using AmongUs.GameOptions;
using System.Diagnostics;
using AmongUs.GameOptions;
using EHR.Modules;
using Hazel;
@@ -15,7 +16,7 @@ public class Duality : RoleBase
public override bool IsEnable => On;
public bool KillingPhase;
private long TimerEndTS;
private Stopwatch Timer;
private long LastUpdateTS;
private byte DualityId;
@@ -37,7 +38,8 @@ public class Duality : RoleBase
On = true;
KillingPhase = true;
DualityId = playerId;
ResetTimer();
Timer = new();
LateTask.New(() => Timer = Stopwatch.StartNew(), 10f, log: false);
}
public override bool CanUseKillButton(PlayerControl pc)
@@ -71,7 +73,7 @@ public class Duality : RoleBase
LateTask.New(() =>
{
KillingPhase = false;
killer.RpcChangeRoleBasis(CanVent.GetBool() ? CustomRoles.EngineerEHR : CustomRoles.CrewmateEHR);
killer.RpcSetRoleGlobal(CanVent.GetBool() ? RoleTypes.Engineer : RoleTypes.Crewmate);
killer.RpcResetTasks();
ResetTimer();
}, 0.2f, log: false);
@@ -92,7 +94,7 @@ public class Duality : RoleBase
if (LastUpdateTS == now) return;
LastUpdateTS = now;
if (now >= TimerEndTS)
if (GetRemainingTime() <= 0)
{
pc.Suicide();
if (pc.AmOwner) Achievements.Type.OutOfTime.Complete();
@@ -102,30 +104,33 @@ public class Duality : RoleBase
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}
public override void OnReportDeadBody()
{
Timer.Reset();
}
public override void AfterMeetingTasks()
{
if (TimerEndTS - Utils.TimeStamp < 8)
{
TimerEndTS = Utils.TimeStamp + 8;
Utils.SendRPC(CustomRPC.SyncRoleData, DualityId, TimerEndTS, KillingPhase);
}
ResetTimer();
}
void ResetTimer()
{
TimerEndTS = Utils.TimeStamp + Time.GetInt() + (Main.IntroDestroyed ? 1 : 12);
Utils.SendRPC(CustomRPC.SyncRoleData, DualityId, TimerEndTS, KillingPhase);
Timer = Stopwatch.StartNew();
Utils.SendRPC(CustomRPC.SyncRoleData, DualityId, KillingPhase);
}
long GetRemainingTime() => Time.GetInt() - Timer.Elapsed.Seconds;
public void ReceiveRPC(MessageReader reader)
{
TimerEndTS = long.Parse(reader.ReadString());
KillingPhase = reader.ReadBoolean();
Timer = Stopwatch.StartNew();
}
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
{
if (seer.PlayerId != DualityId || seer.PlayerId != target.PlayerId || (!hud && seer.IsModdedClient()) || meeting || !seer.IsAlive()) return string.Empty;
return $"<size=80%>{string.Format(Translator.GetString(KillingPhase ? "Duality.MustKill" : "Duality.MustDoTask"), TimerEndTS - Utils.TimeStamp)}</size>";
return $"<size=80%>{string.Format(Translator.GetString(KillingPhase ? "Duality.MustKill" : "Duality.MustDoTask"), GetRemainingTime() - 1)}</size>";
}
}
-2
View File
@@ -83,8 +83,6 @@ public class Shifter : RoleBase
CustomRoles targetRole = target.GetCustomRole();
Utils.RemovePlayerFromPreviousRoleData(target);
killer.RpcSetCustomRole(targetRole);
killer.RpcChangeRoleBasis(targetRole);
+4 -2
View File
@@ -8,6 +8,7 @@ public class Thief : RoleBase
private static OptionItem CanStealCovenRoles;
private static OptionItem ImpostorVision;
private static OptionItem CanKillRound1;
public override bool IsEnable => On;
@@ -15,7 +16,8 @@ public class Thief : RoleBase
{
StartSetup(656400)
.AutoSetupOption(ref CanStealCovenRoles, false)
.AutoSetupOption(ref ImpostorVision, false);
.AutoSetupOption(ref ImpostorVision, false)
.AutoSetupOption(ref CanKillRound1, false);
}
public override void Init()
@@ -30,7 +32,7 @@ public class Thief : RoleBase
public override bool CanUseKillButton(PlayerControl pc)
{
return pc.IsAlive();
return !MeetingStates.FirstMeeting || CanKillRound1.GetBool();
}
public override void ApplyGameOptions(IGameOptions opt, byte playerId)