mirror of
https://github.com/D1GQ/BetterAmongUs.git
synced 2026-10-20 01:46:19 +00:00
Merge branch 'v1.3.3-dev' into main
This commit is contained in:
66 files changed
+907
-405
No files matched your search
+21
-114
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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<T0> 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<T0> 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<T0, T1> 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<T0, T1> 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<T0, T1, T2> 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<T0, T1, T2> 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<T0, T1, T2, T3> 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<T0, T1, T2, T3> 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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,6 @@ internal static class Utils
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Intentionally empty - chat failures shouldn't crash the game
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() ? "✓ " : "";
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
|
||||
Reference in new issue
Block a user