diff --git a/NewMod/Components/Birthday/Toast.cs b/NewMod/Components/Birthday/Toast.cs new file mode 100644 index 0000000..7b3efe4 --- /dev/null +++ b/NewMod/Components/Birthday/Toast.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections; +using Reactor.Utilities; +using TMPro; +using UnityEngine; +using NewMod; +using NewMod.Utilities; +using Reactor.Utilities.Attributes; +using Il2CppInterop.Runtime.Attributes; + +[RegisterInIl2Cpp] +public class Toast(IntPtr ptr) : MonoBehaviour(ptr) +{ + public SpriteRenderer toastRend; + public TextMeshPro TimerText; + public bool isExpanded = false; + public void Awake() + { + toastRend = transform.Find("Background").GetComponent(); + TimerText = transform.Find("Timer").GetComponent(); + } + public static Toast CreateToast() + { + var gameObject = Instantiate(NewModAsset.Toast.LoadAsset(), HudManager.Instance.transform); + var toast = gameObject.AddComponent(); + return toast; + } + [HideFromIl2Cpp] + public void SetText(string msg) + { + if (TimerText) TimerText.text = msg; + } + + [HideFromIl2Cpp] + public void StartCountdown(TimeSpan duration) + { + Coroutines.Start(CoCountdown(duration)); + } + [HideFromIl2Cpp] + public IEnumerator CoCountdown(TimeSpan span) + { + var end = DateTime.UtcNow + span; + while (true) + { + var left = end - DateTime.UtcNow; + if (left.TotalSeconds <= 0) break; + + if (TimerText) + TimerText.text = Utils.FormatSpan(left); + + yield return new WaitForSecondsRealtime(0.2f); + } + if (TimerText) TimerText.text = "00:00:00:00"; + + DisconnectAllPlayers(); + } + [HideFromIl2Cpp] + public static void DisconnectAllPlayers() + { + var client = AmongUsClient.Instance; + if (client.GameState == InnerNet.InnerNetClient.GameStates.Started) return; + + client.LastCustomDisconnect = + "The Birthday Update is now live! Please restart to see the new lobby and menu style."; + client.HandleDisconnect(DisconnectReasons.Custom); + } +} \ No newline at end of file diff --git a/NewMod/DebugWindow.cs b/NewMod/DebugWindow.cs index 294e2f3..c183c0e 100644 --- a/NewMod/DebugWindow.cs +++ b/NewMod/DebugWindow.cs @@ -9,11 +9,6 @@ using NewMod.Roles.CrewmateRoles; using NewMod.Roles.ImpostorRoles; using NewMod.Roles.NeutralRoles; -using NewMod.Buttons.EnergyThief; -using NewMod.Buttons.SpecialAgent; -using NewMod.Buttons.Visionary; -using NewMod.Buttons.Prankster; -using NewMod.Buttons.Necromancer; using Reactor.Utilities.Attributes; using Reactor.Utilities.ImGui; using UnityEngine; @@ -42,16 +37,6 @@ public class DebugWindow(nint ptr) : MonoBehaviour(ptr) if (!isFreeplay) return; PlayerControl.LocalPlayer.RpcRemoveModifier(); } - if (GUILayout.Button("Disable Collider")) - { - if (!isFreeplay) return; - PlayerControl.LocalPlayer.Collider.enabled = false; - } - if (GUILayout.Button("Enable Collider")) - { - if (!isFreeplay) return; - PlayerControl.LocalPlayer.Collider.enabled = true; - } if (GUILayout.Button("Become Necromancer")) { if (!isFreeplay) return; @@ -79,27 +64,9 @@ public class DebugWindow(nint ptr) : MonoBehaviour(ptr) } if (GUILayout.Button("Increases Uses by 3")) { - var player = PlayerControl.LocalPlayer; - if (player.Data.Role is NecromancerRole) - { - CustomButtonSingleton.Instance.IncreaseUses(3); - } - else if (player.Data.Role is EnergyThief) - { - CustomButtonSingleton.Instance.IncreaseUses(3); - } - else if (player.Data.Role is SpecialAgent) - { - CustomButtonSingleton.Instance.IncreaseUses(3); - } - else if (player.Data.Role is Prankster) - { - CustomButtonSingleton.Instance.IncreaseUses(3); - } - else + foreach (var button in CustomButtonManager.Buttons) { - CustomButtonSingleton.Instance.IncreaseUses(3); - CustomButtonSingleton.Instance.IncreaseUses(3); + button.SetUses(3); } } if (GUILayout.Button("Randomly Cast a Vote")) diff --git a/NewMod/Features/CustomPlayerTag.cs b/NewMod/Features/CustomPlayerTag.cs new file mode 100644 index 0000000..5833f7e --- /dev/null +++ b/NewMod/Features/CustomPlayerTag.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using HarmonyLib; +using Hazel; +using MiraAPI.Events; +using MiraAPI.Events.Vanilla.Meeting; +using Reactor.Utilities; + +namespace NewMod.Features +{ + public static class CustomPlayerTag + { + public enum TagType : byte + { + Player, + Dev, + Creator, + Tester, + Staff, + Contributor, + Host, + AOUDev + } + + public static readonly Dictionary DefaultHex = new() + { + { TagType.Player, "c0c0c0" }, + { TagType.Dev, "ff4d4d" }, + { TagType.Creator, "ffb000" }, + { TagType.Tester, "00e0ff" }, + { TagType.Staff, "9b59b6" }, + { TagType.Contributor, "7ee081" }, + { TagType.Host, "ff7f50" }, + { TagType.AOUDev, "00ffb3" } + }; + + public static string DisplayName(TagType t) => t switch + { + TagType.Player => "Player", + TagType.Dev => "Developer", + TagType.Creator => "Creator", + TagType.Tester => "Tester", + TagType.Staff => "Staff", + TagType.Contributor => "Contributor", + TagType.Host => "Host", + TagType.AOUDev => "AOU Dev", + _ => "" + }; + + public static string Format(TagType t, string hex) + { + string color = string.IsNullOrWhiteSpace(hex) + ? (DefaultHex.TryGetValue(t, out var h) ? h : "ffffff") + : hex; + string label = DisplayName(t); + return $"\n{label}"; + } + public static TagType GetTag(string friendCode) + { + if (string.Equals(friendCode, "puncool#9009", StringComparison.OrdinalIgnoreCase)) return TagType.Creator; + if (string.Equals(friendCode, "peaktipple#8186", StringComparison.OrdinalIgnoreCase)) return TagType.Dev; + if (string.Equals(friendCode, "shinyrake#9382", StringComparison.OrdinalIgnoreCase)) return TagType.Dev; + if (string.Equals(friendCode, "dimpledue#6629", StringComparison.OrdinalIgnoreCase)) return TagType.AOUDev; + return TagType.Player; + } + + [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RpcSetName))] + public static class RpcSetNamePatch + { + public static bool Prefix(PlayerControl __instance, ref string name) + { + var friendCode = __instance.FriendCode; + TagType tag = GetTag(friendCode); + + var host = GameData.Instance.GetHost(); + bool isHost = host.PlayerId == __instance.PlayerId; + + string baseName = name.Split('\n')[0]; + + string newName = baseName; + if (isHost) + newName += Format(TagType.Host, DefaultHex[TagType.Host]); + if (tag != TagType.Player) + newName += Format(tag, DefaultHex[tag]); + else + newName += Format(TagType.Player, DefaultHex[TagType.Player]); + + Logger.Instance.LogInfo($"Player {__instance.PlayerId} '{baseName}' " + $"FriendCode={friendCode}, Host={isHost}, Tag={DisplayName(tag)} " + $"FinalName='{newName}'"); + + __instance.SetName(newName); + + var writer = AmongUsClient.Instance.StartRpcImmediately(__instance.NetId, (byte)RpcCalls.SetName, SendOption.Reliable, -1); + writer.Write(__instance.Data.NetId); + writer.Write(newName); + AmongUsClient.Instance.FinishRpcImmediately(writer); + + return false; + } + } + + [RegisterEvent] + public static void OnMeetingStart(StartMeetingEvent evt) + { + var host = GameData.Instance.GetHost(); + + foreach (var ps in evt.MeetingHud.playerStates) + { + string baseName = ps.NameText.text.Split('\n')[0]; + bool isHost = ps.TargetPlayerId == host.PlayerId; + + TagType tag = GetTag(GameData.Instance.GetPlayerById(ps.TargetPlayerId).FriendCode); + string newName = baseName; + + if (isHost) + newName += Format(TagType.Host, DefaultHex[TagType.Host]); + + if (tag != TagType.Player) + newName += Format(tag, DefaultHex[tag]); + else + newName += Format(TagType.Player, DefaultHex[TagType.Player]); + + ps.NameText.text = newName; + } + } + } +} diff --git a/NewMod/NewMod.cs b/NewMod/NewMod.cs index 46aa10a..1d2a7a4 100644 --- a/NewMod/NewMod.cs +++ b/NewMod/NewMod.cs @@ -18,7 +18,6 @@ using MiraAPI.Events.Vanilla.Gameplay; using NewMod.Roles.NeutralRoles; using MiraAPI.Roles; -using System; using MiraAPI.Hud; using UnityEngine.Events; using NewMod.Options.Roles.OverloadOptions; diff --git a/NewMod/NewMod.csproj b/NewMod/NewMod.csproj index bf3c6b2..60a4dc4 100644 --- a/NewMod/NewMod.csproj +++ b/NewMod/NewMod.csproj @@ -1,6 +1,6 @@ - 1.2.1 + 1.2.4 dev NewMod is a mod for Among Us that introduces a variety of new roles, unique abilities CallofCreator @@ -21,6 +21,7 @@ + @@ -31,10 +32,4 @@ - - - - ..\libs\MiraAPI.dll - - diff --git a/NewMod/NewModAsset.cs b/NewMod/NewModAsset.cs index 2332d76..d428f25 100644 --- a/NewMod/NewModAsset.cs +++ b/NewMod/NewModAsset.cs @@ -1,14 +1,22 @@ +using Reactor.Utilities; using MiraAPI.Utilities.Assets; +using UnityEngine; namespace NewMod; public static class NewModAsset { + public static AssetBundle Bundle = AssetBundleManager.Load("newmod"); // Miscellaneous public static LoadableResourceAsset Banner { get; } = new("NewMod.Resources.optionImage.png"); public static LoadableResourceAsset Arrow { get; } = new("NewMod.Resources.Arrow.png"); public static LoadableResourceAsset ModLogo { get; } = new("NewMod.Resources.Logo.png"); + public static LoadableResourceAsset CustomCursor { get; } = new("NewMod.Resources.cursor.png"); + // NewMod's First Birthday Assets + public static LoadableResourceAsset MainMenuBG { get; } = new("NewMod.Resources.Birthday.newmod-birthday-v1.png"); + public static LoadableAsset CustomLobby { get; } = new LoadableBundleAsset("CustomLobby", Bundle); + public static LoadableAsset Toast { get; } = new LoadableBundleAsset("Toast", Bundle); // Button icons public static LoadableResourceAsset SpecialAgentButton { get; } = new("NewMod.Resources.givemission.png"); public static LoadableResourceAsset ShowScreenshotButton { get; } = new("NewMod.Resources.showscreenshot.png"); diff --git a/NewMod/NewModDateTime.cs b/NewMod/NewModDateTime.cs index 618f2b2..4514503 100644 --- a/NewMod/NewModDateTime.cs +++ b/NewMod/NewModDateTime.cs @@ -1,14 +1,26 @@ using System; -namespace NewMod; -public static class NewModDateTime +namespace NewMod { - public static DateTime NewModBirthday + public static class NewModDateTime { - get + public static DateTime NewModBirthday { - var thisYear = new DateTime(DateTime.Now.Year, 8, 28); - return DateTime.Now <= thisYear ? thisYear : new DateTime(DateTime.Now.Year + 1, 8, 28); + get + { + var thisYear = new DateTime(DateTime.Now.Year, 8, 28, 16, 0, 0); + return DateTime.Now <= thisYear ? thisYear : new DateTime(DateTime.Now.Year + 1, 8, 28); + } } + public static DateTime NewModBirthdayWeekEnd + { + get + { + return NewModBirthday.AddDays(7); + } + } + + public static bool IsNewModBirthdayWeek => + DateTime.Now >= NewModBirthday && DateTime.Now <= NewModBirthdayWeekEnd; } } diff --git a/NewMod/Options/CompatibilityOptions.cs b/NewMod/Options/CompatibilityOptions.cs index 7db18a3..063e0ff 100644 --- a/NewMod/Options/CompatibilityOptions.cs +++ b/NewMod/Options/CompatibilityOptions.cs @@ -7,29 +7,8 @@ public class CompatibilityOptions : AbstractOptionGroup { public override string GroupName => "Mod Compatibility"; public override Func GroupVisible => ModCompatibility.IsLaunchpadLoaded; - public ModdedToggleOption AllowRevenantHitmanCombo { get; } = new("Allow Revenant & Hitman in Same Match", false) - { - ChangedEvent = value => - { - HudManager.Instance.ShowPopUp(value - ? "You enabled the Revenant & Hitman combo. This may break game balance!" - : "Revenant & Hitman combo disabled. Only one will be allowed per match."); - } - }; - public ModdedEnumOption Compatibility { get; } = new("Mod Compatibility", ModPriority.PreferNewMod) - { - ChangedEvent = value => - { - HudManager.Instance.ShowPopUp( - value switch - { - ModPriority.PreferNewMod => "You selected 'PreferNewMod'. Medic will be disabled.\n" + - "Switch to 'Prefer LaunchpadReloaded' to enable Medic and disable Necromancer.", - ModPriority.PreferLaunchpadReloaded => "You selected 'PreferLaunchpadReloaded'. Necromancer will be disabled.\n" + - "Switch to 'PreferNewMod' to enable Necromancer and disable Medic.", - }); - } - }; + public ModdedToggleOption AllowRevenantHitmanCombo { get; } = new("Allow Revenant & Hitman in Same Match", false); + public ModdedEnumOption Compatibility { get; } = new("Mod Compatibility", ModPriority.PreferNewMod); public enum ModPriority { PreferNewMod, diff --git a/NewMod/Patches/Birthday/LobbyPatch.cs b/NewMod/Patches/Birthday/LobbyPatch.cs new file mode 100644 index 0000000..276f4a5 --- /dev/null +++ b/NewMod/Patches/Birthday/LobbyPatch.cs @@ -0,0 +1,83 @@ +using UnityEngine; +using HarmonyLib; +using System; +using Object = UnityEngine.Object; +using Reactor.Utilities.Extensions; +using System.IO; + +namespace NewMod.Patches.Birthday +{ + [HarmonyPatch(typeof(LobbyBehaviour))] + public static class LobbyPatch + { + public static GameObject CustomLobby; + public static readonly Vector2[] BirthdaySpawns = + [ + new Vector2(-0.6738f, -2.5016f), + new Vector2(-0.7068f, -2.4353f), + new Vector2(-0.4568f, -2.4353f), + new Vector2( 0.8968f, -2.2000f), + new Vector2( 1.6468f, -1.9000f), + new Vector2( 1.5218f, -1.9139f), + new Vector2( 2.5000f, -1.5155f), + new Vector2( 3.0000f, -1.5000f), + new Vector2( 3.0000f, -1.1000f) + ]; + + [HarmonyPatch(nameof(LobbyBehaviour.Start))] + [HarmonyPrefix] + public static bool StartPrefix(LobbyBehaviour __instance) + { + if (!NewModDateTime.IsNewModBirthdayWeek) return true; + + CustomLobby = Object.Instantiate(NewModAsset.CustomLobby.LoadAsset()); + CustomLobby.transform.SetParent(__instance.transform, false); + CustomLobby.transform.localPosition = Vector3.zero; + return true; + } + + [HarmonyPatch(nameof(LobbyBehaviour.Start))] + [HarmonyPostfix] + public static void Postfix(LobbyBehaviour __instance) + { + var toast = Toast.CreateToast(); + toast.transform.localPosition = new Vector3(-4.4217f, 2.2098f, 0f); + + if (DateTime.Now < NewModDateTime.NewModBirthday) + { + TimeSpan countdown = NewModDateTime.NewModBirthday - DateTime.Now; + toast.StartCountdown(countdown); + } + + if (!NewModDateTime.IsNewModBirthdayWeek) return; + + var originalLobby = "Lobby(Clone)"; + GameObject.Find(originalLobby).GetComponent().Destroy(); + GameObject.Find(originalLobby + "/Background").SetActive(false); + GameObject.Find(originalLobby + "/ShipRoom").SetActive(false); + GameObject.Find(originalLobby + "/RightEngine").SetActive(false); + GameObject.Find(originalLobby + "/LeftEngine").SetActive(false); + GameObject.Find(originalLobby + "/SmallBox").SetActive(false); + GameObject.Find(originalLobby + "/Leftbox").SetActive(false); + GameObject.Find(originalLobby + "/RightBox").SetActive(false); + + var wardrobe = GameObject.Find(originalLobby + "/panel_Wardrobe"); + if (wardrobe != null) + { + wardrobe.transform.localPosition = new Vector3(4.6701f, -0.0529f, 0f); + wardrobe.transform.localScale = new Vector3(0.7301f, 0.7f, 1f); + } + __instance.SpawnPositions = new Vector2[BirthdaySpawns.Length]; + + for (int i = 0; i < BirthdaySpawns.Length; i++) + { + __instance.SpawnPositions[i] = BirthdaySpawns[i]; + } + } + [HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.Start))] + public static void Prefix(ShipStatus __instance) + { + CustomLobby.DestroyImmediate(); + } + } +} diff --git a/NewMod/Patches/Compatibility/LaunchpadCompatibility.cs b/NewMod/Patches/Compatibility/LaunchpadCompatibility.cs index e58a5a7..ad3d5ba 100644 --- a/NewMod/Patches/Compatibility/LaunchpadCompatibility.cs +++ b/NewMod/Patches/Compatibility/LaunchpadCompatibility.cs @@ -1,4 +1,3 @@ -using HarmonyLib; using NewMod.Roles.ImpostorRoles; using System.Reflection; using TMPro; @@ -33,6 +32,7 @@ static bool Prefix(object __instance) return true; } } + public static class LaunchpadHackTextPatch { static MethodBase TargetMethod() @@ -57,4 +57,28 @@ static void Postfix(object __instance) } } } + + public static class LaunchpadTagSpacingPatch + { + static MethodBase TargetMethod() + { + if (!ModCompatibility.LaunchpadLoaded(out var asm) || asm == null) + return null; + + var type = asm.GetType("LaunchpadReloaded.Components.PlayerTagManager"); + var method = type?.GetMethod("UpdatePosition", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + return method; + } + + static void Postfix(object __instance) + { + var type = __instance.GetType(); + var tagHolderObj = type.GetField("tagHolder", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(__instance); + if (tagHolderObj is Transform holder) + { + holder.localPosition = new Vector3(0f, 0.5491f, -0.35f); + holder.localScale = new Vector3(0.7455f, 1f, 1f); + } + } + } } diff --git a/NewMod/Patches/Compatibility/StartGamePatch.cs b/NewMod/Patches/Compatibility/StartGamePatch.cs index d88e8be..e5fce8c 100644 --- a/NewMod/Patches/Compatibility/StartGamePatch.cs +++ b/NewMod/Patches/Compatibility/StartGamePatch.cs @@ -30,14 +30,6 @@ public static bool Prefix(AmongUsClient __instance) } } } - if (settings.Compatibility == CompatibilityOptions.ModPriority.PreferNewMod) - { - ModCompatibility.DisableRole("Medic", ModCompatibility.LaunchpadReloaded_GUID); - } - else - { - ModCompatibility.DisableRole("Necromancer", NewMod.Id); - } return true; } } diff --git a/NewMod/Patches/CustomPlayerTagPatch.cs b/NewMod/Patches/CustomPlayerTagPatch.cs new file mode 100644 index 0000000..6ddfa37 --- /dev/null +++ b/NewMod/Patches/CustomPlayerTagPatch.cs @@ -0,0 +1,61 @@ +using HarmonyLib; +using UnityEngine; + +namespace NewMod.Patches +{ + [HarmonyPatch] + public static class CustomPlayerTagPatch + { + public const float Padding = 0.02f; + private static float targetY; + + [HarmonyPatch(typeof(ChatBubble), nameof(ChatBubble.SetName))] + [HarmonyPostfix] + public static void SetNamePostfix(ChatBubble __instance, string playerName, bool isDead, bool voted, Color color) + { + __instance.NameText.ForceMeshUpdate(); + + float nameBottom = __instance.NameText.textBounds.min.y; + float nameLocalY = __instance.NameText.transform.localPosition.y; + + targetY = nameLocalY + nameBottom - Padding; + } + + [HarmonyPatch(typeof(ChatBubble), nameof(ChatBubble.SetText))] + [HarmonyPostfix] + public static void SetTextPostfix(ChatBubble __instance, string chatText) + { + var pos = __instance.TextArea.transform.localPosition; + pos.y = targetY; + + __instance.TextArea.transform.localPosition = pos; + __instance.AlignChildren(); + } + [HarmonyPatch(typeof(NotificationPopper), nameof(NotificationPopper.AddDisconnectMessage))] + [HarmonyPrefix] + public static void StartPrefix(NotificationPopper __instance, ref string item) + { + item = item.Replace("\r", "").Replace("\n", ""); + while (item.Contains(" ")) item = item.Replace(" ", " "); + + item = item.Replace("", ""); + item = item.Replace("", ""); + + _ = item.TrimEnd(); + } + [HarmonyPatch(typeof(ChatNotification), nameof(ChatNotification.SetUp))] + [HarmonyPrefix] + public static void StartPrefix(ChatNotification __instance, PlayerControl sender, ref string text) + { + if (text.Contains('\n')) + text = text.Replace("\r", "").Replace("\n", " "); + + text = text.Replace("", ""); + + while (text.Contains(" ")) + text = text.Replace(" ", " "); + + text = text.TrimEnd(); + } + } +} diff --git a/NewMod/Patches/GameOptionsMenu.cs b/NewMod/Patches/GameOptionsMenu.cs new file mode 100644 index 0000000..90ff21d --- /dev/null +++ b/NewMod/Patches/GameOptionsMenu.cs @@ -0,0 +1,27 @@ +using HarmonyLib; +using MiraAPI.GameOptions; +using NewMod.Options; + +namespace NewMod.Patches; + +[HarmonyPatch(typeof(GameOptionsMenu), nameof(GameOptionsMenu.CloseMenu))] +public static class GameOptionsMenu_CloseMenu_Patch +{ + public static void Postfix(GameOptionsMenu __instance) + { + var opts = OptionGroupSingleton.Instance; + + if (opts.AllowRevenantHitmanCombo.Value) + { + HudManager.Instance.ShowPopUp( + "You enabled the Revenant & Hitman combo. This may break game balance!" + ); + } + else + { + HudManager.Instance.ShowPopUp( + "Revenant & Hitman combo disabled. Only one will be allowed per match." + ); + } + } +} diff --git a/NewMod/Patches/MainMenuPatch.cs b/NewMod/Patches/MainMenuPatch.cs index e4cbe8b..73b5ab3 100644 --- a/NewMod/Patches/MainMenuPatch.cs +++ b/NewMod/Patches/MainMenuPatch.cs @@ -3,23 +3,82 @@ namespace NewMod.Patches { - [HarmonyPatch(typeof(MainMenuManager), nameof(MainMenuManager.Start))] + [HarmonyPatch(typeof(MainMenuManager))] [HarmonyPriority(Priority.VeryHigh)] public static class MainMenuPatch { public static SpriteRenderer LogoSprite; + public static Texture2D _cachedCursor; + public static Transform RightPanel; + [HarmonyPatch(nameof(MainMenuManager.Start))] [HarmonyPostfix] public static void StartPostfix(MainMenuManager __instance) { - var newparent = __instance.transform.FindChild("MainCanvas/MainPanel/RightPanel"); - var Logo = new GameObject("NewModLogo"); - Logo.transform.SetParent(newparent, false); - Logo.transform.localPosition = new(2.34f, -0.7136f, 1f); - LogoSprite = Logo.AddComponent(); - LogoSprite.sprite = NewModAsset.ModLogo.LoadAsset(); - + if (_cachedCursor == null) + { + _cachedCursor = NewModAsset.CustomCursor.LoadAsset().texture; + } + if (_cachedCursor != null) + { + Cursor.SetCursor(_cachedCursor, CursorMode.Auto); + } + RightPanel = __instance.transform.Find("MainUI/AspectScaler/RightPanel"); + + if (NewModDateTime.IsNewModBirthdayWeek) + { + RightPanel.gameObject.SetActive(false); + __instance.screenTint.enabled = false; + + var auLogo = GameObject.Find("LOGO-AU"); + auLogo.transform.localPosition = new Vector3(-3.50f, 1.85f, 0f); + auLogo.transform.localScale = new Vector3(0.32f, 0.32f, 1f); + + var newmodLogo = new GameObject("NewModLogo"); + var parent = __instance.transform.Find("MainUI/AspectScaler/LeftPanel"); + newmodLogo.transform.SetParent(parent, false); + newmodLogo.transform.localPosition = new Vector3(-0.1427f, 2.8094f, 0.7182f); + newmodLogo.transform.localScale = new Vector3(0.3711f, 0.4214f, 1.16f); + LogoSprite = newmodLogo.AddComponent(); + LogoSprite.sprite = NewModAsset.ModLogo.LoadAsset(); + + var auBG = __instance.transform.Find("MainUI/AspectScaler/BackgroundTexture").GetComponent(); + if (auBG != null) + { + auBG.sprite = NewModAsset.MainMenuBG.LoadAsset(); + } + } + else + { + // Preserve the old layout when it's not the birthday update + var Logo = new GameObject("NewModLogo"); + Logo.transform.SetParent(__instance.transform.Find("MainCanvas/MainPanel/RightPanel"), false); + Logo.transform.localPosition = new Vector3(2.34f, -0.7136f, 1f); + LogoSprite = Logo.AddComponent(); + LogoSprite.sprite = NewModAsset.ModLogo.LoadAsset(); + } ModCompatibility.Initialize(); } + [HarmonyPatch(nameof(MainMenuManager.OpenGameModeMenu))] + [HarmonyPatch(nameof(MainMenuManager.OpenCredits))] + [HarmonyPatch(nameof(MainMenuManager.OpenAccountMenu))] + [HarmonyPatch(nameof(MainMenuManager.OpenCreateGame))] + [HarmonyPatch(nameof(MainMenuManager.OpenEnterCodeMenu))] + [HarmonyPatch(nameof(MainMenuManager.OpenOnlineMenu))] + [HarmonyPatch(nameof(MainMenuManager.OpenFindGame))] + public static void Postfix(MainMenuManager __instance) + { + if (!NewModDateTime.IsNewModBirthdayWeek) return; + + RightPanel.gameObject.SetActive(true); + } + [HarmonyPatch(nameof(MainMenuManager.ResetScreen))] + [HarmonyPostfix] + public static void ResetScreenPostfix(MainMenuManager __instance) + { + if (!NewModDateTime.IsNewModBirthdayWeek) return; + + RightPanel.gameObject.SetActive(false); + } } } diff --git a/NewMod/Resources/Birthday/newmod-birthday-v1.png b/NewMod/Resources/Birthday/newmod-birthday-v1.png new file mode 100644 index 0000000..58f17cf Binary files /dev/null and b/NewMod/Resources/Birthday/newmod-birthday-v1.png differ diff --git a/NewMod/Resources/Logo.png b/NewMod/Resources/Logo.png index c7941e9..7d4c6bd 100644 Binary files a/NewMod/Resources/Logo.png and b/NewMod/Resources/Logo.png differ diff --git a/NewMod/Resources/cursor.png b/NewMod/Resources/cursor.png new file mode 100644 index 0000000..0591eaa Binary files /dev/null and b/NewMod/Resources/cursor.png differ diff --git a/NewMod/Resources/newmod-android.bundle b/NewMod/Resources/newmod-android.bundle new file mode 100644 index 0000000..961e761 Binary files /dev/null and b/NewMod/Resources/newmod-android.bundle differ diff --git a/NewMod/Resources/newmod-win.bundle b/NewMod/Resources/newmod-win.bundle new file mode 100644 index 0000000..d56ea00 Binary files /dev/null and b/NewMod/Resources/newmod-win.bundle differ diff --git a/NewMod/Roles/INewModRole.cs b/NewMod/Roles/INewModRole.cs index 584c83f..103c077 100644 --- a/NewMod/Roles/INewModRole.cs +++ b/NewMod/Roles/INewModRole.cs @@ -5,6 +5,7 @@ namespace NewMod.Roles { + #pragma warning disable CS0108 public interface INewModRole : ICustomRole { public static StringBuilder GetRoleTabText(ICustomRole role) diff --git a/NewMod/Utilities/Utils.cs b/NewMod/Utilities/Utils.cs index 2aa4ea6..fbbe117 100644 --- a/NewMod/Utilities/Utils.cs +++ b/NewMod/Utilities/Utils.cs @@ -1072,6 +1072,19 @@ public static IEnumerator CoShakeCamera(FollowerCamera cam, float duration) } cam.transform.localPosition = originalPos; } + + /// + /// Formats a into a string with the format: + /// dd:hh:mm:ss. + /// + /// The to format. + public static string FormatSpan(System.TimeSpan t) + { + int dd = Mathf.Max(0, t.Days); + int hh = Mathf.Clamp(t.Hours, 0, 99); + int mm = Mathf.Clamp(t.Minutes, 0, 59); + int ss = Mathf.Clamp(t.Seconds, 0, 59); + return $"{dd:D1}:{hh:D2}:{mm:D2}:{ss:D2}"; + } } } - diff --git a/README.md b/README.md index 7409425..bed53c1 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,18 @@ GitHub stars + + Downloads + GitHub forks License + + Join +

@@ -39,12 +45,13 @@ | v1.0.0 | 2024.8.13 & 2024.10.29 | [Download](https://github.com/CallOfCreator/NewMod/releases/download/V1.0.0/NewMod.dll) | | v1.1.0 | 2024.11.26 | [Download](https://github.com/CallOfCreator/NewMod/releases/download/V1.1.0/NewMod.dll) | | v1.2.0 | v16.1.0 (2025.6.10) | [Download](https://github.com/CallOfCreator/NewMod/releases/download/V1.2.0/NewMod.zip) +| v1.2.1 | v16.1.0 (2025.6.10) | [Download](https://github.com/CallOfCreator/NewMod/releases/download/V1.2.1/NewMod.zip) | --- # 📥 Installation -### For installation instructions, please visit: https://newmod.up.railway.app +### For installation instructions, please visit: https://newmodau.vercel.app --- @@ -54,7 +61,7 @@ - **🖥️ Description:** Allows crewmates to access security cameras from anywhere. - **👀 Strategic Use:** Monitor other players without needing to go to the security room. -### For role information, please visit: https://newmod.up.railway.app/roles +### For role information, please visit: https://newmodau.vercel.app/roles --- # 🔗 Compatibility