mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-19 15:51:58 +00:00
massive fixes, AFK detection, cooler ID showing in meetings
This commit is contained in:
1 parent
d77c3219e4
commit
692075d296
42 files changed
+757
-412
No files matched your search
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -32,6 +32,7 @@ public class PlayerState(byte playerId)
|
||||
Execution,
|
||||
Disconnected,
|
||||
Fall,
|
||||
AFK,
|
||||
|
||||
// EHR
|
||||
Gambled,
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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))
|
||||
|
||||
@@ -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))})");
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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.",
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in new issue
Block a user