diff --git a/MiraAPI.Example/CustomRole.cs b/MiraAPI.Example/CustomRole.cs index bb4732c..974abc0 100644 --- a/MiraAPI.Example/CustomRole.cs +++ b/MiraAPI.Example/CustomRole.cs @@ -1,15 +1,21 @@ -using MiraAPI.Roles; +using MiraAPI.GameOptions.Attributes; +using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; using UnityEngine; namespace MiraAPI.Example { [RegisterCustomRole] - public class CustomRole : CrewmateRole, ICustomRole + public class CustomRole : ImpostorRole, ICustomRole { - public string RoleName => "Test Role"; - public string RoleDescription => "Role decs?"; - public string RoleLongDescription => "Extra role decs?"; + public string RoleName => "fortnite killer"; + public string RoleLongDescription => "ok so your objective is to eliminate everyone with your fortnite tactical shotgun, good luck"; + public string RoleDescription => RoleLongDescription; public Color RoleColor => Palette.Orange; - public ModdedRoleTeams Team => ModdedRoleTeams.Neutral; + public ModdedRoleTeams Team => ModdedRoleTeams.Impostor; + public LoadableAsset OptionsScreenshot => MiraAssets.Banner; + + [ModdedNumberOption("Testing level", min: 3, max: 9, roleType: typeof(CustomRole))] public float testingLevel { get; set; } = 4; + } } diff --git a/MiraAPI.Example/CustomRole2.cs b/MiraAPI.Example/CustomRole2.cs new file mode 100644 index 0000000..ba2bba1 --- /dev/null +++ b/MiraAPI.Example/CustomRole2.cs @@ -0,0 +1,21 @@ +using MiraAPI.GameOptions.Attributes; +using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; +using UnityEngine; + +namespace MiraAPI.Example +{ + [RegisterCustomRole] + public class CustomRole2 : CrewmateRole, ICustomRole + { + public string RoleName => "fortnite defender"; + public string RoleLongDescription => "ok so your objective is to save everyone from the fortnite killer"; + public string RoleDescription => RoleLongDescription; + public Color RoleColor => Palette.AcceptedGreen; + public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate; + public LoadableAsset OptionsScreenshot => MiraAssets.Banner; + + [ModdedToggleOption("Teleportation ability", roleType: typeof(CustomRole2))] public bool teleport { get; set; } = true; + + } +} diff --git a/MiraAPI.Example/CustomRole3.cs b/MiraAPI.Example/CustomRole3.cs new file mode 100644 index 0000000..109f972 --- /dev/null +++ b/MiraAPI.Example/CustomRole3.cs @@ -0,0 +1,18 @@ +using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; +using UnityEngine; + +namespace MiraAPI.Example +{ + [RegisterCustomRole] + public class CustomRole3 : CrewmateRole, ICustomRole + { + public string RoleName => "fortnite helper"; + public string RoleLongDescription => "ok so your objective is to help"; + public string RoleDescription => RoleLongDescription; + public Color RoleColor => Palette.Brown; + public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate; + public LoadableAsset OptionsScreenshot => MiraAssets.Banner; + + } +} diff --git a/MiraAPI/MiraAPI.csproj b/MiraAPI/MiraAPI.csproj index b3ce2ed..e74fa8c 100644 --- a/MiraAPI/MiraAPI.csproj +++ b/MiraAPI/MiraAPI.csproj @@ -16,11 +16,17 @@ + + + + + + diff --git a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs index fdaf974..2543232 100644 --- a/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs +++ b/MiraAPI/Patches/Options/GameOptionsMenuPatch.cs @@ -29,7 +29,7 @@ public static class GameOptionsMenuPatch categoryHeaderMasked.transform.localPosition = new Vector3(-0.903f, num, -2f); num -= 0.63f; - foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is not null && opt.Group.GroupName == group.GroupName)) + foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is not null && opt.AdvancedRole is null && opt.Group.GroupName == group.GroupName)) { OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer); newOpt.transform.localPosition = new Vector3(0.952f, num, -2f); @@ -60,7 +60,7 @@ public static class GameOptionsMenuPatch num -= 0.63f; - foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is null)) + foreach (var opt in ModdedOptionsManager.Options.Where(opt => opt.Group is null && opt.AdvancedRole is null)) { OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer); newOpt.transform.localPosition = new Vector3(0.952f, num, -2f); diff --git a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs index 34e178e..1e9fd6c 100644 --- a/MiraAPI/Patches/Options/GameSettingMenuPatches.cs +++ b/MiraAPI/Patches/Options/GameSettingMenuPatches.cs @@ -7,6 +7,7 @@ using System.Linq; using TMPro; using UnityEngine; using UnityEngine.Events; +using static UnityEngine.UI.Button; namespace MiraAPI.Patches.Options; @@ -42,14 +43,9 @@ public static class GameSettingMenuPatches nextButton.transform.FindChild("Inactive").gameObject.GetComponent().sprite = MiraAssets.NextButton.LoadAsset(); nextButton.transform.FindChild("Active").gameObject.GetComponent().sprite = MiraAssets.NextButtonActive.LoadAsset(); nextButton.gameObject.GetComponent().DestroyImmediate(); - nextButton.gameObject.GetComponent().DestroyImmediate(); - PassiveButton passiveButton = nextButton.gameObject.AddComponent(); - passiveButton.HoverSound = __instance.BackButton.GetComponent().HoverSound; - passiveButton.ClickSound = __instance.BackButton.GetComponent().ClickSound; - - passiveButton.activeSprites = nextButton.transform.FindChild("Active").gameObject; - passiveButton.inactiveSprites = nextButton.transform.FindChild("Inactive").gameObject; + PassiveButton passiveButton = nextButton.gameObject.GetComponent(); + passiveButton.OnClick = new ButtonClickedEvent(); passiveButton.OnClick.AddListener((UnityAction)(() => { if (currentSelectedMod != MiraPluginManager.Instance.RegisteredPlugins.Count) diff --git a/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs new file mode 100644 index 0000000..73b0fda --- /dev/null +++ b/MiraAPI/Patches/Options/RoleSettingMenuPatches.cs @@ -0,0 +1,186 @@ +using HarmonyLib; +using MiraAPI.Roles; +using MiraAPI.Utilities.Assets; +using Reactor.Utilities.Extensions; +using System; +using System.Linq; +using TMPro; +using UnityEngine; +using UnityEngine.Events; +using static UnityEngine.UI.Button; +using Object = UnityEngine.Object; + +namespace MiraAPI.Patches.Options +{ + [HarmonyPatch(typeof(RolesSettingsMenu))] + public static class RoleSettingMenuPatches + { + [HarmonyPrefix, HarmonyPatch(nameof(RolesSettingsMenu.SetQuotaTab))] + public static bool PatchStart(RolesSettingsMenu __instance) + { + if (GameSettingMenuPatches.currentSelectedMod == 0) + { + __instance.scrollBar.transform.localPosition = new Vector3(-1.4957f, 0.657f, -4); + return true; + } + + float num = 0.662f; + + __instance.AllButton.transform.parent.gameObject.SetActive(false); + __instance.AllButton.gameObject.SetActive(false); + __instance.scrollBar.transform.localPosition = new Vector3(-1.4957f, 1.5261f, -4); + + CategoryHeaderEditRole categoryHeaderEditRole = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform); + categoryHeaderEditRole.SetHeader(StringNames.CrewmateRolesHeader, 20); + categoryHeaderEditRole.transform.localPosition = new Vector3(4.986f, num, -2f); + num -= 0.522f; + int num3 = 0; + + foreach (var role in GameSettingMenuPatches.selectedMod.CustomRoles.Where(role => role.Value.TeamType == RoleTeamTypes.Crewmate)) + { + CreateQuotaOption(__instance, role.Value, ref num, num3); + num3++; + } + + num -= 0.4f; + CategoryHeaderEditRole categoryHeaderEditRole2 = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform); + categoryHeaderEditRole2.SetHeader(StringNames.ImpostorRolesHeader, 20); + categoryHeaderEditRole2.transform.localPosition = new Vector3(4.986f, num, -2f); + num -= 0.522f; + + foreach (var role in GameSettingMenuPatches.selectedMod.CustomRoles.Where(role => role.Value.TeamType == RoleTeamTypes.Impostor)) + { + CreateQuotaOption(__instance, role.Value, ref num, num3); + num3++; + } + + return false; + } + + [HarmonyPrefix, HarmonyPatch(nameof(RolesSettingsMenu.Update))] + public static bool UpdatePatch() + { + if (GameSettingMenuPatches.currentSelectedMod == 0) + { + return true; + } + + return false; + } + + private static void ValueChanged(OptionBehaviour obj) + { + var roleSetting = obj.Cast(); + var role = roleSetting.Role as ICustomRole; + if (role.HideSettings) return; + + MiraAPIPlugin.Instance.Config.TryGetEntry(role.NumConfigDefinition, out var numEntry); + numEntry.Value = roleSetting.RoleMaxCount; + + MiraAPIPlugin.Instance.Config.TryGetEntry(role.ChanceConfigDefinition, out var chanceEntry); + chanceEntry.Value = roleSetting.RoleChance; + + roleSetting.UpdateValuesAndText(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions); + + /* if (AmongUsClient.Instance.AmHost) + { + Rpc.Instance.Send(new SyncRoleOptionsRpc.Data((ushort)roleSetting.Role.Role, numEntry.Value, chanceEntry.Value)); + }*/ + GameOptionsManager.Instance.GameHostOptions = GameOptionsManager.Instance.CurrentGameOptions; + return; + } + + private static void CreateAdvancedSettings(RolesSettingsMenu __instance, RoleBehaviour role) + { + for (int i = 0; i < __instance.advancedSettingChildren.Count; i++) + { + Object.Destroy(__instance.advancedSettingChildren[i].gameObject); + } + + __instance.advancedSettingChildren.Clear(); + + float num = -0.872f; + foreach (var option in GameSettingMenuPatches.selectedMod.Options.Where(option => option.AdvancedRole == role.GetType())) + { + OptionBehaviour newOpt = option.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.AdvancedRolesSettings.transform); + newOpt.transform.localPosition = new Vector3(2.17f, num, -2f); + newOpt.SetClickMask(__instance.ButtonClickMask); + + SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren(true); + for (int i = 0; i < componentsInChildren.Length; i++) + { + componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20); + } + foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren(true)) + { + textMeshPro.fontMaterial.SetFloat("_StencilComp", 3f); + textMeshPro.fontMaterial.SetFloat("_Stencil", 20); + } + + newOpt.LabelBackground.enabled = false; + __instance.advancedSettingChildren.Add(newOpt); + + num += -0.45f; + newOpt.Initialize(); + } + + __instance.scrollBar.CalculateAndSetYBounds((float)(__instance.advancedSettingChildren.Count + 3), 1f, 6f, 0.45f); + } + private static void ChangeTab(RoleBehaviour role, RolesSettingsMenu __instance) + { + ICustomRole customRole = role as ICustomRole; + __instance.roleDescriptionText.text = customRole.RoleLongDescription; + __instance.roleTitleText.text = DestroyableSingleton.Instance.GetString(role.StringName, Array.Empty()); + __instance.roleScreenshot.sprite = Sprite.Create(role.RoleScreenshot.texture, new Rect(0, 0, 370, 230), Vector2.one / 2, 100); + __instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced; + __instance.roleHeaderSprite.color = customRole.RoleColor; + __instance.roleHeaderText.color = new Color(customRole.RoleColor.r - 0.3f, customRole.RoleColor.g - 0.3f, customRole.RoleColor.b - 0.3f); + + CreateAdvancedSettings(__instance, role); + + for (int i = 0; i < __instance.advancedSettingChildren.Count; i++) + { + OptionBehaviour optionBehaviour = __instance.advancedSettingChildren[i]; + optionBehaviour.OnValueChanged = new Action(__instance.ValueChanged); + if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost) + { + optionBehaviour.SetAsPlayer(); + } + } + __instance.RoleChancesSettings.SetActive(false); + __instance.AdvancedRolesSettings.SetActive(true); + __instance.RefreshChildren(); + } + + public static void CreateQuotaOption(RolesSettingsMenu __instance, RoleBehaviour role, ref float yPos, int index) + { + RoleOptionSetting roleOptionSetting = Object.Instantiate(__instance.roleOptionSettingOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform); + roleOptionSetting.transform.localPosition = new Vector3(-0.15f, yPos, -2f); + roleOptionSetting.SetRole(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions, role, 20); + roleOptionSetting.labelSprite.color = (role as ICustomRole).RoleColor; + roleOptionSetting.OnValueChanged = new Action(ValueChanged); + roleOptionSetting.titleText.horizontalAlignment = TMPro.HorizontalAlignmentOptions.Left; + roleOptionSetting.SetClickMask(__instance.ButtonClickMask); + __instance.roleChances.Add(roleOptionSetting); + + PassiveButton newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform); + newButton.name = "ConfigButton"; + newButton.transform.localPosition = new Vector3(0.2419f, -0.2582f, -2f); + newButton.transform.FindChild("Plus_TMP").gameObject.DestroyImmediate(); + newButton.transform.FindChild("InactiveSprite").GetComponent().sprite = MiraAssets.Cog.LoadAsset(); + newButton.transform.FindChild("ActiveSprite").GetComponent().sprite = MiraAssets.CogActive.LoadAsset(); + + PassiveButton passiveButton = newButton.GetComponent(); + passiveButton.OnClick = new ButtonClickedEvent(); + passiveButton.OnClick.AddListener((UnityAction)(() => + { + ChangeTab(role, __instance); + })); + + if (index < GameSettingMenuPatches.selectedMod.CustomRoles.Count - 1) + { + yPos += -0.43f; + } + } + } +} diff --git a/MiraAPI/Resources/Cog.png b/MiraAPI/Resources/Cog.png new file mode 100644 index 0000000..8551db4 Binary files /dev/null and b/MiraAPI/Resources/Cog.png differ diff --git a/MiraAPI/Resources/CogActive.png b/MiraAPI/Resources/CogActive.png new file mode 100644 index 0000000..b4c0dda Binary files /dev/null and b/MiraAPI/Resources/CogActive.png differ diff --git a/MiraAPI/Resources/FortniteBanner.jpeg b/MiraAPI/Resources/FortniteBanner.jpeg new file mode 100644 index 0000000..2536fbf Binary files /dev/null and b/MiraAPI/Resources/FortniteBanner.jpeg differ diff --git a/MiraAPI/Roles/CustomRoleManager.cs b/MiraAPI/Roles/CustomRoleManager.cs index 024aeec..a0e2ecf 100644 --- a/MiraAPI/Roles/CustomRoleManager.cs +++ b/MiraAPI/Roles/CustomRoleManager.cs @@ -62,6 +62,7 @@ public static class CustomRoleManager roleBehaviour.CanVent = customRole.CanUseVent; roleBehaviour.DefaultGhostRole = customRole.GhostRole; roleBehaviour.MaxCount = 15; + roleBehaviour.RoleScreenshot = customRole.OptionsScreenshot.LoadAsset(); if (customRole.IsGhostRole) { diff --git a/MiraAPI/Roles/ICustomRole.cs b/MiraAPI/Roles/ICustomRole.cs index e15ab68..615e56b 100644 --- a/MiraAPI/Roles/ICustomRole.cs +++ b/MiraAPI/Roles/ICustomRole.cs @@ -18,6 +18,7 @@ public interface ICustomRole Color RoleColor { get; } ModdedRoleTeams Team { get; } + LoadableAsset OptionsScreenshot { get; } LoadableAsset Icon => MiraAssets.Empty; diff --git a/MiraAPI/Utilities/Assets/MiraAssets.cs b/MiraAPI/Utilities/Assets/MiraAssets.cs index 3a49328..f42d011 100644 --- a/MiraAPI/Utilities/Assets/MiraAssets.cs +++ b/MiraAPI/Utilities/Assets/MiraAssets.cs @@ -6,5 +6,8 @@ public static readonly LoadableResourceAsset NextButton = new("NextButton.png", "MiraAPI.Resources."); public static readonly LoadableResourceAsset NextButtonActive = new("NextButtonActive.png", "MiraAPI.Resources."); + public static readonly LoadableResourceAsset Cog = new("Cog.png", "MiraAPI.Resources."); + public static readonly LoadableResourceAsset CogActive = new("CogActive.png", "MiraAPI.Resources."); + public static readonly LoadableResourceAsset Banner = new("FortniteBanner.jpeg", "MiraAPI.Resources."); } }