From 8e63bff2f568898eed26ef4d8d6c0419e18aa033 Mon Sep 17 00:00:00 2001 From: gendelo3 Date: Thu, 1 Aug 2024 12:19:33 +0200 Subject: [PATCH] Update v4.6.0 --- README.md | 19 +- TheOtherRoles.sln | 11 +- TheOtherRoles/Buttons.cs | 15 +- .../CustomGameModes/GameModePatches.cs | 66 + TheOtherRoles/CustomGameModes/PropHunt.cs | 36 +- TheOtherRoles/CustomOptionHolder.cs | 45 +- TheOtherRoles/Helpers.cs | 19 +- TheOtherRoles/Main.cs | 17 +- TheOtherRoles/MapOptions.cs | 4 +- TheOtherRoles/Modules/BepInExUpdater.cs | 4 +- TheOtherRoles/Modules/ChatCommands.cs | 2 +- TheOtherRoles/Modules/CustomColors.cs | 32 +- .../CustomHats/Patches/HatParentPatches.cs | 4 +- .../CustomHats/Patches/HatsTabPatches.cs | 14 +- TheOtherRoles/Modules/CustomOptions.cs | 1145 ++++++++--------- TheOtherRoles/Objects/Footprint.cs | 2 +- TheOtherRoles/Objects/Silhouette.cs | 2 - TheOtherRoles/Patches/ClientOptionsPatch.cs | 1 + TheOtherRoles/Patches/ConstantsPatch.cs | 4 +- TheOtherRoles/Patches/CredentialsPatch.cs | 20 +- TheOtherRoles/Patches/EndGamePatch.cs | 96 +- TheOtherRoles/Patches/ExileControllerPatch.cs | 6 +- .../Patches/GameStartManagerPatch.cs | 82 +- TheOtherRoles/Patches/GetStringPatch.cs | 31 + TheOtherRoles/Patches/IntroPatch.cs | 3 +- TheOtherRoles/Patches/MeetingPatch.cs | 16 +- TheOtherRoles/Patches/PlayerControlPatch.cs | 2 +- TheOtherRoles/Patches/RegionMenuPatch.cs | 15 + TheOtherRoles/Patches/RoleAssignmentPatch.cs | 11 +- TheOtherRoles/Patches/ShipStatusPatch.cs | 2 +- TheOtherRoles/Patches/UsablesPatch.cs | 8 +- TheOtherRoles/Players/CachedPlayer.cs | 4 +- TheOtherRoles/RPC.cs | 28 +- TheOtherRoles/Resources/Copy.png | Bin 0 -> 17161 bytes TheOtherRoles/Resources/CopyActive.png | Bin 0 -> 14765 bytes TheOtherRoles/Resources/CopyPasteBG.png | Bin 0 -> 13064 bytes TheOtherRoles/Resources/Minus_Button.png | Bin 0 -> 6295 bytes .../Resources/Minus_ButtonActive.png | Bin 0 -> 6897 bytes TheOtherRoles/Resources/Paste.png | Bin 0 -> 14822 bytes TheOtherRoles/Resources/PasteActive.png | Bin 0 -> 14227 bytes TheOtherRoles/Resources/Plus_Button.png | Bin 0 -> 7047 bytes TheOtherRoles/Resources/Plus_ButtonActive.png | Bin 0 -> 7541 bytes TheOtherRoles/Resources/Settings_Button.png | Bin 0 -> 13493 bytes .../Resources/Settings_ButtonActive.png | Bin 0 -> 13826 bytes TheOtherRoles/TasksHandler.cs | 2 +- TheOtherRoles/TheOtherRoles.csproj | 11 +- TheOtherRoles/packages.lock.json | 130 +- 47 files changed, 1022 insertions(+), 887 deletions(-) create mode 100644 TheOtherRoles/CustomGameModes/GameModePatches.cs create mode 100644 TheOtherRoles/Patches/GetStringPatch.cs create mode 100644 TheOtherRoles/Resources/Copy.png create mode 100644 TheOtherRoles/Resources/CopyActive.png create mode 100644 TheOtherRoles/Resources/CopyPasteBG.png create mode 100644 TheOtherRoles/Resources/Minus_Button.png create mode 100644 TheOtherRoles/Resources/Minus_ButtonActive.png create mode 100644 TheOtherRoles/Resources/Paste.png create mode 100644 TheOtherRoles/Resources/PasteActive.png create mode 100644 TheOtherRoles/Resources/Plus_Button.png create mode 100644 TheOtherRoles/Resources/Plus_ButtonActive.png create mode 100644 TheOtherRoles/Resources/Settings_Button.png create mode 100644 TheOtherRoles/Resources/Settings_ButtonActive.png diff --git a/README.md b/README.md index 4e04661..7ac7ef6 100644 --- a/README.md +++ b/README.md @@ -42,15 +42,16 @@ The [Role Assignment](#role-assignment) section explains how the roles are being # Releases | Among Us - Version| Mod Version | Link | |----------|-------------|-----------------| +| 2024.6.18s| v4.6.0| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.6.0/TheOtherRoles.zip) | 2024.3.5s| v4.5.3| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.5.3/TheOtherRoles.zip) -| 2024.3.5s| v4.5.2| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.5.2/TheOtherRoles.zip) -| 2023.11.28s| v4.5.1| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.5.1/TheOtherRoles.zip)
Click to show older versions | Among Us - Version| Mod Version | Link | |----------|-------------|-----------------| +| 2024.3.5s| v4.5.2| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.5.2/TheOtherRoles.zip) +| 2023.11.28s| v4.5.1| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.5.1/TheOtherRoles.zip) | 2023.11.28s| v4.5.0| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.5.0/TheOtherRoles.zip) | 2023.07.12s| v4.4.2| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.4.2/TheOtherRoles.zip) | 2023.07.12s| v4.4.1| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.4.1/TheOtherRoles.zip) @@ -133,6 +134,20 @@ The [Role Assignment](#role-assignment) section explains how the roles are being
Click to show the Changelog + +** Version 4.6.0** +- Updated to Among Us version 2024.6.18 (Vanilla Update with new UI elements and many Server Side Changes) +- Added a completely new UI for the Mod's Options, using the new Vanilla Design. +- Added a button to switch gamemodes in the lobby +- Added an option to enable the Chat-Notification in game (in Mod Options) +- Fixed a bug where the shielding the bomber would show a murder attempt on the bomber, when they plant a bomb (sorry Vlesk) +- Fixed a bug in PropHunt, where the background of the Prop-Display was visible in the cameras +- Fixed a bug where guessing a player would sometimes not return the votes to the voters +- Fixed a bug where the Lawyer would not properly die when guessing the client +- Removed: Disabled Vanilla Servers for now, as the mod is incompatible with them in its / their current state. Thanks to Miniduikboat for making custom servers great again! +- Experimental: Advanded users can try to install the mod on the Windows Store Version of Among Us. No Support Provided! + + **Version 4.5.3** - Added a new role: Yo-Yo (Impostor) - Mark and blink to a position, then blink back to the start of the blink! - Added a new option to Jackal And Sidekick: Jackal / Sidekick can sabotage lights (cooldown syncs with Imp Sabotages) diff --git a/TheOtherRoles.sln b/TheOtherRoles.sln index 3c00efe..a61126e 100644 --- a/TheOtherRoles.sln +++ b/TheOtherRoles.sln @@ -1,6 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TheOtherRoles", "TheOtherRoles\TheOtherRoles.csproj", "{11FBC798-BAF5-4EE5-9511-BE6DB0592F99}" +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34728.123 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TheOtherRoles", "TheOtherRoles\TheOtherRoles.csproj", "{11FBC798-BAF5-4EE5-9511-BE6DB0592F99}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TheOtherUpdater", "TheOtherUpdater\TheOtherUpdater.vcxproj", "{73A5662B-9C59-4C97-95C5-F5B2A8BBDDCA}" EndProject @@ -19,4 +22,10 @@ Global {73A5662B-9C59-4C97-95C5-F5B2A8BBDDCA}.Release|Any CPU.ActiveCfg = Release|Win32 {73A5662B-9C59-4C97-95C5-F5B2A8BBDDCA}.Release|Any CPU.Build.0 = Release|Win32 EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0CC3F51E-CC66-4122-B4CB-3A0B59DE563C} + EndGlobalSection EndGlobal diff --git a/TheOtherRoles/Buttons.cs b/TheOtherRoles/Buttons.cs index 763bbda..7c56ec1 100644 --- a/TheOtherRoles/Buttons.cs +++ b/TheOtherRoles/Buttons.cs @@ -277,7 +277,7 @@ namespace TheOtherRoles // Add poolable player to the button so that the target outfit is shown button.actionButton.cooldownTimerText.transform.localPosition = new Vector3(0, 0, -1f); // Before the poolable player targetDisplay = UnityEngine.Object.Instantiate(Patches.IntroCutsceneOnDestroyPatch.playerPrefab, button.actionButton.transform); - GameData.PlayerInfo data = target.Data; + NetworkedPlayerInfo data = target.Data; target.SetPlayerMaterialColors(targetDisplay.cosmetics.currentBodySprite.BodySprite); targetDisplay.SetSkin(data.DefaultOutfit.SkinId, data.DefaultOutfit.ColorId); targetDisplay.SetHat(data.DefaultOutfit.HatId, data.DefaultOutfit.ColorId); @@ -364,7 +364,7 @@ namespace TheOtherRoles Vector2 truePosition2 = component.TruePosition; if (Vector2.Distance(truePosition2, truePosition) <= CachedPlayer.LocalPlayer.PlayerControl.MaxReportDistance && CachedPlayer.LocalPlayer.PlayerControl.CanMove && !PhysicsHelpers.AnythingBetween(truePosition, truePosition2, Constants.ShipAndObjectsMask, false)) { - GameData.PlayerInfo playerInfo = GameData.Instance.GetPlayerById(component.ParentId); + NetworkedPlayerInfo playerInfo = GameData.Instance.GetPlayerById(component.ParentId); MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.CleanBody, Hazel.SendOption.Reliable, -1); writer.Write(playerInfo.PlayerId); @@ -1177,7 +1177,7 @@ namespace TheOtherRoles Vector2 truePosition2 = component.TruePosition; if (Vector2.Distance(truePosition2, truePosition) <= CachedPlayer.LocalPlayer.PlayerControl.MaxReportDistance && CachedPlayer.LocalPlayer.PlayerControl.CanMove && !PhysicsHelpers.AnythingBetween(truePosition, truePosition2, Constants.ShipAndObjectsMask, false)) { - GameData.PlayerInfo playerInfo = GameData.Instance.GetPlayerById(component.ParentId); + NetworkedPlayerInfo playerInfo = GameData.Instance.GetPlayerById(component.ParentId); MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.CleanBody, Hazel.SendOption.Reliable, -1); writer.Write(playerInfo.PlayerId); @@ -1448,7 +1448,7 @@ namespace TheOtherRoles Vector2 truePosition = CachedPlayer.LocalPlayer.PlayerControl.GetTruePosition(); Vector2 truePosition2 = component.TruePosition; if (Vector2.Distance(truePosition2, truePosition) <= CachedPlayer.LocalPlayer.PlayerControl.MaxReportDistance && CachedPlayer.LocalPlayer.PlayerControl.CanMove && !PhysicsHelpers.AnythingBetween(truePosition, truePosition2, Constants.ShipAndObjectsMask, false)) { - GameData.PlayerInfo playerInfo = GameData.Instance.GetPlayerById(component.ParentId); + NetworkedPlayerInfo playerInfo = GameData.Instance.GetPlayerById(component.ParentId); MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.CleanBody, Hazel.SendOption.Reliable, -1); writer.Write(playerInfo.PlayerId); @@ -1796,7 +1796,7 @@ namespace TheOtherRoles // Bomber button bomberButton = new CustomButton( () => { - if (Helpers.checkMuderAttempt(Bomber.bomber, Bomber.bomber) != MurderAttemptResult.BlankKill) { + if (Helpers.checkMuderAttempt(Bomber.bomber, Bomber.bomber, ignoreMedic: true) != MurderAttemptResult.BlankKill) { var pos = CachedPlayer.LocalPlayer.transform.position; byte[] buff = new byte[sizeof(float) * 2]; Buffer.BlockCopy(BitConverter.GetBytes(pos.x), 0, buff, 0 * sizeof(float), sizeof(float)); @@ -1931,7 +1931,6 @@ namespace TheOtherRoles Buffer.BlockCopy(BitConverter.GetBytes(pos.y), 0, buff, 1 * sizeof(float), sizeof(float)); if (Yoyo.markedLocation == null) { - TheOtherRolesPlugin.Logger.LogMessage($"marked location is null in button press"); MessageWriter writer = AmongUsClient.Instance.StartRpc(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.YoyoMarkLocation, Hazel.SendOption.Reliable); writer.WriteBytesAndSize(buff); writer.EndMessage(); @@ -1942,9 +1941,7 @@ namespace TheOtherRoles yoyoButton.HasEffect = false; yoyoButton.buttonText = "Blink"; } else { - TheOtherRolesPlugin.Logger.LogMessage("in else for some reason"); // Jump to location - TheOtherRolesPlugin.Logger.LogMessage($"trying to blink!"); var exit = (Vector3)Yoyo.markedLocation; if (SubmergedCompatibility.IsSubmerged) { SubmergedCompatibility.ChangeFloor(exit.y > -7); @@ -2061,7 +2058,7 @@ namespace TheOtherRoles }, () => { return true; }, () => { return; }, - Helpers.loadSpriteFromResources("TheOtherRoles.Resources.MinusButton.png", 150f), // Invisible button! + null, // Invisible button! new Vector3(0.4f, 2.8f, 0), __instance, KeyCode.KeypadPlus diff --git a/TheOtherRoles/CustomGameModes/GameModePatches.cs b/TheOtherRoles/CustomGameModes/GameModePatches.cs new file mode 100644 index 0000000..9aa18d6 --- /dev/null +++ b/TheOtherRoles/CustomGameModes/GameModePatches.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Epic.OnlineServices.Presence; +using HarmonyLib; +using TheOtherRoles.Utilities; +using TMPro; +using UnityEngine; +using static UnityEngine.UI.Button; +using UnityEngine.Events; +using Hazel; +using TheOtherRoles.Players; + +namespace TheOtherRoles.CustomGameModes { + [HarmonyPatch] + class GameModePatches { + /* + Creates a button in the info pane in the lobby to cycle through the game modes of TOR. + */ + [HarmonyPatch(typeof(LobbyInfoPane), nameof(LobbyInfoPane.Update))] + class LobbyInfoPanePatch { + + private static GameObject gameModeButton = null; + public static void Postfix(LobbyInfoPane __instance) { + if (gameModeButton != null|| !AmongUsClient.Instance.AmHost) { return; } + + var template = GameObject.Find("PRIVATE BUTTON"); + var GameModeText = GameObject.Find("GameModeText"); + if (template == null || GameModeText== null) { return; } + gameModeButton = GameObject.Instantiate(template, template.transform.parent); //, GameModeText.transform); + gameModeButton.transform.localPosition = template.transform.localPosition + new Vector3(0f, 0.65f, -2f); + gameModeButton.name = "TOR GameModeButton"; + + var pButton = gameModeButton.GetComponent(); + pButton.buttonText.text = GameModeText.GetComponent().text; + pButton.OnClick.RemoveAllListeners(); + pButton.OnClick = new ButtonClickedEvent(); + __instance.StartCoroutine(Effects.Lerp(0.1f, new Action(p => { pButton.buttonText.text = Helpers.cs(Color.yellow, GameModeText.GetComponent().text); }))); + gameModeButton.transform.GetChild(1).GetComponent().color = new Color(0.1f, 0.1f, 0.1f); + gameModeButton.transform.GetChild(2).GetComponent().color = new Color(0f, 0f, 0f); + pButton.OnClick.AddListener((Action)(() => + { + TORMapOptions.gameMode = (CustomGamemodes)((int)(TORMapOptions.gameMode + 1) % Enum.GetNames(typeof(CustomGamemodes)).Length); + __instance.StartCoroutine(Effects.Lerp(0.1f, new Action(p => { pButton.buttonText.text = Helpers.cs(Color.yellow, GameModeText.GetComponent().text); }))); + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.ShareGamemode, Hazel.SendOption.Reliable, -1); + writer.Write((byte)TORMapOptions.gameMode); + AmongUsClient.Instance.FinishRpcImmediately(writer); + RPCProcedure.shareGamemode((byte)TORMapOptions.gameMode); + })); + pButton.OnMouseOut = new UnityEvent(); + pButton.OnMouseOver = new UnityEvent(); + pButton.OnMouseOver.AddListener((Action)(() => { + gameModeButton.transform.GetChild(1).gameObject.SetActive(true); + gameModeButton.transform.GetChild(2).gameObject.SetActive(false); + })); + pButton.OnMouseOut.AddListener((Action)(() => { + gameModeButton.transform.GetChild(1).gameObject.SetActive(false); + gameModeButton.transform.GetChild(2).gameObject.SetActive(true); + })); + + } + } + } +} diff --git a/TheOtherRoles/CustomGameModes/PropHunt.cs b/TheOtherRoles/CustomGameModes/PropHunt.cs index 633e140..7d0aa06 100644 --- a/TheOtherRoles/CustomGameModes/PropHunt.cs +++ b/TheOtherRoles/CustomGameModes/PropHunt.cs @@ -12,6 +12,8 @@ using TheOtherRoles.Players; using TheOtherRoles.Utilities; using TMPro; using UnityEngine; +using static TheOtherRoles.Snitch; +using static UnityEngine.GraphicsBuffer; namespace TheOtherRoles.CustomGameModes { [HarmonyPatch] @@ -122,17 +124,13 @@ namespace TheOtherRoles.CustomGameModes { return Helpers.loadSpriteFromResources($"TheOtherRoles.Resources.IntroAnimation.intro_{index + 1000}.png", 150f, cache: false); } - public static void updateWhitelistedObjects() { + public static void updateWhitelistedObjects(bool debug=false) { string allNames = Helpers.readTextFromResources("TheOtherRoles.Resources.Txt.Props.txt"); - bool debug = false; if (debug) { allNames = Helpers.readTextFromFile(System.IO.Directory.GetCurrentDirectory() + "\\Props.txt"); } - TheOtherRolesPlugin.Logger.LogMessage($"after debug"); + whitelistedObjects = allNames.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).ToList(); - TheOtherRolesPlugin.Logger.LogMessage($"after split"); - - TheOtherRolesPlugin.Logger.LogMessage($"Last element: {whitelistedObjects.Last()}"); } @@ -172,6 +170,7 @@ namespace TheOtherRoles.CustomGameModes { if (poolablesBackground == null) { poolablesBackground = new GameObject("poolablesBackground"); poolablesBackground.AddComponent(); + poolablesBackground.layer = LayerMask.NameToLayer("UI"); if (poolablesBackgroundSprite == null) poolablesBackgroundSprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.poolablesBackground.jpg", 200f); } poolablesBackground.transform.SetParent(HudManager.Instance.transform); @@ -193,15 +192,13 @@ namespace TheOtherRoles.CustomGameModes { poolablePlayer.cosmetics.nameText.fontSize = 4; } else { // Display Prop - poolablePlayer.cosmetics.nameText.text = Helpers.cs(Palette.CrewmateBlue, pc.Data.PlayerName); ; - if (isCurrentlyRevealed.ContainsKey(pc.PlayerId)) { - - } + poolablePlayer.cosmetics.nameText.text = Helpers.cs(Palette.CrewmateBlue, pc.Data.PlayerName); } // update currently revealed: if (isCurrentlyRevealed.ContainsKey(pc.PlayerId)) { if (!revealRenderer.ContainsKey(pc.PlayerId)) { var go = new GameObject($"reveal_renderer_{pc.PlayerId}"); + go.layer = LayerMask.NameToLayer("UI"); go.AddComponent(); go.transform.SetParent(poolablePlayer.transform.parent, false); go.SetActive(true); @@ -257,6 +254,7 @@ namespace TheOtherRoles.CustomGameModes { } public static void dangerMeterUpdate() { + if (!HudManager.Instance || !HudManager.Instance.DangerMeter) return; if (HudManager.Instance.DangerMeter.gameObject.active) { float dist = 55f; float dist2 = 15f; @@ -358,7 +356,7 @@ namespace TheOtherRoles.CustomGameModes { Collider2D bestCollider = null; float bestDist = 9999; if (whitelistedObjects == null || whitelistedObjects.Count == 0 || verbose) { - updateWhitelistedObjects(); + updateWhitelistedObjects(true); } foreach (Collider2D collider in Physics2D.OverlapCircleAll(origin.transform.position, radius)) { if (verbose) { @@ -522,7 +520,7 @@ namespace TheOtherRoles.CustomGameModes { [HarmonyPatch(typeof(MapConsole), nameof(MapConsole.CanUse))] [HarmonyPostfix] - public static void AdminCanUsePostfix(MapConsole __instance, GameData.PlayerInfo pc, ref bool canUse, ref bool couldUse, ref float __result) { + public static void AdminCanUsePostfix(MapConsole __instance, NetworkedPlayerInfo pc, ref bool canUse, ref bool couldUse, ref float __result) { if (!PropHunt.isPropHuntGM || !PlayerControl.LocalPlayer.Data.Role.IsImpostor) return; if (canUse) { if (HudManagerStartPatch.propHuntAdminButton.Timer > 0) { @@ -565,7 +563,7 @@ namespace TheOtherRoles.CustomGameModes { [HarmonyPrefix] public static bool CheckClickPatch(KillButton __instance) { if (!PropHunt.isPropHuntGM) return true; - __instance.DoClick(); + __instance.DoClick(); return false; } @@ -574,7 +572,7 @@ namespace TheOtherRoles.CustomGameModes { [HarmonyPrefix] public static bool KillButtonClickPatch(KillButton __instance) { if (!PropHunt.isPropHuntGM || __instance.isCoolingDown || PlayerControl.LocalPlayer.Data.IsDead || PlayerControl.LocalPlayer.inVent) return false; - + var targets = PlayerControl.LocalPlayer.Data.Role.GetPlayersInAbilityRangeSorted(RoleBehaviour.GetTempPlayerList(), true).ToArray(); __instance.SetTarget(PlayerControl.LocalPlayer.Data.Role.GetPlayersInAbilityRangeSorted(RoleBehaviour.GetTempPlayerList(), true).ToArray().FirstOrDefault()); if (__instance.currentTarget == null) { @@ -587,6 +585,14 @@ namespace TheOtherRoles.CustomGameModes { return false; } + [HarmonyPatch(typeof(RoleBehaviour), nameof(RoleBehaviour.IsValidTarget))] + [HarmonyPrefix] + public static bool IsValidTarget(RoleBehaviour __instance, NetworkedPlayerInfo target, ref bool __result) { + if (!PropHunt.isPropHuntGM) return true; + __result = !(target == null) && !target.Disconnected && !target.IsDead && target.PlayerId != __instance.Player.PlayerId && !(target.Role == null) && !(target.Object == null) && !target.Object.inVent && !target.Object.inMovingPlat; + return false; + } + [HarmonyPatch(typeof(MapBehaviour), nameof(MapBehaviour.Show))] [HarmonyPrefix] public static void MapBehaviourShowPatch(MapBehaviour __instance, ref MapOptions opts) { @@ -594,7 +600,7 @@ namespace TheOtherRoles.CustomGameModes { if (opts.Mode == MapOptions.Modes.Sabotage) opts.Mode = MapOptions.Modes.Normal; } - + // Disable a lot of stuff [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CmdReportDeadBody))] [HarmonyPatch(typeof(Vent), nameof(Vent.SetOutline))] diff --git a/TheOtherRoles/CustomOptionHolder.cs b/TheOtherRoles/CustomOptionHolder.cs index 505590c..0b1772e 100644 --- a/TheOtherRoles/CustomOptionHolder.cs +++ b/TheOtherRoles/CustomOptionHolder.cs @@ -426,22 +426,21 @@ namespace TheOtherRoles { // Role Options presetSelection = CustomOption.Create(0, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Preset"), presets, null, true); - activateRoles = CustomOption.Create(1, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Enable Mod Roles And Block Vanilla Roles"), true, null, true); - anyPlayerCanStopStart = CustomOption.Create(2, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Any Player Can Stop The Start"), false, null, false); + if (Utilities.EventUtility.canBeEnabled) enableEventMode = CustomOption.Create(10423, Types.General, cs(Color.green, "Enable Special Mode"), true, null, true); // Using new id's for the options to not break compatibilty with older versions - crewmateRolesCountMin = CustomOption.Create(300, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Minimum Crewmate Roles"), 15f, 0f, 15f, 1f, null, true); - crewmateRolesCountMax = CustomOption.Create(301, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Maximum Crewmate Roles"), 15f, 0f, 15f, 1f); - crewmateRolesFill = CustomOption.Create(308, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Fill Crewmate Roles\n(Ignores Min/Max)"), false); + crewmateRolesCountMin = CustomOption.Create(300, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Minimum Crewmate Roles"), 15f, 0f, 15f, 1f, null, true, heading: "Min/Max Roles"); + crewmateRolesCountMax = CustomOption.Create(301, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Maximum Crewmate Roles"), 15f, 0f, 15f, 1f); neutralRolesCountMin = CustomOption.Create(302, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Minimum Neutral Roles"), 15f, 0f, 15f, 1f); neutralRolesCountMax = CustomOption.Create(303, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Maximum Neutral Roles"), 15f, 0f, 15f, 1f); impostorRolesCountMin = CustomOption.Create(304, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Minimum Impostor Roles"), 15f, 0f, 15f, 1f); impostorRolesCountMax = CustomOption.Create(305, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Maximum Impostor Roles"), 15f, 0f, 15f, 1f); modifiersCountMin = CustomOption.Create(306, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Minimum Modifiers"), 15f, 0f, 15f, 1f); modifiersCountMax = CustomOption.Create(307, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Maximum Modifiers"), 15f, 0f, 15f, 1f); - + crewmateRolesFill = CustomOption.Create(308, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Fill Crewmate Roles\n(Ignores Min/Max)"), false); + mafiaSpawnRate = CustomOption.Create(18, Types.Impostor, cs(Janitor.color, "Mafia"), rates, null, true); janitorCooldown = CustomOption.Create(19, Types.Impostor, "Janitor Cooldown", 30f, 10f, 60f, 2.5f, mafiaSpawnRate); @@ -687,7 +686,7 @@ namespace TheOtherRoles { trapperTrapDuration = CustomOption.Create(454, Types.Crewmate, "Trap Duration", 5f, 1f, 15f, 1f, trapperSpawnRate); // Modifier (1000 - 1999) - modifiersAreHidden = CustomOption.Create(1009, Types.Modifier, cs(Color.yellow, "Hide After Death Modifiers"), true, null, true); + modifiersAreHidden = CustomOption.Create(1009, Types.Modifier, cs(Color.yellow, "VIP, Bait & Bloody Are Hidden"), true, null, true, heading: cs(Color.yellow, "Hide After Death Modifiers")); modifierBloody = CustomOption.Create(1000, Types.Modifier, cs(Color.yellow, "Bloody"), rates, null, true); modifierBloodyQuantity = CustomOption.Create(1001, Types.Modifier, cs(Color.yellow, "Bloody Quantity"), ratesModifier, modifierBloody); @@ -734,13 +733,13 @@ namespace TheOtherRoles { modifierShifter = CustomOption.Create(1100, Types.Modifier, cs(Color.yellow, "Shifter"), rates, null, true); // Guesser Gamemode (2000 - 2999) - guesserGamemodeCrewNumber = CustomOption.Create(2001, Types.Guesser, cs(Guesser.color, "Number of Crew Guessers"), 15f, 1f, 15f, 1f, null, true); - guesserGamemodeNeutralNumber = CustomOption.Create(2002, Types.Guesser, cs(Guesser.color, "Number of Neutral Guessers"), 15f, 1f, 15f, 1f, null, true); - guesserGamemodeImpNumber = CustomOption.Create(2003, Types.Guesser, cs(Guesser.color, "Number of Impostor Guessers"), 15f, 1f, 15f, 1f, null, true); - guesserForceJackalGuesser = CustomOption.Create(2007, Types.Guesser, "Force Jackal Guesser", false, null, true); + guesserGamemodeCrewNumber = CustomOption.Create(2001, Types.Guesser, cs(Guesser.color, "Number of Crew Guessers"), 15f, 1f, 15f, 1f, null, true, heading: "Amount of Guessers"); + guesserGamemodeNeutralNumber = CustomOption.Create(2002, Types.Guesser, cs(Guesser.color, "Number of Neutral Guessers"), 15f, 1f, 15f, 1f, null); + guesserGamemodeImpNumber = CustomOption.Create(2003, Types.Guesser, cs(Guesser.color, "Number of Impostor Guessers"), 15f, 1f, 15f, 1f, null); + guesserForceJackalGuesser = CustomOption.Create(2007, Types.Guesser, "Force Jackal Guesser", false, null, true, heading: "Force Guessers"); guesserGamemodeSidekickIsAlwaysGuesser = CustomOption.Create(2012, Types.Guesser, "Sidekick Is Always Guesser", false, null); - guesserForceThiefGuesser = CustomOption.Create(2011, Types.Guesser, "Force Thief Guesser", false, null, true); - guesserGamemodeHaveModifier = CustomOption.Create(2004, Types.Guesser, "Guessers Can Have A Modifier", true, null); + guesserForceThiefGuesser = CustomOption.Create(2011, Types.Guesser, "Force Thief Guesser", false, null); + guesserGamemodeHaveModifier = CustomOption.Create(2004, Types.Guesser, "Guessers Can Have A Modifier", true, null, true, heading: "General Guesser Settings"); guesserGamemodeNumberOfShots = CustomOption.Create(2005, Types.Guesser, "Guesser Number Of Shots", 3f, 1f, 15f, 1f, null); guesserGamemodeHasMultipleShotsPerMeeting = CustomOption.Create(2006, Types.Guesser, "Guesser Can Shoot Multiple Times Per Meeting", false, null); guesserGamemodeKillsThroughShield = CustomOption.Create(2008, Types.Guesser, "Guesses Ignore The Medic Shield", true, null); @@ -764,7 +763,7 @@ namespace TheOtherRoles { hideNSeekCanSabotage = CustomOption.Create(3019, Types.HideNSeekMain, cs(Color.yellow, "Enable Sabotages"), false); hideNSeekHunterWaiting = CustomOption.Create(3022, Types.HideNSeekMain, cs(Color.yellow, "Time The Hunter Needs To Wait"), 15f, 2.5f, 60f, 2.5f); - hunterLightCooldown = CustomOption.Create(3005, Types.HideNSeekRoles, cs(Color.red, "Hunter Light Cooldown"), 30f, 5f, 60f, 1f, null, true); + hunterLightCooldown = CustomOption.Create(3005, Types.HideNSeekRoles, cs(Color.red, "Hunter Light Cooldown"), 30f, 5f, 60f, 1f, null, true, heading: "Hunter Lights Settings"); hunterLightDuration = CustomOption.Create(3006, Types.HideNSeekRoles, cs(Color.red, "Hunter Light Duration"), 5f, 1f, 60f, 1f); hunterLightVision = CustomOption.Create(3007, Types.HideNSeekRoles, cs(Color.red, "Hunter Light Vision"), 3f, 1f, 5f, 0.25f); hunterLightPunish = CustomOption.Create(3008, Types.HideNSeekRoles, cs(Color.red, "Hunter Light Punish In Sec"), 5f, 0f, 30f, 1f); @@ -775,20 +774,20 @@ namespace TheOtherRoles { hunterArrowDuration = CustomOption.Create(3013, Types.HideNSeekRoles, cs(Color.red, "Hunter Arrow Duration"), 5f, 0f, 60f, 1f); hunterArrowPunish = CustomOption.Create(3014, Types.HideNSeekRoles, cs(Color.red, "Hunter Arrow Punish In Sec"), 5f, 0f, 30f, 1f); - huntedShieldCooldown = CustomOption.Create(3015, Types.HideNSeekRoles, cs(Color.gray, "Hunted Shield Cooldown"), 30f, 5f, 60f, 1f, null, true); + huntedShieldCooldown = CustomOption.Create(3015, Types.HideNSeekRoles, cs(Color.gray, "Hunted Shield Cooldown"), 30f, 5f, 60f, 1f, null, true, heading: "Hunter Shields Settings"); huntedShieldDuration = CustomOption.Create(3016, Types.HideNSeekRoles, cs(Color.gray, "Hunted Shield Duration"), 5f, 1f, 60f, 1f); huntedShieldRewindTime = CustomOption.Create(3018, Types.HideNSeekRoles, cs(Color.gray, "Hunted Rewind Time"), 3f, 1f, 10f, 1f); huntedShieldNumber = CustomOption.Create(3026, Types.HideNSeekRoles, cs(Color.gray, "Hunted Shield Number"), 3f, 1f, 15f, 1f); // Prop Hunt General Options propHuntMap = CustomOption.Create(4020, Types.PropHunt, cs(Color.yellow, "Map"), new string[] { "The Skeld", "Mira", "Polus", "Airship", "Fungle", "Submerged", "LI Map"}, null, true, onChange: ()=> { int map = propHuntMap.selection; if (map >= 3) map++; GameOptionsManager.Instance.currentNormalGameOptions.MapId = (byte)map;}); - propHuntTimer = CustomOption.Create(4021, Types.PropHunt, cs(Color.yellow, "Timer In Min"), 5f, 1f, 30f, 0.5f); + propHuntTimer = CustomOption.Create(4021, Types.PropHunt, cs(Color.yellow, "Timer In Min"), 5f, 1f, 30f, 0.5f, null, true, heading: "General PropHunt Settings"); propHuntUnstuckCooldown = CustomOption.Create(4011, Types.PropHunt, cs(Color.yellow, "Unstuck Cooldown"), 30f, 2.5f, 60f, 2.5f); propHuntUnstuckDuration = CustomOption.Create(4012, Types.PropHunt, cs(Color.yellow, "Unstuck Duration"), 2f, 1f, 60f, 1f); propHunterVision = CustomOption.Create(4006, Types.PropHunt, cs(Color.yellow, "Hunter Vision"), 0.5f, 0.25f, 2f, 0.25f); propVision = CustomOption.Create(4007, Types.PropHunt, cs(Color.yellow, "Prop Vision"), 2f, 0.25f, 5f, 0.25f); // Hunter Options - propHuntNumberOfHunters = CustomOption.Create(4000, Types.PropHunt, cs(Color.red, "Number Of Hunters"), 1f, 1f, 5f, 1f, null, true); + propHuntNumberOfHunters = CustomOption.Create(4000, Types.PropHunt, cs(Color.red, "Number Of Hunters"), 1f, 1f, 5f, 1f, null, true, heading: "Hunter Settings"); hunterInitialBlackoutTime = CustomOption.Create(4001, Types.PropHunt, cs(Color.red, "Hunter Initial Blackout Duration"), 10f, 5f, 20f, 1f); hunterMissCooldown = CustomOption.Create(4004, Types.PropHunt, cs(Color.red, "Kill Cooldown After Miss"), 10f, 2.5f, 60f, 2.5f); hunterHitCooldown = CustomOption.Create(4005, Types.PropHunt, cs(Color.red, "Kill Cooldown After Hit"), 10f, 2.5f, 60f, 2.5f); @@ -799,7 +798,7 @@ namespace TheOtherRoles { propHuntFindCooldown = CustomOption.Create(4023, Types.PropHunt, cs(Color.red, "Find Cooldown"), 60f, 2.5f, 1800f, 2.5f); propHuntFindDuration = CustomOption.Create(4024, Types.PropHunt, cs(Color.red, "Find Duration"), 5f, 1f, 15f, 1f); // Prop Options - propBecomesHunterWhenFound = CustomOption.Create(4003, Types.PropHunt, cs(Palette.CrewmateBlue, "Props Become Hunters When Found"), false, null, true); + propBecomesHunterWhenFound = CustomOption.Create(4003, Types.PropHunt, cs(Palette.CrewmateBlue, "Props Become Hunters When Found"), false, null, true, heading: "Prop Settings"); propHuntInvisEnabled = CustomOption.Create(4013, Types.PropHunt, cs(Palette.CrewmateBlue, "Invisibility Enabled"), true, null, true); propHuntInvisCooldown = CustomOption.Create(4014, Types.PropHunt, cs(Palette.CrewmateBlue, "Invisibility Cooldown"), 120f, 10f, 1800f, 2.5f, propHuntInvisEnabled); propHuntInvisDuration = CustomOption.Create(4015, Types.PropHunt, cs(Palette.CrewmateBlue, "Invisibility Duration"), 5f, 1f, 30f, 1f, propHuntInvisEnabled); @@ -811,19 +810,21 @@ namespace TheOtherRoles { // Other options - maxNumberOfMeetings = CustomOption.Create(3, Types.General, "Number Of Meetings (excluding Mayor meeting)", 10, 0, 15, 1, null, true); + maxNumberOfMeetings = CustomOption.Create(3, Types.General, "Number Of Meetings (excluding Mayor meeting)", 10, 0, 15, 1, null, true, heading: "Gameplay Settings"); + anyPlayerCanStopStart = CustomOption.Create(2, Types.General, cs(new Color(204f / 255f, 204f / 255f, 0, 1f), "Any Player Can Stop The Start"), false, null, false); blockSkippingInEmergencyMeetings = CustomOption.Create(4, Types.General, "Block Skipping In Emergency Meetings", false); noVoteIsSelfVote = CustomOption.Create(5, Types.General, "No Vote Is Self Vote", false, blockSkippingInEmergencyMeetings); hidePlayerNames = CustomOption.Create(6, Types.General, "Hide Player Names", false); allowParallelMedBayScans = CustomOption.Create(7, Types.General, "Allow Parallel MedBay Scans", false); shieldFirstKill = CustomOption.Create(8, Types.General, "Shield Last Game First Kill", false); finishTasksBeforeHauntingOrZoomingOut = CustomOption.Create(9, Types.General, "Finish Tasks Before Haunting Or Zooming Out", true); - camsNightVision = CustomOption.Create(11, Types.General, "Cams Switch To Night Vision If Lights Are Off", false, null, true); + deadImpsBlockSabotage = CustomOption.Create(13, Types.General, "Block Dead Impostor From Sabotaging", false, null, false); + camsNightVision = CustomOption.Create(11, Types.General, "Cams Switch To Night Vision If Lights Are Off", false, null, true, heading: "Night Vision Cams"); camsNoNightVisionIfImpVision = CustomOption.Create(12, Types.General, "Impostor Vision Ignores Night Vision Cams", false, camsNightVision, false); - deadImpsBlockSabotage = CustomOption.Create(13, Types.General, cs(Palette.ImpostorRed, "Block Dead Impostor From Sabotaging"), false, null, false); - dynamicMap = CustomOption.Create(500, Types.General, "Play On A Random Map", false, null, true); + + dynamicMap = CustomOption.Create(500, Types.General, "Play On A Random Map", false, null, true, heading: "Random Maps"); dynamicMapEnableSkeld = CustomOption.Create(501, Types.General, "Skeld", rates, dynamicMap, false); dynamicMapEnableMira = CustomOption.Create(502, Types.General, "Mira", rates, dynamicMap, false); dynamicMapEnablePolus = CustomOption.Create(503, Types.General, "Polus", rates, dynamicMap, false); diff --git a/TheOtherRoles/Helpers.cs b/TheOtherRoles/Helpers.cs index 3071494..5f58717 100644 --- a/TheOtherRoles/Helpers.cs +++ b/TheOtherRoles/Helpers.cs @@ -357,7 +357,7 @@ namespace TheOtherRoles { if (Helpers.MushroomSabotageActive()) { var instance = ShipStatus.Instance.CastFast().specialSabotage; MushroomMixupSabotageSystem.CondensedOutfit condensedOutfit = instance.currentMixups[target.PlayerId]; - GameData.PlayerOutfit playerOutfit = instance.ConvertToPlayerOutfit(condensedOutfit); + NetworkedPlayerInfo.PlayerOutfit playerOutfit = instance.ConvertToPlayerOutfit(condensedOutfit); target.MixUpOutfit(playerOutfit); } else target.setLook(target.Data.PlayerName, target.Data.DefaultOutfit.ColorId, target.Data.DefaultOutfit.HatId, target.Data.DefaultOutfit.VisorId, target.Data.DefaultOutfit.SkinId, target.Data.DefaultOutfit.PetId, enforceNightVisionUpdate); @@ -450,7 +450,7 @@ namespace TheOtherRoles { return roleCouldUse; } - public static MurderAttemptResult checkMuderAttempt(PlayerControl killer, PlayerControl target, bool blockRewind = false, bool ignoreBlank = false, bool ignoreIfKillerIsDead = false) { + public static MurderAttemptResult checkMuderAttempt(PlayerControl killer, PlayerControl target, bool blockRewind = false, bool ignoreBlank = false, bool ignoreIfKillerIsDead = false, bool ignoreMedic = false) { var targetRole = RoleInfo.getRoleInfoForPlayer(target, false).FirstOrDefault(); // Modified vanilla checks @@ -474,7 +474,7 @@ namespace TheOtherRoles { } // Block impostor shielded kill - if (Medic.shielded != null && Medic.shielded == target) { + if (!ignoreMedic && Medic.shielded != null && Medic.shielded == target) { MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(killer.NetId, (byte)CustomRPC.ShieldedMurderAttempt, Hazel.SendOption.Reliable, -1); AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.shieldedMurderAttempt(); @@ -606,10 +606,15 @@ namespace TheOtherRoles { if (cam != null && cam.gameObject.name == "UI Camera") cam.orthographicSize = orthographicSize; // The UI is scaled too, else we cant click the buttons. Downside: map is super small. } - if (HudManagerStartPatch.zoomOutButton != null) { - HudManagerStartPatch.zoomOutButton.Sprite = zoomOutStatus ? Helpers.loadSpriteFromResources("TheOtherRoles.Resources.PlusButton.png", 75f) : Helpers.loadSpriteFromResources("TheOtherRoles.Resources.MinusButton.png", 150f); - HudManagerStartPatch.zoomOutButton.PositionOffset = zoomOutStatus ? new Vector3(0f, 3f, 0) : new Vector3(0.4f, 2.8f, 0); + var tzGO = GameObject.Find("TOGGLEZOOMBUTTON"); + if (tzGO != null) { + var rend = tzGO.transform.Find("Inactive").GetComponent(); + var rendActive = tzGO.transform.Find("Active").GetComponent(); + rend.sprite = zoomOutStatus ? Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Plus_Button.png", 100f) : Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Minus_Button.png", 100f); + rendActive.sprite = zoomOutStatus ? Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Plus_ButtonActive.png", 100f) : Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Minus_ButtonActive.png", 100f); + tzGO.transform.localScale = new Vector3(1.2f, 1.2f, 1f) * (zoomOutStatus ? 4 : 1); } + ResolutionManager.ResolutionChanged.Invoke((float)Screen.width / Screen.height, Screen.width, Screen.height, Screen.fullScreen); // This will move button positions to the correct position. } @@ -654,7 +659,7 @@ namespace TheOtherRoles { } } - public static bool hasImpVision(GameData.PlayerInfo player) { + public static bool hasImpVision(NetworkedPlayerInfo player) { return player.Role.IsImpostor || ((Jackal.jackal != null && Jackal.jackal.PlayerId == player.PlayerId || Jackal.formerJackals.Any(x => x.PlayerId == player.PlayerId)) && Jackal.hasImpostorVision) || (Sidekick.sidekick != null && Sidekick.sidekick.PlayerId == player.PlayerId && Sidekick.hasImpostorVision) diff --git a/TheOtherRoles/Main.cs b/TheOtherRoles/Main.cs index bc502ce..066a0bf 100644 --- a/TheOtherRoles/Main.cs +++ b/TheOtherRoles/Main.cs @@ -33,7 +33,7 @@ namespace TheOtherRoles public class TheOtherRolesPlugin : BasePlugin { public const string Id = "me.eisbison.theotherroles"; - public const string VersionString = "4.5.3"; + public const string VersionString = "4.6.0"; public static uint betaDays = 0; // amount of days for the build to be usable (0 for infinite!) public static Version Version = Version.Parse(VersionString); @@ -54,6 +54,7 @@ namespace TheOtherRoles public static ConfigEntry EnableSoundEffects { get; set; } public static ConfigEntry EnableHorseMode { get; set; } public static ConfigEntry ShowVentsOnMap { get; set; } + public static ConfigEntry ShowChatNotifications { get; set; } public static ConfigEntry Ip { get; set; } public static ConfigEntry Port { get; set; } public static ConfigEntry ShowPopUpVersion { get; set; } @@ -108,11 +109,13 @@ namespace TheOtherRoles EnableHorseMode = Config.Bind("Custom", "Enable Horse Mode", false); ShowPopUpVersion = Config.Bind("Custom", "Show PopUp", "0"); ShowVentsOnMap = Config.Bind("Custom", "Show vent positions on minimap", false); - + ShowChatNotifications = Config.Bind("Custom", "Show Chat Notifications", true); + Ip = Config.Bind("Custom", "Custom Server IP", "127.0.0.1"); Port = Config.Bind("Custom", "Custom Server Port", (ushort)22023); defaultRegions = ServerManager.DefaultRegions; - + // Removes vanilla Servers + ServerManager.DefaultRegions = new Il2CppReferenceArray(new IRegionInfo[0]); UpdateRegions(); DebugMode = Config.Bind("Custom", "Enable Debug Mode", "false"); @@ -178,12 +181,12 @@ namespace TheOtherRoles // Spawn dummys - if (Input.GetKeyDown(KeyCode.F)) { + /*if (Input.GetKeyDown(KeyCode.F)) { var playerControl = UnityEngine.Object.Instantiate(AmongUsClient.Instance.PlayerPrefab); var i = playerControl.PlayerId = (byte) GameData.Instance.GetAvailableId(); bots.Add(playerControl); - GameData.Instance.AddPlayer(playerControl); + GameData.Instance.AddPlayer(playerControl, new InnerNet.ClientData(0)); AmongUsClient.Instance.Spawn(playerControl, -2, InnerNet.SpawnFlags.None); playerControl.transform.position = CachedPlayer.LocalPlayer.transform.position; @@ -191,8 +194,8 @@ namespace TheOtherRoles playerControl.NetTransform.enabled = false; playerControl.SetName(RandomString(10)); playerControl.SetColor((byte) random.Next(Palette.PlayerColors.Length)); - GameData.Instance.RpcSetTasks(playerControl.PlayerId, new byte[0]); - } + playerControl.Data.RpcSetTasks(new byte[0]); + }*/ // Terminate round if(Input.GetKeyDown(KeyCode.L)) { diff --git a/TheOtherRoles/MapOptions.cs b/TheOtherRoles/MapOptions.cs index 83cce2d..7df9c30 100644 --- a/TheOtherRoles/MapOptions.cs +++ b/TheOtherRoles/MapOptions.cs @@ -19,6 +19,7 @@ namespace TheOtherRoles{ public static bool enableHorseMode = false; public static bool shieldFirstKill = false; public static bool ShowVentsOnMap = true; + public static bool ShowChatNotifications = true; public static CustomGamemodes gameMode = CustomGamemodes.Classic; // Updating values @@ -54,7 +55,8 @@ namespace TheOtherRoles{ enableSoundEffects = TheOtherRolesPlugin.EnableSoundEffects.Value; enableHorseMode = TheOtherRolesPlugin.EnableHorseMode.Value; ShowVentsOnMap = TheOtherRolesPlugin.ShowVentsOnMap.Value; - + ShowChatNotifications = TheOtherRolesPlugin.ShowChatNotifications.Value; + //Patches.ShouldAlwaysHorseAround.isHorseMode = TheOtherRolesPlugin.EnableHorseMode.Value; } } diff --git a/TheOtherRoles/Modules/BepInExUpdater.cs b/TheOtherRoles/Modules/BepInExUpdater.cs index 42b3591..4f994a7 100644 --- a/TheOtherRoles/Modules/BepInExUpdater.cs +++ b/TheOtherRoles/Modules/BepInExUpdater.cs @@ -19,8 +19,8 @@ namespace TheOtherRoles.Modules; public class BepInExUpdater : MonoBehaviour { - public const string RequiredBepInExVersion = "6.0.0-be.688+49015217f3becf052d33fa4658ac19229f5daa3a"; - public const string BepInExDownloadURL = "https://builds.bepinex.dev/projects/bepinex_be/688/BepInEx-Unity.IL2CPP-win-x86-6.0.0-be.688%2B4901521.zip"; + public const string RequiredBepInExVersion = "6.0.0-be.692+851521cc126e4f9d841d2d9bfe857558f0395939"; + public const string BepInExDownloadURL = "https://builds.bepinex.dev/projects/bepinex_be/692/BepInEx-Unity.IL2CPP-win-x86-6.0.0-be.692%2B851521c.zip"; public static bool UpdateRequired => Paths.BepInExVersion.ToString() != RequiredBepInExVersion; public void Awake() diff --git a/TheOtherRoles/Modules/ChatCommands.cs b/TheOtherRoles/Modules/ChatCommands.cs index 4cabe69..331a913 100644 --- a/TheOtherRoles/Modules/ChatCommands.cs +++ b/TheOtherRoles/Modules/ChatCommands.cs @@ -115,7 +115,7 @@ namespace TheOtherRoles.Modules { public static class SetBubbleName { public static void Postfix(ChatBubble __instance, [HarmonyArgument(0)] string playerName) { PlayerControl sourcePlayer = PlayerControl.AllPlayerControls.ToArray().ToList().FirstOrDefault(x => x.Data != null && x.Data.PlayerName.Equals(playerName)); - if (CachedPlayer.LocalPlayer != null && CachedPlayer.LocalPlayer.Data.Role.IsImpostor && (Spy.spy != null && sourcePlayer.PlayerId == Spy.spy.PlayerId || Sidekick.sidekick != null && Sidekick.wasTeamRed && sourcePlayer.PlayerId == Sidekick.sidekick.PlayerId || Jackal.jackal != null && Jackal.wasTeamRed && sourcePlayer.PlayerId == Jackal.jackal.PlayerId) && __instance != null) __instance.NameText.color = Palette.ImpostorRed; + if (sourcePlayer != null && CachedPlayer.LocalPlayer != null && CachedPlayer.LocalPlayer.Data?.Role?.IsImpostor == true && (Spy.spy != null && sourcePlayer.PlayerId == Spy.spy.PlayerId || Sidekick.sidekick != null && Sidekick.wasTeamRed && sourcePlayer.PlayerId == Sidekick.sidekick.PlayerId || Jackal.jackal != null && Jackal.wasTeamRed && sourcePlayer.PlayerId == Jackal.jackal.PlayerId) && __instance != null) __instance.NameText.color = Palette.ImpostorRed; } } diff --git a/TheOtherRoles/Modules/CustomColors.cs b/TheOtherRoles/Modules/CustomColors.cs index 495af24..9800553 100644 --- a/TheOtherRoles/Modules/CustomColors.cs +++ b/TheOtherRoles/Modules/CustomColors.cs @@ -184,6 +184,36 @@ namespace TheOtherRoles.Modules { return true; } } + + [HarmonyPatch(typeof(ChatNotification), nameof(ChatNotification.SetUp))] + private class ChatNotificationColorsPatch { + public static bool Prefix(ChatNotification __instance, PlayerControl sender, string text) { + if (ShipStatus.Instance && !TORMapOptions.ShowChatNotifications) { + return false; + } + __instance.timeOnScreen = 5f; + __instance.gameObject.SetActive(true); + __instance.SetCosmetics(sender.Data); + string str; + Color color; + try { + str = ColorUtility.ToHtmlStringRGB(Palette.TextColors[__instance.player.ColorId]); + color = Palette.TextOutlineColors[__instance.player.ColorId]; + } catch { + Color32 c = Palette.PlayerColors[__instance.player.ColorId]; + str = ColorUtility.ToHtmlStringRGB(c); + + color = c.r + c.g + c.b > 180 ? Palette.Black : Palette.White; + TheOtherRolesPlugin.Logger.LogMessage($"{c.r}, {c.g}, {c.b}"); + } + __instance.playerColorText.text = __instance.player.ColorBlindName; + __instance.playerNameText.text = "" + (string.IsNullOrEmpty(sender.Data.PlayerName) ? "..." : sender.Data.PlayerName); + __instance.playerNameText.outlineColor = color; + __instance.chatText.text = text; + return false; + } + } + [HarmonyPatch(typeof(PlayerTab), nameof(PlayerTab.OnEnable))] private static class PlayerTabEnablePatch { public static void Postfix(PlayerTab __instance) { // Replace instead @@ -224,7 +254,7 @@ namespace TheOtherRoles.Modules { [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.CheckColor))] private static class PlayerControlCheckColorPatch { private static bool isTaken(PlayerControl player, uint color) { - foreach (GameData.PlayerInfo p in GameData.Instance.AllPlayers.GetFastEnumerator()) + foreach (NetworkedPlayerInfo p in GameData.Instance.AllPlayers.GetFastEnumerator()) if (!p.Disconnected && p.PlayerId != player.PlayerId && p.DefaultOutfit.ColorId == color) return true; return false; diff --git a/TheOtherRoles/Modules/CustomHats/Patches/HatParentPatches.cs b/TheOtherRoles/Modules/CustomHats/Patches/HatParentPatches.cs index ecfaa72..958ca02 100644 --- a/TheOtherRoles/Modules/CustomHats/Patches/HatParentPatches.cs +++ b/TheOtherRoles/Modules/CustomHats/Patches/HatParentPatches.cs @@ -219,7 +219,6 @@ internal static class HatParentPatches { spriteAnimNodeSync.NodeId = __instance.Hat.NoBounce ? 1 : 0; } - if (__instance.Hat.InFront) { __instance.BackLayer.enabled = false; @@ -240,8 +239,7 @@ internal static class HatParentPatches __instance.FrontLayer.sprite = null; __instance.BackLayer.sprite = asset.MainImage; } - - if (!__instance.options.Initialized || !__instance.HideHat()) return false; + if (/*!__instance.options.Initialized ||*/ !__instance.HideHat()) return false; __instance.FrontLayer.enabled = false; __instance.BackLayer.enabled = false; return false; diff --git a/TheOtherRoles/Modules/CustomHats/Patches/HatsTabPatches.cs b/TheOtherRoles/Modules/CustomHats/Patches/HatsTabPatches.cs index 5a258e4..ca2587b 100644 --- a/TheOtherRoles/Modules/CustomHats/Patches/HatsTabPatches.cs +++ b/TheOtherRoles/Modules/CustomHats/Patches/HatsTabPatches.cs @@ -10,14 +10,15 @@ using Object = UnityEngine.Object; namespace TheOtherRoles.Modules.CustomHats.Patches; -[HarmonyPatch(typeof(HatsTab))] +[HarmonyPatch] internal static class HatsTabPatches { private static TextMeshPro textTemplate; - - [HarmonyPatch(nameof(HatsTab.OnEnable))] - [HarmonyPostfix] - private static void OnEnablePostfix(HatsTab __instance) + + + [HarmonyPatch(typeof(HatsTab), nameof(HatsTab.OnEnable))] + [HarmonyPrefix] + private static bool OnEnablePrefix(HatsTab __instance) { for (var i = 0; i < __instance.scroller.Inner.childCount; i++) { @@ -66,6 +67,7 @@ internal static class HatsTabPatches } __instance.scroller.ContentYBounds.max = -(yOffset + 4.1f); + return false; } private static float CreateHatPackage(List> hats, string packageName, float yStart, @@ -106,7 +108,7 @@ internal static class HatsTabPatches colorChip.Button.OnClick.AddListener((Action)(() => hatsTab.SelectHat(hat))); } colorChip.Button.ClickMask = hatsTab.scroller.Hitbox; - colorChip.Inner.SetMaskType(PlayerMaterial.MaskType.ScrollingUI); + colorChip.Inner.SetMaskType(PlayerMaterial.MaskType.SimpleUI); hatsTab.UpdateMaterials(colorChip.Inner.FrontLayer, hat); var background = colorChip.transform.FindChild("Background"); var foreground = colorChip.transform.FindChild("ForeGround"); diff --git a/TheOtherRoles/Modules/CustomOptions.cs b/TheOtherRoles/Modules/CustomOptions.cs index eec9887..dd5b4bd 100644 --- a/TheOtherRoles/Modules/CustomOptions.cs +++ b/TheOtherRoles/Modules/CustomOptions.cs @@ -17,6 +17,7 @@ using AmongUs.GameOptions; using BepInEx.Unity.IL2CPP; using BepInEx; using static ShipStatus; +using TMPro; namespace TheOtherRoles { public class CustomOption { @@ -48,10 +49,11 @@ namespace TheOtherRoles { public bool isHeader; public CustomOptionType type; public Action onChange = null; + public string heading = ""; // Option creation - public CustomOption(int id, CustomOptionType type, string name, System.Object[] selections, System.Object defaultValue, CustomOption parent, bool isHeader, Action onChange = null) { + public CustomOption(int id, CustomOptionType type, string name, System.Object[] selections, System.Object defaultValue, CustomOption parent, bool isHeader, Action onChange = null, string heading = "") { this.id = id; this.name = parent == null ? name : "- " + name; this.selections = selections; @@ -61,6 +63,7 @@ namespace TheOtherRoles { this.isHeader = isHeader; this.type = type; this.onChange = onChange; + this.heading = heading; selection = 0; if (id != 0) { entry = TheOtherRolesPlugin.Instance.Config.Bind($"Preset{preset}", id.ToString(), defaultSelection); @@ -69,19 +72,19 @@ namespace TheOtherRoles { options.Add(this); } - public static CustomOption Create(int id, CustomOptionType type, string name, string[] selections, CustomOption parent = null, bool isHeader = false, Action onChange = null) { - return new CustomOption(id, type, name, selections, "", parent, isHeader, onChange); + public static CustomOption Create(int id, CustomOptionType type, string name, string[] selections, CustomOption parent = null, bool isHeader = false, Action onChange = null, string heading = "") { + return new CustomOption(id, type, name, selections, "", parent, isHeader, onChange, heading); } - public static CustomOption Create(int id, CustomOptionType type, string name, float defaultValue, float min, float max, float step, CustomOption parent = null, bool isHeader = false, Action onChange = null) { + public static CustomOption Create(int id, CustomOptionType type, string name, float defaultValue, float min, float max, float step, CustomOption parent = null, bool isHeader = false, Action onChange = null, string heading = "") { List selections = new(); for (float s = min; s <= max; s += step) selections.Add(s); - return new CustomOption(id, type, name, selections.ToArray(), defaultValue, parent, isHeader, onChange); + return new CustomOption(id, type, name, selections.ToArray(), defaultValue, parent, isHeader, onChange, heading); } - public static CustomOption Create(int id, CustomOptionType type, string name, bool defaultValue, CustomOption parent = null, bool isHeader = false, Action onChange = null) { - return new CustomOption(id, type, name, new string[]{"Off", "On"}, defaultValue ? "On" : "Off", parent, isHeader, onChange); + public static CustomOption Create(int id, CustomOptionType type, string name, bool defaultValue, CustomOption parent = null, bool isHeader = false, Action onChange = null, string heading = "") { + return new CustomOption(id, type, name, new string[]{"Off", "On"}, defaultValue ? "On" : "Off", parent, isHeader, onChange, heading); } // Static behaviour @@ -107,13 +110,19 @@ namespace TheOtherRoles { vanillaSettings.Value = Convert.ToBase64String(GameOptionsManager.Instance.gameOptionsFactory.ToBytes(GameManager.Instance.LogicOptions.currentGameOptions, false)); } - public static void loadVanillaOptions() { + public static bool loadVanillaOptions() { string optionsString = vanillaSettings.Value; - if (optionsString == "") return; - GameOptionsManager.Instance.GameHostOptions = GameOptionsManager.Instance.gameOptionsFactory.FromBytes(Convert.FromBase64String(optionsString)); + if (optionsString == "") return false; + IGameOptions gameOptions = GameOptionsManager.Instance.gameOptionsFactory.FromBytes(Convert.FromBase64String(optionsString)); + if (gameOptions.Version < 8) { + TheOtherRolesPlugin.Logger.LogMessage("tried to paste old settings, not doing this!"); + return false; + } + GameOptionsManager.Instance.GameHostOptions = gameOptions; GameOptionsManager.Instance.CurrentGameOptions = GameOptionsManager.Instance.GameHostOptions; GameManager.Instance.LogicOptions.SetGameOptions(GameOptionsManager.Instance.CurrentGameOptions); GameManager.Instance.LogicOptions.SyncOptions(); + return true; } public static void ShareOptionChange(uint optionId) { @@ -163,10 +172,18 @@ namespace TheOtherRoles { return selection + 1; } - // Option changes - public void updateSelection(int newSelection) { - selection = Mathf.Clamp((newSelection + selections.Length) % selections.Length, 0, selections.Length - 1); + public void updateSelection(int newSelection, bool notifyUsers = true) { + newSelection = Mathf.Clamp((newSelection + selections.Length) % selections.Length, 0, selections.Length - 1); + if (AmongUsClient.Instance?.AmClient == true && notifyUsers && selection != newSelection) { + DestroyableSingleton.Instance.Notifier.AddSettingsChangeMessage((StringNames)(this.id + 6000), selections[newSelection].ToString(), false); + try { + if (GameStartManager.Instance != null && GameStartManager.Instance.LobbyInfoPane != null && GameStartManager.Instance.LobbyInfoPane.LobbyViewSettingsPane != null && GameStartManager.Instance.LobbyInfoPane.LobbyViewSettingsPane.gameObject.activeSelf) { + LobbyViewSettingsPaneChangeTabPatch.Postfix(GameStartManager.Instance.LobbyInfoPane.LobbyViewSettingsPane, GameStartManager.Instance.LobbyInfoPane.LobbyViewSettingsPane.currentTab); + } + } catch { } + } + selection = newSelection; try { if (onChange != null) onChange(); } catch { } @@ -208,9 +225,11 @@ namespace TheOtherRoles { } } - public static void deserializeOptions(byte[] inputValues) { + public static int deserializeOptions(byte[] inputValues) { BinaryReader reader = new BinaryReader(new MemoryStream(inputValues)); int lastId = -1; + bool somethingApplied = false; + int errors = 0; while (reader.BaseStream.Position < inputValues.Length) { try { int selection = reader.ReadByte(); @@ -224,12 +243,20 @@ namespace TheOtherRoles { } if (id == 0) continue; lastId = id; - CustomOption option = CustomOption.options.First(option => option.id == id); - option.updateSelection(selection); + CustomOption option = options.First(option => option.id == id); + option.entry = TheOtherRolesPlugin.Instance.Config.Bind($"Preset{preset}", option.id.ToString(), option.defaultSelection); + option.selection = selection; + if (option.optionBehaviour != null && option.optionBehaviour is StringOption stringOption) { + stringOption.oldValue = stringOption.Value = option.selection; + stringOption.ValueText.text = option.selections[option.selection].ToString(); + } + somethingApplied = true; } catch (Exception e) { - TheOtherRolesPlugin.Logger.LogWarning($"{e}: while deserializing - tried to paste invalid settings!"); + TheOtherRolesPlugin.Logger.LogWarning($"id:{lastId}:{e}: while deserializing - tried to paste invalid settings!"); + errors++; } } + return Convert.ToInt32(somethingApplied) + (errors > 0 ? 0 : 1); } // Copy to or paste from clipboard (as string) @@ -237,587 +264,527 @@ namespace TheOtherRoles { GUIUtility.systemCopyBuffer = $"{TheOtherRolesPlugin.VersionString}!{Convert.ToBase64String(serializeOptions())}!{vanillaSettings.Value}"; } - public static bool pasteFromClipboard() { + public static int pasteFromClipboard() { string allSettings = GUIUtility.systemCopyBuffer; + int torOptionsFine = 0; + bool vanillaOptionsFine = false; try { var settingsSplit = allSettings.Split("!"); - string versionInfo = settingsSplit[0]; + Version versionInfo = Version.Parse(settingsSplit[0]); string torSettings = settingsSplit[1]; string vanillaSettingsSub = settingsSplit[2]; - deserializeOptions(Convert.FromBase64String(torSettings)); - - vanillaSettings.Value = vanillaSettingsSub; - loadVanillaOptions(); - return true; + torOptionsFine = deserializeOptions(Convert.FromBase64String(torSettings)); + ShareOptionSelections(); + if (TheOtherRolesPlugin.Version > versionInfo && versionInfo < Version.Parse("4.6.0")) { + vanillaOptionsFine = false; + FastDestroyableSingleton.Instance.Chat.AddChat(PlayerControl.LocalPlayer, "Host Info: Pasting vanilla settings failed, TOR Options applied!"); + } else { + vanillaSettings.Value = vanillaSettingsSub; + vanillaOptionsFine = loadVanillaOptions(); + } } catch (Exception e) { - TheOtherRolesPlugin.Logger.LogWarning($"{e}: tried to paste invalid settings!"); + TheOtherRolesPlugin.Logger.LogWarning($"{e}: tried to paste invalid settings!\n{allSettings}"); + string errorStr = allSettings.Length > 2 ? allSettings.Substring(0, 3) : "(empty clipboard) "; + FastDestroyableSingleton.Instance.Chat.AddChat(PlayerControl.LocalPlayer, $"Host Info: You tried to paste invalid settings: \"{errorStr}...\""); SoundEffectsManager.Load(); SoundEffectsManager.play("fail"); - return false; } + return Convert.ToInt32(vanillaOptionsFine) + torOptionsFine; } } - [HarmonyPatch(typeof(GameOptionsMenu), nameof(GameOptionsMenu.Start))] - class GameOptionsMenuStartPatch { - public static void Postfix(GameOptionsMenu __instance) { - switch (TORMapOptions.gameMode) { - case CustomGamemodes.Classic: - createClassicTabs(__instance); - break; - case CustomGamemodes.Guesser: - createGuesserTabs(__instance); - break; - case CustomGamemodes.HideNSeek: - createHideNSeekTabs(__instance); - break; - case CustomGamemodes.PropHunt: - createPropHuntTabs(__instance); - break; - } - // create copy to clipboard and paste from clipboard buttons. - var template = GameObject.Find("CloseButton"); - var copyButton = GameObject.Instantiate(template, template.transform.parent); - copyButton.transform.localPosition += Vector3.down * 0.8f; - var copyButtonPassive = copyButton.GetComponent(); - var copyButtonRenderer = copyButton.GetComponent(); - copyButtonRenderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.CopyButton.png", 175f); - copyButtonPassive.OnClick.RemoveAllListeners(); - copyButtonPassive.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); - copyButtonPassive.OnClick.AddListener((System.Action)(() => { - copyToClipboard(); - copyButtonRenderer.color = Color.green; - __instance.StartCoroutine(Effects.Lerp(1f, new System.Action((p) => { - if (p > 0.95) - copyButtonRenderer.color = Color.white; - }))); - })); - var pasteButton = GameObject.Instantiate(template, template.transform.parent); - pasteButton.transform.localPosition += Vector3.down * 1.6f; - var pasteButtonPassive = pasteButton.GetComponent(); - var pasteButtonRenderer = pasteButton.GetComponent(); - pasteButtonRenderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.PasteButton.png", 175f); - pasteButtonPassive.OnClick.RemoveAllListeners(); - pasteButtonPassive.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); - pasteButtonPassive.OnClick.AddListener((System.Action)(() => { - pasteButtonRenderer.color = Color.yellow; - bool success = pasteFromClipboard(); - pasteButtonRenderer.color = success ? Color.green : Color.red; - __instance.StartCoroutine(Effects.Lerp(1f, new System.Action((p) => { - if (p > 0.95) - pasteButtonRenderer.color = Color.white; - }))); - })); + [HarmonyPatch(typeof(GameSettingMenu), nameof(GameSettingMenu.ChangeTab))] + class GameOptionsMenuChangeTabPatch { + public static void Postfix(GameSettingMenu __instance, int tabNum, bool previewOnly) { + if (previewOnly) return; + foreach (var tab in GameOptionsMenuStartPatch.currentTabs) { + if (tab != null) + tab.SetActive(false); + } + foreach (var pbutton in GameOptionsMenuStartPatch.currentButtons) { + pbutton.SelectButton(false); + } + if (tabNum > 2) { + tabNum -= 3; + GameOptionsMenuStartPatch.currentTabs[tabNum].SetActive(true); + GameOptionsMenuStartPatch.currentButtons[tabNum].SelectButton(true); + } } + } - private static void createClassicTabs(GameOptionsMenu __instance) { - bool isReturn = setNames( - new Dictionary() { - ["TORSettings"] = "The Other Roles Settings", - ["ImpostorSettings"] = "Impostor Roles Settings", - ["NeutralSettings"] = "Neutral Roles Settings", - ["CrewmateSettings"] = "Crewmate Roles Settings", - ["ModifierSettings"] = "Modifier Settings" - }); - - if (isReturn) return; - - // Setup TOR tab - var template = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); - if (template == null) return; - var gameSettings = GameObject.Find("Game Settings"); - var gameSettingMenu = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); - - var torSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var torMenu = getMenu(torSettings, "TORSettings"); - - var impostorSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var impostorMenu = getMenu(impostorSettings, "ImpostorSettings"); - - var neutralSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var neutralMenu = getMenu(neutralSettings, "NeutralSettings"); - - var crewmateSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var crewmateMenu = getMenu(crewmateSettings, "CrewmateSettings"); - - var modifierSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var modifierMenu = getMenu(modifierSettings, "ModifierSettings"); - - var roleTab = GameObject.Find("RoleTab"); - var gameTab = GameObject.Find("GameTab"); - - var torTab = UnityEngine.Object.Instantiate(roleTab, roleTab.transform.parent); - var torTabHighlight = getTabHighlight(torTab, "TheOtherRolesTab", "TheOtherRoles.Resources.TabIcon.png"); - - var impostorTab = UnityEngine.Object.Instantiate(roleTab, torTab.transform); - var impostorTabHighlight = getTabHighlight(impostorTab, "ImpostorTab", "TheOtherRoles.Resources.TabIconImpostor.png"); - - var neutralTab = UnityEngine.Object.Instantiate(roleTab, impostorTab.transform); - var neutralTabHighlight = getTabHighlight(neutralTab, "NeutralTab", "TheOtherRoles.Resources.TabIconNeutral.png"); - - var crewmateTab = UnityEngine.Object.Instantiate(roleTab, neutralTab.transform); - var crewmateTabHighlight = getTabHighlight(crewmateTab, "CrewmateTab", "TheOtherRoles.Resources.TabIconCrewmate.png"); - - var modifierTab = UnityEngine.Object.Instantiate(roleTab, crewmateTab.transform); - var modifierTabHighlight = getTabHighlight(modifierTab, "ModifierTab", "TheOtherRoles.Resources.TabIconModifier.png"); - - // Position of Tab Icons - gameTab.transform.position += Vector3.left * 3f; - roleTab.transform.position += Vector3.left * 3f; - torTab.transform.position += Vector3.left * 2f; - impostorTab.transform.localPosition = Vector3.right * 1f; - neutralTab.transform.localPosition = Vector3.right * 1f; - crewmateTab.transform.localPosition = Vector3.right * 1f; - modifierTab.transform.localPosition = Vector3.right * 1f; - - var tabs = new GameObject[] { gameTab, roleTab, torTab, impostorTab, neutralTab, crewmateTab, modifierTab }; - var settingsHighlightMap = new Dictionary { - [gameSettingMenu.RegularGameSettings] = gameSettingMenu.GameSettingsHightlight, - [gameSettingMenu.RolesSettings.gameObject] = gameSettingMenu.RolesSettingsHightlight, - [torSettings.gameObject] = torTabHighlight, - [impostorSettings.gameObject] = impostorTabHighlight, - [neutralSettings.gameObject] = neutralTabHighlight, - [crewmateSettings.gameObject] = crewmateTabHighlight, - [modifierSettings.gameObject] = modifierTabHighlight - }; - for (int i = 0; i < tabs.Length; i++) { - var button = tabs[i].GetComponentInChildren(); - if (button == null) continue; - int copiedIndex = i; - button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); - button.OnClick.AddListener((System.Action)(() => { - setListener(settingsHighlightMap, copiedIndex); - })); + [HarmonyPatch(typeof(LobbyViewSettingsPane), nameof(LobbyViewSettingsPane.SetTab))] + class LobbyViewSettingsPaneRefreshTabPatch { + public static bool Prefix(LobbyViewSettingsPane __instance) { + if ((int)__instance.currentTab < 15) { + LobbyViewSettingsPaneChangeTabPatch.Postfix(__instance, __instance.currentTab); + return false; } + return true; + } + } - destroyOptions(new List>{ - torMenu.GetComponentsInChildren().ToList(), - impostorMenu.GetComponentsInChildren().ToList(), - neutralMenu.GetComponentsInChildren().ToList(), - crewmateMenu.GetComponentsInChildren().ToList(), - modifierMenu.GetComponentsInChildren().ToList() - }); - - List torOptions = new List(); - List impostorOptions = new List(); - List neutralOptions = new List(); - List crewmateOptions = new List(); - List modifierOptions = new List(); - - - List menus = new List() { torMenu.transform, impostorMenu.transform, neutralMenu.transform, crewmateMenu.transform, modifierMenu.transform }; - List> optionBehaviours = new List>() { torOptions, impostorOptions, neutralOptions, crewmateOptions, modifierOptions }; - - for (int i = 0; i < CustomOption.options.Count; i++) { - CustomOption option = CustomOption.options[i]; - if ((int)option.type > 4) continue; - if (option.optionBehaviour == null) { - StringOption stringOption = UnityEngine.Object.Instantiate(template, menus[(int)option.type]); - optionBehaviours[(int)option.type].Add(stringOption); - stringOption.OnValueChanged = new Action((o) => { }); - stringOption.TitleText.text = option.name; - stringOption.Value = stringOption.oldValue = option.selection; - stringOption.ValueText.text = option.selections[option.selection].ToString(); + [HarmonyPatch(typeof(LobbyViewSettingsPane), nameof(LobbyViewSettingsPane.ChangeTab))] + class LobbyViewSettingsPaneChangeTabPatch { + public static void Postfix(LobbyViewSettingsPane __instance, StringNames category) { + int tabNum = (int)category; - option.optionBehaviour = stringOption; - } - option.optionBehaviour.gameObject.SetActive(true); + foreach (var pbutton in LobbyViewSettingsPatch.currentButtons) { + pbutton.SelectButton(false); } + if (tabNum > 20) // StringNames are in the range of 3000+ + return; + __instance.taskTabButton.SelectButton(false); - setOptions( - new List { torMenu, impostorMenu, neutralMenu, crewmateMenu, modifierMenu }, - new List> { torOptions, impostorOptions, neutralOptions, crewmateOptions, modifierOptions }, - new List { torSettings, impostorSettings, neutralSettings, crewmateSettings, modifierSettings } - ); + if (tabNum > 2) { + tabNum -= 3; + //GameOptionsMenuStartPatch.currentTabs[tabNum].SetActive(true); + LobbyViewSettingsPatch.currentButtons[tabNum].SelectButton(true); + LobbyViewSettingsPatch.drawTab(__instance, LobbyViewSettingsPatch.currentButtonTypes[tabNum]); + } + } + } - adaptTaskCount(__instance); + [HarmonyPatch(typeof(LobbyViewSettingsPane), nameof(LobbyViewSettingsPane.Update))] + class LobbyViewSettingsPaneUpdatePatch { + public static void Postfix(LobbyViewSettingsPane __instance) { + if (LobbyViewSettingsPatch.currentButtons.Count == 0) { + LobbyViewSettingsPatch.gameModeChangedFlag = true; + LobbyViewSettingsPatch.Postfix(__instance); + + } } + } + - private static void createGuesserTabs(GameOptionsMenu __instance) { - bool isReturn = setNames( - new Dictionary() { - ["TORSettings"] = "The Other Roles Settings", - ["GuesserSettings"] = "Guesser Mode Settings", - ["ImpostorSettings"] = "Impostor Roles Settings", - ["NeutralSettings"] = "Neutral Roles Settings", - ["CrewmateSettings"] = "Crewmate Roles Settings", - ["ModifierSettings"] = "Modifier Settings" - }); - - if (isReturn) return; - - // Setup TOR tab - var template = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); - if (template == null) return; - var gameSettings = GameObject.Find("Game Settings"); - var gameSettingMenu = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); - - var torSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var torMenu = getMenu(torSettings, "TORSettings"); - - var guesserSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var guesserMenu = getMenu(guesserSettings, "GuesserSettings"); - - var impostorSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var impostorMenu = getMenu(impostorSettings, "ImpostorSettings"); - - var neutralSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var neutralMenu = getMenu(neutralSettings, "NeutralSettings"); - - var crewmateSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var crewmateMenu = getMenu(crewmateSettings, "CrewmateSettings"); - - var modifierSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var modifierMenu = getMenu(modifierSettings, "ModifierSettings"); - - var roleTab = GameObject.Find("RoleTab"); - var gameTab = GameObject.Find("GameTab"); - - var torTab = UnityEngine.Object.Instantiate(roleTab, gameTab.transform.parent); - var torTabHighlight = getTabHighlight(torTab, "TheOtherRolesTab", "TheOtherRoles.Resources.TabIcon.png"); - - var guesserTab = UnityEngine.Object.Instantiate(roleTab, torTab.transform); - var guesserTabHighlight = getTabHighlight(guesserTab, "GuesserTab", "TheOtherRoles.Resources.TabIconGuesserSettings.png"); - - var impostorTab = UnityEngine.Object.Instantiate(roleTab, guesserTab.transform); - var impostorTabHighlight = getTabHighlight(impostorTab, "ImpostorTab", "TheOtherRoles.Resources.TabIconImpostor.png"); - - var neutralTab = UnityEngine.Object.Instantiate(roleTab, impostorTab.transform); - var neutralTabHighlight = getTabHighlight(neutralTab, "NeutralTab", "TheOtherRoles.Resources.TabIconNeutral.png"); - - var crewmateTab = UnityEngine.Object.Instantiate(roleTab, neutralTab.transform); - var crewmateTabHighlight = getTabHighlight(crewmateTab, "CrewmateTab", "TheOtherRoles.Resources.TabIconCrewmate.png"); - - var modifierTab = UnityEngine.Object.Instantiate(roleTab, crewmateTab.transform); - var modifierTabHighlight = getTabHighlight(modifierTab, "ModifierTab", "TheOtherRoles.Resources.TabIconModifier.png"); - - roleTab.active = false; - // Position of Tab Icons - gameTab.transform.position += Vector3.left * 3f; - torTab.transform.position += Vector3.left * 3f; - guesserTab.transform.localPosition = Vector3.right * 1f; - impostorTab.transform.localPosition = Vector3.right * 1f; - neutralTab.transform.localPosition = Vector3.right * 1f; - crewmateTab.transform.localPosition = Vector3.right * 1f; - modifierTab.transform.localPosition = Vector3.right * 1f; - - var tabs = new GameObject[] { gameTab, torTab, impostorTab, neutralTab, crewmateTab, modifierTab, guesserTab }; - var settingsHighlightMap = new Dictionary { - [gameSettingMenu.RegularGameSettings] = gameSettingMenu.GameSettingsHightlight, - [torSettings.gameObject] = torTabHighlight, - [impostorSettings.gameObject] = impostorTabHighlight, - [neutralSettings.gameObject] = neutralTabHighlight, - [crewmateSettings.gameObject] = crewmateTabHighlight, - [modifierSettings.gameObject] = modifierTabHighlight, - [guesserSettings.gameObject] = guesserTabHighlight - }; - for (int i = 0; i < tabs.Length; i++) { - var button = tabs[i].GetComponentInChildren(); - if (button == null) continue; - int copiedIndex = i; - button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); - button.OnClick.AddListener((System.Action)(() => { - setListener(settingsHighlightMap, copiedIndex); + [HarmonyPatch(typeof(LobbyViewSettingsPane), nameof(LobbyViewSettingsPane.Awake))] + class LobbyViewSettingsPatch{ + public static List currentButtons = new(); + public static List currentButtonTypes = new(); + public static bool gameModeChangedFlag = false; + + public static void createCustomButton(LobbyViewSettingsPane __instance, int targetMenu, string buttonName, string buttonText, CustomOptionType optionType) { + buttonName = "View" + buttonName; + var buttonTemplate = GameObject.Find("OverviewTab"); + var torSettingsButton = GameObject.Find(buttonName); + if (torSettingsButton == null) { + torSettingsButton = GameObject.Instantiate(buttonTemplate, buttonTemplate.transform.parent); + torSettingsButton.transform.localPosition += Vector3.right * 1.75f * (targetMenu - 2); + torSettingsButton.name = buttonName; + __instance.StartCoroutine(Effects.Lerp(2f, new Action(p => { torSettingsButton.transform.FindChild("FontPlacer").GetComponentInChildren().text = buttonText; }))); + var torSettingsPassiveButton = torSettingsButton.GetComponent(); + torSettingsPassiveButton.OnClick.RemoveAllListeners(); + torSettingsPassiveButton.OnClick.AddListener((System.Action)(() => { + __instance.ChangeTab((StringNames)targetMenu); })); + torSettingsPassiveButton.OnMouseOut.RemoveAllListeners(); + torSettingsPassiveButton.OnMouseOver.RemoveAllListeners(); + torSettingsPassiveButton.SelectButton(false); + currentButtons.Add(torSettingsPassiveButton); + currentButtonTypes.Add(optionType); } + } - destroyOptions(new List>{ - torMenu.GetComponentsInChildren().ToList(), - guesserMenu.GetComponentsInChildren().ToList(), - impostorMenu.GetComponentsInChildren().ToList(), - neutralMenu.GetComponentsInChildren().ToList(), - crewmateMenu.GetComponentsInChildren().ToList(), - modifierMenu.GetComponentsInChildren().ToList() - }); - - List torOptions = new List(); - List guesserOptions = new List(); - List impostorOptions = new List(); - List neutralOptions = new List(); - List crewmateOptions = new List(); - List modifierOptions = new List(); - - - List menus = new List() { torMenu.transform, impostorMenu.transform, neutralMenu.transform, crewmateMenu.transform, modifierMenu.transform, guesserMenu.transform }; - List> optionBehaviours = new List>() { torOptions, impostorOptions, neutralOptions, crewmateOptions, modifierOptions, guesserOptions }; - List exludedIds = new List { 310, 311, 312, 313, 314, 315, 316, 317, 318 }; - - for (int i = 0; i < CustomOption.options.Count; i++) { - CustomOption option = CustomOption.options[i]; - if (exludedIds.Contains(option.id)) continue; - if ((int)option.type > 5) continue; - if (option.optionBehaviour == null) { - StringOption stringOption = UnityEngine.Object.Instantiate(template, menus[(int)option.type]); - optionBehaviours[(int)option.type].Add(stringOption); - stringOption.OnValueChanged = new Action((o) => { }); - stringOption.TitleText.text = option.name; - stringOption.Value = stringOption.oldValue = option.selection; - stringOption.ValueText.text = option.selections[option.selection].ToString(); + public static void Postfix(LobbyViewSettingsPane __instance) { + currentButtons.ForEach(x => x?.Destroy()); + currentButtons.Clear(); + currentButtonTypes.Clear(); - option.optionBehaviour = stringOption; - } - option.optionBehaviour.gameObject.SetActive(true); - } + removeVanillaTabs(__instance); - setOptions( - new List { torMenu, impostorMenu, neutralMenu, crewmateMenu, modifierMenu, guesserMenu }, - new List> { torOptions, impostorOptions, neutralOptions, crewmateOptions, modifierOptions, guesserOptions }, - new List { torSettings, impostorSettings, neutralSettings, crewmateSettings, modifierSettings, guesserSettings } - ); + createSettingTabs(__instance); - adaptTaskCount(__instance); } - private static void createHideNSeekTabs(GameOptionsMenu __instance) { - bool isReturn = setNames( - new Dictionary(){ - ["TORSettings"] = "The Other Roles Settings", - ["HideNSeekSettings"] = "Hide 'N Seek Settings" - }); - - if (isReturn) return; - - // Setup TOR tab - var template = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); - if (template == null) return; - var gameSettings = GameObject.Find("Game Settings"); - var gameSettingMenu = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); - - var torSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var torMenu = getMenu(torSettings, "TORSettings"); - - var hideNSeekSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var hideNSeekMenu = getMenu(hideNSeekSettings, "HideNSeekSettings"); - - var roleTab = GameObject.Find("RoleTab"); - var gameTab = GameObject.Find("GameTab"); - - var torTab = UnityEngine.Object.Instantiate(roleTab, gameTab.transform.parent); - var torTabHighlight = getTabHighlight(torTab, "TheOtherRolesTab", "TheOtherRoles.Resources.TabIconHideNSeekSettings.png"); - - var hideNSeekTab = UnityEngine.Object.Instantiate(roleTab, torTab.transform); - var hideNSeekTabHighlight = getTabHighlight(hideNSeekTab, "HideNSeekTab", "TheOtherRoles.Resources.TabIconHideNSeekRoles.png"); - - roleTab.active = false; - gameTab.active = false; - - // Position of Tab Icons - torTab.transform.position += Vector3.left * 3f; - hideNSeekTab.transform.position += Vector3.right * 1f; - - var tabs = new GameObject[] { torTab, hideNSeekTab}; - var settingsHighlightMap = new Dictionary { - [torSettings.gameObject] = torTabHighlight, - [hideNSeekSettings.gameObject] = hideNSeekTabHighlight - }; - for (int i = 0; i < tabs.Length; i++) { - var button = tabs[i].GetComponentInChildren(); - if (button == null) continue; - int copiedIndex = i; - button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); - button.OnClick.AddListener((System.Action)(() => { - setListener(settingsHighlightMap, copiedIndex); - })); + public static void removeVanillaTabs(LobbyViewSettingsPane __instance) { + GameObject.Find("RolesTabs")?.Destroy(); + var overview = GameObject.Find("OverviewTab"); + if (!gameModeChangedFlag) { + overview.transform.localScale = new Vector3(0.5f * overview.transform.localScale.x, overview.transform.localScale.y, overview.transform.localScale.z); + overview.transform.localPosition += new Vector3(-1.2f, 0f, 0f); + } + overview.transform.Find("FontPlacer").transform.localScale = new Vector3(1.35f, 1f, 1f); + overview.transform.Find("FontPlacer").transform.localPosition = new Vector3(-0.6f, -0.1f, 0f); + gameModeChangedFlag = false; + } - destroyOptions(new List>{ - torMenu.GetComponentsInChildren().ToList(), - hideNSeekMenu.GetComponentsInChildren().ToList() - }); - - List torOptions = new List(); - List hideNSeekOptions = new List(); - - List menus = new List() { torMenu.transform, hideNSeekMenu.transform}; - List> optionBehaviours = new List>() { torOptions, hideNSeekOptions }; - - for (int i = 0; i < CustomOption.options.Count; i++) { - CustomOption option = CustomOption.options[i]; - if (option.type != CustomOptionType.HideNSeekMain && option.type != CustomOptionType.HideNSeekRoles) continue; - if (option.optionBehaviour == null) { - int index = (int)option.type - 6; - StringOption stringOption = UnityEngine.Object.Instantiate(template, menus[index]); - optionBehaviours[index].Add(stringOption); - stringOption.OnValueChanged = new Action((o) => { }); - stringOption.TitleText.text = option.name; - stringOption.Value = stringOption.oldValue = option.selection; - stringOption.ValueText.text = option.selections[option.selection].ToString(); + public static void drawTab(LobbyViewSettingsPane __instance, CustomOptionType optionType) { + + var relevantOptions = options.Where(x => x.type == optionType || x.type == CustomOption.CustomOptionType.Guesser && optionType == CustomOptionType.General).ToList(); + + if ((int)optionType == 99) { + // Create 4 Groups with Role settings only + relevantOptions.Clear(); + relevantOptions.AddRange(options.Where(x => x.type == CustomOptionType.Impostor && x.isHeader)); + relevantOptions.AddRange(options.Where(x => x.type == CustomOptionType.Neutral && x.isHeader)); + relevantOptions.AddRange(options.Where(x => x.type == CustomOptionType.Crewmate && x.isHeader)); + relevantOptions.AddRange(options.Where(x => x.type == CustomOptionType.Modifier && x.isHeader)); + foreach (var option in options) { + if (option.parent != null && option.parent.getSelection() > 0) { + if (option.id == 103) //Deputy + relevantOptions.Insert(relevantOptions.IndexOf(CustomOptionHolder.sheriffSpawnRate) + 1, option); + else if (option.id == 224) //Sidekick + relevantOptions.Insert(relevantOptions.IndexOf(CustomOptionHolder.jackalSpawnRate) + 1, option); + else if (option.id == 358) //Prosecutor + relevantOptions.Insert(relevantOptions.IndexOf(CustomOptionHolder.lawyerSpawnRate) + 1, option); + } + } + } + + if (TORMapOptions.gameMode == CustomGamemodes.Guesser) // Exclude guesser options in neutral mode + relevantOptions = relevantOptions.Where(x => !(new List { 310, 311, 312, 313, 314, 315, 316, 317, 318 }).Contains(x.id)).ToList(); + + for (int j = 0; j < __instance.settingsInfo.Count; j++) { + __instance.settingsInfo[j].gameObject.Destroy(); + } + __instance.settingsInfo.Clear(); + + float num = 1.44f; + int i = 0; + int singles = 0; + int headers = 0; + int lines = 0; + var curType = CustomOptionType.Modifier; + + foreach (var option in relevantOptions) { + if (option.isHeader && (int)optionType != 99 || (int)optionType == 99 && curType != option.type) { + curType = option.type; + if (i != 0) num -= 0.59f; + if (i % 2 != 0) singles++; + headers++; // for header + CategoryHeaderMasked categoryHeaderMasked = UnityEngine.Object.Instantiate(__instance.categoryHeaderOrigin); + categoryHeaderMasked.SetHeader(StringNames.ImpostorsCategory, 61); + categoryHeaderMasked.Title.text = option.heading != "" ? option.heading : option.name; + if ((int)optionType == 99) + categoryHeaderMasked.Title.text = new Dictionary() { { CustomOptionType.Impostor, "Impostor Roles" }, { CustomOptionType.Neutral, "Neutral Roles" }, + { CustomOptionType.Crewmate, "Crewmate Roles" }, { CustomOptionType.Modifier, "Modifiers" } }[curType]; + categoryHeaderMasked.Title.outlineColor = Color.white; + categoryHeaderMasked.Title.outlineWidth = 0.2f; + categoryHeaderMasked.transform.SetParent(__instance.settingsContainer); + categoryHeaderMasked.transform.localScale = Vector3.one; + categoryHeaderMasked.transform.localPosition = new Vector3(-9.77f, num, -2f); + __instance.settingsInfo.Add(categoryHeaderMasked.gameObject); + num -= 0.85f; + i = 0; + } - option.optionBehaviour = stringOption; + ViewSettingsInfoPanel viewSettingsInfoPanel = UnityEngine.Object.Instantiate(__instance.infoPanelOrigin); + viewSettingsInfoPanel.transform.SetParent(__instance.settingsContainer); + viewSettingsInfoPanel.transform.localScale = Vector3.one; + float num2; + if (i % 2 == 0) { + lines++; + num2 = -8.95f; + if (i > 0) { + num -= 0.59f; + } + } else { + num2 = -3f; } - option.optionBehaviour.gameObject.SetActive(true); + viewSettingsInfoPanel.transform.localPosition = new Vector3(num2, num, -2f); + int value = option.getSelection(); + viewSettingsInfoPanel.SetInfo(StringNames.ImpostorsCategory, option.selections[value].ToString(), 61); + viewSettingsInfoPanel.titleText.text = option.name; + if (option.isHeader && (int)optionType != 99 && option.heading == "" && (option.type == CustomOptionType.Neutral || option.type == CustomOptionType.Crewmate || option.type == CustomOptionType.Impostor || option.type == CustomOptionType.Modifier)) { + viewSettingsInfoPanel.titleText.text = "Spawn Chance"; + } + if ((int)optionType == 99) { + viewSettingsInfoPanel.titleText.outlineColor = Color.white; + viewSettingsInfoPanel.titleText.outlineWidth = 0.2f; + if (option.type == CustomOptionType.Modifier) + viewSettingsInfoPanel.settingText.text = viewSettingsInfoPanel.settingText.text + GameOptionsDataPatch.buildModifierExtras(option); + } + __instance.settingsInfo.Add(viewSettingsInfoPanel.gameObject); + + i++; } + float actual_spacing = (headers * 0.85f + lines * 0.59f) / (headers + lines); + __instance.scrollBar.CalculateAndSetYBounds((float)(__instance.settingsInfo.Count + singles * 2 + headers), 2f, 6f, actual_spacing); - setOptions( - new List{torMenu, hideNSeekMenu}, - new List> {torOptions, hideNSeekOptions}, - new List {torSettings, hideNSeekSettings} - ); + } - torSettings.gameObject.SetActive(true); - torTabHighlight.enabled = true; - gameSettingMenu.RegularGameSettings.SetActive(false); - gameSettingMenu.GameSettingsHightlight.enabled = false; + public static void createSettingTabs(LobbyViewSettingsPane __instance) { + // Handle different gamemodes and tabs needed therein. + int next = 3; + if (TORMapOptions.gameMode == CustomGamemodes.Guesser || TORMapOptions.gameMode == CustomGamemodes.Classic) { + + // create TOR settings + createCustomButton(__instance, next++, "TORSettings", "TOR Settings", CustomOptionType.General); + // create TOR settings + createCustomButton(__instance, next++, "RoleOverview", "Role Overview", (CustomOptionType)99); + // IMp + createCustomButton(__instance, next++, "ImpostorSettings", "Impostor Roles", CustomOptionType.Impostor); + + // Neutral + createCustomButton(__instance, next++, "NeutralSettings", "Neutral Roles", CustomOptionType.Neutral); + // Crew + createCustomButton(__instance, next++, "CrewmateSettings", "Crewmate Roles", CustomOptionType.Crewmate); + // Modifier + createCustomButton(__instance, next++, "ModifierSettings", "Modifiers", CustomOptionType.Modifier); + + } else if (TORMapOptions.gameMode == CustomGamemodes.HideNSeek) { + // create Main HNS settings + createCustomButton(__instance, next++, "HideNSeekMain", "Hide 'N' Seek", CustomOptionType.HideNSeekMain); + // create HNS Role settings + createCustomButton(__instance, next++, "HideNSeekRoles", "Hide 'N' Seek Roles", CustomOptionType.HideNSeekRoles); + } else if (TORMapOptions.gameMode == CustomGamemodes.PropHunt) { + createCustomButton(__instance, next++, "PropHunt", "Prop Hunt", CustomOptionType.PropHunt); + } } + } + [HarmonyPatch(typeof(GameOptionsMenu), nameof(GameOptionsMenu.CreateSettings))] + class GameOptionsMenuCreateSettingsPatch { + public static void Postfix(GameOptionsMenu __instance) { + if (__instance.gameObject.name == "GAME SETTINGS TAB") + adaptTaskCount(__instance); + } - private static void createPropHuntTabs(GameOptionsMenu __instance) { - bool isReturn = setNames( - new Dictionary() { - ["TORSettings"] = "Prop Hunt Settings" - }); + private static void adaptTaskCount(GameOptionsMenu __instance) { + // Adapt task count for main options + var commonTasksOption = __instance.Children.ToArray().FirstOrDefault(x => x.TryCast()?.intOptionName == Int32OptionNames.NumCommonTasks).Cast(); + if (commonTasksOption != null) commonTasksOption.ValidRange = new FloatRange(0f, 4f); + var shortTasksOption = __instance.Children.ToArray().FirstOrDefault(x => x.TryCast()?.intOptionName == Int32OptionNames.NumShortTasks).TryCast(); + if (shortTasksOption != null) shortTasksOption.ValidRange = new FloatRange(0f, 23f); + var longTasksOption = __instance.Children.ToArray().FirstOrDefault(x => x.TryCast()?.intOptionName == Int32OptionNames.NumLongTasks).TryCast(); + if (longTasksOption != null) longTasksOption.ValidRange = new FloatRange(0f, 15f); + } + } - if (isReturn) return; - // Setup TOR tab - var template = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); - if (template == null) return; - var gameSettings = GameObject.Find("Game Settings"); - var gameSettingMenu = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); + [HarmonyPatch(typeof(GameSettingMenu), nameof(GameSettingMenu.Start))] + class GameOptionsMenuStartPatch { + public static List currentTabs = new(); + public static List currentButtons = new(); - var torSettings = UnityEngine.Object.Instantiate(gameSettings, gameSettings.transform.parent); - var torMenu = getMenu(torSettings, "TORSettings"); - var roleTab = GameObject.Find("RoleTab"); - var gameTab = GameObject.Find("GameTab"); + public static void Postfix(GameSettingMenu __instance) { + currentTabs.ForEach(x => x?.Destroy()); + currentButtons.ForEach(x => x?.Destroy()); + currentTabs = new(); + currentButtons = new(); - var torTab = UnityEngine.Object.Instantiate(roleTab, gameTab.transform.parent); - var torTabHighlight = getTabHighlight(torTab, "TheOtherRolesTab", "TheOtherRoles.Resources.TabIconPropHuntSettings.png"); + if (GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek) return; - roleTab.active = false; - gameTab.active = false; + removeVanillaTabs(__instance); - // Position of Tab Icons - torTab.transform.position += Vector3.left * 3f; - - var tabs = new GameObject[] {torTab}; - var settingsHighlightMap = new Dictionary { - [torSettings.gameObject] = torTabHighlight - }; - for (int i = 0; i < tabs.Length; i++) { - var button = tabs[i].GetComponentInChildren(); - if (button == null) continue; - int copiedIndex = i; - button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); - button.OnClick.AddListener((System.Action)(() => { - setListener(settingsHighlightMap, copiedIndex); - })); - } - - destroyOptions(new List>{ - torMenu.GetComponentsInChildren().ToList(), - }); - - List torOptions = new List(); - - List menus = new List() { torMenu.transform }; - List> optionBehaviours = new List>() { torOptions }; - - for (int i = 0; i < CustomOption.options.Count; i++) { - CustomOption option = CustomOption.options[i]; - if (option.type != CustomOptionType.PropHunt) continue; - if (option.optionBehaviour == null) { - int index = 0; - StringOption stringOption = UnityEngine.Object.Instantiate(template, menus[index]); - optionBehaviours[index].Add(stringOption); - stringOption.OnValueChanged = new Action((o) => { }); - stringOption.TitleText.text = option.name; - stringOption.Value = stringOption.oldValue = option.selection; - stringOption.ValueText.text = option.selections[option.selection].ToString(); + createSettingTabs(__instance); - option.optionBehaviour = stringOption; - } - option.optionBehaviour.gameObject.SetActive(true); - } + var GOMGameObject = GameObject.Find("GAME SETTINGS TAB"); - setOptions( - new List { torMenu}, - new List> { torOptions }, - new List { torSettings } - ); - torSettings.gameObject.SetActive(true); - torTabHighlight.enabled = true; - gameSettingMenu.RegularGameSettings.SetActive(false); - gameSettingMenu.GameSettingsHightlight.enabled = false; - } - - private static void setListener(Dictionary settingsHighlightMap, int index) { - foreach (KeyValuePair entry in settingsHighlightMap) { - entry.Key.SetActive(false); - entry.Value.enabled = false; - } - settingsHighlightMap.ElementAt(index).Key.SetActive(true); - settingsHighlightMap.ElementAt(index).Value.enabled = true; + // create copy to clipboard and paste from clipboard buttons. + var template = GameObject.Find("PlayerOptionsMenu(Clone)").transform.Find("CloseButton").gameObject; + var holderGO = new GameObject("copyPasteButtonParent"); + var bgrenderer = holderGO.AddComponent(); + bgrenderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.CopyPasteBG.png", 175f); + holderGO.transform.SetParent(template.transform.parent, false); + holderGO.transform.localPosition = template.transform.localPosition + new Vector3(-8.3f, 0.73f, -2f); + holderGO.layer = template.layer; + holderGO.SetActive(true); + var copyButton = GameObject.Instantiate(template, holderGO.transform); + copyButton.transform.localPosition = new Vector3(-0.3f, 0.02f, -2f); + var copyButtonPassive = copyButton.GetComponent(); + var copyButtonRenderer = copyButton.GetComponentInChildren(); + var copyButtonActiveRenderer = copyButton.transform.GetChild(1).GetComponent(); + copyButtonRenderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Copy.png", 100f); + copyButton.transform.GetChild(1).transform.localPosition = Vector3.zero; + copyButtonActiveRenderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.CopyActive.png", 100f); + copyButtonPassive.OnClick.RemoveAllListeners(); + copyButtonPassive.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); + copyButtonPassive.OnClick.AddListener((System.Action)(() => { + copyToClipboard(); + copyButtonRenderer.color = Color.green; + copyButtonActiveRenderer.color = Color.green; + __instance.StartCoroutine(Effects.Lerp(1f, new System.Action((p) => { + if (p > 0.95) { + copyButtonRenderer.color = Color.white; + copyButtonActiveRenderer.color = Color.white; + } + }))); + })); + var pasteButton = GameObject.Instantiate(template, holderGO.transform); + pasteButton.transform.localPosition = new Vector3(0.3f, 0.02f, -2f); + var pasteButtonPassive = pasteButton.GetComponent(); + var pasteButtonRenderer = pasteButton.GetComponentInChildren(); + var pasteButtonActiveRenderer = pasteButton.transform.GetChild(1).GetComponent(); + pasteButtonRenderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Paste.png", 100f); + pasteButtonActiveRenderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.PasteActive.png", 100f); + pasteButtonPassive.OnClick.RemoveAllListeners(); + pasteButtonPassive.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); + pasteButtonPassive.OnClick.AddListener((System.Action)(() => { + pasteButtonRenderer.color = Color.yellow; + int success = pasteFromClipboard(); + pasteButtonRenderer.color = success == 3 ? Color.green : success == 0 ? Color.red : Color.yellow; + pasteButtonActiveRenderer.color = success == 3 ? Color.green : success == 0 ? Color.red : Color.yellow; + __instance.StartCoroutine(Effects.Lerp(1f, new System.Action((p) => { + if (p > 0.95) { + pasteButtonRenderer.color = Color.white; + pasteButtonActiveRenderer.color = Color.white; + } + }))); + })); } - private static void destroyOptions (List> optionBehavioursList) { - foreach (List optionBehaviours in optionBehavioursList) { - foreach (OptionBehaviour option in optionBehaviours) - UnityEngine.Object.Destroy(option.gameObject); - } - } + private static void createSettings(GameOptionsMenu menu, List options) { + float num = 1.5f; + foreach (CustomOption option in options) { + if (option.isHeader) { + CategoryHeaderMasked categoryHeaderMasked = UnityEngine.Object.Instantiate(menu.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, menu.settingsContainer); + categoryHeaderMasked.SetHeader(StringNames.ImpostorsCategory, 20); + categoryHeaderMasked.Title.text = option.heading != "" ? option.heading : option.name; + categoryHeaderMasked.Title.outlineColor = Color.white; + categoryHeaderMasked.Title.outlineWidth = 0.2f; + categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f; + categoryHeaderMasked.transform.localPosition = new Vector3(-0.903f, num, -2f); + num -= 0.63f; + } + OptionBehaviour optionBehaviour = UnityEngine.Object.Instantiate(menu.stringOptionOrigin, Vector3.zero, Quaternion.identity, menu.settingsContainer); + optionBehaviour.transform.localPosition = new Vector3(0.952f, num, -2f); + optionBehaviour.SetClickMask(menu.ButtonClickMask); + + // "SetUpFromData" + SpriteRenderer[] componentsInChildren = optionBehaviour.GetComponentsInChildren(true); + for (int i = 0; i < componentsInChildren.Length; i++) { + componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20); + } + foreach (TextMeshPro textMeshPro in optionBehaviour.GetComponentsInChildren(true)) { + textMeshPro.fontMaterial.SetFloat("_StencilComp", 3f); + textMeshPro.fontMaterial.SetFloat("_Stencil", (float)20); + } - private static bool setNames (Dictionary gameObjectNameDisplayNameMap) { - foreach (KeyValuePair entry in gameObjectNameDisplayNameMap) { - if (GameObject.Find(entry.Key) != null) { // Settings setup has already been performed, fixing the title of the tab and returning - GameObject.Find(entry.Key).transform.FindChild("GameGroup").FindChild("Text").GetComponent().SetText(entry.Value); - return true; + var stringOption = optionBehaviour as StringOption; + stringOption.OnValueChanged = new Action((o) => { }); + stringOption.TitleText.text = option.name; + if (option.isHeader && option.heading == "" && (option.type == CustomOptionType.Neutral || option.type == CustomOptionType.Crewmate || option.type == CustomOptionType.Impostor || option.type == CustomOptionType.Modifier)) { + stringOption.TitleText.text = "Spawn Chance"; } + if (stringOption.TitleText.text.Length > 25) + stringOption.TitleText.fontSize = 2.2f; + if (stringOption.TitleText.text.Length > 40) + stringOption.TitleText.fontSize = 2f; + stringOption.Value = stringOption.oldValue = option.selection; + stringOption.ValueText.text = option.selections[option.selection].ToString(); + option.optionBehaviour = stringOption; + + menu.Children.Add(optionBehaviour); + num -= 0.45f; + menu.scrollBar.SetYBoundsMax(-num - 1.65f); } - return false; - } - - private static GameOptionsMenu getMenu(GameObject setting, string settingName) { - var menu = setting.transform.FindChild("GameGroup").FindChild("SliderInner").GetComponent(); - setting.name = settingName; - - return menu; + for (int i = 0; i < menu.Children.Count; i++) { + OptionBehaviour optionBehaviour = menu.Children[i]; + if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost) { + optionBehaviour.SetAsPlayer(); + } + } } - private static SpriteRenderer getTabHighlight (GameObject tab, string tabName, string tabSpritePath) { - var tabHighlight = tab.transform.FindChild("Hat Button").FindChild("Tab Background").GetComponent(); - tab.transform.FindChild("Hat Button").FindChild("Icon").GetComponent().sprite = Helpers.loadSpriteFromResources(tabSpritePath, 100f); - tab.name = "tabName"; - - return tabHighlight; + private static void removeVanillaTabs(GameSettingMenu __instance) { + GameObject.Find("What Is This?")?.Destroy(); + GameObject.Find("GamePresetButton")?.Destroy(); + GameObject.Find("RoleSettingsButton")?.Destroy(); + __instance.ChangeTab(1, false); } - private static void setOptions (List menus, List> options, List settings) { - if (!(menus.Count == options.Count && options.Count == settings.Count)) { - TheOtherRolesPlugin.Logger.LogError("List counts are not equal"); - return; + public static void createCustomButton(GameSettingMenu __instance, int targetMenu, string buttonName, string buttonText) { + var leftPanel = GameObject.Find("LeftPanel"); + var buttonTemplate = GameObject.Find("GameSettingsButton"); + if (targetMenu == 3) { + buttonTemplate.transform.localPosition -= Vector3.up * 0.85f; + buttonTemplate.transform.localScale *= Vector2.one * 0.75f; } - for (int i = 0; i < menus.Count; i++) { - menus[i].Children = options[i].ToArray(); - settings[i].gameObject.SetActive(false); + var torSettingsButton = GameObject.Find(buttonName); + if (torSettingsButton == null) { + torSettingsButton = GameObject.Instantiate(buttonTemplate, leftPanel.transform); + torSettingsButton.transform.localPosition += Vector3.up * 0.5f * (targetMenu - 2); + torSettingsButton.name = buttonName; + __instance.StartCoroutine(Effects.Lerp(2f, new Action(p => { torSettingsButton.transform.FindChild("FontPlacer").GetComponentInChildren().text = buttonText; }))); + var torSettingsPassiveButton = torSettingsButton.GetComponent(); + torSettingsPassiveButton.OnClick.RemoveAllListeners(); + torSettingsPassiveButton.OnClick.AddListener((System.Action)(() => { + __instance.ChangeTab(targetMenu, false); + })); + torSettingsPassiveButton.OnMouseOut.RemoveAllListeners(); + torSettingsPassiveButton.OnMouseOver.RemoveAllListeners(); + torSettingsPassiveButton.SelectButton(false); + currentButtons.Add(torSettingsPassiveButton); } } - private static void adaptTaskCount(GameOptionsMenu __instance) { - // Adapt task count for main options - var commonTasksOption = __instance.Children.FirstOrDefault(x => x.name == "NumCommonTasks").TryCast(); - if (commonTasksOption != null) commonTasksOption.ValidRange = new FloatRange(0f, 4f); + public static void createGameOptionsMenu(GameSettingMenu __instance, CustomOptionType optionType, string settingName) { + var tabTemplate = GameObject.Find("GAME SETTINGS TAB"); + currentTabs.RemoveAll(x => x == null); - var shortTasksOption = __instance.Children.FirstOrDefault(x => x.name == "NumShortTasks").TryCast(); - if (shortTasksOption != null) shortTasksOption.ValidRange = new FloatRange(0f, 23f); + var torSettingsTab = GameObject.Instantiate(tabTemplate, tabTemplate.transform.parent); + torSettingsTab.name = settingName; + + var torSettingsGOM = torSettingsTab.GetComponent(); + foreach (var child in torSettingsGOM.Children) { + child.Destroy(); + } + torSettingsGOM.scrollBar.transform.FindChild("SliderInner").DestroyChildren(); + torSettingsGOM.Children.Clear(); + var relevantOptions = options.Where(x => x.type == optionType).ToList(); + if (TORMapOptions.gameMode == CustomGamemodes.Guesser) // Exclude guesser options in neutral mode + relevantOptions = relevantOptions.Where(x => !(new List { 310, 311, 312, 313, 314, 315, 316, 317, 318 }).Contains(x.id)).ToList(); + createSettings(torSettingsGOM, relevantOptions); + + currentTabs.Add(torSettingsTab); + torSettingsTab.SetActive(false); + } - var longTasksOption = __instance.Children.FirstOrDefault(x => x.name == "NumLongTasks").TryCast(); - if (longTasksOption != null) longTasksOption.ValidRange = new FloatRange(0f, 15f); + private static void createSettingTabs(GameSettingMenu __instance) { + // Handle different gamemodes and tabs needed therein. + int next = 3; + if (TORMapOptions.gameMode == CustomGamemodes.Guesser || TORMapOptions.gameMode == CustomGamemodes.Classic) { + + // create TOR settings + createCustomButton(__instance, next++, "TORSettings", "TOR Settings"); + createGameOptionsMenu(__instance, CustomOptionType.General, "TORSettings"); + // Guesser if applicable + if (TORMapOptions.gameMode == CustomGamemodes.Guesser) { + createCustomButton(__instance, next++, "GuesserSettings", "Guesser Settings"); + createGameOptionsMenu(__instance, CustomOptionType.Guesser, "GuesserSettings"); + } + // IMp + createCustomButton(__instance, next++, "ImpostorSettings", "Impostor Roles"); + createGameOptionsMenu(__instance, CustomOptionType.Impostor, "ImpostorSettings"); + + // Neutral + createCustomButton(__instance, next++, "NeutralSettings", "Neutral Roles"); + createGameOptionsMenu(__instance, CustomOptionType.Neutral, "NeutralSettings"); + // Crew + createCustomButton(__instance, next++, "CrewmateSettings", "Crewmate Roles"); + createGameOptionsMenu(__instance, CustomOptionType.Crewmate, "CrewmateSettings"); + // Modifier + createCustomButton(__instance, next++, "ModifierSettings", "Modifiers"); + createGameOptionsMenu(__instance, CustomOptionType.Modifier, "ModifierSettings"); + + } else if (TORMapOptions.gameMode == CustomGamemodes.HideNSeek) { + // create Main HNS settings + createCustomButton(__instance, next++, "HideNSeekMain", "Hide 'N' Seek"); + createGameOptionsMenu(__instance, CustomOptionType.HideNSeekMain, "HideNSeekMain"); + // create HNS Role settings + createCustomButton(__instance, next++, "HideNSeekRoles", "Hide 'N' Seek Roles"); + createGameOptionsMenu(__instance, CustomOptionType.HideNSeekRoles, "HideNSeekRoles"); + } else if (TORMapOptions.gameMode == CustomGamemodes.PropHunt) { + createCustomButton(__instance, next++, "PropHunt", "Prop Hunt"); + createGameOptionsMenu(__instance, CustomOptionType.PropHunt, "PropHunt"); + } } } - [HarmonyPatch(typeof(StringOption), nameof(StringOption.OnEnable))] + [HarmonyPatch(typeof(StringOption), nameof(StringOption.Initialize))] public class StringOptionEnablePatch { public static bool Prefix(StringOption __instance) { CustomOption option = CustomOption.options.FirstOrDefault(option => option.optionBehaviour == __instance); if (option == null) return true; __instance.OnValueChanged = new Action((o) => {}); - __instance.TitleText.text = option.name; + //__instance.TitleText.text = option.name; __instance.Value = __instance.oldValue = option.selection; __instance.ValueText.text = option.selections[option.selection].ToString(); @@ -884,7 +851,7 @@ namespace TheOtherRoles { { public static void Postfix() { - CustomOption.ShareOptionSelections(); + //CustomOption.ShareOptionSelections(); CustomOption.saveVanillaOptions(); } } @@ -900,54 +867,6 @@ namespace TheOtherRoles { } - [HarmonyPatch(typeof(GameOptionsMenu), nameof(GameOptionsMenu.Update))] - class GameOptionsMenuUpdatePatch - { - private static float timer = 1f; - public static void Postfix(GameOptionsMenu __instance) { - // Return Menu Update if in normal among us settings - var gameSettingMenu = UnityEngine.Object.FindObjectsOfType().FirstOrDefault(); - if (gameSettingMenu.RegularGameSettings.active || gameSettingMenu.RolesSettings.gameObject.active) return; - - __instance.GetComponentInParent().ContentYBounds.max = -0.5F + __instance.Children.Length * 0.55F; - timer += Time.deltaTime; - if (timer < 0.1f) return; - timer = 0f; - - float offset = 2.75f; - foreach (CustomOption option in CustomOption.options) { - if (GameObject.Find("TORSettings") && option.type != CustomOption.CustomOptionType.General && option.type != CustomOptionType.HideNSeekMain && option.type != CustomOptionType.PropHunt) - continue; - if (GameObject.Find("ImpostorSettings") && option.type != CustomOption.CustomOptionType.Impostor) - continue; - if (GameObject.Find("NeutralSettings") && option.type != CustomOption.CustomOptionType.Neutral) - continue; - if (GameObject.Find("CrewmateSettings") && option.type != CustomOption.CustomOptionType.Crewmate) - continue; - if (GameObject.Find("ModifierSettings") && option.type != CustomOption.CustomOptionType.Modifier) - continue; - if (GameObject.Find("GuesserSettings") && option.type != CustomOption.CustomOptionType.Guesser) - continue; - if (GameObject.Find("HideNSeekSettings") && option.type != CustomOption.CustomOptionType.HideNSeekRoles) - continue; - if (option?.optionBehaviour != null && option.optionBehaviour.gameObject != null) { - bool enabled = true; - var parent = option.parent; - while (parent != null && enabled) { - enabled = parent.selection != 0; - parent = parent.parent; - } - option.optionBehaviour.gameObject.SetActive(enabled); - if (enabled) { - offset -= option.isHeader ? 0.75f : 0.5f; - option.optionBehaviour.transform.localPosition = new Vector3(option.optionBehaviour.transform.localPosition.x, offset, option.optionBehaviour.transform.localPosition.z); - } - } - } - } - } - - [HarmonyPatch] class GameOptionsDataPatch { @@ -958,7 +877,7 @@ namespace TheOtherRoles { var modifiers = buildOptionsOfType(CustomOption.CustomOptionType.Modifier, true); return impRoles + neutralRoles + crewRoles + modifiers; } - private static string buildModifierExtras(CustomOption customOption) { + public static string buildModifierExtras(CustomOption customOption) { // find options children with quantity var children = CustomOption.options.Where(o => o.parent == customOption); var quantity = children.Where(o => o.name.Contains("Quantity")).ToList(); @@ -1156,7 +1075,7 @@ namespace TheOtherRoles { || __instance.PlayerSpeedMod <= 0f || __instance.PlayerSpeedMod > 3f; } - [HarmonyPatch(typeof(StringOption), nameof(StringOption.OnEnable))] + [HarmonyPatch(typeof(StringOption), nameof(StringOption.Initialize))] [HarmonyPrefix] public static void Prefix(StringOption __instance) @@ -1170,7 +1089,7 @@ namespace TheOtherRoles { } } - [HarmonyPatch(typeof(StringOption), nameof(StringOption.OnEnable))] + [HarmonyPatch(typeof(StringOption), nameof(StringOption.Initialize))] [HarmonyPostfix] public static void Postfix(StringOption __instance) @@ -1252,39 +1171,27 @@ namespace TheOtherRoles { if (Input.GetKeyDown(KeyCode.F1)) HudManagerUpdate.ToggleSettings(HudManager.Instance); if (TheOtherRolesPlugin.optionsPage >= GameOptionsDataPatch.maxPage) TheOtherRolesPlugin.optionsPage = 0; - - if (page != TheOtherRolesPlugin.optionsPage) { - Vector3 position = (Vector3)FastDestroyableSingleton.Instance?.GameSettings?.transform.localPosition; - FastDestroyableSingleton.Instance.GameSettings.transform.localPosition = new Vector3(position.x, 2.9f, position.z); - } } } - [HarmonyPatch(typeof(HudManager), nameof(HudManager.Update))] - public class GameSettingsScalePatch { - public static void Prefix(HudManager __instance) { - if (__instance.GameSettings != null) __instance.GameSettings.fontSize = 1.2f; - } - } - - - // This class is taken and adapted from Town of Us Reactivated, https://github.com/eDonnes124/Town-Of-Us-R/blob/master/source/Patches/CustomOption/Patches.cs, Licensed under GPLv3 + //This class is taken and adapted from Town of Us Reactivated, https://github.com/eDonnes124/Town-Of-Us-R/blob/master/source/Patches/CustomOption/Patches.cs, Licensed under GPLv3 [HarmonyPatch(typeof(HudManager), nameof(HudManager.Update))] public class HudManagerUpdate { + private static GameObject GameSettingsObject; + private static TextMeshPro GameSettings; public static float MinX,/*-5.3F*/ OriginalY = 2.9F, MinY = 2.9F; - public static Scroller Scroller; private static Vector3 LastPosition; private static float lastAspect; private static bool setLastPosition = false; public static void Prefix(HudManager __instance) { - if (__instance.GameSettings?.transform == null) return; + if (GameSettings?.transform == null) return; // Sets the MinX position to the left edge of the screen + 0.1 units Rect safeArea = Screen.safeArea; @@ -1301,11 +1208,11 @@ namespace TheOtherRoles { CreateScroller(__instance); - Scroller.gameObject.SetActive(__instance.GameSettings.gameObject.activeSelf); + Scroller.gameObject.SetActive(GameSettings.gameObject.activeSelf); if (!Scroller.gameObject.active) return; - var rows = __instance.GameSettings.text.Count(c => c == '\n'); + var rows = GameSettings.text.Count(c => c == '\n'); float LobbyTextRowHeight = 0.06F; var maxY = Mathf.Max(MinY, rows * LobbyTextRowHeight + (rows - 38) * LobbyTextRowHeight); @@ -1313,24 +1220,24 @@ namespace TheOtherRoles { // Prevent scrolling when the player is interacting with a menu if (CachedPlayer.LocalPlayer?.PlayerControl.CanMove != true) { - __instance.GameSettings.transform.localPosition = LastPosition; + GameSettings.transform.localPosition = LastPosition; return; } - if (__instance.GameSettings.transform.localPosition.x != MinX || - __instance.GameSettings.transform.localPosition.y < MinY) return; + if (GameSettings.transform.localPosition.x != MinX || + GameSettings.transform.localPosition.y < MinY) return; - LastPosition = __instance.GameSettings.transform.localPosition; + LastPosition = GameSettings.transform.localPosition; } private static void CreateScroller(HudManager __instance) { if (Scroller != null) return; - Transform target = __instance.GameSettings.transform; + Transform target = GameSettings.transform; Scroller = new GameObject("SettingsScroller").AddComponent(); - Scroller.transform.SetParent(__instance.GameSettings.transform.parent); + Scroller.transform.SetParent(GameSettings.transform.parent); Scroller.gameObject.layer = 5; Scroller.transform.localScale = Vector3.one; @@ -1381,9 +1288,7 @@ namespace TheOtherRoles { private static TMPro.TextMeshPro[] settingsTMPs = new TMPro.TextMeshPro[4]; private static GameObject settingsBackground; public static void OpenSettings(HudManager __instance) { - if (__instance.FullScreen == null || MapBehaviour.Instance && MapBehaviour.Instance.IsOpen - /*|| AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started*/ - || GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek) return; + if (__instance.FullScreen == null || MapBehaviour.Instance && MapBehaviour.Instance.IsOpen) return; settingsBackground = GameObject.Instantiate(__instance.FullScreen.gameObject, __instance.transform); settingsBackground.SetActive(true); var renderer = settingsBackground.GetComponent(); @@ -1413,20 +1318,52 @@ namespace TheOtherRoles { static PassiveButton toggleSettingsButton; static GameObject toggleSettingsButtonObject; + + static GameObject toggleZoomButtonObject; + static PassiveButton toggleZoomButton; + [HarmonyPostfix] public static void Postfix(HudManager __instance) { + if (AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started) return; if (!toggleSettingsButton || !toggleSettingsButtonObject) { // add a special button for settings viewing: toggleSettingsButtonObject = GameObject.Instantiate(__instance.MapButton.gameObject, __instance.MapButton.transform.parent); - toggleSettingsButtonObject.transform.localPosition = __instance.MapButton.transform.localPosition + new Vector3(0, -0.66f, -500f); - SpriteRenderer renderer = toggleSettingsButtonObject.GetComponent(); - renderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.CurrentSettingsButton.png", 180f); + toggleSettingsButtonObject.transform.localPosition = __instance.MapButton.transform.localPosition + new Vector3(0, -1.25f, -500f); + toggleSettingsButtonObject.name = "TOGGLESETTINGSBUTTON"; + SpriteRenderer renderer = toggleSettingsButtonObject.transform.Find("Inactive").GetComponent(); + SpriteRenderer rendererActive = toggleSettingsButtonObject.transform.Find("Active").GetComponent(); + toggleSettingsButtonObject.transform.Find("Background").localPosition = Vector3.zero; + renderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Settings_Button.png", 100f); + rendererActive.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Settings_ButtonActive.png", 100); toggleSettingsButton = toggleSettingsButtonObject.GetComponent(); toggleSettingsButton.OnClick.RemoveAllListeners(); toggleSettingsButton.OnClick.AddListener((Action)(() => ToggleSettings(__instance))); } toggleSettingsButtonObject.SetActive(__instance.MapButton.gameObject.active && !(MapBehaviour.Instance && MapBehaviour.Instance.IsOpen) && GameOptionsManager.Instance.currentGameOptions.GameMode != GameModes.HideNSeek); - toggleSettingsButtonObject.transform.localPosition = __instance.MapButton.transform.localPosition + new Vector3(0, -0.66f, -500f); + toggleSettingsButtonObject.transform.localPosition = __instance.MapButton.transform.localPosition + new Vector3(0, -0.8f, -500f); + + + if (!toggleZoomButton || !toggleZoomButtonObject) { + // add a special button for settings viewing: + toggleZoomButtonObject = GameObject.Instantiate(__instance.MapButton.gameObject, __instance.MapButton.transform.parent); + toggleZoomButtonObject.transform.localPosition = __instance.MapButton.transform.localPosition + new Vector3(0, -1.25f, -500f); + toggleZoomButtonObject.name = "TOGGLEZOOMBUTTON"; + SpriteRenderer tZrenderer = toggleZoomButtonObject.transform.Find("Inactive").GetComponent(); + SpriteRenderer tZArenderer = toggleZoomButtonObject.transform.Find("Active").GetComponent(); + toggleZoomButtonObject.transform.Find("Background").localPosition = Vector3.zero; + tZrenderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Minus_Button.png", 100f); + tZArenderer.sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Minus_ButtonActive.png", 100); + toggleZoomButton = toggleZoomButtonObject.GetComponent(); + toggleZoomButton.OnClick.RemoveAllListeners(); + toggleZoomButton.OnClick.AddListener((Action)(() => Helpers.toggleZoom())); + } + var (playerCompleted, playerTotal) = TasksHandler.taskInfo(CachedPlayer.LocalPlayer.Data); + int numberOfLeftTasks = playerTotal - playerCompleted; + bool zoomButtonActive = !(CachedPlayer.LocalPlayer.PlayerControl == null || !CachedPlayer.LocalPlayer.Data.IsDead || (CachedPlayer.LocalPlayer.Data.Role.IsImpostor && !CustomOptionHolder.deadImpsBlockSabotage.getBool())); + zoomButtonActive &= numberOfLeftTasks <= 0 || !CustomOptionHolder.finishTasksBeforeHauntingOrZoomingOut.getBool(); + toggleZoomButtonObject.SetActive(zoomButtonActive); + var posOffset = Helpers.zoomOutStatus ? new Vector3(-1.27f, -7.92f, -52f) : new Vector3(0, -1.6f, -52f); + toggleZoomButtonObject.transform.localPosition = HudManager.Instance.MapButton.transform.localPosition + posOffset; } } } diff --git a/TheOtherRoles/Objects/Footprint.cs b/TheOtherRoles/Objects/Footprint.cs index 3c27bd9..88faa78 100644 --- a/TheOtherRoles/Objects/Footprint.cs +++ b/TheOtherRoles/Objects/Footprint.cs @@ -34,7 +34,7 @@ namespace TheOtherRoles.Objects public Transform Transform; public SpriteRenderer Renderer; public PlayerControl Owner; - public GameData.PlayerInfo Data; + public NetworkedPlayerInfo Data; public float Lifetime; public Footprint() { diff --git a/TheOtherRoles/Objects/Silhouette.cs b/TheOtherRoles/Objects/Silhouette.cs index 5d0f09f..7d2cfaa 100644 --- a/TheOtherRoles/Objects/Silhouette.cs +++ b/TheOtherRoles/Objects/Silhouette.cs @@ -29,7 +29,6 @@ namespace TheOtherRoles.Objects { public Silhouette(Vector3 p, float duration = 1f, bool visibleForEveryOne = true) { if (duration <= 0f) { - TheOtherRolesPlugin.Logger.LogMessage("silhouette: permanent!"); permanent = true; } this.visibleForEveryOne = visibleForEveryOne; @@ -71,7 +70,6 @@ namespace TheOtherRoles.Objects { } if (current.timeRemaining < 0 && !current.permanent) { - TheOtherRolesPlugin.Logger.LogMessage($"update: permanent: {current.permanent}, time: {current.timeRemaining}"); current.gameObject.SetActive(false); UnityEngine.Object.Destroy(current.gameObject); silhouettes.Remove(current); diff --git a/TheOtherRoles/Patches/ClientOptionsPatch.cs b/TheOtherRoles/Patches/ClientOptionsPatch.cs index f212c5c..36b2b61 100644 --- a/TheOtherRoles/Patches/ClientOptionsPatch.cs +++ b/TheOtherRoles/Patches/ClientOptionsPatch.cs @@ -22,6 +22,7 @@ namespace TheOtherRoles.Patches new("Show Lighter / Darker", () => TORMapOptions.showLighterDarker = TheOtherRolesPlugin.ShowLighterDarker.Value = !TheOtherRolesPlugin.ShowLighterDarker.Value, TheOtherRolesPlugin.ShowLighterDarker.Value), new("Enable Sound Effects", () => TORMapOptions.enableSoundEffects = TheOtherRolesPlugin.EnableSoundEffects.Value = !TheOtherRolesPlugin.EnableSoundEffects.Value, TheOtherRolesPlugin.EnableSoundEffects.Value), new("Show Vents On Map", () => TORMapOptions.ShowVentsOnMap = TheOtherRolesPlugin.ShowVentsOnMap.Value = !TheOtherRolesPlugin.ShowVentsOnMap.Value, TheOtherRolesPlugin.ShowVentsOnMap.Value), + new("Show Chat Notifications", () => TORMapOptions.ShowChatNotifications = TheOtherRolesPlugin.ShowChatNotifications.Value = !TheOtherRolesPlugin.ShowChatNotifications.Value, TheOtherRolesPlugin.ShowChatNotifications.Value), }; private static GameObject popUp; diff --git a/TheOtherRoles/Patches/ConstantsPatch.cs b/TheOtherRoles/Patches/ConstantsPatch.cs index c9a5fc3..7670cfe 100644 --- a/TheOtherRoles/Patches/ConstantsPatch.cs +++ b/TheOtherRoles/Patches/ConstantsPatch.cs @@ -4,8 +4,8 @@ namespace TheOtherRoles.Patches { [HarmonyPatch(typeof(Constants), nameof(Constants.GetBroadcastVersion))] public static class ConstantsPatch { public static void Postfix(ref int __result) { - if (AmongUsClient.Instance.NetworkMode == NetworkModes.OnlineGame) - __result += 25; + /*if (AmongUsClient.Instance.NetworkMode == NetworkModes.OnlineGame) + __result += 25;*/ } } } \ No newline at end of file diff --git a/TheOtherRoles/Patches/CredentialsPatch.cs b/TheOtherRoles/Patches/CredentialsPatch.cs index 1f9fbd5..bc252ff 100644 --- a/TheOtherRoles/Patches/CredentialsPatch.cs +++ b/TheOtherRoles/Patches/CredentialsPatch.cs @@ -1,4 +1,5 @@ -using HarmonyLib; +using AmongUs.GameOptions; +using HarmonyLib; using System; using System.Collections.Generic; using System.Net.Http; @@ -32,8 +33,9 @@ $@" Special thanks to Smeggy"; { static void Postfix(PingTracker __instance){ - __instance.text.alignment = TextAlignmentOptions.TopRight; + __instance.text.alignment = TextAlignmentOptions.Top; var position = __instance.GetComponent(); + position.Alignment = AspectPosition.EdgeAlignments.Top; if (AmongUsClient.Instance.GameState == InnerNet.InnerNetClient.GameStates.Started) { string gameModeText = $""; if (HideNSeek.isHideNSeekGM) gameModeText = $"Hide 'N Seek"; @@ -47,11 +49,17 @@ $@" Special thanks to Smeggy"; if (TORMapOptions.gameMode == CustomGamemodes.HideNSeek) gameModeText = $"Hide 'N Seek"; else if (TORMapOptions.gameMode == CustomGamemodes.Guesser) gameModeText = $"Guesser"; else if (TORMapOptions.gameMode == CustomGamemodes.PropHunt) gameModeText = $"Prop Hunt"; - if (gameModeText != "") gameModeText = Helpers.cs(Color.yellow, gameModeText) + "\n"; + if (gameModeText != "") gameModeText = Helpers.cs(Color.yellow, gameModeText); + + __instance.text.text = $"{fullCredentialsVersion}\n{fullCredentials}\n {__instance.text.text}"; + position.DistanceFromEdge = new Vector3( 0f, 0.1f, 0); - var host = $"Host: {GameData.Instance?.GetHost()?.PlayerName}"; - __instance.text.text = $"{fullCredentialsVersion}\n {gameModeText + fullCredentials}\n {host}\n {__instance.text.text}"; - position.DistanceFromEdge = new Vector3(3.5f, 0.1f, 0); + try { + var GameModeText = GameObject.Find("GameModeText")?.GetComponent(); + GameModeText.text = gameModeText == "" ? (GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek ? "Van. HideNSeek" : "Classic" ): gameModeText; + var ModeLabel = GameObject.Find("ModeLabel")?.GetComponentInChildren(); + ModeLabel.text = "Game Mode"; + } catch { } } position.AdjustPosition(); } diff --git a/TheOtherRoles/Patches/EndGamePatch.cs b/TheOtherRoles/Patches/EndGamePatch.cs index 8a1c2fd..485c621 100644 --- a/TheOtherRoles/Patches/EndGamePatch.cs +++ b/TheOtherRoles/Patches/EndGamePatch.cs @@ -101,11 +101,11 @@ namespace TheOtherRoles.Patches { notWinners.AddRange(Jackal.formerJackals); - List winnersToRemove = new List(); - foreach (WinningPlayerData winner in TempData.winners.GetFastEnumerator()) { + List winnersToRemove = new List(); + foreach (CachedPlayerData winner in EndGameResult.CachedWinners.GetFastEnumerator()) { if (notWinners.Any(x => x.Data.PlayerName == winner.PlayerName)) winnersToRemove.Add(winner); } - foreach (var winner in winnersToRemove) TempData.winners.Remove(winner); + foreach (var winner in winnersToRemove) EndGameResult.CachedWinners.Remove(winner); bool jesterWin = Jester.jester != null && gameOverReason == (GameOverReason)CustomGameOverReason.JesterWin; bool arsonistWin = Arsonist.arsonist != null && gameOverReason == (GameOverReason)CustomGameOverReason.ArsonistWin; @@ -119,42 +119,42 @@ namespace TheOtherRoles.Patches { // Mini lose if (miniLose) { - TempData.winners = new Il2CppSystem.Collections.Generic.List(); - WinningPlayerData wpd = new WinningPlayerData(Mini.mini.Data); + EndGameResult.CachedWinners = new Il2CppSystem.Collections.Generic.List(); + CachedPlayerData wpd = new CachedPlayerData(Mini.mini.Data); wpd.IsYou = false; // If "no one is the Mini", it will display the Mini, but also show defeat to everyone - TempData.winners.Add(wpd); + EndGameResult.CachedWinners.Add(wpd); AdditionalTempData.winCondition = WinCondition.MiniLose; } // Jester win else if (jesterWin) { - TempData.winners = new Il2CppSystem.Collections.Generic.List(); - WinningPlayerData wpd = new WinningPlayerData(Jester.jester.Data); - TempData.winners.Add(wpd); + EndGameResult.CachedWinners = new Il2CppSystem.Collections.Generic.List(); + CachedPlayerData wpd = new CachedPlayerData(Jester.jester.Data); + EndGameResult.CachedWinners.Add(wpd); AdditionalTempData.winCondition = WinCondition.JesterWin; } // Arsonist win else if (arsonistWin) { - TempData.winners = new Il2CppSystem.Collections.Generic.List(); - WinningPlayerData wpd = new WinningPlayerData(Arsonist.arsonist.Data); - TempData.winners.Add(wpd); + EndGameResult.CachedWinners = new Il2CppSystem.Collections.Generic.List(); + CachedPlayerData wpd = new CachedPlayerData(Arsonist.arsonist.Data); + EndGameResult.CachedWinners.Add(wpd); AdditionalTempData.winCondition = WinCondition.ArsonistWin; } // Vulture win else if (vultureWin) { - TempData.winners = new Il2CppSystem.Collections.Generic.List(); - WinningPlayerData wpd = new WinningPlayerData(Vulture.vulture.Data); - TempData.winners.Add(wpd); + EndGameResult.CachedWinners = new Il2CppSystem.Collections.Generic.List(); + CachedPlayerData wpd = new CachedPlayerData(Vulture.vulture.Data); + EndGameResult.CachedWinners.Add(wpd); AdditionalTempData.winCondition = WinCondition.VultureWin; } // Jester win else if (prosecutorWin) { - TempData.winners = new Il2CppSystem.Collections.Generic.List(); - WinningPlayerData wpd = new WinningPlayerData(Lawyer.lawyer.Data); - TempData.winners.Add(wpd); + EndGameResult.CachedWinners = new Il2CppSystem.Collections.Generic.List(); + CachedPlayerData wpd = new CachedPlayerData(Lawyer.lawyer.Data); + EndGameResult.CachedWinners.Add(wpd); AdditionalTempData.winCondition = WinCondition.ProsecutorWin; } @@ -163,23 +163,23 @@ namespace TheOtherRoles.Patches { // Double win for lovers, crewmates also win if (!Lovers.existingWithKiller()) { AdditionalTempData.winCondition = WinCondition.LoversTeamWin; - TempData.winners = new Il2CppSystem.Collections.Generic.List(); + EndGameResult.CachedWinners = new Il2CppSystem.Collections.Generic.List(); foreach (PlayerControl p in CachedPlayer.AllPlayers) { if (p == null) continue; if (p == Lovers.lover1 || p == Lovers.lover2) - TempData.winners.Add(new WinningPlayerData(p.Data)); + EndGameResult.CachedWinners.Add(new CachedPlayerData(p.Data)); else if (p == Pursuer.pursuer && !Pursuer.pursuer.Data.IsDead) - TempData.winners.Add(new WinningPlayerData(p.Data)); + EndGameResult.CachedWinners.Add(new CachedPlayerData(p.Data)); else if (p != Jester.jester && p != Jackal.jackal && p != Sidekick.sidekick && p != Arsonist.arsonist && p != Vulture.vulture && !Jackal.formerJackals.Contains(p) && !p.Data.Role.IsImpostor) - TempData.winners.Add(new WinningPlayerData(p.Data)); + EndGameResult.CachedWinners.Add(new CachedPlayerData(p.Data)); } } // Lovers solo win else { AdditionalTempData.winCondition = WinCondition.LoversSoloWin; - TempData.winners = new Il2CppSystem.Collections.Generic.List(); - TempData.winners.Add(new WinningPlayerData(Lovers.lover1.Data)); - TempData.winners.Add(new WinningPlayerData(Lovers.lover2.Data)); + EndGameResult.CachedWinners = new Il2CppSystem.Collections.Generic.List(); + EndGameResult.CachedWinners.Add(new CachedPlayerData(Lovers.lover1.Data)); + EndGameResult.CachedWinners.Add(new CachedPlayerData(Lovers.lover2.Data)); } } @@ -187,41 +187,41 @@ namespace TheOtherRoles.Patches { else if (teamJackalWin) { // Jackal wins if nobody except jackal is alive AdditionalTempData.winCondition = WinCondition.JackalWin; - TempData.winners = new Il2CppSystem.Collections.Generic.List(); - WinningPlayerData wpd = new WinningPlayerData(Jackal.jackal.Data); + EndGameResult.CachedWinners = new Il2CppSystem.Collections.Generic.List(); + CachedPlayerData wpd = new CachedPlayerData(Jackal.jackal.Data); wpd.IsImpostor = false; - TempData.winners.Add(wpd); + EndGameResult.CachedWinners.Add(wpd); // If there is a sidekick. The sidekick also wins if (Sidekick.sidekick != null) { - WinningPlayerData wpdSidekick = new WinningPlayerData(Sidekick.sidekick.Data); + CachedPlayerData wpdSidekick = new CachedPlayerData(Sidekick.sidekick.Data); wpdSidekick.IsImpostor = false; - TempData.winners.Add(wpdSidekick); + EndGameResult.CachedWinners.Add(wpdSidekick); } foreach(var player in Jackal.formerJackals) { - WinningPlayerData wpdFormerJackal = new WinningPlayerData(player.Data); + CachedPlayerData wpdFormerJackal = new CachedPlayerData(player.Data); wpdFormerJackal.IsImpostor = false; - TempData.winners.Add(wpdFormerJackal); + EndGameResult.CachedWinners.Add(wpdFormerJackal); } } // Possible Additional winner: Lawyer if (Lawyer.lawyer != null && Lawyer.target != null && (!Lawyer.target.Data.IsDead || Lawyer.target == Jester.jester) && !Pursuer.notAckedExiled && !Lawyer.isProsecutor) { - WinningPlayerData winningClient = null; - foreach (WinningPlayerData winner in TempData.winners.GetFastEnumerator()) { + CachedPlayerData winningClient = null; + foreach (CachedPlayerData winner in EndGameResult.CachedWinners.GetFastEnumerator()) { if (winner.PlayerName == Lawyer.target.Data.PlayerName) winningClient = winner; } if (winningClient != null) { // The Lawyer wins if the client is winning (and alive, but if he wasn't the Lawyer shouldn't exist anymore) - if (!TempData.winners.ToArray().Any(x => x.PlayerName == Lawyer.lawyer.Data.PlayerName)) - TempData.winners.Add(new WinningPlayerData(Lawyer.lawyer.Data)); + if (!EndGameResult.CachedWinners.ToArray().Any(x => x.PlayerName == Lawyer.lawyer.Data.PlayerName)) + EndGameResult.CachedWinners.Add(new CachedPlayerData(Lawyer.lawyer.Data)); AdditionalTempData.additionalWinConditions.Add(WinCondition.AdditionalLawyerBonusWin); // The Lawyer wins together with the client } } // Possible Additional winner: Pursuer - if (Pursuer.pursuer != null && !Pursuer.pursuer.Data.IsDead && !Pursuer.notAckedExiled && !isPursurerLose && !TempData.winners.ToArray().Any(x => x.IsImpostor)) { - if (!TempData.winners.ToArray().Any(x => x.PlayerName == Pursuer.pursuer.Data.PlayerName)) - TempData.winners.Add(new WinningPlayerData(Pursuer.pursuer.Data)); + if (Pursuer.pursuer != null && !Pursuer.pursuer.Data.IsDead && !Pursuer.notAckedExiled && !isPursurerLose && !EndGameResult.CachedWinners.ToArray().Any(x => x.IsImpostor)) { + if (!EndGameResult.CachedWinners.ToArray().Any(x => x.PlayerName == Pursuer.pursuer.Data.PlayerName)) + EndGameResult.CachedWinners.Add(new CachedPlayerData(Pursuer.pursuer.Data)); AdditionalTempData.additionalWinConditions.Add(WinCondition.AdditionalAlivePursuerWin); } @@ -242,16 +242,16 @@ namespace TheOtherRoles.Patches { UnityEngine.Object.Destroy(pb.gameObject); } int num = Mathf.CeilToInt(7.5f); - List list = TempData.winners.ToArray().ToList().OrderBy(delegate(WinningPlayerData b) + List list = EndGameResult.CachedWinners.ToArray().ToList().OrderBy(delegate(CachedPlayerData b) { if (!b.IsYou) { return 0; } return -1; - }).ToList(); + }).ToList(); for (int i = 0; i < list.Count; i++) { - WinningPlayerData winningPlayerData2 = list[i]; + CachedPlayerData CachedPlayerData2 = list[i]; int num2 = (i % 2 == 0) ? -1 : 1; int num3 = (i + 1) / 2; float num4 = (float)num3 / (float)num; @@ -262,21 +262,21 @@ namespace TheOtherRoles.Patches { float num7 = Mathf.Lerp(1f, 0.65f, num4) * 0.9f; Vector3 vector = new Vector3(num7, num7, 1f); poolablePlayer.transform.localScale = vector; - if (winningPlayerData2.IsDead) { + if (CachedPlayerData2.IsDead) { poolablePlayer.SetBodyAsGhost(); poolablePlayer.SetDeadFlipX(i % 2 == 0); } else { poolablePlayer.SetFlipX(i % 2 == 0); } - poolablePlayer.UpdateFromPlayerOutfit(winningPlayerData2, PlayerMaterial.MaskType.None, winningPlayerData2.IsDead, true); + poolablePlayer.UpdateFromPlayerOutfit(CachedPlayerData2.Outfit, PlayerMaterial.MaskType.None, CachedPlayerData2.IsDead, true); poolablePlayer.cosmetics.nameText.color = Color.white; poolablePlayer.cosmetics.nameText.transform.localScale = new Vector3(1f / vector.x, 1f / vector.y, 1f / vector.z); poolablePlayer.cosmetics.nameText.transform.localPosition = new Vector3(poolablePlayer.cosmetics.nameText.transform.localPosition.x, poolablePlayer.cosmetics.nameText.transform.localPosition.y, -15f); - poolablePlayer.cosmetics.nameText.text = winningPlayerData2.PlayerName; + poolablePlayer.cosmetics.nameText.text = CachedPlayerData2.PlayerName; foreach(var data in AdditionalTempData.playerRoles) { - if (data.PlayerName != winningPlayerData2.PlayerName) continue; + if (data.PlayerName != CachedPlayerData2.PlayerName) continue; var roles = poolablePlayer.cosmetics.nameText.text += $"\n{string.Join("\n", data.Roles.Select(x => Helpers.cs(x.color, x.name)))}"; } @@ -495,7 +495,7 @@ namespace TheOtherRoles.Patches { if (statistics.TeamImpostorsAlive >= statistics.TotalAlive - statistics.TeamImpostorsAlive && statistics.TeamJackalAlive == 0 && !(statistics.TeamImpostorHasAliveLover && statistics.TeamLoversAlive == 2)) { //__instance.enabled = false; GameOverReason endReason; - switch (TempData.LastDeathReason) { + switch (GameData.LastDeathReason) { case DeathReason.Exile: endReason = GameOverReason.ImpostorByVote; break; @@ -550,7 +550,7 @@ namespace TheOtherRoles.Patches { GetPlayerCounts(); } - private bool isLover(GameData.PlayerInfo p) { + private bool isLover(NetworkedPlayerInfo p) { return (Lovers.lover1 != null && Lovers.lover1.PlayerId == p.PlayerId) || (Lovers.lover2 != null && Lovers.lover2.PlayerId == p.PlayerId); } diff --git a/TheOtherRoles/Patches/ExileControllerPatch.cs b/TheOtherRoles/Patches/ExileControllerPatch.cs index 50e1f83..12b1ea2 100644 --- a/TheOtherRoles/Patches/ExileControllerPatch.cs +++ b/TheOtherRoles/Patches/ExileControllerPatch.cs @@ -13,8 +13,8 @@ namespace TheOtherRoles.Patches { [HarmonyPatch(typeof(ExileController), nameof(ExileController.Begin))] [HarmonyPriority(Priority.First)] class ExileControllerBeginPatch { - public static GameData.PlayerInfo lastExiled; - public static void Prefix(ExileController __instance, [HarmonyArgument(0)]ref GameData.PlayerInfo exiled, [HarmonyArgument(1)]bool tie) { + public static NetworkedPlayerInfo lastExiled; + public static void Prefix(ExileController __instance, [HarmonyArgument(0)]ref NetworkedPlayerInfo exiled, [HarmonyArgument(1)]bool tie) { lastExiled = exiled; // Medic shield @@ -158,7 +158,7 @@ namespace TheOtherRoles.Patches { } } - static void WrapUpPostfix(GameData.PlayerInfo exiled) { + static void WrapUpPostfix(NetworkedPlayerInfo exiled) { // Prosecutor win condition if (exiled != null && Lawyer.lawyer != null && Lawyer.target != null && Lawyer.isProsecutor && Lawyer.target.PlayerId == exiled.PlayerId && !Lawyer.lawyer.Data.IsDead) Lawyer.triggerProsecutorWin = true; diff --git a/TheOtherRoles/Patches/GameStartManagerPatch.cs b/TheOtherRoles/Patches/GameStartManagerPatch.cs index c5eb053..afe8f4b 100644 --- a/TheOtherRoles/Patches/GameStartManagerPatch.cs +++ b/TheOtherRoles/Patches/GameStartManagerPatch.cs @@ -24,6 +24,7 @@ namespace TheOtherRoles.Patches { if (CachedPlayer.LocalPlayer != null) { Helpers.shareGameVersion(); } + GameStartManagerUpdatePatch.sendGamemode = true; } } @@ -49,6 +50,7 @@ namespace TheOtherRoles.Patches { private static bool update = false; private static string currentText = ""; private static GameObject copiedStartButton; + public static bool sendGamemode = true; public static void Prefix(GameStartManager __instance) { if (!GameData.Instance ) return; // No instance @@ -63,15 +65,12 @@ namespace TheOtherRoles.Patches { Helpers.shareGameVersion(); } - // Check version handshake infos + // Check version handshake infos bool versionMismatch = false; string message = ""; foreach (InnerNet.ClientData client in AmongUsClient.Instance.allClients.ToArray()) { if (client.Character == null) continue; - var dummyComponent = client.Character.GetComponent(); - if (dummyComponent != null && dummyComponent.enabled) - continue; else if (!playerVersions.ContainsKey(client.Id)) { versionMismatch = true; message += $"{client.Character.Data.PlayerName} has a different or no version of The Other Roles\n"; @@ -90,39 +89,39 @@ namespace TheOtherRoles.Patches { } } } - // Display message to the host if (AmongUsClient.Instance.AmHost) { if (versionMismatch) { - __instance.StartButton.color = __instance.startLabelText.color = Palette.DisabledClear; __instance.GameStartText.text = message; - __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 2; + __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 5; + __instance.GameStartText.transform.localScale = new Vector3(2f, 2f, 1f); + __instance.GameStartTextParent.SetActive(true); } else { - __instance.StartButton.color = __instance.startLabelText.color = ((__instance.LastPlayerCount >= __instance.MinPlayers) ? Palette.EnabledColor : Palette.DisabledClear); - __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition; + __instance.GameStartText.transform.localPosition = Vector3.zero; + __instance.GameStartText.transform.localScale = new Vector3(1.2f, 1.2f, 1f); + if (!__instance.GameStartText.text.StartsWith("Starting")) { + __instance.GameStartText.text = String.Empty; + __instance.GameStartTextParent.SetActive(false); + } } if (__instance.startState != GameStartManager.StartingStates.Countdown) copiedStartButton?.Destroy(); - // Make starting info available to clients: if (startingTimer <= 0 && __instance.startState == GameStartManager.StartingStates.Countdown) { MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.SetGameStarting, Hazel.SendOption.Reliable, -1); AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.setGameStarting(); - // Activate Stop-Button copiedStartButton = GameObject.Instantiate(__instance.StartButton.gameObject, __instance.StartButton.gameObject.transform.parent); copiedStartButton.transform.localPosition = __instance.StartButton.transform.localPosition; - copiedStartButton.GetComponent().sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.StopClean.png", 180f); copiedStartButton.SetActive(true); var startButtonText = copiedStartButton.GetComponentInChildren(); - startButtonText.text = "STOP"; + startButtonText.text = ""; startButtonText.fontSize *= 0.8f; startButtonText.fontSizeMax = startButtonText.fontSize; startButtonText.gameObject.transform.localPosition = Vector3.zero; PassiveButton startButtonPassiveButton = copiedStartButton.GetComponent(); - void StopStartFunc() { __instance.ResetStartState(); copiedStartButton.Destroy(); @@ -130,14 +129,13 @@ namespace TheOtherRoles.Patches { } startButtonPassiveButton.OnClick.AddListener((Action)(() => StopStartFunc())); __instance.StartCoroutine(Effects.Lerp(.1f, new System.Action((p) => { - startButtonText.text = "STOP"; + startButtonText.text = ""; }))); + } - if (__instance.startState == GameStartManager.StartingStates.Countdown) - __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 0.6f; } - + // Client update with handshake infos else { if (!playerVersions.ContainsKey(AmongUsClient.Instance.HostId) || TheOtherRolesPlugin.Version.CompareTo(playerVersions[AmongUsClient.Instance.HostId].version) != 0) { @@ -149,14 +147,21 @@ namespace TheOtherRoles.Patches { } __instance.GameStartText.text = $"The host has no or a different version of The Other Roles\nYou will be kicked in {Math.Round(10 - kickingTimer)}s"; - __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 2; + __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 5; + __instance.GameStartText.transform.localScale = new Vector3(2f, 2f, 1f); + __instance.GameStartTextParent.SetActive(true); } else if (versionMismatch) { __instance.GameStartText.text = $"Players With Different Versions:\n" + message; - __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 2; + __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 5; + __instance.GameStartText.transform.localScale = new Vector3(2f, 2f, 1f); + __instance.GameStartTextParent.SetActive(true); } else { - __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition; - if (!__instance.GameStartText.text.StartsWith("Starting")) + __instance.GameStartText.transform.localPosition = Vector3.zero; + __instance.GameStartText.transform.localScale = new Vector3(1.2f, 1.2f, 1f); + if (!__instance.GameStartText.text.StartsWith("Starting")) { __instance.GameStartText.text = String.Empty; + __instance.GameStartTextParent.SetActive(false); + } } if (!__instance.GameStartText.text.StartsWith("Starting") || !CustomOptionHolder.anyPlayerCanStopStart.getBool()) @@ -166,10 +171,9 @@ namespace TheOtherRoles.Patches { // Activate Stop-Button copiedStartButton = GameObject.Instantiate(__instance.StartButton.gameObject, __instance.StartButton.gameObject.transform.parent); copiedStartButton.transform.localPosition = __instance.StartButton.transform.localPosition; - copiedStartButton.GetComponent().sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.StopClean.png", 180f); copiedStartButton.SetActive(true); var startButtonText = copiedStartButton.GetComponentInChildren(); - startButtonText.text = "STOP"; + startButtonText.text = ""; startButtonText.fontSize *= 0.8f; startButtonText.fontSizeMax = startButtonText.fontSize; startButtonText.gameObject.transform.localPosition = Vector3.zero; @@ -185,20 +189,16 @@ namespace TheOtherRoles.Patches { } startButtonPassiveButton.OnClick.AddListener((Action)(() => StopStartFunc())); __instance.StartCoroutine(Effects.Lerp(.1f, new System.Action((p) => { - startButtonText.text = "STOP"; + startButtonText.text = ""; }))); - } - if (__instance.GameStartText.text.StartsWith("Starting") && CustomOptionHolder.anyPlayerCanStopStart.getBool()) - __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 0.6f; } - // Start Timer if (startingTimer > 0) { startingTimer -= Time.deltaTime; } // Lobby timer - if (!GameData.Instance) return; // No instance + if (!GameData.Instance || !__instance.PlayerCounter) return; // No instance if (update) currentText = __instance.PlayerCounter.text; @@ -207,14 +207,14 @@ namespace TheOtherRoles.Patches { int seconds = (int)timer % 60; string suffix = $" ({minutes:00}:{seconds:00})"; - __instance.PlayerCounter.text = currentText + suffix; - __instance.PlayerCounter.autoSizeTextContainer = true; + if (!AmongUsClient.Instance) return; - if (AmongUsClient.Instance.AmHost) { + if (AmongUsClient.Instance.AmHost && sendGamemode && CachedPlayer.LocalPlayer != null) { MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.ShareGamemode, Hazel.SendOption.Reliable, -1); writer.Write((byte) TORMapOptions.gameMode); AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.shareGamemode((byte) TORMapOptions.gameMode); + sendGamemode = false; } } } @@ -262,13 +262,15 @@ namespace TheOtherRoles.Patches { // 4 = Airship // 5 = Submerged byte chosenMapId = 0; - List probabilities = new List(); - probabilities.Add(CustomOptionHolder.dynamicMapEnableSkeld.getSelection() / 10f); - probabilities.Add(CustomOptionHolder.dynamicMapEnableMira.getSelection() / 10f); - probabilities.Add(CustomOptionHolder.dynamicMapEnablePolus.getSelection() / 10f); - probabilities.Add(CustomOptionHolder.dynamicMapEnableAirShip.getSelection() / 10f); - probabilities.Add(CustomOptionHolder.dynamicMapEnableFungle.getSelection() / 10f); - probabilities.Add(CustomOptionHolder.dynamicMapEnableSubmerged.getSelection() / 10f); + List probabilities = + [ + CustomOptionHolder.dynamicMapEnableSkeld.getSelection() / 10f, + CustomOptionHolder.dynamicMapEnableMira.getSelection() / 10f, + CustomOptionHolder.dynamicMapEnablePolus.getSelection() / 10f, + CustomOptionHolder.dynamicMapEnableAirShip.getSelection() / 10f, + CustomOptionHolder.dynamicMapEnableFungle.getSelection() / 10f, + CustomOptionHolder.dynamicMapEnableSubmerged.getSelection() / 10f, + ]; // if any map is at 100%, remove all maps that are not! if (probabilities.Contains(1.0f)) { diff --git a/TheOtherRoles/Patches/GetStringPatch.cs b/TheOtherRoles/Patches/GetStringPatch.cs new file mode 100644 index 0000000..a39580a --- /dev/null +++ b/TheOtherRoles/Patches/GetStringPatch.cs @@ -0,0 +1,31 @@ +using HarmonyLib; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TheOtherRoles.Patches { + [HarmonyPatch] + class GetStringPatch { + [HarmonyPatch(typeof(TranslationController), nameof(TranslationController.GetString), new[] { + typeof(StringNames), + typeof(Il2CppReferenceArray) + })] + public static bool Prefix(TranslationController __instance, StringNames id, ref string __result) { + if ((int)id < 6000) { + return true; + } + string ourString = ""; + + // For now only do this in custom options. + int idInt = (int)id - 6000; + CustomOption opt = CustomOption.options.FirstOrDefault(x => x.id == idInt); + ourString = opt?.name; + + __result = ourString; + + return false; + } + } +} diff --git a/TheOtherRoles/Patches/IntroPatch.cs b/TheOtherRoles/Patches/IntroPatch.cs index 101b97a..e2bb767 100644 --- a/TheOtherRoles/Patches/IntroPatch.cs +++ b/TheOtherRoles/Patches/IntroPatch.cs @@ -27,7 +27,7 @@ namespace TheOtherRoles.Patches { bottomLeft = new Vector3(xpos / 2, ypos/2, -61f); foreach (PlayerControl p in CachedPlayer.AllPlayers) { - GameData.PlayerInfo data = p.Data; + NetworkedPlayerInfo data = p.Data; PoolablePlayer player = UnityEngine.Object.Instantiate(__instance.PlayerPrefab, FastDestroyableSingleton.Instance.transform); playerPrefab = __instance.PlayerPrefab; p.SetPlayerMaterialColors(player.cosmetics.currentBodySprite.BodySprite); @@ -241,7 +241,6 @@ namespace TheOtherRoles.Patches { } } public static bool Prefix(IntroCutscene __instance) { - if (!CustomOptionHolder.activateRoles.getBool()) return true; seed = rnd.Next(5000); FastDestroyableSingleton.Instance.StartCoroutine(Effects.Lerp(1f, new Action((p) => { SetRoleTexts(__instance); diff --git a/TheOtherRoles/Patches/MeetingPatch.cs b/TheOtherRoles/Patches/MeetingPatch.cs index 0a9023d..e73bbf3 100644 --- a/TheOtherRoles/Patches/MeetingPatch.cs +++ b/TheOtherRoles/Patches/MeetingPatch.cs @@ -17,7 +17,7 @@ namespace TheOtherRoles.Patches { class MeetingHudPatch { static bool[] selections; static SpriteRenderer[] renderers; - private static GameData.PlayerInfo target = null; + private static NetworkedPlayerInfo target = null; private const float scale = 0.65f; private static TMPro.TextMeshPro meetingExtraButtonText; private static PassiveButton[] swapperButtonList; @@ -79,10 +79,10 @@ namespace TheOtherRoles.Patches { Dictionary self = CalculateVotes(__instance); bool tie; KeyValuePair max = self.MaxPair(out tie); - GameData.PlayerInfo exiled = GameData.Instance.AllPlayers.ToArray().FirstOrDefault(v => !tie && v.PlayerId == max.Key && !v.IsDead); + NetworkedPlayerInfo exiled = GameData.Instance.AllPlayers.ToArray().FirstOrDefault(v => !tie && v.PlayerId == max.Key && !v.IsDead); // TieBreaker - List potentialExiled = new List(); + List potentialExiled = new List(); bool skipIsTie = false; if (self.Count > 0) { Tiebreaker.isTiebreak = false; @@ -138,7 +138,7 @@ namespace TheOtherRoles.Patches { [HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.BloopAVoteIcon))] class MeetingHudBloopAVoteIconPatch { - public static bool Prefix(MeetingHud __instance, GameData.PlayerInfo voterPlayer, int index, Transform parent) { + public static bool Prefix(MeetingHud __instance, NetworkedPlayerInfo voterPlayer, int index, Transform parent) { var spriteRenderer = UnityEngine.Object.Instantiate(__instance.PlayerVotePrefab); var showVoteColors = !GameManager.Instance.LogicOptions.GetAnonymousVotes() || (CachedPlayer.LocalPlayer.Data.IsDead && TORMapOptions.ghostsSeeVotes) || @@ -200,7 +200,7 @@ namespace TheOtherRoles.Patches { bool mayorFirstVoteDisplayed = false; for (int j = 0; j < states.Length; j++) { MeetingHud.VoterState voterState = states[j]; - GameData.PlayerInfo playerById = GameData.Instance.GetPlayerById(voterState.VoterId); + NetworkedPlayerInfo playerById = GameData.Instance.GetPlayerById(voterState.VoterId); if (playerById == null) { Debug.LogError(string.Format("Couldn't find player info for voter: {0}", voterState.VoterId)); } else if (i == 0 && voterState.SkippedVote && !playerById.IsDead) { @@ -225,7 +225,7 @@ namespace TheOtherRoles.Patches { [HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.VotingComplete))] class MeetingHudVotingCompletedPatch { - static void Postfix(MeetingHud __instance, [HarmonyArgument(0)]byte[] states, [HarmonyArgument(1)]GameData.PlayerInfo exiled, [HarmonyArgument(2)]bool tie) + static void Postfix(MeetingHud __instance, [HarmonyArgument(0)]byte[] states, [HarmonyArgument(1)]NetworkedPlayerInfo exiled, [HarmonyArgument(2)]bool tie) { // Reset swapper values Swapper.playerId1 = Byte.MaxValue; @@ -422,7 +422,7 @@ namespace TheOtherRoles.Patches { if (roleData.neutralSettings.ContainsKey((byte)roleInfo.roleId) && roleData.neutralSettings[(byte)roleInfo.roleId] == 0) continue; else if (roleData.impSettings.ContainsKey((byte)roleInfo.roleId) && roleData.impSettings[(byte)roleInfo.roleId] == 0) continue; else if (roleData.crewSettings.ContainsKey((byte)roleInfo.roleId) && roleData.crewSettings[(byte)roleInfo.roleId] == 0) continue; - else if (new List() { RoleId.Janitor, RoleId.Godfather, RoleId.Mafioso }.Contains(roleInfo.roleId) && CustomOptionHolder.mafiaSpawnRate.getSelection() == 0) continue; + else if (new List() { RoleId.Janitor, RoleId.Godfather, RoleId.Mafioso }.Contains(roleInfo.roleId) && (CustomOptionHolder.mafiaSpawnRate.getSelection() == 0 || GameOptionsManager.Instance.currentGameOptions.NumImpostors < 3)) continue; else if (roleInfo.roleId == RoleId.Sidekick && (!CustomOptionHolder.jackalCanCreateSidekick.getBool() || CustomOptionHolder.jackalSpawnRate.getSelection() == 0)) continue; if (roleInfo.roleId == RoleId.Deputy && (CustomOptionHolder.deputySpawnRate.getSelection() == 0 || CustomOptionHolder.sheriffSpawnRate.getSelection() == 0)) continue; if (roleInfo.roleId == RoleId.Pursuer && CustomOptionHolder.lawyerSpawnRate.getSelection() == 0) continue; @@ -654,7 +654,7 @@ namespace TheOtherRoles.Patches { [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.StartMeeting))] class StartMeetingPatch { - public static void Prefix(PlayerControl __instance, [HarmonyArgument(0)]GameData.PlayerInfo meetingTarget) { + public static void Prefix(PlayerControl __instance, [HarmonyArgument(0)]NetworkedPlayerInfo meetingTarget) { RoomTracker roomTracker = FastDestroyableSingleton.Instance?.roomTracker; byte roomId = Byte.MinValue; if (roomTracker != null && roomTracker.LastRoom != null) { diff --git a/TheOtherRoles/Patches/PlayerControlPatch.cs b/TheOtherRoles/Patches/PlayerControlPatch.cs index 213d1e2..ace0e5f 100644 --- a/TheOtherRoles/Patches/PlayerControlPatch.cs +++ b/TheOtherRoles/Patches/PlayerControlPatch.cs @@ -1133,7 +1133,7 @@ namespace TheOtherRoles.Patches { [HarmonyPatch(typeof(PlayerControl), nameof(CachedPlayer.LocalPlayer.PlayerControl.CmdReportDeadBody))] class BodyReportPatch { - static void Postfix(PlayerControl __instance, [HarmonyArgument(0)]GameData.PlayerInfo target) + static void Postfix(PlayerControl __instance, [HarmonyArgument(0)]NetworkedPlayerInfo target) { // Medic or Detective report bool isMedicReport = Medic.medic != null && Medic.medic == CachedPlayer.LocalPlayer.PlayerControl && __instance.PlayerId == Medic.medic.PlayerId; diff --git a/TheOtherRoles/Patches/RegionMenuPatch.cs b/TheOtherRoles/Patches/RegionMenuPatch.cs index 2f69c75..b9363c7 100644 --- a/TheOtherRoles/Patches/RegionMenuPatch.cs +++ b/TheOtherRoles/Patches/RegionMenuPatch.cs @@ -36,6 +36,7 @@ namespace TheOtherRoles.Patches { { private static TextBoxTMP ipField; private static TextBoxTMP portField; + private static GameObject serverWarning; public static void Postfix(RegionMenu __instance) { if (!__instance.TryCast()) return; @@ -141,6 +142,20 @@ namespace TheOtherRoles.Patches { TheOtherRolesPlugin.UpdateRegions(); } } + + if (serverWarning == null) { + var tmplt = __instance.ButtonPool.activeChildren[^1] ; + serverWarning = GameObject.Instantiate(tmplt.transform.GetChild(0).gameObject, tmplt.transform); + var comp = serverWarning.GetComponent(); + comp.fontSizeMin = 2; + comp.fontSizeMax = 2; + serverWarning.transform.localPosition = new Vector3(0f, -3f, -10f); + __instance.StartCoroutine(Effects.Lerp(0.1f, new Action((p) => { + comp.text = Helpers.cs(Color.red, "Vanilla Servers Are Currently Not Compatible With TOR"); + }))); + serverWarning.SetActive(true); + } + } } diff --git a/TheOtherRoles/Patches/RoleAssignmentPatch.cs b/TheOtherRoles/Patches/RoleAssignmentPatch.cs index 31c7855..ded2828 100644 --- a/TheOtherRoles/Patches/RoleAssignmentPatch.cs +++ b/TheOtherRoles/Patches/RoleAssignmentPatch.cs @@ -11,10 +11,10 @@ using static TheOtherRoles.TheOtherRoles; using TheOtherRoles.CustomGameModes; namespace TheOtherRoles.Patches { - [HarmonyPatch(typeof(RoleOptionsCollectionV07), nameof(RoleOptionsCollectionV07.GetNumPerGame))] + [HarmonyPatch(typeof(RoleOptionsCollectionV08), nameof(RoleOptionsCollectionV08.GetNumPerGame))] class RoleOptionsDataGetNumPerGamePatch{ public static void Postfix(ref int __result) { - if (CustomOptionHolder.activateRoles.getBool() && GameOptionsManager.Instance.CurrentGameOptions.GameMode == GameModes.Normal) __result = 0; // Deactivate Vanilla Roles if the mod roles are active + if (GameOptionsManager.Instance.CurrentGameOptions.GameMode == GameModes.Normal) __result = 0; // Deactivate Vanilla Roles if the mod roles are active } } @@ -52,8 +52,7 @@ namespace TheOtherRoles.Patches { AmongUsClient.Instance.FinishRpcImmediately(writer); RPCProcedure.resetVariables(); if (TORMapOptions.gameMode == CustomGamemodes.HideNSeek || TORMapOptions.gameMode == CustomGamemodes.PropHunt || GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek) return; // Don't assign Roles in Hide N Seek - if (CustomOptionHolder.activateRoles.getBool()) // Don't assign Roles in Tutorial or if deactivated - assignRoles(); + assignRoles(); } private static void assignRoles() { @@ -66,7 +65,7 @@ namespace TheOtherRoles.Patches { assignRoleTargets(data); // Assign targets for Lawyer & Prosecutor if (isGuesserGamemode) assignGuesserGamemode(); assignModifiers(); // Assign modifier - setRolesAgain(); + //setRolesAgain(); } public static RoleAssignmentData getRoleAssignmentData() { @@ -524,7 +523,7 @@ namespace TheOtherRoles.Patches { playerRoleMap.Add(new Tuple(playerId, roleId)); - MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.SetRole, Hazel.SendOption.Reliable, -1); + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetRole, Hazel.SendOption.Reliable, -1); writer.Write(roleId); writer.Write(playerId); AmongUsClient.Instance.FinishRpcImmediately(writer); diff --git a/TheOtherRoles/Patches/ShipStatusPatch.cs b/TheOtherRoles/Patches/ShipStatusPatch.cs index 18d48a2..26c7392 100644 --- a/TheOtherRoles/Patches/ShipStatusPatch.cs +++ b/TheOtherRoles/Patches/ShipStatusPatch.cs @@ -14,7 +14,7 @@ namespace TheOtherRoles.Patches { { [HarmonyPrefix] [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.CalculateLightRadius))] - public static bool Prefix(ref float __result, ShipStatus __instance, [HarmonyArgument(0)] GameData.PlayerInfo player) { + public static bool Prefix(ref float __result, ShipStatus __instance, [HarmonyArgument(0)] NetworkedPlayerInfo player) { if ((!__instance.Systems.ContainsKey(SystemTypes.Electrical) && !Helpers.isFungle()) || GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek) return true; // If Game Mode is PropHunt: diff --git a/TheOtherRoles/Patches/UsablesPatch.cs b/TheOtherRoles/Patches/UsablesPatch.cs index 6c6569f..0da7c82 100644 --- a/TheOtherRoles/Patches/UsablesPatch.cs +++ b/TheOtherRoles/Patches/UsablesPatch.cs @@ -19,7 +19,7 @@ namespace TheOtherRoles.Patches { [HarmonyPatch(typeof(Vent), nameof(Vent.CanUse))] public static class VentCanUsePatch { - public static bool Prefix(Vent __instance, ref float __result, [HarmonyArgument(0)] GameData.PlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse) { + public static bool Prefix(Vent __instance, ref float __result, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse) { if (GameOptionsManager.Instance.currentGameOptions.GameMode == GameModes.HideNSeek) return true; float num = float.MaxValue; PlayerControl @object = pc.Object; @@ -271,7 +271,7 @@ namespace TheOtherRoles.Patches { [HarmonyPatch(typeof(Console), nameof(Console.CanUse))] public static class ConsoleCanUsePatch { - public static bool Prefix(ref float __result, Console __instance, [HarmonyArgument(0)] GameData.PlayerInfo pc, [HarmonyArgument(1)] out bool canUse, [HarmonyArgument(2)] out bool couldUse) { + public static bool Prefix(ref float __result, Console __instance, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] out bool canUse, [HarmonyArgument(2)] out bool couldUse) { canUse = couldUse = false; if (Swapper.swapper != null && Swapper.swapper == CachedPlayer.LocalPlayer.PlayerControl) return !__instance.TaskTypes.Any(x => x == TaskTypes.FixLights || x == TaskTypes.FixComms); @@ -340,7 +340,7 @@ namespace TheOtherRoles.Patches { if (Hacker.hacker != null && Hacker.hacker == CachedPlayer.LocalPlayer.PlayerControl && Hacker.hackerTimer > 0) { for (int k = 0; k < __instance.vitals.Length; k++) { VitalsPanel vitalsPanel = __instance.vitals[k]; - GameData.PlayerInfo player = vitalsPanel.PlayerInfo; + NetworkedPlayerInfo player = vitalsPanel.PlayerInfo; // Hacker update if (vitalsPanel.IsDead) { @@ -416,7 +416,7 @@ namespace TheOtherRoles.Patches { num2++; DeadBody bodyComponent = collider2D.GetComponent(); if (bodyComponent) { - GameData.PlayerInfo playerInfo = GameData.Instance.GetPlayerById(bodyComponent.ParentId); + NetworkedPlayerInfo playerInfo = GameData.Instance.GetPlayerById(bodyComponent.ParentId); if (playerInfo != null) { var color = Palette.PlayerColors[playerInfo.DefaultOutfit.ColorId]; if (Hacker.onlyColorType) diff --git a/TheOtherRoles/Players/CachedPlayer.cs b/TheOtherRoles/Players/CachedPlayer.cs index 2cbee9a..39dcf97 100644 --- a/TheOtherRoles/Players/CachedPlayer.cs +++ b/TheOtherRoles/Players/CachedPlayer.cs @@ -19,7 +19,7 @@ public class CachedPlayer public PlayerControl PlayerControl; public PlayerPhysics PlayerPhysics; public CustomNetworkTransform NetTransform; - public GameData.PlayerInfo Data; + public NetworkedPlayerInfo Data; public byte PlayerId; public static implicit operator bool(CachedPlayer player) @@ -99,7 +99,7 @@ public static class CachedPlayerPatches CachedPlayer.PlayerPtrs.Remove(__instance.Pointer); } - [HarmonyPatch(typeof(GameData), nameof(GameData.Deserialize))] + [HarmonyPatch(typeof(NetworkedPlayerInfo), nameof(NetworkedPlayerInfo.Deserialize))] [HarmonyPostfix] public static void AddCachedDataOnDeserialize() { diff --git a/TheOtherRoles/RPC.cs b/TheOtherRoles/RPC.cs index 8c11663..1c9da7b 100644 --- a/TheOtherRoles/RPC.cs +++ b/TheOtherRoles/RPC.cs @@ -16,6 +16,7 @@ using TheOtherRoles.CustomGameModes; using AmongUs.Data; using AmongUs.GameOptions; using Assets.CoreScripts; +using Reactor.Utilities.Extensions; namespace TheOtherRoles { public enum RoleId { @@ -83,7 +84,7 @@ namespace TheOtherRoles { // Main Controls - ResetVaribles = 60, + ResetVaribles = 100, ShareOptions, ForceEnd, WorkaroundSetRoles, @@ -101,7 +102,7 @@ namespace TheOtherRoles // Role functionality - EngineerFixLights = 101, + EngineerFixLights = 120, EngineerFixSubmergedOxygen, EngineerUsedRepair, CleanBody, @@ -197,7 +198,7 @@ namespace TheOtherRoles uint optionId = reader.ReadPackedUInt32(); uint selection = reader.ReadPackedUInt32(); CustomOption option = CustomOption.options.First(option => option.id == (int)optionId); - option.updateSelection((int)selection); + option.updateSelection((int)selection, i == numberOfOptions - 1); } } catch (Exception e) { TheOtherRolesPlugin.Logger.LogError("Error while deserializing options: " + e.Message); @@ -212,7 +213,7 @@ namespace TheOtherRoles { GameData.Instance.GetPlayerById(player.PlayerId); // player.RemoveInfected(); (was removed in 2022.12.08, no idea if we ever need that part again, replaced by these 2 lines.) - player.SetRole(RoleTypes.Crewmate); + player.CoSetRole(RoleTypes.Crewmate, true); player.MurderPlayer(player); player.Data.IsDead = true; @@ -222,6 +223,9 @@ namespace TheOtherRoles public static void shareGamemode(byte gm) { TORMapOptions.gameMode = (CustomGamemodes) gm; + LobbyViewSettingsPatch.currentButtons?.ForEach(x => x.gameObject?.Destroy()); + LobbyViewSettingsPatch.currentButtons?.Clear(); + LobbyViewSettingsPatch.currentButtonTypes?.Clear(); } public static void stopStart(byte playerId) { @@ -249,7 +253,7 @@ namespace TheOtherRoles public static void setRole(byte roleId, byte playerId) { foreach (PlayerControl player in CachedPlayer.AllPlayers) { if (player.PlayerId == playerId) { - switch((RoleId)roleId) { + switch ((RoleId)roleId) { case RoleId.Jester: Jester.jester = player; break; @@ -259,7 +263,7 @@ namespace TheOtherRoles case RoleId.Portalmaker: Portalmaker.portalmaker = player; break; - case RoleId.Engineer: + case RoleId.Engineer: Engineer.engineer = player; break; case RoleId.Sheriff: @@ -389,7 +393,7 @@ namespace TheOtherRoles } if (AmongUsClient.Instance.AmHost && Helpers.roleCanUseVents(player) && !player.Data.Role.IsImpostor) { player.RpcSetRole(RoleTypes.Engineer); - player.SetRole(RoleTypes.Engineer); + player.CoSetRole(RoleTypes.Engineer, true); } } } @@ -981,6 +985,7 @@ namespace TheOtherRoles } } + bool lawyerDiedAdditionally = false; if (Lawyer.lawyer != null && !Lawyer.isProsecutor && Lawyer.lawyer.PlayerId == killerId && Lawyer.target != null && Lawyer.target.PlayerId == dyingTargetId) { // Lawyer guessed client. if (CachedPlayer.LocalPlayer.PlayerControl == Lawyer.lawyer) { @@ -988,6 +993,8 @@ namespace TheOtherRoles if (MeetingHudPatch.guesserUI != null) MeetingHudPatch.guesserUIExitButton.OnClick.Invoke(); } Lawyer.lawyer.Exiled(); + lawyerDiedAdditionally = true; + GameHistory.overrideDeathReasonAndKiller(Lawyer.lawyer, DeadPlayer.CustomDeathReason.LawyerSuicide, guesser); } dyingTarget.Exiled(); @@ -997,15 +1004,15 @@ namespace TheOtherRoles HandleGuesser.remainingShots(killerId, true); if (Constants.ShouldPlaySfx()) SoundManager.Instance.PlaySound(dyingTarget.KillSfx, false, 0.8f); if (MeetingHud.Instance) { - MeetingHudPatch.swapperCheckAndReturnSwap(MeetingHud.Instance, dyingTargetId); foreach (PlayerVoteArea pva in MeetingHud.Instance.playerStates) { - if (pva.TargetPlayerId == dyingTargetId || pva.TargetPlayerId == partnerId) { + if (pva.TargetPlayerId == dyingTargetId || pva.TargetPlayerId == partnerId || lawyerDiedAdditionally && Lawyer.lawyer.PlayerId == pva.TargetPlayerId) { pva.SetDead(pva.DidReport, true); pva.Overlay.gameObject.SetActive(true); + MeetingHudPatch.swapperCheckAndReturnSwap(MeetingHud.Instance, pva.TargetPlayerId); } //Give players back their vote if target is shot dead - if (pva.VotedFor != dyingTargetId || pva.VotedFor != partnerId) continue; + if (pva.VotedFor != dyingTargetId && pva.VotedFor != partnerId && (!lawyerDiedAdditionally || Lawyer.lawyer.PlayerId != pva.VotedFor)) continue; pva.UnsetVote(); var voteAreaPlayer = Helpers.playerById(pva.TargetPlayerId); if (!voteAreaPlayer.AmOwner) continue; @@ -1311,7 +1318,6 @@ namespace TheOtherRoles } public static void yoyoBlink(bool isFirstJump, byte[] buff) { - TheOtherRolesPlugin.Logger.LogMessage($"blink fistjumpo: {isFirstJump}"); if (Yoyo.yoyo == null || Yoyo.markedLocation == null) return; var markedPos = (Vector3)Yoyo.markedLocation; Yoyo.yoyo.NetTransform.SnapTo(markedPos); diff --git a/TheOtherRoles/Resources/Copy.png b/TheOtherRoles/Resources/Copy.png new file mode 100644 index 0000000000000000000000000000000000000000..1db34bbf70cef738ff92b5ff32d2eebdeaa7f8ce GIT binary patch literal 17161 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 literal 0 HcmV?d00001 diff --git a/TheOtherRoles/Resources/CopyPasteBG.png b/TheOtherRoles/Resources/CopyPasteBG.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f47cce48e9404ef115793e5d92db858d0bc9ea GIT binary patch literal 13064 zcmY*=Wmuds(=MeHx8e>(ihFS@?(Xiz-C=>^#k~|LP!=e@xVyW%yDjdzaJKJvuJgU; z$3DB+E6-#yncSIsl0>Q~NuwbXA;ZAHpvlTesKLOzLxKLzK!k@r+X)jndZ-t+JCZosL?0tSX7OIAWu!`tXIYh~h_ zzSew4iGLB=r_;E1iZ^1hF0>@}KfVU5ws!V1IH6Ovw- zaNx|)Sts-IF|niKMmyC=l!pMdy~tVlXdvYkgcti}GbT!6#8j&SXc@d`^iRhP>Dr7Y z>?pr!(_;&u+n#Z;_WBdH?G*i@ULc?a)tc3_6?9YX)Oy+f=6}ED706#gyHhL>y{!83 zXBute_<7+R0Os6yt}1=@r4w_Be$hh2bbD)$`mv7HW*mN@vSe=bLxHne^_Uqfulr2D z){%ABY|z@1uf)C}*Wkhe;hN>b)1dXSOv$z9H$mv}Uq>dq(lC<47pkeZn3ed_5wzb{YN{3uNCdJ^T3J78E}h9g5$fZ!ri~SX9ffayw%Hd{E0gptrRe=~ zT)|VR{VN4FvnT@5OU@z6EvDz(ET-8m&~lRTaA_i0HvN^euFBO1fUg1anMsX5nbq(W zzZuK;wWMv0{CRf^5v^GUGB^Xpo^ur{Z@AN^;H;?>X1cn14F z_gQW=sQ#kcv*yR-IeNA?CPfPwyw=-*V;ypRz^WztC!QfL;`~#xKKfq6b=Jy^A6iZ_ z>`s8}oZJAh5d)dEAc9EGr4n7w3DZ54xl0|-u)~vfbQRnk!x@}L_FTwv<%{KH+w?+_ zqN8>c6WIerUPFXCOF9RL)6MntST~vp?A=%3^RdCyp_MFx{OP1aCAHzsSocci>awuy z0kCcUqn=LC7vN9rA&$~VTc_guZjHv$`D}T91?=>h*)V>cMJS=|Ex2s8^RCpEx{O{f z#3qdtKF-lnT)hV+a-ddIW5Vgb*JJFuPvGH;z{;9}_rCx-& zl#Y9})Szd$9#LQ(T+etr+PrZi$+Q!y+PGpHKW>rd@1-o9G9cuJ!aAOe?-PQ8qit!u zac%jjevJ8vjA;iQKc`&gC3F||&X9R%rYAxr)t$6dZDb5(&SW9vM@((2i9sv@KhiBh zH0v}@J#xP9j5TwYz6PH85t9kx8nQHZX}d0Z{FvJFa>YOhDXG6hTJv}j)Q%P^XFJBv z9t{C!R)OIk^47emAH{u1w)zgtr^YcvTaeqyAm?&+qK~`xfaP*0Y^qK*l(uEKg3J@N zlN+*_X!?;sdplgweG~l^rONv{t4mX-Ka9cU9Clas;#lTn1=Y<{EexM^MiP>=aOd_w zdWA`p&QrL}EHsNN$YRSTK|A7*WxHnEg!Q^2XK=FNhgMFP&3-8=^e=YP^F-M8JQ~eX z)Vh7%F(1Vbv18xCe`>+k6C4C@+Ih31Kh!m4l4ng~MRX z__n8O*g!^k50t|%ewa|uXf=P{GBH&k?7nAAg~*n&wmhgnN83s_@yy8}sq|`C#Qx|1a6KIskdq9f6JmPICQZX#^7MRgzT+!Q?yp$xL_n zH)j%m6kxvn$*i?F$I2F{xX@AVcrQH8i|`T)z}b!1=L_s+YA3X6O| zNE$H7p_|$>leF?AEmFtNllfZ3BUJX2Wlhv2HHgydS!6*YC$iUh`u4r4-bRLJru z9G!EHSQ#C7m(H&Sl>DZGd z$+$FHc;jQaGw$Uy1I`oT8Yei96`-Mc1(*3_CjS6MoCKym{#2|ebB}}(qy*LWr}>!f zf-*}9ciI~ft=SqXj(W8u2MV00SE}bnL46jSk0Qf5`3CN&p66EU)=ECC{dX81q}!rZEAy9lCqrjB`Ky3fp-_JgI`|ZKf%7JEhp4szh%6sy6 zx)BH&+)tI{*SR6Y4Cc=4pCHx5lB)E1^7QXQly7E}S71c}M6;h9eh-cM1%+KM2?Gn{ zzow2sz?aDGm~H-kFE%}Qb$^(&-#?p`jB|UU6z5SRuY70%oH){&KOU_j#BqAhXqta; zm?%bHs`l(GG)a>^XX?#1E5%sivx+Ht`KWchN%@ImZ~|2(*0AAu_g%zodov4sCF`eg>It;sf|OuVZJp*err5&oVB+|beFU|r?y{^mno2xui!V=b_`zI{;3w3JT~rI70>MT9ewVA z{W!+|I{uFa_0G_57k55d+Z&-Ef;L5l5XX)4AO0fJnK2b2dABH5Rl2+8>0#=HL2gd!Sn{Xs) zfkQ6p*(6+|gsKO#5lKiX;`T)s5OL%<+c6!iRyEK$>x*$Q+GV|6?A&({i3;|{v1e4RLUtH#B&Tq#$Q;EJ6qScMfkEY=`COhiudMd4U|20IPa6w1* z5$f;a{sts7v6n&!v6sA|Q`&qmbD@&w#Pbj9WtYP#jc`aVp+mx+jV5nJI3E?$a%HhN zqLas(e8g%g&oF4Q`zW)kBc&{qO)ij-AJ+JD6?yTO#LJ0Te`KCg1AGPJR-IH?^0HWt zYk~Xq#f^SSje?zH*{qGYgg`Nnq+DK)2Od%fYIP;j)sW=Wp?%i4YJmIu3;g+09gER$ zb3Eo6VASfdP8hXKRmt>|8^suy(e(w4Tnw6bvoz0z;qid|QXkW$y@;%jSt6V>H)eqU z@?CH&dfQyv+wTd{zB0r^K8rn(?tK9xp?<*!ISx>D&)%0ihXGXM7CM~yV-uw(-_3OO zER*l^oA&E&pXcx01-dg`O9osJHC@*Trk0qB&GMzd*Qn^Mcpm2(oP1@UoZan{yL8`w z6H^hjvuNUTMy{K;bY~h?gd^99=q(gYqjfoO-eS6=35_qNUa99$m5dnNT5^X}(d1Ro zSZsa;cAyPn((td?V{X9hQ=YpWCjxWHv>ha+Oxl{rGSzHcNqb%D+3~~N*tvalY$ysw zw6IoR?T$1MJc8uXwVzi$qen>{1n84#j*OBk7$U|iM)=zr%$=9F|Fzi=o}#9|h8XQV z5owIwiEer7hi{jTMufp7dF&nM&ny_6wkVaF)#O0RYgvR(_9rzt;tjKU#3ItE8gxIi zBC<`nv;X4ak*q2Da+_tl;a&e#A9i6nIq6chlC?lG`3@-%er~wO`+AJWY4*k;=S9P% zNHb=F5&g>hJNZL2tw0-l1%YtPHlLU@bU z`OjNZl6BqusWMBWD(dTdgi@YE_nToTt7D0Yk{`%1XA3$s^j~Tv-p1S@zmay?&ib%I zS+4)3L~9XL#WqQ&pT(qG@SU9$6+TUjvSPa$2eRcr(+VOhVMN$}b5xrI^lf?I4W`z;6R?6UL@sy3= z{WxD=vWIe5lx~zD!YrSTM&SHbdc5=blh>8%Zyl!USQo9Eu6vv!h<4HZvJD%70pb$- z;Uq1W^g6nRJ&-Dtw3IZL)y7J6WL?}{Trah^*;LItqr$LHFai}GUy2#iPFr*_oZp!= z87S|?B17I%EBG*XWc*lv&L+7O_QDk-pv7Ndfa$S6UtSvc2zkNI?nKw zGj&U5DNL=pIKnk1FPY@esw+gSyQS(Spj$c;c>vIw00lYk0C@^k=1{{T>%x*7&zwBS=S+y{f7EpYhhYgAyNWR5iuoMUx zU}44(4u9EG2KX_Cp)GmpYWAEEq8=j=jCl`G#Qzn%cW4TR^-B z8n>#YQUP?xeW@ameDh5X+d+-%sKIQWYF*X1Itv2r5r3=($K!ztH)PyBJ8Y|g?XUcA zeAAnj+qVlw9hXP9uL$QNcm3&hH&dIVT()I_NYM}K&dYuusLMpQ^6rC8&)AgCm|DLT z_!r@$>9}w0OqDJscj4KIytD6tPwNT*HNihi#|Z5c`MdM_#^QmEH(lXeGcGh4I^ws9 zJlz>KjJV1ABm2aPIRd3Kv)yMJ^VRF_k^DIx;@Z2StC!}D&_}u4)*J7)%<-a|q{s>5 zjPlUns8QZCp&%qiDzz#ZS?SEYm2CtOtnFNwRc+9_TiLcD(#&kOV!L)Ylfu#`!&vS_C__ ztVbBjcEj}1X7TM;M8!J%e?)lgjB-6$yX+EiX$gV?F?AN=oJ5;{(WxE})|nq+Y(LCx zyhZmKHC+*-ywP@8uX%fjEjm`2|2jS0bMYKMATlJg+Fwtpp)I%oMh z6T*9`nwSL_4NnqJXjfooCMQ)W&j*Fm(Did|nX3{QAX7XgyN(wB({*_NRv&O#HP83X zH0@J@-Bys_o>;%RMba* zCMFZROfCQy<6T{c>KsX_x^%#`Q^E1RqsYEs_|h%@#g)mw0^1+DlV%4xJ`$qBvm!p` zd!Bgk@^gEkgo#Y9Xx^YIwQiR6d*3+kK*mV#;S(oPNGKACE0$k)3 zyJY!Q5gj^&iB4a8C&lzO0=(wRL1SUn^=R-R(G)p5%et6;y+D!94+CX{q-=H1hmAr% zX3m+e1S&~v1kv{8i8)s&p=Mw~WQIEq$v|zll<>w&KGgHaDI^mwS{3Qub|3Ce6t32UTDj35hS?_m)f7AGMwljN_qBw zl?GhAt2QrtHkPJQW6PN_7;YYZG>0sf6G72hj4}yEL(b$gx}6JRj!s{;Ho|}lz$({Q zJH%$FcNwf;SFZTDy?zsLq)i)E@J{FV_7m~m(T>*I7MgFH!jP{f23o(MM z9xm9aj~P4fm(lt;(`sdg8st@~H(L6fW^n2h(A;Ul)`&_H1(%c5&8kYTmx)|mhVdw> zHE^s-vY|U$^@>=aUHeFb#l;MLre^H0BTqO3^3xBg$R_(Vr7Rbh~3n6P>9&;>L|ayqyAC?|O4Ob*7u z&-xXZ!_EEyPeRZx!M`<0G9wE=z1Qz8vj?beW8B5fVDr9>chhHGkta8Wc+%@*z5a9h z3y@J&dLc|VXNt@XrD@i7-Gv~9UI+*GGLA1B(v|lF)}I|D%0VTY6(zXcXd+@IM`;Y*21adu z55jn3amJ)269=z_y1q@kH^N?&tt_t!B5O*{*Mhp%NT+}=P{Z91Co=m%a!S|=AK4jT zRaGhwFSZbX4z~OhPo~?@8qlN;m90jxZ5yjHottXrA1C0z$L`B5p)43)^i8XnNkwjX zp5mMscMLeC1g!~FS$yrDP4y6l&l_~bZGx?(SxVzI}V(PiQ+It#m% z`gK;s4%5Q~)*H#5A%VN+({FF9M7-tzqG2rrV4Mci~D@t4lYeG~yhz;T4$du|FMc5kk7; zrNS)AQTKf`J6li&Do!o7OeZ#1&3_Q9n@LwC`=K2vNyM8$y$>CdLBam3cNS~}g7~d;7gE^S1hKB=D9?rw<+SEEvjQk@?%m8yvgCGe zoj}c&$mfz<10E}*R+vRxlE20k_QKDj`__9{t3{aIx=^3CfC8Z93U3LCo| zG=^GseW%s&n5RNkM!Xg4x>6-EW-3|`h&W-;`8k?9aUCfzf6iqF-GfSlbvYndRo#qt z?K}Rf)=zkC3f&62O0%b!9g3Ft@+D^6F6|e`zXhQC8F9eG$n^|1xltm-paOM#6ACWU zU5xLOd`)O{J%Et$|1pBMWnxk3;tsaH{oB~jN1#tu&?~Q!aQID03f#FB< zGYH&!yY)^a;S5itT>w=oeQj>KXwZawY^L#qED6+F!9|@4Cz?2V4Kob#t=SuoIo?*H zB~JXPuY|u9TQKL2&Yhuda{cL83=eUD9yu#g&n~9-_|DrEr_d9xOlr--$*a{B7p;X9 zDk@23mj0yGs;I7$7?JZZWv$~5!Dj$W_o*dP~ zQK4hGw$eu|5D3DKmSl#p9b?4i$NS((Yzu%KzL$r{*!}aqI2AM1-U*n{KO6(cQ4=u6 zah(5Vr`+u;OK-FkH7lAKq^I0hc*)BiS{eTJLhCEP8Y4WT0=_vA<3{4z<=^nEAhDOe zE>C|=t-ODm6lCl(xlsN7Kt*)j-K|=r(^^%!=|(29P=_{gf*rbW&6Xgt@mFD^9k4(8P$hAa>@7hG#BfQ%R6Z~iFOpLGSUZ-5Sc z8rn8z1w5vA+F%PxACRDS{ia8EzDvv%me$Dm8@qF!3ekyl+>^X|4X+jczXdL;*BmdR zm1wh)=I<4fF3N}~)c0P{q?wUDyl=bb^|RIuWm=n#p}xmTY=rrv6Of^8pOjmI;dirS z_PhTQ|F=aQ-Q=IXvq&s^8?P7Wx7=3W4y`>lAhTXDes#D(BMOwf9}DGOnLX$60)!rbm702O4RY5rp0YRgA?JI~!6t zZaUn(a0ZN>`4eY?Y*S*G6>H;V{!LNm#gFkTTk+?5IAz7snCmWAysq%jmRsT^c0FVr zEXpdZ{wyzz>PL5NRy!;3E7kEv_;ccoZ^1_+pi2w9D_f0>~(FNK# z78BO`(L$;G?h}RA*#ewk*lY`0;SO(mO;M`tDdB45mqP>Hk(V0Yk#mauu*3ffZP^dY z!i>HQ!ksc)1%Y-H%K_f6T=iY|n!?_F_x9T5{kcb;=S@)n~A@I!DV zDa4DelhN3LM9^h2Psselc!^3*b?vD0eS_Rb6Svx*9AwyCEN1l?(S4@TXnpS-R z>I%BfzG>J8@R9Zw#ej?Pf0+gOHWFCta-h$WI&G_~T?VYoUs#{)?h zIbhqZkni#h%>82#R5_2H+X$JSfS@jiL9yp0~w!-U@iU}VcgzLwo zgOQrt4#wxQ7S4Y+2O%76`plDXE?6D-fTk>=#CULnp6(9JY(~%5llJ+XjG1Zd6BH<{ ztML%(3BK&iqQDEW<&1xu{hJCJx&QP7TfzoGvJVY_qaXi+nAra`A??hSC{$V-kjH=H>}z`x$U0g{W|anrxw1 zcpwp$)sx6;U_h-DI4R)i@!1(;XVz@1#4m_0n^}op%+~0@x9J}Vf<>U}(!NAW&}YyW z(^wk9xEXNdBLkp5W>i3)wnNwZ?JZWobbD*Zb8~@@6K_M!P>OeTiMDZL6|IiSkBObt zN+$z2!~00FSMkz4k&4;+XCQ&Zv0S^*Q8z0}4gpXMUB_s)5DkK49ktTqpX9j0tFt;U zoy3bsfaOZ!;%8A#PH)H-`GJN0fj$6!^+hSd_kFbi>MYHYFm;aOMGrqkP zDOgqX_UfUmgUsgPPHGUf^pP&}BuQ5L7k>13$YEx^Z&V#$g)PwQiR-uQ-C)?5uPZE+ zbzLdu{ON6PvVy0d7Y%b`WLX+3HN4qN66u9)-PO*Wpw;4_CZ|@O?`W?}WT>24OSf(w zY3vw`;J)3=H6dGV`mmsI6$g|#By3ITxVd|29XyrHn<25NYN~3n_^QbRlzF{S{Va* zvPssfuxhf=BvbaM;C8@U_|G+X%kvZ|g2IPs`lt<+aeR&ns{cOO)L99NH6&-)hYpE2 zmSAsZjsaUsWS>NSlLsASDGob9zRiF@nyfqE`%64ak}r0-OlRTf zLq@!6I)HeF`k3lmG427L1yqsSP!^b_=Zx(GBYlnVG5AMuxoz_3x*tsN%n|yYepLnS zbi8G_RmMy5q*Du2JRP?w4Hz2LC|Zg|OCNh4HvZIRs)Hjct~~20Td|so731d%%DhK% z-N)BzMGNVKzRaox65Wb=+35B2h z_=E2KIb}9pA&Rs3i!5QX&&H2EE7B(9Ulkw4>1H&|4kJfsEpL>J;3l=(G+>W2wQuG2 zC<@RLS)>i?qfbpxt0X?zRyqYUvFx@FPR}tPykZTGiXLB}NO$M-%a>UQ0BparuZom8 zdbJF-5;UIj-Uect2Xd@T#~rV0M*mI}HCGF>Y|>TU59?8i4!7r)TRF3pI1T=-`_(Vx zJjh;*$3v4KQ~0U#3l38|-^|uB9R;zE znmHTy78>~OKY|{QG_r?^qf0p17UobyzWpesr9)2ZnaBs%6H02!h^n6HUKgnaBI)Y_ zfDG@g&?kNkKd`IUdL?K*f1ma$UHzF^DTEZdA-5}Bx+_*i{LyJ%vl%z}56Ovn;REk+ z{^%~8(g_nskWFDardL#x*s0PPf809!@QQx(!$l5lT0gh^9sQq`qnhMRCzVavA~#VI zxQBNS2%lR-elP6tB=+GG{?X?O_0mWBLT)Lo3`wyO%Bq?>UB)41rcA>jHYg%Z<@h;| z+k3YYFPJiz*8oB;_`Q5dkZAa)L~Z{ujv0fO-K-wPZE~UbG*%;E}`uV(z=AqZ-M1CoA9uKu}HF%u=BrI6B4HdVu1fDNrC2dHFZ znq{Ct#Ce$Y+K}w$oHSvP2jPhmWl0__<1(en65u>+BhydLz)0Hg8)K%YoTgSud4`vF zHQ4F7>tOxsUn63Se;KjNc71n0_gq4ZRsrPEp?df2biZ$u_1o^At<4Oej|ypwybt9& z-*B|svvrLfSoBg1$dx6Op7ok=R-tKunpqEjdvTj~RC=ed?_$M!ydd8{DR=fd(zN9% zF+k)fMdvZI#2ay2DEigzR&xo{w_t$VTk0Ao&^?b-AY($2uFYU(MCX&EY&}<; z<#2XJ#NJXP9A>wSkr4L271|WIiZR(JQ`vK+mYf zH#23t*vHPppMz!p3rYywsE6}A)jh1R2JBQi2gzMSkBo|*H);`(0CSWwj`d3I!o1u*~A&-sjlCo3;vB(eYsve|R7xzEp( zKX8qGO}6arzQI&)c_8X2Tn?z7jTPPB(ybWPkW?T9%xz`n`tujWqHh|yTxz1{dbsz( z;^^OY(eK@xZ*K#4h!O6k=UI|Q&{Cc?!m$h0n9KwtFBpQ1TGxJ0$KP=A??Sxw(1A5x zs`~?S`-Lx)E$9lPtpn?*tY>d`I2x--l@@--@XrX_5LFm z^9yKE_I9BvYn-bh9*ugW@7z??EJ20Kl^@!U(^#v3$Xfb;J$2;rX*PPDaQ<_e=A{7` zh8*17=r8KZR_Nif`p*5Pakn|4>7(egNk7H zvPqNbo5ab91^fJ7hc)T1tth|@y zGB2wqZ`XTS@&02J`?t~53&xIz>+?n-%WPtgKLSz+fXR1})-cr-l1r2)fIk_W#yb1j zck>M!u39DAqDug5y>w=emx2M}VKkf?M7`n8B|dK2&w8OL8G7ZHKqhIHpE6(>Asce= z3eOV(WeaauSriNUc4C=K@afh5&Ib=DZbu%lX%274t{+rK^Af7u0NZoiy`!QUmYBIJ zYJ)Kgb($3dDZj;?obYj+gH+(#8rzNCT}Si}o`%oz%`BwcJ)PCsK7(_>f71ylZs>z% z`5%ZQX76&z9p6hH#$uIh+}8t6>Q8JN+^L=RC1o$!usag0_0eqC)zV~WlCu={T`ip< z!f6~4S#mU=uNmr>;})Y*s+wbYT&VWRdz0~^$yL=*P{>=BsxmM+j+un04}8OoNi98v ze#I08_Lx{raZz)$NX;;wRgajF7AD&%KvX_%9DL-ll~HL?02y1%jj7_MFB->4BcGq) zRWOb5Vs|B7)|R9PXzx_#BU(E6cisMK619^#vl?qPjZ>fpd_Z-uc2AKcQ9^`y4%z!JjKl_2Bpen%Kx@i&9)c+u5H6KxQ96PRS z7_Y^LJF`IQLjQqv{3nn8Xz_qv*73}m@1-Nbl9hIq_e5DLB2i({i0lA~<)?vs1N!Vt z49Hk&bYR8^#h^>+sE%S7Vhl=~9gf6GT-_PABOyv*WVk-2YMgC1|A<93DgvusuvNNpznSJ6A;^rBQ}5Ic}%|z<~1>Q_Ne| zss-g6rmutO4L*~EI^}$i{;Z4pw5^%>xwx0EE|sPHom9hI?KMY&LEoz}-aE;kEycfB zz>&#nv|vxC1+>!dabt4rv5h0Si8-Oq4I>T9vh@8%u15*isf7l8;6G@j4 z_%(UQ+j4+=s)))Qmc2^S;wxxn;0lYNAhL!l94u0~=6eR5-QS&)n2jsJuk(%*gjip*)V>gsN<;fJ)9VeX#vuv3_G7#gC8-HoihOmGe)-+Qd(5fv+XgSLff6N?4~&SEy*z>RZdo zKEtu6Q0c67a5Nfg&^9@-q;(**>K`QGz}f*A!sJ8~$Z+!Yxse*RkqWR%N~Cj-BT;UP z&(ZyF3cj@##s7smCX^+MV?LZw9mB2IX76N7f186N|@rJF@iNicf-e1<3G}n=bYO{QR=y1^X4O^KO76y8xddWm0j4<)=fAvXR zdB%9CTpL5gMI=){2DHij@kaz|Uf~qP-BdjVkYToV2|bF}XKm`CW0XCS_pW)q6IjW@#^%B`i*(x7tU*$p1_`@Uh`6;Ps8} z8@Vs!!iE{=t?FR_SmMV~+w&km`9=ZVu6hs(?I;Y}R2+I{xfs^VJLqUZKf9nM_#~mG%mj1$#Om1)~5Jf&kd07hl?@J%h3jcKl&h8daFAXIWGbNS8qVJ^)KGw4PsWw*AnXj3Lq zr0Yr-B@|t-m2MO=sqKED-7aq`?{9`m@AkHDpU=Dh#Tn+D^L@V0bH3+!&Y9n=3=5fN zX*R(O27_6$f*9e@|5)f1G0GVFYjZgMM;Oc~B|S1q5zbMCOa|bJG${ZFRG0HB-LdJ*E^Zp+B?Fdznr@xFi+%U4G)LRMUlV&)&QNjJq0~ zcoVO#eSCH9pfLN{HZ>l*XPMZC(qHEH)O*FH^X{r%BjXETaI@+*l_lj5-ed=|GDxU|T1FWfDi^y(^@bU@D+@2T0Md|I{E(5hM_V0aBGpk*e z$!e)BKV=hYZs5%*`(qA$MpxE>&EYdV>oZK?#q%3iwquiZB3bsf(H-VLaATUx;^r!148@ktgI#Mb=utUp z^|9v@?j1F%XB5V$EnMN&=B``(XKeU+R4PGp5q^5_j6(+cHaAR>^oRa2 z&jl$*ljks2l@~^Wu0O|a%`0g-vj@DevxGU5T}cYg zXYVZh#i;Vix9x_qyyzXXDyt7HXE{vvTF{MASD&Rl>nbbQ-JSkORKGBA60Z3?hjXdT z?Hr-Owrct&(u3q&`&jUI*GkX01NRdhb|1KSHYW9Iji|kQbNP%z@o&NwITF*=Crvz@ z%0n;pRoo9Sq-@IJ88?z`t!GbzS^c(p4xxF&(ttaKP0by4$Ipj57O_^3vu@3eBGL8@ zEbsE}@;_Ub?cn>9xm9B->hJ% z;g#sT$SpzoQ#KIqwo$Cqqvr15t+Ctx#O+$X^$)fUk(Ry>pFf<~f9gSBm6K26)ryK; zo6~M@?zCdae_MGZsp)wS?v&tOZL){_*)dx#Rv5$oHlXa$$(HEAtv|ciNfz2|X{zi$ z@~l9OrVo~?N!8!wsP8=ltu~CG!$kDihE9ra8g(*^m9(__`RdEZaRgj1CtLw9>nM%9 zbqF6!pNbz>0l&AogO--==#(Z&1kutbhD!9!?q;i?HGZ2r_bT_+26@sa{mS#ks|suH z)(5g_CP2tf z+1S$uy&h?Na&a5$<~1N>sIc+BX67p$n{$dp9f^ke=gdI^t^s03_vn)^#}@Y`<+vC$ zKJmQj_hg&*pH>l`8{>Bt)<@^nw{hi@A~xp5ArLF7Qkndqsd1?*7n_ zW6@JMx%)D{Q?PsC!(;YEvjb_R_+P(mzw7O?{NmF!t0>z`2gkZ}FN_BmQ{$FgMRh*f zPU9Z+v{`*_QYqPf)GFJowfe~c@$4PzsU@MoL3XJPtAt(GGo?`$J2V~Hjjh zZ3fD6PHdUgz;ThAWnqo}aguCll5-5R#*c_t_xQ@`W5?UHJLfwkVQDR!?|jo?qUUM( z8bgHPc=p^~k%3urtu0$9^Pc1=daCYUx@-XU(@r_}{L6Lqsz*6*o2ai_O@i7Q?rxpc z5`x>7f1%=LhjmrW4U0q+iA=-4ehlk zE{44oYQ9f;)k2KFz5dU{2y-XB+Rpl>l!)~&mb6Ta*RTpK&a8b+$X+l>iV6{k00jrG5+#e}R22=Oph6#$hmIBnF4XQP3mpp;Kogu3)Bq z@b^gzc_j3~1RV~@B`GoirO7Gskv>!8fObe1cQRjqhJw<09%(a;#SVLK zqm@wziljOVEqWx9&wG!PrpS_Y7(NdTBm*KS5IMw*{RpoB1s^u(qxEQK{t^h}?mhoU z=y!VQV$tbJWk`4_TBj@q4WW&f%9rp!K2`S?PY__Z7y$_hV2NZTflI<6DL5VpiKk%5 zToOgV#qvobs90jTf+OYuS}KSf1wuT0G9bX>pakNOFeD^_?8QfNcmM&3DiA_)RA1GDjhh$V5@{sU+h zAd^FBu4TnyP`IJ6`8;YM#L0ou4HR*N09q;*>P)m7Lxq+DNz2hz2gIx!hgL%kkO3Tp zL>4KLB-0SuXyIB)T~Wb(hRcOIUBc5D>c9YBTXDlR=g$$MbzL9y=fHnqib#+s#s3@6 z2k3hix=f*z$dW>3q1;4(r}#Y2r@-%-!lBbnu8^g%{$^4Cf%6$k*dWMOB1;?LKLVIP z)EhdIl0ltRaJcRmpmKOa^2<4?0AIHSkd8xByabL|2teoUaIw7`2S1U>UIYL~#zWah z;Cex|K*0!*96pwW5W_!zHD z0JO=4!4Z&9so-#tSQ3>;pc1f%q1}f2ptUFLyVd$=&mcCNI#LfG?HNU7X%jIbMJi1O z0olhoy~~^bf*WB!oTGndJ`y%$9UzgWK~;4nKA%^K74-m^Gp9_5?WJs0~ z7ZDWm&=_>VLH8y4{e?N4CE7yz2Y-gM?;rF4QGa#vMf!e~>#JN}q`((}zh>80xxPq& zF9LtfuK$}{W*;880WtKsPYFFxZ)_es2|bJ(bAzWbV1wFUb?x@`(1@utXpS5PvpS)D z>7A04^Pxc#1&hrzxod21Me?w0xL*f-<&wx^&?BKQx4ZX!e1SE*zh&CUv{p`W@-r;Wu4y2Xp*DmHHs5T3_ehi&-*OIvxj-Qsj@9#{wvsZ1 z@f(YK#za?pWf(fXSg*2a-R}AdB`ujz_PbwOXs>C#SKl%G{R4eMXJ{i}EM^E}w|{)r FzX7Z8dvpK* literal 0 HcmV?d00001 diff --git a/TheOtherRoles/Resources/Minus_ButtonActive.png b/TheOtherRoles/Resources/Minus_ButtonActive.png new file mode 100644 index 0000000000000000000000000000000000000000..2cd4b5a09b5ffa4dc6f2f5f95c4b4dd9ab4d43e4 GIT binary patch literal 6897 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 literal 0 HcmV?d00001 diff --git a/TheOtherRoles/Resources/PasteActive.png b/TheOtherRoles/Resources/PasteActive.png new file mode 100644 index 0000000000000000000000000000000000000000..b80d73bf80b46baf6dcceb2d2c59a0e1687b1e7c GIT binary patch literal 14227 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 VzQ+mJJa2(evQkQtHR8sB{{#4B_QU`H literal 0 HcmV?d00001 diff --git a/TheOtherRoles/Resources/Plus_Button.png b/TheOtherRoles/Resources/Plus_Button.png new file mode 100644 index 0000000000000000000000000000000000000000..35335d5370f2768ac1f02008f61c86dd10b7221c GIT binary patch literal 7047 zcmeHMc|4SB`yYD=C6OY==v3606*JRVYHVZ4T4brl%rgwLH8U7XNVHId5J^DVm5Jj?$g$wxK8+^GgkOluPt=e@20+D+j=H?-Ap@cxWJPw_~1fYUYE&v6D3_1iN z?0)6J3SM2P^sz&~LRxykp&?N|zhd8&X>aF;iBIlt8O_9xxmWjE8p=q%w5@KUd>(UL z6xE+fh38%HXH{B7*sg~M5&dgTKlbXS^Ubt#-fx_Itc~kKEUljY@Tu&b*5~0uV+r^B z<1J&-FY6?aBq2PjhDK6z!}CTr+6q0cM*<4bxWsgMU>t3Rf|4( z_@$?|CT>JWb=)T`MGUnH{B3s%Tl14rZUZq2?x3$a5gllFTY!pnTPdS^K z`zg>axtAuFdO_@CD_5MzQ;_z}Z+FwPwcRYeQD5|O$=Lm8XQZ9RGIe`Qx8`2c0;m%86P^*cTD!`_UfMC$OxtcZrr8SBJE}R=+c4qnm^lKF}dqYwT&-L(e3R94n?= z?sE4G)p0DXro9X(;dFEJeT>Gcff06%hWD)sRcgtaf=8v-@~j48-W?bjDY`3FDLDFO z@0j2g zlS>ZYgD)woQ>!PfZ0Zv_*b%J^&jlt?R(NN}vt0)CddmvS>Y^DRY+AiKVQ+;d(*aYb zaz7{KGD>WAt*Yoh>DKu8k8+txFKOp{uPcH%P@f&$F0tV@IkJ7$?OnGIuEMx*Bgx)U zMaAcsiks?wa-Y~+*wOE@;kR+3TiKQBdsbQG5}io%d*04R0sDlFUW*T#Kddb`3v`=U z)>ATAa50!!l)5AGz35s;w}>NE-wm70d*!UUd+gT~GZRMm`wt)U&Zh;$fAl(yb^1dZ zk(cH09BJ_oGNhfnz_54fb@Ok}(T_G%_-ii86+}|@Asc_%$_&WJLNrtTi!+rkf1u=j zIB&Wn@|V!CW970V(jQ;hW@ofGB`kkW7KI($dNzXDVYKgRI{b=b$No~x06}sz39iUk zUKVD+=xi5#EWUTW;Dm&#W=U?r`s1to$tP|Cq08%=m^B6ETu&RDcNaLF`DahsktkVw zU#H$3ru_4=ODpT?{u*{TT+Q!K^*BD-rBPKePab8ACMrm&mfjf{q?V;L$Se)c`)yQb zQP!t@whP)kD?|Jj%kG!x_ZKPGs$9RaP@_@%^u{IA&1;TfMDcPzA+=XLaM6}}-6^UF zgcRP0X@VeJ;&Oc+c{u6P0i{9<{EO5i@WV)n*f+qOheOoXq4 z#XSR|AMVuYX7*&=Dk&XG?lsO)x;)VT>T+L=X0}VKa{eh{ z)P`3@=LVyX|L*I9(ArGEc_)zCZw@^%7+L!9aZ}}|O2VUUqwAV9j;>o1u+l@-+|%k# z?vj0W1O8|BS92|`B(|kK6S)OjBwHPB!6@$(nPnZJFWKOb|7&mHgmQ>0wY>$~f~t%S zOJv!*<+yRz>5Eophs!-w{wSynwls_jlG4e$e|f)XSmX|;Ug(husIpzRfb*~cS(Nie z>sG4tgl1^}I@yryOUvFV2e09N=eb zk5lxdSul6$Vomd3rARGZn_FOfPZ{K$$zI0pJLir9EHc8Xyl7@9(;uGjq zv#sh}^(|#;IvbXpmQavdA-yF_!@9?FMI5H6Cwd3=`KRYv7aSjbI*e*YU(Il zxyFY5ETmP(iEy3bWeaH2_{$}lNQEICyZsbf>V@}a$Im3Lv z%+3k3C0!~>amKLlTUOeEdsjMODx3$1!}>=3&bae$_j&|Q{=W9;wH{H~!kEM3hq49? zcaB|_wOOGM6LBE*?8D63Sc?gD;M{Sm=9ls>Hjz+Gt5y6wr5_wjTTUL2g6$10HtM!C zN-V&reR`1bV1?h>2gYs#U3!DVsVgd#&_5~N;)h>&X7O`he41zk7>YKV=&^)soqm(yl!MF zhz_+}v^U=HEa=%w2fG)wj+(xfWF6DBT86_hElJ)PK9PgoT#x=t9o>`Jx=P9kacb#a z$5j>6gekbvqLo97R%EE&_R4wBplS0P)6%NHeaX)yA*Kt zxXr!D2hC=~f2cf9xlt)$l^A*~Z!>SYKT1^Ng)u|DUL$kHd@6wzr69G}CwE8Xt1)WN zxl@o&e*LzAlQ#`_{g(PRz;%(fL|soqYmjT|`{W8)&Ugl1sl9G)Kng~#I& zNHhYChJy$=Ka?$?2;pqLu9#w$!vf$_c?_Sb1H&*B7_t!0%d?iuvp(& z@CBB^-~9ch1>X(44@S5Ed`=LL3RngMY=Q24r(9+bf4t^^AXHAw7~Y+(Wu$4j7bs0$! z6+qJP0N&UaO`*=CaO5$-N}w?3M?NqV2$xO zJR0YVnxPV}17VXB*#w3*Kz{3SVp0Tj4vz(v7lTdXgz&!&xiMIPtAHYw4P}hNBaLt< zoG}W6H^ySVfwloWKA7fWRus|zjh-2yQ3+NcCk0G528-ecAh>M5nE~;_5Wwkx(o)3L z0W#0@gR>!!cmPGf;kj`*OcR(mTBw+Erl_FAxpE=cbEq?hGhl!wuDH3H+f4C8%ruFJ zzXSh?$v*~AlsaCm`^JV)OEfGYTVp1%TrWpV*`JHCJyO8yU%`ZpYL zHeqc*TMjRDo_|+h_iSr+OJXu+q=G_cwgCc#Ix9b)5)8~{E2!h_5Y?Z;_5;BEcCOgI z^fUerBMy(NF%(j@yr5$W#@C~f)EN1*yIQ1 z0JzRzam}nVblqJ3>3xq6@dw1og+ycFNFz8J?S?WU7#b1K#<1Ddh7u9t9rnv?iQ+xT z!GSPe52ARFB9O(2=o-Z3G8q8xd!4@I&HsX%XFr#t|H*tlY}T5@;f8`$>@RQ(VgIfB zzW~lM*fXdAo6q^X(C0&DWtn#nfichafd?FTULw98m~&YoE~NkB&s_HX7d?Q~Kb-s{ zeSgUHL#}_Mz&`^2$gUr9{UZhb5%@=T{lCd2|Lu_*V1xI4A>adbrhEKL@L^om*Us7k z@>%?wTUVG0_AKPuZ0AEDO3mVzL>s4w26oB`$PSir!?J2hI1R6~^m6bcml3kXCO7cA zlqaY_mEG#m<1dsJE3o|Xw%P2~H(+^|EGzW;HP%GuLuF$Sko6L1T3NLVN< z`8Ml{8ZpII`My$j-c=m#_+}Cp&%ayJfvFcA5Df~Y+I*JYIp<6?%X9~+<0-v8m$BzVe*&m$$&PdKlfl)7rya+ z(OrMhbnC^$uJ-mL_Axas(#w0lT(d0Gr^+}Xi{4LUjgG!vO{lr{xj67qz@7o0kE~}M z{J>8WU9x@MPe-d;C)c))Tjvn>WDhf!Sw<=LropcEr?;*$Me`1|*4Q1ot);1Tp?o}# zYDA1k4!qwf^VuggPH)X3*BkZRD@HX9zkhIk-51_(i)xhl_Xvn*fM;o`&zDmmJn+ZX)fQTWtL=!@W32(YAo)(eKT3c1BtI*VR>= xXt?E4^;mvO0qgC}hFU9=GNqP$Ub&1&#kkyi;6d->E-(`yWXmlU=Qi((`5y=?{+aEcyl)V(f7*b-)Vwf3gc0-m>$x_YAFk{Bd$c*)*l%+b7RwPkmYmv&5 zrDRE35M|$@&2mVJ%JUmkdY|*2=RKd#`~26;Z5kiz$eK zKp=6Pt)&a_>jivVMV15KGR1q2fIy-VA#Uy*7kn_7$zo8bG%}dOW0JvSE|mfTaoh9H zQ1=eNB@WC@u44ZdC?KGG=xr!_KlW=_(z`szTHLKE?M<1t(|liF()X|kfN;8uqnyiUzfiLw2q$%ToD z_W~!*sO*=SoI04%e+MyX|LJOX1970Vxtc*a`sKr_?zYhIqA^dku`!Q(ogS0z-VXA| zhdM5edK#s1ui+=SdmFqS?BKReZt@x$@0J)Gcx5y2ru-vFyJ3TA*i^&g!gu9`)vGAZ z52Qk3d{V8%*T3gtn0B)hTE5zWmVy-$WyjjbuggU~@_h8V1g)Wb4!z$8ULhSk`vFz} z6ZY^gEt~E*JsEX6D0<|8Hc{=?SeXzwqU(#TqhQaKKhlo)|7M15q|vS_k0@M-t@zaX zA%nY2iMMrQv-n7$B1jf->*`t+v)($BxW`|gNVSB%^X+w&uMm#)7+aLF7x?Rqk$aL6HITZYE! z#vDJ78y+`EgcmIjcV6=_k4?x(^19)!d2zc&{$a1z-n{fyIS<-pT!TY$QIlV7c*vV3 zc3VKfsa-Z}11n|KB$f0ZWH&o`Q7>ugI;HA2wRgB(~kFSu&vb%?TF zqzyF1c4WlwRQil`SL_psd-N8y0;LO? zDHk=0AFmhIDp2p`)g>5MFDsPta9_WHaj8FF_gD$0RfO(l8%Aw3zM)a8aK@qWns(E+ z*;l;N`PZ)Qq34D+7UoHZ`yaY##@g0qT@7jqy`nXHqaCrlD=T#xnaFMLhsPG$7-yAg zkP$dFQpjF*#k=Xt$+P4Nr>;)eZ1|g@uXnY_N{S`FxG3AubGB|j{4p-^vWtC57^qOy z{;XU!r#|VN-KhW2lL;C0cC#?4!y)hWTO`Xw=1?u2YoVw1`HRS?Wv$k_ZS!=Kjenv3 z=RNcaizAf@@fG50HWq1<2`5$T&PC}uSlWtjx2W3sJc%~)e%tHv(7qQk;_^-FsN0Zp z*+Cnq4Cnhu0wVlccb$LgyRzB@kdz)~yQ7#<@c53G!Gfu8wY8>Xl5OK;FSg0*cRytB zR$qng@{*Ow%e+w_T3MJ%1hZy}Ci0!qHg*-lOtc>d)$Fu$KIN}>U-HiC@uTNvIv}bw zi0!o}&ev0#Iq4Lgr12sDKF^r6i-sm{Wl2RFS0<}x?Gh9|^4tUUk;x`1UQjO2S>GCmoCVH@xX0TeHu*H&omsm{3P1*xj{BVpquP8xo;wjiPZGw8(Oz_JZjj+nYPhWoT&X6!5lpAFnLyc7PxVfsL` z$;-k~(JW(olC75S+IMN-51 z*RQ;D#l4?u9Ilu+8pi`$m>Z?v6xS;`&$Bgx+3_Ol{R5+d;uJg|iVZ7?ct3t2Cp0Xs+V)!qT~WZkQ?907 z#CJKC&U5Lm-mKwrN=^EL$aAUW`VIkOO(lEN0tS^fmf$h28s=BIFVJ({p-$-@w6@+| z5zo!Lz-()0(7W`b;gY$ncgkAzvukg+PJ4`X>q|XLwY3^tN#ba+30cUKa*xyH>pj&+ zEY@u>tCr#zs#aYV)s6E%xon?eK6_cGi{gp63DZnaV;*%>(2f?P20t#QoRBLo(fr)P zxQ1**drwJY+}v5GB2xGOzyBdTOpLVRlA6cOxU*J{=+7*F8PJpNpF~CTydMS0IP#g0x%ImO* z-Gp$Nz>3P*SKHSqUI~z|j&ZkMUpOLiEUNx1_)A3gVCN2(QO%17*AEh-9_sv|2adT0Kl87~# z9qH3#AGjXhvH#KriILHsl$rG<$)WC#;#k@FL(AHF%jt9PPx7v{>3$A>r+PgrI(EmX z{ibtad6`!&sG>ZtO_^pBQ8|XA+V?Jns85b8_E@J34F0UaQ+m^s}gSS0ct+J<_?MF#OeRx?=Zo(L70W z)mK}5olG?(nh7>;kT@Z<{B_sN>|y8&*<;Rec}YRXj#{hQe^n8)3auF@)OUhF0?||p z3ul~##kZ3faQ;dOKW1!uZ}YnQyIqSQ+l1p|Fr;ipxoX(Di*Z(0o!iCo4NiN_sG_L0 zE?(<|+M@QW#BUQ`4B<1)9$X&&O3q!JYp|XI@X@| zJ9+G|)%`7dtyY#@5d8A0YE_hs?iANgRs8XvL6R!F#~?-xvz=)fp7m!|j>lFPZb57J z2lc;pupcgST1T+LshVt1)_WCI{fE~n?}z~}rhD%hRkhRFn4v~5#F%RSRq zA7uv<>x%@ekMfez%~_v&BgacUbxjeY8bTSHXXELSD+Tv?pWjt9GDhquNCSQK>9zBp zY0%pk1WtI~|qxgIteK|K6aqrx$l>W&N?wsOT_du4xA2!LsmMo7-|aJc%k< zlAF(#c$5f8NbEbdjC_SxR$5qeEAlaFW>z4rk5wF{NIV1rNv2SNqp`cg76T%Ku7f8r z2xJ{Doe3PBK_DYjE)!4mCv(6AvJW-D7&2B`0RdA<#*iI)4sZvi1=*Ks8^R*HhB&$r zL;Q(Y62#O*%!q3M5YWjSJeW(T1+Wdc#*lel1K>Zu8wLT-Lpc7%5O)V>umyuf2BUOP zI&i2Jm%0}TF%bhBu}BmH7fb6!3ZP{S@#S!s1~6D~aIj9Wt`39c14Cf3SQs1$Ln5I7 z0?OtEaPVAc09%buvA|(TW)oRdCWp!h0P{KV1V#|Y7y<#t!HeX;DA9@wbS^sJx992E z91;u%G*G~N000aLhhv~{Bov8-Ewu+m9UQ(}2e21a1oVV)@k|&(2M(jtf3je6toHu! z_m>uIH{b~c=0avOf>=bd)n0M{M{TK7CM}4))MpTz%(4HLzq5gZNHymc|f%yapr&kxDX{|BE6bFc>(70!3g+cqj^qhC>NNI0~wV z#v*h{x+oMG0bfFe3t)5b0YoyN3Lw{^0z71-9sx@tV4x%d770b^k}*&WmVkv~CtJB!j5CHHbr#~A?{TK#ITQwq4wM%)fW!!9{{XvD>10<9o-Z2$jljb7F$g$D z4}nFakUv1%$t*UI=6qHJTnD)@LLwSi1Dtpu-KccD4;jV`@R>K^FN^^&9Y9(zF1{N$bp2J|dF&H#s2tPJ3pK`vaz($MZVz7lloHv{YlS%xFTdX;Aybo-?X$1Q_ z@V_y+`Z9t8{%<@#px;@{Se#%6%ioFRMDQaMIe*XdSK#kVF2HWb=CF9U|1hclfiqf2 zSX;oB!Qw6P?@A6_Xf13>H0r!mVDS7lV1Oqs$j`>_C6nft0MKz^i0F$C@F4^H?P9Tg z>!26 z{a*kV7`9M}XoTZr? zw|%6u!e17@w(`&6@aE4GG+K1jsf={)v$%_3yY%DWsQo$+k)u(i;FzwiH_mfHb0BnJ z;OkAB%($_`!~~u<+Uqn50(0G`yA`>1=K6Mr$5`9 ztI>%wEfkxqh=7-DCrir~=??=ltrpT-?JV!1N=odaJ6>}}E*stgvtmL+ea3QRaQlpB zT(%}@9_;&M*kK5>vb^En{&q4|B13(sfcMoQOV+44{?dr0-4r5JcVx{|pSw(J zo2?3o+~%!GIzqzwx}lxpQXmP8z_+XSH_YPoyKJv010srkEwqo;QvN3w>Uk-K;8Fp&&5 zy*2b$i802Z>*b$j<*<@YPa4rbtT+C zx*oTaNfDUy*0QqM9{0#OvJkdL`+FA?<{imS$aYLokQnWj6YIole{)jO= zW}-ZIos{|4)0`9HzPO&_ebIble}1l;`^~(Zf|rHRj3P-~nSA~i50efT2xPBRo^eiO zDPty_lXFdEg8AAozDO(zd=?m1X2NF Y37+gH_Usrv#tM*!Bjycy@bFQ^_?FdcvC-^uNI7moH_{vK1+K6ux#774U z192p3wunYTda&uOYv`&C^q_NsJ6gi*pmeTYPEb0iC(IHF$#ZV%wShZhD`DuDY8)P9 zF;2{5s>6=Ka|3A|24?N;tDd}vT3rqddQ-Fk$>~2X&$WN7Y78iI&}zppD}JIi_pN?K zTO;MabLUX^<;!{2=v&*b$InR@+&WK^Z+veb_wL@!Uw^F#{HA@$z95wJmPXa5P4i~T z?{0K-Dn60W$dBAWZo{T;)}_+~e^P*p8W-0|Bt&>*_w=$B??&gOVq~#F*uatY$02c< z2#O=XwrI`1-PmdbDdXnbfSzBt?a&XqmU>@Ngm^E?t$QP<`<}NGcIurfY;brv;=tIZQ63et^^VV&=v3VHr+iCaR zGoM*zV>^#$gseQ~mb2?GDY=Q?t9w!(&NWu2E;`OGx+QLVk{{HCevFbg>2LheGcy=E zoscA=Naph9z5q9eavv`f{3Np#E8M720>>xN(VX^&k_j~-o==(pad}H7%QIdB2W6UO zYU|$Uz!@D|mgq9%EHEZkTxL=l3Qs)^4QGu&!nS9&1W+DUB#bH#xmN2cR7LSC zIfcfD6Usb}#vg3!Igo_kbJkhZQ>7^@xm860cY`X0Gh+^^cNbW18Iv@<)aL@Dx1Zwlt# zwdMHVmHJ%=&f6MR7D~Ks>=H3g;kO*(yO`0=xiKJrK0Er%gg$0=4(?Qa>Xn`$tC3S_ zV^vARg4L>5Il!qqwHfArbu|;(7d)z1%p3fzqPqZ8$Gi!JD=EyU+Rd;xk(O?~AeZAc zmMGA-F>`q#x|p7GT15tvWo{*$u9NwsxRfu8KV=Q#PT$CW!r!LpAiS;kp-JIc6v=>} zy?slve!o<~n}sd<-2&@RWRTAc_nu*?IWY6wOywoY^!em7oHl%X+2FQDHnzpyPdc+} zBgo|1*3@4_+B-nJ!|a!Tut22}^z5~y8w*NGBF;wi<$68pU@8D3I$GmTB z?0eHL1dBws8H^GD&42w`_UTC{(^JThn<7uJFXeMT;ZFs(#NsO2lyr-Fk1f9QHxmzpQ*Dq~K29C<|BRwxUMrL2|;F0qsjZ zTlHRYjD%_#-M2--G`0~({24pfc6e-L=tp@ncqNAq8x5D$l-}1m;m$sGA-bKVTYMSi^zjU&2o$@|AOJ9i3}`h6wi}5C0YJ3D z!|}-Nr=Wf5!k86S+3wS=cqwbnt36r2l23*z1L_)Khe-4u^n#yuu&Z7}P*;g|9HizS zK9L(tULMtB;#B=IC(V9DKZbd>k&A-m-ow|!nnNsmZ!N)bn4^>6K&1#Lp zv|WnEU$A7(9A=h&`F^EWv?KD)Z>r=Jklk;XGHbx{O+MN3P_hQa*4^ z^T-Hu;%nBbXt(ptvCd5HgHj6?L61_|3_XdkmHg6Cu5!drYwy|*)o;t+AGYc|-aRZk zSngYR_hC^=D(01&@7LmoMs3pWu5jFmuYp`AjP)_~-KIU-Rf#&mwPGx@Zo1x; z0i?3V7TZiq;SR1Rqa5G9Ag_L?!!CbgOFW%Rm$66vOd=`vQ&Ju(iQXjZP@yDQB%nen z@ls3{vuQ{9nU}TrYjqB7-J`>o44SEKLv%bpyjOH)H4b5C%d zEdkjh+1*He83B~+%gR}+6i=BF6zRiBV~{Cf&tD42YqTrG8esm=eArYHfe%vj+87VI z_X)4;D;_u5JYfd2isEn}F6(G>%#yfrqS6co8wp$y43PwXTc*HPZM(HWH)FZM_P|et zVhpi@t)$40w#Z#k(5aR6a+a0I8lq^BLkUZBsketh;;Iwa(1kKIn@9R6`KM46P}|$h z9W|dTiD{7&yC_$NT2q*52(v^Ncy)}#$LS5?#WnWjW348J zJrWFYlW6@Wm85fBMDrCM%8VSkqoqlGa|PIwx`F~Qo^ulqtVsAhaI1q;BSlk6Mo>QW zc5iQ70Hxk&vqSucpd=Zkg>Z)x#T}}$6{uox2b@_vbo=>x7Lghv=}gWTX6nK*lsq^6 zuMgR8Wt+)F+#VA51Sd55^ zccUB?peAiSgbx&`-vYBQps=zvqd3^|{-;7xBUlG%y>S9Td)%`$&VC%L zz_5rORh9^?hqGR&U#u|leMC?{^V01kWj4yks_1MkYz%E>vvM$w^}pQ1SjKT4PiCN4 zR-0B9mX1a>xoSwrnVFN1rcWrK-i4DXb~27VFM5DJkF>8PIA{b{d@?ORu z`ulH-gAt)cCkz0;7A0*%Q#PL2!od!uS6q2^L9bJ3_#EQPuQwUoi4-h)Sy`o z=7a2cp^I{xH%)k76J0T}15SG`I*OC3Ga&3)*Q%}`_GN{fh9v|`q?f(f>qz@fGSnp> z?LT5VW#g0TE-gV1sAx6aBn!v6r0Y~z`#d3=)DgzMJo5aNZyt6P zm#RK67soM$XWQc-&NCng*W8()omP#ZhcIai`DGXl&D;vlF?n zF%$+L!o?r;m-1B=>8}xKg3|b5Am+Kkn2l?!&bL~aW9Gp-w!w{CX?SJoH4j5=U#4YG zrAnB6rU(eoPD>@Sp6Li(=fzPnQC6(PQ^q6=V2TupEZ~G6ysp|CP(pue7fyUO`Pj~O zkVR}4)s&n}8dq6-e;=im^F;}lN@TwDPHaB^4N5RXnJ)6W`_&Y1<6aq?!HQA zg{AC;wuY20Vk@?DYS(#vuLK4PaNt-`1|*Z?eJROU$!TEU>zmU#7m*gJiy2OSSFR)R z0o3ZNe!MqFuiiN4RhBV(k1z6IVUlwRREIkbVMIF!BUkR~mf&PpsCY_6?X{3NzG#BVP^5@}LMWpO=+zkcTR{HAZ$3{9Mt zb;Q_dC;Tanz2>)@C4a_FcF68JT3eRc;;v zwgOG-5@SbJ4HWy>3_9(CNi+1$* zqY>z%b`WJcUX3jX344urTzn=!(8t zexzsEtN@?Yhi6AyJXN>^>xE>5zr~&Gs&ht;A>!BNw6RG-3PN!n>lwmh=kG_NhGHrb zFpahN%658H2LZzgR8SPeC4%<7CQNeJ3$uY7RgXXZIjCL+e44U|IUOinIlo#p_AqL9vd* zh8+1+O1X|S)CEJJuVjChyj-O9?oeG0butZk8g>~s>psB&W?KyvSWb&t>B)5Pm#VyP zCrSz^n~8P+FW=@ZhR_BrjsW6E0cx|6WO?aYg2dGoW!7)>)H0DM*O#@YAh>-&@tE0F z^`3=Fg`FjDaxRuIDf3VSa#-=(;@ zvXuXeE2CBMqXK_G?x>cyvclqVwUBAWwy}>kj;+;j%Ur9YeOAXotHXZDB3%vZOVz;A zA_ZR2es7V0sw84PqJAlMNp@PVrzoVdW0M=eg`t5wZ3{vJE!*yXEYM&Ll8|yf)4HrZ zSQGE6?`idi<4r7KGAq?{tPWZHzO!VC&yQ>qb`|IM&9dL1#paDa4buXY&XK~1zzp7F zxwj{1#T!jIbeI~_li10789dn2*m}uUB%37N!*n*JJJ+D+ka@FNdP_)uXMuRw5zO)Q z!R)2yz3oOBO&*_FEz%C5gVwP=i^v)3D;_I!fJzENU5B z3r?e^AVDxYT+>5kY@?lWYiig`XTN8>cr`mvD@evkLQ{@cKHy?16C<#^!Z{MsA9(r! zRY@_T!T5wEHMO4?%_%HH4DTvv=4|8nnQ_cK-WuTO-J;%()mA9K5fPBVHH8jeUJC^@ znvg}Yf`Pl0JDMKp7KueK|9Y&+5LwELAFpRQ9hItz*FtZQtmiX1dK??UJhsizG+GEsTYQ&L#Pd8(-|-BFXidIdX8F8tz#O#Ku-o|yl*|l zI=D`j6w|%$AIAg|ofJ6UBtthmjG`S=5#rY$MAvWw=<;$C`Oc%K_2hC4j5^ z2tln;TbCDjr_UJeI4LaEq#+_XRD$toKO?yZVD&>RrqQi;mDShXwD3TZ$CRAUdwkzN ztME(Am%-I$fqvJ5D3fRzUq=+{_ur0=)doqxCH3 zLL2Cz%$eiH!jpTYEYr$w1Wj|pGbxU^K2t4I(Z6tUlZz7NcIIeU4j!HM=9C%3=cz=y z6iRV0$;<<(a-0fYkq->U5HD3@CF+FVx5ur*UX_b^3+W|oiDa)N`L;?x#)1`mXj-eg zyg~0Ev+-?|QoFhDtydq5n(2kB>4~hMesD^n%aRy9qXovIN?GLrdu9d21KnNWH{le;2>Wf96AW`DkQB$qZ;T62UkuNTWWi z81v9Xb5l8V52;fqV5rMN_yEtsnwMXW5V=&6L#!sPXxibO+4DzUyA>10DF$KB(E7KJ zyy2@kyBa+ zK4DkzLzdnFU9z80-o=~dEcj@+rXIOj0_aPn8cLj>IC{=}^CD4JC#NtIw|`&?^WSNW zr?S9c3X*6f{`ygzh^X3hgn40$C;<3qTXaUzC|C?$97Rj+f<9zh71=Tj{M~-8Z9|K{Y}WrV|Q!?_k=xKH zR3idM;tlUtACET?lbGH6Gi)eU>?{ki79W9JOJQJ5Z(lmM8YL-FM|N2$s$lIOX_v1oi907Ug z6qMKug_2^0^&`TGIt0e%H4@L@H0p_{8eV>Ek-?5Ni|iu}S})LvZg>{n9gw}J&`?G~ zLJon+$!RLf$^Gpwf5adC8PDS-mAa&SWzKR0S^;sx;?tDgdb_ zSM-7~C2bQb^qHVw+DJXj2Ue!kCg?IpsNJ)j%3}&oZ_Kr=3D-KEA1^KXEWAT~@hqam za-KZfCOJMzp-0tS0k16=^>(Y1B#21h)YFC@f8^Nh9b9E7W}5~TNI{uiij_uaE2ulxB*oNk&BV!YIgOq%GlP+UFfxQJ**KLH|CMKg zXn8Tk2JXWY(qZF_DeVxUgMM@JA=n^$I1lUF81`P2G#!yWa0Cu$J20|P` zP;O6qC&V)vBqWK)o=!lpE!33`1hs-WNHQF@zGt9=K_nRrgw%M|oaCU^FePs|RL5Ig z7wl~d7K1Q6mco(n1Rx0Pp{_tWPkTED7l5ZE!!KR{;`h&H9tOH!5La7C217MXIypx; zlunRakeioF!4u}r&me_ECjp080<`57|D-^iNitZwx;g=Pcsx8jxIF~89pP3yd}3l^ zJiPoo{QO)91ec4KgDcRJ%fW^5C&eEe@=zBr9OmQ-b9A8l$q58Gy17a+Fd+Kr{v=2A zf)zXwjek1$dH##u#TCM%j5rZQj0XYW;pgQQ<>KY%;uquj-5$}aruKJh2bVupMCi%m z33TG&reBKm7eu3m09)qc9$AsEeZ;91K-(hdQ`2{_fPt&dud_pKdPD zpH08wwu4ymAcFek`FEQq%4(W_xA`ff70ll0m&H%????#v?>HwnxZN)d1k3}qgW4kk zaX~Qi{RQs|v;4;d{bf8qd;VJ>2zP(y{|owWdi{#!m#zSLN3h#Zr^@n@3_s%qKpep^ z2;kRGUQvF20X~p07t|6W$R!Bn195>wAwVu5zZjU0Prwo)AO!l2O4-5173ctl{-i>X zbHflk2&A9@ScI1g3I;;C1V#A;xqv_rFPEs0s2Efb2nO+jgny&ZfWr`#0JQr(s-IL4 z1Qj9(Fi=>GmrF>H55gq~5k*k(f`z$+ARsXzkeCRR7s&sM>gPNFWHgl}8Th$*|Iwmp z2XwV`gxe#^3+4cE^l%pBS&OD4&=xA1|MX;BSojP`C>s&404; z@pAM3(E|Yk6cL<2M7qK3fmTo+CkLxvygw%ffEW%!THw#>KrsJmM~ns_2ZsV(9pSo; zj&_m^Kcl7lN%^a&=p_Cu7l5iG_?O`?Fck8$;{L2TS)diquTu%0e+B+;Ogh$%9uEI+ zJpX|Hoka%j>fs2t)qrb&Y@lG*f6epHz<+1bMyz%&u5d5q|6x%72TtOTgjGV=I>Nnv z^REMS{^RVACCLuwHU7oa;7@@onZI{wiGwgx&_K@scipT+jKcGy2j zWJ@toUVbq?F)pYm1d+%BU_>o|`1rXXLQq8RK|oM2zrbJFT^uc4J%Dhij1?jW5c7;E zu3z&^$M$FbvHunCVGaG6T)c=9;T7iM=hx*E1_%iQ1cVv>m~A=|jv%Uv>Ih-uz#1zuEtpqyLln@323t)%q~-va+9yZ$5Bzoo#x1^!QV{eP1S=O4G+PzS_yp9kWG`oP|l z9C0&_0aAG)kM!f`JNsQxBBBM`Ny*3s35lTV=Lfmhu^57A#B^0wQ^4H9AR`cE+W*!X zg@lAlrYtX`>p8cd>7|*pN!|N+RSMp}<`%MqY5!@j6g2OpVOo^A3chju_lJ>WyI7o7=pC2HP9j90NO}H=H zT5ttS-mM-YDTGPV@gv)gd+a|amv*gjP_PonE=9FelEzt{_QGq5lCD8h#YAzXL9Ilh z(;64Ut8`rkI8iroQ#TQqq!5^3Rz^ylsoM!tzIsJk*U%8NP%~!D^vFgcFfb4mb7Aec zO8hx%>x754j*h@;h1=fe>T;F&j+Q$7zM6#}9bbHR-Nw)Nrf+3iZES2jclZ6_`_F`o zg&lXzmy1T$y@5}jJgIfQy_Hvtp{%Z|n11BGca?N<0!P@eFhiw|U5KvkdFoFSBB7w5 zTD8dhD=fsI?+!<iaaku8Lc z!=p1hb{*UJa~D{zPcc`P8SAA zy!Si7H4q?fL^H)BWRL5KG)WM1#}1o^_%GdnD-U+2&7 zGqCfemFN`WKl(lzr#X|nL@<@M8veP|OV3lKRTY9;bc+YF4=dq5i@9FiT%J%uoiiS`PscS4-J?0H) zK~hR`LWV?aeiBLh@KFB1vz~g}X`02Z&bI_kmvDMuPENT=b^7=?5rGwTZcQ!~J@(!0 zlDNdx(MUve#8jYxzTQz*?Bne6auPxO;Ld=GlK8H$d+@yrtqcN8hsIVerZXfHVRAMO zuAI`EpbsBZnid@v97GbB0x_ba@!QTm4&JO*bE&r3uJ*aJ3%S)#PER*%oa{@7Fs-<^ z!e9;`*N*Fq&hUe>I0voMLnpT7^`?YBXY!M#4E8sFvvQ-A0`K5~K%f?`H-OgrY7=dA z;*SCR$&|0w2;<}PD-8=;Nt6=|8{HXC@lSR2buSDpttXUU{lb9DVx+zN~pgej5#N3UH2JOC=YHuqmsLETDv-)~>A?wLruO3FG?~ zed)jH9IR+gi$JWPD{!&a@`N-2%S2vrps1>wCz6LK)uh?meH3FoJ^iABYRK_DpW915 zAoz2YC1dh2nbiCx_E|F7+j<`fn$ps;;&y#CdCwOIUC09igKUr5=g=?4H#i)yT5A)C zz9x8od31Ao`wrY+qY6{zy9~MXUYW=Ni_O*viuhD$>*?iwB1^ur*yf6@w^#wOsHd+I zD?yFE_N~Z?wy!xTMybGH_A4KHa83FKvT8Q{zAW8~?Dhiyt=|m|2EEJqkdf+Z{%Ea_S{_nP#wr4cW+?>&?lmXC{cvtr zW>#)oM#A{HuFI7w&lW+m3AcI{AS9QK#H1y>v{a({G$ksQ!hh4aX>g<4vKGio-ckn$ zxt}d_RGi)JA5d5?*)CAYIapl!@{^@p0+XN`HL_G2b+@LwT38K~1HaR5o|QAy&enlW zm^qZ|D5k)k0t!m%pKPxaq9v!GsK=jiypG&Is3ldP7iA+?(2DC`o}bUYcGBr!6aKO1 zEKwS^P}@whzUd--h<9+NhDML3UX)1OTWeGgQp=KBTi;If)Qyd0#-&e{v?T!M_40~{ zg%ls?AO$_U40#HqqT{CDC}R8p>^uPFeT>cg4hgEwBd*tpp53aX7W31X=_D;jgbjUUi7KaD!01e(pFT+;^E+&w2;xG>D!WC3!JYu_oy;(?A}KdoRrku zzI(03*eZRPq}?O6$k`93&cv-~HN?F&myRDd>i`Z1Ne3cf?Usk_BhCP7sdq^ciL3%* z4hc(bCwD|O$cgOt>E%<9ypIM^lgLPG0dDwOT4NX2*VpTx5v$Iw5C>-a!1{cf%f3R= z5iB4iBm{_qGa2Zy^|YUK*t}2f)boDk+*{c?y_}E`^qaLQj-gaB;%}a~vbNwy%0znl z9GfFqJr0c?;tjcM8N>bb(-JGm6hIG7r?836H|fJGEsDW;i#t<4}FMOJ~}O=^`^|qwACg?1wUKhsIdU`|2LwIOQvZjzkiwGg%4p%s{4F&i+kAsbJstz$qe<;{s?Yf#a%0*4D; zW3_ttD|+wHxfB5{-#7K*+HL~Z*t9z6R!F^n)E zD=TY6$iFA{g;)fYxL;D~zyS9F$ZWig`!y*`VuYLR^a{L%mm<0gn%=d`Tl7B5{K4lX z9ww%w{=~woi@gRapSls`k>LbpFT+k6eKNl_vH(SSPqa>Y`r`eP?D_q#2M-0Xv9T|g zRM#2;a4{N-Bw*tZNYAo;nYZpeL}0;7mhUpX&as>BqbMiCYJ{A(wPogoXX*Btqp%|81< z(3KsreS<$n8JV=|ORgSF;N6S0SP4?NJ=8{QLTp^@GD;|~KVe9_s0es@ZQhr)5Pn^q z%EdU`A|!}|%aa46_{8-|dY;x-^oKo7dMPj>f}W}ATACYib#lvi6n4Pv;D0t_!rAv^ zrUJH;1Zz9GiCi?j(s{dL8n!5M`f2X&#M>ZGDM)#Tuy0)oWbc$rLXuO?>dD_1l zEd5wsr1|oIit2;hJqL>MsFGkE4-e-BlA|qafr0|&j3F@@n;jblMh491B`8cxOvfpk z=J_oxExqUR^78j-Xq+b~hi~_<%BS?zzh)f4cZrWS1`pQuJ(luwb2Gl!{UAjQM$S?) zf$7wHsB`Uj;pl5=pe`89=zN`7W&n2Gt+=e|t?jHrM@`*n wS^3hmx%l^p4xCJ__m5DUG8+6|kuLkUazObn`INB`7tKh@3hMHevKB%A1uK=1U;qFB literal 0 HcmV?d00001 diff --git a/TheOtherRoles/Resources/Settings_ButtonActive.png b/TheOtherRoles/Resources/Settings_ButtonActive.png new file mode 100644 index 0000000000000000000000000000000000000000..813c937c11708ea37eb1d8ed0db7aa15fd430917 GIT binary patch literal 13826 zcmeHuWmH?;)-Ddk9ZHb^#VrI4#jUuv6mK8_f;S276fN$qMO&muaV<^@6nBb4u>wU3 zwVU>R&pGe8-*?Y<&lvaqJITmS_FB(;=3LL3bFIC{9*NS^xlcmKK!|~XL87jvY=HhY zM}G_n@X$xfmdCLe7`MZHj2@v3Af5m>gsUyw2?juUyTJf3FSsoRhSy?Mwp|+Ay@arz zjx_Z+-t~+NNL&2+Z`X<%+yw;|9n~%cB2<~ctJMS}haVq)3SP*48y5H8RvM}8E5F{1 z7HT|WJ@{O9Z2bItz)GNE*Ky|V0dMm`_;!+C=a2l&i*HA*EfQ|4y!mgGl&<%C8g}pW zTOIADhgZkt#0;H5%t=yTvf7Q4AjR~?8&LdU8J)t>&jJ2FGlBy(EN_;b9g!E8H-Y)D zRtd~evpRz?%c^=i44og|+q^7nHOp%X^>X;T-eSk%l*DIaIyVrb{TaS1T_MZN(+)#( zXP&UZeVV%b!+I;d@67QJ#)%?q5wk zhG~~_A8y>a7JkcVyzF-Sr~C3jv%@DF;kFNTZ3moFr$A{qN$l;@=A`tW8@o12j_N~q z*avb3`(xf}kb{U%bK1|SNhu_9Bng+goAT%6HjE?id&QBqArG3Sl!AfG}(_c+`+9Ug4|k-HGyTb&^9l=YO*7+0}8nNZ$ByvEqnTT}k+bho%Ab zhzU=`{4QY@g)xEGK2VXVcT>bM+k(R<8A-_LDWx$IE9P{EhTN)Rz{QNnCw8cK=h4FH z%=G|E^POok<*T7ZKt4cVs=QsMU*emi5Ai0?78z3T(O4=ehD$^SK>*xl_^d$ZO&{O9^n;IB+yR?8K1ThRp6~GR>n#`qVz1M zAV-}3)FMbReOP1@?y8v+{5UHz!2$U?OdevO&@ffzdC`Xd`@5>rz7sY_2WWZed5iO*T|sd<9ca`-<(! zL``N|BdUE&iNY&&=^D~BDQS@Q!S7Nr7+R{ebu?rFYLknH%5xlBqa*RP`W^I2V5pAm1v7*~nWuV&B0XG=6@vhAOmcV7(Y&q}$rL>0)B zFu2d`XiVdJ=L%P*@4?=0nLdQ%QV&tW3y%6vM9Sw@C&Tt4214gZ zC-Xq<++kMA=8qSlBQwFu8l-I%gUmW#DSv)gjDUB!voZm66L@bI2{2(qj();ZTa?*I zElu5uZ@Wb~eoT$2+G_7M5GKpocGY`3fYzIO(8YQe%pt+ERvD1{CeV(3YUT?3zB}`2 z@!=ff7_mpVFed5|K-zawR`Kr%K=-23F&cdcu> z9OwBjW_9~7RcjjLy+sI`-p|q78bFF&sZ?}=Ff}yA+jPITAK9LroQ8anAHrfOKc^;{ zR8g-}ylAQ|S;d{}>Pz#XGyh3xwr}C~UDK(;v*$t#pNAGXUHZU$^8IDCLI zwj15Axu3u&0*rcs(h@_)pqfm(7^HKeysLwY7^xH7X1918Y(Cv9pAnmAGGz)5xjk)0 zcn&^x!3wLVE9N?3sALiRNNFa97a1am&kea#$d4!1q32%nq(M%h?C}?)NZ5V8x>kgB zr1w~*%^mSKiYYybJEp6^pJ8Vq@hum*4>dHy{HZpNiOvcW^Q6g{>Sbaa$|HRj2O{1) z#9Vvcg5B<;;KfDI1(SQ-YW^+@WUH@BwfoXx@(B?jucG8aY1f)k48YTMU7t|uzC^UQ zDtpcuyhpXt8FH_du#S!}R^F7xtyARQUi5G|hnRY#@Pm%G$pE6EOli^1%*+buZd^&G zPbMr}eU`D|cG|3JBckuJ6vvIlc$j$~G><9WDB3m8Z$jU^>|MS^qhId9Xcf9={q(A? z9?Vcw(R)sMkiHrdlHVt}Y6PVs&C{IQ_b2G@pW=BDWG(k`A^9SX0c9*u3*LzZ6trjc z;^79CSsExra@@QETy*ERdQqRJ>T*+FOGLy=YBy?AM{{4>x7CT6ZS= zpo%+}tJB=}Y?ZJRzR#Lh-y9x#(z!7U;f}n)FhO=L8Hh!{6A6Fg^xpJvmIg1iN`khR zSlwV_HY;VH-XQ6=3EM~=P8V

mCJntpUuK(9&#l&u^q#Bvnk2S9yhx_nq0!BG#>p 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 jbMEF-!8u2apZK*HW^ke$^+NO|E{3{_j&il) taskInfo(GameData.PlayerInfo playerInfo) { + public static Tuple taskInfo(NetworkedPlayerInfo playerInfo) { int TotalTasks = 0; int CompletedTasks = 0; if (!playerInfo.Disconnected && playerInfo.Tasks != null && diff --git a/TheOtherRoles/TheOtherRoles.csproj b/TheOtherRoles/TheOtherRoles.csproj index 9c056c1..500b874 100644 --- a/TheOtherRoles/TheOtherRoles.csproj +++ b/TheOtherRoles/TheOtherRoles.csproj @@ -1,14 +1,15 @@  net6.0 - 4.5.3 + 4.6.0 TheOtherRoles Eisbison latest true true - x86 + AnyCPU true + AnyCPU @@ -16,9 +17,9 @@ - + - + @@ -32,5 +33,5 @@ - + diff --git a/TheOtherRoles/packages.lock.json b/TheOtherRoles/packages.lock.json index 3bcb0fd..e83bd99 100644 --- a/TheOtherRoles/packages.lock.json +++ b/TheOtherRoles/packages.lock.json @@ -4,9 +4,9 @@ "net6.0": { "AmongUs.GameLibs.Steam": { "type": "Direct", - "requested": "[2024.3.5, )", - "resolved": "2024.3.5", - "contentHash": "5KOcBSFnql0DmrL82GikWb5I+KLvD8h8RsS28BKTCws2fPKwWauUm/L9Zh73pIHcnOMCpvnAxWeZh/AqxAEI0Q==" + "requested": "[2024.6.18, )", + "resolved": "2024.6.18", + "contentHash": "xb+HP1xXn4/ES8AgXpk9xeoW568LFYw5NTjY6bHwd6vQjHDf2kT1xHMmQ1me5Vfee/Gp69YIMZDXUAUToG7Elg==" }, "BepInEx.IL2CPP.MSBuild": { "type": "Direct", @@ -16,19 +16,19 @@ }, "BepInEx.Unity.IL2CPP": { "type": "Direct", - "requested": "[6.0.0-be.688, )", - "resolved": "6.0.0-be.688", - "contentHash": "a68J6PA9AdgYlj8HeuEK1hM4Vv+j1x/6TflK5/PaYAd05/TPdjNI09LsSTIOaS9hdWZwCcpPEw/HxLhXaQKodw==", + "requested": "[6.0.0-be.692, )", + "resolved": "6.0.0-be.692", + "contentHash": "rYjnJCN2APD/04dZUKhRCbu30jhzseIKd0+pfaWWgfDJNggd8rq+whEzM/ZC0fEkkLgcIkEgbtX2LuSqsCrIgg==", "dependencies": { - "BepInEx.Core": "6.0.0-be.688", - "BepInEx.Unity.Common": "6.0.0-be.688", + "BepInEx.Core": "6.0.0-be.692", + "BepInEx.Unity.Common": "6.0.0-be.692", "HarmonyX": "2.10.1", - "Iced": "1.18.0", - "Il2CppInterop.Generator": "1.4.6-ci.389", - "Il2CppInterop.HarmonySupport": "1.4.6-ci.389", - "Il2CppInterop.Runtime": "1.4.6-ci.389", + "Iced": "1.20.0", + "Il2CppInterop.Generator": "1.4.6-ci.426", + "Il2CppInterop.HarmonySupport": "1.4.6-ci.426", + "Il2CppInterop.Runtime": "1.4.6-ci.426", "MonoMod.RuntimeDetour": "22.5.1.1", - "Samboy063.Cpp2IL.Core": "2022.1.0-development.866" + "Samboy063.Cpp2IL.Core": "2022.1.0-development.972" } }, "Reactor": { @@ -42,48 +42,56 @@ }, "AsmResolver": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "7PjujQzgQ3wjzqZYT4CY8SV6zx7FY6swIwj3Yk4QLolLqo9sBPb7FLF3W30QFvCsCkNfiVWXlSbeZKs4xGgQOQ==" + "resolved": "5.5.0", + "contentHash": "IuOC+b/D/da4fohYVNwo/NL+8w+q89O9B32bLotegJ+hmFlBEpQEKgtyhxc9ZfEd+x0KzwzD52RISuyLgf/BAA==" }, "AsmResolver.DotNet": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "oKrBU94OaSrbbTwydvpLRFalYxozrNXGkl13nIkm823ZFQtPNYOiOMJb5DQLvkCJlWyis7q865tg803T5zg9IA==", + "resolved": "5.5.0", + "contentHash": "T19DazP0h4qKXwPspTEB0ZzwlftPuvVAd68HhsS8eNYd9lRd/rtppcBmM2QLQdZeGGNJc+QEzLiAW+wrA3nKAw==", "dependencies": { - "AsmResolver.PE": "5.1.0", - "System.Text.Json": "6.0.7" + "AsmResolver.PE": "5.5.0", + "System.Text.Json": "6.0.8" } }, "AsmResolver.PE": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "XD3iAgLSInJIODKUUlpHWXf8MR9H1SFMb1lsmabb4GxxxnDQRPjMxjglv2NOCjQQuNB2Oh0bvjHE7ewf82UqyQ==", + "resolved": "5.5.0", + "contentHash": "bSPe0A+z4iEAabpD9h7ACQ5dkcTirx4fx05P194u/+OIUqlexHf7Zq/PEPdOkesbqHoZmgVmJ1H0GWqqouvZfA==", "dependencies": { - "AsmResolver.PE.File": "5.1.0" + "AsmResolver.PE.File": "5.5.0" } }, "AsmResolver.PE.File": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "YituBYe0Fh471sXQMfu+f4tWKX/JZvRwrX6g/SVsr4XkUIUzYi/aGT3/QvjHoYx19fE1KYOB6zBKqPkkOK7HPw==", + "resolved": "5.5.0", + "contentHash": "XHWHnPQf0e0VEJPTaWMoiajuikF0n0fDXS2XWzEXM+/xcY+LABsp1p7ETlg+bFsDVITQRtNTa/xV8eSDlSZjNQ==", "dependencies": { - "AsmResolver": "5.1.0" + "AsmResolver": "5.5.0" } }, - "AssetRipper.Primitives": { + "AssetRipper.CIL": { "type": "Transitive", - "resolved": "2.1.0", - "contentHash": "nrgwUMa69ztOfMKbmytNI3mU/fuN3eWptq2Mu8aLg2ly394w51KJbUx5z477HbRvabD8TD8rM4wRe6EBqB2xdA==" + "resolved": "1.0.0", + "contentHash": "l639dKLllpJ5TcX+Z1OVYz+hsKVZyMpEm59Dl4Qd6YrCHPVj9yPtF/UNjn9nFxoDm7suEFP/0vlZD/bSeXjz6Q==", + "dependencies": { + "AsmResolver.DotNet": "5.5.0" + } }, - "AssetRipper.VersionUtilities": { + "AssetRipper.Gee.External.Capstone": { "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "knQ/W1YwTZS2AbdaZh+nhyyBEZQPQDcahhSOnh88i+cdg4/FEoFJiCwaavgFvCMNuUeHTbcZc4qttF8WQ8xLIA==" + "resolved": "2.3.1", + "contentHash": "WVujToXc2sB5nVEY4iMpjhsQaqGw5RjG3c0Hl1qT/pZMbfwtafpaOwAa0G/GS5gd2jcuJwrsG880Pkyjkhi3vQ==" + }, + "AssetRipper.Primitives": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "GhFAVMWpG8MyZscQxwYCd1HSS36UhIQeW0Gn9iPYiininZWlo8RTV9tGYv0CNNXw5bODYJtXf4qiqo6sQOVAEg==" }, "BepInEx.Core": { "type": "Transitive", - "resolved": "6.0.0-be.688", - "contentHash": "34VErIttx6PckRukoaRXIb6ZF5oby+9Oktt9E3ZafOGzA0lBWOmY/BKHfg766XZBplE06KijHp+vrryCjfLP7Q==", + "resolved": "6.0.0-be.692", + "contentHash": "I/JuhVhOXUFRrSxy7MqMHpygKoYNt39XY5V8e7PTLbq+nfCBGYzOhszcPx90Wj5xGPx6IFuABJocWAbFDHUddA==", "dependencies": { "HarmonyX": "2.10.1", "MonoMod.Utils": "22.5.1.1", @@ -92,10 +100,10 @@ }, "BepInEx.Unity.Common": { "type": "Transitive", - "resolved": "6.0.0-be.688", - "contentHash": "RRPG9sEgIdGgut0LOSQopW+6mMpE0LDggKZWfLBBeaJj1p0WFrVBCqEAruFLO0FrwT/+dEC7EtV62n13RV1JfA==", + "resolved": "6.0.0-be.692", + "contentHash": "QYsp8Sf4kQwmx4qwFLhS7SW+Dp67iBibb0zo5IdMQXGmS0KY2WH7/SZ+fFmG6ce06BT+E58wsxk0rU6NW0qWbg==", "dependencies": { - "AssetRipper.VersionUtilities": "1.2.1", + "AssetRipper.Primitives": "3.1.1", "MonoMod.Utils": "22.5.1.1" } }, @@ -115,13 +123,13 @@ }, "Iced": { "type": "Transitive", - "resolved": "1.18.0", - "contentHash": "G60lZOvbvqttA+SBSv/yY6/wUboXKJlkffhSNR9iOrx0M5gpcK1TRr6xrFR+qAhqIAhvrsPF1jkn9fYb4lEufQ==" + "resolved": "1.20.0", + "contentHash": "cjk+vNDIatLI5vQ22tOCAzsgowZ32Ob1Fy4ljQSfqfzMe5ATKXU+dMPRE6+cIUUFetoyyWkUJArJoxe3QwDXAA==" }, "Il2CppInterop.Common": { "type": "Transitive", - "resolved": "1.4.6-ci.389", - "contentHash": "KTyf2f66Q2mS9a6xhvoPWwD0VzRIoskPMVQehaVMo1dx4Di1FPFsKqukQ+ob6wgP5Rbnx53kiH0PY+IRSb35XA==", + "resolved": "1.4.6-ci.426", + "contentHash": "pI0kU0qqJKMP6z9eFz4+WJtGIwklfPxW2yWEY9vKnrN9bbYMusK3v5uFWV8al7S/HPSATV5jNWH1hyTvP7kglA==", "dependencies": { "Iced": "1.17.0", "Microsoft.Extensions.Logging.Abstractions": "6.0.1", @@ -130,36 +138,31 @@ }, "Il2CppInterop.Generator": { "type": "Transitive", - "resolved": "1.4.6-ci.389", - "contentHash": "XJX4sn/RzAEBNIZZXuyDcLZrFx5tCoahujDu5aKWjApiMj/nTRQAxZn17daIIvd9aHqLaAfiWWth5QDLIt2bKw==", + "resolved": "1.4.6-ci.426", + "contentHash": "kecdx/vbNl0RtfzfYW592jU8PMyq1JbzpvoJSvqwO+tV6icp9SljXy6yG8bT/wt0gaPj9mFUNDTe2Wu6M6XGZQ==", "dependencies": { - "Il2CppInterop.Common": "1.4.6-ci.389", + "Il2CppInterop.Common": "1.4.6-ci.426", "Mono.Cecil": "0.11.3" } }, "Il2CppInterop.HarmonySupport": { "type": "Transitive", - "resolved": "1.4.6-ci.389", - "contentHash": "Xd2zK6sLqwA0ReyUy83SWKcALwY99fq6vVL5gFOMN5Ct8OOVhtNIws0ysCWsfx1GL0RJiRYfR4pza/MmWRz4PQ==", + "resolved": "1.4.6-ci.426", + "contentHash": "rd2T5o6SybIc12hmX3HktTF5n27hFb0w4mNSExq7h1YsmrNlt7x+Ac1MBnnB0JwPLIg7w8LjnKjoIX6Gf626ng==", "dependencies": { "HarmonyX": "2.10.0", - "Il2CppInterop.Runtime": "1.4.6-ci.389" + "Il2CppInterop.Runtime": "1.4.6-ci.426" } }, "Il2CppInterop.Runtime": { "type": "Transitive", - "resolved": "1.4.6-ci.389", - "contentHash": "nzYjuVzDG/O8+AQ8ax3WVHf+ZVsQKrVtp/yvuW8XoEPLdkbgryIrMo75dvN+tPqxOU3o2MPt9MZ9j25P5aVJMw==", + "resolved": "1.4.6-ci.426", + "contentHash": "0O0oI7gOqJixrsjvh1firhuGYK3Yr0uDLp6Q+t5CfcTMmeEX8Plk3pqAP9ZYyilBvh7uW0gv4Golqz08L1GXug==", "dependencies": { "Iced": "1.17.0", - "Il2CppInterop.Common": "1.4.6-ci.389" + "Il2CppInterop.Common": "1.4.6-ci.426" } }, - "js6pak.Gee.External.Capstone": { - "type": "Transitive", - "resolved": "2.1.0", - "contentHash": "YYpq7NM50bSSVUDjXyV/eiITk6syXqItPjKBOb3jEWS6RFsk0DNhpWMPW6b3hKDmArARuWDU6S2pVu1IeVrvIA==" - }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", "resolved": "6.0.1", @@ -309,15 +312,16 @@ }, "Samboy063.Cpp2IL.Core": { "type": "Transitive", - "resolved": "2022.1.0-development.866", - "contentHash": "QPMuix/3jRngWBdNSLhAbm9BHYf95Ln1Ph1eCK/cDZdVibxFDFP3a6Yczyx1p7GbN7bRQwrhs2skPJSS/z3m2w==", + "resolved": "2022.1.0-pre-release.13", + "contentHash": "d3/E3e2rCDlnRiFdNyLxE94XKabsCdMEzVblzsraodYxQhjCXacn75KHkp3Jwj+6Y8A3usehMInDWep4L/+TQQ==", "dependencies": { - "AsmResolver.DotNet": "5.1.0", + "AsmResolver.DotNet": "5.5.0", + "AssetRipper.CIL": "1.0.0", + "AssetRipper.Gee.External.Capstone": "2.3.1", "Disarm": "2022.1.0-master.26", - "Iced": "1.18.0", - "Samboy063.LibCpp2IL": "2022.1.0-development.866", - "StableNameDotNet": "0.1.0-development.866", - "js6pak.Gee.External.Capstone": "2.1.0" + "Iced": "1.20.0", + "Samboy063.LibCpp2IL": "2022.1.0-pre-release.13", + "StableNameDotNet": "0.1.0-pre-release.13" } }, "Samboy063.LibCpp2IL": { @@ -882,8 +886,8 @@ }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", + "resolved": "6.0.8", + "contentHash": "WhW6zPEgRZoo+c1NEvSSmrME4+LqXmW6tcsRFsEiSMeco+qZ9rpLs7tT53EIkE/s9GNTYS4/STQoaGiKDSWifQ==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "6.0.0" -- 2.39.5