diff --git a/MiraAPI/Patches/Roles/LogicRoleSelectionNormalPatch.cs b/MiraAPI/Patches/Roles/LogicRoleSelectionNormalPatch.cs deleted file mode 100644 index b0540bb..0000000 --- a/MiraAPI/Patches/Roles/LogicRoleSelectionNormalPatch.cs +++ /dev/null @@ -1,93 +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; - - var assignmentData = source.Where(x => !x.IsDead).Select(role => - new RoleManager.RoleAssignmentData( - role, - roleOptions.GetNumPerGame(role.Role), - roleOptions.GetChancePerGame(role.Role))).ToList(); - var source2 = CustomRoleUtils.GetPossibleRoles(assignmentData, x => x.Chance == 100); - var guaranteedRoles = source.Where(x => source2.Contains(((ushort)x.Role, 100))); - foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) => - new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100))) - { - while (true) - { - RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData; - int count = roleAssignmentData2.Count; - roleAssignmentData2.Count = count - 1; - if (count <= 0) - { - break; - } - - list.Add(roleAssignmentData.Role.Role); - } - } - - __instance.AssignRolesFromList(players, teamMax, list, ref num); - - var list2 = source.Where(x => !x.IsDead).Select(role => - new RoleManager.RoleAssignmentData( - role, - roleOptions.GetNumPerGame(role.Role), - roleOptions.GetChancePerGame(role.Role))).ToList(); - - list.Clear(); - foreach (RoleManager.RoleAssignmentData roleAssignmentData3 in list2) - { - for (int i = 0; i < roleAssignmentData3.Count; i++) - { - if (HashRandom.Next(101) < roleAssignmentData3.Chance) - { - list.Add(roleAssignmentData3.Role.Role); - } - } - } - - __instance.AssignRolesFromList(players, teamMax, list, ref num); - var defaultRole2 = team is RoleTeamTypes.Crewmate ? RoleTypes.Crewmate : RoleTypes.Impostor; - try - { - defaultRole2 = defaultRole.Value; - } - catch - { - // Ignored - } - - while (list.Count < players.Count && list.Count + 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++; + } + } +}