From 589cf8285de89020572609e1a963e5632e054f56 Mon Sep 17 00:00:00 2001 From: D1GQ Date: Fri, 27 Feb 2026 23:40:40 -0600 Subject: [PATCH] add comments --- .../Anticheat/CheckPlayerLevelPatch.cs | 5 +- .../Gameplay/Anticheat/PlatformSpoofPatch.cs | 7 ++- src/Patches/Gameplay/LobbyPatch.cs | 27 ++++++++--- .../Gameplay/Player/CosmeticsLayerPatch.cs | 4 +- .../Gameplay/Player/PlayerControlPatch.cs | 3 ++ src/Patches/Gameplay/RolePatch.cs | 4 +- src/Patches/Gameplay/Ship/ShipStatusPatch.cs | 3 +- src/Patches/Gameplay/Ship/VentPatch.cs | 4 +- .../Gameplay/UI/Chat/ChatNotificationPatch.cs | 1 + src/Patches/Gameplay/UI/Chat/ChatPatch.cs | 2 +- .../Gameplay/UI/Chat/QuickChatPatch.cs | 5 +- src/Patches/Gameplay/UI/GameSummaryPatch.cs | 39 ++++++++++++---- src/Patches/Gameplay/UI/IntroCutscenePatch.cs | 8 ++++ src/Patches/Gameplay/UI/KillOverlayPatch.cs | 3 +- src/Patches/Gameplay/UI/MeetingHudPatch.cs | 7 ++- .../Gameplay/UI/MiniMapBehaviourPatch.cs | 46 +++++++++++++++---- src/Patches/Gameplay/UI/PlayerTabPatch.cs | 29 +++++++++--- src/Patches/Gameplay/UI/RoomTrackerPatch.cs | 1 + .../Gameplay/UI/Settings/GameSettingsPatch.cs | 45 ++++++++++++++---- .../Gameplay/UI/Settings/NumberOptionPatch.cs | 24 +++++++--- src/Patches/Gameplay/ZoomPatch.cs | 22 +++++++-- src/Patches/Unity/StandaloneSteamPatch.cs | 12 +++-- src/Patches/Unity/UnityWebRequestPatch.cs | 10 +++- 23 files changed, 242 insertions(+), 69 deletions(-) diff --git a/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs b/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs index d1e33c8..159c9d8 100644 --- a/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs +++ b/src/Patches/Gameplay/Anticheat/CheckPlayerLevelPatch.cs @@ -14,10 +14,11 @@ internal class CheckPlayerLevelPatch { if (GameState.IsHost) { - if (!__instance.IsLocalPlayer() && (__instance.Data.PlayerLevel < BetterGameSettings.KickLevelBelow.GetInt())) + // Kick players below minimum level + if (!__instance.IsLocalPlayer() && __instance.Data.PlayerLevel < BetterGameSettings.KickLevelBelow.GetInt()) { __instance.Kick(setReasonInfo: $" is level {__instance.Data.PlayerLevel}, level must be equal or above {BetterGameSettings.KickLevelBelow.GetInt()} to join"); } } } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/Anticheat/PlatformSpoofPatch.cs b/src/Patches/Gameplay/Anticheat/PlatformSpoofPatch.cs index ab2dd95..e6f10e2 100644 --- a/src/Patches/Gameplay/Anticheat/PlatformSpoofPatch.cs +++ b/src/Patches/Gameplay/Anticheat/PlatformSpoofPatch.cs @@ -27,10 +27,12 @@ internal class PlatformSpoofPatch if (player != null && __instance?.Platform != null) { + // Check Xbox/Windows store players for invalid platform ID length if (__instance.Platform is Platforms.StandaloneWin10 or Platforms.Xbox) { if (__instance.XboxPlatformId.ToString().Length is < 10 or > 16) { + // Invalid ID length, likely spoofing player.ReportPlayer(ReportReasons.Cheating_Hacking); BetterNotificationManager.NotifyCheat(player, Translator.GetString("AntiCheat.Reason.PlatformSpoofer"), @@ -40,10 +42,12 @@ internal class PlatformSpoofPatch } } + // Check Playstation players for invalid platform ID length if (__instance.Platform is Platforms.Playstation) { if (__instance.PsnPlatformId.ToString().Length is < 14 or > 20) { + // Invalid ID length, likely spoofing player.ReportPlayer(ReportReasons.Cheating_Hacking); BetterNotificationManager.NotifyCheat(player, Translator.GetString("AntiCheat.Reason.PlatformSpoofer"), @@ -53,6 +57,7 @@ internal class PlatformSpoofPatch } } + // Check for unknown or undefined platforms if (__instance.Platform is Platforms.Unknown || !Enum.IsDefined(__instance.Platform)) { BetterNotificationManager.NotifyCheat(player, @@ -67,4 +72,4 @@ internal class PlatformSpoofPatch catch { } } } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/LobbyPatch.cs b/src/Patches/Gameplay/LobbyPatch.cs index 722c844..b9ef096 100644 --- a/src/Patches/Gameplay/LobbyPatch.cs +++ b/src/Patches/Gameplay/LobbyPatch.cs @@ -14,10 +14,11 @@ internal static class LobbyPatch [HarmonyPostfix] private static void LobbyBehaviour_Start_Postfix() { + // Reset player selection options when lobby starts OptionPlayerItem.ResetAllValues(); } - // Disabled annoying music + // Disable annoying lobby music if setting is enabled [HarmonyPatch(typeof(LobbyBehaviour), nameof(LobbyBehaviour.Update))] [HarmonyPostfix] private static void LobbyBehaviour_Update_Postfix() @@ -30,9 +31,10 @@ internal static class LobbyPatch [HarmonyPostfix] private static void LobbyBehaviour_RpcExtendLobbyTimer_Postfix() { - lobbyTimer += 30f; + lobbyTimer += 30f; // Add 30 seconds to lobby timer } + // Apply UI colors to settings pane buttons [HarmonyPatch(typeof(LobbyViewSettingsPane), nameof(LobbyViewSettingsPane.Awake))] [HarmonyPostfix] private static void LobbyViewSettingsPane_Awake_Postfix(LobbyViewSettingsPane __instance) @@ -42,20 +44,22 @@ internal static class LobbyPatch __instance.rolesTabButton.gameObject.SetUIColors("Icon"); } - internal static float lobbyTimer = 600f; + internal static float lobbyTimer = 600f; // 10 minute default internal static string lobbyTimerDisplay = ""; [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.Start))] [HarmonyPostfix] private static void GameStartManager_Start_Postfix(GameStartManager __instance) { - lobbyTimer = 600f; + lobbyTimer = 600f; // Reset timer + // Apply UI colors to buttons __instance.StartButton?.gameObject?.SetUIColors("Icon"); __instance.EditButton?.gameObject?.SetUIColors("Icon"); __instance.ClientViewButton?.gameObject?.SetUIColors("Icon"); __instance.HostViewButton?.gameObject?.SetUIColors("Icon"); + // Move start buttons to host panel if ping tracker not disabled if (!BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_BetterPingTracker)) { __instance.StartButton?.transform?.SetParent(__instance.HostInfoPanel?.transform); @@ -67,12 +71,13 @@ internal static class LobbyPatch [HarmonyPrefix] private static void GameStartManager_Update_Prefix(GameStartManager __instance) { + // Update lobby timer countdown lobbyTimer = Mathf.Max(0f, lobbyTimer -= Time.deltaTime); int minutes = (int)lobbyTimer / 60; int seconds = (int)lobbyTimer % 60; lobbyTimerDisplay = $"{minutes:00}:{seconds:00}"; - __instance.MinPlayers = 1; + __instance.MinPlayers = 1; // Allow starting with just 1 player } [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.Update))] @@ -81,20 +86,25 @@ internal static class LobbyPatch { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_CancelStartingGame)) return; + // Hide start button for non-hosts if (!GameState.IsHost) { __instance.StartButton.gameObject.SetActive(false); return; - } + + // Custom start button behavior for hosts __instance.GameStartTextParent.SetActive(false); __instance.StartButton.gameObject.SetActive(true); + if (__instance.startState == GameStartManager.StartingStates.Countdown) { + // Show cancel button with countdown __instance.StartButton.buttonText.text = string.Format("{0}: {1}", Translator.GetString(StringNames.Cancel), (int)__instance.countDownTimer + 1); } else { + // Show normal start button __instance.StartButton.buttonText.text = Translator.GetString(StringNames.StartLabel); } } @@ -105,6 +115,7 @@ internal static class LobbyPatch { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_CancelStartingGame)) return true; + // If countdown is active, clicking cancels the start if (__instance.startState == GameStartManager.StartingStates.Countdown) { SoundManager.instance.StopSound(__instance.gameStartSound); @@ -112,6 +123,7 @@ internal static class LobbyPatch return false; } + // Shift+click starts game immediately (bypasses countdown) if (Input.GetKey(KeyCode.LeftShift)) { __instance.startState = GameStartManager.StartingStates.Countdown; @@ -122,10 +134,11 @@ internal static class LobbyPatch return true; } + // Log game start info [HarmonyPatch(typeof(GameStartManager), nameof(GameStartManager.FinallyBegin))] [HarmonyPrefix] private static void GameStartManager_FinallyBegin_Prefix(/*GameStartManager __instance*/) { Logger_.LogHeader($"Game Has Started - {Enum.GetName(typeof(MapNames), GameState.GetActiveMapId)}/{GameState.GetActiveMapId}", "GamePlayManager"); } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/Player/CosmeticsLayerPatch.cs b/src/Patches/Gameplay/Player/CosmeticsLayerPatch.cs index 671d000..267dc3f 100644 --- a/src/Patches/Gameplay/Player/CosmeticsLayerPatch.cs +++ b/src/Patches/Gameplay/Player/CosmeticsLayerPatch.cs @@ -10,13 +10,15 @@ internal static class CosmeticsLayerPatch [HarmonyPrefix] private static bool CosmeticsLayer_GetColorBlindText_Prefix(CosmeticsLayer __instance, ref string __result) { - // Skip processing if color ID is out of bounds (custom colors) + // Skip for custom colors not in vanilla palette if (__instance.bodyMatProperties.ColorId > Palette.PlayerColors.Length) return true; + // Get color name from palette string colorName = Palette.GetColorName(__instance.bodyMatProperties.ColorId); if (!string.IsNullOrEmpty(colorName)) { + // Capitalize first letter, lowercase rest, and apply color formatting __result = (char.ToUpperInvariant(colorName[0]) + colorName[1..].ToLowerInvariant()) .ToColor(Palette.PlayerColors[__instance.bodyMatProperties.ColorId]); } diff --git a/src/Patches/Gameplay/Player/PlayerControlPatch.cs b/src/Patches/Gameplay/Player/PlayerControlPatch.cs index d0d53ab..47d3140 100644 --- a/src/Patches/Gameplay/Player/PlayerControlPatch.cs +++ b/src/Patches/Gameplay/Player/PlayerControlPatch.cs @@ -1,5 +1,6 @@ using BepInEx.Unity.IL2CPP.Utils.Collections; using BetterAmongUs.Helpers; +using BetterAmongUs.Modules; using BetterAmongUs.Modules.OptionItems; using BetterAmongUs.Mono; using HarmonyLib; @@ -26,6 +27,8 @@ internal static class PlayerControlPatch private static IEnumerator CoSetFavoriteColor(PlayerControl player) { + if (!GameState.IsLobby) yield break; + // Apply player's favorite color setting if they own this character if (player.AmOwner) { diff --git a/src/Patches/Gameplay/RolePatch.cs b/src/Patches/Gameplay/RolePatch.cs index 7506523..bf56404 100644 --- a/src/Patches/Gameplay/RolePatch.cs +++ b/src/Patches/Gameplay/RolePatch.cs @@ -10,13 +10,15 @@ internal static class RolePatch [HarmonyPrefix] private static bool NoisemakerRole_NotifyOfDeath_Prefix(NoisemakerRole __instance) { + // Prevent duplicate noisemaker notifications if (__instance.Player.BetterData().RoleInfo.HasNoisemakerNotify) { return false; } + // Mark that notification has been sent __instance.Player.BetterData().RoleInfo.HasNoisemakerNotify = true; return true; } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/Ship/ShipStatusPatch.cs b/src/Patches/Gameplay/Ship/ShipStatusPatch.cs index 216fcf5..fcaa566 100644 --- a/src/Patches/Gameplay/Ship/ShipStatusPatch.cs +++ b/src/Patches/Gameplay/Ship/ShipStatusPatch.cs @@ -10,6 +10,7 @@ internal static class ShipStatusPatch [HarmonyPostfix] private static void ShipStatus_Awake_Postfix(ShipStatus __instance) { + // Calculate vent groups when ship initializes VentGroups.CalculateAllVentGroups(__instance.AllVents); } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/Ship/VentPatch.cs b/src/Patches/Gameplay/Ship/VentPatch.cs index eb67049..24af5f1 100644 --- a/src/Patches/Gameplay/Ship/VentPatch.cs +++ b/src/Patches/Gameplay/Ship/VentPatch.cs @@ -12,8 +12,10 @@ internal static class VentPatch [HarmonyPrefix] private static bool Vent_SetOutline_Prefix(Vent __instance, bool on, bool mainTarget) { + // Skip if vent color groups are disabled - use vanilla behavior if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_VentColorGroups)) return true; + // Get group color for this vent and apply outline Color color = VentGroups.GetVentGroupColor(__instance); __instance.myRend.material.SetFloat("_Outline", on ? 1f : 0f); __instance.myRend.material.SetColor("_OutlineColor", color); @@ -21,4 +23,4 @@ internal static class VentPatch return false; } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/UI/Chat/ChatNotificationPatch.cs b/src/Patches/Gameplay/UI/Chat/ChatNotificationPatch.cs index 1a04763..f515e58 100644 --- a/src/Patches/Gameplay/UI/Chat/ChatNotificationPatch.cs +++ b/src/Patches/Gameplay/UI/Chat/ChatNotificationPatch.cs @@ -13,6 +13,7 @@ internal static class ChatNotificationPatch { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_BetterPingTracker)) return; + // Reposition to top left __instance.GetComponent().DistanceFromEdge = new Vector3(-2.8f, 0.3f, -40f); __instance.transform.localScale = new Vector3(0.45f, 0.42f, 1f); } diff --git a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs index c9d3d91..ec492bb 100644 --- a/src/Patches/Gameplay/UI/Chat/ChatPatch.cs +++ b/src/Patches/Gameplay/UI/Chat/ChatPatch.cs @@ -62,7 +62,7 @@ internal static class ChatPatch [HarmonyPatch(typeof(ChatController), nameof(ChatController.Toggle))] [HarmonyPostfix] - private static void ChatController_Toggle_Postfix(/*ChatController __instance*/) + private static void ChatController_Toggle_Postfix() { // Apply chat theme when chat is opened/closed SetChatTheme(); diff --git a/src/Patches/Gameplay/UI/Chat/QuickChatPatch.cs b/src/Patches/Gameplay/UI/Chat/QuickChatPatch.cs index 15bdd8c..2a4b8c6 100644 --- a/src/Patches/Gameplay/UI/Chat/QuickChatPatch.cs +++ b/src/Patches/Gameplay/UI/Chat/QuickChatPatch.cs @@ -11,6 +11,7 @@ internal static class QuickChatPatch [HarmonyPrefix] private static void QuickChatMenu_Awake_Prefix(QuickChatMenu __instance) { + // Apply custom UI colors to close button __instance.closeButton?.gameObject?.SetUIColors("Icon"); } @@ -18,6 +19,7 @@ internal static class QuickChatPatch [HarmonyPrefix] private static void QuickChatMenuLandingPage_Initialize_Prefix(QuickChatMenuLandingPage __instance) { + // Apply custom UI colors to landing page buttons __instance.buttonTemplate?.Button?.gameObject?.SetUIColors("Icon"); __instance.favoritesButton?.Button?.gameObject?.SetUIColors("Icon"); __instance.remarksButton?.Button?.gameObject?.SetUIColors("Icon"); @@ -27,8 +29,9 @@ internal static class QuickChatPatch [HarmonyPrefix] private static void QuickChatMenuPhrasesPage_Awake_Prefix(QuickChatMenuPhrasesPage __instance) { + // Apply custom UI colors to crewmate and phrase buttons with multiple color layers __instance.crewmateButtonTemplate?.Button?.gameObject?.SetUIColors("Icon", "Background", "PlayerMask", "Skin", "Visor", "Back", "Front", "Normal", "Horse", "Seeker", "LongBoiBody", "LongHead", "LongNeck", "ForegroundNeck", "LongHands"); __instance.phraseButtonTemplate?.Button?.gameObject?.SetUIColors("Icon"); } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/UI/GameSummaryPatch.cs b/src/Patches/Gameplay/UI/GameSummaryPatch.cs index 2eb808d..15c0b3b 100644 --- a/src/Patches/Gameplay/UI/GameSummaryPatch.cs +++ b/src/Patches/Gameplay/UI/GameSummaryPatch.cs @@ -16,20 +16,24 @@ internal static class GameSummaryPatch [HarmonyPostfix] private static void EndGameManager_SetEverythingUp_Postfix(EndGameManager __instance) { + // Log game end to console LogGameEnd(); + // Create visual summary unless disabled if (!BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_EndGameSummary)) { CreateGameSummary(__instance); } } + // Log game end info to console private static void LogGameEnd() { Logger_.LogHeader($"Game Has Ended - {Enum.GetName(typeof(MapNames), GameState.GetActiveMapId)}/{GameState.GetActiveMapId}", "GamePlayManager"); Logger_.LogHeader("Game Summary Start", "GameSummary"); } + // Creates the visual game summary on screen private static void CreateGameSummary(EndGameManager endGameManager) { var summaryObject = CreateSummaryObject(endGameManager); @@ -39,9 +43,11 @@ internal static class GameSummaryPatch ConfigureSummaryText(summaryText); + // Get win condition info var (winTeam, winTag, winColor) = GetWinInfo(); Logger_.Log($"{winTeam}: {winTag}", "GameSummary"); + // Build and display summary text var summaryHeader = BuildSummaryHeader(winTeam, winTag, winColor); var playerList = BuildPlayerList(); @@ -49,6 +55,7 @@ internal static class GameSummaryPatch Logger_.LogHeader("Game Summary End", "GameSummary"); } + // Creates the text object for the summary private static GameObject CreateSummaryObject(EndGameManager endGameManager) { var summaryObject = UnityEngine.Object.Instantiate( @@ -59,6 +66,7 @@ internal static class GameSummaryPatch summaryObject.name = "SummaryObj (TMP)"; summaryObject.transform.SetSiblingIndex(0); + // Position in top-left corner var camera = HudManager.InstanceExists ? HudManager.Instance.GetComponentInChildren() : Camera.main; @@ -75,6 +83,7 @@ internal static class GameSummaryPatch return summaryObject; } + // Sets text properties for the summary private static void ConfigureSummaryText(TextMeshPro text) { text.autoSizeTextContainer = false; @@ -84,59 +93,66 @@ internal static class GameSummaryPatch text.color = Color.white; } + // Determines which team won and how private static (string Team, string Tag, string Color) GetWinInfo() { return EndGameResult.CachedGameOverReason switch { + // Classic mode wins GameOverReason.CrewmatesByTask => ( Translator.GetString(StringNames.Crewmates), Translator.GetString("Game.Summary.Result.TasksCompletion"), - "#8cffff" + Colors.CrewmateBlue.ColorToHex() ), GameOverReason.CrewmatesByVote => ( Translator.GetString(StringNames.Crewmates), Translator.GetString("Game.Summary.Result.ImpostersVotedOut"), - "#8cffff" + Colors.CrewmateBlue.ColorToHex() ), GameOverReason.ImpostorDisconnect => ( Translator.GetString(StringNames.Crewmates), Translator.GetString("Game.Summary.Result.ImpostorsDisconnected"), - "#8cffff" + Colors.CrewmateBlue.ColorToHex() ), GameOverReason.ImpostorsByKill => ( Translator.GetString(StringNames.ImpostorsCategory), Translator.GetString("Game.Summary.Result.CrewOutnumbered"), - "#f00202" + Colors.ImpostorRed.ColorToHex() ), GameOverReason.ImpostorsBySabotage => ( Translator.GetString(StringNames.ImpostorsCategory), Translator.GetString("Game.Summary.Result.Sabotage"), - "#f00202" + Colors.ImpostorRed.ColorToHex() ), GameOverReason.ImpostorsByVote => ( Translator.GetString(StringNames.ImpostorsCategory), Translator.GetString("Game.Summary.Result.CrewOutnumbered"), - "#f00202" + Colors.ImpostorRed.ColorToHex() ), GameOverReason.CrewmateDisconnect => ( Translator.GetString(StringNames.ImpostorsCategory), Translator.GetString("Game.Summary.Result.CrematesDisconnected"), - "#f00202" + Colors.ImpostorRed.ColorToHex() ), + + // Hide & Seek mode wins GameOverReason.HideAndSeek_CrewmatesByTimer => ( Translator.GetString("Game.Summary.Hiders"), Translator.GetString("Game.Summary.Result.TimeOut"), - "#8cffff" + Colors.CrewmateBlue.ColorToHex() ), GameOverReason.HideAndSeek_ImpostorsByKills => ( Translator.GetString("Game.Summary.Seekers"), Translator.GetString("Game.Summary.Result.NoSurvivors"), - "#f00202" + Colors.ImpostorRed.ColorToHex() ), + + // Fallback for unknown win conditions _ => ("Unknown", "Unknown", "#ffffff") }; } + // Builds the header text with win info private static string BuildSummaryHeader(string winTeam, string winTag, string winColor) { return $" {Translator.GetString("GameSummary")}" + @@ -144,6 +160,7 @@ internal static class GameSummaryPatch $"\n\n{Translator.GetString("Game.Summary.By")} {winTag}"; } + // Sorts players for display: disconnected first, then dead, then alive private static NetworkedPlayerInfo[] GetSortedPlayers() { return GameData.Instance.AllPlayers @@ -154,6 +171,7 @@ internal static class GameSummaryPatch .ToArray(); } + // Builds the complete player list text private static StringBuilder BuildPlayerList() { var playersData = GetSortedPlayers(); @@ -169,6 +187,7 @@ internal static class GameSummaryPatch return stringBuilder; } + // Builds a single player line with name, role info, and status private static string BuildPlayerLine(NetworkedPlayerInfo playerData) { var name = $"{playerData.BetterData().RealName}"; @@ -178,6 +197,7 @@ internal static class GameSummaryPatch return $"{name} {roleInfo} {deathReason}"; } + // Builds role info with stats (kills for impostors, tasks for crew) private static string BuildRoleInfo(NetworkedPlayerInfo playerData) { var themeColor = Utils.GetTeamHexColor(playerData.Role.TeamType); @@ -196,6 +216,7 @@ internal static class GameSummaryPatch return $"({roleName}) → {theme($"{Translator.GetString("Tasks")}: {completedTasks}/{totalTasks}")}"; } + // Builds player status (DC/Dead/Alive) private static string BuildDeathReason(NetworkedPlayerInfo playerData) { if (playerData.Disconnected) diff --git a/src/Patches/Gameplay/UI/IntroCutscenePatch.cs b/src/Patches/Gameplay/UI/IntroCutscenePatch.cs index b83766d..58c8f3c 100644 --- a/src/Patches/Gameplay/UI/IntroCutscenePatch.cs +++ b/src/Patches/Gameplay/UI/IntroCutscenePatch.cs @@ -13,20 +13,28 @@ internal static class IntroCutscenePatch [HarmonyPostfix] private static void IntroCutscene_CoBegin_Postfix(IntroCutscene __instance) { + // Start coroutine to customize intro after text appears __instance.StartCoroutine(CoWaitForShowRole(__instance)); } + // Waits for intro text to appear, then customizes colors to match player's role private static IEnumerator CoWaitForShowRole(IntroCutscene __instance) { + // Wait until "You are" text is active while (!__instance.YouAreText.gameObject.active) { yield return null; } + // Get role color for local player var introCutscene = __instance; Color RoleColor = Utils.HexToColor32(PlayerControl.LocalPlayer.Data.RoleType.GetRoleHex()); + + // Hide vanilla team text introCutscene.ImpostorText.gameObject.SetActive(false); introCutscene.TeamTitle.gameObject.SetActive(false); + + // Apply role color to all intro elements introCutscene.BackgroundBar.material.color = RoleColor; introCutscene.BackgroundBar.transform.SetLocalZ(-15); introCutscene.transform.Find("BackgroundLayer").transform.SetLocalZ(-16); diff --git a/src/Patches/Gameplay/UI/KillOverlayPatch.cs b/src/Patches/Gameplay/UI/KillOverlayPatch.cs index 5e79ec9..d851fbd 100644 --- a/src/Patches/Gameplay/UI/KillOverlayPatch.cs +++ b/src/Patches/Gameplay/UI/KillOverlayPatch.cs @@ -11,6 +11,7 @@ internal static class KillOverlayPatch [HarmonyPrefix] private static bool KillOverlay_ShowKillAnimation_Prefix() { + // Don't show kill animation if local player is dead if (!PlayerControl.LocalPlayer.IsAlive()) { return false; @@ -18,4 +19,4 @@ internal static class KillOverlayPatch return true; } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/UI/MeetingHudPatch.cs b/src/Patches/Gameplay/UI/MeetingHudPatch.cs index 86a3230..0d57f00 100644 --- a/src/Patches/Gameplay/UI/MeetingHudPatch.cs +++ b/src/Patches/Gameplay/UI/MeetingHudPatch.cs @@ -15,6 +15,7 @@ internal static class MeetingHudPatch [HarmonyPostfix] private static void MeetingHud_Start_Postfix(MeetingHud __instance) { + // Add meeting info display to each player state foreach (var pva in __instance.playerStates) { var target = Utils.PlayerFromPlayerId(pva.TargetPlayerId); @@ -23,7 +24,7 @@ internal static class MeetingHudPatch if (!GameState.IsOnlineGame) return; - // Add host icon to meeting hud + // Add host icon to meeting UI __instance.ProceedButton.gameObject.transform.localPosition = new(-2.5f, 2.2f, 0); __instance.ProceedButton.gameObject.GetComponent().enabled = false; __instance.ProceedButton.GetComponent().enabled = false; @@ -36,6 +37,7 @@ internal static class MeetingHudPatch Logger_.LogHeader("Meeting Has Started"); } + // Updates host icon with current host info internal static void UpdateHostIcon() { if (MeetingHud.Instance == null) return; @@ -46,7 +48,7 @@ internal static class MeetingHudPatch internal static float timeOpen = 0f; - // Set player meeting info + // Track how long meeting has been open [HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.Update))] [HarmonyPostfix] private static void MeetingHud_Update_Postfix(MeetingHud __instance) @@ -61,6 +63,7 @@ internal static class MeetingHudPatch timeOpen = 0f; Logger_.LogHeader("Meeting Has Ended"); + // Clear chat when meeting ends if gameplay chat is enabled if (BAUPlugin.ChatInGameplay.Value && !GameState.IsFreePlay && PlayerControl.LocalPlayer.IsAlive()) { ChatPatch.ClearPlayerChats(); diff --git a/src/Patches/Gameplay/UI/MiniMapBehaviourPatch.cs b/src/Patches/Gameplay/UI/MiniMapBehaviourPatch.cs index 55790c2..a308cce 100644 --- a/src/Patches/Gameplay/UI/MiniMapBehaviourPatch.cs +++ b/src/Patches/Gameplay/UI/MiniMapBehaviourPatch.cs @@ -10,6 +10,7 @@ namespace BetterAmongUs.Patches.Gameplay.UI; [HarmonyPatch] internal static class MiniMapBehaviourPatch { + // Layer offsets for different icon types (prevents overlapping) private const float VentLayerOffset = -0.1f; private const float VentArrowLayerOffset = -0.1f; // VentLayerOffset + VentArrowLayerOffset = -0.2 private const float UsableLayerOffset = -0.3f; @@ -17,35 +18,39 @@ internal static class MiniMapBehaviourPatch [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.ShowNormalMap))] [HarmonyPostfix] private static void MapBehaviour_ShowNormalMap_Postfix(MapBehaviour __instance) - => __instance.ColorControl.SetColor(new Color(0.05f, 0.6f, 1f, 1f)); + => __instance.ColorControl.SetColor(new Color(0.05f, 0.6f, 1f, 1f)); // Blue tint for normal map [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.ShowDetectiveMap))] [HarmonyPostfix] private static void MapBehaviour_ShowDetectiveMap_Postfix(MapBehaviour __instance) - => __instance.ColorControl.SetColor(new Color(0.05f, 0.6f, 1f, 1f)); + => __instance.ColorControl.SetColor(new Color(0.05f, 0.6f, 1f, 1f)); // Blue tint for detective map [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.ShowSabotageMap))] [HarmonyPostfix] private static void MapBehaviour_ShowSabotageMap_Postfix(MapBehaviour __instance) - => __instance.ColorControl.SetColor(new Color(1f, 0.3f, 0f, 1f)); + => __instance.ColorControl.SetColor(new Color(1f, 0.3f, 0f, 1f)); // Orange tint for sabotage map [HarmonyPatch(typeof(MapCountOverlay), nameof(MapCountOverlay.OnEnable))] [HarmonyPostfix] private static void MapCountOverlay_OnEnable_Postfix(MapCountOverlay __instance) - => __instance.BackgroundColor.SetColor(PlayerTask.PlayerHasTaskOfType(PlayerControl.LocalPlayer) ? Palette.DisabledGrey : new Color(0.2f, 0.5f, 0f, 1f)); + // Green background normally, gray if comms sabotaged + => __instance.BackgroundColor.SetColor(PlayerTask.PlayerHasTaskOfType(PlayerControl.LocalPlayer) ? Palette.DisabledGrey : new Color(0.2f, 0.5f, 0f, 1f)); [HarmonyPatch(typeof(MapCountOverlay), nameof(MapCountOverlay.Update))] [HarmonyPrefix] private static void MapCountOverlay_Update_Prefix(MapCountOverlay __instance) { + // Handle comms sabotage effect on map if (PlayerTask.PlayerHasTaskOfType(PlayerControl.LocalPlayer)) { + // Comms sabotaged - disable map __instance.isSab = true; __instance.BackgroundColor.SetColor(Palette.DisabledGrey); __instance.SabotageText.gameObject.SetActive(true); } - else if (!PlayerTask.PlayerHasTaskOfType(PlayerControl.LocalPlayer)) + else { + // Comms working - normal map __instance.isSab = false; __instance.BackgroundColor.SetColor(new Color(0.2f, 0.5f, 0f, 1f)); __instance.SabotageText.gameObject.SetActive(false); @@ -55,9 +60,9 @@ internal static class MiniMapBehaviourPatch [HarmonyPatch(typeof(MapConsole), nameof(MapConsole.Use))] [HarmonyPostfix] private static void MapConsole_ShowCountOverlay_Postfix() - => MapBehaviour.Instance.ColorControl.SetColor(new Color(0.2f, 0.5f, 0f, 1f)); + => MapBehaviour.Instance.ColorControl.SetColor(new Color(0.2f, 0.5f, 0f, 1f)); // Green tint for admin map - private static Transform? _icons; + private static Transform? _icons; // Container for all custom map icons [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.Show))] [HarmonyPostfix] @@ -65,14 +70,17 @@ internal static class MiniMapBehaviourPatch { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_MinimapIcons)) return; + // Make infected overlay buttons semi-transparent and smaller foreach (var button in __instance.infectedOverlay.allButtons) { button.spriteRenderer.color = new Color(1f, 1f, 1f, 0.8f); button.transform.localScale = new Vector3(0.6f, 0.6f, 1f); } + // Create icons if they don't exist yet if (_icons == null) { + // Create container for all icons var icons = new GameObject("Icons") { layer = LayerMask.NameToLayer("UI") @@ -82,21 +90,25 @@ internal static class MiniMapBehaviourPatch icons.transform.localScale = Vector3.one; _icons = icons.transform; + // Add vent icons foreach (var vent in BAUPlugin.AllVents) { CreateVentIcon(vent); } + // Add map console icons foreach (var map in UnityEngine.Object.FindObjectsOfType()) { CreateUsableIcon(map.Cast()); } + // Add system console icons foreach (var system in UnityEngine.Object.FindObjectsOfType()) { CreateSystemConsoleIcon(system); } + // Add zipline icons foreach (var zipline in UnityEngine.Object.FindObjectsOfType()) { var icon = CreateIcon(zipline.image.sprite, "SystemIcon"); @@ -105,16 +117,20 @@ internal static class MiniMapBehaviourPatch SetPosFromShip(zipline.transform.position, icon.transform, new Vector3(0f, 0.16f, UsableLayerOffset)); } + // Add task console icons for critical sabotages foreach (var console in UnityEngine.Object.FindObjectsOfType()) { foreach (var taskType in console.TaskTypes) { + // Check if this console is for critical sabotage tasks if (taskType is TaskTypes.FixLights or TaskTypes.FixComms or TaskTypes.RestoreOxy or TaskTypes.ResetReactor or TaskTypes.ResetSeismic) { var icon = CreateIcon(console.Image.sprite, "ConsoleIcon"); icon.color = Color.white * 0.7f; icon.transform.localScale = Vector3.one * 0.5f; SetPosFromShip(console.transform.position, icon.transform, new Vector3(0f, 0f, UsableLayerOffset)); + + // Add animation when player has this task var animatedMapIcon = icon.gameObject.AddComponent(); animatedMapIcon.ShouldAnimate += () => { @@ -127,6 +143,7 @@ internal static class MiniMapBehaviourPatch } } + // Creates a vent icon with connection arrows to neighboring vents private static void CreateVentIcon(Vent vent) { var icon = CreateIcon(Utils.LoadSprite("BetterAmongUs.Resources.Images.Icons.Vent.png", 380), "VentIcon"); @@ -135,11 +152,13 @@ internal static class MiniMapBehaviourPatch SetPosFromShip(vent.transform.position, icon.transform, new Vector3(0f, 0f, VentLayerOffset)); + // Get connected vents Vent[] nearbyVents = [vent.Left, vent.Right, vent.Center]; float maxSpreadShift = 0.5f; float minSpreadShift = 0.2f; float closeDistance = 10f; + // Create arrows to each connected vent for (int i = 0; i < nearbyVents.Length; i++) { Vent neighborVent = nearbyVents[i]; @@ -149,9 +168,11 @@ internal static class MiniMapBehaviourPatch arrowIcon.color = VentGroups.GetVentGroupColor(neighborVent); arrowIcon.transform.SetParent(icon.transform); + // Calculate arrow position and rotation based on neighbor location Vector3 directionToNeighbor = neighborVent.transform.position - vent.transform.position; float distanceToNeighbor = directionToNeighbor.magnitude; + // Adjust arrow spread based on distance float spreadShift; if (distanceToNeighbor < closeDistance) { @@ -168,6 +189,7 @@ internal static class MiniMapBehaviourPatch SetPosFromShip(arrowOffset, arrowIcon.transform, new Vector3(0f, 0f, VentArrowLayerOffset)); + // Rotate arrow to point toward neighbor Vector3 ventMapPos = vent.transform.position / ShipStatus.Instance.MapScale; ventMapPos.x *= Mathf.Sign(ShipStatus.Instance.transform.localScale.x); @@ -182,6 +204,7 @@ internal static class MiniMapBehaviourPatch } } + // Creates icon for system consoles private static void CreateSystemConsoleIcon(SystemConsole systemConsole) { if (systemConsole.UseIcon is not ImageNames.UseButton) @@ -197,8 +220,10 @@ internal static class MiniMapBehaviourPatch } } + // Creates icon for usables private static void CreateUsableIcon(IUsable usable) { + // Special handling for emergency buttons if (usable.TryCast(out var systemConsole)) { if (systemConsole.MinigamePrefab.name == "EmergencyMinigame") @@ -213,11 +238,12 @@ internal static class MiniMapBehaviourPatch { if (systemConsole.UseIcon is ImageNames.UseButton) { - return; + return; // Skip default use button icons } } } + // Create icon based on use button settings if (HudManager.Instance.UseButton.fastUseSettings.TryGetValue(usable.UseIcon, out var settings)) { var icon = CreateIcon(settings.Image, "UsableIcon"); @@ -227,6 +253,7 @@ internal static class MiniMapBehaviourPatch } } + // Helper to create a new icon sprite renderer private static SpriteRenderer CreateIcon(Sprite sprite, string name = "Icon") { var go = new GameObject(name) @@ -240,6 +267,7 @@ internal static class MiniMapBehaviourPatch return spriteRenderer; } + // Helper to position icons on map relative to ship position private static void SetPosFromShip(Vector3 shipPos, Transform mapTransform, Vector3? offset = null) { offset ??= Vector3.zero; @@ -249,4 +277,4 @@ internal static class MiniMapBehaviourPatch vector.z = -1f; mapTransform.transform.localPosition = new Vector3(vector.x + offset.Value.x, vector.y + offset.Value.y, offset.Value.z); } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/UI/PlayerTabPatch.cs b/src/Patches/Gameplay/UI/PlayerTabPatch.cs index 31f44a6..c1f17da 100644 --- a/src/Patches/Gameplay/UI/PlayerTabPatch.cs +++ b/src/Patches/Gameplay/UI/PlayerTabPatch.cs @@ -11,13 +11,14 @@ namespace BetterAmongUs.Patches.Gameplay.UI; [HarmonyPatch] internal static class PlayerTabPatch { - private static List presetButtons = []; - private static float cooldown = 0f; + private static List presetButtons = []; // Outfit preset buttons + private static float cooldown = 0f; // Button click cooldown [HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.OnEnable))] [HarmonyPrefix] private static void PlayerTab_OnEnable_Prefix(PlayerTab __instance) { + // Clean up old preset buttons foreach (var button in presetButtons.ToArray()) { if (button == null) continue; @@ -25,23 +26,28 @@ internal static class PlayerTabPatch } presetButtons.Clear(); + // Create preset buttons (0 = Among Us preset, 1-5 = custom presets) for (int i = 0; i <= 5; i++) { int currentI = i; var name = currentI == 0 ? "Among Us Preset" : $"Preset {i}"; var button = __instance.CreateButton(name, new Vector3(2.5f, 1.55f - currentI * 0.45f, 0f), () => { + // Ignore if cooldown active or same preset selected if (cooldown > 0f || BetterDataManager.BetterDataFile.SelectedOutfitPreset == currentI) return; cooldown = 0.5f; + // Update selected preset BetterDataManager.BetterDataFile.SelectedOutfitPreset = currentI; + // Reset all button hover states foreach (var button in presetButtons) { if (button == null) continue; button.SetPassiveButtonHoverStateInactive(); } + // Load and apply outfit from preset var data = OutfitData.GetOutfitData(currentI); data.Load(() => { @@ -67,31 +73,37 @@ internal static class PlayerTabPatch { _favoriteIcons.Clear(); + // Add favorite functionality to color chips for (int i = 0; i < __instance.ColorChips.Count; i++) { var index = i; var colorChip = __instance.ColorChips[i]; + + // Override click behavior colorChip.Button.OnClick = new(); colorChip.Button.OnClick.AddListener((Action)(() => { + // Shift+Click = toggle favorite if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) { if (BAUPlugin.FavoriteColor.Value == index) { - BAUPlugin.FavoriteColor.Value = -1; + BAUPlugin.FavoriteColor.Value = -1; // Remove favorite } else { - BAUPlugin.FavoriteColor.Value = index; + BAUPlugin.FavoriteColor.Value = index; // Set favorite } UpdateFavorite(); return; } + // Normal click = equip color __instance.ClickEquip(); })); + // Add favorite star indicator var checkBox = colorChip.PlayerEquippedForeground.transform.Find("CheckMark").GetComponentInChildren(); var favoriteIcon = UnityEngine.Object.Instantiate(checkBox, colorChip.transform); favoriteIcon.color = Color.yellow; @@ -102,6 +114,7 @@ internal static class PlayerTabPatch UpdateFavorite(); } + // Update favorite star visibility private static void UpdateFavorite() { for (int i = 0; i < _favoriteIcons.Count; i++) @@ -111,6 +124,7 @@ internal static class PlayerTabPatch } } + // Apply outfit to local player private static bool LoadPlayerOutfit(OutfitData data) { var player = PlayerControl.LocalPlayer; @@ -127,6 +141,7 @@ internal static class PlayerTabPatch return false; } + // Helper to create preset buttons private static PassiveButton CreateButton(this PlayerTab __instance, string name, Vector3 pos, Action callback) { var button = UnityEngine.Object.Instantiate(MainMenuManagerPatch.ButtonPrefab, __instance.transform); @@ -146,6 +161,7 @@ internal static class PlayerTabPatch [HarmonyPrefix] private static void PlayerTab_Updatee_Postfix(PlayerTab __instance) { + // Handle cooldown if (cooldown > 0f) { cooldown -= Time.deltaTime; @@ -155,14 +171,15 @@ internal static class PlayerTabPatch cooldown = 0f; } + // Update preset button hover states for (int i = 0; i < presetButtons.Count; i++) { PassiveButton? button = presetButtons[i]; if (button == null) continue; if (i == BetterDataManager.BetterDataFile.SelectedOutfitPreset) { - button.SetPassiveButtonHoverStateActive(); + button.SetPassiveButtonHoverStateActive(); // Highlight selected preset } } } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/UI/RoomTrackerPatch.cs b/src/Patches/Gameplay/UI/RoomTrackerPatch.cs index 489b058..2c0c1e9 100644 --- a/src/Patches/Gameplay/UI/RoomTrackerPatch.cs +++ b/src/Patches/Gameplay/UI/RoomTrackerPatch.cs @@ -10,6 +10,7 @@ internal static class RoomTrackerPatch [HarmonyPostfix] private static void RoomTracker_Awake_Postfix(RoomTracker __instance) { + // Fix aspect positioning var originalParent = __instance.transform.parent; var holder = new GameObject("RoomTrackerHolder"); diff --git a/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs index 2f49306..84be5b4 100644 --- a/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs +++ b/src/Patches/Gameplay/UI/Settings/GameSettingsPatch.cs @@ -9,7 +9,8 @@ using UnityEngine; namespace BetterAmongUs.Patches.Gameplay.UI.Settings; -class BetterGameSettings +// Custom game settings definitions +internal sealed class BetterGameSettings { internal static OptionStringItem? WhenCheating; internal static OptionCheckboxItem? InvalidFriendCode; @@ -30,7 +31,8 @@ class BetterGameSettings internal static OptionCheckboxItem? DisableSabotages; } -class BetterGameSettingsTemp +// Temporary settings for Hide & Seek impostor selection +internal sealed class BetterGameSettingsTemp { internal static OptionPlayerItem? HideAndSeekImp2; internal static OptionPlayerItem? HideAndSeekImp3; @@ -43,19 +45,22 @@ internal static class GameSettingsPatch { internal static OptionTab? BetterSettingsTab; + // Creates all custom game settings and organizes them in tabs internal static void SetupSettings(bool IsPreload = false) { - // Use 1800 next ID + // Note: Use 1800 next ID + // Create main settings tab BetterSettingsTab = OptionTab.Create(3, "BetterSetting", "BetterSetting.Description", Color.green); OptionHeaderItem.Create(BetterSettingsTab, "BetterSetting.MainHeader.System"); OptionPresetItem.Create(); - // Anti-Cheat Settings + // Anti-Cheat Settings section { OptionHeaderItem.Create(BetterSettingsTab, "BetterSetting.MainHeader.AntiCheat"); + // Host-only anti-cheat settings if (IsPreload || GameState.IsHost) { OptionTitleItem.Create(BetterSettingsTab, "BetterSetting.TextHeader.HostOnly"); @@ -69,6 +74,7 @@ internal static class GameSettingsPatch BetterGameSettings.UseBanWordListOnlyLobby = OptionCheckboxItem.Create(1400, BetterSettingsTab, "BetterSetting.Setting.UseBanWordListOnlyLobby", true, BetterGameSettings.UseBanWordList); } + // General detection settings (visible to all) OptionTitleItem.Create(BetterSettingsTab, "BetterSetting.TextHeader.Detections"); BetterGameSettings.CensorDetectionReason = OptionCheckboxItem.Create(1300, BetterSettingsTab, "BetterSetting.Setting.CensorDetectionReason", false); BetterGameSettings.DetectedLevelAbove = OptionIntItem.Create(600, BetterSettingsTab, "BetterSetting.Setting.DetectedLevelAbove", (100, 10000, 5), 500, ("Lv ", "")); @@ -77,6 +83,7 @@ internal static class GameSettingsPatch BetterGameSettings.DetectInvalidRPCs = OptionCheckboxItem.Create(800, BetterSettingsTab, "BetterSetting.Setting.DetectInvalidRPCs", true); } + // Role algorithm settings if (IsPreload || GameState.IsHost) { OptionHeaderItem.Create(BetterSettingsTab, "BetterSetting.MainHeader.RoleAlgorithm"); @@ -84,31 +91,38 @@ internal static class GameSettingsPatch BetterGameSettings.DesyncRoles = OptionCheckboxItem.Create(1200, BetterSettingsTab, "BetterSetting.Setting.DesyncRoles", true); } - // Gameplay Settings + // Gameplay Settings section { + // Show only for hosts in private lobbies if (IsPreload || GameState.IsHost && GameState.IsPrivateOnlyLobby) { + // Normal game mode settings if (IsPreload || !GameState.IsHideNSeek) { OptionHeaderItem.Create(BetterSettingsTab, "BetterSetting.MainHeader.Gameplay"); BetterGameSettings.DisableSabotages = OptionCheckboxItem.Create(1500, BetterSettingsTab, "BetterSetting.Setting.DisableSabotages", false); BetterGameSettings.RemovePetOnDeath = OptionCheckboxItem.Create(1600, BetterSettingsTab, "BetterSetting.Setting.RemovePetOnDeath", false); } + // Hide & Seek specific settings with dynamic impostor selection else if (IsPreload || GameState.IsHideNSeek) { OptionHeaderItem.Create(BetterSettingsTab, "BetterSetting.MainHeader.HideNSeek"); OptionTitleItem.Create(BetterSettingsTab, $"{Translator.GetString(StringNames.ImpostorsCategory)}"); BetterGameSettings.HideAndSeekImpNum = OptionIntItem.Create(1000, BetterSettingsTab, "BetterSetting.Setting.HideAndSeekImpNum", (1, 5, 1), 1); + // Create player selection dropdowns that appear based on impostor count BetterGameSettingsTemp.HideAndSeekImp2 = OptionPlayerItem.Create(0, BetterSettingsTab, "BetterSetting.TempSetting.HideAndSeekImpNum", BetterGameSettings.HideAndSeekImpNum); BetterGameSettingsTemp.HideAndSeekImp2.ShowCondition = () => BetterGameSettings.HideAndSeekImpNum.GetValue() >= 2; + BetterGameSettingsTemp.HideAndSeekImp3 = OptionPlayerItem.Create(1, BetterSettingsTab, "BetterSetting.TempSetting.HideAndSeekImpNum", BetterGameSettings.HideAndSeekImpNum); BetterGameSettingsTemp.HideAndSeekImp3.ShowCondition = () => BetterGameSettings.HideAndSeekImpNum.GetValue() >= 3 && BetterGameSettingsTemp.HideAndSeekImp2.GetValue() != -1; + BetterGameSettingsTemp.HideAndSeekImp4 = OptionPlayerItem.Create(2, BetterSettingsTab, "BetterSetting.TempSetting.HideAndSeekImpNum", BetterGameSettings.HideAndSeekImpNum); BetterGameSettingsTemp.HideAndSeekImp4.ShowCondition = () => BetterGameSettings.HideAndSeekImpNum.GetValue() >= 4 && BetterGameSettingsTemp.HideAndSeekImp2.GetValue() != -1 && BetterGameSettingsTemp.HideAndSeekImp3.GetValue() != -1; + BetterGameSettingsTemp.HideAndSeekImp5 = OptionPlayerItem.Create(3, BetterSettingsTab, "BetterSetting.TempSetting.HideAndSeekImpNum", BetterGameSettings.HideAndSeekImpNum); BetterGameSettingsTemp.HideAndSeekImp5.ShowCondition = () => BetterGameSettings.HideAndSeekImpNum.GetValue() >= 5 && BetterGameSettingsTemp.HideAndSeekImp2.GetValue() != -1 && @@ -121,6 +135,7 @@ internal static class GameSettingsPatch BetterSettingsTab.UpdateVisuals(); } + // Initialize settings [HarmonyPatch(typeof(GameSettingMenu), nameof(GameSettingMenu.Start))] [HarmonyPostfix] private static void GameSettingMenu_Start_Postfix(GameSettingMenu __instance) @@ -129,6 +144,7 @@ internal static class GameSettingsPatch SetupSettings(); + // Adjust menu layout __instance.gameObject.transform.SetLocalY(-0.1f); GameObject PanelSprite = __instance.gameObject.transform.Find("PanelSprite").gameObject; if (PanelSprite != null) @@ -137,18 +153,21 @@ internal static class GameSettingsPatch PanelSprite.transform.localScale = new Vector3(PanelSprite.transform.localScale.x, 0.625f); } + // Clear hover effects __instance.GamePresetsButton.OnMouseOver.RemoveAllListeners(); __instance.GameSettingsButton.OnMouseOver.RemoveAllListeners(); __instance.RoleSettingsButton.OnMouseOver.RemoveAllListeners(); - + // Configure tab behavior based on game mode and host status BetterSettingsTab.TabButton.transform.localPosition = BetterSettingsTab.TabButton.transform.localPosition - new Vector3(0f, 1.265f, 0f); + if (!GameState.IsHideNSeek && GameState.IsHost) { - __instance.ChangeTab(1, false); + __instance.ChangeTab(1, false); // Show game settings tab for hosts } else if (GameState.IsHost) { + // Hide & Seek host: disable role button but keep visible __instance.RoleSettingsButton.gameObject.SetActive(true); __instance.RoleSettingsButton.GetComponent().enabled = false; __instance.RoleSettingsButton.inactiveSprites.GetComponent().color = new(0.5f, 0.5f, 0.5f, 1f); @@ -156,27 +175,30 @@ internal static class GameSettingsPatch } else { + // Non-hosts: disable all vanilla tabs and show custom tab __instance.GamePresetsButton.GetComponent().enabled = false; __instance.GameSettingsButton.GetComponent().enabled = false; __instance.RoleSettingsButton.GetComponent().enabled = false; __instance.GamePresetsButton.inactiveSprites.GetComponent().color = new(0.5f, 0.5f, 0.5f, 1f); __instance.GameSettingsButton.inactiveSprites.GetComponent().color = new(0.5f, 0.5f, 0.5f, 1f); __instance.RoleSettingsButton.inactiveSprites.GetComponent().color = new(0.5f, 0.5f, 0.5f, 1f); - __instance.ChangeTab(3, false); + __instance.ChangeTab(3, false); // Switch to custom tab } } + // Handle tab switching to show/hide custom settings tab [HarmonyPatch(typeof(GameSettingMenu), nameof(GameSettingMenu.ChangeTab))] [HarmonyPrefix] private static void GameSettingMenu_ChangeTab_Prefix(GameSettingMenu __instance, [HarmonyArgument(0)] int tabNum, [HarmonyArgument(1)] bool previewOnly) { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_AllGameOptions)) return; - if (BetterSettingsTab == null) return; + // Hide custom tab when switching away BetterSettingsTab.AUTab?.gameObject?.SetActive(false); BetterSettingsTab.TabButton?.SelectButton(false); + // Show custom tab when selected (tab 3) if (previewOnly && Controller.currentTouchType == Controller.TouchType.Joystick || !previewOnly) { switch (tabNum) @@ -190,12 +212,14 @@ internal static class GameSettingsPatch } } + // Prevent vanilla settings from being created in custom tab [HarmonyPatch(typeof(GameOptionsMenu), nameof(GameOptionsMenu.CreateSettings))] [HarmonyPrefix] private static bool GameOptionsMenu_CreateSettings_Prefix(GameOptionsMenu __instance) { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_AllGameOptions)) return true; + // Skip creation if this is our custom tab if (__instance == BetterSettingsTab.AUTab) { return false; @@ -204,12 +228,13 @@ internal static class GameSettingsPatch return true; } + // Make OptionsConsole usable by anyone (not just host) [HarmonyPatch(typeof(OptionsConsole), nameof(OptionsConsole.CanUse))] [HarmonyPrefix] private static void OptionsConsole_CanUse_Prefix(OptionsConsole __instance) { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_AllGameOptions)) return; - __instance.HostOnly = false; + __instance.HostOnly = false; // Allow non-hosts to use settings console } } \ No newline at end of file diff --git a/src/Patches/Gameplay/UI/Settings/NumberOptionPatch.cs b/src/Patches/Gameplay/UI/Settings/NumberOptionPatch.cs index 07b3ecd..eeeea30 100644 --- a/src/Patches/Gameplay/UI/Settings/NumberOptionPatch.cs +++ b/src/Patches/Gameplay/UI/Settings/NumberOptionPatch.cs @@ -13,23 +13,28 @@ internal static class NumberOptionPatch { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_AllGameOptions)) return true; + // Determine multiplier based on modifier keys int times = 1; if (Input.GetKey(KeyCode.LeftShift)) - times = 5; + times = 5; // Shift = 5x if (Input.GetKey(KeyCode.LeftControl)) - times = 10; + times = 10; // Control = 10x + // Increase value with bounds checking if (__instance.Value + __instance.Increment * times > __instance.ValidRange.max) { - __instance.Value = __instance.ValidRange.max; + __instance.Value = __instance.ValidRange.max; // Cap at max } else { __instance.Value = __instance.ValidRange.Clamp(__instance.Value + __instance.Increment * times); } + + // Update UI and invoke events __instance.UpdateValue(); __instance.OnValueChanged.Invoke(__instance); __instance.AdjustButtonsActiveState(); + return false; } @@ -39,23 +44,28 @@ internal static class NumberOptionPatch { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_AllGameOptions)) return true; + // Determine multiplier based on modifier keys int times = 1; if (Input.GetKey(KeyCode.LeftShift)) - times = 5; + times = 5; // Shift = 5x if (Input.GetKey(KeyCode.LeftControl)) - times = 10; + times = 10; // Control = 10x + // Decrease value with bounds checking if (__instance.Value - __instance.Increment * times < __instance.ValidRange.min) { - __instance.Value = __instance.ValidRange.min; + __instance.Value = __instance.ValidRange.min; // Cap at min } else { __instance.Value = __instance.ValidRange.Clamp(__instance.Value - __instance.Increment * times); } + + // Update UI and invoke events __instance.UpdateValue(); __instance.OnValueChanged.Invoke(__instance); __instance.AdjustButtonsActiveState(); + return false; } -} +} \ No newline at end of file diff --git a/src/Patches/Gameplay/ZoomPatch.cs b/src/Patches/Gameplay/ZoomPatch.cs index f37069f..32bb71b 100644 --- a/src/Patches/Gameplay/ZoomPatch.cs +++ b/src/Patches/Gameplay/ZoomPatch.cs @@ -9,47 +9,56 @@ namespace BetterAmongUs.Patches.Gameplay; [HarmonyPatch] internal class ZoomPatch { - private static bool _wasZooming = false; - private static float _lastOrthographicSize = 0f; + private static bool _wasZooming = false; // Track if user was zooming + private static float _lastOrthographicSize = 0f; // Track last camera size [HarmonyPatch(typeof(HudManager), nameof(HudManager.Update))] [HarmonyPostfix] private static void HudManager_Update_Postfix() { + // Determine if zooming is allowed: + // - Player can move AND + // - Not Guardian Angel AND + // - Either not in gameplay OR player is dead bool canZoom = GameState.IsCanMove && !PlayerControl.LocalPlayer.Is(RoleTypes.GuardianAngel) && (!GameState.IsInGamePlay || !PlayerControl.LocalPlayer.IsAlive()); + // Should we reset zoom (when player was zooming but no longer can) bool shouldReset = !canZoom && _wasZooming; _lastOrthographicSize = Camera.main.orthographicSize; if (shouldReset) { + // Reset to default zoom SetZoomSize(reset: true); _wasZooming = false; } else if (canZoom) { + // Handle scroll wheel input if (Input.mouseScrollDelta.y > 0 && Camera.main.orthographicSize > 3.0f) { _wasZooming = true; - SetZoomSize(zoomIn: true); + SetZoomSize(zoomIn: true); // Scroll up = zoom in } else if (Input.mouseScrollDelta.y < 0 && (GameState.IsDead || GameState.IsFreePlay || GameState.IsLobby) && Camera.main.orthographicSize < 18.0f) { _wasZooming = true; - SetZoomSize(zoomOut: true); + SetZoomSize(zoomOut: true); // Scroll down = zoom out } } } + // Handles zoom in/out and reset functionality private static void SetZoomSize(bool zoomIn = false, bool zoomOut = false, bool reset = false) { if (reset) { + // Reset to default zoom (3.0) Camera.main.orthographicSize = 3.0f; HudManager.Instance.UICamera.orthographicSize = 3.0f; HudManager.Instance.Chat.transform.localScale = Vector3.one; @@ -59,15 +68,18 @@ internal class ZoomPatch } else if (zoomIn || zoomOut) { + // Zoom in (multiply by 1/1.5 = 0.666) or zoom out (multiply by 1.5) float size = zoomIn ? 1 / 1.5f : 1.5f; Camera.main.orthographicSize *= size; HudManager.Instance.UICamera.orthographicSize *= size; } + // Show/hide shadow quad based on zoom level and if player is alive HudManager.Instance?.ShadowQuad?.gameObject?.SetActive( (reset || Camera.main.orthographicSize == 3.0f) && PlayerControl.LocalPlayer.IsAlive()); + // Trigger resolution change event if camera size changed if (Camera.main.orthographicSize != _lastOrthographicSize) { ResolutionManager.ResolutionChanged.Invoke( @@ -76,4 +88,4 @@ internal class ZoomPatch Screen.fullScreen); } } -} +} \ No newline at end of file diff --git a/src/Patches/Unity/StandaloneSteamPatch.cs b/src/Patches/Unity/StandaloneSteamPatch.cs index 6dadd5a..7d425e9 100644 --- a/src/Patches/Unity/StandaloneSteamPatch.cs +++ b/src/Patches/Unity/StandaloneSteamPatch.cs @@ -6,14 +6,17 @@ namespace BetterAmongUs.Patches.Unity; [HarmonyPatch] internal static class StandaloneSteamPatch { + // Try to get the SteamAPI type from the game assembly private static readonly Type? _type = Type.GetType("Steamworks.SteamAPI, Assembly-CSharp-firstpass", false); + // Only apply this patch if SteamAPI type exists (game has Steam integration) [HarmonyPrepare] private static bool Prepare() { return _type != null; } + // Target the RestartAppIfNecessary method in SteamAPI [HarmonyTargetMethod] private static MethodBase TargetMethod() { @@ -24,11 +27,14 @@ internal static class StandaloneSteamPatch { const string file = "steam_appid.txt"; + // Create steam_appid.txt with Among Us app ID if it doesn't exist + // This allows the game to run without Steam running if (!File.Exists(file)) { - File.WriteAllText(file, "945360"); + File.WriteAllText(file, "945360"); // Among Us Steam App ID } - return __result = false; + __result = false; + return false; } -} +} \ No newline at end of file diff --git a/src/Patches/Unity/UnityWebRequestPatch.cs b/src/Patches/Unity/UnityWebRequestPatch.cs index 1afcaad..8f14529 100644 --- a/src/Patches/Unity/UnityWebRequestPatch.cs +++ b/src/Patches/Unity/UnityWebRequestPatch.cs @@ -10,10 +10,12 @@ namespace BetterAmongUs.Patches.Unity; [HarmonyPatch] internal static class UnityWebRequestPatch { + // Build mod version header string public static string GetHeader() { var stringBuilder = new StringBuilder(); + // Format: "Version;BuildType;IsHotfix/HotfixNum/BetaNum" stringBuilder.Append(ModInfo.PLUGIN_VERSION); stringBuilder.Append(';'); stringBuilder.Append(Enum.GetName(ModInfo.ReleaseBuildType)); @@ -27,19 +29,23 @@ internal static class UnityWebRequestPatch return stringBuilder.ToString(); } + // Add custom header to game API requests [HarmonyPatch(typeof(UnityWebRequest), nameof(UnityWebRequest.SendWebRequest))] [HarmonyPrefix] private static void UnityWebRequest_SendWebRequest_Prefix(UnityWebRequest __instance) { if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_BAUHttpHeader)) return; + // Check if this is a game API request var path = new Uri(__instance.url).AbsolutePath; if (path.Contains("/api/games")) { + // Add mod version header so server knows we're modded __instance.SetRequestHeader("BAU-Mod", GetHeader()); } } + // Check server response for mod support acknowledgment [HarmonyPatch(typeof(UnityWebRequest), nameof(UnityWebRequest.SendWebRequest))] [HarmonyPostfix] private static void UnityWebRequest_SendWebRequest_Postfix(UnityWebRequest __instance, UnityWebRequestAsyncOperation __result) @@ -49,10 +55,12 @@ internal static class UnityWebRequestPatch var path = new Uri(__instance.url).AbsolutePath; if (path.Contains("/api/games")) { + // Add callback when request completes __result.add_completed((Action)(_ => { if (!HttpUtils.IsSuccess(__instance.responseCode)) return; + // Check if server supports BAU mod var responseHeader = __instance.GetResponseHeader("BAU-Mod-Processed"); if (responseHeader != null) @@ -62,4 +70,4 @@ internal static class UnityWebRequestPatch })); } } -} +} \ No newline at end of file