]> git.deb.at Git - rhonda/theotherroles.git/commitdiff
Preparation for update v4.5.2
authorgendelo3 <gendelo3@gmail.com>
Mon, 11 Mar 2024 17:21:00 +0000 (18:21 +0100)
committergendelo3 <gendelo3@gmail.com>
Mon, 11 Mar 2024 17:21:00 +0000 (18:21 +0100)
22 files changed:
README.md
TheOtherRoles/CustomOptionHolder.cs
TheOtherRoles/Main.cs
TheOtherRoles/Modules/BepInExUpdater.cs
TheOtherRoles/Modules/CustomHats/CustomHatManager.cs
TheOtherRoles/Modules/CustomHats/HatExtension.cs
TheOtherRoles/Modules/CustomHats/HatsLoader.cs
TheOtherRoles/Modules/CustomHats/Patches/HatParentPatches.cs
TheOtherRoles/Modules/CustomOptions.cs
TheOtherRoles/Modules/DynamicLobbies.cs
TheOtherRoles/Objects/Footprint.cs
TheOtherRoles/Patches/GameStartManagerPatch.cs
TheOtherRoles/Patches/IntroPatch.cs
TheOtherRoles/Patches/MeetingPatch.cs
TheOtherRoles/Patches/PlayerControlPatch.cs
TheOtherRoles/Patches/UpdatePatch.cs
TheOtherRoles/RPC.cs
TheOtherRoles/Resources/StopClean.png [new file with mode: 0644]
TheOtherRoles/TheOtherRoles.cs
TheOtherRoles/TheOtherRoles.csproj
TheOtherRoles/Utilities/EventUtility.cs
TheOtherRoles/packages.lock.json

index da7ba9abd331be8af96fa1fe374a709b3746e4f4..4c577fea5c1e60f9de9515a6e7e5be0c78dda23c 100644 (file)
--- 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)
 
 <details>
   <summary>Click to show older versions</summary>
   
 | 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
 <details>
   <summary>Click to show the Changelog</summary>
   
+ **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 | -
index 876f718c14e1d1ffd2ae7e935cbd227c0bfa4878..0578dcf304088a83737df6c52467cafb8e0ad8aa 100644 (file)
@@ -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; });
index 66c8ce99b6cc84339142109b1e08e54b85621939..5ccb3c5556beb581c59a59030d92c1147b018387 100644 (file)
@@ -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);
index 9702678dc260b0530e5b6ecc34fa8659dd540827..42b3591e4195996c603af7a2a8685a610acacf4f 100644 (file)
@@ -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()
index 4eb5185771108641d500d507576bd9e172557628..27e5fc1510395498f4d30e005bc9f3a34f184a61 100644 (file)
@@ -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<CustomHat> UnregisteredHats = new();
+    internal static List<CustomHat> UnregisteredHats = new();
     internal static readonly Dictionary<string, HatViewData> ViewDataCache = new();
     internal static readonly Dictionary<string, HatExtension> 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<HatManager>.Instance.PlayerMaterial;
-
         var viewData = ViewDataCache[ch.Name] = ScriptableObject.CreateInstance<HatViewData>();
         var hat = ScriptableObject.CreateInstance<HatData>();
 
@@ -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<CustomHat> loadHorseHats() {
+        List<CustomHat> hatdatas = new();
+        Assembly assembly = Assembly.GetExecutingAssembly();
+        string[] resourceNames = assembly.GetManifestResourceNames();
+        List<string> hatFiles = new();
+        Dictionary<string, List<string>> hatFilesSorted = new Dictionary<string, List<string>>();
+        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<string>());
+            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
index a50f5b74b729f2813ac70085f47339ea367c2a1b..4b4c28a373a45c2d86ae0d4865774b7ba874377f 100644 (file)
@@ -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
index d8b0584f4699ec120fa5c4667ff8293a277e18c0..b0b3c1e8087678d12dcace8c904109eab6db38d9 100644 (file)
@@ -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");
 
index 6112de08fd580f2e7479ecd3a0bc3af671f8d509..ecfaa72b6e60c1f57661dad84a4b7936893c0bea 100644 (file)
@@ -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<HatManager>.Instance.PlayerMaterial;
             if (__instance.BackLayer)
             {
-                __instance.BackLayer.sharedMaterial = asset.AltShader;
+                __instance.BackLayer.sharedMaterial = DestroyableSingleton<HatManager>.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)
     {
index b072bf5d6b00327aae7a5f45e5816e36212bc924..61bf0f3e9282b5b504ba40899a62591190fab4d1 100644 (file)
@@ -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() {
index 3719bfeb85603ea860c8ef6dadbf57dc804ec94b..cabb5eeae8a2d0635a694401e7a0f189fec2f5ea 100644 (file)
@@ -27,7 +27,7 @@ namespace TheOtherRoles.Modules {
                                     if (LobbyLimit != GameOptionsManager.Instance.currentNormalGameOptions.MaxPlayers) {
                                         GameOptionsManager.Instance.currentNormalGameOptions.MaxPlayers = LobbyLimit;
                                         FastDestroyableSingleton<GameStartManager>.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}");
index 8d8138e61ed847b7e95f97e52d8a2c029dd62c00..3c27bd905fb13807dd6ca401d33b8c442f6592e8 100644 (file)
@@ -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<FootprintHolder>();
 
         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<FootprintHolder>();
             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<SpriteRenderer>();
@@ -46,22 +47,20 @@ namespace TheOtherRoles.Objects
             }
         }
 
-       
+
 
         private readonly ConcurrentBag<Footprint> _pool = new();
         private readonly List<Footprint> _activeFootprints = new();
         private readonly List<Footprint> _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;
         }
     }
index c6b25762682b2ec6c206ca80ec2fca1fcaaccf11..c5eb053389bba74d5a808921c3aad2ffcdf70415 100644 (file)
@@ -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<SpriteRenderer>().sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.StopClean.png", 180f);
+                        copiedStartButton.SetActive(true);
+                        var startButtonText = copiedStartButton.GetComponentInChildren<TMPro.TextMeshPro>();
+                        startButtonText.text = "STOP";
+                        startButtonText.fontSize *= 0.8f;
+                        startButtonText.fontSizeMax = startButtonText.fontSize;
+                        startButtonText.gameObject.transform.localPosition = Vector3.zero;
+                        PassiveButton startButtonPassiveButton = copiedStartButton.GetComponent<PassiveButton>();
+
+                        void StopStartFunc() {
+                            __instance.ResetStartState();
+                            copiedStartButton.Destroy();
+                            startingTimer = 0;
+                        }
+                        startButtonPassiveButton.OnClick.AddListener((Action)(() => StopStartFunc()));
+                        __instance.StartCoroutine(Effects.Lerp(.1f, new System.Action<float>((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<SpriteRenderer>().sprite = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.StopClean.png", 180f);
+                        copiedStartButton.SetActive(true);
+                        var startButtonText = copiedStartButton.GetComponentInChildren<TMPro.TextMeshPro>();
+                        startButtonText.text = "STOP";
+                        startButtonText.fontSize *= 0.8f;
+                        startButtonText.fontSizeMax = startButtonText.fontSize;
+                        startButtonText.gameObject.transform.localPosition = Vector3.zero;
+                        PassiveButton startButtonPassiveButton = copiedStartButton.GetComponent<PassiveButton>();
+
+                        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<float>((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
index 71b46094c816c0f3cd1c606009677e95e962653f..101b97a88a1b8f6137e1aae84f98f3de0bbb2bf7 100644 (file)
@@ -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
+        }
     }
 }
 
index 30cc11da82d171baeecdb24274fcdc2ab00769d8..39b26a854730c50495f33c0000d85bbba1bc242b 100644 (file)
@@ -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);
 
index 456732f37fd76f15f166f959978e077655e26d13..68355cb65195ab942f6420df469a98337e62b943 100644 (file)
@@ -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 &&
index 1b2bfa1dd4c2ea9ec925a8cf25ad5e9ba613cf1c..7d25225497d034967c2ba4ee10607b1cd6849942 100644 (file)
@@ -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() {
index 4edcbdf1a58ccf1c6d57b86f2a26a46fcb8ffb3d..6153efdff7153f046070c99d6d94aca8a30d9f37 100644 (file)
@@ -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 (file)
index 0000000..d284137
Binary files /dev/null and b/TheOtherRoles/Resources/StopClean.png differ
index 6143779be7b0bc28ce35d8507595ddec8cc29b5f..8281ba9953e29b29955165500426021d7d0f78f1 100644 (file)
@@ -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) + ".";
index f5915799c51b6b01a215a2b4e7bab79ed33f0f02..16218068458fe0ba9aa435e6795f0f0f21eff765 100644 (file)
@@ -16,7 +16,7 @@
     </ItemGroup>
    
        <ItemGroup>
-               <PackageReference Include="AmongUs.GameLibs.Steam" Version="2023.11.28" />
+               <PackageReference Include="AmongUs.GameLibs.Steam" Version="2024.3.5" />
                <PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.671" />
                <PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.1.0-rc.1" />
         <PackageReference Include="Reactor" Version="2.2.0" />
index caacedcd6d1d9cd141323c82e5808f7f29d571e4..a7c7a1ccbdd701e3cf952a0de67a5a59e756b475 100644 (file)
@@ -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<EventTypes> eventQueue = null;
-    public static bool eventInvert = false;
+    }
 
     public static void clearAndReload() {
-        eventQueue = new List<EventTypes>();
-        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<float>((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<CachedPlayer> 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<CachedPlayer> 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<HudManager>.Instance.StartCoroutine(Effects.Lerp(eventDurations[(int)EventTypes.Animation], new Action<float>((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<HudManager>.Instance.roomTracker;
-                string lastRoom = "";
-                try { lastRoom = FastDestroyableSingleton<TranslationController>.Instance.GetString(tracker.LastRoom.RoomId); } catch { }
-                msg += lastRoom != ""? lastRoom : (TheOtherRoles.rnd.Next(2) > 0 ? "sus" : "safe");
-
-                FastDestroyableSingleton<HudManager>.Instance.Chat.AddChat(relevantPlayers[rnd.Next(relevantPlayers.Count)], $"{msg}");
-                break;
-            case EventTypes.Invert:
-                eventInvert = true;
-                FastDestroyableSingleton<HudManager>.Instance.StartCoroutine(Effects.Lerp(eventDurations[(int)EventTypes.Invert], new Action<float>((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
index 3a2a5c34f48d972ff7a99cf7cb4b18f7556e5374..e3543ec28cfd20171c4d5ed778bef3d99cefb669 100644 (file)
@@ -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",