massive fixes, AFK detection, cooler ID showing in meetings

This commit is contained in:
Gurge44 committed 2024-07-07 21:45:36 +02:00
1 parent d77c3219e4
commit 692075d296
42 files changed
+757 -412

No files matched your search

+2
View File
@@ -2,6 +2,7 @@
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">SOLUTION</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AFK/@EntryIndexedValue">AFK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ATK/@EntryIndexedValue">ATK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AU/@EntryIndexedValue">AU</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CD/@EntryIndexedValue">CD</s:String>
@@ -12,6 +13,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FFA/@EntryIndexedValue">FFA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FFF/@EntryIndexedValue">FFF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FPS/@EntryIndexedValue">FPS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GA/@EntryIndexedValue">GA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GM/@EntryIndexedValue">GM</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HP/@EntryIndexedValue">HP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HUD/@EntryIndexedValue">HUD</s:String>
+140
View File
@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace EHR.Modules
{
public static class AFKDetector
{
private static OptionItem EnableDetector;
private static OptionItem ConsequenceOption;
private static readonly Dictionary<byte, Data> PlayerData = [];
public static readonly HashSet<byte> ExemptedPlayers = [];
public static readonly HashSet<byte> ShieldedPlayers = [];
public static int NumAFK;
public static void SetupCustomOption()
{
new TextOptionItem(100080, "MenuTitle.AFKDetection", TabGroup.GameSettings)
.SetColor(new Color32(0, 255, 160, 255))
.SetHeader(true);
EnableDetector = new BooleanOptionItem(90, "EnableAFKDetector", true, TabGroup.GameSettings)
.SetColor(new Color32(0, 255, 160, 255));
ConsequenceOption = new StringOptionItem(91, "AFKConsequence", Enum.GetNames<Consequence>().Select(x => $"AFKConsequence.{x}").ToArray(), 0, TabGroup.GameSettings)
.SetParent(EnableDetector)
.SetColor(new Color32(0, 255, 160, 255));
}
public static void RecordPosition(PlayerControl pc)
{
if (!EnableDetector.GetBool() || ExemptedPlayers.Contains(pc.PlayerId)) return;
PlayerData[pc.PlayerId] = new()
{
LastPosition = pc.Pos(),
Timer = 10f + (pc.Is(CustomRoles.Truant) ? Options.TruantWaitingTime.GetFloat() : 0f)
};
}
public static void OnFixedUpdate(PlayerControl pc)
{
if (!EnableDetector.GetBool() || !PlayerData.TryGetValue(pc.PlayerId, out var data)) return;
if (Vector2.Distance(pc.Pos(), data.LastPosition) > 0.1f)
{
PlayerData.Remove(pc.PlayerId);
ShieldedPlayers.Remove(pc.PlayerId);
return;
}
int lastTimer = (int)Math.Round(data.Timer);
data.Timer -= Time.fixedDeltaTime;
int currentTimer = (int)Math.Round(data.Timer);
if (data.Timer <= 0f)
{
switch (data.CurrentPhase)
{
case Data.Phase.Detection:
if (!pc.IsModClient()) NumAFK++;
data.CurrentPhase = Data.Phase.Warning;
data.Timer = 15f;
break;
case Data.Phase.Warning:
data.CurrentPhase = Data.Phase.Consequence;
HandleConsequence(pc, (Consequence)ConsequenceOption.GetInt());
break;
}
if (Main.AllAlivePlayerControls.Length / 2 <= NumAFK)
{
Logger.SendInGame(Translator.GetString("AFKTooMany"));
PlayerData.Clear();
}
}
if (data.CurrentPhase == Data.Phase.Warning && lastTimer != currentTimer)
{
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
}
}
public static string GetSuffix(PlayerControl seer, PlayerControl target)
{
if (seer.IsAlive() && PlayerData.TryGetValue(seer.PlayerId, out var seerData) && seerData.CurrentPhase > Data.Phase.Detection)
return seerData.CurrentPhase == Data.Phase.Warning ? string.Format(Translator.GetString("AFKWarning"), (int)Math.Round(seerData.Timer)) : Translator.GetString("AFKSuffix");
if (target.IsAlive() && PlayerData.TryGetValue(target.PlayerId, out var targetData) && targetData.CurrentPhase > Data.Phase.Detection)
return Translator.GetString("AFKSuffix");
return string.Empty;
}
private static void HandleConsequence(PlayerControl pc, Consequence consequence)
{
switch (consequence)
{
case Consequence.Shield:
ShieldedPlayers.Add(pc.PlayerId);
Utils.NotifyRoles(SpecifyTarget: pc);
break;
case Consequence.Kick:
AmongUsClient.Instance.KickPlayer(pc.GetClientId(), false);
Logger.SendInGame(string.Format(Translator.GetString("AFKKick"), pc.PlayerId.ColoredPlayerName()));
break;
case Consequence.Suicide:
pc.Suicide(PlayerState.DeathReason.AFK);
break;
case Consequence.Nothing:
Utils.NotifyRoles(SpecifyTarget: pc);
break;
}
PlayerData.Remove(pc.PlayerId);
}
enum Consequence
{
Nothing,
Shield,
Suicide,
Kick
}
class Data
{
public enum Phase
{
Detection,
Warning,
Consequence
}
public Vector2 LastPosition { get; init; }
public float Timer { get; set; }
public Phase CurrentPhase { get; set; }
}
}
}
+7 -1
View File
@@ -348,7 +348,7 @@ namespace EHR
}
}
public static void FixedUpdate() => AllObjects.ForEach(x => x.OnFixedUpdate());
public static void FixedUpdate() => AllObjects.ToArray().Do(x => x.OnFixedUpdate());
public static CustomNetObject Get(int id) => AllObjects.FirstOrDefault(x => x.Id == id);
public static void Reset()
@@ -395,6 +395,8 @@ namespace EHR
protected override void OnFixedUpdate()
{
base.OnFixedUpdate();
Timer += Time.deltaTime;
if (Timer >= Duration / 5f && Frame == 0)
{
@@ -448,6 +450,8 @@ namespace EHR
protected override void OnFixedUpdate()
{
base.OnFixedUpdate();
Timer += Time.deltaTime;
if (Timer >= WaitDuration * 0.75f && State == 0)
{
@@ -477,6 +481,8 @@ namespace EHR
protected override void OnFixedUpdate()
{
base.OnFixedUpdate();
try
{
if (Gone) return;
+2 -14
View File
@@ -785,17 +785,7 @@ internal static class CustomRolesHelper
CustomRoles.Undead or
CustomRoles.Egoist;
public static bool IsImpOnlyAddon(this CustomRoles role) => role is
CustomRoles.Mare or
CustomRoles.LastImpostor or
CustomRoles.Mare or
CustomRoles.Mimic or
CustomRoles.Damocles or
CustomRoles.DeadlyQuota or
CustomRoles.Circumvent or
CustomRoles.TicketsStealer or
CustomRoles.Taskcounter or
CustomRoles.Swift;
public static bool IsImpOnlyAddon(this CustomRoles role) => Options.GroupedAddons[AddonTypes.ImpOnly].Contains(role);
public static bool IsTaskBasedCrewmate(this CustomRoles role) => role is
CustomRoles.Snitch or
@@ -825,7 +815,7 @@ internal static class CustomRolesHelper
public static bool IsNoAnimationShifter(this CustomRoles role) => role is
CustomRoles.Echo;
public static bool CheckAddonConflict(CustomRoles role, PlayerControl pc) => role.IsAdditionRole() && (!Main.NeverSpawnTogetherCombos.TryGetValue(OptionItem.CurrentPreset, out var neverList) || !neverList.TryGetValue(pc.GetCustomRole(), out var bannedAddonList) || !bannedAddonList.Contains(role)) && pc.GetCustomRole() is not CustomRoles.GuardianAngelEHR and not CustomRoles.God && !pc.Is(CustomRoles.Madmate) && !pc.Is(CustomRoles.GM) && role is not CustomRoles.Lovers && !pc.Is(CustomRoles.Needy) && (!pc.HasSubRole() || pc.GetCustomSubRoles().Count < Options.NoLimitAddonsNumMax.GetInt()) && (!Options.AddonCanBeSettings.TryGetValue(role, out var o) || ((o.Imp.GetBool() || !pc.GetCustomRole().IsImpostor()) && (o.Neutral.GetBool() || !pc.GetCustomRole().IsNeutral()) && (o.Crew.GetBool() || !pc.IsCrewmate()))) && role switch
public static bool CheckAddonConflict(CustomRoles role, PlayerControl pc) => role.IsAdditionRole() && (!Main.NeverSpawnTogetherCombos.TryGetValue(OptionItem.CurrentPreset, out var neverList) || !neverList.TryGetValue(pc.GetCustomRole(), out var bannedAddonList) || !bannedAddonList.Contains(role)) && pc.GetCustomRole() is not CustomRoles.GuardianAngelEHR and not CustomRoles.God && !pc.Is(CustomRoles.Madmate) && !pc.Is(CustomRoles.GM) && role is not CustomRoles.Lovers && !pc.Is(CustomRoles.Needy) && (!pc.HasSubRole() || pc.GetCustomSubRoles().Count < Options.NoLimitAddonsNumMax.GetInt()) && (!Options.AddonCanBeSettings.TryGetValue(role, out var o) || ((o.Imp.GetBool() || !pc.GetCustomRole().IsImpostor()) && (o.Neutral.GetBool() || !pc.GetCustomRole().IsNeutral()) && (o.Crew.GetBool() || !pc.IsCrewmate()))) && (!role.IsImpOnlyAddon() || pc.IsImpostor()) && role switch
{
CustomRoles.Rookie when !pc.CanUseKillButton() => false,
CustomRoles.Energetic when !Options.UsePets.GetBool() => false,
@@ -840,7 +830,6 @@ internal static class CustomRolesHelper
CustomRoles.Stressed when !pc.IsCrewmate() || pc.GetCustomRole().IsTasklessCrewmate() => false,
CustomRoles.Lazy when pc.GetCustomRole().IsNeutral() || pc.IsImpostor() || (pc.GetCustomRole().IsTasklessCrewmate() && !Options.TasklessCrewCanBeLazy.GetBool()) || (pc.GetCustomRole().IsTaskBasedCrewmate() && !Options.TaskBasedCrewCanBeLazy.GetBool()) => false,
CustomRoles.TicketsStealer or CustomRoles.Swift or CustomRoles.DeadlyQuota or CustomRoles.Damocles or CustomRoles.Mare when pc.Is(CustomRoles.Bomber) || pc.Is(CustomRoles.Nuker) || pc.Is(CustomRoles.BoobyTrap) || pc.Is(CustomRoles.Capitalism) => false,
CustomRoles.TicketsStealer or CustomRoles.Mimic or CustomRoles.Swift or CustomRoles.DeadlyQuota or CustomRoles.Circumvent or CustomRoles.Damocles or CustomRoles.Taskcounter or CustomRoles.Mare when !pc.IsImpostor() => false,
CustomRoles.Torch when !pc.IsCrewmate() || pc.Is(CustomRoles.Bewilder) || pc.Is(CustomRoles.Sunglasses) || pc.Is(CustomRoles.GuardianAngelEHR) => false,
CustomRoles.Bewilder when pc.Is(CustomRoles.Torch) || pc.Is(CustomRoles.GuardianAngelEHR) || pc.Is(CustomRoles.Sunglasses) => false,
CustomRoles.Sunglasses when pc.Is(CustomRoles.Torch) || pc.Is(CustomRoles.GuardianAngelEHR) || pc.Is(CustomRoles.Bewilder) => false,
@@ -968,7 +957,6 @@ internal static class CustomRolesHelper
CustomRoles.Charmed or
CustomRoles.Recruit or
CustomRoles.Contagious or
CustomRoles.Sidekick or
CustomRoles.Undead or
CustomRoles.Lovers;
+9 -10
View File
@@ -153,17 +153,16 @@ static class ExtendedPlayerControl
Main.LastNotifyNames[(player.PlayerId, seer.PlayerId)] = name;
Logger.Info($"Set:{player.Data?.PlayerName}:{name} for {seer.GetNameWithRole().RemoveHtmlTags()}", "RpcSetNamePrivate");
if (seer == null || player == null) return;
var clientId = seer.GetClientId();
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)RpcCalls.SetName, SendOption.Reliable, clientId);
try
{
writer.Write(player.Data.NetId);
writer.Write(name);
}
finally
{
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
var sender = CustomRpcSender.Create(name: "SetNamePrivate");
sender.AutoStartRpc(player.NetId, (byte)RpcCalls.SetName, clientId)
.Write(seer.Data.NetId)
.Write(name)
.EndRpc();
sender.SendMessage();
}
public static void RpcSetRoleDesync(this PlayerControl player, RoleTypes role, bool canOverride, int clientId)
+2 -4
View File
@@ -60,7 +60,7 @@ namespace EHR
/// <param name="collection">The collection to iterate over</param>
/// <param name="action">The action to execute for each element</param>
/// <typeparam name="T">The type of the elements in the collection</typeparam>
public static IEnumerable<T> Do<T>(this IEnumerable<T> collection, Action<T> action)
public static void Do<T>(this IEnumerable<T> collection, Action<T> action)
{
if (collection is List<T> list)
{
@@ -69,15 +69,13 @@ namespace EHR
action(list[i]);
}
return collection;
return;
}
foreach (T element in collection)
{
action(element);
}
return collection;
}
/// <summary>
@@ -214,6 +214,10 @@ public class PlayerGameOptionsSender(PlayerControl player) : GameOptionsSender
break;
}
// When impostor alert is off, and the player is a desync crewmate, set impostor alert as true
if (role.IsDesyncRole() && role.IsCrewmate() && !CrewmateVanillaRoles.NoiseMakerImpostorAlert.GetBool()) AURoleOptions.NoisemakerImpostorAlert = true;
else AURoleOptions.NoisemakerImpostorAlert = CrewmateVanillaRoles.NoiseMakerImpostorAlert.GetBool();
if (Shifter.WasShifter.Contains(player.PlayerId) && role.IsImpostor()) opt.SetVision(true);
Main.PlayerStates[player.PlayerId].Role.ApplyGameOptions(opt, player.PlayerId);
+1
View File
@@ -32,6 +32,7 @@ public class PlayerState(byte playerId)
Execution,
Disconnected,
Fall,
AFK,
// EHR
Gambled,
+12 -6
View File
@@ -31,15 +31,16 @@ namespace EHR.Modules
var suitableRole = GetSuitableGhostRole(pc);
Logger.Warn($"Assigning Ghost Role: {pc.GetNameWithRole()} => {suitableRole}", "GhostRolesManager");
pc.RpcSetCustomRole(suitableRole);
pc.RpcSetRole(RoleTypes.GuardianAngel);
IGhostRole instance = CreateGhostRoleInstance(suitableRole);
pc.RpcSetCustomRole(suitableRole);
if (instance.ChangeToGA) pc.RpcSetRole(RoleTypes.GuardianAngel);
instance.OnAssign(pc);
Main.ResetCamPlayerList.Add(pc.PlayerId);
AssignedGhostRoles[pc.PlayerId] = (suitableRole, instance);
if (suitableRole == CustomRoles.Haunter) GhostRoles.Remove(suitableRole);
NotifyAboutGhostRole(pc);
NotifyAboutGhostRole(pc, true);
}
public static void SpecificAssignGhostRole(byte id, CustomRoles role, bool set)
@@ -47,18 +48,23 @@ namespace EHR.Modules
if (AssignedGhostRoles.Any(x => x.Key == id || x.Value.Role == role)) return;
var pc = Utils.GetPlayerById(id);
if (set) pc.RpcSetRole(RoleTypes.GuardianAngel);
IGhostRole instance = CreateGhostRoleInstance(role);
if (set && instance.ChangeToGA) pc.RpcSetRole(RoleTypes.GuardianAngel);
instance.OnAssign(pc);
Main.ResetCamPlayerList.Add(pc.PlayerId);
AssignedGhostRoles[id] = (role, instance);
}
public static void NotifyAboutGhostRole(PlayerControl pc)
public static void NotifyAboutGhostRole(PlayerControl pc, bool first = false)
{
if (!AssignedGhostRoles.TryGetValue(pc.PlayerId, out var ghostRole)) return;
if (!first && pc.IsModClient()) return;
CustomRoles role = ghostRole.Role;
pc.Notify($"{Translator.GetString("GotGhostRoleNotify")}\n<size=80%>{GetMessage(Translator.GetString($"{role}InfoLong").Split("\n")[1..].Join(delimiter: "\n"))}</size>", 300f);
var text = $"{Translator.GetString("GotGhostRoleNotify")}\n<size=80%>{GetMessage(Translator.GetString($"{role}InfoLong").Split("\n")[1..].Join(delimiter: "\n"))}</size>";
Utils.SendMessage(title: text, sendTo: pc.PlayerId, text: "\n");
pc.Notify(text, 5 * text.Count(x => x == '\n'));
return;
string GetMessage(string baseMessage)
+26 -4
View File
@@ -578,8 +578,6 @@ public static class GuessManager
return true;
}
private static TextMeshPro NameText(this PlayerControl p) => p.cosmetics.nameText;
public static void RpcGuesserMurderPlayer(this PlayerControl pc /*, float delay = 0f*/)
{
// DEATH STUFF //
@@ -597,7 +595,7 @@ public static class GuessManager
if (amOwner)
{
hudManager.ShadowQuad.gameObject.SetActive(false);
pc.NameText().GetComponent<MeshRenderer>().material.SetInt(Mask, 0);
pc.cosmetics.nameText.GetComponent<MeshRenderer>().material.SetInt(Mask, 0);
pc.RpcSetScanner(false);
ImportantTextTask importantTextTask = new GameObject("_Player").AddComponent<ImportantTextTask>();
importantTextTask.transform.SetParent(AmongUsClient.Instance.transform, false);
@@ -642,7 +640,7 @@ public static class GuessManager
if (amOwner)
{
hudManager.ShadowQuad.gameObject.SetActive(false);
pc.NameText().GetComponent<MeshRenderer>().material.SetInt(Mask, 0);
pc.cosmetics.nameText.GetComponent<MeshRenderer>().material.SetInt(Mask, 0);
pc.RpcSetScanner(false);
ImportantTextTask importantTextTask = new GameObject("_Player").AddComponent<ImportantTextTask>();
importantTextTask.transform.SetParent(AmongUsClient.Instance.transform, false);
@@ -733,6 +731,28 @@ public static class GuessManager
}
}
private static void CreateIDLabels(MeetingHud __instance)
{
foreach (var pva in __instance.playerStates)
{
var levelDisplay = pva.transform.FindChild("PlayerLevel").gameObject;
var panel = Object.Instantiate(levelDisplay, pva.transform, true);
var panelTransform = panel.transform;
var background = panel.GetComponent<SpriteRenderer>();
background.color = Palette.Purple;
background.sortingOrder = 99;
panelTransform.SetAsFirstSibling();
panelTransform.localPosition = new(-1.2f, -0.6f, 0f);
var levelLabel = panelTransform.FindChild("LevelLabel").GetComponents<TextMeshPro>()[0];
levelLabel.DestroyTranslator();
levelLabel.text = "ID";
levelLabel.sortingOrder = 100;
var levelNumber = panelTransform.FindChild("LevelNumber").GetComponent<TextMeshPro>();
levelNumber.text = pva.TargetPlayerId.ToString();
levelNumber.sortingOrder = 100;
}
}
static void GuesserSelectRole(CustomRoleTypes Role, bool SetPage = true)
{
CurrentTeamType = Role;
@@ -1154,6 +1174,8 @@ public static class GuessManager
if (alive && lp.Is(CustomRoles.Guesser))
CreateGuesserButton(__instance);
}
CreateIDLabels(__instance);
}
}
+7 -1
View File
@@ -270,6 +270,12 @@ internal class RPCHandlerPatch
Version version = Version.Parse(reader.ReadString());
string tag = reader.ReadString();
string forkId = reader.ReadString();
if (!Main.PlayerVersion.ContainsKey(__instance.PlayerId))
{
RPC.RpcVersionCheck();
}
Main.PlayerVersion[__instance.PlayerId] = new(version, tag, forkId);
if (Main.VersionCheat.Value && __instance.IsHost()) RPC.RpcVersionCheck();
@@ -278,7 +284,7 @@ internal class RPCHandlerPatch
Main.PlayerVersion[__instance.PlayerId] = Main.PlayerVersion[0];
// Kick Unmached Player Start
if (AmongUsClient.Instance.AmHost && tag != $"{ThisAssembly.Git.Commit}({ThisAssembly.Git.Branch})" && forkId != Main.ForkId)
if (AmongUsClient.Instance.AmHost && tag != $"{ThisAssembly.Git.Commit}({ThisAssembly.Git.Branch})" && forkId != Main.ForkId && !Main.VersionCheat.Value)
{
LateTask.New(() =>
{
+15 -7
View File
@@ -317,7 +317,7 @@ public static class Utils
{
try
{
var data = new Il2CppSystem.Collections.Generic.Dictionary<int, Il2CppSystem.Collections.Generic.Dictionary<string, Il2CppSystem.Collections.Generic.List<int>>>();
var data = new Il2CppSystem.Collections.Generic.Dictionary<int, Il2CppSystem.Collections.Generic.Dictionary<string, Il2CppSystem.Collections.Generic.List<string>>>();
var dict = path.Contains("Always") ? Main.AlwaysSpawnTogetherCombos : Main.NeverSpawnTogetherCombos;
dict.Do(kvp =>
{
@@ -326,7 +326,7 @@ public static class Utils
{
var key = pair.Key.ToString();
data[kvp.Key][key] = new();
pair.Value.Do(x => data[kvp.Key][key].Add((int)x));
pair.Value.Do(x => data[kvp.Key][key].Add(x.ToString()));
});
});
File.WriteAllText(path, JsonConvert.SerializeObject(data, Formatting.Indented));
@@ -350,7 +350,7 @@ public static class Utils
try
{
if (!File.Exists(path)) return;
var data = JsonConvert.DeserializeObject<Il2CppSystem.Collections.Generic.Dictionary<int, Il2CppSystem.Collections.Generic.Dictionary<string, Il2CppSystem.Collections.Generic.List<int>>>>(File.ReadAllText(path));
var data = JsonConvert.DeserializeObject<Il2CppSystem.Collections.Generic.Dictionary<int, Il2CppSystem.Collections.Generic.Dictionary<string, Il2CppSystem.Collections.Generic.List<string>>>>(File.ReadAllText(path));
var dict = path.Contains("Always") ? Main.AlwaysSpawnTogetherCombos : Main.NeverSpawnTogetherCombos;
dict.Clear();
foreach (var kvp in data)
@@ -362,7 +362,7 @@ public static class Utils
dict[kvp.Key][key] = [];
foreach (var n in pair.Value)
{
dict[kvp.Key][key].Add((CustomRoles)n);
dict[kvp.Key][key].Add(Enum.Parse<CustomRoles>(n));
}
}
}
@@ -733,6 +733,7 @@ public static class Utils
case CustomRoles.Gaulois when !Options.UsePets.GetBool() || !Gaulois.UsePet.GetBool():
case CustomRoles.Analyst when !Options.UsePets.GetBool() || !Analyst.UsePet.GetBool():
case CustomRoles.Witness when !Options.UsePets.GetBool() || !Options.WitnessUsePet.GetBool():
case CustomRoles.Goose:
case CustomRoles.Pursuer:
case CustomRoles.Spiritcaller:
case CustomRoles.PlagueBearer:
@@ -1905,6 +1906,8 @@ public static class Utils
return add;
}
public static string ColoredPlayerName(this byte id) => ColorString(Main.PlayerColors.GetValueOrDefault(id, Color.white), Main.AllPlayerNames.GetValueOrDefault(id, $"Someone (ID {id})"));
public static PlayerControl GetPlayerById(int PlayerId, bool fast = true)
{
if (PlayerId is > byte.MaxValue or < byte.MinValue) return null;
@@ -1919,7 +1922,7 @@ public static class Utils
//if (Options.DeepLowLoad.GetBool()) await Task.Run(() => { DoNotifyRoles(isForMeeting, SpecifySeer, NoCache, ForceLoop, CamouflageIsForMeeting, GuesserIsForMeeting); });
/*else */
if (!SetUpRoleTextPatch.IsInIntro && ((SpecifySeer != null && SpecifySeer.IsModClient()) || !AmongUsClient.Instance.AmHost || Main.AllPlayerControls == null || GameStates.IsMeeting || GameStates.IsLobby)) return;
if (!SetUpRoleTextPatch.IsInIntro && ((SpecifySeer != null && SpecifySeer.IsModClient()) || !AmongUsClient.Instance.AmHost || Main.AllPlayerControls == null || (GameStates.IsMeeting && !isForMeeting) || GameStates.IsLobby)) return;
await DoNotifyRoles(isForMeeting, SpecifySeer, SpecifyTarget, NoCache, ForceLoop, CamouflageIsForMeeting, GuesserIsForMeeting, MushroomMixup);
}
@@ -2423,6 +2426,8 @@ public static class Utils
if (MeetingStates.FirstMeeting && Main.FirstDied != string.Empty && Main.FirstDied == target.FriendCode && Main.ShieldPlayer != string.Empty && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.SoloKombat or CustomGameMode.FFA)
TargetSuffix.Append(GetString("DiedR1Warning"));
TargetSuffix.Append(AFKDetector.GetSuffix(seer, target));
}
string TargetDeathReason = string.Empty;
@@ -2548,8 +2553,8 @@ public static class Utils
}
Lovers.IsChatActivated = false;
Main.ProcessShapeshifts = true;
AFKDetector.NumAFK = 0;
foreach (var pc in Main.AllPlayerControls)
{
@@ -2571,11 +2576,14 @@ public static class Utils
if (Options.UsePets.GetBool()) pc.AddAbilityCD(includeDuration: false);
AFKDetector.RecordPosition(pc);
Main.PlayerStates[pc.PlayerId].Role.AfterMeetingTasks();
}
else
{
if (pc.IsCrewmate() && !pc.GetTaskState().IsTaskFinished)
TaskState taskState = pc.GetTaskState();
if (pc.IsCrewmate() && !taskState.IsTaskFinished && taskState.hasTasks)
pc.Notify(GetString("DoYourTasksPlease"), 10f);
GhostRolesManager.NotifyAboutGhostRole(pc);
+10 -3
View File
@@ -778,6 +778,13 @@ internal class ChatCommands
break;
case "/afkexempt":
canceled = true;
if (args.Length < 2 || !byte.TryParse(args[1], out var afkId)) break;
AFKDetector.ExemptedPlayers.Add(afkId);
Utils.SendMessage("\n", localPlayerId, string.Format(GetString("PlayerExemptedFromAFK"), afkId.ColoredPlayerName()));
break;
case "/m":
case "/myrole":
canceled = true;
@@ -806,7 +813,7 @@ internal class ChatCommands
sb.Replace(subRole.ToString().ToLower(), subRole.ToColoredString());
}
Utils.SendMessage("\n", localPlayerId, settings.ToString());
if (settings.Length > 0) Utils.SendMessage("\n", localPlayerId, settings.ToString());
Utils.SendMessage(sb.Append("</size>").ToString(), localPlayerId, titleSb.ToString());
}
else Utils.SendMessage((lp.FriendCode.GetDevUser().HasTag() ? "\n" : string.Empty) + GetString("Message.CanNotUseInLobby"), localPlayerId);
@@ -1440,7 +1447,7 @@ internal class ChatCommands
sb.Clear().Append(txt);
if (rl.PetActivatedAbility()) sb.Append($"<size=50%>{GetString("SupportsPetMessage")}</size>");
Utils.SendMessage(text: "\n", sendTo: playerId, title: settings.ToString());
if (settings.Length > 0) Utils.SendMessage(text: "\n", sendTo: playerId, title: settings.ToString());
Utils.SendMessage(text: sb.ToString(), sendTo: playerId, title: title);
return;
@@ -1557,7 +1564,7 @@ internal class ChatCommands
sb.Replace(subRole.ToString().ToLower(), subRole.ToColoredString());
}
Utils.SendMessage("\n", player.PlayerId, settings.ToString());
if (settings.Length > 0) Utils.SendMessage("\n", player.PlayerId, settings.ToString());
Utils.SendMessage(sb.Append("</size>").ToString(), player.PlayerId, titleSb.ToString());
}
else Utils.SendMessage(GetString("Message.CanNotUseInLobby"), player.PlayerId);
-153
View File
@@ -1,153 +0,0 @@
using System;
using AmongUs.GameOptions;
using HarmonyLib;
using TMPro;
using UnityEngine;
// All errors in this file are fake, the build succeeds
// https://github.com/CrowdedMods/CrowdedMod/blob/master/src/CrowdedMod/Patches/CreateGameOptionsPatches.cs
namespace EHR.Patches
{
internal static class CreateGameOptionsPatches
{
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.Awake))]
public static class CreateOptionsPickerAwakePatch
{
public static void Postfix(CreateOptionsPicker __instance)
{
if (__instance.mode != SettingsMode.Host) return;
var firstButtonRenderer = __instance.MaxPlayerButtons[0];
firstButtonRenderer.GetComponentInChildren<TextMeshPro>().text = "-";
firstButtonRenderer.enabled = false;
var firstButtonButton = firstButtonRenderer.GetComponent<PassiveButton>();
firstButtonButton.OnClick.RemoveAllListeners();
firstButtonButton.OnClick.AddListener((Action)(() =>
{
for (var i = 1; i < 11; i++)
{
var playerButton = __instance.MaxPlayerButtons[i];
var tmp = playerButton.GetComponentInChildren<TextMeshPro>();
var newValue = Mathf.Max(byte.Parse(tmp.text) - 10, byte.Parse(playerButton.name) - 2);
tmp.text = newValue.ToString();
}
__instance.UpdateMaxPlayersButtons(__instance.GetTargetOptions());
}));
var lastButtonRenderer = __instance.MaxPlayerButtons[^1];
lastButtonRenderer.GetComponentInChildren<TextMeshPro>().text = "+";
lastButtonRenderer.enabled = false;
var lastButtonButton = lastButtonRenderer.GetComponent<PassiveButton>();
lastButtonButton.OnClick.RemoveAllListeners();
lastButtonButton.OnClick.AddListener((Action)(() =>
{
for (var i = 1; i < 11; i++)
{
var playerButton = __instance.MaxPlayerButtons[i];
var tmp = playerButton.GetComponentInChildren<TextMeshPro>();
var newValue = Mathf.Min(byte.Parse(tmp.text) + 10,
127 - 14 + byte.Parse(playerButton.name));
tmp.text = newValue.ToString();
}
__instance.UpdateMaxPlayersButtons(__instance.GetTargetOptions());
}));
for (var i = 1; i < 11; i++)
{
var playerButton = __instance.MaxPlayerButtons[i].GetComponent<PassiveButton>();
var text = playerButton.GetComponentInChildren<TextMeshPro>();
playerButton.OnClick.RemoveAllListeners();
playerButton.OnClick.AddListener((Action)(() =>
{
var maxPlayers = byte.Parse(text.text);
var maxImp = Mathf.Min(__instance.GetTargetOptions().NumImpostors, maxPlayers / 2);
__instance.GetTargetOptions().SetInt(Int32OptionNames.NumImpostors, maxImp);
__instance.ImpostorButtons[1].TextMesh.text = maxImp.ToString();
__instance.SetMaxPlayersButtons(maxPlayers);
}));
}
foreach (var button in __instance.MaxPlayerButtons)
{
button.enabled = button.GetComponentInChildren<TextMeshPro>().text == __instance.GetTargetOptions().MaxPlayers.ToString();
}
{
var secondButton = __instance.ImpostorButtons[1];
secondButton.SpriteRenderer.enabled = false;
var secondButtonText = secondButton.TextMesh;
secondButtonText.text = __instance.GetTargetOptions().NumImpostors.ToString();
var firstButton = __instance.ImpostorButtons[0];
firstButton.SpriteRenderer.enabled = false;
firstButton.TextMesh.text = "-";
var firstPassiveButton = firstButton.PassiveButton;
firstPassiveButton.OnClick.RemoveAllListeners();
firstPassiveButton.OnClick.AddListener((Action)(() =>
{
var newVal = Mathf.Clamp(
byte.Parse(secondButtonText.text) - 1,
1,
__instance.GetTargetOptions().MaxPlayers / 2
);
__instance.SetImpostorButtons(newVal);
secondButtonText.text = newVal.ToString();
}));
var thirdButton = __instance.ImpostorButtons[2];
thirdButton.SpriteRenderer.enabled = false;
thirdButton.TextMesh.text = "+";
var thirdPassiveButton = thirdButton.PassiveButton;
thirdPassiveButton.OnClick.RemoveAllListeners();
thirdPassiveButton.OnClick.AddListener((Action)(() =>
{
var newVal = Mathf.Clamp(
byte.Parse(secondButtonText.text) + 1,
1,
__instance.GetTargetOptions().MaxPlayers / 2
);
__instance.SetImpostorButtons(newVal);
secondButtonText.text = newVal.ToString();
}));
}
}
}
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateMaxPlayersButtons))]
public static class CreateOptionsPickerUpdateMaxPlayersButtonsPatch
{
public static bool Prefix(CreateOptionsPicker __instance, [HarmonyArgument(0)] IGameOptions opts)
{
if (__instance.CrewArea)
{
__instance.CrewArea.SetCrewSize(opts.MaxPlayers, opts.NumImpostors);
}
var selectedAsString = opts.MaxPlayers.ToString();
for (var i = 1; i < __instance.MaxPlayerButtons.Count - 1; i++)
{
__instance.MaxPlayerButtons[i].enabled = __instance.MaxPlayerButtons[i].GetComponentInChildren<TextMeshPro>().text == selectedAsString;
}
return false;
}
}
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateImpostorsButtons))]
public static class CreateOptionsPickerUpdateImpostorsButtonsPatch
{
public static bool Prefix()
{
return false;
}
}
}
}
+187
View File
@@ -0,0 +1,187 @@
using System;
using AmongUs.GameOptions;
using HarmonyLib;
using TMPro;
using UnityEngine;
// All errors in this file are fake, the build succeeds
// https://github.com/CrowdedMods/CrowdedMod/blob/master/src/CrowdedMod/Patches/CreateGameOptionsPatches.cs
namespace EHR.Patches
{
internal static class Crowded
{
public const int MaxPlayers = 127;
public const int MaxImpostors = 127 / 2;
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.Awake))]
public static class CreateOptionsPickerAwake
{
public static void Postfix(CreateOptionsPicker __instance)
{
if (__instance.mode != SettingsMode.Host) return;
{
var firstButtonRenderer = __instance.MaxPlayerButtons[0];
firstButtonRenderer.GetComponentInChildren<TextMeshPro>().text = "-";
firstButtonRenderer.enabled = false;
var firstButtonButton = firstButtonRenderer.GetComponent<PassiveButton>();
firstButtonButton.OnClick.RemoveAllListeners();
firstButtonButton.OnClick.AddListener((Action)(() =>
{
for (var i = 1; i < 11; i++)
{
var playerButton = __instance.MaxPlayerButtons[i];
var tmp = playerButton.GetComponentInChildren<TextMeshPro>();
var newValue = Mathf.Max(byte.Parse(tmp.text) - 10, byte.Parse(playerButton.name) - 2);
tmp.text = newValue.ToString();
}
__instance.UpdateMaxPlayersButtons(__instance.GetTargetOptions());
}));
Object.Destroy(firstButtonRenderer);
var lastButtonRenderer = __instance.MaxPlayerButtons[^1];
lastButtonRenderer.GetComponentInChildren<TextMeshPro>().text = "+";
lastButtonRenderer.enabled = false;
var lastButtonButton = lastButtonRenderer.GetComponent<PassiveButton>();
lastButtonButton.OnClick.RemoveAllListeners();
lastButtonButton.OnClick.AddListener((Action)(() =>
{
for (var i = 1; i < 11; i++)
{
var playerButton = __instance.MaxPlayerButtons[i];
var tmp = playerButton.GetComponentInChildren<TextMeshPro>();
var newValue = Mathf.Min(byte.Parse(tmp.text) + 10,
MaxPlayers - 14 + byte.Parse(playerButton.name));
tmp.text = newValue.ToString();
}
__instance.UpdateMaxPlayersButtons(__instance.GetTargetOptions());
}));
Object.Destroy(lastButtonRenderer);
for (var i = 1; i < 11; i++)
{
var playerButton = __instance.MaxPlayerButtons[i].GetComponent<PassiveButton>();
var text = playerButton.GetComponentInChildren<TextMeshPro>();
playerButton.OnClick.RemoveAllListeners();
playerButton.OnClick.AddListener((Action)(() =>
{
var maxPlayers = byte.Parse(text.text);
var maxImp = Mathf.Min(__instance.GetTargetOptions().NumImpostors, maxPlayers / 2);
__instance.GetTargetOptions().SetInt(Int32OptionNames.NumImpostors, maxImp);
__instance.ImpostorButtons[1].TextMesh.text = maxImp.ToString();
__instance.SetMaxPlayersButtons(maxPlayers);
}));
}
foreach (var button in __instance.MaxPlayerButtons)
{
button.enabled = button.GetComponentInChildren<TextMeshPro>().text == __instance.GetTargetOptions().MaxPlayers.ToString();
}
}
{
var secondButton = __instance.ImpostorButtons[1];
secondButton.SpriteRenderer.enabled = false;
Object.Destroy(secondButton.transform.FindChild("ConsoleHighlight").gameObject);
Object.Destroy(secondButton.PassiveButton);
Object.Destroy(secondButton.BoxCollider);
var secondButtonText = secondButton.TextMesh;
secondButtonText.text = __instance.GetTargetOptions().NumImpostors.ToString();
var firstButton = __instance.ImpostorButtons[0];
firstButton.SpriteRenderer.enabled = false;
firstButton.TextMesh.text = "-";
var firstPassiveButton = firstButton.PassiveButton;
firstPassiveButton.OnClick.RemoveAllListeners();
firstPassiveButton.OnClick.AddListener((Action)(() =>
{
var newVal = Mathf.Clamp(
byte.Parse(secondButtonText.text) - 1,
1,
__instance.GetTargetOptions().MaxPlayers / 2
);
__instance.SetImpostorButtons(newVal);
secondButtonText.text = newVal.ToString();
}));
var thirdButton = __instance.ImpostorButtons[2];
thirdButton.SpriteRenderer.enabled = false;
thirdButton.TextMesh.text = "+";
var thirdPassiveButton = thirdButton.PassiveButton;
thirdPassiveButton.OnClick.RemoveAllListeners();
thirdPassiveButton.OnClick.AddListener((Action)(() =>
{
var newVal = Mathf.Clamp(
byte.Parse(secondButtonText.text) + 1,
1,
__instance.GetTargetOptions().MaxPlayers / 2
);
__instance.SetImpostorButtons(newVal);
secondButtonText.text = newVal.ToString();
}));
}
}
}
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateMaxPlayersButtons))]
public static class CreateOptionsPickerUpdateMaxPlayersButtons
{
public static bool Prefix(CreateOptionsPicker __instance, [HarmonyArgument(0)] IGameOptions opts)
{
if (__instance.CrewArea)
{
__instance.CrewArea.SetCrewSize(opts.MaxPlayers, opts.NumImpostors);
}
var selectedAsString = opts.MaxPlayers.ToString();
for (var i = 1; i < __instance.MaxPlayerButtons.Count - 1; i++)
{
__instance.MaxPlayerButtons[i].enabled = __instance.MaxPlayerButtons[i].GetComponentInChildren<TextMeshPro>().text == selectedAsString;
}
return false;
}
}
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateImpostorsButtons))]
public static class CreateOptionsPickerUpdateImpostorsButtons
{
public static bool Prefix()
{
return false;
}
}
[HarmonyPatch(typeof(GameOptionsData), nameof(GameOptionsData.AreInvalid))]
public static class InvalidOptionsPatches
{
public static bool Prefix(GameOptionsData __instance, [HarmonyArgument(0)] int maxExpectedPlayers)
{
return __instance.MaxPlayers > maxExpectedPlayers ||
__instance.NumImpostors < 1 ||
__instance.NumImpostors + 1 > maxExpectedPlayers / 2 ||
__instance.KillDistance is < 0 or > 2 ||
__instance.PlayerSpeedMod is <= 0f or > 3f;
}
}
[HarmonyPatch(typeof(SecurityLogger), nameof(SecurityLogger.Awake))]
public static class SecurityLoggerPatch
{
public static void Postfix(ref SecurityLogger __instance)
{
__instance.Timers = new float[127];
}
}
}
}
+56 -47
View File
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using BepInEx.Unity.IL2CPP.Utils.Collections;
using EHR.Patches;
using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using TMPro;
@@ -300,52 +301,58 @@ public static class GameOptionsMenuPatch
private static BaseGameSetting GetSetting(OptionItem item)
{
// ReSharper disable Unity.IncorrectScriptableObjectInstantiation
BaseGameSetting baseGameSetting = item switch
BaseGameSetting baseGameSetting;
switch (item)
{
BooleanOptionItem => new CheckboxGameSetting
{
Type = OptionTypes.Checkbox
},
IntegerOptionItem integerOptionItem => new IntGameSetting
{
Type = OptionTypes.Int,
Value = integerOptionItem.GetInt(),
Increment = integerOptionItem.Rule.Step,
ValidRange = new(integerOptionItem.Rule.MinValue, integerOptionItem.Rule.MaxValue),
ZeroIsInfinity = false,
SuffixType = NumberSuffixes.Multiplier,
FormatString = string.Empty
},
FloatOptionItem floatOptionItem => new FloatGameSetting
{
Type = OptionTypes.Float,
Value = floatOptionItem.GetFloat(),
Increment = floatOptionItem.Rule.Step,
ValidRange = new(floatOptionItem.Rule.MinValue, floatOptionItem.Rule.MaxValue),
ZeroIsInfinity = false,
SuffixType = NumberSuffixes.Multiplier,
FormatString = string.Empty
},
StringOptionItem stringOptionItem => new StringGameSetting
{
Type = OptionTypes.String,
Values = new StringNames[stringOptionItem.Selections.Count],
Index = stringOptionItem.GetInt()
},
PresetOptionItem presetOptionItem => new IntGameSetting
{
Type = OptionTypes.Int,
Value = presetOptionItem.GetInt(),
Increment = presetOptionItem.Rule.Step,
ValidRange = new(presetOptionItem.Rule.MinValue, presetOptionItem.Rule.MaxValue),
ZeroIsInfinity = false,
SuffixType = NumberSuffixes.Multiplier,
FormatString = string.Empty
},
_ => null
};
// ReSharper restore Unity.IncorrectScriptableObjectInstantiation
case BooleanOptionItem:
var checkboxGameSetting = ScriptableObject.CreateInstance<CheckboxGameSetting>();
checkboxGameSetting.Type = OptionTypes.Checkbox;
baseGameSetting = checkboxGameSetting;
break;
case IntegerOptionItem integerOptionItem:
var intGameSetting = ScriptableObject.CreateInstance<IntGameSetting>();
intGameSetting.Type = OptionTypes.Int;
intGameSetting.Value = integerOptionItem.GetInt();
intGameSetting.Increment = integerOptionItem.Rule.Step;
intGameSetting.ValidRange = new(integerOptionItem.Rule.MinValue, integerOptionItem.Rule.MaxValue);
intGameSetting.ZeroIsInfinity = false;
intGameSetting.SuffixType = NumberSuffixes.Multiplier;
intGameSetting.FormatString = string.Empty;
baseGameSetting = intGameSetting;
break;
case FloatOptionItem floatOptionItem:
var floatGameSetting = ScriptableObject.CreateInstance<FloatGameSetting>();
floatGameSetting.Type = OptionTypes.Float;
floatGameSetting.Value = floatOptionItem.GetFloat();
floatGameSetting.Increment = floatOptionItem.Rule.Step;
floatGameSetting.ValidRange = new(floatOptionItem.Rule.MinValue, floatOptionItem.Rule.MaxValue);
floatGameSetting.ZeroIsInfinity = false;
floatGameSetting.SuffixType = NumberSuffixes.Multiplier;
floatGameSetting.FormatString = string.Empty;
baseGameSetting = floatGameSetting;
break;
case StringOptionItem stringOptionItem:
var stringGameSetting = ScriptableObject.CreateInstance<StringGameSetting>();
stringGameSetting.Type = OptionTypes.String;
stringGameSetting.Values = new StringNames[stringOptionItem.Selections.Count];
stringGameSetting.Index = stringOptionItem.GetInt();
baseGameSetting = stringGameSetting;
break;
case PresetOptionItem presetOptionItem:
var presetIntGameSetting = ScriptableObject.CreateInstance<IntGameSetting>();
presetIntGameSetting.Type = OptionTypes.Int;
presetIntGameSetting.Value = presetOptionItem.GetInt();
presetIntGameSetting.Increment = presetOptionItem.Rule.Step;
presetIntGameSetting.ValidRange = new(presetOptionItem.Rule.MinValue, presetOptionItem.Rule.MaxValue);
presetIntGameSetting.ZeroIsInfinity = false;
presetIntGameSetting.SuffixType = NumberSuffixes.Multiplier;
presetIntGameSetting.FormatString = string.Empty;
baseGameSetting = presetIntGameSetting;
break;
default:
baseGameSetting = null;
break;
}
if (baseGameSetting != null)
{
@@ -426,8 +433,10 @@ public static class NumberOptionPatch
__instance.Increment = 0.05f;
__instance.Value = (float)Math.Round(__instance.Value, 2);
break;
case StringNames.GameNumImpostors when DebugModeManager.IsDebugMode:
__instance.ValidRange.min = 0;
case StringNames.GameNumImpostors:
__instance.ValidRange = new(1, Crowded.MaxImpostors);
__instance.Value = (float)Math.Round(__instance.Value, 2);
if (DebugModeManager.AmDebugger) __instance.ValidRange.min = 0;
break;
}
+1 -1
View File
@@ -136,7 +136,7 @@ public class GameStartManagerPatch
}
}
if (AmongUsClient.Instance == null || GameData.Instance == null || !AmongUsClient.Instance.AmHost || !GameData.Instance) return false;
if (AmongUsClient.Instance == null || GameData.Instance == null || !AmongUsClient.Instance.AmHost || !GameData.Instance) return true;
if (Main.AutoStart != null && Main.AutoStart.Value)
{
+30 -15
View File
@@ -418,24 +418,28 @@ class BeginCrewmatePatch
CustomRoles.Disperser
=> ShipStatus.Instance.VentMoveSounds.FirstOrDefault(),
_ => PlayerControl.LocalPlayer.GetCustomRoleTypes() switch
{
CustomRoleTypes.Impostor => GetIntroSound(RoleTypes.Impostor),
CustomRoleTypes.Crewmate => GetIntroSound(RoleTypes.Crewmate),
CustomRoleTypes.Neutral => GetIntroSound(RoleTypes.Shapeshifter),
_ => GetIntroSound(RoleTypes.Crewmate)
}
CustomRoles.Tracker
or CustomRoles.EvilTracker
=> GetIntroSound(RoleTypes.Tracker),
CustomRoles.Noisemaker
or CustomRoles.NoisemakerEHR
=> GetIntroSound(RoleTypes.Noisemaker),
CustomRoles.Phantom
or CustomRoles.PhantomEHR
=> GetIntroSound(RoleTypes.Phantom),
CustomRoles.Shapeshifter
or CustomRoles.ShapeshifterEHR
=> GetIntroSound(RoleTypes.Shapeshifter),
_ => GetAudioClipFromCustomRoleType()
};
}
catch (Exception ex)
{
PlayerControl.LocalPlayer.Data.Role.IntroSound = PlayerControl.LocalPlayer.GetCustomRoleTypes() switch
{
CustomRoleTypes.Impostor => GetIntroSound(RoleTypes.Impostor),
CustomRoleTypes.Crewmate => GetIntroSound(RoleTypes.Crewmate),
CustomRoleTypes.Neutral => GetIntroSound(RoleTypes.Shapeshifter),
_ => GetIntroSound(RoleTypes.Crewmate)
};
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetAudioClipFromCustomRoleType();
Logger.Warn($"Could not set intro sound\n{ex}", "IntroSound");
}
@@ -555,6 +559,17 @@ class BeginCrewmatePatch
__instance.TeamTitle.color = Color.magenta;
StartFadeIntro(__instance, Color.magenta, Color.magenta);
}
AudioClip GetAudioClipFromCustomRoleType()
{
return PlayerControl.LocalPlayer.GetCustomRoleTypes() switch
{
CustomRoleTypes.Impostor => GetIntroSound(RoleTypes.Impostor),
CustomRoleTypes.Crewmate => GetIntroSound(RoleTypes.Crewmate),
CustomRoleTypes.Neutral => GetIntroSound(RoleTypes.Shapeshifter),
_ => GetIntroSound(RoleTypes.Crewmate)
};
}
}
public static AudioClip GetIntroSound(RoleTypes roleType)
@@ -743,7 +758,7 @@ class IntroCutsceneDestroyPatch
5 => new RandomSpawn.FungleSpawnMap(),
_ => null
};
if (map != null) Main.AllAlivePlayerControls.Do(map.RandomTeleport);
if (map != null && AmongUsClient.Instance.AmHost) Main.AllAlivePlayerControls.Do(map.RandomTeleport);
}
if (Main.ResetCamPlayerList.Contains(PlayerControl.LocalPlayer.PlayerId))
-26
View File
@@ -892,34 +892,8 @@ class MeetingHudStartPatch
// Name Color Manager
pva.NameText.text = pva.NameText.text.ApplyNameColorData(seer, target, true);
var seerRole = seer.GetCustomRole();
// Guesser Mode //
if (((Options.GuesserMode.GetBool() && !seer.Data.IsDead && !target.Data.IsDead &&
((Options.CrewmatesCanGuess.GetBool() && seer.IsCrewmate() && !seer.Is(CustomRoles.Judge) && !seer.Is(CustomRoles.NiceSwapper) && !seer.Is(CustomRoles.Lookout) && !seer.Is(CustomRoles.ParityCop)) ||
(Options.ImpostorsCanGuess.GetBool() && seerRole.IsImpostor() && !seer.Is(CustomRoles.Councillor)) ||
(Options.NeutralKillersCanGuess.GetBool() && seer.IsNeutralKiller()) ||
(Options.PassiveNeutralsCanGuess.GetBool() && seerRole.IsNonNK() && !seer.Is(CustomRoles.Doomsayer)))) ||
// Other Roles and Add-ons that can see player IDs //
(seer.Data.IsDead && !target.Data.IsDead && seerRole is CustomRoles.Mafia) ||
(!seer.Data.IsDead && !target.Data.IsDead &&
(seer.Is(CustomRoles.Guesser) ||
seerRole is
CustomRoles.Judge or
CustomRoles.NiceSwapper or
CustomRoles.Lookout or
CustomRoles.ParityCop or
CustomRoles.Councillor or
CustomRoles.Doomsayer or
CustomRoles.EvilGuesser or
CustomRoles.NiceGuesser
))))
{
pva.NameText.text = $"{Utils.ColorString(Utils.GetRoleColor(seerRole), target.PlayerId.ToString())} {pva.NameText.text}";
}
if (seer.KnowDeathReason(target))
sb.Append($"({Utils.ColorString(Utils.GetRoleColor(CustomRoles.Doctor), Utils.GetVitalText(target.PlayerId))})");
+45 -25
View File
@@ -209,8 +209,9 @@ internal class ChangeRoleSettings
Provocateur.Provoked = [];
Crusader.ForCrusade = [];
Godfather.GodfatherTarget = byte.MaxValue;
ChatManager.ResetHistory();
CustomNetObject.Reset();
Crewpostor.TasksDone = [];
Express.SpeedNormal = [];
Express.SpeedUp = [];
ReportDeadBodyPatch.CanReport = [];
SabotageMapPatch.TimerTexts = [];
@@ -225,6 +226,8 @@ internal class ChangeRoleSettings
RandomSpawn.CustomNetworkTransformPatch.NumOfTP = [];
AFKDetector.ShieldedPlayers.Clear();
MeetingTimeManager.Init();
Main.DefaultCrewmateVision = Main.RealOptionsData.GetFloat(FloatOptionNames.CrewLightMod);
Main.DefaultImpostorVision = Main.RealOptionsData.GetFloat(FloatOptionNames.ImpostorLightMod);
@@ -305,10 +308,6 @@ internal class ChangeRoleSettings
Logger.Exception(ex, "Init Roles");
}
Crewpostor.TasksDone = [];
Express.SpeedNormal = [];
Express.SpeedUp = [];
Main.ChangedRole = false;
try
@@ -332,6 +331,8 @@ internal class ChangeRoleSettings
DoorsReset.Initialize();
GhostRolesManager.Initialize();
RoleBlockManager.Reset();
ChatManager.ResetHistory();
CustomNetObject.Reset();
IRandom.SetInstanceById(Options.RoleAssigningAlgorithm.GetValue());
@@ -406,18 +407,26 @@ internal class SelectRolesPatch
}
var rd = IRandom.Instance;
BasisChangingAddons.Remove(CustomRoles.Bloodlust);
bool bloodlustSpawn = rd.Next(1, 100) <= (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Bloodlust, out var option3) ? option3.GetFloat() : 0) && CustomRoles.Bloodlust.IsEnable();
HashSet<byte> bloodlustList = RoleResult.Where(x => x.Value.IsCrewmate() && !x.Value.IsTaskBasedCrewmate()).Select(x => x.Key.PlayerId).ToHashSet();
if (bloodlustList.Count == 0) bloodlustSpawn = false;
if (Main.SetAddOns.Values.Any(x => x.Contains(CustomRoles.Bloodlust)))
{
bloodlustSpawn = true;
bloodlustList = Main.SetAddOns.Where(x => x.Value.Contains(CustomRoles.Bloodlust)).Select(x => x.Key).ToHashSet();
}
BasisChangingAddons.Clear();
if (bloodlustSpawn) BasisChangingAddons[CustomRoles.Bloodlust] = bloodlustList.Shuffle().Take(CustomRoles.Bloodlust.GetCount()).ToList();
try
{
var rd = IRandom.Instance;
bool bloodlustSpawn = rd.Next(1, 100) <= (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Bloodlust, out var option3) ? option3.GetFloat() : 0) && CustomRoles.Bloodlust.IsEnable();
HashSet<byte> bloodlustList = RoleResult.Where(x => x.Value.IsCrewmate() && !x.Value.IsTaskBasedCrewmate() && (Options.UsePets.GetBool() || x.Value.GetRoleTypes() != RoleTypes.Impostor)).Select(x => x.Key.PlayerId).ToHashSet();
if (bloodlustList.Count == 0) bloodlustSpawn = false;
if (Main.SetAddOns.Values.Any(x => x.Contains(CustomRoles.Bloodlust)))
{
bloodlustSpawn = true;
bloodlustList = Main.SetAddOns.Where(x => x.Value.Contains(CustomRoles.Bloodlust)).Select(x => x.Key).ToHashSet();
}
if (bloodlustSpawn) BasisChangingAddons[CustomRoles.Bloodlust] = bloodlustList.Shuffle().Take(CustomRoles.Bloodlust.GetCount()).ToList();
}
catch (Exception e)
{
Utils.ThrowException(e);
}
Dictionary<(byte, byte), RoleTypes> rolesMap = [];
@@ -450,8 +459,6 @@ internal class SelectRolesPatch
{
var rd = IRandom.Instance;
new[] { CustomRoles.Nimble, CustomRoles.Physicist, CustomRoles.Finder, CustomRoles.Noisy }.Do(x => BasisChangingAddons.Remove(x));
bool physicistSpawn = rd.Next(100) < (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Physicist, out var option1) ? option1.GetFloat() : 0) && CustomRoles.Physicist.IsEnable();
bool nimbleSpawn = rd.Next(100) < (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Nimble, out var option2) ? option2.GetFloat() : 0) && CustomRoles.Nimble.IsEnable();
bool finderSpawn = rd.Next(100) < (Options.CustomAdtRoleSpawnRate.TryGetValue(CustomRoles.Finder, out var option3) ? option3.GetFloat() : 0) && CustomRoles.Finder.IsEnable();
@@ -664,19 +671,19 @@ internal class SelectRolesPatch
var aapc = Main.AllAlivePlayerControls;
var addonNum = aapc.ToDictionary(x => x, _ => 0);
AddonRolesList
.Except(BasisChangingAddons.Keys)
.Where(x => x.IsEnable())
.SelectMany(x => Enumerable.Repeat(x, Math.Clamp(x.GetCount(), 0, aapc.Length)))
.Where(x => IRandom.Instance.Next(1, 100) <= (Options.CustomAdtRoleSpawnRate.TryGetValue(x, out var sc) ? sc.GetFloat() : 0))
.Shuffle()
.Chunk(aapc.Length)
.SelectMany(a => a.Select(x =>
.Select(x =>
{
var suitablePlayer = aapc
.OrderBy(p => addonNum[p])
.FirstOrDefault(p => CustomRolesHelper.CheckAddonConflict(x, p));
if (suitablePlayer != null) addonNum[suitablePlayer]++;
return (Role: x, SuitablePlayer: suitablePlayer);
}))
})
.DoIf(x => x.SuitablePlayer != null, x => Main.PlayerStates[x.SuitablePlayer.PlayerId].SetSubRole(x.Role));
@@ -863,12 +870,25 @@ internal class SelectRolesPatch
{
foreach (PlayerControl seer in Main.AllPlayerControls)
{
var sender = senders[seer.PlayerId];
foreach (PlayerControl target in Main.AllPlayerControls)
{
if (rolesMap.TryGetValue((seer.PlayerId, target.PlayerId), out var role))
try
{
if (rolesMap.TryGetValue((seer.PlayerId, target.PlayerId), out var role))
{
// Change Scientist to Noisemaker when the role is desync and target have the Noisemaker role << Thanks: TommyXL
if (role is RoleTypes.Scientist && RoleResult.Any(x => x.Key.PlayerId == seer.PlayerId && x.Value is CustomRoles.NoisemakerEHR or CustomRoles.Noisemaker))
{
Logger.Info($"seer: {seer.PlayerId}, target: {target.PlayerId}, {role} => {RoleTypes.Noisemaker}", "OverrideRoleForDesync");
role = RoleTypes.Noisemaker;
}
var sender = senders[seer.PlayerId];
sender.RpcSetRole(seer, role, target.GetClientId());
}
}
catch
{
sender.RpcSetRole(seer, role, target.GetClientId());
}
}
}
+2 -1
View File
@@ -48,7 +48,7 @@ class EndGamePatch
SummaryText[id] = Utils.SummaryTexts(id, disableColor: false);
if (state.SubRoles.Count == 0) continue;
Main.LastAddOns[id] = $"<size=70%>{Utils.ColorString(Main.PlayerColors.GetValueOrDefault(id, Color.white), Main.AllPlayerNames.GetValueOrDefault(id, $"ID {id}"))}: {state.SubRoles.Join(x => x.ToColoredString())}</size>";
Main.LastAddOns[id] = $"<size=70%>{id.ColoredPlayerName()}: {state.SubRoles.Join(x => x.ToColoredString())}</size>";
}
if (Options.DumpLogAfterGameEnd.GetBool())
@@ -110,6 +110,7 @@ class EndGamePatch
CustomNetObject.Reset();
Main.LoversPlayers.Clear();
Bloodmoon.OnMeetingStart();
AFKDetector.ExemptedPlayers.Clear();
foreach (var state in Main.PlayerStates.Values)
{
+2 -2
View File
@@ -75,7 +75,7 @@ class ExternalRpcPetPatch
Logger.Info($"Player {pc.GetNameWithRole().RemoveHtmlTags()} petted their pet", "PetActionTrigger");
LateTask.New(() => { OnPetUse(pc); }, 0.2f, $"OnPetUse: {pc.GetNameWithRole().RemoveHtmlTags()}", false);
LateTask.New(() => OnPetUse(pc), 0.2f, $"OnPetUse: {pc.GetNameWithRole().RemoveHtmlTags()}", false);
}
public static void OnPetUse(PlayerControl pc)
@@ -109,7 +109,7 @@ class ExternalRpcPetPatch
if (target != null) hasKillTarget = true;
if (!pc.CanUseKillButton()) hasKillTarget = false;
if (pc.GetCustomRole().UsesPetInsteadOfKill() && hasKillTarget && pc.Data.RoleType != RoleTypes.Impostor)
if (pc.GetCustomRole().UsesPetInsteadOfKill() && hasKillTarget && (pc.Data.RoleType != RoleTypes.Impostor || pc.GetCustomRole() is CustomRoles.Necromancer or CustomRoles.Deathknight))
{
pc.AddKCDAsAbilityCD();
if (Main.PlayerStates[pc.PlayerId].Role.OnCheckMurder(pc, target))
+30 -3
View File
@@ -150,7 +150,6 @@ class CheckMurderPatch
if (target.Data == null
|| target.inVent
|| target.walkingToVent
|| target.inMovingPlat
|| target.MyPhysics.Animations.IsPlayingEnterVentAnimation()
|| target.MyPhysics.Animations.IsPlayingAnyLadderAnimation()
@@ -312,6 +311,12 @@ class CheckMurderPatch
return false;
}
if (AFKDetector.ShieldedPlayers.Contains(target.PlayerId))
{
Notify("AFKShielded");
return false;
}
if ((killer.Is(CustomRoles.Jackal) && target.Is(CustomRoles.Sidekick) && !Options.JackalCanKillSidekick.GetBool()) ||
(killer.Is(CustomRoles.Sidekick) && target.Is(CustomRoles.Jackal) && !Options.SidekickCanKillJackal.GetBool()) ||
(killer.Is(CustomRoles.Jackal) && target.Is(CustomRoles.Recruit) && !Options.JackalCanKillSidekick.GetBool()) ||
@@ -1040,7 +1045,7 @@ class ReportDeadBodyPatch
Camouflage.RpcSetSkin(pc, RevertToDefault: true);
}
if (Main.CurrentMap == MapNames.Fungle && pc.IsMushroomMixupActive())
if (Main.CurrentMap == MapNames.Fungle && (pc.IsMushroomMixupActive() || IsActive(SystemTypes.MushroomMixupSabotage)))
{
pc.FixMixedUpOutfit();
}
@@ -1159,7 +1164,12 @@ class FixedUpdatePatch
NameNotifyManager.OnFixedUpdate(player);
TargetArrow.OnFixedUpdate(player);
LocateArrow.OnFixedUpdate(player);
Camouflage.OnFixedUpdate(player);
if (AmongUsClient.Instance.AmHost)
{
Camouflage.OnFixedUpdate(player);
AFKDetector.OnFixedUpdate(player);
}
if (RPCHandlerPatch.ReportDeadBodyRPCs.Remove(playerId))
Logger.Info($"Cleared ReportDeadBodyRPC Count for {player.GetRealName().RemoveHtmlTags()}", "FixedUpdatePatch");
@@ -1428,6 +1438,8 @@ class FixedUpdatePatch
if (self) Suffix.Append(CustomTeamManager.GetSuffix(seer));
Suffix.Append(AFKDetector.GetSuffix(seer, target));
switch (target.GetCustomRole())
{
case CustomRoles.Snitch when seer.IsImpostor() && target.Is(CustomRoles.Madmate) && target.GetTaskState().IsTaskFinished:
@@ -2141,4 +2153,19 @@ class CmdCheckAppearPatch
return false;
}
}
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.AssertWithTimeout))]
class AssertWithTimeoutPatch
{
public static bool Prefix() => false;
}
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CmdCheckName))]
class CmdCheckNameVersionCheckPatch
{
public static void Postfix(PlayerControl __instance)
{
RPC.RpcVersionCheck();
}
}
+47 -2
View File
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AmongUs.Data;
@@ -7,6 +8,7 @@ using EHR.Crewmate;
using EHR.Modules;
using EHR.Neutral;
using HarmonyLib;
using Hazel;
using InnerNet;
using static EHR.Translator;
@@ -84,6 +86,33 @@ class OnPlayerJoinedPatch
public static void Postfix( /*AmongUsClient __instance,*/ [HarmonyArgument(0)] ClientData client)
{
Logger.Info($"{client.PlayerName} (ClientID: {client.Id} / FriendCode: {client.FriendCode}) joined the lobby", "Session");
LateTask.New(() =>
{
try
{
if (AmongUsClient.Instance.AmHost)
{
if (!client.Character.Data.Disconnected && client.Character.Data.IsIncomplete)
{
Logger.SendInGame(GetString("Error.InvalidColor") + $" {client.Id}/{client.PlayerName}");
AmongUsClient.Instance.KickPlayer(client.Id, false);
Logger.Info($"Kicked client {client.Id}/{client.PlayerName} because PlayerControl is not spawned in time.", "OnPlayerJoinedPatchPostfix");
return;
}
if (!Main.PlayerVersion.TryGetValue(client.Character.PlayerId, out _))
{
var retry = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.RequestRetryVersionCheck, SendOption.None, client.Id);
AmongUsClient.Instance.FinishRpcImmediately(retry);
}
}
}
catch
{
}
}, 3f, "green bean kick late task", false);
if (AmongUsClient.Instance.AmHost && client.FriendCode == "" && Options.KickPlayerFriendCodeNotExist.GetBool() && !GameStates.IsLocalGame)
{
if (!BanManager.TempBanWhiteList.Contains(client.GetHashedPuid()))
@@ -101,7 +130,7 @@ class OnPlayerJoinedPatch
Logger.Info(msg, "Android Kick");
}
if (AmongUsClient.Instance.AmHost && client.PlayerName.Contains("Silasticm", StringComparison.OrdinalIgnoreCase))
if (AmongUsClient.Instance.AmHost && (client.PlayerName.EndsWith("cm", StringComparison.OrdinalIgnoreCase) || client.PlayerName.EndsWith("sm", StringComparison.OrdinalIgnoreCase)) && (client.PlayerName.Length == 4 || client.PlayerName.Count(x => x is 'i' or 'I') >= 2))
{
AmongUsClient.Instance.KickPlayer(client.Id, false);
Logger.SendInGame("They were probably hacking tbh");
@@ -176,7 +205,7 @@ class OnPlayerLeftPatch
if (Main.HostClientId == __instance.ClientId)
{
var clientId = -1;
const int clientId = -1;
var player = PlayerControl.LocalPlayer;
var title = "<color=#aaaaff>" + GetString("DefaultSystemMessageTitle") + "</color>";
var name = player?.Data?.PlayerName;
@@ -238,6 +267,22 @@ class OnPlayerLeftPatch
Main.SayStartTimes.Remove(__instance.ClientId);
Main.SayBanwordsTimes.Remove(__instance.ClientId);
Main.PlayerVersion.Remove(data?.Character?.PlayerId ?? byte.MaxValue);
Main.MessagesToSend.RemoveAll(x => x.RECEIVER_ID == data?.Character.PlayerId);
if (data != null && data.Character != null)
{
var netid = data.Character.NetId;
LateTask.New(() =>
{
if (GameStates.IsOnlineGame)
{
MessageWriter messageWriter = AmongUsClient.Instance.Streams[1];
messageWriter.StartMessage(5);
messageWriter.WritePacked(netid);
messageWriter.EndMessage();
}
}, 2.5f, "Repeat Despawn", false);
}
}
Utils.CountAlivePlayers(true);
+35 -21
View File
@@ -1143,6 +1143,7 @@
"MenuTitle.Guessers": "★ Guesser Mode ★",
"MenuTitle.GuesserModeRoles": "★ Roles and Add-ons for Guesser Mode ★",
"MenuTitle.CTA": "★ Custom Team Assignment ★",
"MenuTitle.AFKDetection": "★ AFK Detection ★",
"ShieldPersonDiedFirst": "Shield the person who got killed first in the last game",
"SidekickKnowOtherSidekick": "<color=#00b4eb>Sidekicks</color> know each other",
"SidekickKnowOtherSidekickRole": "<color=#00b4eb>Sidekicks</color> know the roles of other <color=#00b4eb>Sidekicks</color>",
@@ -1639,6 +1640,7 @@
"DoppelgangerWarning": "<#ffff00>The Doppelganger swapped skins with you!</color>",
"CheckMurderFail": "<#ff0000>The murder was canceled due to the following reason:\\n</color>",
"SameCTATeam": "The player you attacked is in the same custom team as you",
"AFKShielded": "According to the Host's settings, AFK players cannot be killed",
"JackalSidekick": "This Jackal/Sidekick/Recruit interaction is not allowed",
"ContagiousPlayers": "Contagious players cannot kill each other",
"MadmateKillImpostor": "Madmates cannot kill Impostors",
@@ -1680,6 +1682,14 @@
"KickCommandKicked": "was kicked from the game.",
"KickCommandKickedRole": "Their role was",
"ModeratorTag": "<#8000ff>Moderator</color>",
"EnableAFKDetector": "Enable AFK Detector",
"AFKWarning": "<#ffff00>Please move to avoid being marked as AFK</color> <#ffffff>({0})</color>",
"AFKSuffix": "<#ffa500>(AFK)</color>",
"AFKConsequence": "Consequence for AFK players",
"AFKConsequence.Nothing": "Nothing",
"AFKConsequence.Shield": "Shield",
"AFKConsequence.Suicide": "Suicide",
"AFKConsequence.Kick": "Kick",
"DeathReason.Kill": "Kill",
"DeathReason.Vote": "Ejected",
"DeathReason.Suicide": "Suicide",
@@ -1697,6 +1707,7 @@
"DeathReason.Execution": "Executed",
"DeathReason.Disconnected": "Disconnected",
"DeathReason.Fall": "Fall",
"DeathReason.AFK": "AFK",
"DeathReason.Revenge": "Revenge",
"DeathReason.Eaten": "Eaten",
"DeathReason.Sacrifice": "Victim",
@@ -2226,27 +2237,27 @@
"RoleRateNoColor": "Random",
"RoleOnNoColor": "Always",
"RoleOffNoColor": "Disabled",
"Rate0": "<#444444>0%</color>",
"Rate5": "<#808F8F>5%</color>",
"Rate10": "<#799595>10%</color>",
"Rate15": "<#719C9C>15%</color>",
"Rate20": "<#6AA2A2>20%</color>",
"Rate25": "<#62A9A9>25%</color>",
"Rate30": "<#5BB0B0>30%</color>",
"Rate35": "<#53B6B6>35%</color>",
"Rate40": "<#4CBDBD>40%</color>",
"Rate45": "<#44C4C4>45%</color>",
"Rate50": "<#3CCACA>50%</color>",
"Rate55": "<#35D1D1>55%</color>",
"Rate60": "<#2DD7D7>60%</color>",
"Rate65": "<#26DEDE>65%</color>",
"Rate70": "<#1EE5E5>70%</color>",
"Rate75": "<#17EBEB>75%</color>",
"Rate80": "<#0FF2F2>80%</color>",
"Rate85": "<#08F8F8>85%</color>",
"Rate90": "<#07F9F9>90%</color>",
"Rate95": "<#00FFFF>95%</color>",
"Rate100": "<#00FFA5>100%</color>",
"Rate0": "<#440000>0%</color>",
"Rate5": "<#00ffff>5%</color>",
"Rate10": "<#00fffb>10%</color>",
"Rate15": "<#00fff8>15%</color>",
"Rate20": "<#00fff4>20%</color>",
"Rate25": "<#00fff0>25%</color>",
"Rate30": "<#00ffec>30%</color>",
"Rate35": "<#00ffe7>35%</color>",
"Rate40": "<#00ffe3>40%</color>",
"Rate45": "<#00ffde>45%</color>",
"Rate50": "<#00ffd9>50%</color>",
"Rate55": "<#00ffd4>55%</color>",
"Rate60": "<#00ffcf>60%</color>",
"Rate65": "<#00ffc9>65%</color>",
"Rate70": "<#00ffc4>70%</color>",
"Rate75": "<#00ffbe>75%</color>",
"Rate80": "<#00ffb8>80%</color>",
"Rate85": "<#00ffb2>85%</color>",
"Rate90": "<#00ffac>90%</color>",
"Rate95": "<#00ffa6>95%</color>",
"Rate100": "<#ffff44>100%</color>",
"Preset": "Preset",
"Preset_1": "Preset 1",
"Preset_2": "Preset 2",
@@ -2395,6 +2406,8 @@
"KickXboxPlayer": "<b>{0}</b> was kicked due to playing on Xbox.",
"KickPlayStationPlayer": "<b>{0}</b> was kicked due to playing on PlayStation.",
"KickNintendoPlayer": "<b>{0}</b> was kicked due to playing on Nintendo Switch.",
"AFKKick": "{0} was kicked because they went AFK",
"AFKTooMany": "AFK detection was disabled for this round since there are too many AFK players",
"KBDisplayScore": "Rank: {0} Kills: {1}",
"KBTimeRemain": "<color=#00ffa5>Time Remaining:</color> <b>{0}</b>s",
"ResetDoorsEveryTurns": "Reset All Doors After Meetings",
@@ -2547,6 +2560,7 @@
"pet_UFO": "UFO",
"pet_D2WormPet": "Worm",
"pet_RANDOM_FOR_EVERYONE": "<color=#ffff00>Random</color>",
"PlayerExemptedFromAFK": "{0} was exempted from AFK detection for 1 game",
"Swapster": "Swapster",
"SwapsterInfo": "Swap Players",
"SwapsterInfoLong": "(Impostors):\\nAs the Swapster, you can select 2 players using your shapeshift menu to swap them.",
+1
View File
@@ -11,6 +11,7 @@ namespace EHR.AddOns.GhostRoles
private static readonly Dictionary<byte, long> ScheduledDeaths = [];
public Team Team => Team.Impostor | Team.Neutral;
public int Cooldown => Duration.GetInt() + CD.GetInt();
public bool ChangeToGA => true;
public void OnAssign(PlayerControl pc)
{
+1
View File
@@ -11,6 +11,7 @@ namespace EHR.AddOns.GhostRoles
public Team Team => Team.Crewmate;
public int Cooldown => CD.GetInt();
public bool ChangeToGA => true;
public void OnProtect(PlayerControl pc, PlayerControl target)
{
+1
View File
@@ -21,6 +21,7 @@ namespace EHR.AddOns.GhostRoles
private long WarnTimeStamp = 0;
public Team Team => Team.Crewmate | Team.Neutral;
public int Cooldown => 900;
public bool ChangeToGA => false;
public void OnProtect(PlayerControl pc, PlayerControl target)
{
+1
View File
@@ -4,6 +4,7 @@
{
public Team Team { get; }
public int Cooldown { get; }
public bool ChangeToGA { get; }
public void OnProtect(PlayerControl pc, PlayerControl target);
public void OnAssign(PlayerControl pc);
}
+1
View File
@@ -11,6 +11,7 @@ namespace EHR.AddOns.GhostRoles
public Team Team => Team.Impostor;
public int Cooldown => CD.GetInt();
public bool ChangeToGA => true;
public void OnProtect(PlayerControl pc, PlayerControl target)
{
+1
View File
@@ -11,6 +11,7 @@ namespace EHR.AddOns.GhostRoles
public bool IsWon;
public Team Team => Team.Neutral;
public int Cooldown => 900;
public bool ChangeToGA => false;
public void OnAssign(PlayerControl pc)
{
+1
View File
@@ -14,6 +14,7 @@ namespace EHR.AddOns.GhostRoles
public Team Team => Team.Crewmate;
public int Cooldown => CD.GetInt();
public bool ChangeToGA => true;
public void OnProtect(PlayerControl pc, PlayerControl target)
{
+1 -2
View File
@@ -3,7 +3,6 @@ using System.Linq;
using AmongUs.GameOptions;
using EHR.Modules;
using Hazel;
using UnityEngine;
namespace EHR.Crewmate
{
@@ -132,7 +131,7 @@ namespace EHR.Crewmate
{
var duration = AidDur.GetInt();
var now = Utils.TimeStamp;
var formatted = ShieldedPlayers.Select(x => string.Format(Translator.GetString("AidCounterTarget"), Utils.ColorString(Main.PlayerColors.GetValueOrDefault(x.Key, Color.white), Main.AllPlayerNames.GetValueOrDefault(x.Key, "Someone")), duration - (now - x.Value)));
var formatted = ShieldedPlayers.Select(x => string.Format(Translator.GetString("AidCounterTarget"), x.Key.ColoredPlayerName(), duration - (now - x.Value)));
return string.Join("\n", formatted);
}
+4 -3
View File
@@ -61,14 +61,15 @@ public class Divinator : RoleBase
var players = Main.AllAlivePlayerControls;
int rolesNeeded = players.Length * (RolesPerCategory - 1);
AllPlayerRoleList = Enum.GetValues<CustomRoles>()
var roleList = Enum.GetValues<CustomRoles>()
.Where(x => !x.IsVanilla() && !x.IsAdditionRole() && x is not CustomRoles.GM and not CustomRoles.Convict && !x.IsForOtherGameMode())
.OrderBy(x => x.IsEnable() ? IRandom.Instance.Next(10) : IRandom.Instance.Next(10, 100))
.Take(rolesNeeded)
.Chunk(RolesPerCategory - 1)
.Zip(players, (Array, Player) => (RoleList: Array.ToList(), Player))
.Do(x => x.RoleList.Insert(IRandom.Instance.Next(x.RoleList.Count), x.Player.GetCustomRole()))
.ToDictionary(x => x.Player.PlayerId, x => x.RoleList);
.ToArray();
roleList.Do(x => x.RoleList.Insert(IRandom.Instance.Next(x.RoleList.Count), x.Player.GetCustomRole()));
AllPlayerRoleList = roleList.ToDictionary(x => x.Player.PlayerId, x => x.RoleList);
Logger.Info(string.Join(" ---- ", AllPlayerRoleList.Select(x => $"ID {x.Key}: {string.Join(", ", x.Value)}")), "Divinator Roles");
}
+4
View File
@@ -8,6 +8,7 @@
public static OptionItem OptionSpeedDuringDrag;
public static OptionItem OptionVictimCanUseAbilities;
public static OptionItem CanBeGuessed;
public static OptionItem Cooldown;
public void SetupCustomOption()
{
@@ -22,6 +23,9 @@
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Goose]);
CanBeGuessed = new BooleanOptionItem(Id + 15, "CanBeGuessed", true, TabGroup.CrewmateRoles)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Goose]);
Cooldown = new FloatOptionItem(Id + 16, "AbilityCooldown", new(0f, 180f, 0.5f), 15f, TabGroup.CrewmateRoles)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Goose])
.SetValueFormat(OptionFormat.Seconds);
}
}
}
+1 -1
View File
@@ -47,7 +47,7 @@ public class Mediumshiper : RoleBase
public static void OnReportDeadBody(NetworkedPlayerInfo target)
{
ContactPlayer = [];
if (target == null) return;
if (target == null || target.Object == null) return;
foreach (var pc in Main.AllAlivePlayerControls.Where(x => playerIdList.Contains(x.PlayerId) && x.PlayerId != target.PlayerId).ToArray())
{
if (pc.GetAbilityUseLimit() < 1) continue;
+19 -7
View File
@@ -1,4 +1,5 @@
using EHR.Crewmate;
using AmongUs.GameOptions;
using EHR.Crewmate;
using EHR.Modules;
using Hazel;
using UnityEngine;
@@ -14,21 +15,24 @@ namespace EHR.Impostor
private static OptionItem OptionMeetingKill;
private static OptionItem OptionSpeedDuringDrag;
private static OptionItem OptionVictimCanUseAbilities;
private float AbductTimer;
private float AbductTimerLimit;
private PlayerControl AbductVictim;
private float Cooldown;
int Count = 0;
private int Count;
private float DefaultSpeed;
private bool IsGoose;
private long LastNotify;
private bool MeetingKill;
private PlayerControl Penguin_;
private PlayerControl Penguin_;
private byte PenguinId = byte.MaxValue;
private float SpeedDuringDrag;
private bool stopCount;
private bool VictimCanUseAbilities;
@@ -77,6 +81,7 @@ namespace EHR.Impostor
MeetingKill = OptionMeetingKill.GetBool();
SpeedDuringDrag = OptionSpeedDuringDrag.GetFloat();
VictimCanUseAbilities = OptionVictimCanUseAbilities.GetBool();
Cooldown = Options.DefaultKillCooldown;
}
else
{
@@ -84,9 +89,10 @@ namespace EHR.Impostor
MeetingKill = false;
SpeedDuringDrag = Goose.OptionSpeedDuringDrag.GetFloat();
VictimCanUseAbilities = Goose.OptionVictimCanUseAbilities.GetBool();
Cooldown = Goose.Cooldown.GetFloat();
}
LateTask.New(() => { DefaultSpeed = Main.AllPlayerSpeed[playerId]; }, 9f, log: false);
LateTask.New(() => DefaultSpeed = Main.AllPlayerSpeed[playerId], 9f, log: false);
PenguinId = playerId;
Penguin_ = Utils.GetPlayerById(playerId);
@@ -96,9 +102,15 @@ namespace EHR.Impostor
LastNotify = 0;
}
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = Options.DefaultKillCooldown;
public override bool CanUseImpostorVentButton(PlayerControl pc) => AbductVictim == null;
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = Cooldown;
public override bool CanUseImpostorVentButton(PlayerControl pc) => AbductVictim == null && !IsGoose;
public override bool CanUseKillButton(PlayerControl pc) => pc.IsAlive();
public override bool CanUseSabotage(PlayerControl pc) => !IsGoose && pc.IsAlive();
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
{
if (IsGoose) opt.SetVision(false);
}
void SendRPC()
{
@@ -190,7 +202,7 @@ namespace EHR.Impostor
}
else
{
if (!killer.RpcCheckAndMurder(target, check: true)) return false;
if (!IsGoose && !killer.RpcCheckAndMurder(target, check: true)) return false;
doKill = false;
AddVictim(target);
}
+41 -45
View File
@@ -9,41 +9,44 @@ namespace EHR.Impostor
public class Stealth : RoleBase
{
private const int Id = 641900;
private static List<byte> playerIdList = [];
private static List<byte> PlayerIdList = [];
private static OptionItem optionExcludeImpostors;
private static OptionItem optionDarkenDuration;
private static OptionItem OptionExcludeImpostors;
private static OptionItem OptionDarkenDuration;
private static bool excludeImpostors;
private static float darkenDuration;
private static float darkenTimer;
private static PlayerControl[] darkenedPlayers = [];
private static SystemTypes? darkenedRoom;
private static bool ExcludeImpostors;
private static float DarkenDuration;
private PlayerControl[] DarkenedPlayers = [];
private SystemTypes? DarkenedRoom;
private float DarkenTimer;
public override bool IsEnable => playerIdList.Count > 0;
private PlayerControl StealthPC;
public override bool IsEnable => PlayerIdList.Count > 0;
public static void SetupCustomOption()
{
Options.SetupSingleRoleOptions(Id, TabGroup.ImpostorRoles, CustomRoles.Stealth);
optionExcludeImpostors = new BooleanOptionItem(Id + 10, "StealthExcludeImpostors", false, TabGroup.ImpostorRoles)
OptionExcludeImpostors = new BooleanOptionItem(Id + 10, "StealthExcludeImpostors", false, TabGroup.ImpostorRoles)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Stealth]);
optionDarkenDuration = new FloatOptionItem(Id + 20, "StealthDarkenDuration", new(0.5f, 10f, 0.5f), 3f, TabGroup.ImpostorRoles)
OptionDarkenDuration = new FloatOptionItem(Id + 20, "StealthDarkenDuration", new(0.5f, 10f, 0.5f), 3f, TabGroup.ImpostorRoles)
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Stealth])
.SetValueFormat(OptionFormat.Seconds);
}
public override void Init()
{
playerIdList = [];
PlayerIdList = [];
ExcludeImpostors = OptionExcludeImpostors.GetBool();
DarkenDuration = DarkenTimer = OptionDarkenDuration.GetFloat();
}
public override void Add(byte playerId)
{
excludeImpostors = optionExcludeImpostors.GetBool();
darkenDuration = darkenTimer = optionDarkenDuration.GetFloat();
darkenedPlayers = null;
StealthPC = Utils.GetPlayerById(playerId);
DarkenedPlayers = null;
playerIdList.Add(playerId);
PlayerIdList.Add(playerId);
}
public override void SetKillCooldown(byte id) => Main.AllPlayerKillCooldown[id] = Options.DefaultKillCooldown;
@@ -51,10 +54,6 @@ namespace EHR.Impostor
public override void OnMurder(PlayerControl killer, PlayerControl target)
{
if (!IsEnable) return;
if (!killer.CanUseKillButton() || killer == null || target == null)
{
return;
}
var playersToDarken = FindPlayersInSameRoom(target);
if (playersToDarken == null)
@@ -63,7 +62,7 @@ namespace EHR.Impostor
return;
}
if (excludeImpostors)
if (ExcludeImpostors)
{
playersToDarken = playersToDarken.Where(player => !player.Is(CustomRoleTypes.Impostor)).ToArray();
}
@@ -75,21 +74,16 @@ namespace EHR.Impostor
PlayerControl[] FindPlayersInSameRoom(PlayerControl killedPlayer)
{
var room = killedPlayer.GetPlainShipRoom();
if (room == null)
{
return null;
}
if (room == null) return null;
var roomArea = room.roomArea;
var roomName = room.RoomId;
RpcDarken(roomName);
return Main.AllAlivePlayerControls.Where(player => player != Utils.GetPlayerById(playerIdList[0]) && player.Collider.IsTouching(roomArea)).ToArray();
RpcDarken(room.RoomId);
return Main.AllAlivePlayerControls.Where(p => p.PlayerId != StealthPC.PlayerId && p.GetPlainShipRoom() == room).ToArray();
}
/// <summary>Give the given player zero visibility for <see cref="darkenDuration"/> seconds.</summary>
static void DarkenPlayers(PlayerControl[] playersToDarken)
/// <summary>Give the given player zero visibility for <see cref="DarkenDuration"/> seconds.</summary>
void DarkenPlayers(PlayerControl[] playersToDarken)
{
darkenedPlayers = [.. playersToDarken];
DarkenedPlayers = [.. playersToDarken];
foreach (PlayerControl player in playersToDarken)
{
Main.PlayerStates[player.PlayerId].IsBlackOut = true;
@@ -106,12 +100,12 @@ namespace EHR.Impostor
}
// when you're darkening someone
if (darkenedPlayers != null)
if (DarkenedPlayers != null)
{
// reduce timer
darkenTimer -= Time.fixedDeltaTime;
DarkenTimer -= Time.fixedDeltaTime;
// When the timer reaches 0, return everyone's vision and reset the timer and darkening player.
if (darkenTimer <= 0)
if (DarkenTimer <= 0)
{
ResetDarkenState();
}
@@ -131,14 +125,15 @@ namespace EHR.Impostor
{
if (!IsEnable) return;
Logger.Info($"Set the darkened room to {roomType?.ToString() ?? "null"}", "Stealth");
darkenedRoom = roomType;
DarkenedRoom = roomType;
SendRPC(roomType);
}
void SendRPC(SystemTypes? roomType)
{
if (!IsEnable) return;
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.PenguinSync, SendOption.Reliable);
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.StealthDarken, SendOption.Reliable);
writer.Write(StealthPC.PlayerId);
writer.Write((byte?)roomType ?? byte.MaxValue);
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
@@ -146,27 +141,28 @@ namespace EHR.Impostor
public static void ReceiveRPC(MessageReader reader)
{
var roomId = reader.ReadByte();
darkenedRoom = roomId == byte.MaxValue ? null : (SystemTypes)roomId;
var stealthId = reader.ReadByte();
((Stealth)Main.PlayerStates[stealthId].Role).DarkenedRoom = roomId == byte.MaxValue ? null : (SystemTypes)roomId;
}
/// <summary>Removes the darkening effect that has occurred.</summary>
void ResetDarkenState()
{
if (!IsEnable) return;
if (darkenedPlayers != null)
if (DarkenedPlayers != null)
{
foreach (PlayerControl player in darkenedPlayers)
foreach (PlayerControl player in DarkenedPlayers)
{
Main.PlayerStates[player.PlayerId].IsBlackOut = false;
player.MarkDirtySettings();
}
darkenedPlayers = null;
DarkenedPlayers = null;
}
darkenTimer = darkenDuration;
DarkenTimer = DarkenDuration;
RpcDarken(null);
Utils.NotifyRoles(SpecifySeer: Utils.GetPlayerById(playerIdList[0]), SpecifyTarget: Utils.GetPlayerById(playerIdList[0]));
Utils.NotifyRoles(SpecifySeer: StealthPC, SpecifyTarget: StealthPC);
}
public override string GetSuffix(PlayerControl seer, PlayerControl seen, bool hud = false, bool meeting = false)
@@ -174,9 +170,9 @@ namespace EHR.Impostor
if (Main.PlayerStates[seer.PlayerId].Role is not Stealth { IsEnable: true }) return string.Empty;
seen ??= seer;
// During the meeting, unless it's my suffix, or it's dark everywhere, I won't show anything.
return meeting || seen != seer || !darkenedRoom.HasValue || (seer.IsModClient() && !hud)
return meeting || seen != seer || !DarkenedRoom.HasValue || (seer.IsModClient() && !hud)
? string.Empty
: string.Format(Translator.GetString("StealthDarkened"), DestroyableSingleton<TranslationController>.Instance.GetString(darkenedRoom.Value));
: string.Format(Translator.GetString("StealthDarkened"), DestroyableSingleton<TranslationController>.Instance.GetString(DarkenedRoom.Value));
}
}
}
+4 -4
View File
@@ -205,8 +205,8 @@ public class Swooper : RoleBase
LateTask.New(() =>
{
float limit = pc.GetAbilityUseLimit();
bool naN = float.IsNaN(limit);
if (CanGoInvis && (naN || limit >= 1))
bool wraith = UsedRole == CustomRoles.Wraith;
if (CanGoInvis && (wraith || limit >= 1))
{
ventedId = ventId;
@@ -215,7 +215,7 @@ public class Swooper : RoleBase
AmongUsClient.Instance.FinishRpcImmediately(writer);
InvisTime = Utils.TimeStamp;
if (!naN) pc.RpcRemoveAbilityUse();
if (!wraith) pc.RpcRemoveAbilityUse();
SendRPC();
pc.Notify(GetString("SwooperInvisState"), Duration);
}
@@ -229,7 +229,7 @@ public class Swooper : RoleBase
public override void OnEnterVent(PlayerControl pc, Vent vent)
{
if (!IsInvis) return;
if (!IsInvis || InvisTime == Utils.TimeStamp) return;
InvisTime = -10;
lastTime = Utils.TimeStamp;
+1 -1
View File
@@ -86,7 +86,7 @@ public class Glitch : RoleBase
public override void SetButtonTexts(HudManager hud, byte id)
{
hud.SabotageButton?.ToggleVisible(true);
hud.SabotageButton?.ToggleVisible(!Main.PlayerStates[id].IsDead);
hud.SabotageButton?.OverrideText(Translator.GetString("HackButtonText"));
}
+1 -1
View File
@@ -173,7 +173,7 @@ public class Vulture : RoleBase
return;
}
if (ChangeRoleWhenCantWin.GetBool() && Main.AllAlivePlayerControls.Length <= (NumberOfReportsToWin.GetInt() - BodyReportCount))
if (ChangeRoleWhenCantWin.GetBool() && Main.AllAlivePlayerControls.Length - 1 <= (NumberOfReportsToWin.GetInt() - BodyReportCount))
{
var role = ChangeRoles[ChangeRole.GetValue()];
pc.RpcSetCustomRole(role);
+2 -2
View File
@@ -29,8 +29,8 @@ public class Main : BasePlugin
private const string DebugKeyHash = "c0fd562955ba56af3ae20d7ec9e64c664f0facecef4b3e366e109306adeae29d";
private const string DebugKeySalt = "59687b";
private const string PluginGuid = "com.gurge44.endlesshostroles";
public const string PluginVersion = "4.0.0";
public const string PluginDisplayVersion = "4.0.0";
public const string PluginVersion = "4.1.0";
public const string PluginDisplayVersion = "4.1.0";
public const string NeutralColor = "#ffab1b";
public const string ImpostorColor = "#ff1919";
public const string CrewmateColor = "#8cffff";