From: gendelo3 Date: Mon, 11 Mar 2024 17:21:00 +0000 (+0100) Subject: Preparation for update v4.5.2 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=8c55511599d73ba2c166491f7d6d4351d04f7ce8;p=rhonda%2Ftheotherroles.git Preparation for update v4.5.2 --- diff --git a/README.md b/README.md index da7ba9a..4c577fe 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.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)
Click to show older versions | Among Us - Version| Mod Version | Link | |----------|-------------|-----------------| +| 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) | 2023.07.12s| v4.4.0| [Download](https://github.com/TheOtherRolesAU/TheOtherRoles/releases/download/v4.4.0/TheOtherRoles.zip) | 2023.07.12s| v4.3.4| [Download](https://github.com/Eisbison/TheOtherRoles/releases/download/v4.3.4/TheOtherRoles.zip) @@ -131,6 +132,16 @@ The [Role Assignment](#role-assignment) section explains how the roles are being
Click to show the Changelog + **Version 4.5.2** +- Updated to Among Us version 2024.3.5s (various small / "long" features and bugfixes) +- Updated BepInEx dependency to 688 +- Added a new feature: Stop the game start (With an option to allow any player to stop it) +- Added a new option (guesser mode): Sidekick is always guesser +- Fixed the Arsonist Win - death reasons for already dead players are no longer replaced +- Changed the implementation of the Detective footprints - Improved performance can be expected +- Changed the medic shield: Is now also displayed in meetings to players who can see it with brackets around the name + + **Version 4.5.1** - Fix a bug that lead to Props not being able to use the disguise button @@ -853,6 +864,7 @@ Thanks to miniduikboot & GD for hosting modded servers (and so much more) # Settings The mod adds a few new settings to Among Us (in addition to the role settings): +- **Any Player Can Stop The Start:** If turned off, only the host can stop the game start. If on, all players can do it. Non-hosts stopping the start will send a chat message indicating who stopped it. - **Number of Crewmates:** The number of Crewmate roles can be set inside a lobby. - **Fill Crewmate Roles (Ignores Min/Max):** Everyone will get a role, even if the settings say there would be plain Crewmates (needs enough roles over 0%). - **Number of Neutrals:** The number of Neutral roles can be set inside a lobby. @@ -1584,7 +1596,7 @@ The Time Master won't be affected by the rewind. ## Medic ### **Team: Crewmates** -The Medic can shield (highlighted by an outline around the player) one player per game, which makes the player unkillable.\ +The Medic can shield (highlighted by an outline around the player) one player per game, which makes the player unkillable.\ The shield is also shown in the meeting as brackets around the shielded player's name. The shielded player can still be voted out and might also be an Impostor.\ If set in the options, the shielded player and/or the Medic will get a red flash on their screen if someone (Impostor, Sheriff, ...) tried to murder them.\ If the Medic dies, the shield disappears with them.\ @@ -2086,7 +2098,8 @@ Players can additionally have a modifier, if enabled (e.g. Medic Guesser Mini). | Number of Neutral Guessers | - | Number of Impostor Guessers | - | Force Jackal Guesser | If set to "On", the first neutral role who will be Guesser is the Jackal. -| Force Thief Guesser | If set to "On", the first (or second if Force Jackal Guesser) neutral role who will be Guesser is the Thief. +| Sidekick Is Always Guesser | The converted sidekick will become a guesser | - +| Force Thief Guesser | If set to "On", the first (or second if Force Jackal Guesser) neutral role who will be Guesser is the Thief. | Guessers Can Have A Modifier | - | Guesser Number Of Shots | - | Guesser Can Shoot Multiple Times Per Meeting | - diff --git a/TheOtherRoles/CustomOptionHolder.cs b/TheOtherRoles/CustomOptionHolder.cs index 876f718..0578dcf 100644 --- a/TheOtherRoles/CustomOptionHolder.cs +++ b/TheOtherRoles/CustomOptionHolder.cs @@ -21,8 +21,8 @@ namespace TheOtherRoles { public static CustomOption modifiersCountMin; public static CustomOption modifiersCountMax; - public static CustomOption enableCodenameHorsemode; - public static CustomOption enableCodenameDisableHorses; + public static CustomOption anyPlayerCanStopStart; + public static CustomOption enableEventMode; public static CustomOption mafiaSpawnRate; public static CustomOption janitorCooldown; @@ -331,6 +331,7 @@ namespace TheOtherRoles { public static CustomOption guesserGamemodeKillsThroughShield; public static CustomOption guesserGamemodeEvilCanKillSpy; public static CustomOption guesserGamemodeCantGuessSnitchIfTaksDone; + public static CustomOption guesserGamemodeSidekickIsAlwaysGuesser; // Hide N Seek Gamemode public static CustomOption hideNSeekHunterCount; @@ -412,9 +413,9 @@ 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) enableCodenameHorsemode = CustomOption.Create(10423, Types.General, cs(Color.green, "Enable Codename Horsemode"), true, null, true); - if (Utilities.EventUtility.canBeEnabled) enableCodenameDisableHorses = CustomOption.Create(10424, Types.General, cs(Color.green, "Disable Horses"), false, enableCodenameHorsemode, 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); @@ -426,7 +427,7 @@ namespace TheOtherRoles { 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); - + 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); @@ -710,6 +711,7 @@ namespace TheOtherRoles { 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); + 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); guesserGamemodeNumberOfShots = CustomOption.Create(2005, Types.Guesser, "Guesser Number Of Shots", 3f, 1f, 15f, 1f, null); @@ -717,6 +719,8 @@ namespace TheOtherRoles { guesserGamemodeKillsThroughShield = CustomOption.Create(2008, Types.Guesser, "Guesses Ignore The Medic Shield", true, null); guesserGamemodeEvilCanKillSpy = CustomOption.Create(2009, Types.Guesser, "Evil Guesser Can Guess The Spy", true, null); guesserGamemodeCantGuessSnitchIfTaksDone = CustomOption.Create(2010, Types.Guesser, "Guesser Can't Guess Snitch When Tasks Completed", true, null); + // Care: 2012 already taken! + // Hide N Seek Gamemode (3000 - 3999) hideNSeekMap = CustomOption.Create(3020, Types.HideNSeekMain, cs(Color.yellow, "Map"), new string[] { "The Skeld", "Mira", "Polus", "Airship", "Fungle", "Submerged", "LI Map"}, null, true, onChange: () => { int map = hideNSeekMap.selection; if (map >= 3) map++; GameOptionsManager.Instance.currentNormalGameOptions.MapId = (byte)map; }); diff --git a/TheOtherRoles/Main.cs b/TheOtherRoles/Main.cs index 66c8ce9..5ccb3c5 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.1"; + public const string VersionString = "4.5.2"; public static uint betaDays = 0; // amount of days for the build to be usable (0 for infinite!) public static Version Version = Version.Parse(VersionString); diff --git a/TheOtherRoles/Modules/BepInExUpdater.cs b/TheOtherRoles/Modules/BepInExUpdater.cs index 9702678..42b3591 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.671+9caf61dca07043beae57b0771f6a5283aa02436b"; - public const string BepInExDownloadURL = "https://builds.bepinex.dev/projects/bepinex_be/671/BepInEx-Unity.IL2CPP-win-x86-6.0.0-be.671%2B9caf61d.zip"; + 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 static bool UpdateRequired => Paths.BepInExVersion.ToString() != RequiredBepInExVersion; public void Awake() diff --git a/TheOtherRoles/Modules/CustomHats/CustomHatManager.cs b/TheOtherRoles/Modules/CustomHats/CustomHatManager.cs index 4eb5185..27e5fc1 100644 --- a/TheOtherRoles/Modules/CustomHats/CustomHatManager.cs +++ b/TheOtherRoles/Modules/CustomHats/CustomHatManager.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Security.Cryptography; using UnityEngine; using UnityEngine.AddressableAssets; +using System.Reflection; namespace TheOtherRoles.Modules.CustomHats; @@ -28,12 +30,11 @@ public static class CustomHatManager internal static string CustomSkinsDirectory => Path.Combine(Path.GetDirectoryName(Application.dataPath)!, ResourcesDirectory); internal static string HatsDirectory => CustomSkinsDirectory; - internal static readonly List UnregisteredHats = new(); + internal static List UnregisteredHats = new(); internal static readonly Dictionary ViewDataCache = new(); internal static readonly Dictionary ExtensionCache = new(); private static readonly HatsLoader Loader; - private static Material cachedShader; internal static HatExtension TestExtension { get; private set; } @@ -71,8 +72,6 @@ public static class CustomHatManager internal static HatData CreateHatBehaviour(CustomHat ch, bool testOnly = false) { - if (cachedShader == null) cachedShader = DestroyableSingleton.Instance.PlayerMaterial; - var viewData = ViewDataCache[ch.Name] = ScriptableObject.CreateInstance(); var hat = ScriptableObject.CreateInstance(); @@ -101,16 +100,11 @@ public static class CustomHatManager hat.ChipOffset = new Vector2(0f, 0.2f); hat.Free = true; - if (ch.Adaptive && cachedShader != null) - { - viewData.AltShader = cachedShader; - } - - var extend = new HatExtension - { + var extend = new HatExtension { Author = ch.Author ?? "Unknown", Package = ch.Package ?? "Misc.", - Condition = ch.Condition ?? "none" + Condition = ch.Condition ?? "none", + Adaptive = ch.Adaptive, }; if (ch.FlipResource != null) @@ -141,6 +135,8 @@ public static class CustomHatManager private static Sprite CreateHatSprite(string path) { var texture = Helpers.loadTextureFromDisk(Path.Combine(HatsDirectory, path)); + if (texture == null) + texture = Helpers.loadTextureFromResources(path); if (texture == null) return null; var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), @@ -280,4 +276,40 @@ public static class CustomHatManager return toDownload; } + + public static List loadHorseHats() { + List hatdatas = new(); + Assembly assembly = Assembly.GetExecutingAssembly(); + string[] resourceNames = assembly.GetManifestResourceNames(); + List hatFiles = new(); + Dictionary> hatFilesSorted = new Dictionary>(); + foreach (string resourceName in resourceNames) { + if (resourceName.Contains("TheOtherRoles.Resources.HorseHats.") && resourceName.Contains(".png")) { + hatFiles.Add(resourceName); + } + } + + foreach (string s in hatFiles) { + string value = s.Substring(0, s.LastIndexOf("HorseSpecialHat") + 17); + if (value.Contains(".")) value.Remove(value.LastIndexOf(".")); + if (!hatFilesSorted.ContainsKey(value)) hatFilesSorted.Add(value, new List()); + hatFilesSorted[value].Add(s); + } + int i = 0; + foreach (var item in hatFilesSorted) { + CustomHat info = new CustomHat(); + info.Name = $"April Hat {i++:D2}"; + info.Author = "A Fool"; + info.Resource = item.Value.FirstOrDefault(x => !x.Contains("back")); + info.BackResource = item.Value.FirstOrDefault(x => x.Contains("back")); + info.Adaptive = info.Resource != null && info.Resource.Contains("adaptive"); + info.FlipResource = item.Value.FirstOrDefault(x => x.Contains("flip")); + info.ClimbResource = item.Value.FirstOrDefault(x => x.Contains("climb")); + info.Package = "April Fools Hats"; + if (info.Resource == null || info.Name == null) // required + continue; + hatdatas.Add(info); + } + return hatdatas; + } } \ No newline at end of file diff --git a/TheOtherRoles/Modules/CustomHats/HatExtension.cs b/TheOtherRoles/Modules/CustomHats/HatExtension.cs index a50f5b7..4b4c28a 100644 --- a/TheOtherRoles/Modules/CustomHats/HatExtension.cs +++ b/TheOtherRoles/Modules/CustomHats/HatExtension.cs @@ -9,4 +9,5 @@ public class HatExtension public string Condition { get; set; } public Sprite FlipImage { get; set; } public Sprite BackFlipImage { get; set; } + public bool Adaptive { get; set; } } \ No newline at end of file diff --git a/TheOtherRoles/Modules/CustomHats/HatsLoader.cs b/TheOtherRoles/Modules/CustomHats/HatsLoader.cs index d8b0584..b0b3c1e 100644 --- a/TheOtherRoles/Modules/CustomHats/HatsLoader.cs +++ b/TheOtherRoles/Modules/CustomHats/HatsLoader.cs @@ -2,6 +2,7 @@ using System.IO; using System.Text.Json; using BepInEx.Unity.IL2CPP.Utils; +using TheOtherRoles.Utilities; using UnityEngine; using UnityEngine.Networking; using static TheOtherRoles.Modules.CustomHats.CustomHatManager; @@ -51,6 +52,7 @@ public class HatsLoader : MonoBehaviour UnregisteredHats.AddRange(SanitizeHats(response)); var toDownload = GenerateDownloadList(UnregisteredHats); + if (EventUtility.isEnabled) UnregisteredHats.AddRange(CustomHatManager.loadHorseHats()); TheOtherRolesPlugin.Logger.LogMessage($"I'll download {toDownload.Count} hat files"); diff --git a/TheOtherRoles/Modules/CustomHats/Patches/HatParentPatches.cs b/TheOtherRoles/Modules/CustomHats/Patches/HatParentPatches.cs index 6112de0..ecfaa72 100644 --- a/TheOtherRoles/Modules/CustomHats/Patches/HatParentPatches.cs +++ b/TheOtherRoles/Modules/CustomHats/Patches/HatParentPatches.cs @@ -4,6 +4,7 @@ using System.Linq; using HarmonyLib; using PowerTools; using TheOtherRoles; +using TheOtherRoles.Modules.CustomHats.Extensions; using UnityEngine; namespace TheOtherRoles.Modules.CustomHats.Patches; @@ -24,7 +25,7 @@ internal static class HatParentPatches private static bool SetHatPrefix(HatParent __instance, HatData hat, int color) { if (SetCustomHat(__instance)) return true; - __instance.PopulateFromHatViewData(); + __instance.PopulateFromViewData(); __instance.SetMaterialColor(color); return false; } @@ -34,8 +35,8 @@ internal static class HatParentPatches private static bool SetHatPrefix(HatParent __instance, int color) { if (!__instance.IsCached()) return true; - __instance.hatDataAsset = null; - __instance.PopulateFromHatViewData(); + __instance.viewAsset = null; + __instance.PopulateFromViewData(); __instance.SetMaterialColor(color); return false; } @@ -45,12 +46,13 @@ internal static class HatParentPatches private static bool UpdateMaterialPrefix(HatParent __instance) { if (!__instance.TryGetCached(out var asset)) return true; - if (asset && asset.AltShader) + var extend = HatDataExtensions.GetHatExtension(__instance.Hat); + if (asset && extend != null && extend.Adaptive) { - __instance.FrontLayer.sharedMaterial = asset.AltShader; + __instance.FrontLayer.sharedMaterial = DestroyableSingleton.Instance.PlayerMaterial; if (__instance.BackLayer) { - __instance.BackLayer.sharedMaterial = asset.AltShader; + __instance.BackLayer.sharedMaterial = DestroyableSingleton.Instance.PlayerMaterial; } } else @@ -185,8 +187,8 @@ internal static class HatParentPatches { if (!__instance.Hat) return false; if (!__instance.IsCached()) return true; - __instance.hatDataAsset = null; - __instance.PopulateFromHatViewData(); + __instance.viewAsset = null; + __instance.PopulateFromViewData(); __instance.SetMaterialColor(colorId); return false; } @@ -203,7 +205,7 @@ internal static class HatParentPatches return false; } - [HarmonyPatch(nameof(HatParent.PopulateFromHatViewData))] + [HarmonyPatch(nameof(HatParent.PopulateFromViewData))] [HarmonyPrefix] private static bool PopulateFromHatViewDataPrefix(HatParent __instance) { diff --git a/TheOtherRoles/Modules/CustomOptions.cs b/TheOtherRoles/Modules/CustomOptions.cs index b072bf5..61bf0f3 100644 --- a/TheOtherRoles/Modules/CustomOptions.cs +++ b/TheOtherRoles/Modules/CustomOptions.cs @@ -104,7 +104,7 @@ namespace TheOtherRoles { } public static void saveVanillaOptions() { - vanillaSettings.Value = Convert.ToBase64String(GameOptionsManager.Instance.gameOptionsFactory.ToBytes(GameManager.Instance.LogicOptions.currentGameOptions)); + vanillaSettings.Value = Convert.ToBase64String(GameOptionsManager.Instance.gameOptionsFactory.ToBytes(GameManager.Instance.LogicOptions.currentGameOptions, false)); } public static void loadVanillaOptions() { diff --git a/TheOtherRoles/Modules/DynamicLobbies.cs b/TheOtherRoles/Modules/DynamicLobbies.cs index 3719bfe..cabb5ee 100644 --- a/TheOtherRoles/Modules/DynamicLobbies.cs +++ b/TheOtherRoles/Modules/DynamicLobbies.cs @@ -27,7 +27,7 @@ namespace TheOtherRoles.Modules { if (LobbyLimit != GameOptionsManager.Instance.currentNormalGameOptions.MaxPlayers) { GameOptionsManager.Instance.currentNormalGameOptions.MaxPlayers = LobbyLimit; FastDestroyableSingleton.Instance.LastPlayerCount = LobbyLimit; - CachedPlayer.LocalPlayer.PlayerControl.RpcSyncSettings(GameOptionsManager.Instance.gameOptionsFactory.ToBytes(GameOptionsManager.Instance.currentGameOptions)); // TODO Maybe simpler?? + CachedPlayer.LocalPlayer.PlayerControl.RpcSyncSettings(GameOptionsManager.Instance.gameOptionsFactory.ToBytes(GameOptionsManager.Instance.currentGameOptions, false)); // TODO Maybe simpler?? __instance.AddChat(CachedPlayer.LocalPlayer.PlayerControl, $"Lobby Size changed to {LobbyLimit} players"); } else { __instance.AddChat(CachedPlayer.LocalPlayer.PlayerControl, $"Lobby Size is already {LobbyLimit}"); diff --git a/TheOtherRoles/Objects/Footprint.cs b/TheOtherRoles/Objects/Footprint.cs index 8d8138e..3c27bd9 100644 --- a/TheOtherRoles/Objects/Footprint.cs +++ b/TheOtherRoles/Objects/Footprint.cs @@ -1,33 +1,35 @@ +using InnerNet; +using Reactor.Utilities.Extensions; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; +using TheOtherRoles.Players; using TheOtherRoles.Utilities; using UnityEngine; +using static TheOtherRoles.TheOtherRoles; namespace TheOtherRoles.Objects { - public class FootprintHolder : MonoBehaviour - { + public class FootprintHolder : MonoBehaviour { static FootprintHolder() => ClassInjector.RegisterTypeInIl2Cpp(); public FootprintHolder(IntPtr ptr) : base(ptr) { } private static FootprintHolder _instance; - public static FootprintHolder Instance - { + public static FootprintHolder Instance { get => _instance ? _instance : _instance = new GameObject("FootprintHolder").AddComponent(); set => _instance = value; } - + private static Sprite _footprintSprite; private static Sprite FootprintSprite => _footprintSprite ??= Helpers.loadSpriteFromResources("TheOtherRoles.Resources.Footprint.png", 600f); private static bool AnonymousFootprints => TheOtherRoles.Detective.anonymousFootprints; private static float FootprintDuration => TheOtherRoles.Detective.footprintDuration; - - private class Footprint - { + + private class Footprint { public GameObject GameObject; public Transform Transform; public SpriteRenderer Renderer; @@ -35,8 +37,7 @@ namespace TheOtherRoles.Objects public GameData.PlayerInfo Data; public float Lifetime; - public Footprint() - { + public Footprint() { GameObject = new("Footprint") { layer = 8 }; Transform = GameObject.transform; Renderer = GameObject.AddComponent(); @@ -46,22 +47,20 @@ namespace TheOtherRoles.Objects } } - + private readonly ConcurrentBag _pool = new(); private readonly List _activeFootprints = new(); private readonly List _toRemove = new(); - + [HideFromIl2Cpp] - public void MakeFootprint(PlayerControl player) - { - if (!_pool.TryTake(out var print)) - { + public void MakeFootprint(PlayerControl player) { + if (!_pool.TryTake(out var print)) { print = new(); } print.Lifetime = FootprintDuration; - + var pos = player.transform.position; pos.z = pos.y / 1000f + 0.001f; print.Transform.SetPositionAndRotation(pos, Quaternion.EulerRotation(0, 0, UnityEngine.Random.Range(0.0f, 360.0f))); @@ -71,31 +70,29 @@ namespace TheOtherRoles.Objects _activeFootprints.Add(print); } - private void Update() - { - var dt = Time.deltaTime; + private static float updateDt = 0.10f; + + private void Start() { + InvokeRepeating(nameof(FootprintUpdate), updateDt, updateDt); + } + + private void FootprintUpdate() { + var dt = updateDt; _toRemove.Clear(); - foreach (var activeFootprint in _activeFootprints) - { + foreach (var activeFootprint in _activeFootprints) { var p = activeFootprint.Lifetime / FootprintDuration; - - if (activeFootprint.Lifetime <= 0) - { + + if (activeFootprint.Lifetime <= 0) { _toRemove.Add(activeFootprint); continue; } - + Color color; - if (AnonymousFootprints || Camouflager.camouflageTimer > 0 || Helpers.MushroomSabotageActive()) - { + if (AnonymousFootprints || Camouflager.camouflageTimer > 0 || Helpers.MushroomSabotageActive()) { color = Palette.PlayerColors[6]; - } - else if (activeFootprint.Owner == Morphling.morphling && Morphling.morphTimer > 0 && Morphling.morphTarget && Morphling.morphTarget.Data != null) - { + } else if (activeFootprint.Owner == Morphling.morphling && Morphling.morphTimer > 0 && Morphling.morphTarget && Morphling.morphTarget.Data != null) { color = Palette.PlayerColors[Morphling.morphTarget.Data.DefaultOutfit.ColorId]; - } - else - { + } else { color = Palette.PlayerColors[activeFootprint.Data.DefaultOutfit.ColorId]; } @@ -104,17 +101,15 @@ namespace TheOtherRoles.Objects activeFootprint.Lifetime -= dt; } - - foreach (var footprint in _toRemove) - { + + foreach (var footprint in _toRemove) { footprint.GameObject.SetActive(false); _activeFootprints.Remove(footprint); _pool.Add(footprint); } } - private void OnDestroy() - { + private void OnDestroy() { Instance = null; } } diff --git a/TheOtherRoles/Patches/GameStartManagerPatch.cs b/TheOtherRoles/Patches/GameStartManagerPatch.cs index c6b2576..c5eb053 100644 --- a/TheOtherRoles/Patches/GameStartManagerPatch.cs +++ b/TheOtherRoles/Patches/GameStartManagerPatch.cs @@ -8,6 +8,7 @@ using System; using TheOtherRoles.Players; using TheOtherRoles.Utilities; using System.Linq; +using Reactor.Utilities.Extensions; namespace TheOtherRoles.Patches { public class GameStartManagerPatch { @@ -47,7 +48,8 @@ namespace TheOtherRoles.Patches { public static float startingTimer = 0; private static bool update = false; private static string currentText = ""; - + private static GameObject copiedStartButton; + public static void Prefix(GameStartManager __instance) { if (!GameData.Instance ) return; // No instance __instance.MinPlayers = 1; @@ -100,12 +102,40 @@ namespace TheOtherRoles.Patches { __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition; } + 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.fontSize *= 0.8f; + startButtonText.fontSizeMax = startButtonText.fontSize; + startButtonText.gameObject.transform.localPosition = Vector3.zero; + PassiveButton startButtonPassiveButton = copiedStartButton.GetComponent(); + + void StopStartFunc() { + __instance.ResetStartState(); + copiedStartButton.Destroy(); + startingTimer = 0; + } + startButtonPassiveButton.OnClick.AddListener((Action)(() => StopStartFunc())); + __instance.StartCoroutine(Effects.Lerp(.1f, new System.Action((p) => { + startButtonText.text = "STOP"; + }))); + } + if (__instance.startState == GameStartManager.StartingStates.Countdown) + __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 0.6f; } // Client update with handshake infos @@ -125,16 +155,42 @@ namespace TheOtherRoles.Patches { __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 2; } else { __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition; - if (__instance.startState != GameStartManager.StartingStates.Countdown && startingTimer <= 0) { + if (!__instance.GameStartText.text.StartsWith("Starting")) __instance.GameStartText.text = String.Empty; + } + + if (!__instance.GameStartText.text.StartsWith("Starting") || !CustomOptionHolder.anyPlayerCanStopStart.getBool()) + copiedStartButton?.Destroy(); + if (CustomOptionHolder.anyPlayerCanStopStart.getBool() && copiedStartButton == null && __instance.GameStartText.text.StartsWith("Starting")) { + + // 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.fontSize *= 0.8f; + startButtonText.fontSizeMax = startButtonText.fontSize; + startButtonText.gameObject.transform.localPosition = Vector3.zero; + PassiveButton startButtonPassiveButton = copiedStartButton.GetComponent(); + + void StopStartFunc() { + MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(CachedPlayer.LocalPlayer.PlayerControl.NetId, (byte)CustomRPC.StopStart, Hazel.SendOption.Reliable, AmongUsClient.Instance.HostId); + writer.Write(PlayerControl.LocalPlayer.PlayerId); + AmongUsClient.Instance.FinishRpcImmediately(writer); + copiedStartButton.Destroy(); + __instance.GameStartText.text = String.Empty; + startingTimer = 0; } - else { - __instance.GameStartText.text = $"Starting in {(int)startingTimer + 1}"; - if (startingTimer <= 0) { - __instance.GameStartText.text = String.Empty; - } - } + startButtonPassiveButton.OnClick.AddListener((Action)(() => StopStartFunc())); + __instance.StartCoroutine(Effects.Lerp(.1f, new System.Action((p) => { + startButtonText.text = "STOP"; + }))); + } + if (__instance.GameStartText.text.StartsWith("Starting") && CustomOptionHolder.anyPlayerCanStopStart.getBool()) + __instance.GameStartText.transform.localPosition = __instance.StartButton.transform.localPosition + Vector3.up * 0.6f; } // Start Timer diff --git a/TheOtherRoles/Patches/IntroPatch.cs b/TheOtherRoles/Patches/IntroPatch.cs index 71b4609..101b97a 100644 --- a/TheOtherRoles/Patches/IntroPatch.cs +++ b/TheOtherRoles/Patches/IntroPatch.cs @@ -273,12 +273,20 @@ namespace TheOtherRoles.Patches { } } - [HarmonyPatch(typeof(Constants), nameof(Constants.ShouldHorseAround))] + /* Horses are broken since 2024.3.5 - keeping this code in case they return. + * [HarmonyPatch(typeof(AprilFoolsMode), nameof(AprilFoolsMode.ShouldHorseAround))] public static class ShouldAlwaysHorseAround { public static bool Prefix(ref bool __result) { - __result = EventUtility.isEnabled && !EventUtility.disableHorses; + __result = EventUtility.isEnabled && !EventUtility.disableEventMode; return false; } + }*/ + + [HarmonyPatch(typeof(AprilFoolsMode), nameof(AprilFoolsMode.ShouldShowAprilFoolsToggle))] + public static class ShouldShowAprilFoolsToggle { + public static void Postfix(ref bool __result) { + __result = __result || EventUtility.isEventDate || EventUtility.canBeEnabled; // Extend it to a 7 day window instead of just 1st day of the Month + } } } diff --git a/TheOtherRoles/Patches/MeetingPatch.cs b/TheOtherRoles/Patches/MeetingPatch.cs index 30cc11d..39b26a8 100644 --- a/TheOtherRoles/Patches/MeetingPatch.cs +++ b/TheOtherRoles/Patches/MeetingPatch.cs @@ -592,13 +592,6 @@ namespace TheOtherRoles.Patches { }))); } - //Fix visor in Meetings - /** - foreach (PlayerVoteArea pva in __instance.playerStates) { - if(pva.PlayerIcon != null && pva.PlayerIcon.VisorSlot != null){ - pva.PlayerIcon.VisorSlot.transform.position += new Vector3(0, 0, -1f); - } - } */ bool isGuesser = HandleGuesser.isGuesser(CachedPlayer.LocalPlayer.PlayerId); diff --git a/TheOtherRoles/Patches/PlayerControlPatch.cs b/TheOtherRoles/Patches/PlayerControlPatch.cs index 456732f..68355cb 100644 --- a/TheOtherRoles/Patches/PlayerControlPatch.cs +++ b/TheOtherRoles/Patches/PlayerControlPatch.cs @@ -69,13 +69,8 @@ namespace TheOtherRoles.Patches { bool isMorphedMorphling = target == Morphling.morphling && Morphling.morphTarget != null && Morphling.morphTimer > 0f; bool hasVisibleShield = false; Color color = Medic.shieldedColor; - if (Camouflager.camouflageTimer <= 0f && !Helpers.MushroomSabotageActive() && Medic.shielded != null && ((target == Medic.shielded && !isMorphedMorphling) || (isMorphedMorphling && Morphling.morphTarget == Medic.shielded))) { - hasVisibleShield = Medic.showShielded == 0 || Helpers.shouldShowGhostInfo() // Everyone or Ghost info - || (Medic.showShielded == 1 && (CachedPlayer.LocalPlayer.PlayerControl == Medic.shielded || CachedPlayer.LocalPlayer.PlayerControl == Medic.medic)) // Shielded + Medic - || (Medic.showShielded == 2 && CachedPlayer.LocalPlayer.PlayerControl == Medic.medic); // Medic only - // Make shield invisible till after the next meeting if the option is set (the medic can already see the shield) - hasVisibleShield = hasVisibleShield && (Medic.meetingAfterShielding || !Medic.showShieldAfterMeeting || CachedPlayer.LocalPlayer.PlayerControl == Medic.medic || Helpers.shouldShowGhostInfo()); - } + if (Camouflager.camouflageTimer <= 0f && !Helpers.MushroomSabotageActive() && Medic.shieldVisible(target)) + hasVisibleShield = true; if (Camouflager.camouflageTimer <= 0f && !Helpers.MushroomSabotageActive() && TORMapOptions.firstKillPlayer != null && TORMapOptions.shieldFirstKill && ((target == TORMapOptions.firstKillPlayer && !isMorphedMorphling) || (isMorphedMorphling && Morphling.morphTarget == TORMapOptions.firstKillPlayer))) { hasVisibleShield = true; @@ -1438,7 +1433,7 @@ namespace TheOtherRoles.Patches { public static class PlayerPhysicsFixedUpdate { public static void Postfix(PlayerPhysics __instance) { - bool shouldInvert = (Invert.invert.FindAll(x => x.PlayerId == CachedPlayer.LocalPlayer.PlayerId).Count > 0 && Invert.meetings > 0) ^ EventUtility.eventInvert; // xor. if already invert, eventInvert will turn it off for 10s + bool shouldInvert = Invert.invert.FindAll(x => x.PlayerId == CachedPlayer.LocalPlayer.PlayerId).Count > 0 && Invert.meetings > 0; if (__instance.AmOwner && AmongUsClient.Instance && AmongUsClient.Instance.GameState == InnerNet.InnerNetClient.GameStates.Started && diff --git a/TheOtherRoles/Patches/UpdatePatch.cs b/TheOtherRoles/Patches/UpdatePatch.cs index 1b2bfa1..7d25225 100644 --- a/TheOtherRoles/Patches/UpdatePatch.cs +++ b/TheOtherRoles/Patches/UpdatePatch.cs @@ -231,6 +231,16 @@ namespace TheOtherRoles.Patches { if (target != null) player.NameText.text += $" ({(Helpers.isLighterColor(target) ? "L" : "D")})"; } } + + // Add medic shield info: + if (MeetingHud.Instance != null && Medic.medic != null && Medic.shielded != null && Medic.shieldVisible(Medic.shielded)) { + foreach (PlayerVoteArea player in MeetingHud.Instance.playerStates) + if (player.TargetPlayerId == Medic.shielded.PlayerId) { + player.NameText.text = Helpers.cs(Medic.color, "[") + player.NameText.text + Helpers.cs(Medic.color, "]"); + // player.HighlightedFX.color = Medic.color; + // player.HighlightedFX.enabled = true; + } + } } static void updateShielded() { diff --git a/TheOtherRoles/RPC.cs b/TheOtherRoles/RPC.cs index 4edcbdf..6153efd 100644 --- a/TheOtherRoles/RPC.cs +++ b/TheOtherRoles/RPC.cs @@ -16,7 +16,6 @@ using TheOtherRoles.CustomGameModes; using AmongUs.Data; using AmongUs.GameOptions; using Assets.CoreScripts; - namespace TheOtherRoles { public enum RoleId { @@ -97,6 +96,7 @@ namespace TheOtherRoles DynamicMapOption, SetGameStarting, ShareGamemode, + StopStart, // Role functionality @@ -220,6 +220,13 @@ namespace TheOtherRoles TORMapOptions.gameMode = (CustomGamemodes) gm; } + public static void stopStart(byte playerId) { + if (AmongUsClient.Instance.AmHost && CustomOptionHolder.anyPlayerCanStopStart.getBool()) { + GameStartManager.Instance.ResetStartState(); + PlayerControl.LocalPlayer.RpcSendChat($"{Helpers.playerById(playerId).Data.PlayerName} stopped the game start!"); + } + } + public static void workaroundSetRoles(byte numberOfRoles, MessageReader reader) { for (int i = 0; i < numberOfRoles; i++) @@ -236,7 +243,7 @@ namespace TheOtherRoles } public static void setRole(byte roleId, byte playerId) { - foreach (PlayerControl player in CachedPlayer.AllPlayers) + foreach (PlayerControl player in CachedPlayer.AllPlayers) { if (player.PlayerId == playerId) { switch((RoleId)roleId) { case RoleId.Jester: @@ -373,7 +380,12 @@ namespace TheOtherRoles Bomber.bomber = player; break; } - } + if (AmongUsClient.Instance.AmHost && Helpers.roleCanUseVents(player) && !player.Data.Role.IsImpostor) { + player.RpcSetRole(RoleTypes.Engineer); + player.SetRole(RoleTypes.Engineer); + } + } + } } public static void setModifier(byte modifierId, byte playerId, byte flag) { @@ -676,6 +688,8 @@ namespace TheOtherRoles Sidekick.wasSpy = wasSpy; Sidekick.wasImpostor = wasImpostor; if (player == CachedPlayer.LocalPlayer.PlayerControl) SoundEffectsManager.play("jackalSidekick"); + if (HandleGuesser.isGuesserGm && CustomOptionHolder.guesserGamemodeSidekickIsAlwaysGuesser.getBool() && !HandleGuesser.isGuesser(targetId)) + setGuesserGm(targetId); } Jackal.canCreateSidekick = false; } @@ -919,7 +933,7 @@ namespace TheOtherRoles public static void arsonistWin() { Arsonist.triggerArsonistWin = true; foreach (PlayerControl p in CachedPlayer.AllPlayers) { - if (p != Arsonist.arsonist) { + if (p != Arsonist.arsonist && !p.Data.IsDead) { p.Exiled(); overrideDeathReasonAndKiller(p, DeadPlayer.CustomDeathReason.Arson, Arsonist.arsonist); } @@ -1063,6 +1077,8 @@ namespace TheOtherRoles if (target == Sidekick.sidekick) { Sidekick.sidekick = thief; Jackal.formerJackals.Add(target); + if (HandleGuesser.isGuesserGm && CustomOptionHolder.guesserGamemodeSidekickIsAlwaysGuesser.getBool() && !HandleGuesser.isGuesser(thief.PlayerId)) + setGuesserGm(thief.PlayerId); } if (target == Guesser.evilGuesser) Guesser.evilGuesser = thief; if (target == Godfather.godfather) Godfather.godfather = thief; @@ -1519,6 +1535,9 @@ namespace TheOtherRoles byte gm = reader.ReadByte(); RPCProcedure.shareGamemode(gm); break; + case (byte)CustomRPC.StopStart: + RPCProcedure.stopStart(reader.ReadByte()); + break; // Game mode case (byte)CustomRPC.SetGuesserGm: diff --git a/TheOtherRoles/Resources/StopClean.png b/TheOtherRoles/Resources/StopClean.png new file mode 100644 index 0000000..d284137 Binary files /dev/null and b/TheOtherRoles/Resources/StopClean.png differ diff --git a/TheOtherRoles/TheOtherRoles.cs b/TheOtherRoles/TheOtherRoles.cs index 6143779..8281ba9 100644 --- a/TheOtherRoles/TheOtherRoles.cs +++ b/TheOtherRoles/TheOtherRoles.cs @@ -77,7 +77,6 @@ namespace TheOtherRoles HandleGuesser.clearAndReload(); HideNSeek.clearAndReload(); PropHunt.clearAndReload(); - } public static class Jester { @@ -447,6 +446,20 @@ namespace TheOtherRoles return buttonSprite; } + public static bool shieldVisible(PlayerControl target) { + bool hasVisibleShield = false; + + bool isMorphedMorphling = target == Morphling.morphling && Morphling.morphTarget != null && Morphling.morphTimer > 0f; + if (Medic.shielded != null && ((target == Medic.shielded && !isMorphedMorphling) || (isMorphedMorphling && Morphling.morphTarget == Medic.shielded))) { + hasVisibleShield = Medic.showShielded == 0 || Helpers.shouldShowGhostInfo() // Everyone or Ghost info + || (Medic.showShielded == 1 && (CachedPlayer.LocalPlayer.PlayerControl == Medic.shielded || CachedPlayer.LocalPlayer.PlayerControl == Medic.medic)) // Shielded + Medic + || (Medic.showShielded == 2 && CachedPlayer.LocalPlayer.PlayerControl == Medic.medic); // Medic only + // Make shield invisible till after the next meeting if the option is set (the medic can already see the shield) + hasVisibleShield = hasVisibleShield && (Medic.meetingAfterShielding || !Medic.showShieldAfterMeeting || CachedPlayer.LocalPlayer.PlayerControl == Medic.medic || Helpers.shouldShowGhostInfo()); + } + return hasVisibleShield; + } + public static void clearAndReload() { medic = null; shielded = null; @@ -1466,7 +1479,7 @@ namespace TheOtherRoles if (!roleString.Contains("Impostor") && !roleString.Contains("Crewmate")) msg = "If my role hasn't been saved, there's no " + roleString + " in the game anymore."; else - msg = "I am a " + roleString + " without an other role."; + msg = "I was a " + roleString + " without another role."; } else if (randomNumber == 1) msg = "I'm not sure, but I guess a " + typeOfColor + " color killed me."; else if (randomNumber == 2) msg = "If I counted correctly, I died " + Math.Round(timeSinceDeath / 1000) + "s before the next meeting started."; else msg = "It seems like my killer is the " + RoleInfo.GetRolesString(Medium.target.killerIfExisting, false, false, true) + "."; diff --git a/TheOtherRoles/TheOtherRoles.csproj b/TheOtherRoles/TheOtherRoles.csproj index f591579..1621806 100644 --- a/TheOtherRoles/TheOtherRoles.csproj +++ b/TheOtherRoles/TheOtherRoles.csproj @@ -16,7 +16,7 @@ - + diff --git a/TheOtherRoles/Utilities/EventUtility.cs b/TheOtherRoles/Utilities/EventUtility.cs index caacedc..a7c7a1c 100644 --- a/TheOtherRoles/Utilities/EventUtility.cs +++ b/TheOtherRoles/Utilities/EventUtility.cs @@ -9,73 +9,30 @@ using static TheOtherRoles.TheOtherRoles; using System.Linq; using InnerNet; using TheOtherRoles.Modules; +using HarmonyLib; namespace TheOtherRoles.Utilities; +[HarmonyPatch] public static class EventUtility { - public enum EventTypes { - Communication, - Animation, - Invert, - KnockKnock - } - - public static readonly float[] eventFrequencies = {15f, 60f, 60f, 300f}; - public static readonly float[] eventDurations = {0f, 1f, 5f, 0f}; - public static double[] eventProbabilities; - private static bool knocked = false; - public static bool disableHorses = false; - public static void Load() { if (!isEnabled) return; - eventProbabilities = new double[6]; - foreach (EventTypes curEvent in Enum.GetValues(typeof(EventTypes))) { - float desired_trials = 60 * eventFrequencies[(int)curEvent]; - eventProbabilities[(int)curEvent] = desired_trials != 0 ? 1f / desired_trials : 1f; - } - - } - - private static List eventQueue = null; - public static bool eventInvert = false; + } public static void clearAndReload() { - eventQueue = new List(); - eventInvert = false; - if (canBeEnabled && CustomOptionHolder.enableCodenameDisableHorses != null) - disableHorses = CustomOptionHolder.enableCodenameDisableHorses.getBool(); } public static void Update() { - if (!isEnabled || eventQueue == null || AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started || TheOtherRoles.rnd == null || IntroCutscene.Instance) return; - foreach (EventTypes curEvent in eventQueue.ToArray()) { - if (TheOtherRoles.rnd.NextSingle() < eventProbabilities[(int)curEvent]) { - eventQueue.Remove(curEvent); - StartEvent(curEvent); - } - } - - AddToQueue(EventTypes.Animation); - AddToQueue(EventTypes.Invert); - if (!knocked) { - AddToQueue(EventTypes.KnockKnock); - } + if (!isEnabled || AmongUsClient.Instance.GameState != InnerNet.InnerNetClient.GameStates.Started || TheOtherRoles.rnd == null || IntroCutscene.Instance) return; } - private static DateTime enabled = DateTime.FromBinary(-8585213068854775808); + public static DateTime enabled = DateTime.FromBinary(638475264000000000); public static bool isEventDate => DateTime.Today.Date == enabled; - public static bool canBeEnabled => DateTime.Today.Date > enabled && DateTime.Today.Date <= enabled.AddDays(7); // One Week after the EVENT - public static bool isEnabled => isEventDate || canBeEnabled && CustomOptionHolder.enableCodenameHorsemode != null && CustomOptionHolder.enableCodenameHorsemode.getBool(); - - public static void AddToQueue(EventTypes newEvent) { - if (!isEnabled || eventQueue == null || eventQueue.Contains(newEvent)) return; - eventQueue.Add(newEvent); - } + public static bool canBeEnabled => DateTime.Today.Date >= enabled && DateTime.Today.Date <= enabled.AddDays(7); // One Week after the EVENT + public static bool isEnabled => isEventDate || canBeEnabled && CustomOptionHolder.enableEventMode != null && CustomOptionHolder.enableEventMode.getBool(); - - private static string defaultHat = "default"; public static void meetingEndsUpdate() { if (!isEnabled) return; // TODO - Implement Horse hats @@ -85,75 +42,24 @@ public static class EventUtility { public static void meetingStartsUpdate() { if (!isEnabled) return; - if (rnd.NextDouble() <= 0.3f) { AddToQueue(EventTypes.Communication); } - PlayerControl.LocalPlayer.RpcSetHat(defaultHat); - HudManager.Instance.StartCoroutine(Effects.Lerp(1f, new Action((p) => { - if (MeetingHud.Instance && MeetingHud.Instance.playerStates != null) { - foreach (PlayerVoteArea pva in MeetingHud.Instance.playerStates) { - GameData.PlayerInfo pInfo = GameData.Instance.AllPlayers.ToArray().First(x => x.PlayerId == pva.TargetPlayerId); - pva.SetCosmetics(pInfo); // Needed cause cosmetics are set async'd. - } - } - }))); } public static void gameStartsUpdate() { if (!isEnabled) return; - defaultHat = PlayerControl.LocalPlayer.Data.DefaultOutfit.HatId; - meetingEndsUpdate(); - List relevantPlayers = CachedPlayer.AllPlayers.Where(x => !x.Data.IsDead && x != CachedPlayer.LocalPlayer).ToList(); - foreach (CachedPlayer pc in relevantPlayers) - pc.PlayerControl.MyPhysics.SetBodyType(disableHorses ? PlayerBodyTypes.Normal : PlayerBodyTypes.Horse); - } public static void gameEndsUpdate() { if (!isEnabled) return; - if (defaultHat != "default") - PlayerControl.LocalPlayer.RpcSetHat(defaultHat); } - public static void StartEvent(EventTypes eventToStart) { - List relevantPlayers = CachedPlayer.AllPlayers.Where(x => !x.Data.IsDead && x != CachedPlayer.LocalPlayer).ToList(); - switch (eventToStart) { - case EventTypes.Animation: - CachedPlayer animationPlayer = relevantPlayers[rnd.Next(relevantPlayers.Count)]; - animationPlayer.PlayerPhysics.SetBodyType(rnd.Next(2) > 0 ? (disableHorses ? PlayerBodyTypes.Horse : PlayerBodyTypes.Normal) : PlayerBodyTypes.Seeker); - FastDestroyableSingleton.Instance.StartCoroutine(Effects.Lerp(eventDurations[(int)EventTypes.Animation], new Action((p) => { - if (p==1) - animationPlayer.PlayerControl.MyPhysics.SetBodyType(disableHorses ? PlayerBodyTypes.Normal : PlayerBodyTypes.Horse); - }))); - break; - case EventTypes.Communication: - int index = TheOtherRoles.rnd.Next(relevantPlayers.Count); - CachedPlayer firstPlayer = relevantPlayers[index]; - relevantPlayers.RemoveAt(index); - string msg = firstPlayer.Data.PlayerName + " "; - foreach (CachedPlayer pc in relevantPlayers.ToArray()) { - if (TheOtherRoles.rnd.NextSingle() < 1f / relevantPlayers.Count) { - relevantPlayers.Remove(pc); - msg += pc.Data.PlayerName + " "; - } - } - RoomTracker tracker = FastDestroyableSingleton.Instance.roomTracker; - string lastRoom = ""; - try { lastRoom = FastDestroyableSingleton.Instance.GetString(tracker.LastRoom.RoomId); } catch { } - msg += lastRoom != ""? lastRoom : (TheOtherRoles.rnd.Next(2) > 0 ? "sus" : "safe"); - - FastDestroyableSingleton.Instance.Chat.AddChat(relevantPlayers[rnd.Next(relevantPlayers.Count)], $"{msg}"); - break; - case EventTypes.Invert: - eventInvert = true; - FastDestroyableSingleton.Instance.StartCoroutine(Effects.Lerp(eventDurations[(int)EventTypes.Invert], new Action((p) => { - if (p == 1) - eventInvert = false; - }))); - break; - case EventTypes.KnockKnock: - SoundEffectsManager.play("knockKnock"); - knocked = true; - break; + [HarmonyPatch(typeof(ChatController), nameof(ChatController.AddChat))] + public static class AddChatPatch { + public static void Prefix(ChatController __instance, PlayerControl sourcePlayer, ref string chatText, bool censor) { + if (!isEnabled) return; + var charArray = chatText.ToCharArray(); + Array.Reverse(charArray); + chatText = new string(charArray); } } } \ No newline at end of file diff --git a/TheOtherRoles/packages.lock.json b/TheOtherRoles/packages.lock.json index 3a2a5c3..e3543ec 100644 --- a/TheOtherRoles/packages.lock.json +++ b/TheOtherRoles/packages.lock.json @@ -4,9 +4,9 @@ "net6.0": { "AmongUs.GameLibs.Steam": { "type": "Direct", - "requested": "[2023.11.28, )", - "resolved": "2023.11.28", - "contentHash": "EsKrZS4tjTRrY7mtZls3tMy54zije4vleEA9s0UGUr4SF3qm2axR6wMncyIJCk4ACXlN5Bxpsd3/upDe9Gnx6A==" + "requested": "[2024.3.5, )", + "resolved": "2024.3.5", + "contentHash": "5KOcBSFnql0DmrL82GikWb5I+KLvD8h8RsS28BKTCws2fPKwWauUm/L9Zh73pIHcnOMCpvnAxWeZh/AqxAEI0Q==" }, "BepInEx.IL2CPP.MSBuild": { "type": "Direct",