mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 01:46:15 +00:00
Compare commits
32
Commits
options-changes
...
0.3.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ad003ed1e | ||
|
|
93eea6ea2f | ||
|
|
8e09b7fee0 | ||
|
|
1e4894538c | ||
|
|
303aedafc7 | ||
|
|
8ed4355504 | ||
|
|
76b02835a7 | ||
|
|
8d5bc710e7 | ||
|
|
798ce451d5 | ||
|
|
136bbde516 | ||
|
|
5252620108 | ||
|
|
c9b1c57774 | ||
|
|
d97df1a891 | ||
|
|
e93ef6fd1d | ||
|
|
3afe437101 | ||
|
|
62c01b2f72 | ||
|
|
089d2c1ea5 | ||
|
|
c65511d03e | ||
|
|
aee304fb96 | ||
|
|
11ba564eed | ||
|
|
259b9b7097 | ||
|
|
7e3a279b56 | ||
|
|
1fd2dd3539 | ||
|
|
3f60bcae2c | ||
|
|
fe1580bb7e | ||
|
|
8b23021b2a | ||
|
|
fdc715dac2 | ||
|
|
e1ca50be3e | ||
|
|
056a9b6102 | ||
|
|
ae917a9c77 | ||
|
|
fc9e9740be | ||
|
|
f9096b6160 |
No files matched your search
@@ -8,7 +8,7 @@
|
||||
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
|
||||
<VersionPrefix>0.3.3</VersionPrefix>
|
||||
<VersionPrefix>0.3.4</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
|
||||
<Authors>All Of Us, XtraCube, Angxl</Authors>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace MiraAPI.Events.Vanilla.Player;
|
||||
|
||||
/// <summary>
|
||||
/// The event that is invoked when a player completes a task within Hide and Seek. Non-cancelable.
|
||||
/// </summary>
|
||||
public class CompleteHnsTaskEvent : MiraEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance of the <see cref="PlayerControl"/>.
|
||||
/// </summary>
|
||||
public PlayerControl Player { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the <see cref="PlayerTask"/> that the player completed.
|
||||
/// </summary>
|
||||
public PlayerTask Task { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the <see cref="HnsManager"/> attached to the game.
|
||||
/// </summary>
|
||||
public HideAndSeekManager HnsManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CompleteHnsTaskEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="hnsManager">The Hide and Seek Manager currently active.</param>
|
||||
/// <param name="player">The player who completed the task.</param>
|
||||
/// <param name="task">The task that the player completed.</param>
|
||||
public CompleteHnsTaskEvent(HideAndSeekManager hnsManager, PlayerControl player, PlayerTask task)
|
||||
{
|
||||
HnsManager = hnsManager;
|
||||
Player = player;
|
||||
Task = task;
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@ public interface IModdedOption
|
||||
uint Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the option.
|
||||
/// Gets or sets the title of the option.
|
||||
/// </summary>
|
||||
string Title { get; }
|
||||
string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the StringName for the option, used for localization.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.GameOptions.OptionTypes;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using UnityEngine;
|
||||
@@ -14,6 +15,7 @@ public class MiraNumberOptionComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
/// <summary>
|
||||
/// Gets or sets the modded option associated with the object.
|
||||
/// </summary>
|
||||
[HideFromIl2Cpp]
|
||||
public ModdedNumberOption NumberOption { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -22,7 +24,17 @@ public class MiraNumberOptionComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
public float DefaultIncrement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of whether or not holding shift will split the increment in half.
|
||||
/// Gets or sets a value indicating whether holding shift will split the increment in half.
|
||||
/// </summary>
|
||||
public bool ShiftIncrementToggle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating what Zero displays.
|
||||
/// </summary>
|
||||
public string ZeroValue { get; set; } = "#";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating what Negative One displays.
|
||||
/// </summary>
|
||||
public string NegativeValue { get; set; } = "#";
|
||||
}
|
||||
@@ -32,16 +32,83 @@ public class ModdedNumberOption : ModdedOption<float>
|
||||
/// </summary>
|
||||
public MiraNumberSuffixes SuffixType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating what zero appears as.
|
||||
/// </summary>
|
||||
public string ZeroWordValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating what a negative one appears as.
|
||||
/// </summary>
|
||||
public string NegativeWordValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether zero is infinity.
|
||||
/// </summary>
|
||||
public bool ZeroInfinity { get; }
|
||||
public bool ZeroInfinity => ZeroWordValue == "∞";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether holding shift will divide the increment.
|
||||
/// </summary>
|
||||
public bool ShiftIncrement { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModdedNumberOption"/> class.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the option.</param>
|
||||
/// <param name="defaultValue">The default value as a float.</param>
|
||||
/// <param name="min">The minimum value.</param>
|
||||
/// <param name="max">The maximum value.</param>
|
||||
/// <param name="increment">The increment.</param>
|
||||
/// <param name="suffixType">The suffix type.</param>
|
||||
/// <param name="formatString">Optional format string for the option screen.</param>
|
||||
/// <param name="zeroBehavior">Determines what is shown for zero. Options: ∞, #, or a word value (such as Off).</param>
|
||||
/// <param name="negativeBehavior">Determines what is shown for negative 1. Options: ∞, #, or a word value (such as Off).</param>
|
||||
/// <param name="halfIncrements">Whether increments can be split in half.</param>
|
||||
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
|
||||
public ModdedNumberOption(
|
||||
string title,
|
||||
float defaultValue,
|
||||
float min,
|
||||
float max,
|
||||
float increment,
|
||||
string zeroBehavior,
|
||||
string negativeBehavior,
|
||||
MiraNumberSuffixes suffixType,
|
||||
string? formatString = null,
|
||||
bool halfIncrements = false,
|
||||
bool includeInPreset = true) : base(title, defaultValue, includeInPreset)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
Increment = increment;
|
||||
SuffixType = suffixType;
|
||||
ZeroWordValue = zeroBehavior;
|
||||
NegativeWordValue = negativeBehavior;
|
||||
ShiftIncrement = halfIncrements;
|
||||
|
||||
Value = Mathf.Clamp(defaultValue, min, max);
|
||||
|
||||
Data = ScriptableObject.CreateInstance<FloatGameSetting>();
|
||||
|
||||
var data = (FloatGameSetting)Data;
|
||||
data.Type = global::OptionTypes.Float;
|
||||
data.Title = StringName;
|
||||
data.Value = Value;
|
||||
data.Increment = Increment;
|
||||
data.ValidRange = new FloatRange(Min, Max);
|
||||
data.FormatString = formatString ??
|
||||
(defaultValue.IsInteger() &&
|
||||
Increment.IsInteger() &&
|
||||
Value.IsInteger() &&
|
||||
Min.IsInteger() &&
|
||||
Max.IsInteger() ? "0" : "0.0");
|
||||
|
||||
data.ZeroIsInfinity = ZeroInfinity;
|
||||
data.SuffixType = (NumberSuffixes)SuffixType;
|
||||
data.OptionName = FloatOptionNames.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModdedNumberOption"/> class.
|
||||
/// </summary>
|
||||
@@ -54,7 +121,6 @@ public class ModdedNumberOption : ModdedOption<float>
|
||||
/// <param name="formatString">Optional format string for the option screen.</param>
|
||||
/// <param name="zeroInfinity">Whether zero is infinity or not.</param>
|
||||
/// <param name="includeInPreset">Whether to include this option in the preset or not.</param>
|
||||
/// <param name="shiftIncrement">Whether holding shift will divide the increment or not.</param>
|
||||
public ModdedNumberOption(
|
||||
string title,
|
||||
float defaultValue,
|
||||
@@ -70,7 +136,8 @@ public class ModdedNumberOption : ModdedOption<float>
|
||||
Max = max;
|
||||
Increment = increment;
|
||||
SuffixType = suffixType;
|
||||
ZeroInfinity = zeroInfinity;
|
||||
ZeroWordValue = zeroInfinity ? "∞" : "#";
|
||||
NegativeWordValue = "#";
|
||||
ShiftIncrement = increment != 1f;
|
||||
|
||||
Value = Mathf.Clamp(defaultValue, min, max);
|
||||
@@ -110,6 +177,8 @@ public class ModdedNumberOption : ModdedOption<float>
|
||||
optionComponent.NumberOption = this;
|
||||
optionComponent.DefaultIncrement = Increment;
|
||||
optionComponent.ShiftIncrementToggle = ShiftIncrement;
|
||||
optionComponent.NegativeValue = NegativeWordValue;
|
||||
optionComponent.ZeroValue = ZeroWordValue;
|
||||
|
||||
numberOption.SetUpFromData(Data, 20);
|
||||
numberOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
|
||||
|
||||
@@ -21,7 +21,7 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
public uint Id { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Title { get; }
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringNames StringName { get; }
|
||||
|
||||
@@ -64,9 +64,14 @@ public abstract class CustomActionButton
|
||||
public virtual float EffectDuration => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum amount of uses the button has. If the button has infinite uses, set to 0.
|
||||
/// Gets a value indicating whether the button has limited uses.
|
||||
/// </summary>
|
||||
public virtual int MaxUses => 0;
|
||||
public bool LimitedUses => ZeroIsInfinite ? MaxUses > 0 : MaxUses >= 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number of uses the button has. If the button has infinite uses, set to 0 or -1 based on what value that ZeroIsInfinite is set to.
|
||||
/// </summary>
|
||||
public virtual int MaxUses => ZeroIsInfinite ? 0 : -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value indicating uses mode.
|
||||
@@ -89,9 +94,9 @@ public abstract class CustomActionButton
|
||||
public virtual ButtonLocation Location { get; set; } = ButtonLocation.BottomLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the button has limited uses.
|
||||
/// Gets or sets a value indicating whether limited uses are determined via zero or a negative number of uses.
|
||||
/// </summary>
|
||||
public bool LimitedUses => MaxUses > 0;
|
||||
public virtual bool ZeroIsInfinite { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the effect is currently active, if there is one.
|
||||
@@ -206,7 +211,39 @@ public abstract class CustomActionButton
|
||||
{
|
||||
if (Enabled(PlayerControl.LocalPlayer.Data.Role))
|
||||
{
|
||||
ClickHandler();
|
||||
// Invoke the generic button click event.
|
||||
var genericEvent = new MiraButtonClickEvent(this);
|
||||
MiraEventManager.InvokeEvent(genericEvent);
|
||||
if (genericEvent.IsCancelled)
|
||||
{
|
||||
MiraEventManager.InvokeEvent(new MiraButtonCancelledEvent(this));
|
||||
}
|
||||
|
||||
// Invoke the button click event for specific button.
|
||||
var eventType = CustomButtonManager.ButtonEventTypes[GetType()];
|
||||
var @event = (MiraCancelableEvent)Activator.CreateInstance(eventType, this, genericEvent)!;
|
||||
var specificInvoked = MiraEventManager.InvokeEvent(@event, eventType);
|
||||
if (@event.IsCancelled)
|
||||
{
|
||||
var cancelEventType = CustomButtonManager.ButtonCancelledEventTypes[GetType()];
|
||||
var cancelEvent = (MiraEvent)Activator.CreateInstance(cancelEventType, this)!;
|
||||
MiraEventManager.InvokeEvent(cancelEvent, cancelEventType);
|
||||
}
|
||||
|
||||
if (specificInvoked)
|
||||
{
|
||||
if (!@event.IsCancelled)
|
||||
{
|
||||
ClickHandler();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!genericEvent.IsCancelled)
|
||||
{
|
||||
ClickHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -214,10 +251,10 @@ public abstract class CustomActionButton
|
||||
Helpers.CreateKeybindIcon(
|
||||
Button.gameObject,
|
||||
Keybind.CurrentKey,
|
||||
new Vector3(MaxUses <= 0 ? -0.4f : 0.4f, 0.45f, -9f)
|
||||
new Vector3(0.4f, 0.45f, -9f)
|
||||
);
|
||||
KeybindText = KeybindIcon.transform.GetChild(0).GetComponent<TextMeshPro>();
|
||||
HudManagerPatches.moddedKeybindIcons.Add(KeybindText);
|
||||
HudManagerPatches.ModdedKeybindIcons.Add(KeybindText);
|
||||
Button.usesRemainingSprite.transform.localPosition = new(-0.341f, 0.45f, -0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@ public class VanillaKeybind : BaseKeybind
|
||||
public VanillaKeybind(int id)
|
||||
{
|
||||
RewiredInputAction = KeybindUtils.GetInputActionById(id);
|
||||
Id = RewiredInputAction?.name;
|
||||
Id = RewiredInputAction != null ? RewiredInputAction.name : id.ToString();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -87,11 +87,11 @@ public static class CustomMurderRpc
|
||||
bool playKillSound = true)
|
||||
{
|
||||
source.isKilling = false;
|
||||
var data = target.Data;
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedError))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var data = target.Data;
|
||||
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) ||
|
||||
(resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1))
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Player;
|
||||
|
||||
namespace MiraAPI.Patches.Events;
|
||||
|
||||
[HarmonyPatch(typeof(HideAndSeekManager))]
|
||||
public static class HnsFinishTaskPatch
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(HideAndSeekManager.FinishTask))]
|
||||
public static void FinishTaskPostfix(HideAndSeekManager __instance, PlayerTask task)
|
||||
{
|
||||
var completeTaskEvent = new CompleteHnsTaskEvent(__instance, task.Owner, task);
|
||||
MiraEventManager.InvokeEvent(completeTaskEvent);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using HarmonyLib;
|
||||
/*
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
using MiraAPI.Events.Vanilla.Gameplay;
|
||||
|
||||
@@ -18,3 +19,4 @@ internal static class SpawnInMinigamePatch
|
||||
MiraEventManager.InvokeEvent(@event2);
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
/*
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Events;
|
||||
@@ -34,3 +35,4 @@ internal static class SubmergedSpawnGamePatch
|
||||
MiraEventManager.InvokeEvent(@event2);
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -21,10 +21,14 @@ using Object = UnityEngine.Object;
|
||||
namespace MiraAPI.Patches.Freeplay;
|
||||
|
||||
[HarmonyPatch(typeof(TaskAdderGame))]
|
||||
internal static class TaskAdderPatches
|
||||
public static class TaskAdderPatches
|
||||
{
|
||||
private static Scroller _scroller = null!;
|
||||
private static Dictionary<string, TaskFolder> folders = new();
|
||||
public static string CrewmateName => TranslationController.Instance.GetString(StringNames.Crewmate);
|
||||
public static string ImpostorName => TranslationController.Instance.GetString(StringNames.Impostor);
|
||||
public static string NeutralName => "Neutral";
|
||||
public static string ModifiersName => "Modifiers";
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(TaskAdderGame.Begin))]
|
||||
@@ -66,19 +70,16 @@ internal static class TaskAdderPatches
|
||||
__instance.RootFolderPrefab.GetComponent<PassiveButton>().ClickMask = collider;
|
||||
__instance.RootFolderPrefab.gameObject.SetActive(false);
|
||||
__instance.TaskParent = inner.transform;
|
||||
var crewLocale = TranslationController.Instance.GetString(StringNames.Crewmate);
|
||||
var impLocale = TranslationController.Instance.GetString(StringNames.Impostor);
|
||||
|
||||
var crewmateFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == crewLocale)!;
|
||||
var impostorFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == impLocale)!;
|
||||
var crewmateFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == CrewmateName)!;
|
||||
var impostorFolder = __instance.Root.SubFolders.ToArray().FirstOrDefault(x => x.FolderName == ImpostorName)!;
|
||||
//var neutralFolder = __instance.CreateFolder("Neutral", __instance.Root, 2, Color.gray);
|
||||
var modifiersFolder = __instance.CreateFolder("Modifiers", __instance.Root, 0, Color.blue);
|
||||
var modifiersFolder = __instance.CreateFolder(ModifiersName, __instance.Root, 0, Color.blue);
|
||||
|
||||
folders.Clear();
|
||||
folders.Add(crewmateFolder.FolderName, crewmateFolder);
|
||||
folders.Add(impostorFolder.FolderName, impostorFolder);
|
||||
//folders.Add("Neutrals", neutralFolder);
|
||||
folders.Add("Modifiers", modifiersFolder);
|
||||
folders.Add(ModifiersName, modifiersFolder);
|
||||
|
||||
int folderIdx = 2;
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins)
|
||||
@@ -137,8 +138,8 @@ internal static class TaskAdderPatches
|
||||
Object.Instantiate(__instance.RootFolderPrefab, __instance.transform);
|
||||
impFolder.gameObject.SetActive(false);
|
||||
impFolder.SetFolderColor(TaskFolder.FolderColor.Red);
|
||||
crewFolder.FolderName = TranslationController.Instance.GetString(StringNames.Crewmate);
|
||||
impFolder.FolderName = TranslationController.Instance.GetString(StringNames.Impostor);
|
||||
crewFolder.FolderName = CrewmateName;
|
||||
impFolder.FolderName = ImpostorName;
|
||||
rootFolder.SubFolders.Insert(0, crewFolder);
|
||||
rootFolder.SubFolders.Insert(0, impFolder);
|
||||
Il2CppSystem.Collections.Generic.List<RoleBehaviour> impRoles = new();
|
||||
@@ -440,7 +441,7 @@ internal static class TaskAdderPatches
|
||||
}
|
||||
}
|
||||
|
||||
if (folders.TryGetValue("Modifiers", out var modifiersFolder) && taskFolder.IsChildOf(modifiersFolder))
|
||||
if (folders.TryGetValue(ModifiersName, out var modifiersFolder) && taskFolder.IsChildOf(modifiersFolder))
|
||||
{
|
||||
var plugin = MiraPluginManager.GetPluginByGuid(taskFolder.name.Replace("(Clone)", string.Empty));
|
||||
if (plugin != null)
|
||||
|
||||
@@ -27,7 +27,7 @@ public static class HudManagerPatches
|
||||
|
||||
private static Dictionary<TextMeshPro, int> vanillaKeybindIcons = new();
|
||||
|
||||
internal static List<TextMeshPro> moddedKeybindIcons = new();
|
||||
internal static List<TextMeshPro> ModdedKeybindIcons = new();
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
@@ -90,7 +90,7 @@ public static class HudManagerPatches
|
||||
fakeButton.ToggleVisible(true);
|
||||
fakeButton.Destroy();
|
||||
}
|
||||
moddedKeybindIcons = [];
|
||||
ModdedKeybindIcons = [];
|
||||
|
||||
foreach (var button in CustomButtonManager.CustomButtons)
|
||||
{
|
||||
@@ -123,7 +123,7 @@ public static class HudManagerPatches
|
||||
aspectPosition.AdjustPosition();
|
||||
|
||||
vanillaKeybindIcons = [];
|
||||
var keybindIconPos = new Vector3(-0.4f, 0.4f, -9.5f);
|
||||
var keybindIconPos = new Vector3(0.4f, 0.4f, -9.5f);
|
||||
var vanillaButtons = new Dictionary<GameObject, int>
|
||||
{
|
||||
{ __instance.KillButton.gameObject, 8 },
|
||||
@@ -192,7 +192,7 @@ public static class HudManagerPatches
|
||||
btnIcon.Key.text = KeybindUtils.GetKeycodeByActionId(btnIcon.Value).ToString();
|
||||
btnIcon.Key.transform.parent.gameObject.SetActive(canSeeBinds);
|
||||
}
|
||||
foreach (var btnIcon in moddedKeybindIcons)
|
||||
foreach (var btnIcon in ModdedKeybindIcons)
|
||||
{
|
||||
btnIcon.transform.parent.gameObject.SetActive(canSeeBinds);
|
||||
}
|
||||
|
||||
@@ -110,12 +110,15 @@ internal static class GameSettingMenuPatches
|
||||
{
|
||||
case 3:
|
||||
_modifiersButton?.SelectButton(true);
|
||||
__instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.ModifierMenuDescription : "Configure modifiers and their settings here!";
|
||||
break;
|
||||
case 4:
|
||||
_customOneButton?.SelectButton(true);
|
||||
__instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuOneDescription : "Apply game settings for this mod!";
|
||||
break;
|
||||
case 5:
|
||||
_customTwoButton?.SelectButton(true);
|
||||
__instance.MenuDescriptionText.text = SelectedMod != null ? SelectedMod.MiraPlugin.CustomOptionMenuTwoDescription : "Apply game settings for this mod!";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,9 @@ public static class LobbyViewPanePatches
|
||||
__instance.settingsInfo.Add(categoryHeaderRoleVariant.gameObject);
|
||||
num -= 0.696f;
|
||||
var roles = RoleManager.Instance.AllRoles.ToArray().Where(x =>
|
||||
x is not ICustomRole && x.Role != RoleTypes.Crewmate && x.Role != RoleTypes.Impostor &&
|
||||
x is not ICustomRole && !x.IsRoleBlacklisted() && x.Role != RoleTypes.Crewmate &&
|
||||
x.Role != RoleTypes.Impostor && ((i == 0 && x.TeamType is RoleTeamTypes.Crewmate) ||
|
||||
(i == 1 && x.TeamType is RoleTeamTypes.Impostor)) &&
|
||||
x.Role != RoleTypes.CrewmateGhost && x.Role != RoleTypes.ImpostorGhost).ToList();
|
||||
for (int j = 0; j < roles.Count; j++)
|
||||
{
|
||||
@@ -380,7 +382,7 @@ public static class LobbyViewPanePatches
|
||||
|
||||
foreach (var grouping in sortedRoleGroups)
|
||||
{
|
||||
if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings))
|
||||
if (!grouping.Any() || grouping.All(x => x.Configuration.HideSettings || !x.VisibleInSettings()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -411,7 +413,7 @@ public static class LobbyViewPanePatches
|
||||
|
||||
foreach (var customRole in grouping)
|
||||
{
|
||||
if (customRole.Configuration.HideSettings)
|
||||
if (customRole.Configuration.HideSettings || !customRole.VisibleInSettings())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -74,10 +74,10 @@ public static class RoleSettingMenuPatches
|
||||
var num = 0.662f;
|
||||
|
||||
var list = CustomRoleManager.AllRoles.Where(
|
||||
r => !r.IsCustomRole() && r.TeamType == RoleTeamTypes.Crewmate && r.Role != RoleTypes.Crewmate &&
|
||||
r => !r.IsCustomRole() && !r.IsRoleBlacklisted() && r.TeamType == RoleTeamTypes.Crewmate && r.Role != RoleTypes.Crewmate &&
|
||||
r.Role != RoleTypes.CrewmateGhost).ToList();
|
||||
var list2 = CustomRoleManager.AllRoles.Where(
|
||||
r => !r.IsCustomRole() && r.TeamType == RoleTeamTypes.Impostor && r.Role != RoleTypes.Impostor &&
|
||||
r => !r.IsCustomRole() && !r.IsRoleBlacklisted() && r.TeamType == RoleTeamTypes.Impostor && r.Role != RoleTypes.Impostor &&
|
||||
r.Role != RoleTypes.ImpostorGhost).ToList();
|
||||
|
||||
if (__instance.roleTabs == null || __instance.roleTabs.Count == 0)
|
||||
@@ -160,7 +160,7 @@ public static class RoleSettingMenuPatches
|
||||
|
||||
foreach (var grouping in sortedRoleGroups)
|
||||
{
|
||||
if (!grouping.Any() || grouping.All(x=>x.Configuration.HideSettings))
|
||||
if (!grouping.Any() || grouping.All(x=> x.Configuration.HideSettings || !x.VisibleInSettings()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.GameOptions.OptionTypes;
|
||||
using MiraAPI.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -28,7 +31,33 @@ public static class SettingPatches
|
||||
string result;
|
||||
var suffix = (MiraNumberSuffixes)__instance.SuffixType;
|
||||
|
||||
if (__instance.ZeroIsInfinity && Mathf.Abs(value) < 0.0001f)
|
||||
var custom =
|
||||
ModdedOptionsManager.ModdedOptions.Values.FirstOrDefault(opt =>
|
||||
opt.OptionBehaviour != null && opt.OptionBehaviour.Data == __instance);
|
||||
if (custom is ModdedNumberOption moddedNumberOption)
|
||||
{
|
||||
if (moddedNumberOption.NegativeWordValue != "#" && (int)value == -1)
|
||||
{
|
||||
result = $"<b>{moddedNumberOption.NegativeWordValue}</b>";
|
||||
}
|
||||
else if (moddedNumberOption.ZeroWordValue != "#" && Mathf.Abs(value) < 0.0001f)
|
||||
{
|
||||
result = $"<b>{moddedNumberOption.ZeroWordValue}</b>";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = suffix switch
|
||||
{
|
||||
MiraNumberSuffixes.None => value.ToString(__instance.FormatString, NumberFormatInfo.InvariantInfo),
|
||||
MiraNumberSuffixes.Multiplier => value.ToString(__instance.FormatString, NumberFormatInfo.InvariantInfo) + "x",
|
||||
MiraNumberSuffixes.Percent => value.ToString(__instance.FormatString, NumberFormatInfo.InvariantInfo) + "%",
|
||||
_ => TranslationController.Instance.GetString(
|
||||
StringNames.GameSecondsAbbrev,
|
||||
(Il2CppSystem.Object[])[value.ToString(__instance.FormatString, CultureInfo.InvariantCulture)]),
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (__instance.ZeroIsInfinity && Mathf.Abs(value) < 0.0001f)
|
||||
{
|
||||
result = "<b>∞</b>";
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public static class KillButtonPatches
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use Custom Murder if player is custom role.
|
||||
/// Use Custom Murder instead of Murder Player from vanilla.
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(KillButton.DoClick))]
|
||||
@@ -58,11 +58,6 @@ public static class KillButtonPatches
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PlayerControl.LocalPlayer.Data.Role is not ICustomRole)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
PlayerControl.LocalPlayer.RpcCustomMurder(__instance.currentTarget);
|
||||
__instance.SetTarget(null);
|
||||
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
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)
|
||||
{
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Before Guaranteed Assignment");
|
||||
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);
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}");
|
||||
}
|
||||
// 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++;
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Seeker is {networkedPlayerInfo.PlayerName}, ID: {networkedPlayerInfo.PlayerId}");
|
||||
}
|
||||
}
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: After 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);
|
||||
}
|
||||
}
|
||||
AssignRolesFromList(newImpostors, 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(newImpostors, teamMax, list, ref num);
|
||||
var basicRole = RoleTypes.Impostor;
|
||||
while (list.Count < newImpostors.Count && list.Count + num < teamMax)
|
||||
{
|
||||
list.Add(basicRole);
|
||||
}
|
||||
|
||||
AssignRolesFromList(newImpostors, 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,93 @@
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public static class NameTagPatch
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PlayerControl.LocalPlayer.Data.Role.IsImpostor && otherPlayerRole.IsImpostor)
|
||||
if (PlayerControl.LocalPlayer.Data?.Role?.IsImpostor == true && otherPlayerRole.IsImpostor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public static class NameTagPatch
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!PlayerControl.LocalPlayer || !PlayerControl.LocalPlayer.Data || !PlayerControl.LocalPlayer.Data.Role || !otherPlayerRole)
|
||||
if (!PlayerControl.LocalPlayer || !PlayerControl.LocalPlayer.Data || !PlayerControl.LocalPlayer.Data?.Role || !otherPlayerRole)
|
||||
{
|
||||
__result = Color.white;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -22,6 +22,21 @@ public interface IMiraPlugin
|
||||
/// </summary>
|
||||
public virtual string CustomOptionMenuNameTwo => "Custom Category 2";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description for the second custom category in the game options menu, if any.
|
||||
/// </summary>
|
||||
public virtual string ModifierMenuDescription => "Configure modifiers and their settings here!";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description for the first custom category in the game options menu, if any.
|
||||
/// </summary>
|
||||
public virtual string CustomOptionMenuOneDescription => "Apply game settings for this mod!";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description for the second custom category in the game options menu, if any.
|
||||
/// </summary>
|
||||
public virtual string CustomOptionMenuTwoDescription => "Apply game settings for this mod!";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BepInEx configuration file for the plugin.
|
||||
/// </summary>
|
||||
|
||||
@@ -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,7 +1,8 @@
|
||||
using AmongUs.GameOptions;
|
||||
using AmongUs.GameOptions;
|
||||
using System;
|
||||
using MiraAPI.Patches.Freeplay;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
/// <summary>
|
||||
@@ -9,6 +10,16 @@ namespace MiraAPI.Roles;
|
||||
/// </summary>
|
||||
public record struct CustomRoleConfiguration
|
||||
{
|
||||
#pragma warning disable S1133
|
||||
[Obsolete("Default constructor is not supported. Please use the constructor that takes an ICustomRole parameter.")]
|
||||
#pragma warning restore S1133
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
public CustomRoleConfiguration()
|
||||
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
||||
{
|
||||
throw new NotImplementedException("Default constructor is not supported. Please use the constructor that takes an ICustomRole parameter.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomRoleConfiguration"/> struct.
|
||||
/// </summary>
|
||||
@@ -36,7 +47,7 @@ public record struct CustomRoleConfiguration
|
||||
{
|
||||
ModdedRoleTeams.Crewmate => TranslationController.Instance.GetString(StringNames.Crewmate),
|
||||
ModdedRoleTeams.Impostor => TranslationController.Instance.GetString(StringNames.Impostor),
|
||||
_ => "Neutral",
|
||||
_ => TaskAdderPatches.NeutralName,
|
||||
};
|
||||
IntroSound = role.Team is ModdedRoleTeams.Crewmate
|
||||
? CustomRoleManager.CrewmateIntroSound
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Text;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Modifiers;
|
||||
@@ -239,4 +240,15 @@ 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();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the function that determines whether the role should be toggled on or off in the game settings.
|
||||
/// </summary>
|
||||
public virtual Func<bool> VisibleInSettings => () => true;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using MiraAPI.Roles;
|
||||
@@ -536,4 +537,19 @@ public static class Helpers
|
||||
}
|
||||
return role.NiceName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the role is blacklisted from appearing and spawning. (Only applicable to vanilla roles)
|
||||
/// </summary>
|
||||
/// <param name="role">The role to check.</param>
|
||||
/// <returns>The role's blacklist status.</returns>
|
||||
public static bool IsRoleBlacklisted(this RoleBehaviour role)
|
||||
{
|
||||
// This should be patchable by mods when a vanilla role is meant to be replaced by a custom role.
|
||||
if (role.Role is RoleTypes.Engineer)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user