From 2e337ce6b8aa98d4f3569eb4cc1167005cb1a098 Mon Sep 17 00:00:00 2001 From: Ape <155788660+ApeMV@users.noreply.github.com> Date: Thu, 15 Jan 2026 23:18:45 +0100 Subject: [PATCH] MASSIVE database change, fix million things, add million things Please don't break the whole mod --- AmongUsRevamped.cs | 23 +++++- Modules/CustomOptionsHolder.cs | 20 +++-- Modules/FixedUpdate.cs | 2 + Modules/Utils.cs | 36 +++++++- Patches/ChatPatches.cs | 142 +++++++++++++++++++++++++------- Patches/CredentialsPatch.cs | 1 + Patches/DiscordPatch.cs | 50 +++++++++++ Patches/EndGamePatches.cs | 37 ++++++++- Patches/JoinAndLeavePatches.cs | 44 ++++++++-- Patches/OnGameStartedPatch.cs | 3 + Patches/PlayerControlPatches.cs | 15 +++- 11 files changed, 324 insertions(+), 49 deletions(-) create mode 100644 Patches/DiscordPatch.cs diff --git a/AmongUsRevamped.cs b/AmongUsRevamped.cs index 04a190b..e114411 100644 --- a/AmongUsRevamped.cs +++ b/AmongUsRevamped.cs @@ -29,7 +29,7 @@ public partial class Main : BasePlugin public static NormalGameOptionsV10 NormalOptions => GameOptionsManager.Instance != null ? GameOptionsManager.Instance.currentNormalGameOptions : null; public static bool HasArgumentException; public static string CredentialsText; - public const string ModVersion = "v1.1.1"; + public const string ModVersion = "v1.2.0"; public static float GameTimer; @@ -84,4 +84,25 @@ public partial class Main : BasePlugin LateTask.Update(Time.deltaTime); } } + + public enum ColorToString + { + Red = 0, + Blue = 1, + Green = 2, + Pink = 3, + Orange = 4, + Yellow = 5, + Black = 6, + White = 7, + Purple = 8, + Brown = 9, + Cyan = 10, + Lime = 11, + Maroon = 12, + Rose = 13, + Banana = 14, + Gray = 15, + Tan = 16 + } } \ No newline at end of file diff --git a/Modules/CustomOptionsHolder.cs b/Modules/CustomOptionsHolder.cs index 77334e6..bd2bb8d 100644 --- a/Modules/CustomOptionsHolder.cs +++ b/Modules/CustomOptionsHolder.cs @@ -41,6 +41,9 @@ namespace AmongUsRevamped public static OptionItem ApplyDenyNameList; public static OptionItem ApplyModeratorList; + public static OptionItem TabGroupAutomation; + + public static OptionItem AutoSendGameInfo; public static OptionItem AutoRejoinLobby; public static OptionItem AutoStartTimer; public static OptionItem WaitAutoStart; @@ -190,8 +193,8 @@ namespace AmongUsRevamped .SetColor(Color.green) .SetHeader(true); - TabGroupMain = TextOptionItem.Create(60000, "Main", TabGroup.SystemSettings) - .SetColor(Color.yellow); + TabGroupMain = TextOptionItem.Create(60000, "Moderation", TabGroup.SystemSettings) + .SetColor(Color.blue); KickLowLevelPlayer = IntegerOptionItem.Create(60050, "Kick Players Under Level", new(0, 100, 1), 0, TabGroup.SystemSettings, false) .SetValueFormat(OptionFormat.Level); @@ -206,18 +209,21 @@ namespace AmongUsRevamped ApplyDenyNameList = BooleanOptionItem.Create(60120, "Apply DenyName List", true, TabGroup.SystemSettings, false); ApplyModeratorList = BooleanOptionItem.Create(60121, "Apply Moderator List", true, TabGroup.SystemSettings, false); + TabGroupAutomation = TextOptionItem.Create(60149, "Automation", TabGroup.SystemSettings) + .SetColor(Color.yellow); + + AutoSendGameInfo = BooleanOptionItem.Create(60150, "Automatically send winner info", true, TabGroup.SystemSettings, false); AutoRejoinLobby = BooleanOptionItem.Create(60210, "Auto Rejoin Lobby", false, TabGroup.SystemSettings, false); - AutoStartTimer = IntegerOptionItem.Create(44420, "Countdown For Auto Start", new(1, 600, 1), 5, TabGroup.SystemSettings, false) + AutoStartTimer = IntegerOptionItem.Create(64420, "Countdown For Auto Start", new(1, 600, 1), 5, TabGroup.SystemSettings, false) .SetValueFormat(OptionFormat.Seconds); - WaitAutoStart = IntegerOptionItem.Create(44421, "Auto Start After", new(10, 600, 10), 300, TabGroup.SystemSettings, false) + WaitAutoStart = IntegerOptionItem.Create(64421, "Auto Start After", new(10, 600, 10), 300, TabGroup.SystemSettings, false) .SetValueFormat(OptionFormat.Seconds); - PlayerAutoStart = IntegerOptionItem.Create(44422, "Players Needed To Auto Start", new(1, 15, 1), 1, TabGroup.SystemSettings, false); + PlayerAutoStart = IntegerOptionItem.Create(64422, "Players Needed To Auto Start", new(1, 15, 1), 1, TabGroup.SystemSettings, false); NoGameEnd = BooleanOptionItem.Create(60380, "No Game End", false, TabGroup.SystemSettings, false) .SetColor(Color.red) .SetHeader(true); - // Gamemode Settings TabGroupHNS = TextOptionItem.Create(70000, "Hide and Seek", TabGroup.GamemodeSettings) .SetColor(Color.green); @@ -264,7 +270,7 @@ namespace AmongUsRevamped .SetColor(new Color32(255, 255, 153, byte.MaxValue)) .SetParent(OverrideTaskSettings); - DisableMiraTasks = BooleanOptionItem.Create(23000, "Disable Mira Tasks", false, TabGroup.ModSettings, false) + DisableMiraTasks = BooleanOptionItem.Create(23000, "Disable Mira HQ Tasks", false, TabGroup.ModSettings, false) .SetColor(new Color32(173, 216, 230, byte.MaxValue)) .SetParent(OverrideTaskSettings); diff --git a/Modules/FixedUpdate.cs b/Modules/FixedUpdate.cs index 884d9d6..4615514 100644 --- a/Modules/FixedUpdate.cs +++ b/Modules/FixedUpdate.cs @@ -40,6 +40,7 @@ public static class FixedUpdate writer.Write((byte)GameOverReason.ImpostorDisconnect); AmongUsClient.Instance.FinishEndGame(writer); Logger.Info($" Crewmates won because the game took longer than {Options.CrewAutoWinsGameAfter.GetInt()}s", "SNSManager"); + NormalGameEndChecker.LastWinReason = $"★ Crewmates win! (Timer)\n\nImpostors:\n" + string.Join("\n", NormalGameEndChecker.imps.Select(p => p.Data.PlayerName)); } } // 3 = Speedrun @@ -55,6 +56,7 @@ public static class FixedUpdate writer.Write((byte)GameOverReason.CrewmatesByVote); AmongUsClient.Instance.FinishEndGame(writer); Logger.Info($" No one won because the game took longer than {Options.GameAutoEndsAfter.GetInt()}s", "SpeedrunManager"); + NormalGameEndChecker.LastWinReason = $"★ No one wins! (Timer)"; } } } diff --git a/Modules/Utils.cs b/Modules/Utils.cs index 75d073d..240dfa3 100644 --- a/Modules/Utils.cs +++ b/Modules/Utils.cs @@ -26,6 +26,8 @@ public static class Utils public static long TimeStamp => EpochStartSeconds + (long)Stopwatch.Elapsed.TotalSeconds; public static int allAlivePlayersCount => AllAlivePlayerControls.Count(); + public static int AliveCrewmates => AllAlivePlayerControls.Count(pc => !pc.Data.Role.IsImpostor); + public static int AliveImpostors => AllAlivePlayerControls.Count(pc => pc.Data.Role.IsImpostor); public static bool IsLobby => AmongUsClient.Instance.GameState == InnerNetClient.GameStates.Joined; public static bool InGame => AmongUsClient.Instance && AmongUsClient.Instance.GameState == InnerNetClient.GameStates.Started; @@ -198,7 +200,9 @@ public static class Utils OnGameJoinedPatch.AutoStartCheck = false; Main.GameTimer = 0f; MurderPlayerPatch.misfireCount.Clear(); - LateTask.Tasks.Clear(); + LateTask.Tasks.Clear(); + FixedUpdateInGamePatch.ProcessedModerators.Clear(); + NormalGameEndChecker.ImpCheckComplete = false; } public static PlayerControl[] AllAlivePlayerControls @@ -222,4 +226,34 @@ public static class Utils return result; } } + + public static void ShowLastResult(byte playerId = byte.MaxValue) + { + if (InGame) + { + Logger.SendInGame($"Hi, you're currently in-game. Let's use this command afterwards"); + return; + } + + if (string.IsNullOrEmpty(NormalGameEndChecker.LastWinReason)) + { + Logger.SendInGame($"Your command was canceled due to not having the required info"); + return; + } + + PlayerControl.LocalPlayer.RpcSendChat($"{NormalGameEndChecker.LastWinReason}"); + } + + public static bool TryGetColorId(string input, out byte colorId) + { + colorId = 0; + + if (Enum.TryParse(input, true, out var color)) + { + colorId = (byte)color; + return true; + } + + return false; + } } \ No newline at end of file diff --git a/Patches/ChatPatches.cs b/Patches/ChatPatches.cs index 6619a49..bf35be2 100644 --- a/Patches/ChatPatches.cs +++ b/Patches/ChatPatches.cs @@ -36,7 +36,16 @@ internal static class SendChatPatch return false; } - if (__instance.timeSinceLastMessage < 3f) return false; + if (__instance.timeSinceLastMessage < 3f || OnGameJoinedPatch.WaitingForChat) return false; + + if (text == "/l" || text == "/lastgame") + { + Utils.ShowLastResult(); + __instance.timeSinceLastMessage = 0.8f; + __instance.freeChatField.textArea.Clear(); + __instance.freeChatField.textArea.SetText(string.Empty); + return false; + } if (text == "/0kc" || text == "/0kcd" || text == "/0killcooldown") { @@ -56,6 +65,7 @@ internal static class SendChatPatch new LateTask(() => { + if (!Utils.IsLobby) return; PlayerControl.LocalPlayer.RpcSendChat($"Crew wins by tasks/surviving {Options.CrewAutoWinsGameAfter.GetInt()}s.\nImp wins by killing Crew.\n1 Wrong kill = Can't kill for {Options.CantKillTime.GetInt()}s.\n{Options.MisfiresToSuicide.GetInt()} Wrong kills = suicide."); __instance.timeSinceLastMessage = 0.8f; }, 2.2f, "SNSTutorial2"); @@ -71,46 +81,120 @@ internal static class SendChatPatch return false; } - else return true; - } -} - -[HarmonyPatch(typeof(PlayerControl), "RpcSendChat")] -public static class PC_RpcSendChat -{ - // Banning works by Name and not Id - public static void Prefix(PlayerControl __instance, ref string chatText) - { - - if (!AmongUsClient.Instance.AmHost) - return; - - bool isKick = chatText.StartsWith("/kick "); - bool isBan = chatText.StartsWith("/ban "); - - if (!isKick && !isBan) - return; - - if (__instance == PlayerControl.LocalPlayer || Utils.IsPlayerModerator(__instance.Data.FriendCode)) + else { - string kickedPlayerName = chatText.Substring(isBan ? 5 : 6).Trim(); + bool isKick = text.StartsWith("/kick "); + bool isBan = text.StartsWith("/ban "); + + bool isColorKick = text.StartsWith("/ckick "); + bool isColorBan = text.StartsWith("/cban "); + + bool banLog = isBan || isColorBan; + + if (!isKick && !isBan && !isColorKick && !isColorBan) + { + Logger.Info($" {PlayerControl.LocalPlayer.Data.PlayerName}: {text}", "SendChat"); + return true; + } + + string arg = text.Substring(isKick ? 6 : isBan ? 5 : isColorKick ? 7 : isColorBan ? 6 : 0).Trim(); PlayerControl target = null; foreach (PlayerControl p in PlayerControl.AllPlayerControls) { - if (p.Data != null && !Utils.IsPlayerModerator(p.Data.FriendCode) && p != PlayerControl.LocalPlayer && p.Data.PlayerName.Equals(kickedPlayerName, StringComparison.OrdinalIgnoreCase)) + if (p.Data == null || p == PlayerControl.LocalPlayer) continue; + + if ((isKick || isBan) && p.Data.PlayerName.Equals(arg, StringComparison.OrdinalIgnoreCase)) { target = p; break; - } + } + + if ((isColorKick || isColorBan) && Utils.TryGetColorId(arg, out byte colorId)) + { + if (p.Data.DefaultOutfit.ColorId == colorId) + { + target = p; + break; + } + } } - if (target == null) - return; + if (target != null) + { + AmongUsClient.Instance.KickPlayer(target.Data.ClientId, isBan || isColorBan); + Logger.Info($" {(banLog ? "banned" : "kicked")} {target.Data.PlayerName}", "Kick&BanCommand"); + __instance.freeChatField.textArea.Clear(); + __instance.freeChatField.textArea.SetText(string.Empty); + } + return false; + } + } +} - AmongUsClient.Instance.KickPlayer(target.Data.ClientId, isBan); - Logger.Info($" {__instance.Data.PlayerName} {(isBan ? "banned" : "kicked")} {target.Data.PlayerName}", "Kick&BanCommand"); +[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.HandleRpc))] +public static class RPCHandlerPatch +{ + public static void Prefix(PlayerControl __instance, byte callId, MessageReader reader) + { + if (!AmongUsClient.Instance.AmHost) return; + + var rpcType = (RpcCalls)callId; + MessageReader subReader = MessageReader.Get(reader); + + switch (rpcType) + { + case RpcCalls.SendChat: + { + string chatText = subReader.ReadString(); + Logger.Info($" {__instance.Data.PlayerName}: {chatText}", "SendChat"); + + // Banning works by name and color. Commands are seperated incase someone has a color as their name + bool isKick = chatText.StartsWith("/kick "); + bool isBan = chatText.StartsWith("/ban "); + + bool isColorKick = chatText.StartsWith("/ckick "); + bool isColorBan = chatText.StartsWith("/cban "); + + bool banLog = isBan || isColorBan; + + if (!isKick && !isBan && !isColorKick && !isColorBan) return; + + if (Utils.IsPlayerModerator(__instance.Data.FriendCode)) + { + string arg = chatText.Substring(isKick ? 6 : isBan ? 5 : isColorKick ? 7 : isColorBan ? 6 : 0).Trim(); + + PlayerControl target = null; + + foreach (PlayerControl p in PlayerControl.AllPlayerControls) + { + if (p.Data == null || p == PlayerControl.LocalPlayer || Utils.IsPlayerModerator(p.Data.FriendCode)) continue; + + if ((isKick || isBan) && p.Data.PlayerName.Equals(arg, StringComparison.OrdinalIgnoreCase)) + { + target = p; + break; + } + + if ((isColorKick || isColorBan) && Utils.TryGetColorId(arg, out byte colorId)) + { + if (p.Data.DefaultOutfit.ColorId == colorId) + { + target = p; + break; + } + } + } + + if (target != null) + { + AmongUsClient.Instance.KickPlayer(target.Data.ClientId, isBan || isColorBan); + Logger.Info($" {__instance.Data.PlayerName} {(banLog ? "banned" : "kicked")} {target.Data.PlayerName}", "Kick&BanCommand"); + } + } + break; + } } } } \ No newline at end of file diff --git a/Patches/CredentialsPatch.cs b/Patches/CredentialsPatch.cs index 9faa989..92f890d 100644 --- a/Patches/CredentialsPatch.cs +++ b/Patches/CredentialsPatch.cs @@ -52,6 +52,7 @@ namespace AmongUsRevamped private static void Postfix(VersionShower __instance) { Utils.ClearLeftoverData(); + NormalGameEndChecker.LastWinReason = ""; Main.CredentialsText = $"Among Us Revamped {Main.ModVersion}"; diff --git a/Patches/DiscordPatch.cs b/Patches/DiscordPatch.cs new file mode 100644 index 0000000..ecf7a65 --- /dev/null +++ b/Patches/DiscordPatch.cs @@ -0,0 +1,50 @@ +using System; +using AmongUs.Data; +using Discord; +using HarmonyLib; + +namespace AmongUsRevamped; + +// Originally from "Town of Us Rewritten", by Det +[HarmonyPatch(typeof(ActivityManager), nameof(ActivityManager.UpdateActivity))] +public class DiscordRPC +{ + private static string Lobbycode = ""; + private static string Region = ""; + + public static void Prefix([HarmonyArgument(0)] Activity activity) + { + if (activity == null) return; + + var details = $"AmongUsRevamped {Main.ModVersion}"; + activity.Details = details; + + try + { + if (activity.State != "In Menus") + { + if (!DataManager.Settings.Gameplay.StreamerMode) + { + if (Utils.IsLobby) + { + Lobbycode = GameStartManager.Instance.GameRoomNameCode.text; + Region = Utils.GetRegionName(); + } + + if (Lobbycode != "" && Region != "") details = $"AmongUsRevamped - {Lobbycode} ({Region})"; + } + else + details = $"AmongUsRevamped {Main.ModVersion}"; + + activity.Details = details; + } + } + catch (Exception ex) + { + Logger.Error("Error in updating discord rpc", "DiscordPatch"); + Logger.Exception(ex, "DiscordPatch"); + details = $"AmongUsRevamped v{Main.ModVersion}"; + activity.Details = details; + } + } +} \ No newline at end of file diff --git a/Patches/EndGamePatches.cs b/Patches/EndGamePatches.cs index 9a24af6..3ace68d 100644 --- a/Patches/EndGamePatches.cs +++ b/Patches/EndGamePatches.cs @@ -21,12 +21,44 @@ public static class EndGameManagerPatch [HarmonyPatch(typeof(LogicGameFlowNormal), nameof(LogicGameFlowNormal.CheckEndCriteria))] class NormalGameEndChecker { + public static bool ImpCheckComplete; + public static string LastWinReason = ""; + public static List imps = new List(); + public static bool Prefix() { - if (Options.NoGameEnd.GetBool() || Options.Gamemode.GetValue() == 3 || Options.Gamemode.GetValue() == 2 && Main.NormalOptions.NumImpostors*2 < Utils.allAlivePlayersCount ) return false; - else return true; + + if (Options.NoGameEnd.GetBool() || Options.Gamemode.GetValue() == 3 || Options.Gamemode.GetValue() == 2 && Main.NormalOptions.NumImpostors*2 < Utils.allAlivePlayersCount) return false; + + var allPlayers = PlayerControl.AllPlayerControls.ToArray(); + + if (!ImpCheckComplete) + { + imps.AddRange(allPlayers.Where(pc => pc.Data.Role.IsImpostor)); + ImpCheckComplete = true; + } + + if (Utils.AliveImpostors == 0) + { + LastWinReason = $"★ Crewmates win!\n\nImpostors:\n" + string.Join("\n", imps.Select(p => p.Data.PlayerName)); + } + else if (Utils.AliveImpostors >= Utils.AliveCrewmates) + { + LastWinReason = $"★ Impostors win!\n\nImpostors:\n★" + string.Join("\n★", imps.Select(p => p.Data.PlayerName)); + } + else if (GameData.Instance != null && GameData.Instance.TotalTasks > 0 && GameData.Instance.CompletedTasks >= GameData.Instance.TotalTasks) + { + LastWinReason = $"★ Crewmates win! (Tasks)\n\nImpostors:\n" + string.Join("\n", imps.Select(p => p.Data.PlayerName)); + } + else + { + LastWinReason = $"★ Impostors win! (Sabotage)\n\nImpostors:\n★" + string.Join("\n★", imps.Select(p => p.Data.PlayerName)); + } + + return true; } } + [HarmonyPatch(typeof(LogicGameFlowHnS), nameof(LogicGameFlowHnS.CheckEndCriteria))] class HNSGameEndChecker { @@ -36,6 +68,7 @@ class HNSGameEndChecker else return true; } } + [HarmonyPatch(typeof(ControllerManager), nameof(ControllerManager.Update))] internal class ControllerManagerUpdatePatch { diff --git a/Patches/JoinAndLeavePatches.cs b/Patches/JoinAndLeavePatches.cs index 2a0ee0e..413d631 100644 --- a/Patches/JoinAndLeavePatches.cs +++ b/Patches/JoinAndLeavePatches.cs @@ -10,17 +10,39 @@ namespace AmongUsRevamped; [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnGameJoined))] internal static class OnGameJoinedPatch { + public static bool WaitingForChat; public static bool AutoStartCheck; - public static void Postfix(AmongUsClient __instance) + public static void Postfix() { - if (!AmongUsClient.Instance.AmHost || !Main.AutoStart.Value) return; + if (!AmongUsClient.Instance.AmHost) return; - LateTask.Tasks.Clear(); + WaitingForChat = false; + AutoStartCheck = false; - new LateTask(() => + if (Main.AutoStart.Value) { - AutoStartCheck = true; - }, Options.WaitAutoStart.GetFloat(), "AutoStartTimer"); + LateTask.Tasks.Clear(); + + new LateTask(() => + { + AutoStartCheck = true; + }, Options.WaitAutoStart.GetFloat(), "AutoStartTimer"); + } + + if (Options.AutoSendGameInfo.GetBool() && !string.IsNullOrEmpty(NormalGameEndChecker.LastWinReason)) + { + WaitingForChat = true; + + new LateTask(() => + { + Utils.ShowLastResult(); + }, 3f, "AutoSendGameInfo"); + + new LateTask(() => + { + WaitingForChat = false; + }, 5.2f, "AutoSendGameInfo2"); + } } } @@ -80,4 +102,14 @@ class OnPlayerJoinedPatch } } } +} + +[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnPlayerLeft))] +class OnPlayerLeftPatch +{ + static void Prefix([HarmonyArgument(0)] ClientData client) + { + if (client?.Character == null) return; + FixedUpdateInGamePatch.ProcessedModerators.Remove(client.Character.PlayerId); + } } \ No newline at end of file diff --git a/Patches/OnGameStartedPatch.cs b/Patches/OnGameStartedPatch.cs index ec512cc..660bea7 100644 --- a/Patches/OnGameStartedPatch.cs +++ b/Patches/OnGameStartedPatch.cs @@ -11,6 +11,9 @@ internal class ApplyCustomImpostorCount { if (!AmongUsClient.Instance.AmHost) return; + NormalGameEndChecker.imps.Clear(); + NormalGameEndChecker.LastWinReason = ""; + // Normal game = Impostor to prevent blocking task win. HNS = Crewmate to prevent Seeker detection range bugging. if (Main.GM.Value) { diff --git a/Patches/PlayerControlPatches.cs b/Patches/PlayerControlPatches.cs index e1ca819..e70161d 100644 --- a/Patches/PlayerControlPatches.cs +++ b/Patches/PlayerControlPatches.cs @@ -9,9 +9,17 @@ namespace AmongUsRevamped; [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))] class FixedUpdateInGamePatch { + public static readonly HashSet ProcessedModerators = new HashSet(); + public static void Postfix(PlayerControl __instance) { if (__instance == null || __instance.PlayerId == 255) return; + + if (Utils.IsPlayerModerator(__instance.Data.FriendCode) && !ProcessedModerators.Contains(__instance.PlayerId)) + { + __instance.SetName($"★{__instance.Data.PlayerName}★"); + ProcessedModerators.Add(__instance.PlayerId); + } GameObject g = GameObject.Find("GameSettingsLabel"); @@ -64,9 +72,10 @@ class FixedUpdateInGamePatch new LateTask(() => { - MessageWriter writer = AmongUsClient.Instance.StartEndGame(); - writer.Write((byte)GameOverReason.ImpostorsByVote); - AmongUsClient.Instance.FinishEndGame(writer); + NormalGameEndChecker.LastWinReason = $"★ {__instance.Data.PlayerName} Wins! (Completed tasks first)"; + MessageWriter writer = AmongUsClient.Instance.StartEndGame(); + writer.Write((byte)GameOverReason.ImpostorsByVote); + AmongUsClient.Instance.FinishEndGame(writer); }, 1f, "SpeedrunSetWinner"); } }