diff --git a/MiraAPI/Patches/Roles/LogicRoleSelectionNormalPatch.cs b/MiraAPI/Patches/Roles/LogicRoleSelectionNormalPatch.cs deleted file mode 100644 index 0dc9474..0000000 --- a/MiraAPI/Patches/Roles/LogicRoleSelectionNormalPatch.cs +++ /dev/null @@ -1,76 +0,0 @@ -using AmongUs.GameOptions; -using HarmonyLib; -using Il2CppSystem.Collections.Generic; -using System.Linq; -using MiraAPI.Roles; - -namespace MiraAPI.Patches.Roles; - -[HarmonyPatch(typeof(LogicRoleSelectionNormal))] -public static class LogicRoleSelectionNormalPatch -{ - [HarmonyPrefix] - [HarmonyPatch(nameof(LogicRoleSelectionNormal.AssignRolesForTeam))] - public static bool AssignRolesForTeam( - LogicRoleSelectionNormal __instance, - List players, - IGameOptions opts, - RoleTeamTypes team, - int teamMax, - Il2CppSystem.Nullable defaultRole) - { - int num = 0; - var source = RoleManager.Instance.AllRoles.ToArray() - .Where(role => role.TeamType == team && !RoleManager.IsGhostRole(role.Role) && - CustomRoleUtils.CanSpawnOnCurrentMode(role)); - List list = new List(); - IRoleOptionsCollection roleOptions = opts.RoleOptions; - - // Assign guaranteed roles first, just like the vanilla selector. This is - // important because the list of players is shared by both team passes. - foreach (var role in source.Where(x => roleOptions.GetChancePerGame(x.Role) == 100) - .Select(role => role.Role)) - { - for (var i = 0; i < roleOptions.GetNumPerGame(role); i++) - { - list.Add(role); - } - } - - __instance.AssignRolesFromList(players, teamMax, list, ref num); - - // A 100% role was already assigned above. Including it here can consume - // another player and, more importantly, leaves the fallback count wrong. - list.Clear(); - foreach (var role in source.Where(x => - roleOptions.GetChancePerGame(x.Role) > 0 && - roleOptions.GetChancePerGame(x.Role) < 100) - .Select(role => role.Role)) - { - for (var i = 0; i < roleOptions.GetNumPerGame(role); i++) - { - if (HashRandom.Next(101) < roleOptions.GetChancePerGame(role)) - { - list.Add(role); - } - } - } - - __instance.AssignRolesFromList(players, teamMax, list, ref num); - - // Do not read defaultRole here. The nullable argument is not marshalled - // reliably by IL2CPP and can return Crewmate for the impostor pass. - var defaultRole2 = team is RoleTeamTypes.Crewmate ? RoleTypes.Crewmate : RoleTypes.Impostor; - - // Assign the remaining players up to the requested team limit. The - // vanilla list has been consumed by AssignRolesFromList at this point, - // so checking list.Count can leave players unassigned. - while (players.Count > 0 && num < teamMax) - { - list.Add(defaultRole2); - __instance.AssignRolesFromList(players, teamMax, list, ref num); - } - - return false; - } -} diff --git a/MiraAPI/Patches/Roles/SelectRolesPatch.cs b/MiraAPI/Patches/Roles/SelectRolesPatch.cs new file mode 100644 index 0000000..dc51f03 --- /dev/null +++ b/MiraAPI/Patches/Roles/SelectRolesPatch.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using AmongUs.GameOptions; +using HarmonyLib; +using System.Linq; +using InnerNet; +using MiraAPI.Roles; + +namespace MiraAPI.Patches.Roles; + +[HarmonyPatch(typeof(RoleManager))] +public static class SelectRolesPatch +{ + public static bool ApiHandlesRoleSelect = true; + [HarmonyPrefix] + [HarmonyPatch(nameof(RoleManager.SelectRoles))] + public static bool SelectRoles() + { + if (!ApiHandlesRoleSelect) + { + return true; + } + Il2CppSystem.Collections.Generic.List list = new(); + AmongUsClient.Instance.GetAllClients(list); + List list2 = list.ToArray() + .Where(c => c.Character != null && c.Character.Data != null && !c.Character.Data.Disconnected && + !c.Character.Data.IsDead).OrderBy(c => c.Id).Select(c => c.Character.Data) + .ToList(); + + foreach (NetworkedPlayerInfo networkedPlayerInfo in GameData.Instance.AllPlayers) + { + if (networkedPlayerInfo.Object != null && networkedPlayerInfo.Object.isDummy) + { + list2.Add(networkedPlayerInfo); + } + } + IGameOptions currentGameOptions = GameOptionsManager.Instance.CurrentGameOptions; + int adjustedNumImpostors = GameOptionsManager.Instance.CurrentGameOptions.GetAdjustedNumImpostors(list2.Count); + AssignRolesForTeam(list2, currentGameOptions, RoleTeamTypes.Impostor, adjustedNumImpostors, RoleTypes.Impostor); + AssignRolesForTeam(list2, currentGameOptions, RoleTeamTypes.Crewmate, int.MaxValue, RoleTypes.Crewmate); + return false; + } + + public static void AssignRolesForTeam( + List players, + IGameOptions opts, + RoleTeamTypes team, + int teamMax, + RoleTypes defaultRole) + { + int num = 0; + var source = RoleManager.Instance.AllRoles.ToArray() + .Where(role => role.TeamType == team && !RoleManager.IsGhostRole(role.Role) && + CustomRoleUtils.CanSpawnOnCurrentMode(role)); + List list = new List(); + IRoleOptionsCollection roleOptions = opts.RoleOptions; + + // Assign guaranteed roles first, just like the vanilla selector. This is + // important because the list of players is shared by both team passes. + foreach (var role in source.Where(x => roleOptions.GetChancePerGame(x.Role) == 100) + .Select(role => role.Role)) + { + for (var i = 0; i < roleOptions.GetNumPerGame(role); i++) + { + list.Add(role); + } + } + + AssignRolesFromList(players, teamMax, list, ref num); + + // A 100% role was already assigned above. Including it here can consume + // another player and, more importantly, leaves the fallback count wrong. + list.Clear(); + foreach (var role in source.Where(x => + roleOptions.GetChancePerGame(x.Role) > 0 && + roleOptions.GetChancePerGame(x.Role) < 100) + .Select(role => role.Role)) + { + for (var i = 0; i < roleOptions.GetNumPerGame(role); i++) + { + if (HashRandom.Next(101) < roleOptions.GetChancePerGame(role)) + { + list.Add(role); + } + } + } + + AssignRolesFromList(players, teamMax, list, ref num); + + // Assign the remaining players up to the requested team limit. The + // vanilla list has been consumed by AssignRolesFromList at this point, + // so checking list.Count can leave players unassigned. + while (players.Count > 0 && num < teamMax) + { + list.Add(defaultRole); + AssignRolesFromList(players, teamMax, list, ref num); + } + } + private static void AssignRolesFromList(List players, int teamMax, List roleList, ref int rolesAssigned) + { + while (roleList.Count > 0 && players.Count > 0 && rolesAssigned < teamMax) + { + int index = HashRandom.FastNext(roleList.Count); + RoleTypes roleType = roleList[index]; + roleList.RemoveAt(index); + int index2 = HashRandom.FastNext(players.Count); + players[index2].Object.RpcSetRole(roleType, false); + players.RemoveAt(index2); + rolesAssigned++; + } + } +}