Merge branch 'v1.3.3-dev' into main

This commit is contained in:
D1GQ authored and GitHub committed 2026-03-17 13:03:15 -05:00
commit d9de9da91c
66 files changed
+907 -405

No files matched your search

+21 -114
View File
@@ -1,9 +1,9 @@
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BetterAmongUs.Attributes;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Data.Json;
using BetterAmongUs.Enums;
using BetterAmongUs.Helpers;
@@ -11,7 +11,6 @@ using BetterAmongUs.Modules;
using BetterAmongUs.Modules.OptionItems;
using BetterAmongUs.Modules.Support;
using BetterAmongUs.Network;
using BetterAmongUs.Patches.Client;
using BetterAmongUs.Patches.Gameplay.UI.Settings;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
@@ -56,6 +55,11 @@ internal class BAUPlugin : BasePlugin
return text;
}
/// <summary>
/// Gets the BAUPlugin instance.
/// </summary>
internal static BAUPlugin? Instance { get; private set; }
/// <summary>
/// Gets the Harmony instance used for patching.
/// </summary>
@@ -81,14 +85,6 @@ internal class BAUPlugin : BasePlugin
/// </summary>
internal static PlatformSpecificData PlatformData => Constants.GetPlatformData();
/// <summary>
/// Gets the list of supported Among Us versions.
/// </summary>
internal static string[] SupportedAmongUsVersions =
[
"2025.11.18",
];
/// <summary>
/// Gets the list of all PlayerControl instances.
/// </summary>
@@ -116,6 +112,8 @@ internal class BAUPlugin : BasePlugin
public override void Load()
{
Instance = this;
try
{
foreach (var listener in BepInEx.Logging.Logger.Listeners)
@@ -146,7 +144,7 @@ internal class BAUPlugin : BasePlugin
BAUModdedSupportFlags.Initialize();
GithubAPI.Connect();
LoadOptions();
BAUConfigs.LoadConfigs();
BetterDataManager.Initialize();
Translator.Initialize();
Harmony.PatchAll();
@@ -161,7 +159,7 @@ internal class BAUPlugin : BasePlugin
File.WriteAllText(Path.Combine(BetterDataManager.filePathFolder, "better-log.txt"), "");
Logger_.Log("Better Among Us successfully loaded!");
string SupportedVersions = string.Join(" ", SupportedAmongUsVersions);
string SupportedVersions = string.Join(" ", ModInfo.SupportedAmongUsVersions);
Logger_.Log($"BetterAmongUs {BetterAmongUsVersion}-{ModInfo.BuildDate} - [{AppVersion} --> {SupportedVersions}] {Utils.GetPlatformName(PlatformData.Platform)}");
}
@@ -206,107 +204,6 @@ internal class BAUPlugin : BasePlugin
}
}
/// <summary>
/// Gets or sets the configuration entry for private only lobby setting.
/// </summary>
internal static ConfigEntry<bool>? PrivateOnlyLobby { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for anti-cheat setting.
/// </summary>
internal static ConfigEntry<bool>? AntiCheat { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for sending Better RPC setting.
/// </summary>
internal static ConfigEntry<bool>? SendBetterRpc { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for better notifications setting.
/// </summary>
internal static ConfigEntry<bool>? BetterNotifications { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for force own language setting.
/// </summary>
internal static ConfigEntry<bool>? ForceOwnLanguage { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for chat dark mode setting.
/// </summary>
internal static ConfigEntry<bool>? ChatDarkMode { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for chat in gameplay setting.
/// </summary>
internal static ConfigEntry<bool>? ChatInGameplay { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for lobby player info setting.
/// </summary>
internal static ConfigEntry<bool>? LobbyPlayerInfo { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for disable lobby theme setting.
/// </summary>
internal static ConfigEntry<bool>? DisableLobbyTheme { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for unlock FPS setting.
/// </summary>
internal static ConfigEntry<bool>? UnlockFPS { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for show FPS setting.
/// </summary>
internal static ConfigEntry<bool>? ShowFPS { get; private set; }
/// <summary>
/// Gets or sets the configuration entry for command prefix setting.
/// </summary>
internal static ConfigEntry<string>? CommandPrefix { get; set; }
/// <summary>
/// Gets or sets the configuration entry for favorite color setting.
/// </summary>
internal static ConfigEntry<int>? FavoriteColor { get; set; }
/// <summary>
/// Gets or sets the configuration entry for the settings preset.
/// </summary>
internal static ConfigEntry<int>? SettingsPreset { get; private set; }
/// <summary>
/// Loads configuration options from BepInEx config file.
/// </summary>
private void LoadOptions()
{
PrivateOnlyLobby = Config.Bind("Mod", "PrivateOnlyLobby", false);
AntiCheat = Config.Bind("Better Options", "AntiCheat", true);
SendBetterRpc = Config.Bind("Better Options", "SendBetterRpc", true);
BetterNotifications = Config.Bind("Better Options", "BetterNotifications", true);
ForceOwnLanguage = Config.Bind("Better Options", "ForceOwnLanguage", false);
ChatDarkMode = Config.Bind("Better Options", "ChatDarkMode", true);
ChatInGameplay = Config.Bind("Better Options", "ChatInGameplay", true);
LobbyPlayerInfo = Config.Bind("Better Options", "LobbyPlayerInfo", true);
DisableLobbyTheme = Config.Bind("Better Options", "DisableLobbyTheme", true);
UnlockFPS = Config.Bind("Better Options", "UnlockFPS", false);
ShowFPS = Config.Bind("Better Options", "ShowFPS", false);
CommandPrefix = Config.Bind("Client Options", "CommandPrefix", "/");
FavoriteColor = Config.Bind("Mod", "FavoriteColor", -1);
SettingsPreset = Config.Bind("Mod", "SettingsPreset", 0);
BAUModdedSupportEvents.InvokeAll_OnBAUConfigEntriesLoaded([
PrivateOnlyLobby, AntiCheat, SendBetterRpc,
BetterNotifications, ForceOwnLanguage, ChatDarkMode,
ChatInGameplay, LobbyPlayerInfo, DisableLobbyTheme,
UnlockFPS, ShowFPS, CommandPrefix,
FavoriteColor, SettingsPreset
]);
OptionsMenuBehaviourPatch.UpdateFrameRate();
}
/// <summary>
/// Gets the persistent data path for Among Us.
/// </summary>
@@ -317,5 +214,15 @@ internal class BAUPlugin : BasePlugin
/// Gets the game installation path for Among Us.
/// </summary>
/// <returns>The game installation path string.</returns>
internal static string GetGamePathToAmongUs() => Path.GetDirectoryName(Application.dataPath) ?? Application.dataPath;
internal static string GetGamePathToAmongUs()
{
if (!ModInfo.Starlight)
{
return Path.GetDirectoryName(Application.dataPath) ?? throw new Exception("Unable to find `Application.dataPath` path");
}
else
{
return GetDataPathToAmongUs();
}
}
}
+1
View File
@@ -9,6 +9,7 @@ internal sealed class DumpCommand : BaseCommand
{
internal override string Name => "dump";
internal override string Description => "Dump the entire log to the user's desktop";
internal override bool ShowCommand() => !ModInfo.Starlight;
internal override bool CanRunCommand(out string reason)
{
+3 -2
View File
@@ -1,5 +1,6 @@
using BetterAmongUs.Attributes;
using BetterAmongUs.Commands.Arguments;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Modules.Support;
namespace BetterAmongUs.Commands;
@@ -21,11 +22,11 @@ internal sealed class SetPrefixCommand : BaseCommand
internal override void Run()
{
var oldPrefix = BAUPlugin.CommandPrefix.Value;
var oldPrefix = BAUConfigs.CommandPrefix.Value;
var prefix = prefixArgument.Arg.ToCharArray()?.First().ToString();
if (!string.IsNullOrEmpty(prefix))
{
BAUPlugin.CommandPrefix.Value = prefix;
BAUConfigs.CommandPrefix.Value = prefix;
CommandResultText($"Command prefix set from <#c1c100>{oldPrefix}</color> to <#c1c100>{prefix}</color>");
}
else
+4 -3
View File
@@ -1,4 +1,5 @@
using BetterAmongUs.Data.Json;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Data.Json;
using BetterAmongUs.Helpers;
namespace BetterAmongUs.Data;
@@ -56,7 +57,7 @@ internal static class BetterDataManager
/// <summary>
/// Current compressed settings file path.
/// </summary>
internal static string SettingsFile => Path.Combine(filePathFolderSettings, $"Preset-{BAUPlugin.SettingsPreset?.Value ?? 0}.dat");
internal static string SettingsFile => Path.Combine(filePathFolderSettings, $"Preset-{BAUConfigs.SettingsPreset?.Value ?? 0}.dat");
/// <summary>
/// File containing banned player identifiers.
@@ -172,7 +173,7 @@ internal static class BetterDataManager
{
if (File.Exists(SettingsFile_Legacy))
{
BAUPlugin.SettingsPreset.Value = 1;
BAUConfigs.SettingsPreset.Value = 1;
File.Move(SettingsFile_Legacy, SettingsFile);
}
}
+59
View File
@@ -0,0 +1,59 @@
using BepInEx.Configuration;
namespace BetterAmongUs.Data.Config;
/// <summary>
/// Represents a configuration entry wrapper for Better Among Us.
/// Provides type-safe access to BepInEx configuration entries with implicit conversion support.
/// </summary>
/// <typeparam name="T">The type of the configuration value.</typeparam>
internal sealed class BAUConfigEntry<T>
{
/// <summary>
/// Initializes a new instance of the <see cref="BAUConfigEntry{T}"/> class.
/// </summary>
/// <param name="category">The configuration category/section name.</param>
/// <param name="name">The name of the configuration entry.</param>
/// <param name="defaultValue">The default value for the configuration entry.</param>
internal BAUConfigEntry(string category, string name, T defaultValue)
{
_config = BAUPlugin.Instance.Config.Bind(category, name, defaultValue);
}
private readonly ConfigEntry<T> _config;
/// <summary>
/// Gets or sets the current value of the configuration entry.
/// </summary>
internal T Value
{
get
{
return _config.Value;
}
set
{
_config.Value = value;
}
}
/// <summary>
/// Implicitly converts a <see cref="BAUConfigEntry{T}"/> to a <see cref="ConfigEntry{T}"/>.
/// </summary>
/// <param name="entry">The BAU configuration entry to convert.</param>
/// <returns>The underlying BepInEx configuration entry.</returns>
public static implicit operator ConfigEntry<T>(BAUConfigEntry<T> entry)
{
return entry._config;
}
/// <summary>
/// Implicitly converts a <see cref="BAUConfigEntry{T}"/> to a <see cref="ConfigEntryBase"/>.
/// </summary>
/// <param name="entry">The BAU configuration entry to convert.</param>
/// <returns>The underlying BepInEx configuration entry as a base type.</returns>
public static implicit operator ConfigEntryBase(BAUConfigEntry<T> entry)
{
return entry._config;
}
}
+106
View File
@@ -0,0 +1,106 @@
using BetterAmongUs.Modules.Support;
using BetterAmongUs.Patches.Client;
namespace BetterAmongUs.Data.Config;
/// <summary>
/// Manages configuration entries for the Better Among Us.
/// </summary>
internal static class BAUConfigs
{
/// <summary>
/// Gets the configuration entry for anti-cheat setting.
/// </summary>
internal static BAUConfigEntry<bool>? AntiCheat { get; } = new("Better Options", "AntiCheat", true);
/// <summary>
/// Gets the configuration entry for sending Better RPC setting.
/// </summary>
internal static BAUConfigEntry<bool>? SendBetterRpc { get; } = new("Better Options", "SendBetterRpc", true);
/// <summary>
/// Gets the configuration entry for better notifications setting.
/// </summary>
internal static BAUConfigEntry<bool>? BetterNotifications { get; } = new("Better Options", "BetterNotifications", true);
/// <summary>
/// Gets the configuration entry for force own language setting.
/// </summary>
internal static BAUConfigEntry<bool>? ForceOwnLanguage { get; } = new("Better Options", "ForceOwnLanguage", false);
/// <summary>
/// Gets the configuration entry for chat dark mode setting.
/// </summary>
internal static BAUConfigEntry<bool>? ChatDarkMode { get; } = new("Better Options", "ChatDarkMode", true);
/// <summary>
/// Gets the configuration entry for chat in gameplay setting.
/// </summary>
internal static BAUConfigEntry<bool>? ChatInGameplay { get; } = new("Better Options", "ChatInGameplay", true);
/// <summary>
/// Gets the configuration entry for lobby player info setting.
/// </summary>
internal static BAUConfigEntry<bool>? LobbyPlayerInfo { get; } = new("Better Options", "LobbyPlayerInfo", true);
/// <summary>
/// Gets the configuration entry for disable lobby theme setting.
/// </summary>
internal static BAUConfigEntry<bool>? DisableLobbyTheme { get; } = new("Better Options", "DisableLobbyTheme", true);
/// <summary>
/// Gets the configuration entry for unlock FPS setting.
/// </summary>
internal static BAUConfigEntry<bool>? UnlockFPS { get; } = new("Better Options", "UnlockFPS", false);
/// <summary>
/// Gets the configuration entry for show FPS setting.
/// </summary>
internal static BAUConfigEntry<bool>? ShowFPS { get; } = new("Better Options", "ShowFPS", false);
/// <summary>
/// Gets the configuration entry for minimap icons setting.
/// </summary>
internal static BAUConfigEntry<bool>? MinimapIcons { get; } = new("Better Options", "MinimapIcons", true);
/// <summary>
/// Gets the configuration entry for vent color groups setting.
/// </summary>
internal static BAUConfigEntry<bool>? VentColorGroups { get; } = new("Better Options", "VentColorGroups", true);
/// <summary>
/// Gets the configuration entry for command prefix setting.
/// </summary>
internal static BAUConfigEntry<string>? CommandPrefix { get; } = new("Mod", "CommandPrefix", "/");
/// <summary>
/// Gets the configuration entry for private only lobby setting.
/// </summary>
internal static BAUConfigEntry<bool>? PrivateOnlyLobby { get; } = new("Mod", "PrivateOnlyLobby", false);
/// <summary>
/// Gets the configuration entry for favorite color setting.
/// </summary>
internal static BAUConfigEntry<int>? FavoriteColor { get; } = new("Mod", "FavoriteColor", -1);
/// <summary>
/// Gets the configuration entry for the settings preset.
/// </summary>
internal static BAUConfigEntry<int>? SettingsPreset { get; } = new("Mod", "SettingsPreset", 0);
/// <summary>
/// Loads configuration options from BepInEx config file.
/// </summary>
internal static void LoadConfigs()
{
BAUModdedSupportEvents.InvokeAll_OnBAUConfigEntriesLoaded([
PrivateOnlyLobby, AntiCheat, SendBetterRpc,
BetterNotifications, ForceOwnLanguage, ChatDarkMode,
ChatInGameplay, LobbyPlayerInfo, DisableLobbyTheme,
UnlockFPS, ShowFPS, MinimapIcons, VentColorGroups, CommandPrefix,
FavoriteColor, SettingsPreset
]);
OptionsMenuBehaviourPatch.UpdateFrameRate();
}
}
+17
View File
@@ -142,4 +142,21 @@ internal sealed class OutfitData
VisorId = DataManager.Player.Customization.Visor;
NamePlateId = DataManager.Player.Customization.NamePlate;
}
/// <summary>
/// Converts to network player outfit.
/// </summary>
/// <returns></returns>
internal NetworkedPlayerInfo.PlayerOutfit ToPlayerOutfit()
{
return new NetworkedPlayerInfo.PlayerOutfit
{
HatId = HatId,
PetId = PetId,
SkinId = SkinId,
VisorId = VisorId,
NamePlateId = NamePlateId,
ColorId = DataManager.Player.Customization.Color
};
}
}
+69
View File
@@ -0,0 +1,69 @@
using UnityEngine.Events;
namespace BetterAmongUs.Helpers;
/// <summary>
/// Provides extension methods for UnityEvent classes to simplify adding listeners with Action delegates.
/// </summary>
internal static class ActionExtensions
{
/// <summary>
/// Adds a listener to the UnityEvent that invokes the specified action when the event is raised.
/// </summary>
/// <param name="unityEvent">The UnityEvent to add the listener to.</param>
/// <param name="action">The action to invoke when the event is raised.</param>
internal static void AddListener(this UnityEvent unityEvent, Action action)
{
unityEvent.AddListener(action);
}
/// <summary>
/// Adds a listener to the UnityEvent&lt;T0&gt; that invokes the specified action when the event is raised.
/// </summary>
/// <typeparam name="T0">The type of the first event parameter.</typeparam>
/// <param name="unityEvent">The UnityEvent&lt;T0&gt; to add the listener to.</param>
/// <param name="action">The action to invoke when the event is raised, accepting one parameter of type T0.</param>
internal static void AddListener<T0>(this UnityEvent<T0> unityEvent, Action<T0> action)
{
unityEvent.AddListener(action);
}
/// <summary>
/// Adds a listener to the UnityEvent&lt;T0, T1&gt; that invokes the specified action when the event is raised.
/// </summary>
/// <typeparam name="T0">The type of the first event parameter.</typeparam>
/// <typeparam name="T1">The type of the second event parameter.</typeparam>
/// <param name="unityEvent">The UnityEvent&lt;T0, T1&gt; to add the listener to.</param>
/// <param name="action">The action to invoke when the event is raised, accepting two parameters of types T0 and T1.</param>
internal static void AddListener<T0, T1>(this UnityEvent<T0, T1> unityEvent, Action<T0, T1> action)
{
unityEvent.AddListener(action);
}
/// <summary>
/// Adds a listener to the UnityEvent&lt;T0, T1, T2&gt; that invokes the specified action when the event is raised.
/// </summary>
/// <typeparam name="T0">The type of the first event parameter.</typeparam>
/// <typeparam name="T1">The type of the second event parameter.</typeparam>
/// <typeparam name="T2">The type of the third event parameter.</typeparam>
/// <param name="unityEvent">The UnityEvent&lt;T0, T1, T2&gt; to add the listener to.</param>
/// <param name="action">The action to invoke when the event is raised, accepting three parameters of types T0, T1, and T2.</param>
internal static void AddListener<T0, T1, T2>(this UnityEvent<T0, T1, T2> unityEvent, Action<T0, T1, T2> action)
{
unityEvent.AddListener(action);
}
/// <summary>
/// Adds a listener to the UnityEvent&lt;T0, T1, T2, T3&gt; that invokes the specified action when the event is raised.
/// </summary>
/// <typeparam name="T0">The type of the first event parameter.</typeparam>
/// <typeparam name="T1">The type of the second event parameter.</typeparam>
/// <typeparam name="T2">The type of the third event parameter.</typeparam>
/// <typeparam name="T3">The type of the fourth event parameter.</typeparam>
/// <param name="unityEvent">The UnityEvent&lt;T0, T1, T2, T3&gt; to add the listener to.</param>
/// <param name="action">The action to invoke when the event is raised, accepting four parameters of types T0, T1, T2, and T3.</param>
internal static void AddListener<T0, T1, T2, T3>(this UnityEvent<T0, T1, T2, T3> unityEvent, Action<T0, T1, T2, T3> action)
{
unityEvent.AddListener(action);
}
}
+15
View File
@@ -54,6 +54,21 @@ internal static class ObjectHelper
/// <param name="mono">The MonoBehaviour whose GameObject should be searched.</param>
internal static void DestroyTextTranslators(this MonoBehaviour mono) => mono.gameObject.DestroyTextTranslators();
/// <summary>
/// Destroys a specified component on a game object
/// </summary>
/// <typeparam name="T">The Type of the Component to destroy</typeparam>
/// <param name="obj"></param>
internal static void RemoveComponent<T>(this GameObject obj) where T : Component
{
var comp = obj.GetComponent<T>();
if (comp != null)
{
UnityEngine.Object.Destroy(comp);
}
}
/// <summary>
/// Sets the color of all SpriteRenderer components in the children of a GameObject.
/// </summary>
+2 -1
View File
@@ -12,7 +12,7 @@ namespace BetterAmongUs.Helpers;
/// <summary>
/// Provides extension methods and utilities for working with PlayerControl instances.
/// </summary>
static class PlayerControlHelper
internal static class PlayerControlHelper
{
/// <summary>
/// Gets the ClientData associated with a player.
@@ -83,6 +83,7 @@ static class PlayerControlHelper
{
return false;
}
return true;
}
-1
View File
@@ -203,7 +203,6 @@ internal static class Utils
}
catch
{
// Intentionally empty - chat failures shouldn't crash the game
}
}
+2 -1
View File
@@ -1,4 +1,5 @@
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Mono;
@@ -29,7 +30,7 @@ internal static class BetterNotificationManager
/// <param name="Time">The duration in seconds to show the notification.</param>
internal static void Notify(string text, float Time = 5f)
{
if (!BAUPlugin.BetterNotifications.Value) return;
if (!BAUConfigs.BetterNotifications.Value) return;
if (BAUNotificationManagerObj != null)
{
+2 -2
View File
@@ -57,11 +57,11 @@ internal sealed class UpdateManager : MonoBehaviour
if (button != null)
{
button.OnClick = new();
button.OnClick.AddListener((Action)(() =>
button.OnClick.AddListener(() =>
{
if (AmUpdateing || WaitForRestart) return;
this.StartCoroutine(CoPressDownload(doNotPress));
}));
});
}
var obj = new GameObject("Update(TMP)");
+18 -5
View File
@@ -11,7 +11,7 @@ internal static class ModInfo
/// <summary>
/// Gets the release type of the current build.
/// </summary>
internal static readonly ReleaseTypes ReleaseBuildType = ReleaseTypes.Release;
internal static readonly ReleaseTypes ReleaseBuildType = ReleaseTypes.Beta;
/// <summary>
/// Gets the Git commit hash from assembly metadata.
@@ -26,17 +26,17 @@ internal static class ModInfo
/// <summary>
/// The beta number for beta releases.
/// </summary>
internal const string BETA_NUM = "0";
internal const string BETA_NUM = "1";
/// <summary>
/// The hotfix number for hotfix releases.
/// </summary>
internal const string HOTFIX_NUM = "3";
internal const string HOTFIX_NUM = "0";
/// <summary>
/// Indicates whether this is a hotfix release.
/// </summary>
internal const bool IS_HOTFIX = true;
internal const bool IS_HOTFIX = false;
/// <summary>
/// The name of BAU.
@@ -51,7 +51,15 @@ internal static class ModInfo
/// <summary>
/// The version of BAU.
/// </summary>
internal const string PLUGIN_VERSION = "1.3.2";
internal const string PLUGIN_VERSION = "1.3.3";
/// <summary>
/// Gets the list of supported Among Us versions.
/// </summary>
internal static string[] SupportedAmongUsVersions =
[
"2025.11.18",
];
/// <summary>
/// The GitHub repository URL for BAU.
@@ -63,6 +71,11 @@ internal static class ModInfo
/// </summary>
internal const string DISCORD = "https://discord.gg/vjYrXpzNAn";
/// <summary>
/// Indicator rather that BAU is running on Starlight.
/// </summary>
internal static readonly bool Starlight = OperatingSystem.IsAndroid();
/// <summary>
/// Retrieves metadata from the assembly attributes.
/// </summary>
+3 -2
View File
@@ -1,5 +1,6 @@
using BetterAmongUs.Attributes;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Enums;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
@@ -82,7 +83,7 @@ internal static class BetterAntiCheat
internal static void CheckRPC(PlayerControl player, byte callId, MessageReader oldReader)
{
if (player == null || player?.Data == null) return;
if (!IsEnabled || !BAUPlugin.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !BetterGameSettings.DetectInvalidRPCs.GetBool()) return;
if (!IsEnabled || !BAUConfigs.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !BetterGameSettings.DetectInvalidRPCs.GetBool()) return;
if (player.IsLocalPlayer() && player.IsHost()) return;
MessageReader reader = MessageReader.Get(oldReader);
@@ -102,7 +103,7 @@ internal static class BetterAntiCheat
try
{
if (player == null || player?.Data == null) return true;
if (!IsEnabled || !BAUPlugin.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !BetterGameSettings.DetectInvalidRPCs.GetBool()) return true;
if (!IsEnabled || !BAUConfigs.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !BetterGameSettings.DetectInvalidRPCs.GetBool()) return true;
if (player.IsLocalPlayer() && player.IsHost()) return true;
MessageReader reader = MessageReader.Get(oldReader);
@@ -1,5 +1,6 @@
using BetterAmongUs.Attributes;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Enums;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
@@ -31,7 +32,7 @@ internal sealed class AUMChatHandler : RPCHandler
Logger_.Log($"{sender.Data.PlayerName} -> {msgString}", "AUMChatLog");
if (!BAUPlugin.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !BetterGameSettings.DetectCheatClients.GetBool()) return;
if (!BAUConfigs.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !BetterGameSettings.DetectCheatClients.GetBool()) return;
var flag = string.IsNullOrEmpty(nameString) && string.IsNullOrEmpty(msgString);
@@ -1,5 +1,6 @@
using BetterAmongUs.Attributes;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Enums;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
@@ -18,7 +19,7 @@ internal sealed class AUMHandler : RPCHandler
internal override void HandleCheatRpcCheck(PlayerControl? sender, MessageReader reader)
{
if (BAUPlugin.AntiCheat.Value && !BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) && BetterGameSettings.DetectCheatClients.GetBool())
if (BAUConfigs.AntiCheat.Value && !BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) && BetterGameSettings.DetectCheatClients.GetBool())
{
var aumid = reader.ReadByte();
@@ -1,5 +1,6 @@
using BetterAmongUs.Attributes;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Enums;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
@@ -18,7 +19,7 @@ internal sealed class KillNetworkChatHandler : RPCHandler
internal override void HandleCheatRpcCheck(PlayerControl? sender, MessageReader reader)
{
if (!BAUPlugin.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !BetterGameSettings.DetectCheatClients.GetBool()) return;
if (!BAUConfigs.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !BetterGameSettings.DetectCheatClients.GetBool()) return;
if (!BetterDataManager.BetterDataFile.KNData.Any(info => info.CheckPlayerData(sender.Data)))
{
@@ -1,5 +1,6 @@
using BetterAmongUs.Attributes;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Enums;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
@@ -18,7 +19,7 @@ internal sealed class KillNetworkHandler : RPCHandler
internal override void HandleCheatRpcCheck(PlayerControl? sender, MessageReader reader)
{
if (BAUPlugin.AntiCheat.Value && !BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) && BetterGameSettings.DetectCheatClients.GetBool())
if (BAUConfigs.AntiCheat.Value && !BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) && BetterGameSettings.DetectCheatClients.GetBool())
{
if (!BetterDataManager.BetterDataFile.KNData.Any(info => info.CheckPlayerData(sender.Data)))
{
@@ -1,5 +1,6 @@
using BetterAmongUs.Attributes;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Enums;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
@@ -18,7 +19,7 @@ internal sealed class SickoHandler : RPCHandler
internal override void HandleCheatRpcCheck(PlayerControl? sender, MessageReader reader)
{
if (BAUPlugin.AntiCheat.Value && !BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) && BetterGameSettings.DetectCheatClients.GetBool())
if (BAUConfigs.AntiCheat.Value && !BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) && BetterGameSettings.DetectCheatClients.GetBool())
{
if (reader.BytesRemaining == 0 && !BetterDataManager.BetterDataFile.SickoData.Any(info => info.CheckPlayerData(sender.Data)))
{
+137 -106
View File
@@ -1,7 +1,7 @@
using BepInEx.Configuration;
using BetterAmongUs.Helpers;
using BetterAmongUs.Patches.Client;
using UnityEngine;
using Object = UnityEngine.Object;
namespace BetterAmongUs.Modules;
@@ -23,24 +23,24 @@ internal sealed class ClientOptionItem
/// <summary>
/// Gets the list of all created client option items.
/// </summary>
internal static readonly List<ClientOptionItem> ClientOptions = [];
internal static readonly Dictionary<int, List<ClientOptionItem>> ClientOptions = [];
/// <summary>
/// Creates a toggle option with configuration binding.
/// </summary>
/// <param name="name">The display name of the option.</param>
/// <param name="config">The configuration entry to bind to.</param>
/// <param name="optionsMenuBehaviour">The options menu behavior instance.</param>
/// <param name="onToggle">Optional action to execute when the toggle is changed.</param>
/// <param name="toggleCheck">Optional function to check if the toggle can be changed.</param>
/// <returns>A new ClientOptionItem instance.</returns>
public static ClientOptionItem CreateToggle(string name, ConfigEntry<bool> config, OptionsMenuBehaviour optionsMenuBehaviour, Action? onToggle = null, Func<bool>? toggleCheck = null)
public static ClientOptionItem CreateToggle(string name, ConfigEntry<bool> config, int page, OptionsMenuBehaviour optionsMenuBehaviour, Action? onToggle = null, Func<bool>? toggleCheck = null)
{
var toggleButton = CreateToggleButton(name, config.Value, optionsMenuBehaviour);
var toggleButton = CreateToggleButton(name, optionsMenuBehaviour, GetOrCreatePage(page, optionsMenuBehaviour).transform);
var item = new ClientOptionItem(name, config, toggleButton);
item.SetupToggleButton(onToggle, toggleCheck);
ClientOptions.Add(item);
if (!ClientOptions.TryGetValue(page, out var options))
{
options = ClientOptions[page] = [];
}
options.Add(item);
UpdateAllButtonPositions();
return item;
}
@@ -48,38 +48,19 @@ internal sealed class ClientOptionItem
/// <summary>
/// Creates a button option without toggle state.
/// </summary>
/// <param name="name">The display name of the button.</param>
/// <param name="optionsMenuBehaviour">The options menu behavior instance.</param>
/// <param name="onClick">The action to execute when the button is clicked.</param>
/// <param name="clickCheck">Optional function to check if the button can be clicked.</param>
/// <returns>A new ClientOptionItem instance.</returns>
public static ClientOptionItem CreateButton(string name, OptionsMenuBehaviour optionsMenuBehaviour, Action onClick, Func<bool>? clickCheck = null)
public static ClientOptionItem CreateButton(string name, int page, OptionsMenuBehaviour optionsMenuBehaviour, Action onClick, Func<bool>? clickCheck = null)
{
var toggleButton = CreateToggleButton(name, false, optionsMenuBehaviour);
var toggleButton = CreateToggleButton(name, optionsMenuBehaviour, GetOrCreatePage(page, optionsMenuBehaviour).transform);
var item = new ClientOptionItem(name, null, toggleButton);
item.SetupButton(onClick, clickCheck);
ClientOptions.Add(item);
if (!ClientOptions.TryGetValue(page, out var options))
{
options = ClientOptions[page] = [];
}
options.Add(item);
return item;
}
/// <summary>
/// Creates a toggle option without configuration binding (manual state management).
/// </summary>
/// <param name="name">The display name of the option.</param>
/// <param name="initialState">The initial state of the toggle.</param>
/// <param name="optionsMenuBehaviour">The options menu behavior instance.</param>
/// <param name="onToggle">The action to execute when the toggle is changed, receiving the new state.</param>
/// <param name="toggleCheck">Optional function to check if the toggle can be changed.</param>
/// <returns>A new ClientOptionItem instance.</returns>
public static ClientOptionItem CreateManualToggle(string name, bool initialState, OptionsMenuBehaviour optionsMenuBehaviour, Action<bool> onToggle, Func<bool>? toggleCheck = null)
{
var toggleButton = CreateToggleButton(name, initialState, optionsMenuBehaviour);
var item = new ClientOptionItem(name, null, toggleButton);
item.SetupManualToggle(initialState, onToggle, toggleCheck);
ClientOptions.Add(item);
UpdateAllButtonPositions();
return item;
}
@@ -87,10 +68,7 @@ internal sealed class ClientOptionItem
/// <summary>
/// Initializes a new instance of the ClientOptionItem class.
/// </summary>
/// <param name="name">The name of the option.</param>
/// <param name="config">The configuration entry, or null for non-config options.</param>
/// <param name="toggleButton">The toggle button behavior instance.</param>
private ClientOptionItem(string name, ConfigEntry<bool>? config, ToggleButtonBehaviour toggleButton)
internal ClientOptionItem(string name, ConfigEntry<bool>? config, ToggleButtonBehaviour toggleButton)
{
Config = config;
ToggleButton = toggleButton;
@@ -100,36 +78,54 @@ internal sealed class ClientOptionItem
/// <summary>
/// Creates a toggle button GameObject for the options menu.
/// </summary>
/// <param name="name">The name of the toggle button.</param>
/// <param name="initialState">The initial state of the toggle.</param>
/// <param name="optionsMenuBehaviour">The options menu behavior instance.</param>
/// <returns>A new ToggleButtonBehaviour instance.</returns>
private static ToggleButtonBehaviour CreateToggleButton(string name, bool initialState, OptionsMenuBehaviour optionsMenuBehaviour)
private static ToggleButtonBehaviour CreateToggleButton(string name, OptionsMenuBehaviour optionsMenuBehaviour, Transform parent)
{
if (OptionsMenuBehaviourPatch.BetterOptionsTab?.Content == null)
{
throw new InvalidOperationException("BetterOptionsTab is not initialized");
}
var mouseMoveToggle = optionsMenuBehaviour.DisableMouseMovement;
var toggleButton = Object.Instantiate(mouseMoveToggle, OptionsMenuBehaviourPatch.BetterOptionsTab.Content.transform);
toggleButton.transform.localPosition = CalculateButtonPosition();
var toggleButton = UnityEngine.Object.Instantiate(mouseMoveToggle, parent);
toggleButton.name = name;
toggleButton.Text.text = name;
return toggleButton;
}
/// <summary>
/// Calculates all option positions.
/// </summary>
private static void UpdateAllButtonPositions()
{
foreach (var kvp in ClientOptions)
{
int page = kvp.Key;
for (int i = 0; i < kvp.Value.Count; i++)
{
ClientOptionItem? option = kvp.Value[i];
option.ToggleButton.gameObject.transform.localPosition = CalculateButtonPosition(page, i);
}
}
}
/// <summary>
/// Calculates the position for a new button based on the current number of options.
/// </summary>
/// <returns>A Vector3 representing the button position.</returns>
private static Vector3 CalculateButtonPosition()
private static Vector3 CalculateButtonPosition(int page, int count)
{
if (page == -1)
{
if (!ClientOptions.TryGetValue(page, out var options))
{
options = ClientOptions[page] = [];
}
return new Vector3(
options.Count == 1 ? 0f : count % 2 == 0 ? -1.3f : 1.3f,
-1.8f,
-6f
);
}
return new Vector3(
ClientOptions.Count % 2 == 0 ? -1.3f : 1.3f,
1.8f - 0.5f * (ClientOptions.Count / 2),
count % 2 == 0 ? -1.3f : 1.3f,
1.8f - 0.5f * (count / 2),
-6f
);
}
@@ -137,14 +133,12 @@ internal sealed class ClientOptionItem
/// <summary>
/// Sets up a configuration-bound toggle button with click handler.
/// </summary>
/// <param name="onToggle">Optional action to execute when toggled.</param>
/// <param name="toggleCheck">Optional function to check if the toggle can be changed.</param>
private void SetupToggleButton(Action? onToggle, Func<bool>? toggleCheck)
internal void SetupToggleButton(Action? onToggle, Func<bool>? toggleCheck)
{
var passiveButton = ToggleButton.GetComponent<PassiveButton>();
passiveButton.OnClick = new();
passiveButton.OnClick.AddListener((Action)(() =>
passiveButton.OnClick.AddListener(() =>
{
if (toggleCheck?.Invoke() == false) return;
@@ -154,7 +148,7 @@ internal sealed class ClientOptionItem
UpdateToggle();
}
onToggle?.Invoke();
}));
});
UpdateToggle();
}
@@ -162,9 +156,7 @@ internal sealed class ClientOptionItem
/// <summary>
/// Sets up a button (non-toggle) with click handler.
/// </summary>
/// <param name="onClick">The action to execute when clicked.</param>
/// <param name="clickCheck">Optional function to check if the button can be clicked.</param>
private void SetupButton(Action onClick, Func<bool>? clickCheck)
internal void SetupButton(Action onClick, Func<bool>? clickCheck)
{
var passiveButton = ToggleButton.GetComponent<PassiveButton>();
passiveButton.OnClick = new();
@@ -174,36 +166,11 @@ internal sealed class ClientOptionItem
ToggleButton.Rollover?.ChangeOutColor(new Color32(0, 150, 0, 255));
ToggleButton.Text.color = new Color(1f, 1f, 1f, 1f);
passiveButton.OnClick.AddListener((Action)(() =>
passiveButton.OnClick.AddListener(() =>
{
if (clickCheck?.Invoke() == false) return;
onClick?.Invoke();
}));
}
/// <summary>
/// Sets up a manually managed toggle button with state tracking.
/// </summary>
/// <param name="initialState">The initial state of the toggle.</param>
/// <param name="onToggle">The action to execute when toggled, receiving the new state.</param>
/// <param name="toggleCheck">Optional function to check if the toggle can be changed.</param>
private void SetupManualToggle(bool initialState, Action<bool> onToggle, Func<bool>? toggleCheck)
{
var passiveButton = ToggleButton.GetComponent<PassiveButton>();
passiveButton.OnClick = new();
bool currentState = initialState;
passiveButton.OnClick.AddListener((Action)(() =>
{
if (toggleCheck?.Invoke() == false) return;
currentState = !currentState;
UpdateManualToggle(currentState);
onToggle?.Invoke(currentState);
}));
UpdateManualToggle(initialState);
});
}
/// <summary>
@@ -216,21 +183,9 @@ internal sealed class ClientOptionItem
UpdateToggleVisuals(Config.Value);
}
/// <summary>
/// Updates the visual state of a manually managed toggle button.
/// </summary>
/// <param name="isEnabled">The current enabled state of the toggle.</param>
internal void UpdateManualToggle(bool isEnabled)
{
if (ToggleButton == null) return;
UpdateToggleVisuals(isEnabled);
}
/// <summary>
/// Updates the visual appearance of a toggle button based on its state.
/// </summary>
/// <param name="isEnabled">Whether the toggle is enabled.</param>
private void UpdateToggleVisuals(bool isEnabled)
{
var color = isEnabled ?
@@ -246,4 +201,80 @@ internal sealed class ClientOptionItem
ToggleButton.Text.color = textColor;
ToggleButton.Text.text = $"{ToggleButton.name}: {(isEnabled ? "On" : "Off")}";
}
/// <summary>
/// Retrieves an existing page GameObject or creates a new one with navigation buttons.
/// </summary>
private static GameObject? GetOrCreatePage(int page, OptionsMenuBehaviour optionsMenuBehaviour, bool doNotCreate = false)
{
if (page == -1)
{
return OptionsMenuBehaviourPatch.BetterOptionsTab.Content;
}
string name = "Page " + page;
var currentPage = OptionsMenuBehaviourPatch.BetterOptionsTab.Content.transform.Find(name)?.gameObject;
if (currentPage == null)
{
if (doNotCreate)
{
return null;
}
currentPage = new GameObject(name);
currentPage.SetActive(page == 1);
currentPage.transform.SetParent(OptionsMenuBehaviourPatch.BetterOptionsTab.Content.transform);
currentPage.transform.localPosition = Vector3.zero;
currentPage.transform.localScale = Vector3.one;
int previous = page - 1;
if (previous > 0)
{
var previousPage = GetOrCreatePage(previous, optionsMenuBehaviour, true);
if (previousPage != null)
{
CreatePreviousButton(currentPage, previousPage, optionsMenuBehaviour);
CreateNextButton(previousPage, currentPage, optionsMenuBehaviour);
}
}
}
return currentPage;
}
/// <summary>
/// Creates a "Next" navigation button that switches to the specified next page.
/// </summary>
private static void CreateNextButton(GameObject page, GameObject nextPage, OptionsMenuBehaviour optionsMenuBehaviour)
{
var button = CreateToggleButton("Next >", optionsMenuBehaviour, page.transform);
button.transform.localPosition = new Vector3(2f, -2.5f, 0f);
button.transform.Find("Background")?.localScale = new Vector3(0.5f, 1f, 1f);
button.transform.Find("ButtonHighlight")?.localScale = new Vector3(0.5f, 0.95f, 1f);
var passiveButton = button.GetComponent<PassiveButton>();
passiveButton.OnClick = new();
passiveButton.OnClick.AddListener(() =>
{
page.SetActive(false);
nextPage.SetActive(true);
});
}
/// <summary>
/// Creates a "Previous" navigation button that switches to the specified previous page.
/// </summary>
private static void CreatePreviousButton(GameObject page, GameObject previousPage, OptionsMenuBehaviour optionsMenuBehaviour)
{
var button = CreateToggleButton("< Prev", optionsMenuBehaviour, page.transform);
button.transform.localPosition = new Vector3(-2f, -2.5f, 0f);
button.transform.Find("Background")?.localScale = new Vector3(0.5f, 1f, 1f);
button.transform.Find("ButtonHighlight")?.localScale = new Vector3(0.5f, 0.95f, 1f);
var passiveButton = button.GetComponent<PassiveButton>();
passiveButton.OnClick = new();
passiveButton.OnClick.AddListener(() =>
{
page.SetActive(false);
previousPage.SetActive(true);
});
}
}
+2 -1
View File
@@ -1,4 +1,5 @@
using AmongUs.GameOptions;
using BetterAmongUs.Data.Config;
namespace BetterAmongUs.Modules;
@@ -241,7 +242,7 @@ internal static class GameState
/// <summary>
/// Gets whether the lobby is private-only (requires specific settings).
/// </summary>
internal static bool IsPrivateOnlyLobby => (BAUPlugin.PrivateOnlyLobby.Value || AmongUsClient.Instance.AmLocalHost) && IsHost;
internal static bool IsPrivateOnlyLobby => (BAUConfigs.PrivateOnlyLobby.Value || AmongUsClient.Instance.AmLocalHost) && IsHost;
/// <summary>
/// Gets whether the local player can move.
+4 -3
View File
@@ -1,5 +1,6 @@
using BepInEx.Unity.IL2CPP.Utils;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Enums;
using BetterAmongUs.Helpers;
using BetterAmongUs.Mono;
@@ -29,7 +30,7 @@ internal sealed class HandshakeHandler
private IEnumerator CoWaitSendSecretToPlayer()
{
if (!BAUPlugin.SendBetterRpc.Value) yield break;
if (!BAUConfigs.SendBetterRpc.Value) yield break;
while (extendedData._Data?.Object == null || PlayerControl.LocalPlayer == null)
{
@@ -43,7 +44,7 @@ internal sealed class HandshakeHandler
internal void ResendSecretToPlayer()
{
if (!BAUPlugin.SendBetterRpc.Value) return;
if (!BAUConfigs.SendBetterRpc.Value) return;
if (HasSendSharedSecret && extendedData.IsVerifiedBetterUser) return;
HasSendSharedSecret = false;
@@ -88,7 +89,7 @@ internal sealed class HandshakeHandler
private void SendSecretHashToSender(int tempKey, int senderClientId)
{
if (!BAUPlugin.SendBetterRpc.Value) return;
if (!BAUConfigs.SendBetterRpc.Value) return;
int hash = SharedSecret.GetSharedSecretHash();
@@ -78,7 +78,7 @@ public sealed class OptionCheckboxItem : OptionItem<bool>
toggleOption.TitleText.text = Name;
var button = toggleOption.buttons[0];
button.OnClick = new();
button.OnClick.AddListener((Action)(() => SetValue(!Value)));
button.OnClick.AddListener(() => SetValue(!Value));
}
}
+2 -2
View File
@@ -105,9 +105,9 @@ public class OptionFloatItem : OptionItem<float>
numberOption.DestroyTextTranslators();
numberOption.TitleText.text = Name;
numberOption.PlusBtn.OnClick = new();
numberOption.PlusBtn.OnClick.AddListener((Action)(() => Increase()));
numberOption.PlusBtn.OnClick.AddListener(Increase);
numberOption.MinusBtn.OnClick = new();
numberOption.MinusBtn.OnClick.AddListener((Action)(() => Decrease()));
numberOption.MinusBtn.OnClick.AddListener(Decrease);
}
}
+2 -2
View File
@@ -105,9 +105,9 @@ public sealed class OptionIntItem : OptionItem<int>
numberOption.DestroyTextTranslators();
numberOption.TitleText.text = Name;
numberOption.PlusBtn.OnClick = new();
numberOption.PlusBtn.OnClick.AddListener((Action)(() => Increase()));
numberOption.PlusBtn.OnClick.AddListener(Increase);
numberOption.MinusBtn.OnClick = new();
numberOption.MinusBtn.OnClick.AddListener((Action)(() => Decrease()));
numberOption.MinusBtn.OnClick.AddListener(Decrease);
}
}
+2 -2
View File
@@ -299,14 +299,14 @@ public abstract class OptionItem
button.interactableClickColor = Color.white;
button.buttonSprite.sprite = Utils.LoadSprite("BetterAmongUs.Resources.Images.QuestionMark.png", 50);
button.OnClick = new();
button.OnClick.AddListener((Action)(() =>
button.OnClick.AddListener(() =>
{
var menu = GameSettingMenu.Instance;
if (menu != null)
{
menu.MenuDescriptionText.text = text;
}
}));
});
}
/// <summary>
+2 -2
View File
@@ -107,9 +107,9 @@ public sealed class OptionPlayerItem : OptionItem<int>
numberOption.DestroyTextTranslators();
numberOption.TitleText.text = Name;
numberOption.PlusBtn.OnClick = new();
numberOption.PlusBtn.OnClick.AddListener((Action)(() => Increase()));
numberOption.PlusBtn.OnClick.AddListener(Increase);
numberOption.MinusBtn.OnClick = new();
numberOption.MinusBtn.OnClick.AddListener((Action)(() => Decrease()));
numberOption.MinusBtn.OnClick.AddListener(Decrease);
}
}
+3 -2
View File
@@ -1,4 +1,5 @@
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Patches.Gameplay.UI.Settings;
namespace BetterAmongUs.Modules.OptionItems;
@@ -33,7 +34,7 @@ internal sealed class OptionPresetItem : OptionStringItem
Item.TranslatorStrings = Enumerable.Repeat(string.Empty, 10).ToArray();
Item.Range = new IntRange(0, 10);
Item.DefaultValue = 0;
Item.Value = BAUPlugin.SettingsPreset.Value;
Item.Value = BAUConfigs.SettingsPreset.Value;
Item.CreateBehavior();
return Item;
@@ -41,7 +42,7 @@ internal sealed class OptionPresetItem : OptionStringItem
internal override void OnValueChange(int oldValue, int newValue)
{
BAUPlugin.SettingsPreset.Value = newValue;
BAUConfigs.SettingsPreset.Value = newValue;
BetterDataManager.BetterGameSettingsFile = new();
BetterDataManager.BetterGameSettingsFile.Init();
foreach (var opt in AllOptions)
+2 -2
View File
@@ -110,9 +110,9 @@ public class OptionStringItem : OptionItem<int>
numberOption.DestroyTextTranslators();
numberOption.TitleText.text = Name;
numberOption.PlusBtn.OnClick = new();
numberOption.PlusBtn.OnClick.AddListener((Action)(() => Increase()));
numberOption.PlusBtn.OnClick.AddListener(Increase);
numberOption.MinusBtn.OnClick = new();
numberOption.MinusBtn.OnClick.AddListener((Action)(() => Decrease()));
numberOption.MinusBtn.OnClick.AddListener(Decrease);
}
}
+2 -2
View File
@@ -121,10 +121,10 @@ internal sealed class OptionTab
SettingsButton.gameObject.GetComponent<BoxCollider2D>().size = new Vector2(2.5f, 0.6176f);
SettingsButton.OnClick.AddListener(new Action(() =>
SettingsButton.OnClick.AddListener(() =>
{
GameSettingMenu.Instance.ChangeTab(Id, false);
}));
});
var SettingsTab = UnityEngine.Object.Instantiate(GameSettingMenu.Instance.GameSettingsTab, GameSettingMenu.Instance.GameSettingsTab.transform.parent);
AUTab = SettingsTab;
+3 -2
View File
@@ -1,3 +1,4 @@
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using System.Globalization;
@@ -205,7 +206,7 @@ internal static class Translator
}
var langId = TranslationController.InstanceExists ? TranslationController.Instance.currentLanguage.languageID : SupportedLangs.English;
if (useConsoleLanguage) langId = SupportedLangs.English;
if (BAUPlugin.ForceOwnLanguage.Value) langId = GetUserSystemLanguage();
if (BAUConfigs.ForceOwnLanguage.Value) langId = GetUserSystemLanguage();
string str = GetString(key, langId, showInvalid);
if (formatting != null)
str = string.Format(str, formatting);
@@ -284,7 +285,7 @@ internal static class Translator
internal static SupportedLangs GetTargetLanguageId(bool useConsoleLanguage = false)
{
if (useConsoleLanguage) return SupportedLangs.English;
if (BAUPlugin.ForceOwnLanguage.Value) return GetUserSystemLanguage();
if (BAUConfigs.ForceOwnLanguage.Value) return GetUserSystemLanguage();
return TranslationController.InstanceExists ?
TranslationController.Instance.currentLanguage.languageID :
+7 -1
View File
@@ -1,4 +1,5 @@
using UnityEngine;
using BetterAmongUs.Data.Config;
using UnityEngine;
namespace BetterAmongUs.Modules;
@@ -109,6 +110,11 @@ internal static class VentGroups
/// </returns>
internal static Color GetVentGroupColor(Vent vent)
{
if (!BAUConfigs.VentColorGroups.Value)
{
return Color.gray;
}
if (ventToLowestId.TryGetValue(vent.Id, out int lowestId))
{
return groupColors[lowestId];
+3 -2
View File
@@ -1,4 +1,5 @@
using BetterAmongUs.Helpers;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using System.Text;
using TMPro;
@@ -71,7 +72,7 @@ internal sealed class BetterPingTracker : MonoBehaviour
sb.Append($"<color=#00dbdb><size=75%>BetterAmongUs {BAUPlugin.GetVersionText(true)}</size></color>\n");
sb.Append($"<color=#8A8A8A>{ModInfo.GITHUB}</color>\n".Size(52f));
if (BAUPlugin.ShowFPS.Value)
if (BAUConfigs.ShowFPS.Value)
{
float FPSNum = 1.0f / Time.deltaTime;
sb.AppendFormat("<color=#0dff00><size=75%>FPS: <b>{0}</b></size></color>\n", (int)FPSNum);
+5 -1
View File
@@ -1,6 +1,7 @@
using AmongUs.Data;
using AmongUs.GameOptions;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Patches.Gameplay.UI.Settings;
@@ -163,7 +164,7 @@ internal class PlayerInfoDisplay : MonoBehaviour
return;
}
if (!BAUPlugin.LobbyPlayerInfo.Value && GameState.IsLobby)
if (!BAUConfigs.LobbyPlayerInfo.Value && GameState.IsLobby)
{
ResetText();
_player.RawSetName(_player.Data.PlayerName);
@@ -379,6 +380,9 @@ internal class PlayerInfoDisplay : MonoBehaviour
[HideFromIl2Cpp]
private void SetLobbyInfo(ref string newName, ExtendedPlayerInfo betterData, StringBuilder sbTag)
{
if (_player.IsHost() && BAUConfigs.LobbyPlayerInfo.Value)
newName = _player.GetPlayerNameAndColor();
if ((_player.IsLocalPlayer() || betterData.IsBetterUser) && !GameState.IsInGamePlay)
{
string verificationSymbol = betterData.IsVerifiedBetterUser || _player.IsLocalPlayer() ? "✓ " : "";
+3 -2
View File
@@ -1,4 +1,5 @@
using BepInEx.Unity.IL2CPP.Utils;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
using BetterAmongUs.Modules;
@@ -27,7 +28,7 @@ internal static class ClientPatch
private static bool SignInStatusComponent_SetOnline_Prefix(SignInStatusComponent __instance)
{
// Get supported Among Us versions for BAU
var varSupportedVersions = BAUPlugin.SupportedAmongUsVersions;
var varSupportedVersions = ModInfo.SupportedAmongUsVersions;
Version currentVersion = new(BAUPlugin.AppVersion);
Version firstSupportedVersion = new(varSupportedVersions.First());
Version lastSupportedVersion = new(varSupportedVersions.Last());
@@ -101,7 +102,7 @@ internal static class ClientPatch
private static void AmongUsClient_CoStartGame_Postfix(AmongUsClient __instance)
{
// Clear in-game chat if chat feature is enabled
if (BAUPlugin.ChatInGameplay.Value)
if (BAUConfigs.ChatInGameplay.Value)
{
ChatPatch.ClearChat();
}
+46 -23
View File
@@ -1,5 +1,6 @@
using BepInEx;
using BepInEx;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
using BetterAmongUs.Modules;
@@ -39,30 +40,52 @@ internal static class OptionsMenuBehaviourPatch
ClientOptionItem.ClientOptions.Clear();
// Toggle options with config binding
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.AntiCheat"), BAUPlugin.AntiCheat, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.SendBetterRpc"), BAUPlugin.SendBetterRpc, __instance, SendBetterRpcAction);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.BetterNotifications"), BAUPlugin.BetterNotifications, __instance, ClearNotifications);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ForceOwnLanguage"), BAUPlugin.ForceOwnLanguage, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ChatDarkMode"), BAUPlugin.ChatDarkMode, __instance, ChatPatch.SetChatTheme);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ChatInGame"), BAUPlugin.ChatInGameplay, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.LobbyInfo"), BAUPlugin.LobbyPlayerInfo, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.LobbyTheme"), BAUPlugin.DisableLobbyTheme, __instance, ToggleLobbyTheme);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.UnlockFPS"), BAUPlugin.UnlockFPS, __instance, UpdateFrameRate);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ShowFPS"), BAUPlugin.ShowFPS, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.AntiCheat"), BAUConfigs.AntiCheat, 1, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.SendBetterRpc"), BAUConfigs.SendBetterRpc, 1, __instance, SendBetterRpcAction);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.BetterNotifications"), BAUConfigs.BetterNotifications, 1, __instance, ClearNotifications);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ForceOwnLanguage"), BAUConfigs.ForceOwnLanguage, 1, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ChatDarkMode"), BAUConfigs.ChatDarkMode, 1, __instance, ChatPatch.SetChatTheme);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ChatInGame"), BAUConfigs.ChatInGameplay, 1, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.LobbyInfo"), BAUConfigs.LobbyPlayerInfo, 1, __instance);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.LobbyTheme"), BAUConfigs.DisableLobbyTheme, 1, __instance, ToggleLobbyTheme);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.UnlockFPS"), BAUConfigs.UnlockFPS, 1, __instance, UpdateFrameRate);
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.ShowFPS"), BAUConfigs.ShowFPS, 1, __instance);
// Button options (no toggle)
ClientOptionItem.CreateButton(Translator.GetString("BetterOption.SaveData"), __instance, OpenSaveData, () =>
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.VentColorGroups"), BAUConfigs.VentColorGroups, 2, __instance, toggleCheck: () =>
{
// Only allow opening save data in lobby/main menu, not during gameplay
bool cannotOpen = GameState.IsInGame && !GameState.IsLobby;
if (cannotOpen)
bool cannotToggle = GameState.IsInGame;
if (cannotToggle)
{
BetterNotificationManager.Notify($"Cannot open save data while in gameplay!", 2.5f);
BetterNotificationManager.Notify($"Unable to toggle while in gameplay!", 2.5f);
}
return !cannotOpen;
return !cannotToggle;
});
ClientOptionItem.CreateToggle(Translator.GetString("BetterOption.MinimapIcons"), BAUConfigs.MinimapIcons, 2, __instance, toggleCheck: () =>
{
bool cannotToggle = GameState.IsInGame;
if (cannotToggle)
{
BetterNotificationManager.Notify($"Unable to toggle while in gameplay!", 2.5f);
}
return !cannotToggle;
});
ClientOptionItem.CreateButton(Translator.GetString("BetterOption.ToVanilla"), __instance, SwitchToVanilla, () =>
// Button options (no toggle)
if (!ModInfo.Starlight)
{
ClientOptionItem.CreateButton(Translator.GetString("BetterOption.SaveData"), -1, __instance, OpenSaveData, () =>
{
// Only allow opening save data in lobby/main menu, not during gameplay
bool cannotOpen = GameState.IsInGame && !GameState.IsLobby;
if (cannotOpen)
{
BetterNotificationManager.Notify($"Cannot open save data while in gameplay!", 2.5f);
}
return !cannotOpen;
});
}
ClientOptionItem.CreateButton(Translator.GetString("BetterOption.ToVanilla"), -1, __instance, SwitchToVanilla, () =>
{
// Prevent switching to vanilla while in a game
bool cannotSwitch = GameState.IsInGame;
@@ -107,7 +130,7 @@ internal static class OptionsMenuBehaviourPatch
private static void ToggleLobbyTheme()
{
// Play lobby theme music if re-enabled while in lobby
if (GameState.IsLobby && !BAUPlugin.DisableLobbyTheme.Value)
if (GameState.IsLobby && !BAUConfigs.DisableLobbyTheme.Value)
{
SoundManager.instance.CrossFadeSound("MapTheme", LobbyBehaviour.Instance.MapTheme, 0.5f, 1.5f);
}
@@ -116,7 +139,7 @@ internal static class OptionsMenuBehaviourPatch
internal static void UpdateFrameRate()
{
// Toggle between 60 FPS (default) and 165 FPS
Application.targetFrameRate = BAUPlugin.UnlockFPS.Value ? 999 : 60;
Application.targetFrameRate = BAUConfigs.UnlockFPS.Value ? 999 : 60;
}
private static void OpenSaveData()
@@ -158,11 +181,11 @@ internal static class OptionsMenuBehaviourPatch
var index = __instance.Tabs.Length - 1;
var button = tab.GetComponent<PassiveButton>();
button.OnClick = new();
button.OnClick.AddListener((Action)(() =>
button.OnClick.AddListener(() =>
{
tab.Rollover.SetEnabledColors();
__instance.OpenTabGroup(index);
}));
});
return tab;
}
+8 -7
View File
@@ -1,4 +1,5 @@
using BetterAmongUs.Helpers;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Modules.Support;
using HarmonyLib;
@@ -21,7 +22,7 @@ internal static class PrivateLobbyPatch
// Check if other mods have disabled private lobby feature
if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_PrivateLobby))
{
BAUPlugin.PrivateOnlyLobby.Value = false;
BAUConfigs.PrivateOnlyLobby.Value = false;
return;
}
@@ -48,7 +49,7 @@ internal static class PrivateLobbyPatch
{
onButton.gameObject.SetActive(false);
onButton.OnClick = new();
onButton.OnClick.AddListener((Action)(() => TogglePrivateOnlyLobby(true)));
onButton.OnClick.AddListener(() => TogglePrivateOnlyLobby(true));
}
// Set up OFF button click handler
@@ -57,7 +58,7 @@ internal static class PrivateLobbyPatch
{
offButton.gameObject.SetActive(false);
offButton.OnClick = new();
offButton.OnClick.AddListener((Action)(() => TogglePrivateOnlyLobby(false)));
offButton.OnClick.AddListener(() => TogglePrivateOnlyLobby(false));
}
// Position toggle at top-right of create game screen
@@ -77,7 +78,7 @@ internal static class PrivateLobbyPatch
}
// Initialize toggle state from saved configuration
TogglePrivateOnlyLobby(BAUPlugin.PrivateOnlyLobby.Value);
TogglePrivateOnlyLobby(BAUConfigs.PrivateOnlyLobby.Value);
}
private static void TogglePrivateOnlyLobby(bool modeOn)
@@ -103,7 +104,7 @@ internal static class PrivateLobbyPatch
}
// Save setting to configuration
BAUPlugin.PrivateOnlyLobby.Value = modeOn;
BAUConfigs.PrivateOnlyLobby.Value = modeOn;
}
[HarmonyPatch(typeof(LobbyInfoPane), nameof(LobbyInfoPane.Update))]
@@ -111,7 +112,7 @@ internal static class PrivateLobbyPatch
private static void LobbyInfoPane_Update_Postfix(LobbyInfoPane __instance)
{
// Enforce private lobby setting if enabled by host
if (BAUPlugin.PrivateOnlyLobby.Value && !GameState.IsLocalGame && GameState.IsHost)
if (BAUConfigs.PrivateOnlyLobby.Value && !GameState.IsLocalGame && GameState.IsHost)
{
// Ensure game stays private
if (AmongUsClient.Instance.IsGamePublic)
@@ -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");
}
}
}
}
}
@@ -1,4 +1,5 @@
using BetterAmongUs.Helpers;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
using BetterAmongUs.Modules;
using BetterAmongUs.Modules.Support;
@@ -15,7 +16,7 @@ internal class PlatformSpoofPatch
[HarmonyPostfix]
internal static void PlatformSpecificData_Deserialize_Postfix(PlatformSpecificData __instance)
{
if (!BAUPlugin.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !GameState.IsVanillaServer) return;
if (!BAUConfigs.AntiCheat.Value || BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_Anticheat) || !GameState.IsVanillaServer) return;
if (GameState.IsLobby)
{
@@ -27,10 +28,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 +43,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 +58,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 +73,4 @@ internal class PlatformSpoofPatch
catch { }
}
}
}
}
+23 -9
View File
@@ -1,4 +1,5 @@
using BetterAmongUs.Helpers;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Modules.OptionItems;
using BetterAmongUs.Modules.Support;
@@ -14,15 +15,16 @@ 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()
{
if (BAUPlugin.DisableLobbyTheme.Value)
if (BAUConfigs.DisableLobbyTheme.Value)
SoundManager.instance.StopSound(LobbyBehaviour.Instance.MapTheme);
}
@@ -30,9 +32,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 +45,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 +72,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 +87,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 +116,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 +124,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 +135,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");
}
}
}
@@ -1,4 +1,5 @@
using BetterAmongUs.Helpers;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Managers;
using BetterAmongUs.Modules;
using HarmonyLib;
@@ -88,7 +89,7 @@ internal static class HudManagerPatch
// Manage in-game chat visibility based on settings and game state
if (GameState.InGame)
{
if (!BAUPlugin.ChatInGameplay.Value)
if (!BAUConfigs.ChatInGameplay.Value)
{
// Vanilla chat behavior: only show chat when dead or during meetings
if (!PlayerControl.LocalPlayer.IsAlive())
@@ -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]);
}
@@ -1,5 +1,7 @@
using BepInEx.Unity.IL2CPP.Utils.Collections;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Modules.OptionItems;
using BetterAmongUs.Mono;
using HarmonyLib;
@@ -26,13 +28,15 @@ 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)
{
if (BAUPlugin.FavoriteColor.Value >= 0 && player.cosmetics.ColorId != (byte)BAUPlugin.FavoriteColor.Value)
if (BAUConfigs.FavoriteColor.Value >= 0 && player.cosmetics.ColorId != (byte)BAUConfigs.FavoriteColor.Value)
{
// Send command to server to change color
player.CmdCheckColor((byte)BAUPlugin.FavoriteColor.Value);
player.CmdCheckColor((byte)BAUConfigs.FavoriteColor.Value);
}
}
@@ -1,4 +1,5 @@
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Mono;
@@ -77,8 +78,8 @@ internal static class PlayerJoinAndLeftPatch
// Reclaim favorite color when player leaves in lobby
if (GameState.IsLobby)
{
var favColorId = (byte)BAUPlugin.FavoriteColor.Value;
if (BAUPlugin.FavoriteColor.Value >= 0)
var favColorId = (byte)BAUConfigs.FavoriteColor.Value;
if (BAUConfigs.FavoriteColor.Value >= 0)
{
if (PlayerControl.LocalPlayer.cosmetics.ColorId != favColorId && data.ColorId == favColorId)
{
+3 -1
View File
@@ -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;
}
}
}
+2 -1
View File
@@ -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);
}
}
}
+6 -3
View File
@@ -1,4 +1,5 @@
using BetterAmongUs.Modules;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Modules;
using BetterAmongUs.Modules.Support;
using HarmonyLib;
using UnityEngine;
@@ -12,8 +13,10 @@ internal static class VentPatch
[HarmonyPrefix]
private static bool Vent_SetOutline_Prefix(Vent __instance, bool on, bool mainTarget)
{
if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_VentColorGroups)) return true;
// Skip if vent color groups are disabled - use vanilla behavior
if (!BAUConfigs.VentColorGroups.Value || 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 +24,4 @@ internal static class VentPatch
return false;
}
}
}
@@ -1,4 +1,5 @@
using BetterAmongUs.Commands;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Enums;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
@@ -13,7 +14,7 @@ namespace BetterAmongUs.Patches.Gameplay.UI.Chat;
internal static class ChatCommandsPatch
{
private static bool _enabled = true;
internal static string CommandPrefix => BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Force_BAU_Command_Prefix) ? "bau:" : BAUPlugin.CommandPrefix.Value;
internal static string CommandPrefix => BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Force_BAU_Command_Prefix) ? "bau:" : BAUConfigs.CommandPrefix.Value;
// Execute command when valid command is typed
private static void HandleCommand()
@@ -13,6 +13,7 @@ internal static class ChatNotificationPatch
{
if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_BetterPingTracker)) return;
// Reposition to top left
__instance.GetComponent<AspectPosition>().DistanceFromEdge = new Vector3(-2.8f, 0.3f, -40f);
__instance.transform.localScale = new Vector3(0.45f, 0.42f, 1f);
}
+4 -3
View File
@@ -1,5 +1,6 @@
using AmongUs.GameOptions;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Mono;
@@ -73,7 +74,7 @@ internal static class ChatPatch
private static void ChatController_Update_Prefix(ChatController __instance)
{
// Apply dark/light theme to chat input field
if (BAUPlugin.ChatDarkMode.Value)
if (BAUConfigs.ChatDarkMode.Value)
{
// Free chat color
__instance.freeChatField.background.color = new Color32(40, 40, 40, byte.MaxValue);
@@ -211,7 +212,7 @@ internal static class ChatPatch
{
var chat = HudManager.Instance.Chat;
if (BAUPlugin.ChatDarkMode.Value)
if (BAUConfigs.ChatDarkMode.Value)
{
// Quick chat color
chat.quickChatField.background.color = new Color32(40, 40, 40, byte.MaxValue);
@@ -240,7 +241,7 @@ internal static class ChatPatch
{
var chatBubble = asChatBubble;
if (BAUPlugin.ChatDarkMode.Value)
if (BAUConfigs.ChatDarkMode.Value)
{
chatBubble.transform.Find("ChatText (TMP)").GetComponentInChildren<TextMeshPro>(true).color = Color.white;
chatBubble.transform.Find("Background").GetComponentInChildren<SpriteRenderer>(true).color = new Color(0.15f, 0.15f, 0.15f, 1f);
@@ -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");
}
}
}
+30 -9
View File
@@ -20,20 +20,24 @@ internal static class GameSummaryPatch
if (__instance == null)
return;
// 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);
@@ -43,9 +47,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();
@@ -53,6 +59,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(
@@ -63,6 +70,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>()
: Camera.main;
@@ -79,6 +87,7 @@ internal static class GameSummaryPatch
return summaryObject;
}
// Sets text properties for the summary
private static void ConfigureSummaryText(TextMeshPro text)
{
text.autoSizeTextContainer = false;
@@ -88,59 +97,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 $"<align=\"center\"><size=150%> {Translator.GetString("GameSummary")}</size></align>" +
@@ -148,6 +164,7 @@ internal static class GameSummaryPatch
$"\n<size=60%>\n{Translator.GetString("Game.Summary.By")} {winTag}</size>";
}
// Sorts players for display: disconnected first, then dead, then alive
private static NetworkedPlayerInfo[] GetSortedPlayers()
{
return GameData.Instance.AllPlayers
@@ -158,6 +175,7 @@ internal static class GameSummaryPatch
.ToArray();
}
// Builds the complete player list text
private static StringBuilder BuildPlayerList()
{
var playersData = GetSortedPlayers();
@@ -173,6 +191,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 = $"<color={Utils.Color32ToHex(Palette.PlayerColors[playerData.DefaultOutfit.ColorId])}>{playerData.BetterData().RealName}</color>";
@@ -182,6 +201,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);
@@ -200,6 +220,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)
@@ -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);
+2 -1
View File
@@ -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;
}
}
}
+6 -2
View File
@@ -1,3 +1,4 @@
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Mono;
@@ -17,6 +18,7 @@ internal static class MeetingHudPatch
{
if (__instance == null || __instance.playerStates == null) return;
// Add meeting info display to each player state
foreach (var pva in __instance.playerStates)
{
if (pva == null) continue;
@@ -45,6 +47,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;
@@ -64,7 +67,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)
@@ -79,7 +82,8 @@ internal static class MeetingHudPatch
timeOpen = 0f;
Logger_.LogHeader("Meeting Has Ended");
if (BAUPlugin.ChatInGameplay.Value && !GameState.IsFreePlay && PlayerControl.LocalPlayer.IsAlive())
// Clear chat when meeting ends if gameplay chat is enabled
if (BAUConfigs.ChatInGameplay.Value && !GameState.IsFreePlay && PlayerControl.LocalPlayer.IsAlive())
{
ChatPatch.ClearPlayerChats();
}
@@ -1,4 +1,5 @@
using BetterAmongUs.Helpers;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules;
using BetterAmongUs.Modules.Support;
using BetterAmongUs.Mono;
@@ -10,6 +11,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 +19,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<IHudOverrideTask>(PlayerControl.LocalPlayer) ? Palette.DisabledGrey : new Color(0.2f, 0.5f, 0f, 1f));
// Green background normally, gray if comms sabotaged
=> __instance.BackgroundColor.SetColor(PlayerTask.PlayerHasTaskOfType<IHudOverrideTask>(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<IHudOverrideTask>(PlayerControl.LocalPlayer))
{
// Comms sabotaged - disable map
__instance.isSab = true;
__instance.BackgroundColor.SetColor(Palette.DisabledGrey);
__instance.SabotageText.gameObject.SetActive(true);
}
else if (!PlayerTask.PlayerHasTaskOfType<IHudOverrideTask>(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,24 +61,27 @@ 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]
private static void MapBehaviour_Show_Postfix(MapBehaviour __instance)
{
if (BAUModdedSupportFlags.HasFlag(BAUModdedSupportFlags.Disable_MinimapIcons)) return;
if (!BAUConfigs.MinimapIcons.Value || 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 +91,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<MapConsole>())
{
CreateUsableIcon(map.Cast<IUsable>());
}
// Add system console icons
foreach (var system in UnityEngine.Object.FindObjectsOfType<SystemConsole>())
{
CreateSystemConsoleIcon(system);
}
// Add zipline icons
foreach (var zipline in UnityEngine.Object.FindObjectsOfType<ZiplineConsole>())
{
var icon = CreateIcon(zipline.image.sprite, "SystemIcon");
@@ -105,16 +118,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<Console>())
{
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>();
animatedMapIcon.ShouldAnimate += () =>
{
@@ -127,6 +144,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 +153,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 +169,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 +190,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 +205,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 +221,10 @@ internal static class MiniMapBehaviourPatch
}
}
// Creates icon for usables
private static void CreateUsableIcon(IUsable usable)
{
// Special handling for emergency buttons
if (usable.TryCast<SystemConsole>(out var systemConsole))
{
if (systemConsole.MinigamePrefab.name == "EmergencyMinigame")
@@ -213,11 +239,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 +254,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 +268,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 +278,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);
}
}
}
+89 -24
View File
@@ -1,4 +1,6 @@
using BetterAmongUs.Data;
using AmongUs.Data;
using BetterAmongUs.Data;
using BetterAmongUs.Data.Config;
using BetterAmongUs.Data.Json;
using BetterAmongUs.Helpers;
using BetterAmongUs.Patches.Client.Managers;
@@ -11,87 +13,122 @@ namespace BetterAmongUs.Patches.Gameplay.UI;
[HarmonyPatch]
internal static class PlayerTabPatch
{
private static List<PassiveButton> presetButtons = [];
private static float cooldown = 0f;
private static List<PassiveButton> presetButtons = []; // Outfit preset buttons
private static List<PoolablePlayer> presetPreviews = []; // Outfit previews
private static float cooldown = 0f; // Button click cooldown
private static readonly List<SpriteRenderer> _favoriteIcons = [];
[HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.OnEnable))]
[HarmonyPrefix]
private static void PlayerTab_OnEnable_Prefix(PlayerTab __instance)
{
SetupOutfitPresets(__instance);
}
[HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.OnEnable))]
[HarmonyPostfix]
private static void PlayerTab_OnEnable_Postfix(PlayerTab __instance)
{
SetupFavoriteColor(__instance);
}
private static void SetupOutfitPresets(PlayerTab playerTab)
{
// Clean up old preset buttons and previews
foreach (var button in presetButtons.ToArray())
{
if (button == null) continue;
UnityEngine.Object.Destroy(button.gameObject);
}
presetButtons.Clear();
foreach (var preview in presetPreviews.ToArray())
{
if (preview == null) continue;
UnityEngine.Object.Destroy(preview.gameObject);
}
presetPreviews.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), () =>
var data = OutfitData.GetOutfitData(currentI);
var button = playerTab.CreateOutfitPresetButton(name, new Vector3(2.5f, 1.55f - currentI * 0.45f, 0f), out var playerPreview, () =>
{
// 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();
}
var data = OutfitData.GetOutfitData(currentI);
// Load and apply outfit from preset
data.Load(() =>
{
if (LoadPlayerOutfit(data))
{
__instance.PlayerPreview.UpdateFromLocalPlayer(PlayerMaterial.MaskType.None);
playerTab.PlayerPreview.UpdateFromLocalPlayer(PlayerMaterial.MaskType.None);
}
else
{
__instance.PlayerPreview.UpdateFromDataManager(PlayerMaterial.MaskType.None);
playerTab.PlayerPreview.UpdateFromDataManager(PlayerMaterial.MaskType.None);
}
});
});
// Set up preview
playerPreview.UpdateFromPlayerOutfit(data.ToPlayerOutfit(), PlayerMaterial.MaskType.None, false, true);
playerPreview.ToggleName(false);
playerPreview.transform.position += new Vector3(0f, 0f, -1f * currentI); // Fix rendering order
presetPreviews.Add(playerPreview);
presetButtons.Add(button);
}
}
private static readonly List<SpriteRenderer> _favoriteIcons = [];
[HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.OnEnable))]
[HarmonyPostfix]
private static void PlayerTab_OnEnable_Postfix(PlayerTab __instance)
private static void SetupFavoriteColor(PlayerTab playerTab)
{
_favoriteIcons.Clear();
for (int i = 0; i < __instance.ColorChips.Count; i++)
// Add favorite functionality to color chips
for (int i = 0; i < playerTab.ColorChips.Count; i++)
{
var index = i;
var colorChip = __instance.ColorChips[i];
var colorChip = playerTab.ColorChips[i];
// Override click behavior
colorChip.Button.OnClick = new();
colorChip.Button.OnClick.AddListener((Action)(() =>
colorChip.Button.OnClick.AddListener(() =>
{
// Shift+Click = toggle favorite
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
if (BAUPlugin.FavoriteColor.Value == index)
if (BAUConfigs.FavoriteColor.Value == index)
{
BAUPlugin.FavoriteColor.Value = -1;
BAUConfigs.FavoriteColor.Value = -1; // Remove favorite
}
else
{
BAUPlugin.FavoriteColor.Value = index;
BAUConfigs.FavoriteColor.Value = index; // Set favorite
}
UpdateFavorite();
return;
}
__instance.ClickEquip();
}));
// Normal click = equip color
playerTab.ClickEquip();
});
// Add favorite star indicator
var checkBox = colorChip.PlayerEquippedForeground.transform.Find("CheckMark").GetComponentInChildren<SpriteRenderer>();
var favoriteIcon = UnityEngine.Object.Instantiate(checkBox, colorChip.transform);
favoriteIcon.color = Color.yellow;
@@ -102,15 +139,17 @@ internal static class PlayerTabPatch
UpdateFavorite();
}
// Update favorite star visibility
private static void UpdateFavorite()
{
for (int i = 0; i < _favoriteIcons.Count; i++)
{
SpriteRenderer? fav = _favoriteIcons[i];
fav.gameObject.SetActive(i == BAUPlugin.FavoriteColor.Value);
fav.gameObject.SetActive(i == BAUConfigs.FavoriteColor.Value);
}
}
// Apply outfit to local player
private static bool LoadPlayerOutfit(OutfitData data)
{
var player = PlayerControl.LocalPlayer;
@@ -127,18 +166,35 @@ internal static class PlayerTabPatch
return false;
}
private static PassiveButton CreateButton(this PlayerTab __instance, string name, Vector3 pos, Action callback)
// Helper to create preset buttons
private static PassiveButton CreateOutfitPresetButton(this PlayerTab __instance, string name, Vector3 pos, out PoolablePlayer playerPreview, Action callback)
{
var button = UnityEngine.Object.Instantiate(MainMenuManagerPatch.ButtonPrefab, __instance.transform);
button.gameObject.SetActive(true);
button.gameObject.SetLayers("UI");
button.transform.localPosition = pos;
button.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
// Create preview
button.transform.Find("Highlight/Icon")?.gameObject.DestroyObj();
button.transform.Find("Inactive/Icon")?.gameObject.DestroyObj();
var outfitPreview = UnityEngine.Object.Instantiate(__instance.PlayerPreview);
outfitPreview.transform.SetParent(button.transform);
outfitPreview.transform.localPosition = new Vector3(-1.3f, 0.15f, -10f);
outfitPreview.transform.localScale = Vector3.one * 0.45f;
outfitPreview.ResetCosmetics();
foreach (var pet in outfitPreview.GetComponentsInChildren<PetBehaviour>(true))
{
pet.gameObject.DestroyObj();
}
playerPreview = outfitPreview;
button.OnClick = new();
button.OnClick.AddListener(callback);
button.DestroyTextTranslators();
var text = button.GetComponentInChildren<TextMeshPro>();
text?.SetText(name);
return button;
}
@@ -146,6 +202,7 @@ internal static class PlayerTabPatch
[HarmonyPrefix]
private static void PlayerTab_Updatee_Postfix(PlayerTab __instance)
{
// Handle cooldown
if (cooldown > 0f)
{
cooldown -= Time.deltaTime;
@@ -155,14 +212,22 @@ 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
}
// Update preview color
var preview = presetPreviews[i];
if (preview.Cosmetics.ColorId != DataManager.Player.Customization.Color)
{
preview.SetBodyColor(DataManager.Player.Customization.Color);
}
}
}
}
}
@@ -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");
@@ -1,4 +1,5 @@
using BetterAmongUs.Modules.Support;
using BetterAmongUs.Helpers;
using BetterAmongUs.Modules.Support;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using UnityEngine;
@@ -74,7 +75,7 @@ internal static class ServerDropdownPatch
new Il2CppReferenceArray<Il2CppSystem.Object>(0));
serverListButton.Text.ForceMeshUpdate(false, false);
serverListButton.Button.OnClick.RemoveAllListeners();
serverListButton.Button.OnClick.AddListener((Action)(() => __instance.ChooseOption(region)));
serverListButton.Button.OnClick.AddListener(() => __instance.ChooseOption(region));
__instance.controllerSelectable.Add(serverListButton.Button);
// Move to next column if current column is full
@@ -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, $"<color={RoleTypes.Impostor.GetRoleHex()}>{Translator.GetString(StringNames.ImpostorsCategory)}</color>");
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<PassiveButton>().enabled = false;
__instance.RoleSettingsButton.inactiveSprites.GetComponent<SpriteRenderer>().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<PassiveButton>().enabled = false;
__instance.GameSettingsButton.GetComponent<PassiveButton>().enabled = false;
__instance.RoleSettingsButton.GetComponent<PassiveButton>().enabled = false;
__instance.GamePresetsButton.inactiveSprites.GetComponent<SpriteRenderer>().color = new(0.5f, 0.5f, 0.5f, 1f);
__instance.GameSettingsButton.inactiveSprites.GetComponent<SpriteRenderer>().color = new(0.5f, 0.5f, 0.5f, 1f);
__instance.RoleSettingsButton.inactiveSprites.GetComponent<SpriteRenderer>().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
}
}
@@ -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;
}
}
}
+17 -5
View File
@@ -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);
}
}
}
}
+9 -3
View File
@@ -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;
}
}
}
+9 -1
View File
@@ -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<AsyncOperation>)(_ =>
{
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
}));
}
}
}
}
+3
View File
@@ -60,6 +60,9 @@
"BetterOption.LobbyTheme": "Disable Lobby Theme",
"BetterOption.UnlockFPS": "UnlockFPS",
"BetterOption.ShowFPS": "ShowFPS",
"BetterOption.VentColorGroups": "Vent Color Groups",
"BetterOption.MinimapIcons": "Minimap Icons",
"BetterOption.SaveData": "Open Save Data",
"BetterOption.ToVanilla": "Switch To Vanilla",