mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-19 15:51:48 +00:00
Add logic for modifiers & roles in hide and seek
This commit is contained in:
1 parent
7e3a279b56
commit
259b9b7097
9 files changed
+402
-4
No files matched your search
@@ -179,6 +179,7 @@ public static class ModifierManager
|
||||
private static bool IsGameModifierValid(PlayerControl player, GameModifier modifier, uint modifierId)
|
||||
{
|
||||
return (player.Data.Role is not ICustomRole role || role.IsModifierApplicable(modifier)) &&
|
||||
modifier.IsModifierValidOn(player.Data.Role) && !player.HasModifier(modifierId);
|
||||
modifier.IsModifierValidOn(player.Data.Role) && !player.HasModifier(modifierId) &&
|
||||
modifier.CanSpawnOnCurrentMode();
|
||||
}
|
||||
}
|
||||
@@ -39,4 +39,10 @@ public abstract class GameModifier : BaseModifier
|
||||
/// <param name="reason">The reason why the game ended.</param>
|
||||
/// <returns>True if the player won, false if they lost. Return null to use the player's default win condition.</returns>
|
||||
public virtual bool? DidWin(GameOverReason reason) => null;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the modifier can spawn in general, accounting for gamemodes and everything else.
|
||||
/// </summary>
|
||||
/// <returns>True if the modifier is able to spawn, otherwise false.</returns>
|
||||
public virtual bool CanSpawnOnCurrentMode() => !GameManager.Instance.IsHideAndSeek();
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using Il2CppSystem.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MiraAPI.Roles;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
[HarmonyPatch(typeof(LogicRoleSelectionHnS))]
|
||||
public static class LogicRoleSelectionHnsPatch
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(LogicRoleSelectionHnS.AssignRolesForTeam))]
|
||||
public static bool AssignRolesForTeam(
|
||||
LogicRoleSelectionHnS __instance,
|
||||
List<NetworkedPlayerInfo> players,
|
||||
IGameOptions opts,
|
||||
RoleTeamTypes team,
|
||||
int teamMax,
|
||||
Il2CppSystem.Nullable<RoleTypes> defaultRole)
|
||||
{
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Team: {team}, Max: {teamMax}, Players: {players.Count}, DefaultRole: {defaultRole}");
|
||||
int num = 0;
|
||||
IRoleOptionsCollection roleOptions = opts.RoleOptions;
|
||||
var source = RoleManager.Instance.AllRoles.ToArray()
|
||||
.Where(role => role.TeamType == team && !RoleManager.IsGhostRole(role.Role) &&
|
||||
CustomRoleUtils.CanSpawnOnCurrentMode(role));
|
||||
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)));
|
||||
List<RoleTypes> list = new List<RoleTypes>();
|
||||
if (team == RoleTeamTypes.Crewmate)
|
||||
{
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Before Guaranteed Assignment");
|
||||
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
||||
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
||||
int count = roleAssignmentData2.Count;
|
||||
roleAssignmentData2.Count = count - 1;
|
||||
if (count <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
list.Add(roleAssignmentData.Role.Role);
|
||||
}
|
||||
}
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Guaranteed Assignment");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Potential Assignment");
|
||||
|
||||
AssignRolesFromList(players, teamMax, list, ref num);
|
||||
var basicRole = RoleTypes.Engineer;
|
||||
while (list.Count < players.Count && list.Count + num < teamMax)
|
||||
{
|
||||
list.Add(basicRole);
|
||||
}
|
||||
|
||||
AssignRolesFromList(players, teamMax, list, ref num);
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After Fallback Assignment");
|
||||
}
|
||||
else if (team == RoleTeamTypes.Impostor)
|
||||
{
|
||||
var newImpostors = new List<NetworkedPlayerInfo>();
|
||||
// Specified Seeker
|
||||
if (__instance.hnsManager.LogicOptionsHnS.HasImpostorPlayerID() &&
|
||||
__instance.hnsManager.LogicOptionsHnS.ValidateImpostorPlayerID(players) &&
|
||||
!AmongUsClient.Instance.IsGamePublic)
|
||||
{
|
||||
NetworkedPlayerInfo networkedPlayerInfo = players.ToArray()
|
||||
.First(p => p.PlayerId == __instance.hnsManager.LogicOptionsHnS.ImpostorPlayerID());
|
||||
players.Remove(networkedPlayerInfo);
|
||||
newImpostors.Add(networkedPlayerInfo);
|
||||
}
|
||||
// Random Seeker
|
||||
else
|
||||
{
|
||||
int num2 = 0;
|
||||
while (num2 < teamMax && players.Count > 0)
|
||||
{
|
||||
PseudoRandomList<NetworkedPlayerInfo> pseudoRandomList = new PseudoRandomList<NetworkedPlayerInfo>(AmongUsClient.Instance.GameId);
|
||||
players._items.Do(x => pseudoRandomList.Add(x));
|
||||
for (int i = 0; i < GameData.RoundsPlayedInSession; i++)
|
||||
{
|
||||
pseudoRandomList.PickRandom();
|
||||
}
|
||||
NetworkedPlayerInfo networkedPlayerInfo = pseudoRandomList.PickRandom();
|
||||
players.Remove(networkedPlayerInfo);
|
||||
newImpostors.Add(networkedPlayerInfo);
|
||||
num2++;
|
||||
}
|
||||
}
|
||||
foreach (RoleManager.RoleAssignmentData roleAssignmentData in guaranteedRoles.Select((x) =>
|
||||
new RoleManager.RoleAssignmentData(x, roleOptions.GetNumPerGame(x.Role), 100)))
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
RoleManager.RoleAssignmentData roleAssignmentData2 = roleAssignmentData;
|
||||
int count = roleAssignmentData2.Count;
|
||||
roleAssignmentData2.Count = count - 1;
|
||||
if (count <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
list.Add(roleAssignmentData.Role.Role);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssignRolesFromList(players, teamMax, list, ref num);
|
||||
var basicRole = RoleTypes.Impostor;
|
||||
while (list.Count < players.Count && list.Count + num < teamMax)
|
||||
{
|
||||
list.Add(basicRole);
|
||||
}
|
||||
|
||||
AssignRolesFromList(players, teamMax, list, ref num);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void AssignRolesFromList(List<NetworkedPlayerInfo> players, int teamMax, List<RoleTypes> 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
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<NetworkedPlayerInfo> players,
|
||||
IGameOptions opts,
|
||||
RoleTeamTypes team,
|
||||
int teamMax,
|
||||
Il2CppSystem.Nullable<RoleTypes> defaultRole)
|
||||
{
|
||||
int num = 0;
|
||||
var source = RoleManager.Instance.AllRoles.ToArray()
|
||||
.Where(role => role.TeamType == team && !RoleManager.IsGhostRole(role.Role) &&
|
||||
CustomRoleUtils.CanSpawnOnCurrentMode(role));
|
||||
List<RoleTypes> list = new List<RoleTypes>();
|
||||
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)))
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
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);
|
||||
if (defaultRole != null)
|
||||
{
|
||||
while (list.Count < players.Count && list.Count + num < teamMax)
|
||||
{
|
||||
list.Add(defaultRole.Value);
|
||||
}
|
||||
|
||||
__instance.AssignRolesFromList(players, teamMax, list, ref num);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
using MiraAPI.Modifiers;
|
||||
|
||||
@@ -51,7 +51,7 @@ public class MiraPluginInfo
|
||||
public IReadOnlyCollection<CustomActionButton> Buttons { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only dictionary of this plugin's custom game modes.
|
||||
/// Gets a read only dictionary of this plugin's custom presets.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<OptionPreset> Presets { get; internal set; } = null!;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.PluginLoading;
|
||||
@@ -18,6 +19,13 @@ public static class PresetManager
|
||||
/// </summary>
|
||||
public static string PresetDirectory { get; } = Path.GetFullPath("mira_presets", Application.persistentDataPath);
|
||||
|
||||
internal static List<OptionPreset> InternalMasterPresets { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read only dictionary of this plugin's custom presets.
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<OptionPreset> MasterPresets { get; internal set; } = null!;
|
||||
|
||||
internal static void CreateDefaultPreset(MiraPluginInfo plugin)
|
||||
{
|
||||
var presetPath = Path.Combine(PresetDirectory, plugin.PluginId);
|
||||
@@ -93,4 +101,58 @@ public static class PresetManager
|
||||
}
|
||||
plugin.Presets = [..plugin.InternalPresets];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the master presets by reading the configuration files from the preset directory.
|
||||
/// </summary>
|
||||
public static void LoadMasterPreset()
|
||||
{
|
||||
/*foreach (var btn in plugin.InternalPresets.Select(x=>x.PresetButton))
|
||||
{
|
||||
if (btn != null)
|
||||
{
|
||||
Object.DestroyImmediate(btn);
|
||||
}
|
||||
}
|
||||
|
||||
plugin.InternalPresets.Clear();*/
|
||||
if (!Directory.Exists(PresetDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(PresetDirectory);
|
||||
}
|
||||
|
||||
var pluginPresetPath = Path.Combine(PresetDirectory, "MasterPresets");
|
||||
if (!Directory.Exists(pluginPresetPath))
|
||||
{
|
||||
Directory.CreateDirectory(pluginPresetPath);
|
||||
}
|
||||
|
||||
foreach (var file in Directory.GetFiles(pluginPresetPath, "*.cfg"))
|
||||
{
|
||||
var fileName = Path.GetFileName(file);
|
||||
Info($"Loading preset file {fileName}");
|
||||
var presetName = Path.GetFileNameWithoutExtension(file);
|
||||
var presetConfig = new ConfigFile(file, false)
|
||||
{
|
||||
SaveOnConfigSet = false,
|
||||
};
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins)
|
||||
{
|
||||
foreach (var option in plugin.InternalOptions.Where(x=>x.IncludeInPreset))
|
||||
{
|
||||
option.Bind(presetConfig);
|
||||
}
|
||||
|
||||
foreach (var role in plugin.InternalRoles.Values.OfType<ICustomRole>().Where(x=>!x.Configuration.HideSettings))
|
||||
{
|
||||
role.BindConfig(presetConfig);
|
||||
}
|
||||
}
|
||||
|
||||
presetConfig.Save();
|
||||
|
||||
// InternalMasterPresets.Add(new OptionPreset(presetName, plugin, presetConfig));
|
||||
}
|
||||
MasterPresets = [..InternalMasterPresets];
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -13,6 +14,62 @@ namespace MiraAPI.Roles;
|
||||
/// </summary>
|
||||
public static class CustomRoleUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified role can spawn in general, accounting for gamemodes and everything else.
|
||||
/// </summary>
|
||||
/// <param name="role">The role you would like to check for.</param>
|
||||
/// <returns>True if the role is able to spawn, otherwise false.</returns>
|
||||
public static bool CanSpawnOnCurrentMode(RoleBehaviour role)
|
||||
{
|
||||
if (role is ICustomRole custom)
|
||||
{
|
||||
return custom.CanSpawnOnCurrentMode();
|
||||
}
|
||||
|
||||
if (GameManager.Instance.IsHideAndSeek())
|
||||
{
|
||||
return role.Role is RoleTypes.Engineer || role.Role is RoleTypes.Impostor;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<(ushort RoleType, int Chance)> GetPossibleRoles(
|
||||
List<RoleManager.RoleAssignmentData> assignmentData,
|
||||
Func<RoleManager.RoleAssignmentData, bool>? predicate = null)
|
||||
{
|
||||
var roles = new List<(ushort, int)>();
|
||||
|
||||
assignmentData.Where(x => predicate == null || predicate(x)).ToList().ForEach(x =>
|
||||
{
|
||||
for (var i = 0; i < x.Count; i++)
|
||||
{
|
||||
roles.Add(((ushort)x.Role.Role, x.Chance));
|
||||
}
|
||||
});
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
public static RoleManager.RoleAssignmentData GetAssignData(RoleTypes roleType)
|
||||
{
|
||||
var currentGameOptions = GameOptionsManager.Instance.CurrentGameOptions;
|
||||
var roleOptions = currentGameOptions.RoleOptions;
|
||||
|
||||
var role = GetRegisteredRole(roleType);
|
||||
var assignmentData = new RoleManager.RoleAssignmentData(role, roleOptions.GetNumPerGame(role!.Role),
|
||||
roleOptions.GetChancePerGame(role.Role));
|
||||
|
||||
return assignmentData;
|
||||
}
|
||||
|
||||
public static RoleBehaviour? GetRegisteredRole(RoleTypes roleType)
|
||||
{
|
||||
// we want to prioritize the custom roles because the role has the right RoleColour/TeamColor
|
||||
var role = CustomRoleManager.AllRoles.FirstOrDefault(x => x.Role == roleType);
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all active in-game roles.
|
||||
/// </summary>
|
||||
|
||||
@@ -239,4 +239,10 @@ public interface ICustomRole : IOptionable
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the role can spawn in general, accounting for gamemodes and everything else.
|
||||
/// </summary>
|
||||
/// <returns>True if the role is able to spawn, otherwise false.</returns>
|
||||
public virtual bool CanSpawnOnCurrentMode() => !GameManager.Instance.IsHideAndSeek();
|
||||
}
|
||||
Reference in new issue
Block a user