Compare commits

..
3 Commits
Author SHA1 Message Date
Turret 08af14a890 Translated using Weblate (Polish)
Currently translated at 100.0% (77 of 77 strings)

Translation: Mira API/Mira API
Translate-URL: https://weblate.duikbo.at/projects/mira-api/mira-api/pl/
2026-10-20 14:19:07 +00:00
Anonymous e2a3dc782b Translated using Weblate (Russian)
Currently translated at 12.9% (10 of 77 strings)

Translation: Mira API/Mira API
Translate-URL: https://weblate.duikbo.at/projects/mira-api/mira-api/ru/
2026-10-19 06:19:07 +00:00
Anonymous 109dc796c5 Translated using Weblate (French)
Currently translated at 12.9% (10 of 77 strings)

Translation: Mira API/Mira API
Translate-URL: https://weblate.duikbo.at/projects/mira-api/mira-api/fr/
2026-10-19 06:19:07 +00:00
27 changed files with 158 additions and 491 deletions

No files matched your search

@@ -14,13 +14,8 @@ public sealed class ModdedOptionChangedAttribute(string? methodName = null, Type
{
internal Delegate? GetChanged(AbstractOptionGroup group, PropertyInfo property, IModdedOption option)
{
if (ResolveMethod(group, property, out var type, out var instanceGetter, out var notFound) is not { } method)
{
if (notFound)
Error($"Method {MemberName} does not exist in {type.FullName}");
if (ResolveMethod(group, property, out var type, out var instanceGetter) is not { } method)
return null;
}
var valueType = option.GetValueType();
@@ -33,13 +28,8 @@ public sealed class ModdedOptionChangedAttribute(string? methodName = null, Type
internal Func<int, Delegate?>? GetListChanged(AbstractOptionGroup group, PropertyInfo property, Type valueType)
{
if (ResolveMethod(group, property, out var type, out var instanceGetter, out var notFound) is not { } method)
{
if (notFound)
Error($"Method {MemberName} does not exist in {type.FullName}");
if (ResolveMethod(group, property, out var type, out var instanceGetter) is not { } method)
return null;
}
if (!ValidateListSignature(method, typeof(void), out var hasIndex, valueType))
return null;
@@ -14,13 +14,8 @@ public sealed class ModdedOptionGetterAttribute(string? methodName = null, Type?
{
internal Delegate? GetGetter(AbstractOptionGroup group, PropertyInfo property, IModdedOption option)
{
if (ResolveMethod(group, property, out var type, out var instanceGetter, out var notFound) is not { } method)
{
if (notFound)
Error($"Method {MemberName} does not exist in {type.FullName}");
if (ResolveMethod(group, property, out var type, out var instanceGetter) is not { } method)
return null;
}
var valueType = option.GetValueType();
@@ -33,13 +28,8 @@ public sealed class ModdedOptionGetterAttribute(string? methodName = null, Type?
internal Func<int, Delegate?>? GetListGetter(AbstractOptionGroup group, PropertyInfo property, Type valueType)
{
if (ResolveMethod(group, property, out var type, out var instanceGetter, out var notFound) is not { } method)
{
if (notFound)
Error($"Method {MemberName} does not exist in {type.FullName}");
if (ResolveMethod(group, property, out var type, out var instanceGetter) is not { } method)
return null;
}
if (!ValidateListSignature(method, valueType, out var hasIndex, valueType))
return null;
@@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using MiraAPI.Utilities;
namespace MiraAPI.GameOptions.Attributes;
@@ -128,7 +127,7 @@ public abstract class ModdedOptionMemberAttribute(string? memberName, Type? hold
}
/// <summary>
/// Resolves the target <see cref="MemberInfo"/> (property, field or method) and its associated singleton instance getter, if applicable.
/// Resolves the target <see cref="MemberInfo"/> (property or method) and its associated singleton instance getter, if applicable.
/// </summary>
/// <param name="group">The option group instance the property belongs to.</param>
/// <param name="property">The option property decorated with this attribute.</param>
@@ -137,18 +136,15 @@ public abstract class ModdedOptionMemberAttribute(string? memberName, Type? hold
/// When this method returns, contains the <see cref="OptionGroupSingleton{T}.Instance"/> getter <see cref="MethodInfo"/>
/// if the resolved member is an instance member on an <see cref="AbstractOptionGroup"/>; otherwise, <see langword="null"/>.
/// </param>
/// <param name="notFound">When this method returns, contains a flag that indicates if the member was not found, as null can be returned from other circumstances too.</param>
/// <returns>The resolved <see cref="MemberInfo"/> if found; otherwise, <see langword="null"/>.</returns>
[SuppressMessage("Major Code Smell", "S3011:Reflection should not be used to increase accessibility of classes, methods, or fields", Justification = "It is safe.")]
protected MemberInfo? ResolveMember(AbstractOptionGroup group, PropertyInfo property, out Type type, out MethodInfo? instanceGetter, out bool notFound)
protected MemberInfo? ResolveMember(AbstractOptionGroup group, PropertyInfo property, out Type type, out MethodInfo? instanceGetter)
{
var groupType = group.GetType();
var flags = BindingFlags.Static | BindingFlags.Public;
MemberName ??= property.Name + defaultSuffix;
type = holderType ?? groupType;
instanceGetter = null;
MemberName ??= property.Name + defaultSuffix;
if (type.IsAssignableTo(typeof(AbstractOptionGroup)))
{
@@ -162,69 +158,12 @@ public abstract class ModdedOptionMemberAttribute(string? memberName, Type? hold
.GetProperty(nameof(OptionGroupSingleton<>.Instance))!
.GetGetMethod();
}
else if (!type.IsStatic())
else
{
flags |= BindingFlags.Instance;
var canSearchPrivates = type.GetCustomAttribute<SearchPrivateAttribute>() != null;
if (canSearchPrivates)
flags |= BindingFlags.NonPublic;
var getterFlags = BindingFlags.Static | BindingFlags.Public | (canSearchPrivates ? BindingFlags.NonPublic : BindingFlags.Default);
foreach (var method in type.GetMethods(getterFlags))
{
if (method.GetParameters().Length > 0 || (method.GetCustomAttribute<InstanceGetterAttribute>() == null && method.Name != "GetInstance"))
continue;
instanceGetter = method;
break;
}
if (instanceGetter == null)
{
foreach (var prop in type.GetProperties(getterFlags))
{
if (prop.GetCustomAttribute<InstanceGetterAttribute>() == null && prop.Name != "Instance")
continue;
var getMethod = prop.GetGetMethod(canSearchPrivates);
if (getMethod == null)
continue;
instanceGetter = getMethod;
break;
}
}
if (instanceGetter == null)
{
Error($"No valid static instance getter (method GetInstance or property Instance) found in instanced type {type.Name}. Try renaming the method/property, or decorate it with InstanceGetterAttribute.");
notFound = false;
return null;
}
if (!instanceGetter.ReturnType.IsAssignableTo(type))
{
Error($"Found instance getter {instanceGetter.Name}, but the return type did not match what was expected ({type.Name}).");
notFound = false;
return null;
}
}
else if (type.GetCustomAttribute<SearchPrivateAttribute>() != null)
{
flags |= BindingFlags.NonPublic;
instanceGetter = null;
}
var member = type.GetMember(MemberName, flags).FirstOrDefault();
notFound = true;
if (member == null)
return null;
notFound = false;
if (instanceGetter == null)
return member;
@@ -257,11 +196,10 @@ public abstract class ModdedOptionMemberAttribute(string? memberName, Type? hold
/// When this method returns, contains the <see cref="OptionGroupSingleton{T}.Instance"/> getter <see cref="MethodInfo"/>
/// if the resolved method is an instance method on an <see cref="AbstractOptionGroup"/>; otherwise, <see langword="null"/>.
/// </param>
/// <param name="notFound">When this method returns, contains a flag that indicates if the member was not found, as null can be returned from other circumstances too.</param>
/// <returns>The resolved <see cref="MethodInfo"/> if found; otherwise, <see langword="null"/>.</returns>
protected MethodInfo? ResolveMethod(AbstractOptionGroup group, PropertyInfo property, out Type type, out MethodInfo? instanceGetter, out bool notFound)
protected MethodInfo? ResolveMethod(AbstractOptionGroup group, PropertyInfo property, out Type type, out MethodInfo? instanceGetter)
{
return ResolveMember(group, property, out type, out instanceGetter, out notFound) as MethodInfo;
return ResolveMember(group, property, out type, out instanceGetter) as MethodInfo;
}
/// <summary>
@@ -14,13 +14,8 @@ public sealed class ModdedOptionSetterAttribute(string? methodName = null, Type?
{
internal Delegate? GetSetter(AbstractOptionGroup group, PropertyInfo property, IModdedOption option)
{
if (ResolveMethod(group, property, out var type, out var instanceGetter, out var notFound) is not { } method)
{
if (notFound)
Error($"Method {MemberName} does not exist in {type.FullName}");
if (ResolveMethod(group, property, out var type, out var instanceGetter) is not { } method)
return null;
}
var valueType = option.GetValueType();
@@ -33,13 +28,8 @@ public sealed class ModdedOptionSetterAttribute(string? methodName = null, Type?
internal Func<int, Delegate?>? GetListSetter(AbstractOptionGroup group, PropertyInfo property, Type valueType)
{
if (ResolveMethod(group, property, out var type, out var instanceGetter, out var notFound) is not { } method)
{
if (notFound)
Error($"Method {MemberName} does not exist in {type.FullName}");
if (ResolveMethod(group, property, out var type, out var instanceGetter) is not { } method)
return null;
}
if (!ValidateListSignature(method, valueType, out var hasIndex, valueType, valueType))
return null;
@@ -20,11 +20,9 @@ public sealed class ModdedOptionVisibilityAttribute(string? memberName = null, T
internal Func<bool>? GetVisibility(AbstractOptionGroup group, PropertyInfo property)
{
if (ResolveMember(group, property, out Type type, out var instanceGetter, out var notFound) is not { } member)
if (ResolveMember(group, property, out Type type, out var instanceGetter) is not { } member)
{
if (notFound)
Error($"Member {MemberName} does not exist in {type.FullName}");
Error($"Member {MemberName} does not exist in {type.FullName}");
return null;
}
@@ -109,11 +107,9 @@ public sealed class ModdedOptionVisibilityAttribute(string? memberName = null, T
internal Func<int, Delegate?>? GetListVisibility(AbstractOptionGroup group, PropertyInfo property)
{
if (ResolveMember(group, property, out Type type, out var instanceGetter, out var notFound) is not { } member)
if (ResolveMember(group, property, out Type type, out var instanceGetter) is not { } member)
{
if (notFound)
Error($"Member {MemberName} does not exist in {type.FullName}");
Error($"Member {MemberName} does not exist in {type.FullName}");
return null;
}
@@ -1,15 +0,0 @@
using System;
namespace MiraAPI.GameOptions.Attributes;
/// <summary>
/// An attribute used to mark a method or property as one that returns an instance of the class it's declared in.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public sealed class InstanceGetterAttribute : Attribute;
/// <summary>
/// An attribute used to mark a class as one whose reflection search should include private members.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class SearchPrivateAttribute : Attribute;
-5
View File
@@ -77,11 +77,6 @@ public interface IModdedOption
/// </summary>
bool IncludeInPreset { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the option should show a notification, toggled on by an overall option synchronization.
/// </summary>
public bool ShowNotification { get; set; }
/// <summary>
/// Gets or sets the <see cref="BepInEx.Configuration.ConfigDefinition"/> for the option, used for BepInEx configuration.
/// </summary>
File renamed without changes.
+3 -4
View File
@@ -365,11 +365,11 @@ public static class ModdedOptionsManager
while (chunks.Count > 0)
{
Rpc<SyncOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, new MiraOptionNetData(false, chunks.Dequeue()));
Rpc<SyncOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, chunks.Dequeue());
}
}
internal static void HandleSyncOptions(bool showNotifs, NetData[] data)
internal static void HandleSyncOptions(NetData[] data)
{
// necessary to disable then re-enable this setting
// we don't know how other plugins handle their configs
@@ -388,7 +388,6 @@ public static class ModdedOptionsManager
continue;
}
option.ShowNotification = showNotifs;
option.HandleNetData(netData.Data);
}
@@ -398,7 +397,7 @@ public static class ModdedOptionsManager
plugin.PluginConfig.SaveOnConfigSet = oldConfigSetting[plugin];
}
if (LobbyInfoPane.InstanceExists)
if (LobbyInfoPane.Instance)
{
LobbyInfoPane.Instance.RefreshPane();
}
@@ -93,16 +93,13 @@ public class ModdedEnumOption : ModdedOption<int>
/// <inheritdoc />
protected override void OnValueChanged(int newValue)
{
if (ShowNotification)
{
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
Data.GetValueString(newValue),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
}
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
Data.GetValueString(newValue),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
if (!OptionBehaviour)
{
return;
@@ -199,17 +196,13 @@ public class ModdedEnumOption<T> : ModdedOption<T>
/// <inheritdoc />
protected override void OnValueChanged(T newValue)
{
if (ShowNotification)
{
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
Data.GetValueString(Convert.ToInt32(newValue, NumberFormatInfo.InvariantInfo)),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
}
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
Data.GetValueString(Convert.ToInt32(newValue, NumberFormatInfo.InvariantInfo)),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
if (!OptionBehaviour)
{
return;
@@ -199,16 +199,13 @@ public class ModdedNumberOption : ModdedOption<float>
protected override void OnValueChanged(float newValue)
{
Value = Mathf.Clamp(newValue, Min, Max);
if (ShowNotification)
{
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
Data.GetValueString(newValue),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
}
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
Data.GetValueString(newValue),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
if (OptionBehaviour is NumberOption opt)
{
@@ -79,9 +79,6 @@ public abstract class ModdedOption<T> : IModdedOption
/// <inheritdoc />
public bool IncludeInPreset { get; set; }
/// <inheritdoc />
public bool ShowNotification { get; set; } = true;
/// <inheritdoc />
public OptionBehaviour? OptionBehaviour { get; protected set; }
@@ -159,11 +156,10 @@ public abstract class ModdedOption<T> : IModdedOption
entry.Value = _value;
}
Rpc<SyncOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, new MiraOptionNetData(true, [GetNetData()]), true);
Rpc<SyncOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [GetNetData()], true);
}
OnValueChanged(_value);
ShowNotification = true;
}
/// <inheritdoc />
@@ -99,17 +99,13 @@ public class ModdedToggleOption : ModdedOption<bool>
/// <inheritdoc />
protected override void OnValueChanged(bool newValue)
{
if (ShowNotification)
{
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
(newValue ? OnValue : OffValue).Translate(),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
}
ModdedOptionsManager.AddSettingsChangeMessage(
HudManager.Instance.Notifier,
StringName,
(newValue ? OnValue : OffValue).Translate(),
Configuration.PopUpTextColor,
Configuration.PopUpIconTmp,
false);
if (!OptionBehaviour)
{
return;
-43
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Il2CppInterop.Runtime.Attributes;
using MiraAPI.GameOptions;
using MiraAPI.PluginLoading;
@@ -97,48 +96,6 @@ public abstract class BaseModifier : IOptionable
/// </summary>
public virtual string ModifierCategoryTitle => MiraLocaleManager.Get("Modifier");
/// <summary>
/// Gets a value indicating whether to exclude the "amount" from the displayed amount text within the role guide.
/// </summary>
public virtual bool ShowModifierAsUnique => false;
/// <summary>
/// Gets the displayed amount and chance of the modifier. Used in the role guide.
/// By default, 0 amount or chance results in a complete 0 amount and chance, so override if necessary.
/// </summary>
/// <param name="amount">The amount of the modifier.</param>
/// <param name="chance">The chance of the modifier.</param>
/// <param name="source">The mod source of the modifier.</param>
/// <returns>The <see cref="string"/> result.</returns>
public virtual string GetAmountText(int amount, int chance, string source)
{
if (amount == 0)
{
chance = 0;
}
if (ShowModifierAsUnique)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0}% ({1})",
chance.ToString(CultureInfo.InvariantCulture),
source);
}
if (chance == 0)
{
amount = 0;
}
return string.Format(
CultureInfo.InvariantCulture,
"{0} at {1}% ({2})",
amount.ToString(CultureInfo.InvariantCulture),
chance.ToString(CultureInfo.InvariantCulture),
source);
}
/// <summary>
/// Gets whether the modifier is forcibly shown or disabled in the wiki screen.
/// </summary>
+1 -2
View File
@@ -1,5 +1,4 @@
using System.Globalization;
using MiraAPI.GameModes;
using MiraAPI.GameModes;
using MiraAPI.PluginLoading;
namespace MiraAPI.Modifiers.Types;
+10 -13
View File
@@ -9,32 +9,29 @@ namespace MiraAPI.Networking;
[RegisterCustomRpc((uint)MiraRpc.SyncGameOptions)]
#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppression.)
// ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated by Reactor.)
internal sealed class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, MiraOptionNetData>(plugin, id)
internal sealed class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
#pragma warning restore SA1515 // Single-line comment should be preceded by blank line
{
public override RpcLocalHandling LocalHandling => RpcLocalHandling.None;
public override void Write(MessageWriter writer, MiraOptionNetData data)
public override void Write(MessageWriter writer, NetData[]? data)
{
/*if (data == null)
if (data == null)
{
writer.WritePacked(0U);
return;
}*/
}
var info = data.Data;
writer.Write(data.ShouldShowNotifications);
writer.WritePacked((uint)info.Length);
foreach (var netData in info)
writer.WritePacked((uint)data.Length);
foreach (var netData in data)
{
writer.WritePacked(netData.Id);
writer.WriteBytesAndSize(netData.Data);
}
}
public override MiraOptionNetData Read(MessageReader reader)
public override NetData[] Read(MessageReader reader)
{
var shouldShowNotif = reader.ReadBoolean();
var length = reader.ReadPackedUInt32();
var data = new NetData[length];
for (var i = 0; i < length; i++)
@@ -44,16 +41,16 @@ internal sealed class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCust
data[i] = new NetData(dataId, bytes);
}
return new MiraOptionNetData(shouldShowNotif, data);
return data;
}
public override void Handle(PlayerControl playerControl, MiraOptionNetData data)
public override void Handle(PlayerControl playerControl, NetData[]? data)
{
if (AmongUsClient.Instance.HostId != playerControl.OwnerId)
{
return;
}
ModdedOptionsManager.HandleSyncOptions(data.ShouldShowNotifications, data.Data ?? []);
ModdedOptionsManager.HandleSyncOptions(data ?? []);
}
}
+10 -19
View File
@@ -5,41 +5,32 @@ using Reactor.Networking.Rpc;
namespace MiraAPI.Networking;
internal readonly struct MiraOptionNetData(bool showNotif, NetData[] data)
{
public bool ShouldShowNotifications { get; } = showNotif;
public NetData[] Data { get; } = data;
}
[RegisterCustomRpc((uint)MiraRpc.SyncRoleOptions)]
#pragma warning disable SA1515 // Single-line comment should be preceded by blank line (Justification: ReSharper suppression.)
// ReSharper disable once ClassNeverInstantiated.Global (Justification: Instantiated by Reactor.)
internal sealed class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, MiraOptionNetData>(plugin, id)
internal sealed class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
#pragma warning restore SA1515 // Single-line comment should be preceded by blank line
{
public override RpcLocalHandling LocalHandling => RpcLocalHandling.None;
public override void Write(MessageWriter writer, MiraOptionNetData data)
public override void Write(MessageWriter writer, NetData[]? data)
{
/*if (data == null)
if (data == null)
{
writer.WritePacked(0U);
return;
}*/
}
var info = data.Data;
writer.Write(data.ShouldShowNotifications);
writer.WritePacked((uint)info.Length);
foreach (var netData in info)
writer.WritePacked((uint)data.Length);
foreach (var netData in data)
{
writer.WritePacked(netData.Id);
writer.WriteBytesAndSize(netData.Data);
}
}
public override MiraOptionNetData Read(MessageReader reader)
public override NetData[] Read(MessageReader reader)
{
var shouldShowNotif = reader.ReadBoolean();
var length = reader.ReadPackedUInt32();
var data = new NetData[length];
for (var i = 0; i < length; i++)
@@ -49,16 +40,16 @@ internal sealed class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : Player
data[i] = new NetData(dataId, bytes);
}
return new MiraOptionNetData(shouldShowNotif, data);
return data;
}
public override void Handle(PlayerControl playerControl, MiraOptionNetData data)
public override void Handle(PlayerControl playerControl, NetData[]? data)
{
if (AmongUsClient.Instance.HostId != playerControl.OwnerId)
{
return;
}
CustomRoleManager.HandleSyncRoleOptions(data.ShouldShowNotifications, data.Data ?? []);
CustomRoleManager.HandleSyncRoleOptions(data ?? []);
}
}
@@ -1,7 +1,5 @@
using HarmonyLib;
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using MiraAPI.Roles;
namespace MiraAPI.Patches.GameModes;
@@ -57,17 +55,4 @@ internal static class GameStartManagerPatches
__instance.MinPlayers = 1;
}
}
[HarmonyPrefix, HarmonyPatch(nameof(GameStartManager.BeginGame))]
public static void Prefix(GameStartManager __instance)
{
if (!AmongUsClient.Instance.AmHost)
{
return;
}
ModdedOptionsManager.SyncAllOptions();
CustomRoleManager.SyncAllRoleSettings();
GameModeOption.RpcSyncGamemode(PlayerControl.LocalPlayer, GameModeOption.Value);
}
}
+1 -1
View File
@@ -29,7 +29,7 @@ public static class HudManagerPatches
public static IEnumerator CoResizeUI()
{
while (!HudManager.InstanceExists) yield return null;
while (!HudManager.Instance) yield return null;
yield return new WaitForSeconds(0.01f);
ResizeUI(LocalSettingsTabSingleton<MiraApiSettings>.Instance.ButtonUIFactorSlider.Value);
@@ -495,7 +495,7 @@ public static class RoleSettingMenuPatches
if (AmongUsClient.Instance.AmHost)
{
Rpc<SyncRoleOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, new MiraOptionNetData(true, [role.GetNetData()]), true);
Rpc<SyncRoleOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [role.GetNetData()], true);
}
GameOptionsManager.Instance.GameHostOptions = GameOptionsManager.Instance.CurrentGameOptions;
+21 -100
View File
@@ -754,8 +754,7 @@ public static class RoleGuidePatches
modifier.ModifierCategoryTitle,
modifier.ParentMod.MiraPlugin.GetAbbreviatedModName(),
null,
modifier,
modifier.TypeId));
modifier));
}
}
@@ -791,8 +790,7 @@ public static class RoleGuidePatches
roleBehaviour.GetCategoryTitle(),
modId,
roleBehaviour,
null,
(uint)roleBehaviour.Role));
null));
}
_roleEntriesMode = activeModeType;
@@ -930,7 +928,7 @@ public static class RoleGuidePatches
}
}
private sealed class DetailedPanel(string title, string category, string modId, RoleBehaviour? role, GameModifier? modifier, uint typeId)
private sealed class DetailedPanel(string title, string category, string modId, RoleBehaviour? role, GameModifier? modifier)
{
public MatchInfoRolePanel? Panel { get; set; }
public string Title { get; } = title;
@@ -938,7 +936,6 @@ public static class RoleGuidePatches
public string ModId { get; } = modId;
public RoleBehaviour? Role { get; } = role;
public GameModifier? Modifier { get; } = modifier;
public uint TypeId { get; } = typeId;
public int Amount { get; set; }
public int Chance { get; set; }
public float Likelihood { get; set; }
@@ -1160,9 +1157,7 @@ public static class RoleGuidePatches
entry.Visible = entry.CanShow && MatchesSearch(entry, searchText);
if (entry.Panel)
{
dynamic entryType = entry.Modifier != null ? entry.Modifier : entry.Role!;
var text = GetPanelCountText(entryType, entry.Amount, entry.Chance, entry.ModId);
SetPanelCount(entry.Panel!, text);
SetPanelCount(entry.Panel!, entry.Amount, entry.Chance, entry.ModId);
if (!entry.Visible && entry.Panel!.gameObject.activeSelf)
{
entry.Panel.gameObject.SetActive(false);
@@ -1187,58 +1182,16 @@ public static class RoleGuidePatches
return shown;
}
public static IEnumerable<GameModifier> GetActiveModifiers()
{
return PlayerControl.LocalPlayer.GetModifiers<GameModifier>();
}
public static IEnumerable<RoleBehaviour> GetActiveRoles()
{
IEnumerable<RoleBehaviour> comparisons = [];
var curRole = PlayerControl.LocalPlayer.Data.Role;
comparisons = comparisons.AddItem(curRole);
if (PlayerControl.LocalPlayer && PlayerControl.LocalPlayer.Data && PlayerControl.LocalPlayer.Data.IsDead &&
!comparisons.Any(x => x.Role == PlayerControl.LocalPlayer.Data.RoleWhenAlive.Value))
{
comparisons = comparisons.AddItem(RoleManager.Instance.GetRole(PlayerControl.LocalPlayer.Data.RoleWhenAlive.Value));
}
return comparisons;
}
public static List<uint> GetActiveIds()
{
var activeModifiers = GetActiveModifiers();
List<uint> comparisons = activeModifiers.Select(x => x.TypeId).ToList();
comparisons.AddRange(GetActiveRoles().Select(x => (uint)x.Role));
return comparisons;
}
private static List<DetailedPanel> SortEntries(List<DetailedPanel> entries, string searchText)
{
var activeModifiers = PlayerControl.LocalPlayer.GetModifiers<GameModifier>().Select(x => x.TypeId);
var roles = GetActiveRoles().Select(x => (uint)x.Role).ToList();
List<uint> comparisons = activeModifiers.ToList();
comparisons.AddRange(roles);
var comparer = new BasicGuideComparer(comparisons);
var sorted = entries.Where(p => p.Visible)
.OrderByDescending(p => p.Title.Equals(searchText, StringComparison.OrdinalIgnoreCase))
.ThenByDescending(p =>
p.Title.Contains(searchText, StringComparison.OrdinalIgnoreCase) ||
p.Category.Contains(searchText, StringComparison.OrdinalIgnoreCase))
.ThenBy(x => x.Modifier != null ? x.Modifier.TypeId : (uint)x.Role!.Role, comparer);
return
[
.. _sortMethod is SortingMethod.Alphabetical
p.Category.Contains(searchText, StringComparison.OrdinalIgnoreCase));
return [.. _sortMethod is SortingMethod.Alphabetical
? sorted.ThenBy(p => p.GetSortKey(_sortGrouping))
: sorted.ThenByDescending(p => p.GetSortKey(_sortGrouping))
];
: sorted.ThenByDescending(p => p.GetSortKey(_sortGrouping))];
}
private static bool MatchesSearch(DetailedPanel entry, string searchText)
@@ -1698,15 +1651,10 @@ public static class RoleGuidePatches
public static Sprite GetRandomRoleIcon()
{
var roles = GetActiveRoles().Shuffle().ToList();
foreach (var role in roles)
var aliveRole = PlayerControl.LocalPlayer.Data?.Role;
if (aliveRole != null && aliveRole.RoleIconSolid != null)
{
if (role.RoleIconSolid == null)
{
continue;
}
return role.RoleIconSolid;
return aliveRole.RoleIconSolid;
}
return GetRandomFactionIcon();
@@ -1845,22 +1793,21 @@ public static class RoleGuidePatches
return false;
}
__instance.roleCount.text = string.Format(
CultureInfo.InvariantCulture,
"{0} at {1}%",
numPerGame.ToString(CultureInfo.InvariantCulture),
chancePerGame);
if (role is ICustomRole customRole)
{
__instance.roleName.text = customRole.RoleName;
__instance.roleDescription.text =
$"<size=60%>{role.GetCategoryTitle()}</size>\n" + customRole.RoleMedDescription;
__instance.roleIcon.sprite = customRole.Configuration.Icon?.LoadAsset();
var text = GetCustomRolePanelCountText(customRole, numPerGame, chancePerGame, customRole.ParentMod.MiraPlugin.GetAbbreviatedModName());
SetPanelCount(__instance, text);
__instance.roleCount.text += $" ({customRole.ParentMod.MiraPlugin.GetAbbreviatedModName()})";
}
else
{
__instance.roleCount.text = string.Format(
CultureInfo.InvariantCulture,
"{0} at {1}%",
numPerGame.ToString(CultureInfo.InvariantCulture),
chancePerGame);
__instance.roleName.text = role.NiceName;
__instance.roleDescription.text = $"<size=60%>{role.GetCategoryTitle()}</size>\n" + role.BlurbMed;
__instance.roleIcon.sprite = role.RoleIconColor;
@@ -1876,20 +1823,9 @@ public static class RoleGuidePatches
return false;
}
// NOTE: This is specifically meant to be a different method name as to not have the game check this method and the GetPanelCountText method made for roles overall.
private static string GetCustomRolePanelCountText(ICustomRole custom, int amount, int chance, string modId)
private static void SetPanelCount(MatchInfoRolePanel panel, int amount, int chance, string modId)
{
return custom.GetAmountText(amount, chance, modId);
}
private static string GetPanelCountText(RoleBehaviour role, int amount, int chance, string modId)
{
if (role is ICustomRole custom)
{
return custom.GetAmountText(amount, chance, modId);
}
return string.Format(
panel.roleCount.text = string.Format(
CultureInfo.InvariantCulture,
"{0} at {1}% ({2})",
amount.ToString(CultureInfo.InvariantCulture),
@@ -1897,18 +1833,6 @@ public static class RoleGuidePatches
modId);
}
private static string GetPanelCountText(BaseModifier modifier, int amount, int chance, string modId)
{
return modifier.GetAmountText(amount, chance, modId);
}
private static void SetPanelCount(MatchInfoRolePanel panel, string amountChanceText)
{
panel.roleCount.text = string.Format(
CultureInfo.InvariantCulture,
amountChanceText);
}
public static void SetRolePanel(
this MatchInfoRolePanel instance,
RoleBehaviour role,
@@ -1921,16 +1845,14 @@ public static class RoleGuidePatches
instance.roleDescription.text =
$"<size=60%>{role.GetCategoryTitle()}</size>\n" + customRole.RoleMedDescription;
instance.roleIcon.sprite = customRole.Configuration.Icon?.LoadAsset();
var text = GetCustomRolePanelCountText(customRole, numPerGame, chancePerGame, customRole.ParentMod.MiraPlugin.GetAbbreviatedModName());
SetPanelCount(instance, text);
SetPanelCount(instance, numPerGame, chancePerGame, customRole.ParentMod.MiraPlugin.GetAbbreviatedModName());
}
else
{
instance.roleName.text = role.NiceName;
instance.roleDescription.text = $"<size=60%>{role.GetCategoryTitle()}</size>\n" + role.BlurbMed;
instance.roleIcon.sprite = role.RoleIconColor;
var text = GetPanelCountText(role, numPerGame, chancePerGame, "AU");
SetPanelCount(instance, text);
SetPanelCount(instance, numPerGame, chancePerGame, "AU");
}
instance.roleIcon.SetSizeLimit(0.13f);
@@ -1951,8 +1873,7 @@ public static class RoleGuidePatches
instance.roleDescription.text =
$"<size=60%>{modifier.ModifierCategoryTitle}</size>\n" + modifier.ModifierMedDescription;
instance.roleIcon.sprite = modifier.ModifierIcon?.LoadAsset();
var text = GetPanelCountText(modifier, numPerGame, chancePerGame, modifier.ParentMod.MiraPlugin.GetAbbreviatedModName());
SetPanelCount(instance, text);
SetPanelCount(instance, numPerGame, chancePerGame, modifier.ParentMod.MiraPlugin.GetAbbreviatedModName());
instance.roleIcon.SetSizeLimit(0.13f);
instance.roleIcon.material.SetInt(PlayerMaterial.MaskLayer, 50);
+11 -11
View File
@@ -1,14 +1,14 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="MiraApi.TopRightButtonScaleFactor">Échelle de tailles de boutons</string>
<string name="MiraApi.WikiOnBottomRow">Bouton pour le Wiki sous l'interface principale</string>
<string name="Roles">Rôles</string>
<string name="Modifier">Modificateur</string>
<string name="Modifiers">Modificateurs</string>
<string name="MiraApi.RoleTeam.Crewmate">Coéquipier</string>
<string name="MiraApi.RoleTeam.Crewmate.Short">Équipage</string>
<string name="MiraApi.RoleTeam.Impostor">Imposteur</string>
<string name="MiraApi.RoleTeam.Impostor.Short">Impo</string>
<string name="MiraApi.RoleTeam.Neutral">Neutre</string>
<string name="MiraApi.RoleTeam.Neutral.Short">Neutre</string>
<string name="MiraApi.TopRightButtonScaleFactor">Échelle de tailles de boutons</string>
<string name="MiraApi.WikiOnBottomRow">Bouton pour le Wiki sous l'interface principale</string>
<string name="Roles">Rôles</string>
<string name="Modifier">Modificateur</string>
<string name="Modifiers">Modificateurs</string>
<string name="MiraApi.RoleTeam.Crewmate">Coéquipier</string>
<string name="MiraApi.RoleTeam.Crewmate.Short">Équipage</string>
<string name="MiraApi.RoleTeam.Impostor">Imposteur</string>
<string name="MiraApi.RoleTeam.Impostor.Short">Impo</string>
<string name="MiraApi.RoleTeam.Neutral">Neutre</string>
<string name="MiraApi.RoleTeam.Neutral.Short">Neutre</string>
</resources>
+40 -1
View File
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="MiraApi.TopRightButtonScaleFactor">Skala Przycisków w Prawym Górnym</string>
<string name="MiraApi.WikiOnBottomRow">Pokaż Wiki w Dolnym Rzędzie</string>
<string name="MiraApi.WikiOnBottomRow">Pokaż Przycisk w Dolnym Rzędzie</string>
<string name="Roles">Role</string>
<string name="Modifier">Modyfikator</string>
<string name="Modifiers">Modyfikatory</string>
@@ -40,4 +40,43 @@ Przeżyj przez wymagany czas, albo zabij chowających.</string>
<string name="MiraApi.RoleTeam.Impostor.Group">Role Intruzów</string>
<string name="MiraApi.RoleTeam.Neutral.Group">Role Neutralne</string>
<string name="MiraApi.RoleTeam.Other">Inne</string>
<string name="MiraApi.MatchInfoGuide">Informacje O Meczu</string>
<string name="MiraApi.WikiTabPosition.LeftSide">Po Lewo</string>
<string name="MiraApi.WikiTabPosition.RightSide">Po Prawo</string>
<string name="MiraApi.WikiTabPosition.Below">Poniżej UI</string>
<string name="MiraApi.Gamemode">Tryb Gry</string>
<string name="MiraApi.MatchInfoGuideScaleFactor">Skala Menu Z Informacjami O Meczu</string>
<string name="MiraApi.WikiTabPosition">Położenie Przycisków Informacji O Meczu</string>
<string name="MiraApi.ModifierDisplay.Page">Strona</string>
<string name="MiraApi.Page">Strona</string>
<string name="MiraApi.Main">Główne</string>
<string name="MiraApi.AbilityType.Basic">Umiejętność</string>
<string name="MiraApi.AbilityType.Indirect">Umiejętność Pośrednia</string>
<string name="MiraApi.AbilityType.Interaction">Umiejętność Interakcyjna</string>
<string name="MiraApi.AbilityType.Radius">Umiejętność z Zasięgiem</string>
<string name="MiraApi.AbilityType.Menu">Umiejętność z Menu</string>
<string name="MiraApi.AbilityType.Meeting">Umiejętność na Spotkaniu</string>
<string name="MiraApi.AbilityType.SelectiveMeeting">Umiejętność na Spotkaniu z Wyborem</string>
<string name="MiraApi.AbilityType.MeetingMenu">Umiejętność z Menu na Spotkaniu</string>
<string name="Options">Opcję</string>
<string name="MiraApi.Wiki.PlayersTab">Lista Graczy</string>
<string name="MiraApi.Wiki.SettingsTab">Ustawienia Gry</string>
<string name="MiraApi.Wiki.SearchPlaceholder">Wyszukaj...</string>
<string name="MiraApi.Wiki.FilterLabel">Filtruj</string>
<string name="MiraApi.Wiki.OrderLabel">Kolejność</string>
<string name="MiraApi.Wiki.GroupingLabel">Grupuj</string>
<string name="MiraApi.Wiki.VanillaSettings">Vanilla</string>
<string name="MiraApi.Wiki.SearchResults">Wyniki Wyszukiwania</string>
<string name="MiraApi.Wiki.PreviousPage">Poprzednia</string>
<string name="MiraApi.Wiki.NextPage">Następna</string>
<string name="MiraApi.Wiki.NoResultsFound">Brak Wyników</string>
<string name="On">Wł</string>
<string name="Off">Wył</string>
<string name="Enabled">Włączone</string>
<string name="Disabled">Wyłączone</string>
<string name="Active">Aktywne</string>
<string name="Inactive">Nieaktywne</string>
<string name="Activated">Aktywowane</string>
<string name="Deactivated">Dezaktywowane</string>
<string name="MiraApi.EnableVariableColors">Włącz Kolory Zmienne</string>
</resources>
+11 -11
View File
@@ -1,14 +1,14 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="MiraApi.TopRightButtonScaleFactor">Множитель разрешения верх.-правого интерфейса</string>
<string name="MiraApi.WikiOnBottomRow">Кнопка вики под основным интерфейсом</string>
<string name="Roles">Роли</string>
<string name="Modifier">Атрибут</string>
<string name="Modifiers">Атрибуты</string>
<string name="MiraApi.RoleTeam.Crewmate">Член Экипажа</string>
<string name="MiraApi.RoleTeam.Crewmate.Short">Экипаж</string>
<string name="MiraApi.RoleTeam.Impostor">Предатель</string>
<string name="MiraApi.RoleTeam.Impostor.Short">Пред</string>
<string name="MiraApi.RoleTeam.Neutral">Нейтрал</string>
<string name="MiraApi.RoleTeam.Neutral.Short">Нейт</string>
<string name="MiraApi.TopRightButtonScaleFactor">Множитель разрешения верх.-правого интерфейса</string>
<string name="MiraApi.WikiOnBottomRow">Кнопка вики под основным интерфейсом</string>
<string name="Roles">Роли</string>
<string name="Modifier">Атрибут</string>
<string name="Modifiers">Атрибуты</string>
<string name="MiraApi.RoleTeam.Crewmate">Член Экипажа</string>
<string name="MiraApi.RoleTeam.Crewmate.Short">Экипаж</string>
<string name="MiraApi.RoleTeam.Impostor">Предатель</string>
<string name="MiraApi.RoleTeam.Impostor.Short">Пред</string>
<string name="MiraApi.RoleTeam.Neutral">Нейтрал</string>
<string name="MiraApi.RoleTeam.Neutral.Short">Нейт</string>
</resources>
+3 -6
View File
@@ -280,11 +280,11 @@ public static class CustomRoleManager
while (data.Count > 0)
{
Rpc<SyncRoleOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, new MiraOptionNetData(false, data.Dequeue()));
Rpc<SyncRoleOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.Dequeue());
}
}
internal static void HandleSyncRoleOptions(bool showNotifs, NetData[] data)
internal static void HandleSyncRoleOptions(NetData[] data)
{
// necessary to disable then re-enable this setting
// we don't know how other plugins handle their configs
@@ -312,10 +312,7 @@ public static class CustomRoleManager
var num = BitConverter.ToInt32(netData.Data, 0);
var chance = BitConverter.ToInt32(netData.Data, 4);
if (showNotifs)
{
HudManager.Instance.Notifier.AddRoleSettingsChangeMessage(role.StringName, num, chance, role.TeamType, false);
}
HudManager.Instance.Notifier.AddRoleSettingsChangeMessage(role.StringName, num, chance, role.TeamType, false);
try
{
-43
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using BepInEx.Configuration;
using Il2CppInterop.Runtime.Attributes;
@@ -93,48 +92,6 @@ public interface ICustomRole : IOptionable
_ => MiraLocaleManager.Get("MiraApi.RoleTeam.Other"),
};
/// <summary>
/// Gets a value indicating whether to exclude the "amount" from the displayed amount text within the role guide.
/// </summary>
public virtual bool ShowRoleAsUnique => Configuration.MaxRoleCount == 1;
/// <summary>
/// Gets the displayed amount and chance of the role. Used in the role guide.
/// By default, 0 amount or chance results in a complete 0 amount and chance, so override if necessary.
/// </summary>
/// <param name="amount">The amount of the role.</param>
/// <param name="chance">The chance of the role.</param>
/// <param name="source">The mod source of the role.</param>
/// <returns>The <see cref="string"/> result.</returns>
public virtual string GetAmountText(int amount, int chance, string source)
{
if (amount == 0)
{
chance = 0;
}
if (ShowRoleAsUnique)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0}% ({1})",
chance.ToString(CultureInfo.InvariantCulture),
source);
}
if (chance == 0)
{
amount = 0;
}
return string.Format(
CultureInfo.InvariantCulture,
"{0} at {1}% ({2})",
amount.ToString(CultureInfo.InvariantCulture),
chance.ToString(CultureInfo.InvariantCulture),
source);
}
/// <summary>
/// Gets the <see cref="Color"/> of the role.
/// </summary>
-41
View File
@@ -1,41 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace MiraAPI.Utilities;
public class BasicGuideComparer(IEnumerable<uint> activeIds) : IComparer<uint>
{
public int Compare(uint x, uint y)
{
if (x == null && y == null)
{
return 0;
}
if (x == null)
{
return -1;
}
if (y == null)
{
return 1;
}
var xActive = activeIds.Contains(x);
var yActive = activeIds.Contains(y);
if (xActive && !yActive)
{
return -1;
}
if (!xActive && yActive)
{
return 1;
}
return 0;
}
}