mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-19 15:51:58 +00:00
attempt fortegreen disconnect fix & some improvements
This commit is contained in:
1 parent
4aaae6d20b
commit
3a388706f7
44 files changed
+1142
-381
No files matched your search
+1
-1
@@ -8,7 +8,7 @@ dotnet_diagnostic.ide0060.severity = none
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_style_namespace_declarations = block_scoped:silent
|
||||
csharp_style_namespace_declarations = file_scoped:none
|
||||
csharp_style_prefer_method_group_conversion = true:silent
|
||||
csharp_style_prefer_top_level_statements = true:silent
|
||||
csharp_style_prefer_primary_constructors = true:suggestion
|
||||
|
||||
+13
-1
@@ -107,7 +107,19 @@ namespace EHR
|
||||
|
||||
IsDeadCache.Clear();
|
||||
IsCached = false;
|
||||
if (doSend) SendGameData();
|
||||
|
||||
if (doSend)
|
||||
{
|
||||
SendGameData();
|
||||
LateTask.New(RestoreIsDeadByExile, 0.3f, "AntiBlackOut.RestoreIsDeadByExile");
|
||||
}
|
||||
}
|
||||
|
||||
private static void RestoreIsDeadByExile()
|
||||
{
|
||||
var sender = CustomRpcSender.Create("AntiBlackout.RestoreIsDeadByExile", SendOption.Reliable);
|
||||
Main.AllPlayerControls.DoIf(x => x.Data.IsDead && !x.Data.Disconnected, x => sender.AutoStartRpc(x.NetId, (byte)RpcCalls.Exiled).EndRpc());
|
||||
sender.SendMessage();
|
||||
}
|
||||
|
||||
public static void SendGameData([CallerMemberName] string callerMethodName = "")
|
||||
|
||||
@@ -1485,7 +1485,7 @@ namespace EHR
|
||||
public static void RpcExileV2(this PlayerControl player)
|
||||
{
|
||||
player.Exiled();
|
||||
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)RpcCalls.Exiled, SendOption.None);
|
||||
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(player.NetId, (byte)RpcCalls.Exiled, SendOption.Reliable);
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
FixedUpdatePatch.LoversSuicide(player.PlayerId);
|
||||
}
|
||||
|
||||
@@ -533,7 +533,7 @@ namespace EHR
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsOnlineGame) return false;
|
||||
if (IsLocalGame && !IsNotJoined) return true;
|
||||
|
||||
const string domain = "among.us";
|
||||
|
||||
|
||||
+84
-74
@@ -148,10 +148,14 @@ other: ∟ ⌠ ⌡ ╬ ╨ ▓ ▒ ░ « » █ ▄ ▌▀▐│ ┤ ╡ ╢
|
||||
}
|
||||
}
|
||||
|
||||
if (AmongUsClient.Instance.AmHost)
|
||||
switch (AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
nt.SnapTo(location, (ushort)(nt.lastSequenceId + 328));
|
||||
nt.SetDirtyBit(uint.MaxValue);
|
||||
case true:
|
||||
nt.SnapTo(location, (ushort)(nt.lastSequenceId + 328));
|
||||
nt.SetDirtyBit(uint.MaxValue);
|
||||
break;
|
||||
case false when !nt.AmOwner:
|
||||
return false;
|
||||
}
|
||||
|
||||
var newSid = (ushort)(nt.lastSequenceId + 8);
|
||||
@@ -3090,88 +3094,94 @@ other: ∟ ⌠ ⌡ ╬ ╨ ▓ ▒ ░ « » █ ▄ ▌▀▐│ ┤ ╡ ╢
|
||||
|
||||
public static string SummaryTexts(byte id, bool disableColor = true, bool check = false)
|
||||
{
|
||||
//var RolePos = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.English or SupportedLangs.Russian ? 37 : 34;
|
||||
//var KillsPos = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.English or SupportedLangs.Russian ? 14 : 12;
|
||||
string name = Main.AllPlayerNames[id].RemoveHtmlTags().Replace("\r\n", string.Empty);
|
||||
|
||||
if (id == PlayerControl.LocalPlayer.PlayerId)
|
||||
name = DataManager.player.Customization.Name;
|
||||
else
|
||||
name = GetPlayerById(id)?.Data.PlayerName ?? name;
|
||||
|
||||
TaskState taskState = Main.PlayerStates[id].TaskState;
|
||||
string TaskCount;
|
||||
|
||||
if (taskState.HasTasks)
|
||||
try
|
||||
{
|
||||
NetworkedPlayerInfo info = GetPlayerInfoById(id);
|
||||
Color TaskCompleteColor = HasTasks(info) ? Color.green : Color.cyan;
|
||||
Color NonCompleteColor = HasTasks(info) ? Color.yellow : Color.white;
|
||||
string name = Main.AllPlayerNames[id].RemoveHtmlTags().Replace("\r\n", string.Empty);
|
||||
|
||||
if (Workhorse.IsThisRole(id)) NonCompleteColor = Workhorse.RoleColor;
|
||||
if (id == PlayerControl.LocalPlayer.PlayerId)
|
||||
name = DataManager.player.Customization.Name;
|
||||
else
|
||||
name = GetPlayerById(id)?.Data.PlayerName ?? name;
|
||||
|
||||
Color NormalColor = taskState.IsTaskFinished ? TaskCompleteColor : NonCompleteColor;
|
||||
TaskState taskState = Main.PlayerStates[id].TaskState;
|
||||
string TaskCount;
|
||||
|
||||
if (Main.PlayerStates.TryGetValue(id, out PlayerState ps))
|
||||
if (taskState.HasTasks)
|
||||
{
|
||||
NormalColor = ps.MainRole switch
|
||||
NetworkedPlayerInfo info = GetPlayerInfoById(id);
|
||||
Color TaskCompleteColor = HasTasks(info) ? Color.green : Color.cyan;
|
||||
Color NonCompleteColor = HasTasks(info) ? Color.yellow : Color.white;
|
||||
|
||||
if (Workhorse.IsThisRole(id)) NonCompleteColor = Workhorse.RoleColor;
|
||||
|
||||
Color NormalColor = taskState.IsTaskFinished ? TaskCompleteColor : NonCompleteColor;
|
||||
|
||||
if (Main.PlayerStates.TryGetValue(id, out PlayerState ps))
|
||||
{
|
||||
CustomRoles.Crewpostor => Color.red,
|
||||
CustomRoles.Cherokious => GetRoleColor(CustomRoles.Cherokious),
|
||||
_ => NormalColor
|
||||
};
|
||||
NormalColor = ps.MainRole switch
|
||||
{
|
||||
CustomRoles.Crewpostor => Color.red,
|
||||
CustomRoles.Cherokious => GetRoleColor(CustomRoles.Cherokious),
|
||||
_ => NormalColor
|
||||
};
|
||||
}
|
||||
|
||||
Color TextColor = NormalColor;
|
||||
var Completed = $"{taskState.CompletedTasksCount}";
|
||||
TaskCount = ColorString(TextColor, $" ({Completed}/{taskState.AllTasksCount})");
|
||||
}
|
||||
else
|
||||
TaskCount = string.Empty;
|
||||
|
||||
var summary = $"{ColorString(Main.PlayerColors[id], name)} - {GetDisplayRoleName(id, true)}{TaskCount}{GetKillCountText(id)} ({GetVitalText(id, true)})";
|
||||
|
||||
switch (Options.CurrentGameMode)
|
||||
{
|
||||
case CustomGameMode.SoloKombat:
|
||||
summary = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.SChinese or SupportedLangs.TChinese ? $"{GetProgressText(id)}\t<pos=22%>{ColorString(Main.PlayerColors[id], name)}</pos>" : $"{ColorString(Main.PlayerColors[id], name)}<pos=30%>{GetProgressText(id)}</pos>";
|
||||
if (GetProgressText(id).Trim() == string.Empty) return string.Empty;
|
||||
|
||||
break;
|
||||
case CustomGameMode.FFA:
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} {GetKillCountText(id, true)}";
|
||||
break;
|
||||
case CustomGameMode.Speedrun:
|
||||
case CustomGameMode.MoveAndStop:
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} -{TaskCount.Replace("(", string.Empty).Replace(")", string.Empty)} ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.HotPotato:
|
||||
int time = HotPotato.GetSurvivalTime(id);
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time}</color>s")}</color> ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.NaturalDisasters:
|
||||
int time2 = NaturalDisasters.SurvivalTime(id);
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time2 == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time2}</color>s")}</color> ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.RoomRush:
|
||||
int time3 = RoomRush.GetSurvivalTime(id);
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time3 == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time3}</color>s")}</color> ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)}: {CTFManager.GetStatistics(id)}";
|
||||
if (CTFManager.IsDeathPossible) summary += $" ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.AllInOne:
|
||||
string survivalTimeText = !Main.PlayerStates[id].IsDead ? string.Empty : $" ({GetString("SurvivedTimePrefix")}: <#f542ad>{RoomRush.GetSurvivalTime(id)}s</color>)";
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} -{TaskCount}{GetKillCountText(id, true)} ({GetVitalText(id, true)}){survivalTimeText}";
|
||||
break;
|
||||
}
|
||||
|
||||
Color TextColor = NormalColor;
|
||||
var Completed = $"{taskState.CompletedTasksCount}";
|
||||
TaskCount = ColorString(TextColor, $" ({Completed}/{taskState.AllTasksCount})");
|
||||
return check && GetDisplayRoleName(id, true).RemoveHtmlTags().Contains("INVALID:NotAssigned")
|
||||
? "INVALID"
|
||||
: disableColor
|
||||
? summary.RemoveHtmlTags()
|
||||
: summary;
|
||||
}
|
||||
else
|
||||
TaskCount = string.Empty;
|
||||
|
||||
var summary = $"{ColorString(Main.PlayerColors[id], name)} - {GetDisplayRoleName(id, true)}{TaskCount}{GetKillCountText(id)} ({GetVitalText(id, true)})";
|
||||
|
||||
switch (Options.CurrentGameMode)
|
||||
catch (Exception e)
|
||||
{
|
||||
case CustomGameMode.SoloKombat:
|
||||
summary = TranslationController.Instance.currentLanguage.languageID is SupportedLangs.SChinese or SupportedLangs.TChinese ? $"{GetProgressText(id)}\t<pos=22%>{ColorString(Main.PlayerColors[id], name)}</pos>" : $"{ColorString(Main.PlayerColors[id], name)}<pos=30%>{GetProgressText(id)}</pos>";
|
||||
if (GetProgressText(id).Trim() == string.Empty) return string.Empty;
|
||||
|
||||
break;
|
||||
case CustomGameMode.FFA:
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} {GetKillCountText(id, true)}";
|
||||
break;
|
||||
case CustomGameMode.Speedrun:
|
||||
case CustomGameMode.MoveAndStop:
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} -{TaskCount.Replace("(", string.Empty).Replace(")", string.Empty)} ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.HotPotato:
|
||||
int time = HotPotato.GetSurvivalTime(id);
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time}</color>s")}</color> ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.NaturalDisasters:
|
||||
int time2 = NaturalDisasters.SurvivalTime(id);
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time2 == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time2}</color>s")}</color> ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.RoomRush:
|
||||
int time3 = RoomRush.GetSurvivalTime(id);
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time3 == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time3}</color>s")}</color> ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)}: {CTFManager.GetStatistics(id)}";
|
||||
if (CTFManager.IsDeathPossible) summary += $" ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.AllInOne:
|
||||
string survivalTimeText = !Main.PlayerStates[id].IsDead ? string.Empty : $" ({GetString("SurvivedTimePrefix")}: <#f542ad>{RoomRush.GetSurvivalTime(id)}s</color>)";
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} -{TaskCount}{GetKillCountText(id, true)} ({GetVitalText(id, true)}){survivalTimeText}";
|
||||
break;
|
||||
ThrowException(e);
|
||||
return $"{id.ColoredPlayerName()} - ERROR";
|
||||
}
|
||||
|
||||
return check && GetDisplayRoleName(id, true).RemoveHtmlTags().Contains("INVALID:NotAssigned")
|
||||
? "INVALID"
|
||||
: disableColor
|
||||
? summary.RemoveHtmlTags()
|
||||
: summary;
|
||||
}
|
||||
|
||||
public static string GetRemainingKillers(bool notify = false, bool president = false)
|
||||
|
||||
+365
-277
@@ -1,355 +1,443 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using EHR.Modules;
|
||||
using HarmonyLib;
|
||||
using Hazel;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using Il2CppSystem.Collections.Generic;
|
||||
using InnerNet;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using static EHR.Translator;
|
||||
|
||||
namespace EHR
|
||||
namespace EHR;
|
||||
|
||||
[HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.MakePublic))]
|
||||
internal static class MakePublicPatch
|
||||
{
|
||||
[HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.MakePublic))]
|
||||
internal static class MakePublicPatch
|
||||
public static bool Prefix()
|
||||
{
|
||||
public static bool Prefix()
|
||||
if (ModUpdater.IsBroken || (ModUpdater.HasUpdate && ModUpdater.ForceUpdate) || !VersionChecker.IsSupported)
|
||||
{
|
||||
if (ModUpdater.IsBroken || (ModUpdater.HasUpdate && ModUpdater.ForceUpdate) || !VersionChecker.IsSupported)
|
||||
{
|
||||
var message = string.Empty;
|
||||
if (!VersionChecker.IsSupported) message = GetString("UnsupportedVersion");
|
||||
var message = string.Empty;
|
||||
if (!VersionChecker.IsSupported) message = GetString("UnsupportedVersion");
|
||||
|
||||
if (ModUpdater.IsBroken) message = GetString("ModBrokenMessage");
|
||||
if (ModUpdater.IsBroken) message = GetString("ModBrokenMessage");
|
||||
|
||||
if (ModUpdater.HasUpdate) message = GetString("CanNotJoinPublicRoomNoLatest");
|
||||
if (ModUpdater.HasUpdate) message = GetString("CanNotJoinPublicRoomNoLatest");
|
||||
|
||||
Logger.Info(message, "MakePublicPatch");
|
||||
Logger.SendInGame(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
Logger.Info(message, "MakePublicPatch");
|
||||
Logger.SendInGame(message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MMOnlineManager), nameof(MMOnlineManager.Start))]
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MMOnlineManager), nameof(MMOnlineManager.Start))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
internal static class MMOnlineManagerStartPatch
|
||||
internal static class MMOnlineManagerStartPatch
|
||||
{
|
||||
public static void Postfix()
|
||||
{
|
||||
public static void Postfix()
|
||||
if (!((ModUpdater.HasUpdate && ModUpdater.ForceUpdate) || ModUpdater.IsBroken)) return;
|
||||
|
||||
GameObject obj = GameObject.Find("FindGameButton");
|
||||
|
||||
if (obj)
|
||||
{
|
||||
if (!((ModUpdater.HasUpdate && ModUpdater.ForceUpdate) || ModUpdater.IsBroken)) return;
|
||||
obj.SetActive(false);
|
||||
TextMeshPro textObj = Object.Instantiate(obj.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>());
|
||||
textObj.transform.position = new(1f, -0.3f, 0);
|
||||
textObj.name = "CanNotJoinPublic";
|
||||
|
||||
GameObject obj = GameObject.Find("FindGameButton");
|
||||
string message = ModUpdater.IsBroken
|
||||
? $"<size=2>{Utils.ColorString(Color.red, GetString("ModBrokenMessage"))}</size>"
|
||||
: $"<size=2>{Utils.ColorString(Color.red, GetString("CanNotJoinPublicRoomNoLatest"))}</size>";
|
||||
|
||||
if (obj)
|
||||
{
|
||||
obj.SetActive(false);
|
||||
TextMeshPro textObj = Object.Instantiate(obj.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>());
|
||||
textObj.transform.position = new(1f, -0.3f, 0);
|
||||
textObj.name = "CanNotJoinPublic";
|
||||
|
||||
string message = ModUpdater.IsBroken
|
||||
? $"<size=2>{Utils.ColorString(Color.red, GetString("ModBrokenMessage"))}</size>"
|
||||
: $"<size=2>{Utils.ColorString(Color.red, GetString("CanNotJoinPublicRoomNoLatest"))}</size>";
|
||||
|
||||
LateTask.New(() => { textObj.text = message; }, 0.01f, "CanNotJoinPublic");
|
||||
}
|
||||
LateTask.New(() => { textObj.text = message; }, 0.01f, "CanNotJoinPublic");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(SplashManager), nameof(SplashManager.Update))]
|
||||
internal static class SplashLogoAnimatorPatch
|
||||
[HarmonyPatch(typeof(SplashManager), nameof(SplashManager.Update))]
|
||||
internal static class SplashLogoAnimatorPatch
|
||||
{
|
||||
public static void Prefix(SplashManager __instance)
|
||||
{
|
||||
public static void Prefix(SplashManager __instance)
|
||||
{
|
||||
__instance.sceneChanger.AllowFinishLoadingScene();
|
||||
__instance.startedSceneLoad = true;
|
||||
}
|
||||
__instance.sceneChanger.AllowFinishLoadingScene();
|
||||
__instance.startedSceneLoad = true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(EOSManager), nameof(EOSManager.IsAllowedOnline))]
|
||||
internal static class RunLoginPatch
|
||||
[HarmonyPatch(typeof(EOSManager), nameof(EOSManager.IsAllowedOnline))]
|
||||
internal static class RunLoginPatch
|
||||
{
|
||||
public const int ClickCount = 0;
|
||||
|
||||
public static void Prefix(ref bool canOnline)
|
||||
{
|
||||
public const int ClickCount = 0;
|
||||
|
||||
public static void Prefix(ref bool canOnline)
|
||||
{
|
||||
if (DebugModeManager.AmDebugger) canOnline = true;
|
||||
}
|
||||
if (DebugModeManager.AmDebugger) canOnline = true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(BanMenu), nameof(BanMenu.SetVisible))]
|
||||
internal static class BanMenuSetVisiblePatch
|
||||
[HarmonyPatch(typeof(BanMenu), nameof(BanMenu.SetVisible))]
|
||||
internal static class BanMenuSetVisiblePatch
|
||||
{
|
||||
public static bool Prefix(BanMenu __instance, bool show)
|
||||
{
|
||||
public static bool Prefix(BanMenu __instance, bool show)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost) return true;
|
||||
if (!AmongUsClient.Instance.AmHost) return true;
|
||||
|
||||
show &= PlayerControl.LocalPlayer && PlayerControl.LocalPlayer.Data != null;
|
||||
__instance.BanButton.gameObject.SetActive(AmongUsClient.Instance.CanBan());
|
||||
__instance.KickButton.gameObject.SetActive(AmongUsClient.Instance.CanKick());
|
||||
__instance.MenuButton.gameObject.SetActive(show);
|
||||
show &= PlayerControl.LocalPlayer && PlayerControl.LocalPlayer.Data != null;
|
||||
__instance.BanButton.gameObject.SetActive(AmongUsClient.Instance.CanBan());
|
||||
__instance.KickButton.gameObject.SetActive(AmongUsClient.Instance.CanKick());
|
||||
__instance.MenuButton.gameObject.SetActive(show);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.CanBan))]
|
||||
internal static class InnerNetClientCanBanPatch
|
||||
{
|
||||
public static bool Prefix(InnerNetClient __instance, ref bool __result)
|
||||
{
|
||||
__result = __instance.AmHost;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.KickPlayer))]
|
||||
internal static class KickPlayerPatch
|
||||
{
|
||||
public static bool Prefix( /*InnerNetClient __instance,*/ int clientId, bool ban)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost && !OnGameJoinedPatch.JoiningGame) return true;
|
||||
|
||||
if (AmongUsClient.Instance.ClientId == clientId)
|
||||
{
|
||||
Logger.SendInGame($"Game Attempting to {(ban ? "Ban" : "Kick")} Host, Blocked the attempt.");
|
||||
Logger.Info("Game attempted to kick/ban host....", "KickPlayerPatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ban) BanManager.AddBanPlayer(AmongUsClient.Instance.GetRecentClient(clientId));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.CanBan))]
|
||||
internal static class InnerNetClientCanBanPatch
|
||||
[HarmonyPatch(typeof(ResolutionManager), nameof(ResolutionManager.SetResolution))]
|
||||
internal static class SetResolutionManager
|
||||
{
|
||||
public static void Postfix()
|
||||
{
|
||||
public static bool Prefix(InnerNetClient __instance, ref bool __result)
|
||||
{
|
||||
__result = __instance.AmHost;
|
||||
return false;
|
||||
}
|
||||
if (MainMenuManagerPatch.UpdateButton != null) MainMenuManagerPatch.UpdateButton.transform.localPosition = MainMenuManagerPatch.Template.transform.localPosition + new Vector3(0.25f, 0.75f);
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.KickPlayer))]
|
||||
internal static class KickPlayerPatch
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendAllStreamedObjects))]
|
||||
internal static class InnerNetObjectSerializePatch
|
||||
{
|
||||
public static void Prefix()
|
||||
{
|
||||
public static bool Prefix( /*InnerNetClient __instance,*/ int clientId, bool ban)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost && !OnGameJoinedPatch.JoiningGame) return true;
|
||||
if (AmongUsClient.Instance.AmHost) Main.Instance.StartCoroutine(GameOptionsSender.SendAllGameOptionsAsync());
|
||||
}
|
||||
}
|
||||
|
||||
if (AmongUsClient.Instance.ClientId == clientId)
|
||||
[HarmonyPatch(typeof(InnerNetClient))]
|
||||
public class InnerNetClientPatch
|
||||
{
|
||||
private static byte Timer;
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendInitialData))]
|
||||
[HarmonyPrefix]
|
||||
public static bool SendInitialDataPrefix(InnerNetClient __instance, int clientId)
|
||||
{
|
||||
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return true;
|
||||
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
|
||||
messageWriter.StartMessage(6);
|
||||
messageWriter.Write(__instance.GameId);
|
||||
messageWriter.WritePacked(clientId);
|
||||
List<InnerNetObject> obj = __instance.allObjects;
|
||||
|
||||
lock (obj)
|
||||
{
|
||||
System.Collections.Generic.HashSet<GameObject> hashSet = [];
|
||||
|
||||
for (int i = 0; i < __instance.allObjects.Count; i++)
|
||||
{
|
||||
Logger.SendInGame($"Game Attempting to {(ban ? "Ban" : "Kick")} Host, Blocked the attempt.");
|
||||
Logger.Info("Game attempted to kick/ban host....", "KickPlayerPatch");
|
||||
return false;
|
||||
InnerNetObject innerNetObject = __instance.allObjects[i]; // False error
|
||||
|
||||
if (innerNetObject && (innerNetObject.OwnerId != -4 || __instance.AmModdedHost) && hashSet.Add(innerNetObject.gameObject))
|
||||
{
|
||||
GameManager gameManager = innerNetObject as GameManager;
|
||||
|
||||
if (gameManager != null)
|
||||
{
|
||||
__instance.SendGameManager(clientId, gameManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (innerNetObject is not NetworkedPlayerInfo)
|
||||
__instance.WriteSpawnMessage(innerNetObject, innerNetObject.OwnerId, innerNetObject.SpawnFlags, messageWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ban) BanManager.AddBanPlayer(AmongUsClient.Instance.GetRecentClient(clientId));
|
||||
|
||||
return true;
|
||||
messageWriter.EndMessage();
|
||||
__instance.SendOrDisconnect(messageWriter);
|
||||
messageWriter.Recycle();
|
||||
}
|
||||
|
||||
DelaySpawnPlayerInfo(__instance, clientId);
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(ResolutionManager), nameof(ResolutionManager.SetResolution))]
|
||||
internal static class SetResolutionManager
|
||||
private static void DelaySpawnPlayerInfo(InnerNetClient __instance, int clientId)
|
||||
{
|
||||
public static void Postfix()
|
||||
var players = GameData.Instance.AllPlayers.ToArray().ToList();
|
||||
|
||||
while (players.Count > 0)
|
||||
{
|
||||
if (MainMenuManagerPatch.UpdateButton != null) MainMenuManagerPatch.UpdateButton.transform.localPosition = MainMenuManagerPatch.Template.transform.localPosition + new Vector3(0.25f, 0.75f);
|
||||
}
|
||||
}
|
||||
var batch = players.Take(5).ToList();
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendAllStreamedObjects))]
|
||||
internal static class InnerNetObjectSerializePatch
|
||||
{
|
||||
public static void Prefix()
|
||||
{
|
||||
if (AmongUsClient.Instance.AmHost) Main.Instance.StartCoroutine(GameOptionsSender.SendAllGameOptionsAsync());
|
||||
}
|
||||
}
|
||||
|
||||
internal static class InnerNetClientPatch
|
||||
{
|
||||
private static byte Timer;
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendInitialData))]
|
||||
[HarmonyPrefix]
|
||||
public static bool SendInitialDataPrefix(InnerNetClient __instance, int clientId)
|
||||
{
|
||||
if (!Constants.IsVersionModded() || GameStates.IsInGame || __instance.NetworkMode != NetworkModes.OnlineGame) return true;
|
||||
|
||||
// We make sure other stuff like PlayerControl and LobbyBehavior is spawned properly
|
||||
// Then we spawn the networked data for new clients
|
||||
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
|
||||
messageWriter.StartMessage(6);
|
||||
messageWriter.Write(__instance.GameId);
|
||||
messageWriter.WritePacked(clientId);
|
||||
List<InnerNetObject> obj = __instance.allObjects;
|
||||
|
||||
lock (obj)
|
||||
foreach (var player in batch)
|
||||
{
|
||||
System.Collections.Generic.HashSet<GameObject> hashSet = [];
|
||||
if (messageWriter.Length > 500) break;
|
||||
|
||||
for (var i = 0; i < __instance.allObjects.Count; i++)
|
||||
if (player != null && player.ClientId != clientId && !player.Disconnected)
|
||||
{
|
||||
InnerNetObject innerNetObject = __instance.allObjects[i]; // False error
|
||||
|
||||
if (innerNetObject && (innerNetObject.OwnerId != -4 || __instance.AmModdedHost) && hashSet.Add(innerNetObject.gameObject))
|
||||
{
|
||||
var gameManager = innerNetObject as GameManager;
|
||||
|
||||
if (gameManager != null)
|
||||
__instance.SendGameManager(clientId, gameManager);
|
||||
else
|
||||
{
|
||||
if (innerNetObject is not NetworkedPlayerInfo) __instance.WriteSpawnMessage(innerNetObject, innerNetObject.OwnerId, innerNetObject.SpawnFlags, messageWriter);
|
||||
}
|
||||
}
|
||||
__instance.WriteSpawnMessage(player, player.OwnerId, player.SpawnFlags, messageWriter);
|
||||
}
|
||||
|
||||
messageWriter.EndMessage();
|
||||
__instance.SendOrDisconnect(messageWriter);
|
||||
messageWriter.Recycle();
|
||||
players.Remove(player);
|
||||
}
|
||||
|
||||
DelaySpawnPlayerInfo(__instance, clientId);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void DelaySpawnPlayerInfo(InnerNetClient __instance, int clientId)
|
||||
{
|
||||
Il2CppArrayBase<NetworkedPlayerInfo> players = GameData.Instance.AllPlayers.ToArray();
|
||||
|
||||
// We send 5 players at a time to prevent too large packets
|
||||
foreach (NetworkedPlayerInfo[] batch in players.Chunk(5))
|
||||
{
|
||||
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
|
||||
messageWriter.StartMessage(6);
|
||||
messageWriter.Write(__instance.GameId);
|
||||
messageWriter.WritePacked(clientId);
|
||||
|
||||
batch.DoIf(p => p != null && p.ClientId != clientId && !p.Disconnected, p => __instance.WriteSpawnMessage(p, p.OwnerId, p.SpawnFlags, messageWriter));
|
||||
|
||||
messageWriter.EndMessage();
|
||||
__instance.SendOrDisconnect(messageWriter);
|
||||
messageWriter.Recycle();
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendAllStreamedObjects))]
|
||||
[HarmonyPrefix]
|
||||
public static bool SendAllStreamedObjectsPrefix(InnerNetClient __instance, ref bool __result)
|
||||
{
|
||||
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return true;
|
||||
|
||||
// Bypass all NetworkedData here.
|
||||
__result = false;
|
||||
List<InnerNetObject> obj = __instance.allObjects;
|
||||
|
||||
lock (obj)
|
||||
{
|
||||
for (var i = 0; i < __instance.allObjects.Count; i++)
|
||||
{
|
||||
InnerNetObject innerNetObject = __instance.allObjects[i]; // False error
|
||||
|
||||
if (innerNetObject && innerNetObject is not NetworkedPlayerInfo && innerNetObject.IsDirty && (innerNetObject.AmOwner || (innerNetObject.OwnerId == -2 && __instance.AmHost)))
|
||||
{
|
||||
MessageWriter messageWriter = __instance.Streams[(int)innerNetObject.sendMode];
|
||||
messageWriter.StartMessage(1);
|
||||
messageWriter.WritePacked(innerNetObject.NetId);
|
||||
|
||||
try
|
||||
{
|
||||
if (innerNetObject.Serialize(messageWriter, false))
|
||||
messageWriter.EndMessage();
|
||||
else
|
||||
messageWriter.CancelMessage();
|
||||
|
||||
if (innerNetObject.Chunked && innerNetObject.IsDirty) __result = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Exception(ex, "SendAllStreamedObjectsPrefix");
|
||||
messageWriter.CancelMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < __instance.Streams.Length; j++)
|
||||
{
|
||||
MessageWriter messageWriter2 = __instance.Streams[j];
|
||||
|
||||
if (messageWriter2.HasBytes(7))
|
||||
{
|
||||
messageWriter2.EndMessage();
|
||||
__instance.SendOrDisconnect(messageWriter2);
|
||||
messageWriter2.Clear((SendOption)j);
|
||||
messageWriter2.StartMessage(5);
|
||||
messageWriter2.Write(__instance.GameId);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.FixedUpdate))]
|
||||
[HarmonyPostfix]
|
||||
public static void FixedUpdatePostfix(InnerNetClient __instance)
|
||||
{
|
||||
if (!Constants.IsVersionModded() || GameStates.IsInGame || !__instance.AmHost || __instance.Streams == null || __instance.NetworkMode != NetworkModes.OnlineGame) return;
|
||||
|
||||
if (Timer == 0)
|
||||
{
|
||||
Timer = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkedPlayerInfo player = GameData.Instance.AllPlayers.ToArray().FirstOrDefault(x => x.IsDirty);
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
Timer = 0;
|
||||
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
|
||||
messageWriter.StartMessage(5);
|
||||
messageWriter.Write(__instance.GameId);
|
||||
messageWriter.StartMessage(1);
|
||||
messageWriter.WritePacked(player.NetId);
|
||||
|
||||
try
|
||||
{
|
||||
if (player.Serialize(messageWriter, false))
|
||||
messageWriter.EndMessage();
|
||||
else
|
||||
{
|
||||
messageWriter.CancelMessage();
|
||||
player.ClearDirtyBits();
|
||||
return;
|
||||
}
|
||||
|
||||
messageWriter.EndMessage();
|
||||
__instance.SendOrDisconnect(messageWriter);
|
||||
messageWriter.Recycle();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Exception(ex, "FixedUpdatePostfix");
|
||||
messageWriter.CancelMessage();
|
||||
player.ClearDirtyBits();
|
||||
}
|
||||
}
|
||||
messageWriter.EndMessage();
|
||||
__instance.SendOrDisconnect(messageWriter);
|
||||
messageWriter.Recycle();
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
internal static class AuthTimeoutPatch
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.SendAllStreamedObjects))]
|
||||
[HarmonyPrefix]
|
||||
public static bool SendAllStreamedObjectsPrefix(InnerNetClient __instance, ref bool __result)
|
||||
{
|
||||
[HarmonyPatch(typeof(AuthManager._CoConnect_d__4), nameof(AuthManager._CoConnect_d__4.MoveNext))]
|
||||
[HarmonyPatch(typeof(AuthManager._CoWaitForNonce_d__6), nameof(AuthManager._CoWaitForNonce_d__6.MoveNext))]
|
||||
[HarmonyPrefix]
|
||||
// From Reactor.gg
|
||||
// https://github.com/NuclearPowered/Reactor/blob/master/Reactor/Patches/Miscellaneous/CustomServersPatch.cs
|
||||
public static bool CoWaitforNoncePrefix(ref bool __result)
|
||||
{
|
||||
if (GameStates.IsVanillaServer) return true;
|
||||
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return true;
|
||||
__result = false;
|
||||
List<InnerNetObject> obj = __instance.allObjects;
|
||||
|
||||
__result = false;
|
||||
return false;
|
||||
lock (obj)
|
||||
{
|
||||
for (int i = 0; i < __instance.allObjects.Count; i++)
|
||||
{
|
||||
InnerNetObject innerNetObject = __instance.allObjects[i]; // False error
|
||||
|
||||
if (innerNetObject && innerNetObject is not NetworkedPlayerInfo && innerNetObject.IsDirty && (innerNetObject.AmOwner || (innerNetObject.OwnerId == -2 && __instance.AmHost)))
|
||||
{
|
||||
MessageWriter messageWriter = __instance.Streams[(int)innerNetObject.sendMode];
|
||||
messageWriter.StartMessage(1);
|
||||
messageWriter.WritePacked(innerNetObject.NetId);
|
||||
|
||||
try
|
||||
{
|
||||
if (innerNetObject.Serialize(messageWriter, false))
|
||||
{
|
||||
messageWriter.EndMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
messageWriter.CancelMessage();
|
||||
}
|
||||
|
||||
if (innerNetObject.Chunked && innerNetObject.IsDirty)
|
||||
{
|
||||
__result = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Exception(ex, "SendAllStreamedObjectsPrefix");
|
||||
messageWriter.CancelMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If you don't patch this, you still need to wait for 5 s.
|
||||
// I have no idea why this is happening
|
||||
[HarmonyPatch(typeof(AmongUsClient._CoJoinOnlinePublicGame_d__1), nameof(AmongUsClient._CoJoinOnlinePublicGame_d__1.MoveNext))]
|
||||
[HarmonyPrefix]
|
||||
public static void EnableUdpMatchmakingPrefix(AmongUsClient._CoJoinOnlinePublicGame_d__1 __instance)
|
||||
for (int j = 0; j < __instance.Streams.Length; j++)
|
||||
{
|
||||
// Skip to state 1, which just calls CoJoinOnlineGameDirect
|
||||
if (__instance.__1__state == 0 && !ServerManager.Instance.IsHttp)
|
||||
{
|
||||
__instance.__1__state = 1;
|
||||
MessageWriter messageWriter2 = __instance.Streams[j];
|
||||
|
||||
__instance.__8__1 = new()
|
||||
{
|
||||
matchmakerToken = string.Empty
|
||||
};
|
||||
if (messageWriter2.HasBytes(7))
|
||||
{
|
||||
messageWriter2.EndMessage();
|
||||
__instance.SendOrDisconnect(messageWriter2);
|
||||
messageWriter2.Clear((SendOption)j);
|
||||
messageWriter2.StartMessage(5);
|
||||
messageWriter2.Write(__instance.GameId);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.Spawn))]
|
||||
[HarmonyPostfix]
|
||||
public static void Spawn_Postfix(InnerNetClient __instance, InnerNetObject netObjParent, int ownerId = -2, SpawnFlags flags = SpawnFlags.None)
|
||||
{
|
||||
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return;
|
||||
|
||||
if (__instance.AmHost)
|
||||
{
|
||||
switch (netObjParent)
|
||||
{
|
||||
case NetworkedPlayerInfo playerinfo:
|
||||
LateTask.New(() =>
|
||||
{
|
||||
if (playerinfo != null && AmongUsClient.Instance.AmConnected)
|
||||
{
|
||||
var client = AmongUsClient.Instance.GetClient(playerinfo.ClientId);
|
||||
|
||||
if (client != null && !client.IsDisconnected())
|
||||
{
|
||||
if (playerinfo.IsIncomplete)
|
||||
{
|
||||
Logger.Info($"Disconnecting Client [{client.Id}]{client.PlayerName} {client.FriendCode} for playerinfo timeout", "DelayedNetworkedData");
|
||||
AmongUsClient.Instance.SendLateRejection(client.Id, DisconnectReasons.ClientTimeout);
|
||||
__instance.OnPlayerLeft(client, DisconnectReasons.ClientTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 5f, "PlayerInfo Green Bean Kick", false);
|
||||
|
||||
break;
|
||||
case PlayerControl player:
|
||||
LateTask.New(() =>
|
||||
{
|
||||
if (player != null && !player.notRealPlayer && !player.isDummy && AmongUsClient.Instance.AmConnected)
|
||||
{
|
||||
var client = AmongUsClient.Instance.GetClient(player.OwnerId);
|
||||
|
||||
if (client != null && !client.IsDisconnected())
|
||||
{
|
||||
if (player.Data == null || player.Data.IsIncomplete)
|
||||
{
|
||||
Logger.Info($"Disconnecting Client [{client.Id}]{client.PlayerName} {client.FriendCode} for playercontrol timeout", "DelayedNetworkedData");
|
||||
AmongUsClient.Instance.SendLateRejection(client.Id, DisconnectReasons.ClientTimeout);
|
||||
__instance.OnPlayerLeft(client, DisconnectReasons.ClientTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 5.5f, "PlayerControl Green Bean Kick", false);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!__instance.AmHost)
|
||||
{
|
||||
Debug.LogError("Tried to spawn while not host:" + (netObjParent?.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.FixedUpdate))]
|
||||
[HarmonyPostfix]
|
||||
public static void FixedUpdatePostfix(InnerNetClient __instance)
|
||||
{
|
||||
if (!Constants.IsVersionModded() || __instance.NetworkMode != NetworkModes.OnlineGame) return;
|
||||
if (!__instance.AmHost || __instance.Streams == null) return;
|
||||
|
||||
if (Timer == 0)
|
||||
{
|
||||
Timer = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
var player = GameData.Instance.AllPlayers.ToArray().FirstOrDefault(x => x.IsDirty);
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
Timer = 0;
|
||||
MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
|
||||
messageWriter.StartMessage(5);
|
||||
messageWriter.Write(__instance.GameId);
|
||||
messageWriter.StartMessage(1);
|
||||
messageWriter.WritePacked(player.NetId);
|
||||
|
||||
try
|
||||
{
|
||||
if (player.Serialize(messageWriter, false))
|
||||
{
|
||||
messageWriter.EndMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
messageWriter.CancelMessage();
|
||||
player.ClearDirtyBits();
|
||||
return;
|
||||
}
|
||||
|
||||
messageWriter.EndMessage();
|
||||
__instance.SendOrDisconnect(messageWriter);
|
||||
messageWriter.Recycle();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Exception(ex, "FixedUpdatePostfix");
|
||||
messageWriter.CancelMessage();
|
||||
player.ClearDirtyBits();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameData), nameof(GameData.DirtyAllData))]
|
||||
internal class DirtyAllDataPatch
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static bool Prefix()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
internal static class AuthTimeoutPatch
|
||||
{
|
||||
[HarmonyPatch(typeof(AuthManager._CoConnect_d__4), nameof(AuthManager._CoConnect_d__4.MoveNext))]
|
||||
[HarmonyPatch(typeof(AuthManager._CoWaitForNonce_d__6), nameof(AuthManager._CoWaitForNonce_d__6.MoveNext))]
|
||||
[HarmonyPrefix]
|
||||
// From Reactor.gg
|
||||
// https://github.com/NuclearPowered/Reactor/blob/master/Reactor/Patches/Miscellaneous/CustomServersPatch.cs
|
||||
public static bool CoWaitforNoncePrefix(ref bool __result)
|
||||
{
|
||||
if (GameStates.IsVanillaServer) return true;
|
||||
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If you don't patch this, you still need to wait for 5 s.
|
||||
// I have no idea why this is happening
|
||||
[HarmonyPatch(typeof(AmongUsClient._CoJoinOnlinePublicGame_d__1), nameof(AmongUsClient._CoJoinOnlinePublicGame_d__1.MoveNext))]
|
||||
[HarmonyPrefix]
|
||||
public static void EnableUdpMatchmakingPrefix(AmongUsClient._CoJoinOnlinePublicGame_d__1 __instance)
|
||||
{
|
||||
// Skip to state 1, which just calls CoJoinOnlineGameDirect
|
||||
if (__instance.__1__state == 0 && !ServerManager.Instance.IsHttp)
|
||||
{
|
||||
__instance.__1__state = 1;
|
||||
|
||||
__instance.__8__1 = new()
|
||||
{
|
||||
matchmakerToken = string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+491
-18
@@ -1,33 +1,506 @@
|
||||
using AmongUs.GameOptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EHR.Patches;
|
||||
|
||||
// https://github.com/CrowdedMods/CrowdedMod/blob/master/src/CrowdedMod
|
||||
namespace EHR.Patches
|
||||
{
|
||||
internal static class Crowded
|
||||
{
|
||||
public const int MaxImpostors = 127 / 2;
|
||||
// Niko adjusted mono behavior patches to fit into non-reactor mods
|
||||
|
||||
[HarmonyPatch(typeof(GameOptionsData), nameof(GameOptionsData.AreInvalid))]
|
||||
public static class InvalidOptionsPatches
|
||||
internal static class Crowded
|
||||
{
|
||||
private static CreateOptionsPicker Instance;
|
||||
public static readonly int MaxImpostors = GameOptionsManager.Instance.currentHostOptions.MaxPlayers / 2;
|
||||
private static int MaxPlayers => GameStates.IsVanillaServer ? 15 : 127;
|
||||
|
||||
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.Awake))]
|
||||
public static class CreateOptionsPickerAwake
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static void Prefix(CreateOptionsPicker __instance)
|
||||
{
|
||||
public static bool Prefix(GameOptionsData __instance, [HarmonyArgument(0)] int maxExpectedPlayers)
|
||||
Instance = __instance;
|
||||
|
||||
if (GameStates.IsVanillaServer)
|
||||
{
|
||||
return __instance.MaxPlayers > maxExpectedPlayers ||
|
||||
__instance.NumImpostors < 1 ||
|
||||
__instance.NumImpostors + 1 > maxExpectedPlayers / 2 ||
|
||||
__instance.KillDistance is < 0 or > 2 ||
|
||||
__instance.PlayerSpeedMod is <= 0f or > 3f;
|
||||
if (GameOptionsManager.Instance.GameHostOptions != null)
|
||||
{
|
||||
if (GameOptionsManager.Instance.GameHostOptions.MaxPlayers > 15)
|
||||
{
|
||||
GameOptionsManager.Instance.GameHostOptions.SetInt(Int32OptionNames.MaxPlayers, 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(SecurityLogger), nameof(SecurityLogger.Awake))]
|
||||
public static class SecurityLoggerPatch
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static void Postfix(CreateOptionsPicker __instance)
|
||||
{
|
||||
public static void Postfix(ref SecurityLogger __instance)
|
||||
if (__instance.mode != SettingsMode.Host) return;
|
||||
|
||||
{
|
||||
__instance.Timers = new float[127];
|
||||
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 = "+"; // False error
|
||||
lastButtonRenderer.enabled = false; // False error
|
||||
var lastButtonButton = lastButtonRenderer.GetComponent<PassiveButton>(); // False error
|
||||
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); // False error
|
||||
|
||||
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(ServerManager), nameof(ServerManager.SetRegion))]
|
||||
public static class ServerManager_SetRegion
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static void Postfix()
|
||||
{
|
||||
if (GameStates.IsVanillaServer)
|
||||
{
|
||||
if (GameOptionsManager.Instance.GameHostOptions != null && GameOptionsManager.Instance.GameHostOptions.MaxPlayers > 15)
|
||||
GameOptionsManager.Instance.GameHostOptions.SetInt(Int32OptionNames.MaxPlayers, 15);
|
||||
|
||||
if (Instance)
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateMaxPlayersButtons))]
|
||||
public static class CreateOptionsPickerUpdateMaxPlayersButtons
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static bool Prefix(CreateOptionsPicker __instance, [HarmonyArgument(0)] IGameOptions opts)
|
||||
{
|
||||
if (__instance.mode != SettingsMode.Host) return true;
|
||||
|
||||
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; // False errors
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.UpdateImpostorsButtons))]
|
||||
public static class CreateOptionsPickerUpdateImpostorsButtons
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static bool Prefix(CreateOptionsPicker __instance)
|
||||
{
|
||||
return __instance.mode != SettingsMode.Host;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.SetImpostorButtons))]
|
||||
public static class CreateOptionsPickerSetImpostorButtons
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static bool Prefix(CreateOptionsPicker __instance, int numImpostors)
|
||||
{
|
||||
if (__instance.mode != SettingsMode.Host) return true;
|
||||
IGameOptions targetOptions = __instance.GetTargetOptions();
|
||||
targetOptions.SetInt(Int32OptionNames.NumImpostors, numImpostors);
|
||||
__instance.SetTargetOptions(targetOptions);
|
||||
__instance.UpdateImpostorsButtons(numImpostors);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.SetMaxPlayersButtons))]
|
||||
public static class CreateOptionsPickerSetMaxPlayersButtons
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static bool Prefix(CreateOptionsPicker __instance, int maxPlayers)
|
||||
{
|
||||
if (DestroyableSingleton<FindAGameManager>.InstanceExists || __instance.mode != SettingsMode.Host)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
IGameOptions targetOptions = __instance.GetTargetOptions();
|
||||
targetOptions.SetInt(Int32OptionNames.MaxPlayers, maxPlayers);
|
||||
__instance.SetTargetOptions(targetOptions);
|
||||
__instance.UpdateMaxPlayersButtons(targetOptions);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameOptionsData), nameof(GameOptionsData.AreInvalid))]
|
||||
public static class InvalidOptionsPatches
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
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
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static void Postfix(ref SecurityLogger __instance)
|
||||
{
|
||||
__instance.Timers = new float[127];
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.Update))]
|
||||
public static class PlayerTabIsSelectedItemEquippedPatch
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static void Postfix(PlayerTab __instance)
|
||||
{
|
||||
if (GameOptionsManager.Instance.CurrentGameOptions.MaxPlayers > 15)
|
||||
{
|
||||
__instance.currentColorIsEquipped = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.UpdateAvailableColors))]
|
||||
public static class PlayerTabUpdateAvailableColorsPatch
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public static bool Prefix(PlayerTab __instance)
|
||||
{
|
||||
if (GameOptionsManager.Instance.CurrentGameOptions.MaxPlayers <= 15)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.AvailableColors.Clear();
|
||||
|
||||
for (var i = 0; i < Palette.PlayerColors.Count; i++)
|
||||
{
|
||||
if (!PlayerControl.LocalPlayer || PlayerControl.LocalPlayer.CurrentOutfit.ColorId != i)
|
||||
{
|
||||
__instance.AvailableColors.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AbstractPagingBehaviour(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
{
|
||||
protected const string PageIndexGameObjectName = "CrowdedMod_PageIndex";
|
||||
|
||||
// ReSharper disable once ReplaceWithFieldKeyword
|
||||
private int _page;
|
||||
|
||||
protected static int MaxPerPage => 15;
|
||||
|
||||
public virtual int PageIndex
|
||||
{
|
||||
get => _page;
|
||||
set
|
||||
{
|
||||
_page = value;
|
||||
OnPageChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual int MaxPageIndex => throw new("MaxPageIndex must be overridden");
|
||||
public virtual void Start() => OnPageChanged();
|
||||
|
||||
public virtual void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.UpArrow) || Input.GetKeyDown(KeyCode.LeftArrow) || Input.mouseScrollDelta.y > 0f)
|
||||
Cycle(false);
|
||||
else if (Input.GetKeyDown(KeyCode.DownArrow) || Input.GetKeyDown(KeyCode.RightArrow) || Input.mouseScrollDelta.y < 0f)
|
||||
Cycle(true);
|
||||
}
|
||||
|
||||
public virtual void OnPageChanged() => throw new("OnPageChanged must be overridden");
|
||||
|
||||
/// <summary>
|
||||
/// Loops around if you go over the limits.<br/>
|
||||
/// Attempting to go up a page while on the first page will take you to the last page and vice versa.
|
||||
/// </summary>
|
||||
public virtual void Cycle(bool increment)
|
||||
{
|
||||
var change = increment ? 1 : -1;
|
||||
PageIndex = Mathf.Clamp(PageIndex + change, 0, MaxPageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public class MeetingHudPagingBehaviour(IntPtr ptr) : AbstractPagingBehaviour(ptr)
|
||||
{
|
||||
private readonly MeetingHud meetingHud = null!;
|
||||
[HideFromIl2Cpp] private IEnumerable<PlayerVoteArea> Targets => meetingHud.playerStates.OrderBy(p => p.AmDead);
|
||||
|
||||
protected override int MaxPageIndex => (Targets.Count() - 1) / MaxPerPage;
|
||||
public override void Start() => OnPageChanged();
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
// Sometimes the timer text is spammed with the page counter for some eccentric reason, so this is just a Band-Aid fix for it
|
||||
if (meetingHud.state is MeetingHud.VoteStates.Animating or MeetingHud.VoteStates.Proceeding || meetingHud.TimerText.text.Contains($" ({PageIndex + 1}/{MaxPageIndex + 1})")) return; // TimerText does not update there
|
||||
meetingHud.TimerText.text += $" ({PageIndex + 1}/{MaxPageIndex + 1})";
|
||||
}
|
||||
|
||||
public override void OnPageChanged()
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
foreach (var button in Targets)
|
||||
{
|
||||
if (i >= PageIndex * MaxPerPage && i < (PageIndex + 1) * MaxPerPage)
|
||||
{
|
||||
button.gameObject.SetActive(true);
|
||||
var relativeIndex = i % MaxPerPage;
|
||||
var row = relativeIndex / 3;
|
||||
var col = relativeIndex % 3;
|
||||
var buttonTransform = button.transform;
|
||||
|
||||
buttonTransform.localPosition = meetingHud.VoteOrigin +
|
||||
new Vector3(
|
||||
meetingHud.VoteButtonOffsets.x * col,
|
||||
meetingHud.VoteButtonOffsets.y * row,
|
||||
buttonTransform.localPosition.z
|
||||
);
|
||||
}
|
||||
else button.gameObject.SetActive(false);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ShapeShifterPagingBehaviour(IntPtr ptr) : AbstractPagingBehaviour(ptr)
|
||||
{
|
||||
public ShapeshifterMinigame shapeshifterMinigame = null!;
|
||||
private TextMeshPro PageText = null!;
|
||||
[HideFromIl2Cpp] private IEnumerable<ShapeshifterPanel> Targets => shapeshifterMinigame.potentialVictims.ToArray();
|
||||
|
||||
protected override int MaxPageIndex => (Targets.Count() - 1) / MaxPerPage;
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
PageText = Instantiate(HudManager.Instance.KillButton.cooldownTimerText, shapeshifterMinigame.transform);
|
||||
PageText.name = PageIndexGameObjectName;
|
||||
PageText.enableWordWrapping = false;
|
||||
PageText.gameObject.SetActive(true);
|
||||
PageText.transform.localPosition = new(4.1f, -2.36f, -1f);
|
||||
PageText.transform.localScale *= 0.5f;
|
||||
OnPageChanged();
|
||||
}
|
||||
|
||||
public override void OnPageChanged()
|
||||
{
|
||||
PageText.text = $"({PageIndex + 1}/{MaxPageIndex + 1})";
|
||||
var i = 0;
|
||||
|
||||
foreach (var panel in Targets)
|
||||
{
|
||||
if (i >= PageIndex * MaxPerPage && i < (PageIndex + 1) * MaxPerPage)
|
||||
{
|
||||
panel.gameObject.SetActive(true);
|
||||
var relativeIndex = i % MaxPerPage;
|
||||
var row = relativeIndex / 3;
|
||||
var col = relativeIndex % 3;
|
||||
var buttonTransform = panel.transform;
|
||||
|
||||
buttonTransform.localPosition =
|
||||
new(
|
||||
shapeshifterMinigame.XStart + shapeshifterMinigame.XOffset * col,
|
||||
shapeshifterMinigame.YStart + shapeshifterMinigame.YOffset * row,
|
||||
buttonTransform.localPosition.z
|
||||
);
|
||||
}
|
||||
else panel.gameObject.SetActive(false);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class VitalsPagingBehaviour(IntPtr ptr) : AbstractPagingBehaviour(ptr)
|
||||
{
|
||||
public VitalsMinigame vitalsMinigame = null!;
|
||||
private TextMeshPro PageText = null!;
|
||||
[HideFromIl2Cpp] private IEnumerable<VitalsPanel> Targets => vitalsMinigame.vitals.ToArray();
|
||||
|
||||
protected override int MaxPageIndex => (Targets.Count() - 1) / MaxPerPage;
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
PageText = Instantiate(HudManager.Instance.KillButton.cooldownTimerText, vitalsMinigame.transform);
|
||||
PageText.name = PageIndexGameObjectName;
|
||||
PageText.enableWordWrapping = false;
|
||||
PageText.gameObject.SetActive(true);
|
||||
PageText.transform.localPosition = new(2.7f, -2f, -1f);
|
||||
PageText.transform.localScale *= 0.5f;
|
||||
OnPageChanged();
|
||||
}
|
||||
|
||||
public override void OnPageChanged()
|
||||
{
|
||||
if (PlayerTask.PlayerHasTaskOfType<HudOverrideTask>(PlayerControl.LocalPlayer))
|
||||
return;
|
||||
|
||||
PageText.text = $"({PageIndex + 1}/{MaxPageIndex + 1})";
|
||||
var i = 0;
|
||||
|
||||
foreach (var panel in Targets)
|
||||
{
|
||||
if (i >= PageIndex * MaxPerPage && i < (PageIndex + 1) * MaxPerPage)
|
||||
{
|
||||
panel.gameObject.SetActive(true);
|
||||
var relativeIndex = i % MaxPerPage;
|
||||
var row = relativeIndex / 3;
|
||||
var col = relativeIndex % 3;
|
||||
var panelTransform = panel.transform;
|
||||
|
||||
panelTransform.localPosition =
|
||||
new(
|
||||
vitalsMinigame.XStart + vitalsMinigame.XOffset * col,
|
||||
vitalsMinigame.YStart + vitalsMinigame.YOffset * row,
|
||||
panelTransform.localPosition.z
|
||||
);
|
||||
}
|
||||
else panel.gameObject.SetActive(false);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,10 +511,9 @@ namespace EHR
|
||||
__instance.Value = (float)Math.Round(__instance.Value, 2);
|
||||
break;
|
||||
case StringNames.GameNumImpostors:
|
||||
__instance.ValidRange = new(1, Crowded.MaxImpostors);
|
||||
__instance.ValidRange = new(0, Crowded.MaxImpostors);
|
||||
__instance.Value = (float)Math.Round(__instance.Value, 2);
|
||||
if (DebugModeManager.AmDebugger) __instance.ValidRange.min = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -506,10 +506,7 @@ namespace EHR
|
||||
CustomRoles.NiceGuesser
|
||||
=> PlayerControl.LocalPlayer.KillSfx,
|
||||
|
||||
CustomRoles.Swooper or
|
||||
CustomRoles.Wraith or
|
||||
CustomRoles.Chameleon or
|
||||
CustomRoles.Drainer
|
||||
CustomRoles.Drainer
|
||||
=> PlayerControl.LocalPlayer.MyPhysics.ImpostorDiscoveredSound,
|
||||
|
||||
CustomRoles.Addict or
|
||||
@@ -534,10 +531,15 @@ namespace EHR
|
||||
|
||||
CustomRoles.Noisemaker
|
||||
or CustomRoles.NoisemakerEHR
|
||||
or CustomRoles.DarkHide
|
||||
or CustomRoles.Specter
|
||||
=> GetIntroSound(RoleTypes.Noisemaker),
|
||||
|
||||
CustomRoles.Phantom
|
||||
or CustomRoles.PhantomEHR
|
||||
or CustomRoles.Swooper
|
||||
or CustomRoles.Wraith
|
||||
or CustomRoles.Chameleon
|
||||
=> GetIntroSound(RoleTypes.Phantom),
|
||||
|
||||
CustomRoles.Shapeshifter
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace EHR
|
||||
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnPlayerJoined))]
|
||||
internal static class OnPlayerJoinedPatch
|
||||
{
|
||||
private static bool IsDisconnected(this ClientData client)
|
||||
public static bool IsDisconnected(this ClientData client)
|
||||
{
|
||||
var __instance = AmongUsClient.Instance;
|
||||
|
||||
|
||||
@@ -379,6 +379,11 @@ Source language: English
|
||||
> - Reference: Custom Region Installer (adding modded regions)
|
||||
>
|
||||
|
||||
### :star: [Crowded](https://github.com/CrowdedMods/CrowdedMod)
|
||||
|
||||
>
|
||||
> - Crowded is integrated into EHR
|
||||
|
||||
### Endless Host Roles (EHR), with Endless Possibilities, brought to you by the Endless Network.
|
||||
|
||||
##### Developers:
|
||||
|
||||
@@ -147,5 +147,10 @@ namespace EHR.Crewmate
|
||||
{
|
||||
hud.AbilityButton?.OverrideText(Translator.GetString("AddictVentButtonText"));
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -459,5 +459,10 @@ namespace EHR.Crewmate
|
||||
else
|
||||
hud.AbilityButton?.OverrideText(GetString("AlchemistVentButtonText"));
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,5 +160,10 @@ namespace EHR.Crewmate
|
||||
if (ReviveStartTS != 0) return string.Format(Translator.GetString("AltruistSuffixRevive"), ReviveTime.GetInt() - (Utils.TimeStamp - ReviveStartTS));
|
||||
return string.Format(Translator.GetString("AltruistSuffix"), Translator.GetString(RevivingMode ? "AltruistReviveMode" : "AltruistReportMode"));
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,5 +121,10 @@ namespace EHR.Crewmate
|
||||
{
|
||||
IsTeleported = false;
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,5 +180,10 @@ namespace EHR.Crewmate
|
||||
|
||||
return string.Format(Translator.GetString("Catcher.Suffix"), TrapPlaceDelay.GetInt() - (Utils.TimeStamp - DelayStartTS));
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,5 +58,10 @@ namespace EHR.Crewmate
|
||||
{
|
||||
UseAbility(pc);
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,5 +74,10 @@ namespace EHR.Crewmate
|
||||
|
||||
pc.RpcRemoveAbilityUse();
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -459,5 +459,10 @@ namespace EHR.Crewmate
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,5 +80,10 @@ namespace EHR.Crewmate
|
||||
else
|
||||
pc.Notify(Translator.GetString("OutOfAbilityUsesDoMoreTasks"));
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,5 +96,10 @@ namespace EHR.Crewmate
|
||||
pc.RPCPlayCustomSound("Dove");
|
||||
pc.Notify(string.Format(Translator.GetString("DovesOfNeaceOnGuard"), pc.GetAbilityUseLimit()));
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,5 +113,10 @@ namespace EHR.Crewmate
|
||||
|
||||
PlayersInVents.Clear();
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,5 +213,10 @@ namespace EHR.Crewmate
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,5 +144,10 @@ namespace EHR.Crewmate
|
||||
else
|
||||
pc.Notify(Translator.GetString("OutOfAbilityUsesDoMoreTasks"));
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,5 +140,10 @@ namespace EHR.Crewmate
|
||||
player.MarkDirtySettings();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,5 +95,10 @@ namespace EHR.Crewmate
|
||||
|
||||
OverrideTasksData.Create(9516, TabGroup.CrewmateRoles, CustomRoles.Mayor);
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,5 +50,10 @@ namespace EHR.Crewmate
|
||||
{
|
||||
pc.TPToRandomVent();
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,5 +208,10 @@ namespace EHR.Crewmate
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,6 +187,11 @@ namespace EHR.Crewmate
|
||||
return $"<#ff0000>O<sub>2</sub>:</color> {Utils.ColorString(GetLevelColor(), OxygenLevel.ToString())}%";
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
|
||||
private enum Level
|
||||
{
|
||||
Blind,
|
||||
|
||||
@@ -76,5 +76,10 @@ namespace EHR.Crewmate
|
||||
pc.NoCheckStartMeeting(pc.Data);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,5 +77,10 @@ namespace EHR.Crewmate
|
||||
|
||||
pc.RpcRemoveAbilityUse();
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,5 +131,10 @@ namespace EHR.Crewmate
|
||||
|
||||
return $"\u25b6 ({AbilityDuration.GetInt() - (Utils.TimeStamp - ActivateTimeStamp)}s)";
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,5 +109,10 @@ namespace EHR.Crewmate
|
||||
player.Notify(Translator.GetString("SecurityGuardSkillStop"));
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,5 +201,10 @@ namespace EHR.Crewmate
|
||||
state.FinishPatrolling();
|
||||
}, 0.1f, "SentinelFinishPatrol");
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ namespace EHR.Crewmate
|
||||
|
||||
public static void OnAnyoneDeath(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
Instances.DoIf(x => x.Target == target.PlayerId, _ => Main.AllAlivePlayerControls.Do(p => p.Notify(string.Format(Translator.GetString("SoothsayerDiedNotify"), Utils.ColorString(Main.PlayerColors.GetValueOrDefault(killer.PlayerId), killer.GetRealName())), 10f)));
|
||||
Instances.DoIf(x => x.Target == killer.PlayerId, _ => Main.AllAlivePlayerControls.Do(p => p.Notify(string.Format(Translator.GetString("SoothsayerDiedNotify"), Utils.ColorString(Main.PlayerColors.GetValueOrDefault(killer.PlayerId), killer.GetRealName())), 10f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,6 +211,11 @@ namespace EHR.Crewmate
|
||||
return $"<#ffffff>{Timer}</color>{base.GetProgressText(playerId, comms)}";
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
|
||||
private enum Mode
|
||||
{
|
||||
TeleportTarget,
|
||||
|
||||
@@ -145,5 +145,10 @@ namespace EHR.Crewmate
|
||||
{
|
||||
return Target != byte.MaxValue && seer.PlayerId == target.PlayerId && seer.PlayerId == TetherId ? $"<color=#00ffa5>Target:</color> <color=#ffffff>{Utils.GetPlayerById(Target).GetRealName()}</color>" : string.Empty;
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,5 +141,10 @@ namespace EHR.Crewmate
|
||||
player.Notify(Translator.GetString("TimeMasterSkillStop"), (int)player.GetAbilityUseLimit());
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,6 +304,11 @@ namespace EHR.Crewmate
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
|
||||
private enum ToiletVisibilityOptions
|
||||
{
|
||||
Instant,
|
||||
|
||||
@@ -139,5 +139,10 @@ namespace EHR.Crewmate
|
||||
player.Notify(string.Format(Translator.GetString("VeteranOffGuard"), (int)player.GetAbilityUseLimit()));
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsThisRole(pc) || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,5 +315,10 @@ namespace EHR.Impostor
|
||||
|
||||
return aa == null ? string.Empty : $"<#ffff00>\u26a0 {Delay.GetInt() - (Utils.TimeStamp - aa.ExtraAbilityStartTimeStamp):N0}</color>";
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return !IsMonitor || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,5 +291,10 @@ namespace EHR.Impostor
|
||||
target.RpcCheckAndMurder(target);
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return UsedRole != CustomRoles.Chameleon || pc.GetClosestVent()?.Id == ventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,6 +167,17 @@ namespace EHR
|
||||
return seerRole.IsNK() && seerRole == target.GetCustomRole() && seer.GetTeam() == target.GetTeam();
|
||||
}
|
||||
|
||||
protected bool IsThisRole(PlayerControl pc)
|
||||
{
|
||||
return pc.GetCustomRole() == Enum.Parse<CustomRoles>(GetType().Name, true);
|
||||
}
|
||||
|
||||
protected bool IsThisRole(byte id)
|
||||
{
|
||||
var role = Main.PlayerStates.TryGetValue(id, out var state) ? state.MainRole : CustomRoles.NotAssigned;
|
||||
return role == Enum.Parse<CustomRoles>(GetType().Name, true);
|
||||
}
|
||||
|
||||
// Option setup simplifier
|
||||
protected OptionSetupHandler StartSetup(int id, bool single = false)
|
||||
{
|
||||
@@ -179,6 +190,7 @@ namespace EHR
|
||||
|
||||
if (single) Options.SetupSingleRoleOptions(id++, tab, role, hideMaxSetting: true);
|
||||
else Options.SetupRoleOptions(id++, tab, role);
|
||||
|
||||
return new(id, tab, role);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using BepInEx.Unity.IL2CPP.Utils.Collections;
|
||||
using EHR;
|
||||
using EHR.Modules;
|
||||
using EHR.Neutral;
|
||||
using EHR.Patches;
|
||||
using HarmonyLib;
|
||||
using Il2CppInterop.Runtime.Injection;
|
||||
using UnityEngine;
|
||||
@@ -25,6 +26,8 @@ namespace EHR
|
||||
[BepInPlugin(PluginGuid, "EHR", PluginVersion)]
|
||||
[BepInIncompatibility("jp.ykundesu.supernewroles")]
|
||||
[BepInIncompatibility("MalumMenu")]
|
||||
[BepInIncompatibility("com.ten.thebetterroles")]
|
||||
[BepInIncompatibility("xyz.crowdedmods.crowdedmod")]
|
||||
[BepInProcess("Among Us.exe")]
|
||||
public class Main : BasePlugin
|
||||
{
|
||||
@@ -709,6 +712,13 @@ namespace EHR
|
||||
handler.Info($"{nameof(ThisAssembly.Git.Tag)}: {ThisAssembly.Git.Tag}");
|
||||
|
||||
ClassInjector.RegisterTypeInIl2Cpp<ErrorText>();
|
||||
ClassInjector.RegisterTypeInIl2Cpp<MeetingHudPagingBehaviour>();
|
||||
ClassInjector.RegisterTypeInIl2Cpp<ShapeShifterPagingBehaviour>();
|
||||
ClassInjector.RegisterTypeInIl2Cpp<VitalsPagingBehaviour>();
|
||||
|
||||
NormalGameOptionsV08.RecommendedImpostors = NormalGameOptionsV08.MaxImpostors = Enumerable.Repeat(127, 127).ToArray();
|
||||
NormalGameOptionsV08.MinPlayers = Enumerable.Repeat(4, 127).ToArray();
|
||||
HideNSeekGameOptionsV08.MinPlayers = Enumerable.Repeat(4, 127).ToArray();
|
||||
|
||||
CustomLogger.ClearLog();
|
||||
LogMonitor.StartMonitoring();
|
||||
|
||||
Reference in new issue
Block a user