using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using AmongUs.Data; using AmongUs.GameOptions; using EHR.Gamemodes; using EHR.Patches; using EHR.Roles; using HarmonyLib; using InnerNet; using TMPro; using UnityEngine; using static EHR.Translator; namespace EHR; [HarmonyPatch] public static class GameStartManagerPatch { public static long TimerStartTS; private static TextMeshPro WarningText; public static bool UpdateSpriteStartButton; public static float Timer => Math.Max(0, 597f - (Utils.TimeStamp - TimerStartTS)); [HarmonyPatch(typeof(TimerTextTMP), nameof(TimerTextTMP.UpdateText))] public static class TimerTextTMPUpdateTextPatch { public static bool Prefix(TimerTextTMP __instance) { int seconds = __instance.GetSecondsRemaining(); if (seconds < 60) return true; __instance.text.text = string.Format(GetString("LobbyTimer"), seconds / 60, seconds % 60); return false; } } [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.Start))] public static class GameStartManagerStartPatch { public static TextMeshPro HideName; public static TextMeshPro GameCountdown; public static void Postfix(GameStartManager __instance) { try { try { GameStartManagerUpdatePatch.DiscordManagerBridge.Init(); } catch (Exception e) { Utils.ThrowException(e); } if (!__instance) return; UpdateSpriteStartButton = true; GameCountdown = Object.Instantiate(__instance.PlayerCounter, __instance.HostInfoPanel.transform); GameCountdown.text = string.Empty; if (GameData.Instance && HudManager.InstanceExists && AmongUsClient.Instance.NetworkMode != NetworkModes.LocalGame && GameStates.CurrentServerType == GameStates.ServerType.Vanilla) { HudManager hudManager = HudManager.Instance; hudManager.ShowLobbyTimer(597); hudManager.LobbyTimerExtensionUI.timerText.transform.parent.transform.Find("Icon").gameObject.SetActive(false); GameStartManagerUpdatePatch.Warned = false; } if (AmongUsClient.Instance.AmHost) { __instance.GameStartTextParent.GetComponent().sprite = null; __instance.StartButton.ChangeButtonText(TranslationController.Instance.GetString(StringNames.StartLabel)); __instance.GameStartText.transform.localPosition = new(__instance.GameStartText.transform.localPosition.x, 2f, __instance.GameStartText.transform.localPosition.z); __instance.StartButton.activeTextColor = __instance.StartButton.inactiveTextColor = Color.white; __instance.EditButton.activeTextColor = __instance.EditButton.inactiveTextColor = Color.black; __instance.EditButton.inactiveSprites.GetComponent().color = new(0f, 0.647f, 1f, 1f); __instance.EditButton.activeSprites.GetComponent().color = new(0f, 0.847f, 1f, 1f); __instance.EditButton.inactiveSprites.transform.Find("Shine").GetComponent().color = new(0f, 1f, 1f, 0.5f); __instance.HostViewButton.activeTextColor = __instance.HostViewButton.inactiveTextColor = Color.black; __instance.HostViewButton.inactiveSprites.GetComponent().color = new(0f, 0.647f, 1f, 1f); __instance.HostViewButton.activeSprites.GetComponent().color = new(0f, 0.847f, 1f, 1f); __instance.HostViewButton.inactiveSprites.transform.Find("Shine").GetComponent().color = new(0f, 1f, 1f, 0.5f); } else { __instance.ClientViewButton.activeTextColor = __instance.ClientViewButton.inactiveTextColor = Color.black; __instance.ClientViewButton.inactiveSprites.GetComponent().color = new(0f, 0.647f, 1f, 1f); __instance.ClientViewButton.activeSprites.GetComponent().color = new(0f, 0.847f, 1f, 1f); __instance.ClientViewButton.inactiveSprites.transform.Find("Shine").GetComponent().color = new(0f, 1f, 1f, 0.5f); } if (!AmongUsClient.Instance || AmongUsClient.Instance.IsGameStarted || GameStates.IsInGame || __instance.startState == GameStartManager.StartingStates.Starting) return; __instance.GameRoomNameCode.text = GameCode.IntToGameName(AmongUsClient.Instance.GameId); // Reset lobby countdown timer TimerStartTS = Utils.TimeStamp; HideName = Object.Instantiate(__instance.GameRoomNameCode, __instance.GameRoomNameCode.transform); HideName.text = ColorUtility.TryParseHtmlString(Main.HideColor.Value, out _) ? $"{Main.HideName.Value}" : $"{Main.HideName.Value}"; WarningText = Object.Instantiate(__instance.GameStartText, __instance.transform.parent); WarningText.name = "WarningText"; WarningText.transform.localPosition = new(0f, __instance.transform.localPosition.y + 3f, -1f); WarningText.gameObject.SetActive(false); if (!AmongUsClient.Instance.AmHost) return; if (ModUpdater.IsBroken || ModUpdater.HasUpdate && ModUpdater.ForceUpdate || !Main.AllowPublicRoom) { // __instance.MakePublicButton.color = Palette.DisabledClear; // __instance.privatePublicText.color = Palette.DisabledClear; } if (Main.NormalOptions.KillCooldown == 0f) Main.NormalOptions.KillCooldown = Main.LastKillCooldown.Value; #if IL2CPP AURoleOptions.SetOpt(Main.NormalOptions.CastFast()); #else AURoleOptions.SetOpt(Main.NormalOptions); #endif if (AURoleOptions.ShapeshifterCooldown == 0f) AURoleOptions.ShapeshifterCooldown = Main.LastShapeshifterCooldown.Value; AURoleOptions.ProtectionDurationSeconds = 0f; } catch (Exception ex) { Logger.Error(ex.ToString(), "GameStartManagerStartPatch.Postfix (1)"); } } } [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.Update))] public static class GameStartManagerUpdatePatch { public static float ExitTimer = -1f; private static float MinWait, MaxWait; private static int MinPlayer; private static SpriteRenderer LobbyTimerBg; public static bool Warned; public static class DiscordManagerBridge { private static bool Initialized; public static bool Enabled; public static Func InstanceExistsGetter; public static Func InstanceGetter; public static MethodInfo SetInLobbyHost; public static MethodInfo SetInLobbyClient; public static void Init() { if (Initialized) return; Initialized = true; var discordType = AppDomain.CurrentDomain.GetAssemblies().Select(a => { try { return a.GetType("DiscordManager"); } catch { return null; } }).FirstOrDefault(t => t != null); if (discordType == null) return; var instanceProp = discordType.BaseType!.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static); InstanceGetter = (Func)Delegate.CreateDelegate(typeof(Func), instanceProp!.GetGetMethod()!); var existsProp = discordType.BaseType.GetProperty("InstanceExists", BindingFlags.Public | BindingFlags.Static); InstanceExistsGetter = (Func)Delegate.CreateDelegate(typeof(Func), existsProp!.GetGetMethod()!); SetInLobbyHost = discordType.GetMethod("SetInLobbyHost", BindingFlags.Public | BindingFlags.Instance); SetInLobbyClient = discordType.GetMethod("SetInLobbyClient", BindingFlags.Public | BindingFlags.Instance); Enabled = true; } } public static bool Prefix(GameStartManager __instance) { try { try { __instance.MinPlayers = 1; } catch (Exception ex) { Logger.Error(ex.ToString(), "Surely this can't be causing an issue, right?"); } if (!AmongUsClient.Instance) return false; VanillaUpdate(__instance); if (AmongUsClient.Instance.IsGameStarted || GameStates.IsInGame || !__instance || __instance.startState == GameStartManager.StartingStates.Starting) return false; // Lobby code if (DataManager.Settings != null && DataManager.Settings.Gameplay != null) { if (DataManager.Settings.Gameplay.StreamerMode) { if (__instance.GameRoomNameCode) __instance.GameRoomNameCode.color = new(255, 255, 255, 0); if (GameStartManagerStartPatch.HideName) GameStartManagerStartPatch.HideName.enabled = true; } else { if (__instance.GameRoomNameCode) __instance.GameRoomNameCode.color = new(255, 255, 255, 255); if (GameStartManagerStartPatch.HideName) GameStartManagerStartPatch.HideName.enabled = false; } } if (!GameData.Instance) return true; CheckAutoStart(__instance); } catch (NullReferenceException) { } catch (Exception ex) { Logger.Error(ex.ToString(), "GameStartManagerUpdatePatch.Prefix (2)"); } return false; } private static void CheckAutoStart(GameStartManager __instance) { MinWait = Options.MinWaitAutoStart.GetFloat(); MaxWait = Options.MaxWaitAutoStart.GetFloat(); MinPlayer = Options.PlayerAutoStart.GetInt(); MinWait = 600f - MinWait * 60f; MaxWait *= 60f; bool votedToStart = (int)Math.Round(ChatCommands.VotedToStart.Count / (float)PlayerControl.AllPlayerControls.Count * 100f) > 50; if ((Main.AutoStart == null || !Main.AutoStart.Value) && !votedToStart) return; float timer = Timer; if (timer > 60 && GameSettingMenu.Instance) return; Main.UpdateTime++; if (Main.UpdateTime < 50) return; Main.UpdateTime = 0; if (GameStates.IsCountDown) return; if ((GameData.Instance?.PlayerCount < MinPlayer || timer > MinWait) && timer > MaxWait && !votedToStart) return; PlayerControl[] invalidColor = Main.EnumeratePlayerControls().Where(p => p.Data.DefaultOutfit.ColorId < 0 || Palette.PlayerColors.Length <= p.Data.DefaultOutfit.ColorId).ToArray(); if (invalidColor.Length > 0) { Main.UpdateTime = -100; Main.EnumeratePlayerControls() .Where(p => p.Data.DefaultOutfit.ColorId < 0 || Palette.PlayerColors.Length <= p.Data.DefaultOutfit.ColorId) .Do(p => AmongUsClient.Instance.KickPlayer(p.OwnerId, false)); Logger.SendInGame(GetString("Error.InvalidColorPreventStart"), Color.yellow); string msg = GetString("Error.InvalidColor"); msg += "\n" + invalidColor.Join(",", p => $"{p.GetRealName()}"); Utils.SendMessage(msg, importance: MessageImportance.Low); } UpdateSpriteStartButton = true; if (Options.RandomMapsMode.GetBool()) { Main.NormalOptions.MapId = GameStartRandomMap.SelectRandomMap(); GameOptionsMapPickerPatch.SetDleks = Main.CurrentMap == MapNames.Dleks; } else if (GameOptionsMapPickerPatch.SetDleks) Main.NormalOptions.MapId = 3; else if (GameOptionsMapPickerPatch.SetSubmerged) Main.NormalOptions.MapId = 6; if (Options.OverrideSpeedForEachMap.GetBool() && Options.MapSpeeds.TryGetValue(Main.CurrentMap, out var option)) Main.NormalOptions.PlayerSpeedMod = option.GetFloat(); if (Main.CurrentMap == MapNames.Dleks || Main.NormalOptions.MapId == 6) { #if IL2CPP IGameOptions opt = Main.NormalOptions.CastFast(); #else IGameOptions opt = Main.NormalOptions; #endif Options.DefaultKillCooldown = Main.NormalOptions.KillCooldown; Main.LastKillCooldown.Value = Main.NormalOptions.KillCooldown; AURoleOptions.SetOpt(opt); Main.LastShapeshifterCooldown.Value = AURoleOptions.ShapeshifterCooldown; AURoleOptions.ShapeshifterCooldown = 0f; AURoleOptions.ImpostorsCanSeeProtect = false; GameManager.Instance.LogicOptions.SetDirty(); OptionItem.SyncAllOptions(); } GameStartManager.Instance.startState = GameStartManager.StartingStates.Countdown; GameStartManager.Instance.countDownTimer = Options.AutoStartTimer.GetInt(); __instance?.StartButton.gameObject.SetActive(false); if (HudManager.InstanceExists) HudManager.Instance.Dialogue.Hide(); } private static void VanillaUpdate(GameStartManager instance) { if (!GameData.Instance || !GameManager.Instance) return; try { instance.UpdateMapImage((MapNames)GameManager.Instance.LogicOptions.MapId); } catch (Exception e) { if (!(GameManager.Instance.LogicOptions.MapId == 6 && SubmergedCompatibility.Loaded)) { if (GameManager.Instance.LogicOptions.MapId >= Main.MapNamesValues.Length) ErrorText.Instance.AddError(ErrorCode.UnsupportedMap); Utils.ThrowException(e); } } instance.CheckSettingsDiffs(); instance.StartButton.gameObject.SetActive(AmongUsClient.Instance.AmHost); instance.RulesPresetText.text = TranslationController.Instance.GetString(GameOptionsManager.Instance.CurrentGameOptions.GetRulesPresetTitle()); if (GameCode.IntToGameName(AmongUsClient.Instance.GameId) == null) instance.privatePublicPanelText.text = TranslationController.Instance.GetString(StringNames.LocalButton); else if (AmongUsClient.Instance.IsGamePublic) instance.privatePublicPanelText.text = TranslationController.Instance.GetString(StringNames.PublicHeader); else instance.privatePublicPanelText.text = TranslationController.Instance.GetString(StringNames.PrivateHeader); instance.HostPrivateButton.gameObject.SetActive(!AmongUsClient.Instance.IsGamePublic); instance.HostPublicButton.gameObject.SetActive(AmongUsClient.Instance.IsGamePublic); if ((Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) && Input.GetKeyDown(KeyCode.C)) ClipboardHelper.PutClipboardString(GameCode.IntToGameName(AmongUsClient.Instance.GameId)); if (GameData.Instance.PlayerCount != instance.LastPlayerCount) { instance.LastPlayerCount = GameData.Instance.PlayerCount; var text = ""; if (instance.LastPlayerCount > instance.MinPlayers) text = ""; if (instance.LastPlayerCount == instance.MinPlayers) text = ""; instance.PlayerCounter.text = $"{text}{instance.LastPlayerCount}/{(AmongUsClient.Instance.NetworkMode == NetworkModes.LocalGame ? 15 : GameManager.Instance.LogicOptions.MaxPlayers)}"; instance.StartButton.SetButtonEnableState(instance.LastPlayerCount >= instance.MinPlayers); ActionMapGlyphDisplay startButtonGlyph = instance.StartButtonGlyph; startButtonGlyph?.SetColor(instance.LastPlayerCount >= instance.MinPlayers ? Palette.EnabledColor : Palette.DisabledClear); if (DiscordManagerBridge.Enabled && DiscordManagerBridge.InstanceExistsGetter()) { var method = AmongUsClient.Instance.AmHost && AmongUsClient.Instance.NetworkMode == NetworkModes.OnlineGame ? DiscordManagerBridge.SetInLobbyHost : DiscordManagerBridge.SetInLobbyClient; method.Invoke(DiscordManagerBridge.InstanceGetter(), [instance.LastPlayerCount, GameManager.Instance.LogicOptions.MaxPlayers, AmongUsClient.Instance.GameId]); } } if (AmongUsClient.Instance.AmHost) { if (instance.startState == GameStartManager.StartingStates.Countdown) { instance.StartButton.ChangeButtonText(GetString("Cancel")); if (UpdateSpriteStartButton) { instance.StartButton.inactiveSprites.GetComponent().color = new(0.8f, 0f, 0f, 1f); instance.StartButton.activeSprites.GetComponent().color = Color.red; instance.StartButton.inactiveSprites.transform.Find("Shine").GetComponent().color = new(0.8f, 0.4f, 0.4f, 1f); instance.StartButton.activeTextColor = instance.StartButton.inactiveTextColor = Color.white; } int num = Mathf.CeilToInt(instance.countDownTimer); instance.countDownTimer -= Time.deltaTime; int num2 = Mathf.CeilToInt(instance.countDownTimer); if (!instance.GameStartTextParent.activeSelf) SoundManager.Instance.PlaySound(instance.gameStartSound, false); instance.GameStartTextParent.SetActive(true); instance.GameStartText.text = TranslationController.Instance.GetString(StringNames.GameStarting, num2); if (num != num2) PlayerControl.LocalPlayer.RpcSetStartCounter(num2); if (num2 <= 0) instance.FinallyBegin(); } else { instance.StartButton.ChangeButtonText(TranslationController.Instance.GetString(StringNames.StartLabel)); if (UpdateSpriteStartButton) { instance.StartButton.inactiveSprites.GetComponent().color = new(0.1f, 0.1f, 0.1f, 1f); instance.StartButton.activeSprites.GetComponent().color = new(0.2f, 0.2f, 0.2f, 1f); instance.StartButton.inactiveSprites.transform.Find("Shine").GetComponent().color = new(0.3f, 0.3f, 0.3f, 0.5f); instance.StartButton.activeTextColor = instance.StartButton.inactiveTextColor = Color.white; } instance.GameStartTextParent.SetActive(false); instance.GameStartText.text = string.Empty; } UpdateSpriteStartButton = false; } if (!HudManager.InstanceExists) return; if (instance.LobbyInfoPane.gameObject.activeSelf) { var lobbyViewSettingsPane = instance.LobbyInfoPane.LobbyViewSettingsPane; lobbyViewSettingsPane.scrollBar.enabled = !HudManager.Instance.Chat.IsOpenOrOpening; } //if (instance.LobbyInfoPane.gameObject.activeSelf && HudManager.Instance.Chat.IsOpenOrOpening) // instance.LobbyInfoPane.DeactivatePane(); //instance.LobbyInfoPane.gameObject.SetActive(!HudManager.Instance.Chat.IsOpenOrOpening); } public static void Postfix(GameStartManager __instance) { try { if (!AmongUsClient.Instance || AmongUsClient.Instance.IsGameStarted || GameStates.IsInGame || !__instance || __instance.startState == GameStartManager.StartingStates.Starting) return; var canStartGame = true; var mismatchedClientName = string.Empty; var warningMessage = ""; if (AmongUsClient.Instance.AmHost) { ClientData[] allClients = AmongUsClient.Instance.allClients.ToArray(); lock (allClients) { // ReSharper disable once ForCanBeConvertedToForeach for (var index = 0; index < allClients.Length; index++) { ClientData client = allClients[index]; if (!client.Character) continue; var dummyComponent = client.Character.GetComponent(); if (dummyComponent && dummyComponent.enabled) continue; if (!MatchVersions(client.Character.PlayerId, true)) { canStartGame = false; mismatchedClientName = client.Character.PlayerId.ColoredPlayerName(); } } } if (!canStartGame && __instance.StartButton) __instance.StartButton.gameObject.SetActive(false); } else { if (MatchVersions(0, true)) ExitTimer = 0; else { ExitTimer += Time.deltaTime; if (ExitTimer >= 5) { ExitTimer = 0; AmongUsClient.Instance.ExitGame(DisconnectReasons.ExitGame); SceneChanger.ChangeScene("MainMenu"); } if (ExitTimer != 0) warningMessage = Utils.ColorString(Color.red, string.Format(GetString("Warning.AutoExitAtMismatchedVersion"), $"{Main.ModName}", ((int)Math.Round(5 - ExitTimer)).ToString())); } } if (WarningText) { if (warningMessage == "") WarningText.gameObject.SetActive(false); else { WarningText.text = warningMessage; WarningText.gameObject.SetActive(true); } } __instance.RulesPresetText.text = GetString($"Preset_{OptionItem.CurrentPreset + 1}"); int estimatedGameLength = Options.CurrentGameMode switch { CustomGameMode.SoloPVP => SoloPVP.SoloPVP_GameTime.GetInt(), CustomGameMode.FFA => Math.Clamp(FreeForAll.FFAKcd.GetInt() * (PlayerControl.AllPlayerControls.Count / 2) + FreeForAll.FFAKcd.GetInt(), FreeForAll.FFAKcd.GetInt(), FreeForAll.FFAGameTime.GetInt()), CustomGameMode.StopAndGo => (Main.NormalOptions.NumShortTasks * 30 + Main.NormalOptions.NumLongTasks * 60 + Math.Min(3, Main.NormalOptions.NumCommonTasks) * 40) / (int)(Main.NormalOptions.PlayerSpeedMod - (Main.NormalOptions.PlayerSpeedMod - 1) / 2), CustomGameMode.HotPotato => HotPotato.GetKillInterval() * (PlayerControl.AllPlayerControls.Count - 1), CustomGameMode.HideAndSeek => Math.Min(Seeker.KillCooldown.GetInt() * (PlayerControl.AllPlayerControls.Count - Main.NormalOptions.NumImpostors) / Main.NormalOptions.NumImpostors + Seeker.BlindTime.GetInt() + 15, Math.Min(CustomHnS.MaximumGameLength, (Main.NormalOptions.NumShortTasks * 20 + Main.NormalOptions.NumLongTasks * 30 + Math.Min(3, Main.NormalOptions.NumCommonTasks) * 20) / (int)(Main.NormalOptions.PlayerSpeedMod - (Main.NormalOptions.PlayerSpeedMod - 1) / 2))), CustomGameMode.Speedrun => Speedrun.TimeLimitValue * (Main.NormalOptions.NumShortTasks + Main.NormalOptions.NumLongTasks + Main.NormalOptions.NumCommonTasks) + Speedrun.KCD * (PlayerControl.AllPlayerControls.Count / (Speedrun.RestrictedKilling ? 3 : 4)), CustomGameMode.CaptureTheFlag => CaptureTheFlag.GameEndCriteriaType == 2 ? CaptureTheFlag.MaxGameLength : CaptureTheFlag.IsDeathPossible ? 40 : Math.Max(30, 1500 / (int)Math.Pow(CaptureTheFlag.KCD + 0.5f, 2) * CaptureTheFlag.TotalRoundsToPlay), CustomGameMode.NaturalDisasters => 180 + 15 * NaturalDisasters.FrequencyOfDisasters * Math.Max(1, Math.Min(20, PlayerControl.AllPlayerControls.Count) / 4), CustomGameMode.RoomRush => (int)Math.Round((RoomRush.PointsSystem ? RoomRush.RawPointsToWin * 1.5f : PlayerControl.AllPlayerControls.Count - 1) * ((Main.NormalOptions.MapId is 0 or 3 ? 15 : 20) / Main.NormalOptions.PlayerSpeedMod)), CustomGameMode.KingOfTheZones => Math.Min(KingOfTheZones.MaxGameTime, KingOfTheZones.MaxGameTimeByPoints), CustomGameMode.Deathrace => Deathrace.LapsToWin * (int)Math.Ceiling(25 / Main.NormalOptions.PlayerSpeedMod), _ => 0 }; string suffix = estimatedGameLength != 0 ? $"{GetString("EstimatedGameLength")} - {estimatedGameLength / 60:00}:{estimatedGameLength % 60:00}" : " "; if (Options.NoGameEnd.GetBool()) suffix = Utils.ColorString(Color.yellow, $"{GetString("NoGameEnd").ToUpper()}"); if (!canStartGame) suffix = Utils.ColorString(Color.red, string.Format(GetString("VersionMismatch"), mismatchedClientName)); TextMeshPro tmp = GameStartManagerStartPatch.GameCountdown; if (tmp) { if (tmp.text == string.Empty) { tmp.name = "LobbyInfoText"; tmp.fontSize = tmp.fontSizeMin = tmp.fontSizeMax = 3f; tmp.autoSizeTextContainer = true; tmp.alignment = TextAlignmentOptions.Center; tmp.color = Color.cyan; tmp.outlineColor = Color.black; tmp.outlineWidth = LangHasSensitiveOutlineText() ? 0.1f : 0.4f; tmp.transform.localPosition += new Vector3(-0.625f, -0.12f, 0f); tmp.transform.localScale = new(0.6f, 0.6f, 1f); } tmp.text = suffix; tmp.gameObject.SetActive(true); } // Lobby timer if (GameData.Instance && HudManager.InstanceExists && AmongUsClient.Instance.NetworkMode != NetworkModes.LocalGame && GameStates.CurrentServerType == GameStates.ServerType.Vanilla) { float timer = Timer; if (!LobbyTimerBg) LobbyTimerBg = HudManager.Instance.LobbyTimerExtensionUI.timerText.transform.parent.transform.Find("LabelBackground").GetComponent(); LobbyTimerBg.sprite = Utils.LoadSprite("EHR.Resources.Images.LobbyTimerBG.png", 100f); LobbyTimerBg.color = GetTimerColor(timer); if (timer <= 60 && !Warned && AmongUsClient.Instance.AmHost) { Warned = true; LobbyTimerExtensionUI lobbyTimerExtensionUI = HudManager.Instance.LobbyTimerExtensionUI; lobbyTimerExtensionUI.timerText.transform.parent.transform.Find("Icon")?.gameObject.SetActive(true); SoundManager.Instance.PlaySound(lobbyTimerExtensionUI.lobbyTimerPopUpSound, false); Utils.FlashColor(new(1f, 1f, 0f, 0.4f), 1.4f); } } } catch (NullReferenceException) { } catch (Exception e) { Logger.Error(e.ToString(), "GameStartManagerUpdatePatch.Postfix (3)"); } } private static Color GetTimerColor(float timer) { switch (timer) { case >= 180f: { return new Color32(0x00, 0x04, 0x44, 0xFF); } case >= 120f: { // 120 → 180: #ffff00 → #00ffa5 float lerpT = (timer - 120f) / 60f; return Color.Lerp(new Color32(0xFF, 0xFF, 0x00, 0xFF), new Color32(0x00, 0x04, 0x44, 0xFF), lerpT); } case >= 60f: { // 60 → 120: #ff0000 → #ffff00 float lerpT = (timer - 60f) / 60f; return Color.Lerp(new Color32(0xFF, 0x00, 0x00, 0xFF), new Color32(0xFF, 0xFF, 0x00, 0xFF), lerpT); } default: { return new Color32(0xFF, 0x00, 0x00, 0xFF); } } } private static bool MatchVersions(byte playerId, bool acceptVanilla = false) { if (!Main.PlayerVersion.TryGetValue(playerId, out PlayerVersion version)) return acceptVanilla; return Main.ForkId == version.forkId && Main.Version.CompareTo(version.version) == 0 && version.tag == $"{ThisAssembly.Git.Commit}({ThisAssembly.Git.Branch})"; } } [HarmonyPatch(typeof(TextBoxTMP), nameof(TextBoxTMP.SetText))] public static class HiddenTextPatch { private static void Postfix(TextBoxTMP __instance) { if (__instance.name == "GameIdText") __instance.outputText.text = new('*', __instance.text.Length); } } } [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.BeginGame))] public static class GameStartRandomMap { public static bool Prefix(GameStartManager __instance) { PlayerControl[] invalidColor = Main.EnumeratePlayerControls().Where(p => p.Data.DefaultOutfit.ColorId < 0 || Palette.PlayerColors.Length <= p.Data.DefaultOutfit.ColorId).ToArray(); if (invalidColor.Length > 0) { Logger.SendInGame(GetString("Error.InvalidColorPreventStart"), Color.yellow); string msg = GetString("Error.InvalidColor"); msg += "\n" + invalidColor.Join(",", p => $"{p.GetRealName()}"); Utils.SendMessage(msg, importance: MessageImportance.Low); return false; } GameStartManagerPatch.UpdateSpriteStartButton = true; if (Options.RandomMapsMode.GetBool()) { Main.NormalOptions.MapId = SelectRandomMap(); GameOptionsMapPickerPatch.SetDleks = Main.CurrentMap == MapNames.Dleks; } else if (GameOptionsMapPickerPatch.SetDleks) Main.NormalOptions.MapId = 3; else if (GameOptionsMapPickerPatch.SetSubmerged) Main.NormalOptions.MapId = 6; if (Options.OverrideSpeedForEachMap.GetBool() && Options.MapSpeeds.TryGetValue(Main.CurrentMap, out var option)) Main.NormalOptions.PlayerSpeedMod = option.GetFloat(); if (__instance.startState != GameStartManager.StartingStates.Countdown) { Options.DefaultKillCooldown = Main.NormalOptions.KillCooldown; Main.LastKillCooldown.Value = Main.NormalOptions.KillCooldown; } #if IL2CPP IGameOptions opt = Main.NormalOptions.CastFast(); #else IGameOptions opt = Main.NormalOptions; #endif AURoleOptions.SetOpt(opt); if (__instance.startState == GameStartManager.StartingStates.Countdown) AURoleOptions.ShapeshifterCooldown = Main.LastShapeshifterCooldown.Value; else { Main.LastShapeshifterCooldown.Value = AURoleOptions.ShapeshifterCooldown; AURoleOptions.ShapeshifterCooldown = 0f; } GameManager.Instance.LogicOptions.SetDirty(); OptionItem.SyncAllOptions(); __instance.ReallyBegin(false); return false; } public static byte SelectRandomMap() { Dictionary chance = Enumerable.Range(0, 6).ToDictionary(x => (byte)x, x => x switch { 0 => Options.SkeldChance.GetInt(), 1 => Options.MiraChance.GetInt(), 2 => Options.PolusChance.GetInt(), 3 => Options.DleksChance.GetInt(), 4 => Options.AirshipChance.GetInt(), 5 => Options.FungleChance.GetInt(), _ => 0 }); int playerCount = PlayerControl.AllPlayerControls.Count; if (playerCount < Options.MinPlayersForAirship.GetInt()) chance.Remove(4); if (playerCount < Options.MinPlayersForFungle.GetInt()) chance.Remove(5); byte[] pool = chance.SelectMany(x => Enumerable.Repeat(x.Key, x.Value / 5)).ToArray(); return pool.Length == 0 ? chance.Keys.RandomElement() : pool.RandomElement(); } } [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.ResetStartState))] internal static class ResetStartStatePatch { public static void Prefix(GameStartManager __instance) { SoundManager.Instance.StopSound(__instance.gameStartSound); GameStartManagerPatch.UpdateSpriteStartButton = true; } } [HarmonyPatch(typeof(IGameOptionsExtensions), nameof(IGameOptionsExtensions.GetAdjustedNumImpostors))] internal static class UnrestrictedNumImpostorsPatch { public static bool Prefix(ref int __result) { __result = Main.NormalOptions.NumImpostors; return false; } } public static class GameStartManagerBeginPatch { [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.ReallyBegin))] public static class GameStartManagerStartPatch { public static bool Prefix(GameStartManager __instance) { if (!AmongUsClient.Instance.AmHost) return true; if (__instance.startState == GameStartManager.StartingStates.Countdown) { __instance.ResetStartState(); ChatCommands.VotedToStart.Clear(); return false; } __instance.startState = GameStartManager.StartingStates.Countdown; __instance.GameSizePopup.SetActive(false); DataManager.Player.Onboarding.AlwaysShowMinPlayerWarning = false; DataManager.Player.Onboarding.ViewedMinPlayerWarning = true; DataManager.Player.Save(); __instance.StartButton.gameObject.SetActive(false); __instance.StartButtonClient.gameObject.SetActive(false); __instance.GameStartTextParent.SetActive(false); __instance.countDownTimer = 5.0001f; __instance.startState = GameStartManager.StartingStates.Countdown; AmongUsClient.Instance.KickNotJoinedPlayers(); return false; } } } [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.FinallyBegin))] public static class GameStartManagerFinallyBeginPatch { public static void Prefix(GameStartManager __instance) { SoundManager.Instance.StopSound(__instance.gameStartSound); } }