mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-21 21:43:26 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0161ba85c | ||
|
|
2411ac28a4 | ||
|
|
1bc0950acc | ||
|
|
3aecd5a91c | ||
|
|
836823c477 | ||
|
|
c24404620b | ||
|
|
7471a43d39 | ||
|
|
dd6d57343e | ||
|
|
fd75c99850 | ||
|
|
6e3de08043 | ||
|
|
1fa30485b0 | ||
|
|
faed84d0f6 | ||
|
|
2f7b0692f3 | ||
|
|
8123381e1e | ||
|
|
4d36c13c5b | ||
|
|
91b7901528 | ||
|
|
9a27a0c3d8 | ||
|
|
cda441ee56 | ||
|
|
6ee83f8c7d | ||
|
|
8712a2b4e7 | ||
|
|
30d2edfd9c | ||
|
|
76cb2a6a59 | ||
|
|
f20e7459a4 | ||
|
|
f462a116d7 | ||
|
|
6f771821c8 | ||
|
|
754b8ef684 | ||
|
|
d8eb671b49 | ||
|
|
24ebee2743 | ||
|
|
878cf15a20 | ||
|
|
2f7cf4d664 | ||
|
|
4fcfb2a9ee | ||
|
|
41bedd3e16 | ||
|
|
d27bf67f65 | ||
|
|
76ddfa4126 | ||
|
|
400ad535a4 | ||
|
|
eaf5890f67 | ||
|
|
b2651adcab | ||
|
|
c8c6ab1f61 | ||
|
|
7f65f2d5bb | ||
|
|
17da594207 | ||
|
|
fa8fdfc4f6 | ||
|
|
75dd5e8a35 | ||
|
|
a442f54b82 | ||
|
|
24dca63df1 | ||
|
|
08a4dc75f2 | ||
|
|
4e474dc5b2 | ||
|
|
3deffb701f | ||
|
|
f83dce99ef | ||
|
|
9c14db0321 | ||
|
|
f92d08b825 | ||
|
|
ae10917f2a | ||
|
|
51d08bb168 | ||
|
|
0e56a30c5c | ||
|
|
74055b8a17 | ||
|
|
812cef5f2b | ||
|
|
9665188a18 | ||
|
|
cb943c2173 | ||
|
|
dc483adc1c | ||
|
|
3d9bbc0833 | ||
|
|
b032e91e08 | ||
|
|
3cada4cb3d | ||
|
|
97ded2fc32 | ||
|
|
f207849e5e | ||
|
|
f8bb63483f | ||
|
|
a3dd4acf43 | ||
|
|
f17089f435 | ||
|
|
d1c469746e | ||
|
|
b053684235 | ||
|
|
0f26cadd9a | ||
|
|
a07c332dce | ||
|
|
74ab8c5581 | ||
|
|
aa72770bfc | ||
|
|
2b71438cd0 | ||
|
|
e22032e1fb |
No files matched your search
@@ -0,0 +1,2 @@
|
||||
[*.{cs,vb}]
|
||||
dotnet_diagnostic.CA1707.severity = none
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Reactor" Version="2.3.1" />
|
||||
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.697" Private="false" ExcludeAssets="runtime;native" />
|
||||
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2024.8.13" PrivateAssets="all" />
|
||||
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2024.9.4" PrivateAssets="all" />
|
||||
|
||||
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" />
|
||||
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.0.1" PrivateAssets="all" ExcludeAssets="runtime" />
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
|
||||
<VersionPrefix>0.1.4</VersionPrefix>
|
||||
<VersionPrefix>0.1.9</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
|
||||
<Authors>All Of Us, XtraCube, Angxl</Authors>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using MiraAPI.Example.Modifiers.Freezer;
|
||||
using MiraAPI.Example.Options.Roles;
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
@@ -11,9 +13,9 @@ namespace MiraAPI.Example.Buttons.Freezer;
|
||||
public class FreezeButton : CustomActionButton<PlayerControl>
|
||||
{
|
||||
public override string Name => "Freeze";
|
||||
public override float Cooldown => 5f;
|
||||
public override float Cooldown => OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeDuration;
|
||||
public override float EffectDuration => 0f;
|
||||
public override int MaxUses => 0;
|
||||
public override int MaxUses => (int)OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeUses;
|
||||
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
||||
|
||||
protected override void OnClick()
|
||||
@@ -23,7 +25,7 @@ public class FreezeButton : CustomActionButton<PlayerControl>
|
||||
|
||||
public override PlayerControl? GetTarget()
|
||||
{
|
||||
return PlayerControl.LocalPlayer.GetClosestPlayer(true, Distance, false);
|
||||
return PlayerControl.LocalPlayer.GetClosestPlayer(true, Distance);
|
||||
}
|
||||
|
||||
public override void SetOutline(bool active)
|
||||
|
||||
@@ -21,7 +21,7 @@ public class MeetingButton : CustomActionButton
|
||||
|
||||
public override bool Enabled(RoleBehaviour? role)
|
||||
{
|
||||
return PlayerControl.LocalPlayer.HasModifier<CaptainModifier>();
|
||||
return PlayerControl.LocalPlayer != null && PlayerControl.LocalPlayer.HasModifier<CaptainModifier>();
|
||||
}
|
||||
|
||||
protected override void OnClick()
|
||||
|
||||
@@ -13,7 +13,7 @@ public class TeleportButton : CustomActionButton
|
||||
{
|
||||
public override string Name => "Teleport";
|
||||
|
||||
public override float Cooldown => OptionGroupSingleton<TeleporterOptions>.Instance.TeleportCooldown;
|
||||
public override float Cooldown => OptionGroupSingleton<TeleporterOptions>.Instance.TeleportCooldown.Value;
|
||||
|
||||
public override float EffectDuration => OptionGroupSingleton<TeleporterOptions>.Instance.TeleportDuration;
|
||||
|
||||
@@ -22,7 +22,7 @@ public class TeleportButton : CustomActionButton
|
||||
public override LoadableAsset<Sprite> Sprite => ExampleAssets.TeleportButton;
|
||||
public static bool IsZoom { get; private set; }
|
||||
|
||||
public override bool Enabled(RoleBehaviour role)
|
||||
public override bool Enabled(RoleBehaviour? role)
|
||||
{
|
||||
return role is TeleporterRole;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ public static class ExampleAssets
|
||||
{
|
||||
public static LoadableResourceAsset ExampleButton { get; } = new("MiraAPI.Example.Resources.ExampleButton.png");
|
||||
|
||||
// Credit to EpicHorrors for the teleport button asset.
|
||||
// Credit to EpicHorrors for the teleport button asset.
|
||||
public static LoadableResourceAsset TeleportButton { get; } = new("MiraAPI.Example.Resources.TeleportButton.png");
|
||||
public static LoadableResourceAsset Banner { get; } = new("MiraAPI.Example.Resources.FortniteBanner.jpeg");
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,9 @@ namespace MiraAPI.Example;
|
||||
[RegisterCustomColors]
|
||||
public static class ExampleColors
|
||||
{
|
||||
public static CustomColor Cerulean { get; } = new("Cerulean", new Color(0.0f, 0.48f, 0.65f));
|
||||
public static CustomColor Cerulean { get; } = new("Cerulean", new Color(0.0f, 0.48f, 0.65f));
|
||||
|
||||
public static CustomColor Rose { get; } = new("Rose", new Color(0.98f, 0.26f, 0.62f));
|
||||
|
||||
|
||||
public static CustomColor Gold { get; } = new("Gold", new Color(1.0f, 0.84f, 0.0f));
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,21 @@
|
||||
using Reactor.Utilities.Attributes;
|
||||
using System;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using Reactor.Utilities.ImGui;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example;
|
||||
|
||||
[RegisterInIl2Cpp]
|
||||
public class MiraDebugWindow(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
public class MiraDebugWindow(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
public readonly DragWindow DebuggingWindow = new(new Rect(10, 10, 0, 0), "MIRA API DEBUGGING", () =>
|
||||
{
|
||||
if (GUILayout.Button("Test modifier"))
|
||||
[HideFromIl2Cpp]
|
||||
public DragWindow DebuggingWindow { get; } = new(
|
||||
new Rect(10, 10, 0, 0),
|
||||
"MIRA API DEBUGGING",
|
||||
() =>
|
||||
{
|
||||
//PlayerControl.LocalPlayer.AddModifier<ModifierTimerExample>();
|
||||
}
|
||||
if (GUILayout.Button("Remove modifier"))
|
||||
{
|
||||
//PlayerControl.LocalPlayer.RemoveModifier<ModifierTimerExample>();
|
||||
}
|
||||
})
|
||||
})
|
||||
{
|
||||
Enabled = true,
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@ public class ExampleOptions2 : AbstractOptionGroup
|
||||
{
|
||||
public override string GroupName => "Example Options 2";
|
||||
|
||||
public override uint GroupPriority => 0; // This group will be displayed first. The default value is uint.MaxValue.
|
||||
|
||||
public ModdedToggleOption ToggleOpt1 { get; } = new("Toggle Option 1", false);
|
||||
|
||||
public ModdedToggleOption ToggleOpt2 { get; } = new("Toggle Option 2", false)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Example.Options.Roles;
|
||||
|
||||
public class CustomRole2Settings : AbstractOptionGroup
|
||||
{
|
||||
public override string GroupName => "Custom Role";
|
||||
|
||||
public override Type AdvancedRole => typeof(FreezerRole);
|
||||
|
||||
[ModdedToggleOption("Teleportation ability")] public bool Teleport { get; set; } = true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using System;
|
||||
using MiraAPI.Utilities;
|
||||
|
||||
namespace MiraAPI.Example.Options.Roles;
|
||||
|
||||
public class FreezerRoleSettings : AbstractOptionGroup
|
||||
{
|
||||
public override string GroupName => "Custom Role";
|
||||
|
||||
public override Type AdvancedRole => typeof(FreezerRole);
|
||||
|
||||
[ModdedNumberOption("Freeze Duration", 1, 15, 1, MiraNumberSuffixes.Seconds)]
|
||||
public float FreezeDuration { get; set; } = 5;
|
||||
|
||||
[ModdedNumberOption("Freeze Uses", 1, 5)]
|
||||
public float FreezeUses { get; set; } = 1;
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using MiraAPI.Example.Roles;
|
||||
using System;
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using MiraAPI.GameOptions.OptionTypes;
|
||||
using MiraAPI.Utilities;
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Example.Options.Roles;
|
||||
|
||||
@@ -12,8 +13,7 @@ public class TeleporterOptions : AbstractOptionGroup
|
||||
|
||||
public override Type AdvancedRole => typeof(TeleporterRole);
|
||||
|
||||
[ModdedNumberOption("Teleport Cooldown", 5, 60, 2.5f, MiraNumberSuffixes.Seconds)]
|
||||
public float TeleportCooldown { get; set; } = 5;
|
||||
public ModdedNumberOption TeleportCooldown { get; set; } = new("Teleport Cooldown", 10, 5, 60, 2.5f, MiraNumberSuffixes.Seconds);
|
||||
|
||||
[ModdedNumberOption("Teleport Duration", 5, 25, 1, MiraNumberSuffixes.Seconds)]
|
||||
public float TeleportDuration { get; set; } = 10;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using MiraAPI.Roles;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example.Roles;
|
||||
|
||||
[RegisterCustomRole]
|
||||
public class ChameloenRole : CrewmateRole, ICustomRole
|
||||
{
|
||||
public string RoleName => "Chamelon";
|
||||
public string RoleLongDescription => "Stay invisible while not moving.";
|
||||
public string RoleDescription => RoleLongDescription;
|
||||
public Color RoleColor => Palette.AcceptedGreen;
|
||||
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
|
||||
|
||||
public CustomRoleConfiguration Configuration => new CustomRoleConfiguration(this)
|
||||
{
|
||||
OptionsScreenshot = ExampleAssets.Banner,
|
||||
};
|
||||
|
||||
public void PlayerControlFixedUpdate(PlayerControl playerControl)
|
||||
{
|
||||
if (playerControl.MyPhysics.Velocity.magnitude > 0)
|
||||
{
|
||||
SpriteRenderer rend = playerControl.cosmetics.currentBodySprite.BodySprite;
|
||||
TextMeshPro tmp = playerControl.cosmetics.nameText;
|
||||
tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, 1), Time.deltaTime * 4f);
|
||||
rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, 1), Time.deltaTime * 4f);
|
||||
|
||||
foreach (var cosmetic in playerControl.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
{
|
||||
cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, 1), Time.deltaTime * 4f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SpriteRenderer rend = playerControl.cosmetics.currentBodySprite.BodySprite;
|
||||
TextMeshPro tmp = playerControl.cosmetics.nameText;
|
||||
tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
|
||||
foreach (var cosmetic in playerControl.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
|
||||
{
|
||||
cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, playerControl.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using MiraAPI.Roles;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example.Roles;
|
||||
@@ -13,7 +11,10 @@ public class FreezerRole : ImpostorRole, ICustomRole
|
||||
public string RoleDescription => RoleLongDescription;
|
||||
public Color RoleColor => Palette.Blue;
|
||||
public ModdedRoleTeams Team => ModdedRoleTeams.Impostor;
|
||||
[HideFromIl2Cpp]
|
||||
public LoadableAsset<Sprite> OptionsScreenshot => ExampleAssets.Banner;
|
||||
public int MaxPlayers => 2;
|
||||
|
||||
public CustomRoleConfiguration Configuration => new CustomRoleConfiguration(this)
|
||||
{
|
||||
OptionsScreenshot = ExampleAssets.Banner,
|
||||
MaxRoleCount = 2,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using MiraAPI.Roles;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example.Roles;
|
||||
|
||||
[RegisterCustomRole]
|
||||
public class NeutralKillerRole : ImpostorRole, ICustomRole
|
||||
{
|
||||
public string RoleName => "Neutral Killer";
|
||||
public string RoleDescription => "Neutral who can kill.";
|
||||
public string RoleLongDescription => RoleDescription;
|
||||
public Color RoleColor => Color.magenta;
|
||||
public ModdedRoleTeams Team => ModdedRoleTeams.Neutral;
|
||||
|
||||
public CustomRoleConfiguration Configuration => new CustomRoleConfiguration(this)
|
||||
{
|
||||
UseVanillaKillButton = true,
|
||||
CanGetKilled = true,
|
||||
CanUseVent = true,
|
||||
};
|
||||
|
||||
public override void SpawnTaskHeader(PlayerControl playerControl)
|
||||
{
|
||||
// remove existing task header.
|
||||
}
|
||||
|
||||
public override bool DidWin(GameOverReason gameOverReason)
|
||||
{
|
||||
return GameManager.Instance.DidHumansWin(gameOverReason);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using MiraAPI.Roles;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example.Roles;
|
||||
@@ -13,6 +11,12 @@ public class TeleporterRole : CrewmateRole, ICustomRole
|
||||
public string RoleDescription => RoleLongDescription;
|
||||
public Color RoleColor => new Color32(221, 176, 152, 255);
|
||||
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
|
||||
[HideFromIl2Cpp]
|
||||
public LoadableAsset<Sprite> OptionsScreenshot => ExampleAssets.Banner;
|
||||
|
||||
public CustomRoleConfiguration Configuration => new CustomRoleConfiguration(this)
|
||||
{
|
||||
OptionsScreenshot = ExampleAssets.Banner,
|
||||
CanModifyChance = false,
|
||||
DefaultChance = 73,
|
||||
DefaultRoleCount = 4,
|
||||
};
|
||||
}
|
||||
@@ -6,12 +6,12 @@ using System.Linq;
|
||||
namespace MiraAPI.GameModes;
|
||||
|
||||
/// <summary>
|
||||
/// Manages custom gamemodes
|
||||
/// Manages custom gamemodes.
|
||||
/// </summary>
|
||||
public static class CustomGameModeManager
|
||||
{
|
||||
/// <summary>
|
||||
/// List of registered gamemodes
|
||||
/// List of registered gamemodes.
|
||||
/// </summary>
|
||||
internal static readonly Dictionary<int, CustomGameMode> GameModes = [];
|
||||
|
||||
@@ -21,14 +21,14 @@ public static class CustomGameModeManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current gamemode
|
||||
/// Current gamemode.
|
||||
/// </summary>
|
||||
public static CustomGameMode? ActiveMode { get; internal set; } = new DefaultMode();
|
||||
|
||||
/// <summary>
|
||||
/// Set current gamemode
|
||||
/// Set current gamemode.
|
||||
/// </summary>
|
||||
/// <param name="id">gamemode ID</param>
|
||||
/// <param name="id">gamemode ID.</param>
|
||||
public static void SetGameMode(int id)
|
||||
{
|
||||
if (GameModes.TryGetValue(id, out var gameMode))
|
||||
@@ -41,9 +41,9 @@ public static class CustomGameModeManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register gamemode from type
|
||||
/// Register gamemode from type.
|
||||
/// </summary>
|
||||
/// <param name="gameModeType">Type of gamemode class, should inherit from <see cref="CustomGameMode"/></param>
|
||||
/// <param name="gameModeType">Type of gamemode class, should inherit from <see cref="CustomGameMode"/>.</param>
|
||||
internal static void RegisterGameMode(Type gameModeType)
|
||||
{
|
||||
if (!typeof(CustomGameMode).IsAssignableFrom(gameModeType))
|
||||
@@ -59,7 +59,7 @@ public static class CustomGameModeManager
|
||||
Logger<MiraApiPlugin>.Error($"Failed to create instance of {gameModeType.Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GameModes.Any(x => x.Key == gameMode.Id))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"ID for gamemode {gameMode.Name} already exists!");
|
||||
@@ -68,4 +68,4 @@ public static class CustomGameModeManager
|
||||
|
||||
GameModes.Add(gameMode.Id, gameMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,4 @@ public class DefaultMode : CustomGameMode
|
||||
public override string Name => "Default";
|
||||
public override string Description => "Default Among Us GameMode";
|
||||
public override int Id => 0;
|
||||
}
|
||||
}
|
||||
@@ -25,4 +25,4 @@ public class RegisterGameModeAttribute : Attribute
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,12 @@ public abstract class AbstractOptionGroup
|
||||
/// </summary>
|
||||
public virtual Color GroupColor => Color.clear;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the group priority. This is used to determine the order in which groups are displayed in the options menu.
|
||||
/// Zero is the highest priority, and the default value is the max uint value.
|
||||
/// </summary>
|
||||
public virtual uint GroupPriority => uint.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role the group is associated with. This is used for the advanced role options menu.
|
||||
/// </summary>
|
||||
|
||||
@@ -12,12 +12,12 @@ internal interface IModdedOption
|
||||
string Title { get; }
|
||||
StringNames StringName { get; }
|
||||
IMiraPlugin? ParentMod { get; set; }
|
||||
BaseGameSetting? Data { get; }
|
||||
BaseGameSetting Data { get; }
|
||||
Type? AdvancedRole { get; set; }
|
||||
OptionBehaviour? OptionBehaviour { get; }
|
||||
Func<bool> Visible { get; set; }
|
||||
ConfigDefinition? ConfigDefinition { get; set; }
|
||||
OptionBehaviour? CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
|
||||
OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
|
||||
float GetFloatData();
|
||||
NetData GetNetData();
|
||||
void HandleNetData(byte[] data);
|
||||
|
||||
@@ -18,10 +18,10 @@ namespace MiraAPI.GameOptions;
|
||||
/// </summary>
|
||||
public static class ModdedOptionsManager
|
||||
{
|
||||
private static readonly Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = new();
|
||||
private static readonly Dictionary<Type, AbstractOptionGroup> TypeToGroup = new();
|
||||
private static readonly Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = [];
|
||||
private static readonly Dictionary<Type, AbstractOptionGroup> TypeToGroup = [];
|
||||
|
||||
internal static readonly Dictionary<uint, IModdedOption> ModdedOptions = new();
|
||||
internal static readonly Dictionary<uint, IModdedOption> ModdedOptions = [];
|
||||
internal static readonly List<AbstractOptionGroup> Groups = [];
|
||||
|
||||
internal static uint NextId => _nextId++;
|
||||
@@ -45,6 +45,10 @@ public static class ModdedOptionsManager
|
||||
TypeToGroup.Add(type, group);
|
||||
pluginInfo.OptionGroups.Add(group);
|
||||
|
||||
typeof(OptionGroupSingleton<>).MakeGenericType(type)
|
||||
.GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic)!
|
||||
.SetValue(null, group);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
|
||||
namespace MiraAPI.GameOptions;
|
||||
|
||||
@@ -7,13 +6,14 @@ namespace MiraAPI.GameOptions;
|
||||
/// Singleton for option groups.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The option group type.</typeparam>
|
||||
public class OptionGroupSingleton<T> where T : AbstractOptionGroup
|
||||
public static class OptionGroupSingleton<T> where T : AbstractOptionGroup
|
||||
{
|
||||
private static T? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the option group.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Can't</exception>
|
||||
#pragma warning disable CA1000
|
||||
public static T Instance => _instance ??= ModdedOptionsManager.Groups.OfType<T>().Single();
|
||||
#pragma warning restore CA1000
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using MiraAPI.Networking;
|
||||
using Reactor.Localization.Utilities;
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using MiraAPI.Networking;
|
||||
using Reactor.Localization.Utilities;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
@@ -86,7 +86,7 @@ public class ModdedEnumOption : ModdedOption<int>
|
||||
/// <inheritdoc />
|
||||
protected override void OnValueChanged(int newValue)
|
||||
{
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, Data?.GetValueString(newValue), false);
|
||||
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, Data?.GetValueString(newValue), false);
|
||||
if (!OptionBehaviour)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -134,7 +134,7 @@ public class ModdedNumberOption : ModdedOption<float>
|
||||
protected override void OnValueChanged(float newValue)
|
||||
{
|
||||
Value = Mathf.Clamp(newValue, Min, Max);
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(
|
||||
HudManager.Instance.Notifier.AddSettingsChangeMessage(
|
||||
StringName,
|
||||
Data?.GetValueString(Value),
|
||||
false);
|
||||
|
||||
@@ -35,7 +35,7 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
/// <summary>
|
||||
/// Gets the BaseGameSetting data of the option.
|
||||
/// </summary>
|
||||
public BaseGameSetting? Data { get; protected init; }
|
||||
public BaseGameSetting Data { get; protected init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent mod of the option.
|
||||
|
||||
@@ -69,7 +69,7 @@ public class ModdedToggleOption : ModdedOption<bool>
|
||||
/// <inheritdoc />
|
||||
protected override void OnValueChanged(bool newValue)
|
||||
{
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddSettingsChangeMessage(StringName, newValue ? "On" : "Off", false);
|
||||
HudManager.Instance.Notifier.AddSettingsChangeMessage(StringName, newValue ? "On" : "Off", false);
|
||||
if (!OptionBehaviour)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -167,6 +167,7 @@ public abstract class CustomActionButton
|
||||
public void SetUses(int amount)
|
||||
{
|
||||
UsesLeft = Mathf.Clamp(amount, 0, int.MaxValue);
|
||||
Button?.SetUsesRemaining(MaxUses);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,5 +13,7 @@ public static class CustomButtonSingleton<T> where T : CustomActionButton
|
||||
/// <summary>
|
||||
/// Gets the instance of the button.
|
||||
/// </summary>
|
||||
#pragma warning disable CA1000
|
||||
public static T Instance => _instance ??= CustomButtonManager.CustomButtons.OfType<T>().Single();
|
||||
#pragma warning restore CA1000
|
||||
}
|
||||
@@ -6,4 +6,4 @@ namespace MiraAPI.Hud;
|
||||
/// Attribute to register a button in the HUD. Necessary for the button to be recognized by Mira.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class RegisterButtonAttribute : Attribute;
|
||||
public class RegisterButtonAttribute : Attribute;
|
||||
@@ -16,10 +16,10 @@ namespace MiraAPI;
|
||||
[ReactorModFlags(ModFlags.RequireOnAllClients)]
|
||||
public partial class MiraApiPlugin : BasePlugin
|
||||
{
|
||||
public Harmony Harmony { get; } = new(Id);
|
||||
private static MiraPluginManager PluginManager { get; set; }
|
||||
private static MiraPluginManager? PluginManager { get; set; }
|
||||
|
||||
public static Color MiraColor = new Color32(238, 154, 112, 255);
|
||||
public Harmony Harmony { get; } = new(Id);
|
||||
public static Color MiraColor { get; } = new Color32(238, 154, 112, 255);
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
|
||||
@@ -10,6 +10,11 @@ public abstract class BaseModifier
|
||||
/// </summary>
|
||||
public PlayerControl? Player { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the modifier component that the modifier is attached to.
|
||||
/// </summary>
|
||||
public ModifierComponent? ModifierComponent { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the modifier id.
|
||||
/// </summary>
|
||||
@@ -62,7 +67,8 @@ public abstract class BaseModifier
|
||||
/// <summary>
|
||||
/// Called when the player dies.
|
||||
/// </summary>
|
||||
public virtual void OnDeath()
|
||||
/// <param name="reason">The Death Reason.</param>
|
||||
public virtual void OnDeath(DeathReason reason)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace MiraAPI.Modifiers;
|
||||
/// The component for handling modifiers.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the active modifiers on the player.
|
||||
@@ -39,11 +39,6 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
|
||||
internal void ClearModifiers()
|
||||
{
|
||||
foreach (var modifier in Modifiers)
|
||||
{
|
||||
modifier.OnDeactivate();
|
||||
}
|
||||
|
||||
_toRemove.AddRange(Modifiers);
|
||||
}
|
||||
|
||||
@@ -74,7 +69,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
Modifiers.Add(modifier);
|
||||
modifier.OnActivate();
|
||||
|
||||
if (_player?.AmOwner == true && modifier is TimedModifier { AutoStart: true } timer)
|
||||
if (modifier is TimedModifier { AutoStart: true } timer)
|
||||
{
|
||||
timer.StartTimer();
|
||||
}
|
||||
@@ -84,7 +79,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
{
|
||||
if (_player?.AmOwner == true)
|
||||
{
|
||||
HudManager.Instance?.SetHudActive(true);
|
||||
HudManager.Instance?.SetHudActive(HudManager.Instance.TaskPanel.isActiveAndEnabled);
|
||||
}
|
||||
_toAdd.Clear();
|
||||
_toRemove.Clear();
|
||||
@@ -213,6 +208,7 @@ public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
|
||||
_toAdd.Add(modifier);
|
||||
modifier.Player = _player;
|
||||
modifier.ModifierComponent = this;
|
||||
modifier.ModifierId = modifierId.Value;
|
||||
return modifier;
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@
|
||||
|
||||
namespace MiraAPI.Modifiers;
|
||||
|
||||
/// <summary>
|
||||
/// Marks a class as a modifier that can be registered with the <see cref="ModifierManager"/>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class RegisterModifierAttribute : Attribute;
|
||||
public class RegisterModifierAttribute : Attribute;
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -40,6 +39,9 @@ public abstract class TimedModifier : BaseModifier
|
||||
/// </summary>
|
||||
public float TimeRemaining { get; protected set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HideOnUi => !TimerActive;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HUD information for Timed Modifier, including the time remaining.
|
||||
/// </summary>
|
||||
@@ -54,25 +56,18 @@ public abstract class TimedModifier : BaseModifier
|
||||
/// </summary>
|
||||
public override void FixedUpdate()
|
||||
{
|
||||
if (Player?.AmOwner == false)
|
||||
if (!TimerActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (TimeRemaining > 0 && TimerActive)
|
||||
if (TimeRemaining > 0)
|
||||
{
|
||||
TimeRemaining -= Time.fixedDeltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
TimerActive = false;
|
||||
TimeRemaining = Duration;
|
||||
OnTimerComplete();
|
||||
|
||||
if (RemoveOnComplete)
|
||||
{
|
||||
Player?.RpcRemoveModifier(ModifierId);
|
||||
}
|
||||
StopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +82,52 @@ public abstract class TimedModifier : BaseModifier
|
||||
return;
|
||||
}
|
||||
|
||||
TimeRemaining = Duration;
|
||||
TimerActive = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the timer and calls OnTimerComplete().
|
||||
/// </summary>
|
||||
public void StopTimer()
|
||||
{
|
||||
if (!TimerActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TimerActive = false;
|
||||
TimeRemaining = Duration;
|
||||
OnTimerComplete();
|
||||
|
||||
if (RemoveOnComplete)
|
||||
{
|
||||
ModifierComponent?.RemoveModifier(ModifierId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the timer. Does not call OnTimerComplete().
|
||||
/// </summary>
|
||||
public void ResetTimer()
|
||||
{
|
||||
TimerActive = false;
|
||||
TimeRemaining = Duration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the timer.
|
||||
/// </summary>
|
||||
public void PauseTimer()
|
||||
{
|
||||
TimerActive = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the timer.
|
||||
/// </summary>
|
||||
public void ResumeTimer()
|
||||
{
|
||||
TimerActive = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using Assets.CoreScripts;
|
||||
using BepInEx.Unity.IL2CPP.Utils;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// Custom murder RPCs to fix issues with default ones.
|
||||
/// </summary>
|
||||
public static class CustomMurderRpc
|
||||
{
|
||||
/// <summary>
|
||||
/// Networked Custom Murder method.
|
||||
/// </summary>
|
||||
/// <param name="source">The killer.</param>
|
||||
/// <param name="target">The player to murder.</param>
|
||||
/// <param name="didSucceed">Whether the murder was successful or not.</param>
|
||||
/// <param name="resetKillTimer">Should the kill timer be reset.</param>
|
||||
/// <param name="createDeadBody">Should a dead body be created.</param>
|
||||
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
|
||||
/// <param name="showKillAnim">Should the kill animation be shown.</param>
|
||||
/// <param name="playKillSound">Should the kill sound be played.</param>
|
||||
[MethodRpc((uint)MiraRpc.CustomMurder)]
|
||||
public static void RpcCustomMurder(
|
||||
this PlayerControl source,
|
||||
PlayerControl target,
|
||||
bool didSucceed = true,
|
||||
bool resetKillTimer = true,
|
||||
bool createDeadBody = true,
|
||||
bool teleportMurderer = true,
|
||||
bool showKillAnim = true,
|
||||
bool playKillSound = true)
|
||||
{
|
||||
var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError;
|
||||
var murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags;
|
||||
|
||||
source.CustomMurder(
|
||||
target,
|
||||
murderResultFlags2,
|
||||
resetKillTimer,
|
||||
createDeadBody,
|
||||
teleportMurderer,
|
||||
showKillAnim,
|
||||
playKillSound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom Murder method without networking. If you need a networked version, use <see cref="RpcCustomMurder"/>.
|
||||
/// </summary>
|
||||
/// <param name="source">The killer.</param>
|
||||
/// <param name="target">The player to murder.</param>
|
||||
/// <param name="resultFlags">Murder result flags.</param>
|
||||
/// <param name="resetKillTimer">Should the kill timer be reset.</param>
|
||||
/// <param name="createDeadBody">Should a dead body be created.</param>
|
||||
/// <param name="teleportMurderer">Should the killer be snapped to the dead player.</param>
|
||||
/// <param name="showKillAnim">Should the kill animation be shown.</param>
|
||||
/// <param name="playKillSound">Should the kill sound be played.</param>
|
||||
public static void CustomMurder(
|
||||
this PlayerControl source,
|
||||
PlayerControl target,
|
||||
MurderResultFlags resultFlags,
|
||||
bool resetKillTimer = true,
|
||||
bool createDeadBody = true,
|
||||
bool teleportMurderer = true,
|
||||
bool showKillAnim = true,
|
||||
bool playKillSound = true)
|
||||
{
|
||||
source.isKilling = false;
|
||||
Logger<MiraApiPlugin>.Error($"{source.PlayerId} trying to murder {target.PlayerId}");
|
||||
var data = target.Data;
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedError))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) ||
|
||||
(resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1))
|
||||
{
|
||||
target.protectedByGuardianThisRound = true;
|
||||
var flag = PlayerControl.LocalPlayer.Data.Role.Role == RoleTypes.GuardianAngel;
|
||||
if (flag && PlayerControl.LocalPlayer.Data.PlayerId == target.protectedByGuardianId)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsGuardianAngelCrewmatesProtected);
|
||||
AchievementManager.Instance.OnProtectACrewmate();
|
||||
}
|
||||
|
||||
if (source.AmOwner || flag)
|
||||
{
|
||||
target.ShowFailedMurder();
|
||||
|
||||
if (resetKillTimer)
|
||||
{
|
||||
source.SetKillTimer(
|
||||
GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown) / 2f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target.RemoveProtection();
|
||||
}
|
||||
|
||||
Logger<MiraApiPlugin>.Error($"{source.PlayerId} failed to murder {target.PlayerId} due to guardian angel protection");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resultFlags.HasFlag(MurderResultFlags.Succeeded) &&
|
||||
!resultFlags.HasFlag(MurderResultFlags.DecisionByHost))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DebugAnalytics.Instance.Analytics.Kill(target.Data, source.Data);
|
||||
if (source.AmOwner)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(
|
||||
GameManager.Instance.IsHideAndSeek()
|
||||
? StringNames.StatsImpostorKills_HideAndSeek
|
||||
: StringNames.StatsImpostorKills);
|
||||
|
||||
if (source.CurrentOutfitType == PlayerOutfitType.Shapeshifted)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsShapeshifterShiftedKills);
|
||||
}
|
||||
|
||||
if (Constants.ShouldPlaySfx() && playKillSound)
|
||||
{
|
||||
SoundManager.Instance.PlaySound(source.KillSfx, false, 0.8f);
|
||||
}
|
||||
|
||||
if (resetKillTimer)
|
||||
{
|
||||
source.SetKillTimer(GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown));
|
||||
}
|
||||
}
|
||||
|
||||
UnityTelemetry.Instance.WriteMurder();
|
||||
target.gameObject.layer = LayerMask.NameToLayer("Ghost");
|
||||
if (target.AmOwner)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsTimesMurdered);
|
||||
if (Minigame.Instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
Minigame.Instance.Close();
|
||||
Minigame.Instance.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
if (showKillAnim)
|
||||
{
|
||||
HudManager.Instance.KillOverlay.ShowKillAnimation(source.Data, data);
|
||||
}
|
||||
|
||||
target.cosmetics.SetNameMask(false);
|
||||
target.RpcSetScanner(false);
|
||||
}
|
||||
|
||||
AchievementManager.Instance.OnMurder(
|
||||
source.AmOwner,
|
||||
target.AmOwner,
|
||||
source.CurrentOutfitType == PlayerOutfitType.Shapeshifted,
|
||||
source.shapeshiftTargetPlayerId,
|
||||
target.PlayerId);
|
||||
source.MyPhysics.StartCoroutine(source.KillAnimations.Random()?.CoPerformCustomKill(source, target, createDeadBody, teleportMurderer));
|
||||
Logger<MiraApiPlugin>.Error($"{source.PlayerId} succeeded in murdering {target.PlayerId}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform a custom kill animation.
|
||||
/// </summary>
|
||||
/// <param name="anim">The kill animation.</param>
|
||||
/// <param name="source">The murderer.</param>
|
||||
/// <param name="target">The murdered player.</param>
|
||||
/// <param name="createDeadBody">Should a dead body be created.</param>
|
||||
/// <param name="teleportMurderer">Should the murder be teleported.</param>
|
||||
/// <returns>Coroutine.</returns>
|
||||
public static IEnumerator CoPerformCustomKill(
|
||||
this KillAnimation anim,
|
||||
PlayerControl source,
|
||||
PlayerControl target,
|
||||
bool createDeadBody = true,
|
||||
bool teleportMurderer = true)
|
||||
{
|
||||
var cam = Camera.main?.GetComponent<FollowerCamera>();
|
||||
var isParticipant = PlayerControl.LocalPlayer == source || PlayerControl.LocalPlayer == target;
|
||||
var sourcePhys = source.MyPhysics;
|
||||
|
||||
if (teleportMurderer)
|
||||
{
|
||||
KillAnimation.SetMovement(source, false);
|
||||
}
|
||||
|
||||
KillAnimation.SetMovement(target, false);
|
||||
|
||||
if (isParticipant)
|
||||
{
|
||||
PlayerControl.LocalPlayer.isKilling = true;
|
||||
source.isKilling = true;
|
||||
}
|
||||
|
||||
DeadBody? deadBody = null;
|
||||
|
||||
if (createDeadBody)
|
||||
{
|
||||
deadBody = Object.Instantiate(GameManager.Instance.DeadBodyPrefab);
|
||||
deadBody.enabled = false;
|
||||
deadBody.ParentId = target.PlayerId;
|
||||
deadBody.bodyRenderers.ToList().ForEach(target.SetPlayerMaterialColors);
|
||||
|
||||
target.SetPlayerMaterialColors(deadBody.bloodSplatter);
|
||||
var vector = target.transform.position + anim.BodyOffset;
|
||||
vector.z = vector.y / 1000f;
|
||||
deadBody.transform.position = vector;
|
||||
}
|
||||
|
||||
if (isParticipant)
|
||||
{
|
||||
if (cam != null)
|
||||
{
|
||||
cam.Locked = true;
|
||||
}
|
||||
|
||||
ConsoleJoystick.SetMode_Task();
|
||||
if (PlayerControl.LocalPlayer.AmOwner)
|
||||
{
|
||||
PlayerControl.LocalPlayer.MyPhysics.inputHandler.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
target.Die(DeathReason.Kill, true);
|
||||
yield return source.MyPhysics.Animations.CoPlayCustomAnimation(anim.BlurAnim);
|
||||
sourcePhys.Animations.PlayIdleAnimation();
|
||||
|
||||
if (teleportMurderer)
|
||||
{
|
||||
source.NetTransform.SnapTo(target.transform.position);
|
||||
KillAnimation.SetMovement(source, true);
|
||||
}
|
||||
|
||||
KillAnimation.SetMovement(target, true);
|
||||
|
||||
if (deadBody != null)
|
||||
{
|
||||
deadBody.enabled = true;
|
||||
}
|
||||
|
||||
if (!isParticipant)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (cam != null)
|
||||
{
|
||||
cam.Locked = false;
|
||||
}
|
||||
|
||||
PlayerControl.LocalPlayer.isKilling = false;
|
||||
source.isKilling = false;
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
using AmongUs.GameOptions;
|
||||
using Assets.CoreScripts;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Networking;
|
||||
public static class CustomMurderRpcs
|
||||
{
|
||||
[MethodRpc((uint)MiraRpc.CustomMurder)]
|
||||
public static void RpcCustomMurder(this PlayerControl source, PlayerControl target, bool didSucceed = true,
|
||||
bool resetKillTimer = true, bool createDeadBody = true, bool teleportMurderer = true, bool showKillAnim = true, bool playKillSound = true)
|
||||
{
|
||||
var murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError;
|
||||
var murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags;
|
||||
|
||||
source.CustomMurder(target, murderResultFlags2, resetKillTimer, createDeadBody, teleportMurderer, showKillAnim, playKillSound);
|
||||
}
|
||||
|
||||
public static void CustomMurder(this PlayerControl source, PlayerControl target, MurderResultFlags resultFlags,
|
||||
bool resetKillTimer = true, bool createDeadBody = true, bool teleportMurderer = true, bool showKillAnim = true, bool playKillSound = true)
|
||||
{
|
||||
source.isKilling = false;
|
||||
source.logger.Debug(string.Format("{0} trying to murder {1}", source.PlayerId, target.PlayerId), null);
|
||||
var data = target.Data;
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedError))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) || (resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1))
|
||||
{
|
||||
target.protectedByGuardianThisRound = true;
|
||||
var flag = PlayerControl.LocalPlayer.Data.Role.Role == RoleTypes.GuardianAngel;
|
||||
if (flag && PlayerControl.LocalPlayer.Data.PlayerId == target.protectedByGuardianId)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsGuardianAngelCrewmatesProtected);
|
||||
DestroyableSingleton<AchievementManager>.Instance.OnProtectACrewmate();
|
||||
}
|
||||
if (source.AmOwner || flag)
|
||||
{
|
||||
target.ShowFailedMurder();
|
||||
|
||||
if (resetKillTimer)
|
||||
{
|
||||
source.SetKillTimer(GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown) / 2f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target.RemoveProtection();
|
||||
}
|
||||
source.logger.Debug(string.Format("{0} failed to murder {1} due to guardian angel protection", source.PlayerId, target.PlayerId), null);
|
||||
return;
|
||||
}
|
||||
if (resultFlags.HasFlag(MurderResultFlags.Succeeded) || resultFlags.HasFlag(MurderResultFlags.DecisionByHost))
|
||||
{
|
||||
DestroyableSingleton<DebugAnalytics>.Instance.Analytics.Kill(target.Data, source.Data);
|
||||
if (source.AmOwner)
|
||||
{
|
||||
if (GameManager.Instance.IsHideAndSeek())
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsImpostorKills_HideAndSeek);
|
||||
}
|
||||
else
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsImpostorKills);
|
||||
}
|
||||
if (source.CurrentOutfitType == PlayerOutfitType.Shapeshifted)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsShapeshifterShiftedKills);
|
||||
}
|
||||
if (Constants.ShouldPlaySfx() && playKillSound)
|
||||
{
|
||||
SoundManager.Instance.PlaySound(source.KillSfx, false, 0.8f, null);
|
||||
}
|
||||
|
||||
if (resetKillTimer)
|
||||
{
|
||||
source.SetKillTimer(GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown));
|
||||
}
|
||||
}
|
||||
DestroyableSingleton<UnityTelemetry>.Instance.WriteMurder();
|
||||
target.gameObject.layer = LayerMask.NameToLayer("Ghost");
|
||||
if (target.AmOwner)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsTimesMurdered);
|
||||
if (Minigame.Instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
Minigame.Instance.Close();
|
||||
Minigame.Instance.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (showKillAnim)
|
||||
{
|
||||
DestroyableSingleton<HudManager>.Instance.KillOverlay.ShowKillAnimation(source.Data, data);
|
||||
}
|
||||
|
||||
target.cosmetics.SetNameMask(false);
|
||||
target.RpcSetScanner(false);
|
||||
}
|
||||
DestroyableSingleton<AchievementManager>.Instance.OnMurder(source.AmOwner, target.AmOwner, source.CurrentOutfitType == PlayerOutfitType.Shapeshifted, source.shapeshiftTargetPlayerId, (int)target.PlayerId);
|
||||
Coroutines.Start(source.KillAnimations.Random().CoPerformCustomKill(source, target, createDeadBody, teleportMurderer));
|
||||
source.logger.Debug(string.Format("{0} succeeded in murdering {1}", source.PlayerId, target.PlayerId), null);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerator CoPerformCustomKill(this KillAnimation anim, PlayerControl source, PlayerControl target,
|
||||
bool createDeadBody = true, bool teleportMurderer = true)
|
||||
{
|
||||
var cam = Camera.main.GetComponent<FollowerCamera>();
|
||||
var isParticipant = PlayerControl.LocalPlayer == source || PlayerControl.LocalPlayer == target;
|
||||
var sourcePhys = source.MyPhysics;
|
||||
|
||||
if (teleportMurderer)
|
||||
{
|
||||
KillAnimation.SetMovement(source, false);
|
||||
}
|
||||
|
||||
KillAnimation.SetMovement(target, false);
|
||||
|
||||
if (isParticipant)
|
||||
{
|
||||
PlayerControl.LocalPlayer.isKilling = true;
|
||||
source.isKilling = true;
|
||||
}
|
||||
|
||||
DeadBody deadBody = null;
|
||||
|
||||
if (createDeadBody)
|
||||
{
|
||||
deadBody = Object.Instantiate(GameManager.Instance.DeadBodyPrefab);
|
||||
deadBody.enabled = false;
|
||||
deadBody.ParentId = target.PlayerId;
|
||||
deadBody.bodyRenderers.ToArray().ToList().ForEach(target.SetPlayerMaterialColors);
|
||||
|
||||
target.SetPlayerMaterialColors(deadBody.bloodSplatter);
|
||||
var vector = target.transform.position + anim.BodyOffset;
|
||||
vector.z = vector.y / 1000f;
|
||||
deadBody.transform.position = vector;
|
||||
}
|
||||
|
||||
if (isParticipant)
|
||||
{
|
||||
cam.Locked = true;
|
||||
ConsoleJoystick.SetMode_Task();
|
||||
if (PlayerControl.LocalPlayer.AmOwner)
|
||||
{
|
||||
PlayerControl.LocalPlayer.MyPhysics.inputHandler.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
target.Die(DeathReason.Kill, true);
|
||||
yield return source.MyPhysics.Animations.CoPlayCustomAnimation(anim.BlurAnim);
|
||||
sourcePhys.Animations.PlayIdleAnimation();
|
||||
|
||||
if (teleportMurderer)
|
||||
{
|
||||
source.NetTransform.SnapTo(target.transform.position);
|
||||
KillAnimation.SetMovement(source, true);
|
||||
}
|
||||
KillAnimation.SetMovement(target, true);
|
||||
|
||||
if (deadBody)
|
||||
{
|
||||
deadBody.enabled = true;
|
||||
}
|
||||
|
||||
if (isParticipant)
|
||||
{
|
||||
cam.Locked = false;
|
||||
PlayerControl.LocalPlayer.isKilling = false;
|
||||
source.isKilling = false;
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,37 @@
|
||||
namespace MiraAPI.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// Mira RPCs.
|
||||
/// </summary>
|
||||
public enum MiraRpc : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// Syncs the game options.
|
||||
/// </summary>
|
||||
SyncGameOptions,
|
||||
|
||||
/// <summary>
|
||||
/// Syncs the role options.
|
||||
/// </summary>
|
||||
SyncRoleOptions,
|
||||
|
||||
/// <summary>
|
||||
/// Adds a modifier to a player.
|
||||
/// </summary>
|
||||
AddModifier,
|
||||
|
||||
/// <summary>
|
||||
/// Removes a modifier from a player.
|
||||
/// </summary>
|
||||
RemoveModifier,
|
||||
|
||||
/// <summary>
|
||||
/// Syncs all modifiers at once.
|
||||
/// </summary>
|
||||
SyncModifiers,
|
||||
|
||||
/// <summary>
|
||||
/// Custom Murder RPC.
|
||||
/// </summary>
|
||||
CustomMurder,
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,25 @@
|
||||
namespace MiraAPI.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// Used to network data and mark it with an ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the data.</param>
|
||||
/// <param name="data">The byte[] data.</param>
|
||||
public readonly struct NetData(uint id, byte[] data)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the ID of the data.
|
||||
/// </summary>
|
||||
public uint Id { get; } = id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the byte[] data.
|
||||
/// </summary>
|
||||
public byte[] Data { get; } = data;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data in bytes.
|
||||
/// </summary>
|
||||
/// <returns>An int representing the number of bytes this NetData takes up.</returns>
|
||||
public int GetLength() => 4 + Data.Length;
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,18 @@ using Reactor.Networking.Rpc;
|
||||
namespace MiraAPI.Networking;
|
||||
|
||||
[RegisterCustomRpc((uint)MiraRpc.SyncModifiers)]
|
||||
internal class SyncModifiersRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
internal sealed class SyncModifiersRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
{
|
||||
public override RpcLocalHandling LocalHandling => RpcLocalHandling.None;
|
||||
|
||||
public override void Write(MessageWriter writer, NetData[] data)
|
||||
public override void Write(MessageWriter writer, NetData[]? data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
writer.WritePacked(0U);
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WritePacked((uint)data.Length);
|
||||
foreach (var netData in data)
|
||||
{
|
||||
@@ -26,21 +32,21 @@ internal class SyncModifiersRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc
|
||||
var data = new NetData[length];
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var id = reader.ReadPackedUInt32();
|
||||
var dataId = reader.ReadPackedUInt32();
|
||||
var bytes = reader.ReadBytesAndSize();
|
||||
data[i] = new NetData(id, bytes);
|
||||
data[i] = new NetData(dataId, bytes);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public override void Handle(PlayerControl playerControl, NetData[] data)
|
||||
public override void Handle(PlayerControl playerControl, NetData[]? data)
|
||||
{
|
||||
if (AmongUsClient.Instance.HostId != playerControl.OwnerId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ModifierManager.HandleSyncModifiers(data);
|
||||
ModifierManager.HandleSyncModifiers(data ?? []);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,18 @@ namespace MiraAPI.Networking;
|
||||
|
||||
// METHOD RPC DOESNT WORK WITH THE ARRAYS AND STUFF SO THIS IS HOW WE WILL DO IT FOR NOW
|
||||
[RegisterCustomRpc((uint)MiraRpc.SyncGameOptions)]
|
||||
internal class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
internal sealed class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
{
|
||||
public override RpcLocalHandling LocalHandling => RpcLocalHandling.None;
|
||||
|
||||
public override void Write(MessageWriter writer, NetData[] data)
|
||||
|
||||
public override void Write(MessageWriter writer, NetData[]? data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
writer.WritePacked(0U);
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WritePacked((uint)data.Length);
|
||||
foreach (var netData in data)
|
||||
{
|
||||
@@ -27,21 +33,21 @@ internal class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<M
|
||||
var data = new NetData[length];
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var id = reader.ReadPackedUInt32();
|
||||
var dataId = reader.ReadPackedUInt32();
|
||||
var bytes = reader.ReadBytesAndSize();
|
||||
data[i] = new NetData(id, bytes);
|
||||
data[i] = new NetData(dataId, bytes);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public override void Handle(PlayerControl playerControl, NetData[] data)
|
||||
public override void Handle(PlayerControl playerControl, NetData[]? data)
|
||||
{
|
||||
if (AmongUsClient.Instance.HostId != playerControl.OwnerId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ModdedOptionsManager.HandleSyncOptions(data);
|
||||
ModdedOptionsManager.HandleSyncOptions(data ?? []);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,18 @@ using Reactor.Networking.Rpc;
|
||||
namespace MiraAPI.Networking;
|
||||
|
||||
[RegisterCustomRpc((uint)MiraRpc.SyncRoleOptions)]
|
||||
internal class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
internal sealed class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
{
|
||||
|
||||
public override RpcLocalHandling LocalHandling => RpcLocalHandling.None;
|
||||
|
||||
public override void Write(MessageWriter writer, NetData[] data)
|
||||
public override void Write(MessageWriter writer, NetData[]? data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
writer.WritePacked(0U);
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WritePacked((uint)data.Length);
|
||||
foreach (var netData in data)
|
||||
{
|
||||
@@ -27,21 +32,21 @@ internal class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomR
|
||||
var data = new NetData[length];
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var id = reader.ReadPackedUInt32();
|
||||
var dataId = reader.ReadPackedUInt32();
|
||||
var bytes = reader.ReadBytesAndSize();
|
||||
data[i] = new NetData(id, bytes);
|
||||
data[i] = new NetData(dataId, bytes);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public override void Handle(PlayerControl playerControl, NetData[] data)
|
||||
public override void Handle(PlayerControl playerControl, NetData[]? data)
|
||||
{
|
||||
if (AmongUsClient.Instance.HostId != playerControl.OwnerId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CustomRoleManager.HandleSyncRoleOptions(data);
|
||||
CustomRoleManager.HandleSyncRoleOptions(data ?? []);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,9 @@ using MiraAPI.Roles;
|
||||
|
||||
namespace MiraAPI.Patches;
|
||||
|
||||
/// <summary>
|
||||
/// Sync all options, role settings, and modifiers to the player when they join the game.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.CreatePlayer))]
|
||||
public static class AmongUsClientSyncPatch
|
||||
{
|
||||
@@ -25,4 +28,4 @@ public static class AmongUsClientSyncPatch
|
||||
CustomRoleManager.SyncAllRoleSettings(clientData.Id);
|
||||
ModifierManager.SyncAllModifiers(clientData.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ namespace MiraAPI.Patches.Colors;
|
||||
[HarmonyPatch(typeof(PlayerTab))]
|
||||
public static class ScrollingColorsPatch
|
||||
{
|
||||
/// Collider
|
||||
private static BoxCollider2D _collider;
|
||||
// Collider
|
||||
private static BoxCollider2D? _collider;
|
||||
|
||||
/// <summary>
|
||||
/// Add scrolling to the colors tab
|
||||
@@ -20,7 +20,7 @@ public static class ScrollingColorsPatch
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var tab = PlayerCustomizationMenu.Instance.Tabs[1].Tab;
|
||||
|
||||
if (__instance.scroller == null)
|
||||
@@ -31,7 +31,7 @@ public static class ScrollingColorsPatch
|
||||
var maskObj = new GameObject
|
||||
{
|
||||
layer = 5,
|
||||
name = "SpriteMask"
|
||||
name = "SpriteMask",
|
||||
};
|
||||
maskObj.transform.SetParent(__instance.transform);
|
||||
maskObj.transform.localPosition = new Vector3(0, 0, 0);
|
||||
@@ -55,4 +55,4 @@ public static class ScrollingColorsPatch
|
||||
|
||||
__instance.SetScrollerBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
/*
|
||||
/*
|
||||
[HarmonyPatch]
|
||||
public static class ConsolePatches
|
||||
{
|
||||
@@ -69,4 +68,4 @@ public static class ConsolePatches
|
||||
canUse = couldUse = true;
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
[HarmonyPatch(typeof(DeadBody))]
|
||||
public static class DeadBodyPatch
|
||||
{
|
||||
@@ -10,4 +8,4 @@ public static class DeadBodyPatch
|
||||
{
|
||||
return CustomGameModeManager.ActiveMode == null || CustomGameModeManager.ActiveMode.CanReport(__instance);
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
[HarmonyPatch]
|
||||
public static class GameLogicPatches
|
||||
{
|
||||
@@ -19,4 +17,4 @@ public static class GameLogicPatches
|
||||
CustomGameModeManager.ActiveMode?.AssignRoles(out runOriginal, __instance);
|
||||
return runOriginal;
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
/// <summary>
|
||||
/// Allow Impostors to kill each other if can kill is enabled in gamemode or friendly fire is toggled on
|
||||
/// </summary>
|
||||
@@ -16,11 +14,11 @@ public static class ImpostorTargetPatch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
__result = target is { Disconnected: false, IsDead: false } &&
|
||||
target.PlayerId != __instance.Player.PlayerId && !(target.Role == null) &&
|
||||
!(target.Object == null) && !target.Object.inVent && !target.Object.inMovingPlat;
|
||||
return false;
|
||||
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
[HarmonyPatch(typeof(MapBehaviour))]
|
||||
public static class MapBehaviourPatch
|
||||
{
|
||||
@@ -17,4 +15,4 @@ public static class MapBehaviourPatch
|
||||
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
[HarmonyPatch(typeof(KillAnimation))]
|
||||
public static class OnDeathPatch
|
||||
{
|
||||
@@ -10,4 +8,4 @@ public static class OnDeathPatch
|
||||
{
|
||||
CustomGameModeManager.ActiveMode?.OnDeath(target);
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
@@ -1,10 +1,7 @@
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
[HarmonyPatch(typeof(Vent))]
|
||||
public static class VentPatches
|
||||
{
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(Vent.CanUse))]
|
||||
public static bool CanUseVentPatch(Vent __instance, ref float __result, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse)
|
||||
@@ -47,5 +44,4 @@ public static class VentPatches
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}*/
|
||||
}*/
|
||||
@@ -1,20 +1,20 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Hud;
|
||||
|
||||
namespace MiraAPI.Patches;
|
||||
namespace MiraAPI.Patches.Hud;
|
||||
|
||||
/// <summary>
|
||||
/// Harmony patches for the MeetingHud class.
|
||||
/// Reset button patches
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(MeetingHud))]
|
||||
public static class MeetingHudPatches
|
||||
[HarmonyPatch]
|
||||
public static class ButtonResetPatches
|
||||
{
|
||||
/// <summary>
|
||||
/// Resets the cooldown and effect of all custom buttons when the meeting starts.
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(MeetingHud.Start))]
|
||||
public static void StartPostfix()
|
||||
[HarmonyPatch(typeof(MeetingHud), nameof(MeetingHud.Start))]
|
||||
public static void MeetingHudStartPostfix()
|
||||
{
|
||||
foreach (var customActionButton in CustomButtonManager.CustomButtons)
|
||||
{
|
||||
@@ -23,11 +23,11 @@ public static class MeetingHudPatches
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the cooldown and effect of all custom buttons when the voting is complete.
|
||||
/// Resets the cooldown and effect of all custom buttons after the exile screen is closed.
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(MeetingHud.VotingComplete))]
|
||||
public static void VotingCompletePostfix()
|
||||
[HarmonyPatch(typeof(ExileController), nameof(ExileController.ReEnableGameplay))]
|
||||
public static void ExileControllerReEnableGameplayPostfix()
|
||||
{
|
||||
foreach (var customActionButton in CustomButtonManager.CustomButtons)
|
||||
{
|
||||
@@ -1,5 +1,4 @@
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities.Extensions;
|
||||
@@ -17,62 +16,6 @@ public static class HudManagerPatches
|
||||
// Custom buttons parent.
|
||||
private static GameObject? _bottomLeft;
|
||||
|
||||
// Custom role tab.
|
||||
private static TaskPanelBehaviour? _roleTab;
|
||||
|
||||
/// <summary>
|
||||
/// Update custom role tab and custom role hud elements.
|
||||
/// </summary>
|
||||
/// <param name="__instance">The HudManager instance.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(HudManager.Update))]
|
||||
public static void UpdatePostfix(HudManager __instance)
|
||||
{
|
||||
var local = PlayerControl.LocalPlayer;
|
||||
|
||||
if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// CustomGameModeManager.ActiveMode?.HudUpdate(__instance);
|
||||
|
||||
switch (local?.Data?.Role)
|
||||
{
|
||||
case null:
|
||||
return;
|
||||
|
||||
case ICustomRole customRole:
|
||||
{
|
||||
customRole.HudUpdate(__instance);
|
||||
|
||||
if (customRole.RoleHintType != RoleHintType.RoleTab)
|
||||
{
|
||||
_roleTab?.gameObject.Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_roleTab == null)
|
||||
{
|
||||
_roleTab = CustomRoleManager.CreateRoleTab(customRole);
|
||||
}
|
||||
else
|
||||
{
|
||||
CustomRoleManager.UpdateRoleTab(_roleTab, customRole);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (_roleTab != null)
|
||||
{
|
||||
_roleTab.gameObject.Destroy();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Trigger hudstart on current custom gamemode
|
||||
@@ -136,7 +79,7 @@ public static class HudManagerPatches
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the custom role tab and custom buttons active when the hud is active.
|
||||
/// Set the custom buttons active when the hud is active.
|
||||
/// </summary>
|
||||
/// <param name="__instance">HudManager instance.</param>
|
||||
/// <param name="localPlayer">The local PlayerControl.</param>
|
||||
@@ -146,24 +89,15 @@ public static class HudManagerPatches
|
||||
[HarmonyPatch(nameof(HudManager.SetHudActive), typeof(PlayerControl), typeof(RoleBehaviour), typeof(bool))]
|
||||
public static void SetHudActivePostfix(HudManager __instance, PlayerControl localPlayer, RoleBehaviour role, bool isActive)
|
||||
{
|
||||
if (!localPlayer.Data)
|
||||
__instance.AdminButton.ToggleVisible(isActive && role.IsImpostor && GameOptionsManager.Instance.CurrentGameOptions.GameMode == AmongUs.GameOptions.GameModes.HideNSeek);
|
||||
if (localPlayer.Data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_roleTab)
|
||||
{
|
||||
_roleTab?.gameObject.SetActive(isActive);
|
||||
}
|
||||
|
||||
foreach (var button in CustomButtonManager.CustomButtons)
|
||||
{
|
||||
button.SetActive(isActive, role);
|
||||
}
|
||||
|
||||
if (role is ICustomRole customRole)
|
||||
{
|
||||
__instance.ImpostorVentButton.gameObject.SetActive(isActive && customRole.CanUseVent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,33 +19,38 @@ public static class IntroCutscenePatches
|
||||
__instance.TeamTitle.text = $"<size=70%>{mode.Name}</size>\n<size=20%>{mode.Description}</size>";
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(IntroCutscene.BeginCrewmate))]
|
||||
public static bool BeginCrewmatePatch(IntroCutscene __instance)
|
||||
{
|
||||
if (PlayerControl.LocalPlayer.Data.Role is ICustomRole customRole)
|
||||
if (PlayerControl.LocalPlayer.Data.Role is not ICustomRole customRole)
|
||||
{
|
||||
if (customRole.Team is not ModdedRoleTeams.Neutral)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var barTransform = __instance.BackgroundBar.transform;
|
||||
var position = barTransform.position;
|
||||
position.y -= 0.25f;
|
||||
barTransform.position = position;
|
||||
|
||||
__instance.BackgroundBar.material.SetColor(ShaderID.Color, Color.gray);
|
||||
__instance.TeamTitle.text = "OUTCAST";
|
||||
__instance.impostorScale = 1f;
|
||||
__instance.ImpostorText.text = "You are an Outcast. You do not have a team.";
|
||||
__instance.TeamTitle.color = Color.gray;
|
||||
|
||||
__instance.ourCrewmate = __instance.CreatePlayer(0, Mathf.CeilToInt(7.5f), PlayerControl.LocalPlayer.Data, false);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
if (customRole.Team is not ModdedRoleTeams.Neutral)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var barTransform = __instance.BackgroundBar.transform;
|
||||
var position = barTransform.position;
|
||||
position.y -= 0.25f;
|
||||
barTransform.position = position;
|
||||
|
||||
__instance.BackgroundBar.material.SetColor(ShaderID.Color, Color.gray);
|
||||
__instance.TeamTitle.text = "NEUTRAL";
|
||||
__instance.impostorScale = 1f;
|
||||
__instance.ImpostorText.text = "You are Neutral. You do not have a team.";
|
||||
__instance.TeamTitle.color = Color.gray;
|
||||
|
||||
__instance.ourCrewmate = __instance.CreatePlayer(
|
||||
0,
|
||||
Mathf.CeilToInt(7.5f),
|
||||
PlayerControl.LocalPlayer.Data,
|
||||
false);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -55,4 +60,4 @@ public static class IntroCutscenePatches
|
||||
{
|
||||
CustomGameModeManager.ActiveMode?.Initialize();
|
||||
}*/
|
||||
}
|
||||
}
|
||||
@@ -30,16 +30,27 @@ public static class GameOptionsMenuPatch
|
||||
}
|
||||
|
||||
var num = 2.1f;
|
||||
var filteredGroups = GameSettingMenuPatches.SelectedMod.OptionGroups.Where(x => x.GroupVisible.Invoke() && x.AdvancedRole is null);
|
||||
var filteredGroups =
|
||||
GameSettingMenuPatches.SelectedMod?.OptionGroups.Where(
|
||||
x => x.AdvancedRole is null) ?? [];
|
||||
|
||||
foreach (var group in filteredGroups)
|
||||
{
|
||||
var filteredOpts = group.Options.Where(x => x.Visible.Invoke()).ToList();
|
||||
if (filteredOpts.Count == 0)
|
||||
if (group.Options.Count == 0 || group.Header is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!group.GroupVisible.Invoke())
|
||||
{
|
||||
group.Header.gameObject.SetActive(false);
|
||||
foreach (var option in group.Options)
|
||||
{
|
||||
option.OptionBehaviour?.gameObject.SetActive(false);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
group.Header.gameObject.SetActive(true);
|
||||
group.Header.transform.localScale = Vector3.one * 0.63f;
|
||||
group.Header.transform.localPosition = new Vector3(-0.903f, num, -2f);
|
||||
@@ -50,7 +61,12 @@ public static class GameOptionsMenuPatch
|
||||
{
|
||||
var newOpt = opt.OptionBehaviour;
|
||||
|
||||
if (opt.Visible.Invoke() == false)
|
||||
if (newOpt is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!opt.Visible.Invoke())
|
||||
{
|
||||
newOpt.gameObject.SetActive(false);
|
||||
continue;
|
||||
@@ -70,7 +86,6 @@ public static class GameOptionsMenuPatch
|
||||
}
|
||||
|
||||
__instance.scrollBar.SetYBoundsMax(-num - 1.65f);
|
||||
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
@@ -84,11 +99,15 @@ public static class GameOptionsMenuPatch
|
||||
|
||||
__instance.MapPicker.gameObject.SetActive(false);
|
||||
|
||||
var filteredGroups = GameSettingMenuPatches.SelectedMod.OptionGroups.Where(x => x.AdvancedRole is null);
|
||||
var filteredGroups = GameSettingMenuPatches.SelectedMod?.OptionGroups.Where(x => x.AdvancedRole is null) ?? [];
|
||||
|
||||
foreach (var group in filteredGroups)
|
||||
{
|
||||
var categoryHeaderMasked = Object.Instantiate(__instance.categoryHeaderOrigin, Vector3.zero, Quaternion.identity, __instance.settingsContainer);
|
||||
var categoryHeaderMasked = Object.Instantiate(
|
||||
__instance.categoryHeaderOrigin,
|
||||
Vector3.zero,
|
||||
Quaternion.identity,
|
||||
__instance.settingsContainer);
|
||||
categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20);
|
||||
if (group.GroupColor != Color.clear)
|
||||
{
|
||||
@@ -96,17 +115,24 @@ public static class GameOptionsMenuPatch
|
||||
categoryHeaderMasked.Divider.color = group.GroupColor;
|
||||
categoryHeaderMasked.Title.color = group.GroupColor.GetAlternateColor();
|
||||
}
|
||||
categoryHeaderMasked.Background.size = new Vector2(categoryHeaderMasked.Background.size.x + 1.5f, categoryHeaderMasked.Background.size.y);
|
||||
|
||||
categoryHeaderMasked.Background.size = new Vector2(
|
||||
categoryHeaderMasked.Background.size.x + 1.5f,
|
||||
categoryHeaderMasked.Background.size.y);
|
||||
categoryHeaderMasked.gameObject.SetActive(false);
|
||||
group.Header = categoryHeaderMasked;
|
||||
|
||||
var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform);
|
||||
newText.text = $"<size=70%>(Click to open)</size>";
|
||||
newText.text = "<size=70%>(Click to close)</size>";
|
||||
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
|
||||
newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
|
||||
var options = group.Options.Select(opt => opt.CreateOption(__instance.checkboxOrigin,
|
||||
__instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer));
|
||||
var options = group.Options.Select(
|
||||
opt => opt.CreateOption(
|
||||
__instance.checkboxOrigin,
|
||||
__instance.numberOptionOrigin,
|
||||
__instance.stringOptionOrigin,
|
||||
__instance.settingsContainer));
|
||||
|
||||
foreach (var newOpt in options)
|
||||
{
|
||||
@@ -142,8 +168,10 @@ public static class GameOptionsMenuPatch
|
||||
if (newOpt is ToggleOption toggle)
|
||||
{
|
||||
toggle.CheckMark.sprite = MiraAssets.Checkmark.LoadAsset();
|
||||
toggle.CheckMark.color = group.GroupColor != Color.clear ? group.GroupColor : MiraAssets.AcceptedTeal;
|
||||
var rend = toggle.CheckMark.transform.parent.FindChild("ActiveSprite").GetComponent<SpriteRenderer>();
|
||||
toggle.CheckMark.color =
|
||||
group.GroupColor != Color.clear ? group.GroupColor : MiraAssets.AcceptedTeal;
|
||||
var rend = toggle.CheckMark.transform.parent.FindChild("ActiveSprite")
|
||||
.GetComponent<SpriteRenderer>();
|
||||
rend.sprite = MiraAssets.CheckmarkBox.LoadAsset();
|
||||
rend.color = group.GroupColor != Color.clear ? group.GroupColor : MiraAssets.AcceptedTeal;
|
||||
}
|
||||
@@ -162,11 +190,14 @@ public static class GameOptionsMenuPatch
|
||||
headerBtn.ClickSound = __instance.BackButton.GetComponent<PassiveButton>().ClickSound;
|
||||
headerBtn.OnMouseOver = new UnityEvent();
|
||||
headerBtn.OnMouseOut = new UnityEvent();
|
||||
headerBtn.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
group.AllOptionsHidden = !group.AllOptionsHidden;
|
||||
newText.text = group.AllOptionsHidden ? "<size=70%>(Click to open)</size>" : "<size=70%>(Click to close)</size>";
|
||||
}));
|
||||
headerBtn.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
group.AllOptionsHidden = !group.AllOptionsHidden;
|
||||
newText.text = group.AllOptionsHidden
|
||||
? "<size=70%>(Click to open)</size>"
|
||||
: "<size=70%>(Click to close)</size>";
|
||||
}));
|
||||
headerBtn.SetButtonEnableState(true);
|
||||
}
|
||||
|
||||
@@ -207,4 +238,4 @@ public static class GameOptionsMenuPatch
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,6 @@ public static class GameSettingMenuPatches
|
||||
}
|
||||
|
||||
roles.roleChances.Clear();
|
||||
roles.roleChances = null;
|
||||
roles.AdvancedRolesSettings.gameObject.SetActive(false);
|
||||
roles.RoleChancesSettings.gameObject.SetActive(true);
|
||||
roles.SetQuotaTab();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
@@ -16,10 +16,11 @@ namespace MiraAPI.Patches.Options;
|
||||
[HarmonyPatch(typeof(LobbyViewSettingsPane))]
|
||||
public static class LobbyViewPanePatches
|
||||
{
|
||||
public static int CurrentSelectedMod { get; private set; }
|
||||
|
||||
public static MiraPluginInfo? SelectedMod => CurrentSelectedMod == 0 ? null : MiraPluginManager.Instance.RegisteredPlugins()[CurrentSelectedMod-1];
|
||||
private static int CurrentSelectedMod { get; set; }
|
||||
|
||||
private static MiraPluginInfo? SelectedMod => CurrentSelectedMod == 0
|
||||
? null
|
||||
: MiraPluginManager.Instance.RegisteredPlugins()[CurrentSelectedMod - 1];
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.Awake))]
|
||||
@@ -33,102 +34,104 @@ public static class LobbyViewPanePatches
|
||||
nextButton.transform.localPosition = new Vector3(-5.4f, 2.4f, -2f);
|
||||
nextButton.transform.localScale = new Vector3(3, 3, 2);
|
||||
nextButton.name = "RightArrowButton";
|
||||
|
||||
|
||||
var normal = nextButton.transform.FindChild("Normal").GetComponentInChildren<SpriteRenderer>();
|
||||
normal.transform.localPosition = new Vector3(0, 0f, 0.3f);
|
||||
normal.sprite = MiraAssets.NextButton.LoadAsset();
|
||||
|
||||
|
||||
var hover = nextButton.transform.FindChild("Hover").GetComponentInChildren<SpriteRenderer>();
|
||||
hover.transform.localPosition = new Vector3(0, 0f, 0.3f);
|
||||
hover.sprite = MiraAssets.NextButtonActive.LoadAsset();
|
||||
|
||||
|
||||
var passiveButton = nextButton.gameObject.GetComponent<PassiveButton>();
|
||||
passiveButton.OnClick = new Button.ButtonClickedEvent();
|
||||
passiveButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
CurrentSelectedMod += 1;
|
||||
if (CurrentSelectedMod > MiraPluginManager.Instance.RegisteredPlugins().Length)
|
||||
passiveButton.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
CurrentSelectedMod = 0;
|
||||
}
|
||||
__instance.RefreshTab();
|
||||
__instance.scrollBar.ScrollToTop();
|
||||
}));
|
||||
CurrentSelectedMod += 1;
|
||||
if (CurrentSelectedMod > MiraPluginManager.Instance.RegisteredPlugins().Length)
|
||||
{
|
||||
CurrentSelectedMod = 0;
|
||||
}
|
||||
|
||||
__instance.RefreshTab();
|
||||
__instance.scrollBar.ScrollToTop();
|
||||
}));
|
||||
|
||||
// Create the back button
|
||||
var backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject;
|
||||
backButton.transform.localPosition = new Vector3(-6.3f, 2.4f, -2f);
|
||||
backButton.name = "LeftArrowButton";
|
||||
backButton.transform.FindChild("Normal").gameObject.GetComponentInChildren<SpriteRenderer>().flipX
|
||||
= backButton.transform.FindChild("Hover").gameObject.GetComponentInChildren<SpriteRenderer>().flipX
|
||||
= backButton.transform.FindChild("Hover").gameObject.GetComponentInChildren<SpriteRenderer>().flipX
|
||||
= true;
|
||||
|
||||
|
||||
var passiveButton2 = backButton.gameObject.GetComponent<PassiveButton>();
|
||||
passiveButton2.OnClick = new Button.ButtonClickedEvent();
|
||||
passiveButton2.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
CurrentSelectedMod -= 1;
|
||||
if (CurrentSelectedMod < 0)
|
||||
passiveButton2.OnClick.AddListener(
|
||||
(UnityAction)(() =>
|
||||
{
|
||||
CurrentSelectedMod = MiraPluginManager.Instance.RegisteredPlugins().Length;
|
||||
}
|
||||
__instance.RefreshTab();
|
||||
__instance.scrollBar.ScrollToTop();
|
||||
}));
|
||||
CurrentSelectedMod -= 1;
|
||||
if (CurrentSelectedMod < 0)
|
||||
{
|
||||
CurrentSelectedMod = MiraPluginManager.Instance.RegisteredPlugins().Length;
|
||||
}
|
||||
|
||||
__instance.RefreshTab();
|
||||
__instance.scrollBar.ScrollToTop();
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.Update))]
|
||||
public static void UpdatePatch(LobbyViewSettingsPane __instance)
|
||||
{
|
||||
__instance.gameModeText.text = SelectedMod?.PluginInfo.Metadata.Name ?? "Default";
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.DrawNormalTab))]
|
||||
public static bool DrawNormalTabPatch(LobbyViewSettingsPane __instance)
|
||||
{
|
||||
if (CurrentSelectedMod == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DrawOptionsTab(__instance);
|
||||
return false;
|
||||
if (CurrentSelectedMod == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DrawOptionsTab(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.DrawRolesTab))]
|
||||
public static bool DrawRolesTabPatch(LobbyViewSettingsPane __instance)
|
||||
{
|
||||
if (CurrentSelectedMod == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DrawRolesTab(__instance);
|
||||
return false;
|
||||
if (CurrentSelectedMod == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DrawRolesTab(__instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static void DrawOptionsTab(LobbyViewSettingsPane instance)
|
||||
{
|
||||
if (SelectedMod == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var num = 1.44f;
|
||||
|
||||
|
||||
var filteredGroups = SelectedMod.OptionGroups.Where(x => x.GroupVisible.Invoke() && x.AdvancedRole is null);
|
||||
|
||||
foreach (var group in filteredGroups)
|
||||
{
|
||||
if (group.Options.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var categoryHeaderMasked = Object.Instantiate(instance.categoryHeaderOrigin, instance.settingsContainer, true);
|
||||
var categoryHeaderMasked = Object.Instantiate(
|
||||
instance.categoryHeaderOrigin,
|
||||
instance.settingsContainer,
|
||||
true);
|
||||
categoryHeaderMasked.SetHeader(StringNames.Name, 61);
|
||||
categoryHeaderMasked.Title.text = group.GroupName;
|
||||
categoryHeaderMasked.transform.localScale = Vector3.one;
|
||||
@@ -144,8 +147,11 @@ public static class LobbyViewPanePatches
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var viewSettingsInfoPanel = Object.Instantiate(instance.infoPanelOrigin, instance.settingsContainer, true);
|
||||
|
||||
var viewSettingsInfoPanel = Object.Instantiate(
|
||||
instance.infoPanelOrigin,
|
||||
instance.settingsContainer,
|
||||
true);
|
||||
viewSettingsInfoPanel.transform.localScale = Vector3.one;
|
||||
float num2;
|
||||
if (i % 2 == 0)
|
||||
@@ -160,25 +166,30 @@ public static class LobbyViewPanePatches
|
||||
{
|
||||
num2 = -3f;
|
||||
}
|
||||
|
||||
viewSettingsInfoPanel.transform.localPosition = new Vector3(num2, num, -2f);
|
||||
|
||||
var data = option.Data;
|
||||
|
||||
|
||||
if (data.Type == OptionTypes.Checkbox)
|
||||
{
|
||||
viewSettingsInfoPanel.SetInfoCheckbox(data.Title, 61, Mathf.Approximately(option.GetFloatData(), 1));
|
||||
viewSettingsInfoPanel.SetInfoCheckbox(
|
||||
data.Title,
|
||||
61,
|
||||
Mathf.Approximately(option.GetFloatData(), 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
viewSettingsInfoPanel.SetInfo(data.Title, data.GetValueString(option.GetFloatData()), 61);
|
||||
}
|
||||
|
||||
|
||||
instance.settingsInfo.Add(viewSettingsInfoPanel.gameObject);
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
num -= 0.59f;
|
||||
}
|
||||
|
||||
instance.scrollBar.CalculateAndSetYBounds(instance.settingsInfo.Count + 10, 2f, 6f, 0.59f);
|
||||
}
|
||||
|
||||
@@ -188,7 +199,7 @@ public static class LobbyViewPanePatches
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var num = 0.95f;
|
||||
var num2 = -6.53f;
|
||||
var categoryHeaderMasked =
|
||||
@@ -197,20 +208,20 @@ public static class LobbyViewPanePatches
|
||||
categoryHeaderMasked.transform.localScale = Vector3.one;
|
||||
categoryHeaderMasked.transform.localPosition = new Vector3(-9.77f, 1.26f, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderMasked.gameObject);
|
||||
|
||||
|
||||
var list = new List<Type>();
|
||||
|
||||
foreach (var team in Enum.GetValues<ModdedRoleTeams>())
|
||||
{
|
||||
var filteredRoles = SelectedMod.CustomRoles.Values
|
||||
.OfType<ICustomRole>()
|
||||
.Where(x => !x.HideSettings && x.Team == team).ToList();
|
||||
|
||||
.Where(x => !x.Configuration.HideSettings && x.Team == team).ToList();
|
||||
|
||||
if (filteredRoles.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var categoryHeaderRoleVariant =
|
||||
Object.Instantiate(instance.categoryHeaderRoleOrigin, instance.settingsContainer, true);
|
||||
|
||||
@@ -229,7 +240,7 @@ public static class LobbyViewPanePatches
|
||||
categoryHeaderRoleVariant.Title.color = Color.white;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
categoryHeaderRoleVariant.transform.localScale = Vector3.one;
|
||||
categoryHeaderRoleVariant.transform.localPosition = new Vector3(0.09f, num, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderRoleVariant.gameObject);
|
||||
@@ -242,42 +253,48 @@ public static class LobbyViewPanePatches
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var chancePerGame =
|
||||
GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetChancePerGame(roleBehaviour.Role);
|
||||
var numPerGame =
|
||||
GameOptionsManager.Instance.CurrentGameOptions.RoleOptions.GetNumPerGame(roleBehaviour.Role);
|
||||
|
||||
|
||||
var flag = numPerGame == 0;
|
||||
|
||||
|
||||
var viewSettingsInfoPanelRoleVariant =
|
||||
Object.Instantiate(
|
||||
instance.infoPanelRoleOrigin, instance.settingsContainer, true);
|
||||
instance.infoPanelRoleOrigin,
|
||||
instance.settingsContainer,
|
||||
true);
|
||||
viewSettingsInfoPanelRoleVariant.transform.localScale = Vector3.one;
|
||||
viewSettingsInfoPanelRoleVariant.transform.localPosition = new Vector3(num2, num, -2f);
|
||||
|
||||
|
||||
var advancedRoleOptions = SelectedMod.Options
|
||||
.Where(x => x.AdvancedRole == customRole.GetType())
|
||||
.ToList();
|
||||
|
||||
|
||||
if (!flag && advancedRoleOptions.Count > 0)
|
||||
{
|
||||
list.Add(customRole.GetType());
|
||||
}
|
||||
|
||||
viewSettingsInfoPanelRoleVariant.SetInfo(roleBehaviour.NiceName, numPerGame, chancePerGame,
|
||||
61, customRole.RoleColor, customRole.Icon.LoadAsset(), true);
|
||||
|
||||
viewSettingsInfoPanelRoleVariant.titleText.color =
|
||||
viewSettingsInfoPanelRoleVariant.chanceTitle.color =
|
||||
viewSettingsInfoPanelRoleVariant.chanceBackground.color =
|
||||
viewSettingsInfoPanelRoleVariant.background.color =
|
||||
viewSettingsInfoPanelRoleVariant.SetInfo(
|
||||
roleBehaviour.NiceName,
|
||||
numPerGame,
|
||||
chancePerGame,
|
||||
61,
|
||||
customRole.RoleColor,
|
||||
customRole.Configuration.Icon.LoadAsset(),
|
||||
true);
|
||||
|
||||
viewSettingsInfoPanelRoleVariant.titleText.color =
|
||||
viewSettingsInfoPanelRoleVariant.chanceTitle.color =
|
||||
viewSettingsInfoPanelRoleVariant.chanceBackground.color =
|
||||
viewSettingsInfoPanelRoleVariant.background.color =
|
||||
customRole.RoleColor.GetAlternateColor();
|
||||
instance.settingsInfo.Add(viewSettingsInfoPanelRoleVariant.gameObject);
|
||||
num -= 0.664f;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (list.Count > 0)
|
||||
@@ -312,7 +329,7 @@ public static class LobbyViewPanePatches
|
||||
advancedRoleViewPanel.transform.localScale = Vector3.one;
|
||||
advancedRoleViewPanel.transform.localPosition = new Vector3(num4, num, -2f);
|
||||
var num5 = SetUpAdvancedRoleViewPanel(advancedRoleViewPanel, list[k], 0.59f, 61);
|
||||
|
||||
|
||||
if (num5 > num3)
|
||||
{
|
||||
num3 = num5;
|
||||
@@ -325,59 +342,69 @@ public static class LobbyViewPanePatches
|
||||
instance.scrollBar.SetYBoundsMax(-num);
|
||||
}
|
||||
|
||||
private static float SetUpAdvancedRoleViewPanel(AdvancedRoleViewPanel viewPanel, Type roleType, float spacingY, int maskLayer)
|
||||
private static float SetUpAdvancedRoleViewPanel(
|
||||
AdvancedRoleViewPanel viewPanel,
|
||||
Type roleType,
|
||||
float spacingY,
|
||||
int maskLayer)
|
||||
{
|
||||
if (SelectedMod == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var role = SelectedMod.CustomRoles.Values.FirstOrDefault(x => x.GetType() == roleType);
|
||||
if (SelectedMod == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var role = SelectedMod.CustomRoles.Values.FirstOrDefault(x => x.GetType() == roleType);
|
||||
|
||||
if (role is not ICustomRole customRole)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
viewPanel.header.SetHeader(role.StringName, maskLayer, role.TeamType == RoleTeamTypes.Crewmate, customRole.Icon.LoadAsset());
|
||||
viewPanel.divider.material.SetInt(PlayerMaterial.MaskLayer, maskLayer);
|
||||
|
||||
var num = viewPanel.yPosStart;
|
||||
var num2 = 1.08f;
|
||||
|
||||
var filteredOptions = SelectedMod.Options
|
||||
.Where(x => x.AdvancedRole == roleType)
|
||||
.ToList();
|
||||
|
||||
for (var i = 0; i < filteredOptions.Count; i++)
|
||||
{
|
||||
var option = filteredOptions[i];
|
||||
var baseGameSetting = option.Data;
|
||||
var viewSettingsInfoPanel = Object.Instantiate(viewPanel.infoPanelOrigin, viewPanel.transform, true);
|
||||
viewSettingsInfoPanel.transform.localScale = Vector3.one;
|
||||
viewSettingsInfoPanel.transform.localPosition = new Vector3(viewPanel.xPosStart, num, -2f);
|
||||
|
||||
var value = option.GetFloatData();
|
||||
|
||||
if (baseGameSetting.Type == OptionTypes.Checkbox)
|
||||
{
|
||||
viewSettingsInfoPanel.SetInfoCheckbox(baseGameSetting.Title, maskLayer, value > 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewSettingsInfoPanel.SetInfo(baseGameSetting.Title, baseGameSetting.GetValueString(value), maskLayer);
|
||||
}
|
||||
num -= spacingY;
|
||||
if (i > 0)
|
||||
{
|
||||
num2 += 0.8f;
|
||||
}
|
||||
}
|
||||
return num2;
|
||||
if (role == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (role is not ICustomRole customRole)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
viewPanel.header.SetHeader(
|
||||
role.StringName,
|
||||
maskLayer,
|
||||
role.TeamType == RoleTeamTypes.Crewmate,
|
||||
customRole.Configuration.Icon.LoadAsset());
|
||||
viewPanel.divider.material.SetInt(PlayerMaterial.MaskLayer, maskLayer);
|
||||
|
||||
var num = viewPanel.yPosStart;
|
||||
var num2 = 1.08f;
|
||||
|
||||
var filteredOptions = SelectedMod.Options
|
||||
.Where(x => x.AdvancedRole == roleType)
|
||||
.ToList();
|
||||
|
||||
for (var i = 0; i < filteredOptions.Count; i++)
|
||||
{
|
||||
var option = filteredOptions[i];
|
||||
var baseGameSetting = option.Data;
|
||||
var viewSettingsInfoPanel = Object.Instantiate(viewPanel.infoPanelOrigin, viewPanel.transform, true);
|
||||
viewSettingsInfoPanel.transform.localScale = Vector3.one;
|
||||
viewSettingsInfoPanel.transform.localPosition = new Vector3(viewPanel.xPosStart, num, -2f);
|
||||
|
||||
var value = option.GetFloatData();
|
||||
|
||||
if (baseGameSetting.Type == OptionTypes.Checkbox)
|
||||
{
|
||||
viewSettingsInfoPanel.SetInfoCheckbox(baseGameSetting.Title, maskLayer, value > 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewSettingsInfoPanel.SetInfo(baseGameSetting.Title, baseGameSetting.GetValueString(value), maskLayer);
|
||||
}
|
||||
|
||||
num -= spacingY;
|
||||
if (i > 0)
|
||||
{
|
||||
num2 += 0.8f;
|
||||
}
|
||||
}
|
||||
|
||||
return num2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,34 +6,40 @@ using System.Linq;
|
||||
namespace MiraAPI.Patches.Options;
|
||||
|
||||
[HarmonyPatch(typeof(NotificationPopper))]
|
||||
public class NotificationPopperPatch
|
||||
public static class NotificationPopperPatch
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(NotificationPopper.AddRoleSettingsChangeMessage))]
|
||||
public static bool RoleChangeMsgPatch(NotificationPopper __instance,
|
||||
[HarmonyArgument(0)] StringNames key, [HarmonyArgument(1)] int roleCount, [HarmonyArgument(2)] int roleChance, [HarmonyArgument(3)] RoleTeamTypes teamType,
|
||||
public static bool RoleChangeMsgPatch(
|
||||
NotificationPopper __instance,
|
||||
[HarmonyArgument(0)] StringNames key,
|
||||
[HarmonyArgument(1)] int roleCount,
|
||||
[HarmonyArgument(2)] int roleChance,
|
||||
[HarmonyArgument(3)] RoleTeamTypes teamType,
|
||||
[HarmonyArgument(4)] bool playSound)
|
||||
{
|
||||
if (CustomRoleManager.CustomRoles.Values.Any(role => role.StringName == key))
|
||||
if (CustomRoleManager.CustomRoles.Values.FirstOrDefault(x=>x.StringName==key) is not ICustomRole customRole)
|
||||
{
|
||||
var item = string.Empty;
|
||||
var text = (teamType == RoleTeamTypes.Crewmate) ? Palette.CrewmateSettingChangeText.ToTextColor() : Palette.ImpostorRed.ToTextColor();
|
||||
item = DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.LobbyChangeSettingNotificationRole, new Object[]
|
||||
{
|
||||
string.Concat(
|
||||
[
|
||||
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">",
|
||||
text,
|
||||
DestroyableSingleton<TranslationController>.Instance.GetString(key, Array.Empty<Object>()),
|
||||
"</color></font>"
|
||||
]),
|
||||
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">" + roleCount.ToString() + "</font>",
|
||||
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">" + roleChance.ToString() + "%"
|
||||
});
|
||||
__instance.SettingsChangeMessageLogic(key, item, playSound);
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
var text = teamType == RoleTeamTypes.Crewmate
|
||||
? Palette.CrewmateSettingChangeText.ToTextColor()
|
||||
: Palette.ImpostorRed.ToTextColor();
|
||||
|
||||
var item = TranslationController.Instance.GetString(
|
||||
StringNames.LobbyChangeSettingNotificationRole,
|
||||
string.Concat(
|
||||
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">",
|
||||
text,
|
||||
TranslationController.Instance.GetString(key, Array.Empty<Object>()),
|
||||
"</color></font>"
|
||||
),
|
||||
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">" + roleCount + "</font>",
|
||||
"<font=\"Barlow-Black SDF\" material=\"Barlow-Black Outline\">" + roleChance + "%"
|
||||
);
|
||||
|
||||
__instance.SettingsChangeMessageLogic(key, item, playSound);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,8 @@ public static class OptionsPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.TitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
__instance.TitleText.text =
|
||||
TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -37,7 +38,8 @@ public static class OptionsPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.TitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
__instance.TitleText.text =
|
||||
TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -58,8 +60,11 @@ public static class OptionsPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.TitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
__instance.ValueText.text = DestroyableSingleton<TranslationController>.Instance.GetString(__instance.Values[__instance.Value], Array.Empty<Object>());
|
||||
__instance.TitleText.text =
|
||||
TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>());
|
||||
__instance.ValueText.text = TranslationController.Instance.GetString(
|
||||
__instance.Values[__instance.Value],
|
||||
Array.Empty<Object>());
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -70,4 +75,4 @@ public static class OptionsPatches
|
||||
{
|
||||
return !__instance.IsCustom();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,58 +54,72 @@ public static class RoleSettingMenuPatches
|
||||
|
||||
var num3 = 0;
|
||||
|
||||
var crewRoles = GameSettingMenuPatches.SelectedMod.CustomRoles.Values
|
||||
var crewRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values
|
||||
.OfType<ICustomRole>()
|
||||
.Where(role => role.Team == ModdedRoleTeams.Crewmate && !role.HideSettings)
|
||||
.ToList();
|
||||
.Where(role => role is { Team: ModdedRoleTeams.Crewmate, Configuration.HideSettings: false })
|
||||
.ToList() ?? [];
|
||||
|
||||
if (crewRoles.Count > 0)
|
||||
if (crewRoles is { Count: > 0 })
|
||||
{
|
||||
|
||||
var categoryHeaderEditRole = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform);
|
||||
var categoryHeaderEditRole = Object.Instantiate(
|
||||
__instance.categoryHeaderEditRoleOrigin,
|
||||
Vector3.zero,
|
||||
Quaternion.identity,
|
||||
__instance.RoleChancesSettings.transform);
|
||||
categoryHeaderEditRole.SetHeader(StringNames.CrewmateRolesHeader, 20);
|
||||
categoryHeaderEditRole.transform.localPosition = new Vector3(4.986f, num, -2f);
|
||||
num -= 0.522f;
|
||||
|
||||
foreach (var role in crewRoles)
|
||||
{
|
||||
CreateQuotaOption(__instance, role as RoleBehaviour, ref num, num3);
|
||||
if (role is RoleBehaviour roleBehaviour)
|
||||
{
|
||||
CreateQuotaOption(__instance, roleBehaviour, ref num, num3);
|
||||
}
|
||||
num3++;
|
||||
}
|
||||
}
|
||||
|
||||
var impRoles = GameSettingMenuPatches.SelectedMod.CustomRoles.Values
|
||||
var impRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values
|
||||
.OfType<ICustomRole>()
|
||||
.Where(role => role.Team == ModdedRoleTeams.Impostor && !role.HideSettings)
|
||||
.ToList();
|
||||
.Where(role => role is { Team: ModdedRoleTeams.Impostor, Configuration.HideSettings: false })
|
||||
.ToList() ?? [];
|
||||
|
||||
if (impRoles.Count > 0)
|
||||
if (impRoles is { Count: > 0 })
|
||||
{
|
||||
num -= 0.4f;
|
||||
var categoryHeaderEditRole2 = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin,
|
||||
Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform);
|
||||
var categoryHeaderEditRole2 = Object.Instantiate(
|
||||
__instance.categoryHeaderEditRoleOrigin,
|
||||
Vector3.zero,
|
||||
Quaternion.identity,
|
||||
__instance.RoleChancesSettings.transform);
|
||||
categoryHeaderEditRole2.SetHeader(StringNames.ImpostorRolesHeader, 20);
|
||||
categoryHeaderEditRole2.transform.localPosition = new Vector3(4.986f, num, -2f);
|
||||
num -= 0.522f;
|
||||
|
||||
|
||||
foreach (var role in impRoles)
|
||||
{
|
||||
CreateQuotaOption(__instance, role as RoleBehaviour, ref num, num3);
|
||||
if (role is RoleBehaviour roleBehaviour)
|
||||
{
|
||||
CreateQuotaOption(__instance, roleBehaviour, ref num, num3);
|
||||
}
|
||||
num3++;
|
||||
}
|
||||
}
|
||||
|
||||
var neutRoles = GameSettingMenuPatches.SelectedMod.CustomRoles.Values
|
||||
var neutRoles = GameSettingMenuPatches.SelectedMod?.CustomRoles.Values
|
||||
.OfType<ICustomRole>()
|
||||
.Where(role => role.Team == ModdedRoleTeams.Neutral && !role.HideSettings)
|
||||
.ToList();
|
||||
.Where(role => role is { Team: ModdedRoleTeams.Neutral, Configuration.HideSettings: false })
|
||||
.ToList() ?? [];
|
||||
|
||||
if (neutRoles.Count > 0)
|
||||
if (neutRoles is { Count: > 0 })
|
||||
{
|
||||
num -= 0.4f;
|
||||
var categoryHeaderEditRole3 = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin,
|
||||
Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform);
|
||||
var categoryHeaderEditRole3 = Object.Instantiate(
|
||||
__instance.categoryHeaderEditRoleOrigin,
|
||||
Vector3.zero,
|
||||
Quaternion.identity,
|
||||
__instance.RoleChancesSettings.transform);
|
||||
categoryHeaderEditRole3.SetHeader(StringNames.None, 20);
|
||||
categoryHeaderEditRole3.Title.text = "Neutral Roles";
|
||||
categoryHeaderEditRole3.Background.color = Color.gray;
|
||||
@@ -115,7 +129,10 @@ public static class RoleSettingMenuPatches
|
||||
|
||||
foreach (var role in neutRoles)
|
||||
{
|
||||
CreateQuotaOption(__instance, role as RoleBehaviour, ref num, num3);
|
||||
if (role is RoleBehaviour roleBehaviour)
|
||||
{
|
||||
CreateQuotaOption(__instance, roleBehaviour, ref num, num3);
|
||||
}
|
||||
num3++;
|
||||
}
|
||||
}
|
||||
@@ -142,18 +159,24 @@ public static class RoleSettingMenuPatches
|
||||
{
|
||||
var roleSetting = obj.Cast<RoleOptionSetting>();
|
||||
var role = roleSetting.Role as ICustomRole;
|
||||
if (role is null or { HideSettings: true })
|
||||
if (role is null or { Configuration.HideSettings: true })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
role.ParentMod.PluginConfig.TryGetEntry<int>(role.NumConfigDefinition, out var numEntry);
|
||||
numEntry.Value = roleSetting.RoleMaxCount;
|
||||
if (role.Configuration.MaxRoleCount != 0)
|
||||
{
|
||||
role.ParentMod.PluginConfig.TryGetEntry<int>(role.NumConfigDefinition, out var numEntry);
|
||||
numEntry.Value = Mathf.Clamp(roleSetting.RoleMaxCount, 0, role.Configuration.MaxRoleCount);
|
||||
}
|
||||
|
||||
role.ParentMod.PluginConfig.TryGetEntry<int>(role.ChanceConfigDefinition, out var chanceEntry);
|
||||
chanceEntry.Value = roleSetting.RoleChance;
|
||||
if (role.Configuration.CanModifyChance)
|
||||
{
|
||||
role.ParentMod.PluginConfig.TryGetEntry<int>(role.ChanceConfigDefinition, out var chanceEntry);
|
||||
chanceEntry.Value = roleSetting.RoleChance;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -161,12 +184,18 @@ public static class RoleSettingMenuPatches
|
||||
}
|
||||
|
||||
roleSetting.UpdateValuesAndText(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions);
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddRoleSettingsChangeMessage(roleSetting.Role.StringName, roleSetting.RoleMaxCount, roleSetting.RoleChance, roleSetting.Role.TeamType, false);
|
||||
HudManager.Instance.Notifier.AddRoleSettingsChangeMessage(
|
||||
roleSetting.Role.StringName,
|
||||
roleSetting.RoleMaxCount,
|
||||
roleSetting.RoleChance,
|
||||
roleSetting.Role.TeamType,
|
||||
false);
|
||||
|
||||
if (AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
Rpc<SyncRoleOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [role.GetNetData()], true);
|
||||
}
|
||||
|
||||
GameOptionsManager.Instance.GameHostOptions = GameOptionsManager.Instance.CurrentGameOptions;
|
||||
}
|
||||
|
||||
@@ -181,7 +210,7 @@ public static class RoleSettingMenuPatches
|
||||
|
||||
var num = -0.872f;
|
||||
|
||||
var filteredOptions = GameSettingMenuPatches.SelectedMod.Options.Where(x => x.AdvancedRole == role.GetType());
|
||||
var filteredOptions = GameSettingMenuPatches.SelectedMod?.Options.Where(x => x.AdvancedRole == role.GetType()) ?? [];
|
||||
|
||||
foreach (var option in filteredOptions)
|
||||
{
|
||||
@@ -190,7 +219,12 @@ public static class RoleSettingMenuPatches
|
||||
continue;
|
||||
}
|
||||
|
||||
var newOpt = option.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.AdvancedRolesSettings.transform);
|
||||
var newOpt = option.CreateOption(
|
||||
__instance.checkboxOrigin,
|
||||
__instance.numberOptionOrigin,
|
||||
__instance.stringOptionOrigin,
|
||||
__instance.AdvancedRolesSettings.transform);
|
||||
|
||||
newOpt.transform.localPosition = new Vector3(2.17f, num, -2f);
|
||||
newOpt.SetClickMask(__instance.ButtonClickMask);
|
||||
|
||||
@@ -199,10 +233,11 @@ public static class RoleSettingMenuPatches
|
||||
{
|
||||
renderer.material.SetInt(PlayerMaterial.MaskLayer, 20);
|
||||
}
|
||||
foreach (var textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
|
||||
|
||||
foreach (var fontMat in newOpt.GetComponentsInChildren<TextMeshPro>(true).Select(x => x.fontMaterial))
|
||||
{
|
||||
textMeshPro.fontMaterial.SetFloat(ShaderID.StencilComp, 3f);
|
||||
textMeshPro.fontMaterial.SetFloat(ShaderID.Stencil, 20);
|
||||
fontMat.SetFloat(ShaderID.StencilComp, 3f);
|
||||
fontMat.SetFloat(ShaderID.Stencil, 20);
|
||||
}
|
||||
|
||||
newOpt.LabelBackground.enabled = false;
|
||||
@@ -215,12 +250,24 @@ public static class RoleSettingMenuPatches
|
||||
__instance.scrollBar.CalculateAndSetYBounds(__instance.advancedSettingChildren.Count + 3, 1f, 6f, 0.45f);
|
||||
__instance.scrollBar.ScrollToTop();
|
||||
}
|
||||
|
||||
private static void ChangeTab(RoleBehaviour role, RolesSettingsMenu __instance)
|
||||
{
|
||||
var customRole = role as ICustomRole;
|
||||
if (role is not ICustomRole customRole)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Role {role.NiceName} is not a custom role.");
|
||||
return;
|
||||
}
|
||||
|
||||
__instance.roleDescriptionText.text = customRole.RoleLongDescription;
|
||||
__instance.roleTitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(role.StringName, new Il2CppReferenceArray<Il2CppSystem.Object>(0));
|
||||
__instance.roleScreenshot.sprite = Sprite.Create(customRole.OptionsScreenshot.LoadAsset().texture, new Rect(0, 0, 370, 230), Vector2.one / 2, 100);
|
||||
__instance.roleTitleText.text = TranslationController.Instance.GetString(
|
||||
role.StringName,
|
||||
new Il2CppReferenceArray<Il2CppSystem.Object>(0));
|
||||
__instance.roleScreenshot.sprite = Sprite.Create(
|
||||
customRole.Configuration.OptionsScreenshot.LoadAsset().texture,
|
||||
new Rect(0, 0, 370, 230),
|
||||
Vector2.one / 2,
|
||||
100);
|
||||
__instance.roleScreenshot.drawMode = SpriteDrawMode.Sliced;
|
||||
__instance.roleHeaderSprite.color = customRole.RoleColor;
|
||||
__instance.roleHeaderText.color = customRole.RoleColor.GetAlternateColor();
|
||||
@@ -244,28 +291,51 @@ public static class RoleSettingMenuPatches
|
||||
optionBehaviour.SetAsPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
__instance.RoleChancesSettings.SetActive(false);
|
||||
__instance.AdvancedRolesSettings.SetActive(true);
|
||||
__instance.RefreshChildren();
|
||||
}
|
||||
|
||||
public static void CreateQuotaOption(RolesSettingsMenu __instance, RoleBehaviour role, ref float yPos, int index)
|
||||
private static void CreateQuotaOption(RolesSettingsMenu __instance, RoleBehaviour role, ref float yPos, int index)
|
||||
{
|
||||
var customRole = role as ICustomRole;
|
||||
var roleOptionSetting = Object.Instantiate(__instance.roleOptionSettingOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform);
|
||||
if (role is not ICustomRole customRole)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Role {role.NiceName} is not a custom role.");
|
||||
return;
|
||||
}
|
||||
|
||||
var roleOptionSetting = Object.Instantiate(
|
||||
__instance.roleOptionSettingOrigin,
|
||||
Vector3.zero,
|
||||
Quaternion.identity,
|
||||
__instance.RoleChancesSettings.transform);
|
||||
roleOptionSetting.transform.localPosition = new Vector3(-0.15f, yPos, -2f);
|
||||
|
||||
roleOptionSetting.SetRole(GameOptionsManager.Instance.CurrentGameOptions.RoleOptions, role, 20);
|
||||
roleOptionSetting.labelSprite.color = customRole.RoleColor;
|
||||
roleOptionSetting.OnValueChanged = new Action<OptionBehaviour>(ValueChanged);
|
||||
roleOptionSetting.SetClickMask(__instance.ButtonClickMask);
|
||||
__instance.roleChances.Add(roleOptionSetting);
|
||||
|
||||
if (customRole.Configuration.MaxRoleCount == 0 && customRole.Configuration.CanModifyChance)
|
||||
{
|
||||
roleOptionSetting.CountMinusBtn.gameObject.SetActive(false);
|
||||
roleOptionSetting.CountPlusBtn.gameObject.SetActive(false);
|
||||
}
|
||||
else if (!customRole.Configuration.CanModifyChance)
|
||||
{
|
||||
roleOptionSetting.ChanceMinusBtn.gameObject.SetActive(false);
|
||||
roleOptionSetting.ChancePlusBtn.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
roleOptionSetting.titleText.transform.localPosition = new Vector3(-0.5376f, -0.2923f, 0f);
|
||||
roleOptionSetting.titleText.color = customRole.RoleColor.GetAlternateColor();
|
||||
roleOptionSetting.titleText.horizontalAlignment = HorizontalAlignmentOptions.Left;
|
||||
|
||||
if (GameSettingMenuPatches.SelectedMod is null ||
|
||||
GameSettingMenuPatches.SelectedMod.Options.Any(x => x.AdvancedRole != null && x.AdvancedRole.IsInstanceOfType(role)))
|
||||
GameSettingMenuPatches.SelectedMod.Options.Exists(
|
||||
x => x.AdvancedRole != null && x.AdvancedRole.IsInstanceOfType(role)))
|
||||
{
|
||||
var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
|
||||
newButton.name = "ConfigButton";
|
||||
@@ -289,4 +359,4 @@ public static class RoleSettingMenuPatches
|
||||
yPos += -0.43f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace MiraAPI.Patches.Options;
|
||||
/// Patches for the various game settings.
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public class SettingPatches
|
||||
public static class SettingPatches
|
||||
{
|
||||
/// <summary>
|
||||
/// Prefix for the <see cref="FloatGameSetting.GetValueString"/> method. Adds support for custom number suffixes.
|
||||
@@ -25,33 +25,24 @@ public class SettingPatches
|
||||
ref string __result,
|
||||
[HarmonyArgument(0)] float value)
|
||||
{
|
||||
var result = string.Empty;
|
||||
string result;
|
||||
var suffix = (MiraNumberSuffixes)__instance.SuffixType;
|
||||
|
||||
if (__instance.ZeroIsInfinity && Mathf.Abs(value) < 0.0001f)
|
||||
{
|
||||
result = "<b>∞</b>";
|
||||
}
|
||||
else if (suffix == MiraNumberSuffixes.None)
|
||||
{
|
||||
result = value.ToString(__instance.FormatString);
|
||||
}
|
||||
else if (suffix == MiraNumberSuffixes.Multiplier)
|
||||
{
|
||||
result = value.ToString(__instance.FormatString) + "x";
|
||||
}
|
||||
else if (suffix == MiraNumberSuffixes.Percent)
|
||||
{
|
||||
result = value.ToString(__instance.FormatString) + "%";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = DestroyableSingleton<TranslationController>.Instance.GetString(
|
||||
StringNames.GameSecondsAbbrev,
|
||||
(Il2CppSystem.Object[])
|
||||
[
|
||||
value.ToString(__instance.FormatString, CultureInfo.InvariantCulture)
|
||||
]);
|
||||
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)]),
|
||||
};
|
||||
}
|
||||
|
||||
__result = result;
|
||||
|
||||
@@ -15,6 +15,7 @@ public static class PlayerControlPatches
|
||||
/// <summary>
|
||||
/// Adds the modifier component to the player on start.
|
||||
/// </summary>
|
||||
/// <param name="__instance">PlayerControl instance.</param>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(PlayerControl.Start))]
|
||||
public static void PlayerControlStartPostfix(PlayerControl __instance)
|
||||
@@ -30,21 +31,23 @@ public static class PlayerControlPatches
|
||||
/// <summary>
|
||||
/// Calls the OnDeath method for all active modifiers.
|
||||
/// </summary>
|
||||
/// <param name="__instance">PlayerControl instance.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(PlayerControl.Die))]
|
||||
public static void PlayerControlDiePostfix(PlayerControl __instance)
|
||||
public static void PlayerControlDiePostfix(PlayerControl __instance, DeathReason reason)
|
||||
{
|
||||
var modifiersComponent = __instance.GetComponent<ModifierComponent>();
|
||||
|
||||
if (modifiersComponent)
|
||||
{
|
||||
modifiersComponent.ActiveModifiers.ForEach(x=>x.OnDeath());
|
||||
modifiersComponent.ActiveModifiers.ForEach(x=>x.OnDeath(reason));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FixedUpdate handler for custom roles and custom buttons.
|
||||
/// </summary>
|
||||
/// <param name="__instance">PlayerControl instance.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(PlayerControl.FixedUpdate))]
|
||||
public static void PlayerControlFixedUpdatePostfix(PlayerControl __instance)
|
||||
@@ -74,4 +77,15 @@ public static class PlayerControlPatches
|
||||
button.FixedUpdateHandler(__instance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear from modifier component cache.
|
||||
/// </summary>
|
||||
/// <param name="__instance">PlayerControl instance.</param>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(PlayerControl.OnDestroy))]
|
||||
public static void PlayerControlOnDestroyPrefix(PlayerControl __instance)
|
||||
{
|
||||
Utilities.Extensions.ModifierComponents.Remove(__instance);
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,18 @@ using MiraAPI.Roles;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Patches for custom ejection messages.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(ExileController))]
|
||||
public static class EjectionPatches
|
||||
{
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(ExileController.Begin))]
|
||||
public static void Begin(ExileController __instance)
|
||||
public static void BeginPostfix(ExileController __instance)
|
||||
{
|
||||
if (!__instance.initData.networkedPlayer || !__instance.initData.networkedPlayer.Role || __instance.initData.networkedPlayer.Role is not ICustomRole role)
|
||||
if (!__instance.initData.networkedPlayer || !__instance.initData.networkedPlayer.Role ||
|
||||
__instance.initData.networkedPlayer.Role is not ICustomRole role)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -23,4 +26,4 @@ public static class EjectionPatches
|
||||
|
||||
__instance.completeString = role.GetCustomEjectionMessage(__instance.initData.networkedPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities.Extensions;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// HudManager patches for roles.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(HudManager))]
|
||||
public static class HudManagerPatches
|
||||
{
|
||||
// Custom role tab.
|
||||
private static TaskPanelBehaviour? _roleTab;
|
||||
|
||||
/// <summary>
|
||||
/// Fixes Kill Button not showing for Neutral killing role.
|
||||
/// </summary>
|
||||
/// <param name="__instance">HudManager instance.</param>
|
||||
/// <param name="localPlayer">The local PlayerControl.</param>
|
||||
/// <param name="role">The player's RoleBehaviour.</param>
|
||||
/// <param name="isActive">Whether the Hud should be set active or not.</param>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(HudManager.SetHudActive), typeof(PlayerControl), typeof(RoleBehaviour), typeof(bool))]
|
||||
public static void SetHudActivePostfix(
|
||||
HudManager __instance,
|
||||
PlayerControl localPlayer,
|
||||
RoleBehaviour role,
|
||||
bool isActive)
|
||||
{
|
||||
var flag = localPlayer.Data != null && localPlayer.Data.IsDead;
|
||||
|
||||
if (role is ICustomRole customRole)
|
||||
{
|
||||
__instance.KillButton.ToggleVisible(isActive && customRole.Configuration.UseVanillaKillButton && !flag);
|
||||
__instance.ImpostorVentButton.ToggleVisible(isActive && customRole.Configuration.CanUseVent && !flag);
|
||||
__instance.SabotageButton.gameObject.SetActive(isActive && customRole.Configuration.CanUseSabotage);
|
||||
}
|
||||
|
||||
if (_roleTab)
|
||||
{
|
||||
_roleTab?.gameObject.SetActive(isActive);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update custom role tab and custom role hud elements.
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(HudManager.Update))]
|
||||
public static void UpdatePostfix(HudManager __instance)
|
||||
{
|
||||
var local = PlayerControl.LocalPlayer;
|
||||
|
||||
if (AmongUsClient.Instance.GameState != InnerNetClient.GameStates.Started && !ShipStatus.Instance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var role = local?.Data?.Role;
|
||||
|
||||
if (role is ICustomRole { Configuration.RoleHintType: RoleHintType.RoleTab } customRole)
|
||||
{
|
||||
customRole.HudUpdate(__instance);
|
||||
|
||||
if (_roleTab == null)
|
||||
{
|
||||
_roleTab = CustomRoleManager.CreateRoleTab(customRole);
|
||||
}
|
||||
|
||||
CustomRoleManager.UpdateRoleTab(_roleTab, customRole);
|
||||
}
|
||||
else if (_roleTab)
|
||||
{
|
||||
_roleTab?.gameObject.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using HarmonyLib;
|
||||
using Il2CppSystem;
|
||||
using MiraAPI.Networking;
|
||||
using MiraAPI.Roles;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Fix kill button issues for neutral killers.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(KillButton))]
|
||||
public static class KillButtonPatches
|
||||
{
|
||||
/// <summary>
|
||||
/// SetTarget for custom roles.
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(KillButton.SetTarget))]
|
||||
public static bool SetTargetPrefix(KillButton __instance, PlayerControl target)
|
||||
{
|
||||
if (!PlayerControl.LocalPlayer || PlayerControl.LocalPlayer.Data == null || !PlayerControl.LocalPlayer.Data.Role)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PlayerControl.LocalPlayer.Data.Role is not ICustomRole customRole)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance.currentTarget && __instance.currentTarget != target)
|
||||
{
|
||||
__instance.currentTarget.cosmetics.SetOutline(false, new Nullable<Color>(Color.clear));
|
||||
}
|
||||
__instance.currentTarget = target;
|
||||
if (__instance.currentTarget)
|
||||
{
|
||||
__instance.currentTarget.cosmetics.SetOutline(true, new Nullable<Color>(customRole.Configuration.KillButtonOutlineColor));
|
||||
__instance.SetEnabled();
|
||||
return false;
|
||||
}
|
||||
__instance.SetDisabled();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use Custom Murder if player is custom role.
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(KillButton.DoClick))]
|
||||
public static bool DoClickPrefix(KillButton __instance)
|
||||
{
|
||||
if (PlayerControl.LocalPlayer.Data.Role is not ICustomRole)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!__instance.isActiveAndEnabled || !__instance.currentTarget || __instance.isCoolingDown ||
|
||||
PlayerControl.LocalPlayer.Data.IsDead || !PlayerControl.LocalPlayer.CanMove)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayerControl.LocalPlayer.RpcCustomMurder(__instance.currentTarget);
|
||||
__instance.SetTarget(null);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,4 @@ public static class NameTagPatch
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,4 +22,4 @@ public static class RoleBehaviourPatches
|
||||
__result = behaviour.RoleColor;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,9 @@ public static class RoleManagerPatches
|
||||
return;
|
||||
}
|
||||
|
||||
ModifierManager.AssignModifiers(PlayerControl.AllPlayerControls.ToArray().Where(plr => !plr.Data.IsDead && !plr.Data.Disconnected).ToList());
|
||||
ModifierManager.AssignModifiers(
|
||||
PlayerControl.AllPlayerControls.ToArray().Where(plr => !plr.Data.IsDead && !plr.Data.Disconnected)
|
||||
.ToList());
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
@@ -35,13 +37,12 @@ public static class RoleManagerPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
if (role.GhostRole is RoleTypes.CrewmateGhost or RoleTypes.ImpostorGhost)
|
||||
if (role.Configuration.GhostRole is RoleTypes.CrewmateGhost or RoleTypes.ImpostorGhost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
plr.RpcSetRole(role.GhostRole);
|
||||
return false;
|
||||
|
||||
plr.RpcSetRole(role.Configuration.GhostRole);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,14 @@ public static class RoleOptionsCollectionPatch
|
||||
[HarmonyPatch(nameof(RoleOptionsCollectionV08.GetChancePerGame))]
|
||||
public static bool GetChancePrefix([HarmonyArgument(0)] RoleTypes roleType, ref int __result)
|
||||
{
|
||||
if (!CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole))
|
||||
if (!CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole) || customRole == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (customRole.HideSettings)
|
||||
if (customRole.Configuration.HideSettings)
|
||||
{
|
||||
__result = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -36,18 +37,19 @@ public static class RoleOptionsCollectionPatch
|
||||
[HarmonyPatch(nameof(RoleOptionsCollectionV08.GetNumPerGame))]
|
||||
public static bool GetNumPrefix([HarmonyArgument(0)] RoleTypes roleType, ref int __result)
|
||||
{
|
||||
if (!CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole))
|
||||
if (!CustomRoleManager.GetCustomRoleBehaviour(roleType, out var customRole) || customRole == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (customRole.HideSettings)
|
||||
|
||||
if (customRole.Configuration.HideSettings)
|
||||
{
|
||||
__result = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
customRole.ParentMod.PluginConfig.TryGetEntry<int>(customRole.NumConfigDefinition, out var entry);
|
||||
__result = entry.Value;
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Roles;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
[HarmonyPatch(typeof(SabotageButton))]
|
||||
public class SabotageButtonPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// Patches the Sabotage button to check if the player's custom role can use sabotage.
|
||||
/// </summary>
|
||||
[HarmonyPatch(nameof(SabotageButton.DoClick))]
|
||||
[HarmonyPrefix]
|
||||
public static bool DoClickPrefix(SabotageButton __instance)
|
||||
{
|
||||
var player = PlayerControl.LocalPlayer;
|
||||
|
||||
if (player.Data.Role is not ICustomRole customRole)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!customRole.Configuration.CanUseSabotage || PlayerControl.LocalPlayer.inVent || !GameManager.Instance.SabotagesEnabled())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HudManager.Instance.ToggleMapVisible(
|
||||
new MapOptions
|
||||
{
|
||||
Mode = MapOptions.Modes.Sabotage,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(SabotageButton.Refresh))]
|
||||
public static bool RefreshPrefix(SabotageButton __instance)
|
||||
{
|
||||
var player = PlayerControl.LocalPlayer;
|
||||
if (GameManager.Instance == null || player == null)
|
||||
{
|
||||
__instance.ToggleVisible(false);
|
||||
__instance.SetDisabled();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.Data.Role is not ICustomRole customRole)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (player.inVent || !GameManager.Instance.SabotagesEnabled() || player.petting)
|
||||
{
|
||||
__instance.ToggleVisible(customRole.Configuration.CanUseSabotage && GameManager.Instance.SabotagesEnabled());
|
||||
__instance.SetDisabled();
|
||||
return false;
|
||||
}
|
||||
__instance.SetEnabled();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using Il2CppSystem;
|
||||
using Il2CppSystem.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
@@ -12,12 +12,14 @@ using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Patch TaskAdder for organizing Roles and adding scroller.
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
public static class TaskAdderPatch
|
||||
{
|
||||
private static TaskFolder _rolesFolder;
|
||||
private static readonly System.Collections.Generic.Dictionary<string, string> ModsFolders = new();
|
||||
private static Scroller _scroller;
|
||||
private static readonly Dictionary<string, string> ModsFolders = [];
|
||||
private static Scroller? _scroller;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(TaskAdderGame), nameof(TaskAdderGame.Begin))]
|
||||
@@ -56,18 +58,22 @@ public static class TaskAdderPatch
|
||||
|
||||
__instance.TaskParent = inner.transform;
|
||||
|
||||
_rolesFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner);
|
||||
_rolesFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner);
|
||||
_rolesFolder.gameObject.SetActive(false);
|
||||
_rolesFolder.FolderName = "Roles";
|
||||
_rolesFolder.name = "RolesFolder";
|
||||
var rolesFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner);
|
||||
rolesFolder.gameObject.SetActive(false);
|
||||
rolesFolder.FolderName = "Roles";
|
||||
rolesFolder.name = "RolesFolder";
|
||||
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins())
|
||||
{
|
||||
if (plugin.CustomRoles.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var newFolder = Object.Instantiate(__instance.RootFolderPrefab, _scroller.Inner);
|
||||
newFolder.FolderName = newFolder.name = plugin.PluginInfo.Metadata.Name;
|
||||
newFolder.gameObject.SetActive(false);
|
||||
_rolesFolder.SubFolders.Add(newFolder);
|
||||
rolesFolder.SubFolders.Add(newFolder);
|
||||
|
||||
if (!ModsFolders.ContainsKey(plugin.PluginInfo.Metadata.Name))
|
||||
{
|
||||
@@ -75,7 +81,7 @@ public static class TaskAdderPatch
|
||||
}
|
||||
}
|
||||
|
||||
__instance.Root.SubFolders.Add(_rolesFolder);
|
||||
__instance.Root.SubFolders.Add(rolesFolder);
|
||||
|
||||
__instance.GoToRoot();
|
||||
}
|
||||
@@ -92,7 +98,12 @@ public static class TaskAdderPatch
|
||||
__instance.RolloverHandler.OutColor = __instance.FileImage.color;
|
||||
}
|
||||
|
||||
private static void AddFileAsChildCustom(this TaskAdderGame instance, TaskFolder taskFolder, TaskAddButton item, ref float xCursor, ref float yCursor, ref float maxHeight)
|
||||
private static void AddFileAsChildCustom(
|
||||
this TaskAdderGame instance,
|
||||
TaskAddButton item,
|
||||
ref float xCursor,
|
||||
ref float yCursor,
|
||||
ref float maxHeight)
|
||||
{
|
||||
item.transform.SetParent(instance.TaskParent);
|
||||
item.transform.localPosition = new Vector3(xCursor, yCursor, 0f);
|
||||
@@ -105,9 +116,9 @@ public static class TaskAdderPatch
|
||||
yCursor -= maxHeight;
|
||||
maxHeight = 0f;
|
||||
}
|
||||
|
||||
instance.ActiveItems.Add(item.transform);
|
||||
}
|
||||
|
||||
|
||||
// yes it might be crazy patching the entire method, but i tried so many other methods and only this works :cry:
|
||||
[HarmonyPrefix]
|
||||
@@ -121,11 +132,13 @@ public static class TaskAdderPatch
|
||||
stringBuilder.Append(__instance.Hierarchy.ToArray()[i].FolderName);
|
||||
stringBuilder.Append("\\");
|
||||
}
|
||||
|
||||
__instance.PathText.text = stringBuilder.ToString();
|
||||
for (var j = 0; j < __instance.ActiveItems.Count; j++)
|
||||
{
|
||||
Object.Destroy(__instance.ActiveItems.ToArray()[j].gameObject);
|
||||
}
|
||||
|
||||
__instance.ActiveItems.Clear();
|
||||
var num = 0f;
|
||||
var num2 = 0f;
|
||||
@@ -148,6 +161,7 @@ public static class TaskAdderPatch
|
||||
num2 -= num3;
|
||||
num3 = 0f;
|
||||
}
|
||||
|
||||
__instance.ActiveItems.Add(folderTransform);
|
||||
if (!taskFolder2 || !taskFolder2.Button)
|
||||
{
|
||||
@@ -155,81 +169,102 @@ public static class TaskAdderPatch
|
||||
}
|
||||
|
||||
ControllerManager.Instance.AddSelectableUiElement(taskFolder2.Button);
|
||||
if (!string.IsNullOrEmpty(__instance.restorePreviousSelectionByFolderName) && taskFolder2.FolderName.Equals(__instance.restorePreviousSelectionByFolderName))
|
||||
if (!string.IsNullOrEmpty(__instance.restorePreviousSelectionByFolderName) &&
|
||||
taskFolder2.FolderName.Equals(__instance.restorePreviousSelectionByFolderName, StringComparison.Ordinal))
|
||||
{
|
||||
__instance.restorePreviousSelectionFound = taskFolder2.Button;
|
||||
}
|
||||
}
|
||||
|
||||
var flag = false;
|
||||
var list = new List<PlayerTask>();
|
||||
|
||||
foreach (var item in taskFolder.Children)
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
var list = taskFolder.Children.ToArray().OrderBy(t => t.TaskType).ToList();
|
||||
|
||||
for (var l = 0; l < list.Count; l++)
|
||||
{
|
||||
var taskAddButton = Object.Instantiate(__instance.TaskPrefab);
|
||||
taskAddButton.MyTask = list.ToArray()[l];
|
||||
if (taskAddButton.MyTask.TaskType == TaskTypes.DivertPower)
|
||||
switch (taskAddButton.MyTask.TaskType)
|
||||
{
|
||||
var targetSystem = taskAddButton.MyTask.Cast<DivertPowerTask>().TargetSystem;
|
||||
taskAddButton.Text.text = DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.DivertPowerTo, DestroyableSingleton<TranslationController>.Instance.GetString(targetSystem));
|
||||
}
|
||||
else if (taskAddButton.MyTask.TaskType == TaskTypes.FixWeatherNode)
|
||||
{
|
||||
var nodeId = ((WeatherNodeTask)taskAddButton.MyTask).NodeId;
|
||||
taskAddButton.Text.text = DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.FixWeatherNode, Array.Empty<Il2CppSystem.Object>()) + " " + DestroyableSingleton<TranslationController>.Instance.GetString(WeatherSwitchGame.ControlNames[nodeId],
|
||||
Array.Empty<Il2CppSystem.Object>());
|
||||
}
|
||||
else
|
||||
{
|
||||
taskAddButton.Text.text = DestroyableSingleton<TranslationController>.Instance.GetString(taskAddButton.MyTask.TaskType);
|
||||
}
|
||||
__instance.AddFileAsChildCustom(taskFolder, taskAddButton, ref num, ref num2, ref num3);
|
||||
if (taskAddButton != null && taskAddButton.Button != null)
|
||||
{
|
||||
ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button);
|
||||
if (__instance.Hierarchy.Count != 1 && !flag)
|
||||
case TaskTypes.DivertPower:
|
||||
{
|
||||
var component = ControllerManager.Instance.CurrentUiState.CurrentSelection.GetComponent<TaskFolder>();
|
||||
if (component != null)
|
||||
{
|
||||
__instance.restorePreviousSelectionByFolderName = component.FolderName;
|
||||
}
|
||||
ControllerManager.Instance.SetDefaultSelection(taskAddButton.Button);
|
||||
flag = true;
|
||||
var targetSystem = taskAddButton.MyTask.Cast<DivertPowerTask>().TargetSystem;
|
||||
taskAddButton.Text.text = TranslationController.Instance.GetString(
|
||||
StringNames.DivertPowerTo,
|
||||
TranslationController.Instance.GetString(targetSystem));
|
||||
break;
|
||||
}
|
||||
case TaskTypes.FixWeatherNode:
|
||||
{
|
||||
var nodeId = taskAddButton.MyTask.Cast<WeatherNodeTask>().NodeId;
|
||||
taskAddButton.Text.text =
|
||||
TranslationController.Instance.GetString(
|
||||
StringNames.FixWeatherNode) + " " +
|
||||
TranslationController.Instance.GetString(
|
||||
WeatherSwitchGame.ControlNames[nodeId]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
taskAddButton.Text.text =
|
||||
TranslationController.Instance.GetString(taskAddButton.MyTask.TaskType);
|
||||
break;
|
||||
}
|
||||
|
||||
__instance.AddFileAsChildCustom(taskAddButton, ref num, ref num2, ref num3);
|
||||
if (taskAddButton.Button == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button);
|
||||
if (__instance.Hierarchy.Count == 1 || flag)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var component =
|
||||
ControllerManager.Instance.CurrentUiState.CurrentSelection.GetComponent<TaskFolder>();
|
||||
if (component != null)
|
||||
{
|
||||
__instance.restorePreviousSelectionByFolderName = component.FolderName;
|
||||
}
|
||||
|
||||
ControllerManager.Instance.SetDefaultSelection(taskAddButton.Button);
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (taskFolder.FolderName == "Roles")
|
||||
{
|
||||
for (var m = 0; m < DestroyableSingleton<RoleManager>.Instance.AllRoles.Length; m++)
|
||||
for (var m = 0; m < RoleManager.Instance.AllRoles.Length; m++)
|
||||
{
|
||||
var roleBehaviour = DestroyableSingleton<RoleManager>.Instance.AllRoles[m];
|
||||
if (roleBehaviour.Role != RoleTypes.ImpostorGhost && roleBehaviour.Role != RoleTypes.CrewmateGhost && !CustomRoleManager.CustomRoles.ContainsKey((ushort)roleBehaviour.Role))
|
||||
var roleBehaviour = RoleManager.Instance.AllRoles[m];
|
||||
if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost ||
|
||||
CustomRoleManager.CustomRoles.ContainsKey((ushort)roleBehaviour.Role))
|
||||
{
|
||||
var taskAddButton2 = Object.Instantiate(__instance.RoleButton);
|
||||
taskAddButton2.SafePositionWorld = __instance.SafePositionWorld;
|
||||
taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe";
|
||||
__instance.AddFileAsChildCustom(_rolesFolder, taskAddButton2, ref num, ref num2, ref num3);
|
||||
taskAddButton2.Role = roleBehaviour;
|
||||
if (taskAddButton2 != null && taskAddButton2.Button != null)
|
||||
{
|
||||
ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button);
|
||||
if (m == 0 && __instance.restorePreviousSelectionFound != null)
|
||||
{
|
||||
ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound);
|
||||
__instance.restorePreviousSelectionByFolderName = string.Empty;
|
||||
__instance.restorePreviousSelectionFound = null;
|
||||
}
|
||||
else if (m == 0)
|
||||
{
|
||||
ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var taskAddButton2 = Object.Instantiate(__instance.RoleButton);
|
||||
taskAddButton2.SafePositionWorld = __instance.SafePositionWorld;
|
||||
taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe";
|
||||
__instance.AddFileAsChildCustom(taskAddButton2, ref num, ref num2, ref num3);
|
||||
taskAddButton2.Role = roleBehaviour;
|
||||
|
||||
if (taskAddButton2.Button == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button);
|
||||
switch (m)
|
||||
{
|
||||
case 0 when __instance.restorePreviousSelectionFound != null:
|
||||
ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound);
|
||||
__instance.restorePreviousSelectionByFolderName = string.Empty;
|
||||
__instance.restorePreviousSelectionFound = null;
|
||||
break;
|
||||
case 0:
|
||||
ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,27 +275,33 @@ public static class TaskAdderPatch
|
||||
for (var m = 0; m < plugin.CustomRoles.Count; m++)
|
||||
{
|
||||
var roleBehaviour = plugin.CustomRoles.ElementAt(m).Value;
|
||||
if (roleBehaviour.Role != RoleTypes.ImpostorGhost && roleBehaviour.Role != RoleTypes.CrewmateGhost && !roleBehaviour.IsDead)
|
||||
if (roleBehaviour.Role == RoleTypes.ImpostorGhost || roleBehaviour.Role == RoleTypes.CrewmateGhost ||
|
||||
roleBehaviour.IsDead)
|
||||
{
|
||||
var taskAddButton2 = Object.Instantiate(__instance.RoleButton);
|
||||
taskAddButton2.SafePositionWorld = __instance.SafePositionWorld;
|
||||
taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe";
|
||||
__instance.AddFileAsChildCustom(_rolesFolder, taskAddButton2, ref num, ref num2, ref num3);
|
||||
taskAddButton2.Role = roleBehaviour;
|
||||
if (taskAddButton2 != null && taskAddButton2.Button != null)
|
||||
{
|
||||
ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button);
|
||||
if (m == 0 && __instance.restorePreviousSelectionFound != null)
|
||||
{
|
||||
ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound);
|
||||
__instance.restorePreviousSelectionByFolderName = string.Empty;
|
||||
__instance.restorePreviousSelectionFound = null;
|
||||
}
|
||||
else if (m == 0)
|
||||
{
|
||||
ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var taskAddButton2 = Object.Instantiate(__instance.RoleButton);
|
||||
taskAddButton2.SafePositionWorld = __instance.SafePositionWorld;
|
||||
taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe";
|
||||
__instance.AddFileAsChildCustom(taskAddButton2, ref num, ref num2, ref num3);
|
||||
taskAddButton2.Role = roleBehaviour;
|
||||
if (taskAddButton2.Button == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ControllerManager.Instance.AddSelectableUiElement(taskAddButton2.Button);
|
||||
switch (m)
|
||||
{
|
||||
case 0 when __instance.restorePreviousSelectionFound != null:
|
||||
ControllerManager.Instance.SetDefaultSelection(__instance.restorePreviousSelectionFound);
|
||||
__instance.restorePreviousSelectionByFolderName = string.Empty;
|
||||
__instance.restorePreviousSelectionFound = null;
|
||||
break;
|
||||
case 0:
|
||||
ControllerManager.Instance.SetDefaultSelection(taskAddButton2.Button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,17 +312,19 @@ public static class TaskAdderPatch
|
||||
chip.GetComponent<SpriteRenderer>().maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
|
||||
}
|
||||
|
||||
if (_scroller)
|
||||
if (_scroller && _scroller != null)
|
||||
{
|
||||
_scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 3, 1f);
|
||||
_scroller.SetYBoundsMin(0.0f);
|
||||
_scroller.ScrollToTop();
|
||||
}
|
||||
|
||||
if (__instance.Hierarchy.Count == 1)
|
||||
{
|
||||
ControllerManager.Instance.SetBackButton(__instance.BackButton);
|
||||
return false;
|
||||
}
|
||||
|
||||
ControllerManager.Instance.SetBackButton(__instance.FolderBackButton);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ namespace MiraAPI.Patches.Roles;
|
||||
public static class TaskPanelPatch
|
||||
{
|
||||
/// <summary>
|
||||
/// This patch is to override the automatic updating of the y position on the tab (which is in base game),
|
||||
/// This patch is to override the automatic updating of the y position on the tab (which is in base game)
|
||||
/// because I can't change the custom tab y pos if it's being overriden every frame.
|
||||
/// Im sure there is an easier/better way, but this is the fix that worked for me
|
||||
/// Im sure there is an easier/better way, but this is the fix that worked for me.
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(TaskPanelBehaviour.Update))]
|
||||
@@ -24,7 +24,12 @@ public static class TaskPanelPatch
|
||||
var vector = __instance.background.sprite.bounds.extents;
|
||||
var vector2 = __instance.tab.sprite.bounds.extents;
|
||||
|
||||
transform.localScale = __instance.taskText.textBounds.size.x > 0f ? new Vector3(__instance.taskText.textBounds.size.x + 0.4f, __instance.taskText.textBounds.size.y + 0.3f, 1f) : Vector3.zero;
|
||||
transform.localScale = __instance.taskText.textBounds.size.x > 0f
|
||||
? new Vector3(
|
||||
__instance.taskText.textBounds.size.x + 0.4f,
|
||||
__instance.taskText.textBounds.size.y + 0.3f,
|
||||
1f)
|
||||
: Vector3.zero;
|
||||
|
||||
vector.y = -vector.y;
|
||||
vector = vector.Mul(transform.localScale);
|
||||
@@ -39,20 +44,24 @@ public static class TaskPanelPatch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var closePosition = new Vector3(-__instance.background.sprite.bounds.size.x * __instance.background.transform.localScale.x, __instance.closedPosition.y, __instance.closedPosition.z);
|
||||
|
||||
var closePosition = new Vector3(
|
||||
-__instance.background.sprite.bounds.size.x * __instance.background.transform.localScale.x,
|
||||
__instance.closedPosition.y,
|
||||
__instance.closedPosition.z);
|
||||
__instance.closedPosition = closePosition;
|
||||
if (__instance.open)
|
||||
{
|
||||
__instance.timer = Mathf.Min(1f, __instance.timer + Time.deltaTime / __instance.animationTimeSeconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
__instance.timer = Mathf.Max(0f, __instance.timer - Time.deltaTime / __instance.animationTimeSeconds);
|
||||
}
|
||||
Vector3 relativePos;
|
||||
relativePos = new Vector3(Mathf.SmoothStep(__instance.closedPosition.x, __instance.openPosition.x, __instance.timer), Mathf.SmoothStep(__instance.closedPosition.y, __instance.openPosition.y, __instance.timer), __instance.openPosition.z);
|
||||
__instance.transform.localPosition = AspectPosition.ComputePosition(AspectPosition.EdgeAlignments.LeftTop, relativePos);
|
||||
|
||||
__instance.timer = __instance.open
|
||||
? Mathf.Min(1f, __instance.timer + Time.deltaTime / __instance.animationTimeSeconds)
|
||||
: Mathf.Max(0f, __instance.timer - Time.deltaTime / __instance.animationTimeSeconds);
|
||||
|
||||
var relativePos = new Vector3(
|
||||
Mathf.SmoothStep(__instance.closedPosition.x, __instance.openPosition.x, __instance.timer),
|
||||
Mathf.SmoothStep(__instance.closedPosition.y, __instance.openPosition.y, __instance.timer),
|
||||
__instance.openPosition.z);
|
||||
__instance.transform.localPosition = AspectPosition.ComputePosition(
|
||||
AspectPosition.EdgeAlignments.LeftTop,
|
||||
relativePos);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Vent patches for Roles.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(Vent))]
|
||||
public static class VentPatches
|
||||
{
|
||||
/// <summary>
|
||||
/// Set outline to player's custom role color.
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(Vent.SetOutline))]
|
||||
public static void SetOutlinePostfix(Vent __instance, bool on, bool mainTarget)
|
||||
{
|
||||
if (PlayerControl.LocalPlayer.Data.Role is not ICustomRole customRole)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var color = customRole.RoleColor;
|
||||
|
||||
__instance.myRend.material.SetFloat(ShaderID.Outline, on ? 1 : 0);
|
||||
__instance.myRend.material.SetColor(ShaderID.OutlineColor, color);
|
||||
__instance.myRend.material.SetColor(ShaderID.AddColor, mainTarget ? color : Color.clear);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,40 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Modifiers;
|
||||
namespace MiraAPI.Patches;
|
||||
|
||||
/// <summary>
|
||||
/// Vent patches to make sure the player is able to use the vent.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(Vent))]
|
||||
public static class VentPatches
|
||||
{
|
||||
/// <summary>
|
||||
/// CanUse patch.
|
||||
/// </summary>
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(Vent.CanUse))]
|
||||
public static void CanUseVentPatch(Vent __instance, ref float __result, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse)
|
||||
public static void VentCanUsePostfix(Vent __instance, ref float __result, [HarmonyArgument(0)] NetworkedPlayerInfo pc, [HarmonyArgument(1)] ref bool canUse, [HarmonyArgument(2)] ref bool couldUse)
|
||||
{
|
||||
var @object = pc.Object;
|
||||
var role = @object.Data.Role;
|
||||
|
||||
var canVent = role is ICustomRole customRole ? customRole.Configuration.CanUseVent : role.CanVent;
|
||||
|
||||
var modifiers = @object.GetModifierComponent()?.ActiveModifiers;
|
||||
if (modifiers is null || modifiers.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (role.CanVent)
|
||||
switch (canVent)
|
||||
{
|
||||
case true when modifiers.Any(x => !x.CanVent()):
|
||||
case true when modifiers.Exists(x => !x.CanVent()):
|
||||
couldUse = canUse = false;
|
||||
return;
|
||||
case false when modifiers.Any(x => x.CanVent()):
|
||||
case false when modifiers.Exists(x => x.CanVent()):
|
||||
couldUse = true;
|
||||
break;
|
||||
}
|
||||
@@ -43,4 +51,4 @@ public static class VentPatches
|
||||
}
|
||||
__result = num;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,4 @@ public interface IMiraPlugin
|
||||
{
|
||||
string OptionsTitleText { get; }
|
||||
public ConfigFile GetConfigFile();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ using Reactor.Utilities;
|
||||
|
||||
namespace MiraAPI.PluginLoading;
|
||||
|
||||
internal class MiraPluginManager
|
||||
internal sealed class MiraPluginManager
|
||||
{
|
||||
private readonly Dictionary<Assembly, MiraPluginInfo> _registeredPlugins = [];
|
||||
|
||||
@@ -83,6 +83,8 @@ internal class MiraPluginManager
|
||||
ModdedOptionsManager.RegisterAttributeOption(type, attribute, property, pluginInfo);
|
||||
}
|
||||
}
|
||||
|
||||
pluginInfo.OptionGroups.Sort((x, y) => x.GroupPriority.CompareTo(y.GroupPriority));
|
||||
}
|
||||
|
||||
private static void RegisterRoleAttribute(Assembly assembly, MiraPluginInfo pluginInfo)
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
using AmongUs.GameOptions;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Used to configure the specific settings of a role.
|
||||
/// </summary>
|
||||
public struct CustomRoleConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CustomRoleConfiguration"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="role">The role in which you are configuring.</param>
|
||||
public CustomRoleConfiguration(ICustomRole role)
|
||||
{
|
||||
MaxRoleCount = 15;
|
||||
DefaultRoleCount = 0;
|
||||
DefaultChance = 0;
|
||||
|
||||
OptionsScreenshot = Icon = MiraAssets.Empty;
|
||||
AffectedByLightOnAirship = role.Team == ModdedRoleTeams.Crewmate;
|
||||
KillButtonOutlineColor = role.Team switch
|
||||
{
|
||||
ModdedRoleTeams.Impostor => Palette.ImpostorRed,
|
||||
ModdedRoleTeams.Crewmate => Palette.CrewmateBlue,
|
||||
_ => role.RoleColor,
|
||||
};
|
||||
RoleHintType = RoleHintType.RoleTab;
|
||||
GhostRole = role.Team == ModdedRoleTeams.Impostor ? RoleTypes.ImpostorGhost : RoleTypes.CrewmateGhost;
|
||||
CanGetKilled = role.Team == ModdedRoleTeams.Crewmate;
|
||||
UseVanillaKillButton = role.Team == ModdedRoleTeams.Impostor;
|
||||
CanUseVent = role.Team == ModdedRoleTeams.Impostor;
|
||||
CanUseSabotage = role.Team == ModdedRoleTeams.Impostor;
|
||||
TasksCountForProgress = role.Team == ModdedRoleTeams.Crewmate;
|
||||
IsGhostRole = false;
|
||||
HideSettings = IsGhostRole;
|
||||
CanModifyChance = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hard limit of players that can have this role. This property is used to set a limit in the Role Options menu. If set to 0, the role will not be assigned at start.
|
||||
/// </summary>
|
||||
public int MaxRoleCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default role count.
|
||||
/// </summary>
|
||||
public int DefaultRoleCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default role chance.
|
||||
/// </summary>
|
||||
public int DefaultChance;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the chance option can be changed or not.
|
||||
/// </summary>
|
||||
public bool CanModifyChance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Sprite used for the Role Options menu screenshot.
|
||||
/// </summary>
|
||||
public LoadableAsset<Sprite> OptionsScreenshot;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Sprite used for the Role Icon.
|
||||
/// </summary>
|
||||
public LoadableAsset<Sprite> Icon;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role is affected by light affectors on Airship.
|
||||
/// </summary>
|
||||
public bool AffectedByLightOnAirship;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role can be killed by vanilla murder system.
|
||||
/// </summary>
|
||||
public bool CanGetKilled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role should use the vanilla kill button.
|
||||
/// </summary>
|
||||
public bool UseVanillaKillButton;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role can use vents.
|
||||
/// </summary>
|
||||
public bool CanUseVent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role can use the sabotage button.
|
||||
/// </summary>
|
||||
public bool CanUseSabotage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role's tasks count towards task progress.
|
||||
/// </summary>
|
||||
public bool TasksCountForProgress;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role is a Ghost.
|
||||
/// </summary>
|
||||
public bool IsGhostRole;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role should show up in the Role Options menu.
|
||||
/// </summary>
|
||||
public bool HideSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the outline color for the KillButton if <see cref="UseVanillaKillButton"/> is true.
|
||||
/// </summary>
|
||||
public Color KillButtonOutlineColor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role hint style. See <see cref="RoleHintType"/> enum for all options.
|
||||
/// </summary>
|
||||
public RoleHintType RoleHintType;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Ghost role that is applied when the player is killed.
|
||||
/// </summary>
|
||||
public RoleTypes GhostRole;
|
||||
}
|
||||
@@ -35,6 +35,11 @@ public static class CustomRoleManager
|
||||
internal static void RegisterInRoleManager()
|
||||
{
|
||||
RoleManager.Instance.AllRoles = RoleManager.Instance.AllRoles.Concat(CustomRoles.Values).ToArray();
|
||||
|
||||
foreach (var role in CustomRoles.Values.Where(x => x.IsDead))
|
||||
{
|
||||
RoleManager.GhostRoles.Add(role.Role);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void RegisterRoleTypes(List<Type> roles, MiraPluginInfo pluginInfo)
|
||||
@@ -79,21 +84,21 @@ public static class CustomRoleManager
|
||||
roleBehaviour.StringName = CustomStringName.CreateAndRegister(customRole.RoleName);
|
||||
roleBehaviour.BlurbName = CustomStringName.CreateAndRegister(customRole.RoleDescription);
|
||||
roleBehaviour.BlurbNameLong = CustomStringName.CreateAndRegister(customRole.RoleLongDescription);
|
||||
roleBehaviour.AffectedByLightAffectors = customRole.AffectedByLight;
|
||||
roleBehaviour.CanBeKilled = customRole.CanGetKilled;
|
||||
roleBehaviour.CanUseKillButton = customRole.CanKill;
|
||||
roleBehaviour.TasksCountTowardProgress = customRole.TasksCount;
|
||||
roleBehaviour.CanVent = customRole.CanUseVent;
|
||||
roleBehaviour.DefaultGhostRole = customRole.GhostRole;
|
||||
roleBehaviour.MaxCount = customRole.MaxPlayers;
|
||||
roleBehaviour.RoleScreenshot = customRole.OptionsScreenshot.LoadAsset();
|
||||
roleBehaviour.AffectedByLightAffectors = customRole.Configuration.AffectedByLightOnAirship;
|
||||
roleBehaviour.CanBeKilled = customRole.Configuration.CanGetKilled;
|
||||
roleBehaviour.CanUseKillButton = customRole.Configuration.UseVanillaKillButton;
|
||||
roleBehaviour.TasksCountTowardProgress = customRole.Configuration.TasksCountForProgress;
|
||||
roleBehaviour.CanVent = customRole.Configuration.CanUseVent;
|
||||
roleBehaviour.DefaultGhostRole = customRole.Configuration.GhostRole;
|
||||
roleBehaviour.MaxCount = customRole.Configuration.MaxRoleCount;
|
||||
roleBehaviour.RoleScreenshot = customRole.Configuration.OptionsScreenshot.LoadAsset();
|
||||
|
||||
if (customRole.IsGhostRole)
|
||||
if (customRole.Configuration.IsGhostRole)
|
||||
{
|
||||
RoleManager.GhostRoles.Add(roleBehaviour.Role);
|
||||
}
|
||||
|
||||
var useTaskHint = customRole.RoleHintType is RoleHintType.TaskHint;
|
||||
var useTaskHint = customRole.Configuration.RoleHintType is RoleHintType.TaskHint;
|
||||
var overridesTaskText = customRole.GetType().GetMethod("SpawnTaskHeader")?.DeclaringType == customRole.GetType();
|
||||
|
||||
if (useTaskHint && !overridesTaskText)
|
||||
@@ -103,14 +108,14 @@ public static class CustomRoleManager
|
||||
|
||||
CustomRoles.Add(roleId, roleBehaviour);
|
||||
|
||||
if (customRole.HideSettings)
|
||||
if (customRole.Configuration.HideSettings)
|
||||
{
|
||||
return roleBehaviour;
|
||||
}
|
||||
|
||||
var config = parentMod.PluginConfig;
|
||||
config.Bind(customRole.NumConfigDefinition, 1);
|
||||
config.Bind(customRole.ChanceConfigDefinition, 100);
|
||||
config.Bind(customRole.NumConfigDefinition, Mathf.Clamp(customRole.Configuration.DefaultRoleCount, 0, customRole.Configuration.MaxRoleCount));
|
||||
config.Bind(customRole.ChanceConfigDefinition, Mathf.Clamp(customRole.Configuration.DefaultChance, 0, 100));
|
||||
|
||||
return roleBehaviour;
|
||||
}
|
||||
@@ -181,7 +186,7 @@ public static class CustomRoleManager
|
||||
internal static void SyncAllRoleSettings(int targetId = -1)
|
||||
{
|
||||
var data = CustomRoles.Values
|
||||
.Where(x => x is ICustomRole { HideSettings: false })
|
||||
.Where(x => x is ICustomRole { Configuration.HideSettings: false })
|
||||
.Select(x => ((ICustomRole)x).GetNetData())
|
||||
.ChunkNetData(1000);
|
||||
|
||||
@@ -211,7 +216,7 @@ public static class CustomRoleManager
|
||||
}
|
||||
|
||||
var customRole = role as ICustomRole;
|
||||
if (customRole is null or { HideSettings: true })
|
||||
if (customRole is null or { Configuration.HideSettings: true })
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -219,7 +224,7 @@ public static class CustomRoleManager
|
||||
var num = BitConverter.ToInt32(netData.Data, 0);
|
||||
var chance = BitConverter.ToInt32(netData.Data, 4);
|
||||
|
||||
DestroyableSingleton<HudManager>.Instance.Notifier.AddRoleSettingsChangeMessage(role.StringName, num, chance, role.TeamType, false);
|
||||
HudManager.Instance.Notifier.AddRoleSettingsChangeMessage(role.StringName, num, chance, role.TeamType, false);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// A utility class to get the instance of a custom role.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The role you are trying to access.</typeparam>
|
||||
public static class CustomRoleSingleton<T> where T : ICustomRole
|
||||
{
|
||||
private static ICustomRole? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the role.
|
||||
/// </summary>
|
||||
#pragma warning disable CA1000
|
||||
public static ICustomRole Instance => _instance ??= CustomRoleManager.CustomRoles.Values.OfType<T>().Single();
|
||||
#pragma warning restore CA1000
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
using System.Text;
|
||||
using AmongUs.GameOptions;
|
||||
using BepInEx.Configuration;
|
||||
using Il2CppInterop.Runtime.Attributes;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
@@ -41,76 +38,9 @@ public interface ICustomRole
|
||||
ModdedRoleTeams Team { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the **maximum amount** of players that can have this role at a time. This is not the same as the **number** of players that can have this role in a game.
|
||||
/// Configure advanced settings of the role.
|
||||
/// </summary>
|
||||
int MaxPlayers => 15;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Sprite used for the Role Options menu screenshot.
|
||||
/// </summary>
|
||||
[HideFromIl2Cpp]
|
||||
LoadableAsset<Sprite> OptionsScreenshot => MiraAssets.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Sprite used for the Role Icon.
|
||||
/// </summary>
|
||||
[HideFromIl2Cpp]
|
||||
LoadableAsset<Sprite> Icon => MiraAssets.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BepInEx ConfigDefinition for the amount of players that can have this role.
|
||||
/// </summary>
|
||||
ConfigDefinition NumConfigDefinition => new("Roles", $"Num {GetType().FullName}");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the BepInEx ConfigDefinition for the chance of this role being selected.
|
||||
/// </summary>
|
||||
ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance {GetType().FullName}");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role is affected by light sabotages.
|
||||
/// </summary>
|
||||
bool AffectedByLight => Team == ModdedRoleTeams.Crewmate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role can be killed by others.
|
||||
/// </summary>
|
||||
bool CanGetKilled => Team == ModdedRoleTeams.Crewmate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role can kill others.
|
||||
/// </summary>
|
||||
bool CanKill => Team == ModdedRoleTeams.Impostor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role can use vents.
|
||||
/// </summary>
|
||||
bool CanUseVent => Team == ModdedRoleTeams.Impostor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role's tasks count towards task progress.
|
||||
/// </summary>
|
||||
bool TasksCount => Team == ModdedRoleTeams.Crewmate;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role is a Ghost.
|
||||
/// </summary>
|
||||
bool IsGhostRole => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the role should show up in the Role Options menu.
|
||||
/// </summary>
|
||||
bool HideSettings => IsGhostRole;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the role hint style. See <see cref="RoleHintType"/> enum for all options.
|
||||
/// </summary>
|
||||
RoleHintType RoleHintType => RoleHintType.RoleTab;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Ghost role that is applied when the player is killed.
|
||||
/// </summary>
|
||||
RoleTypes GhostRole => Team == ModdedRoleTeams.Impostor ? RoleTypes.ImpostorGhost : RoleTypes.CrewmateGhost;
|
||||
CustomRoleConfiguration Configuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent mod of this role.
|
||||
@@ -125,6 +55,36 @@ public interface ICustomRole
|
||||
{
|
||||
}
|
||||
|
||||
internal ConfigDefinition NumConfigDefinition => new("Roles", $"Num {GetType().FullName}");
|
||||
internal ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance {GetType().FullName}");
|
||||
|
||||
/// <summary>
|
||||
/// Get the role chance option.
|
||||
/// </summary>
|
||||
/// <returns>The role chance option.</returns>
|
||||
public int? GetChance()
|
||||
{
|
||||
if (ParentMod.PluginConfig.TryGetEntry(ChanceConfigDefinition, out ConfigEntry<int> entry))
|
||||
{
|
||||
return Mathf.Clamp(entry.Value, 0, 100);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the role count option.
|
||||
/// </summary>
|
||||
/// <returns>The role count option.</returns>
|
||||
public int? GetCount()
|
||||
{
|
||||
if (ParentMod.PluginConfig.TryGetEntry(NumConfigDefinition, out ConfigEntry<int> entry))
|
||||
{
|
||||
return Mathf.Clamp(entry.Value, 0, Configuration.MaxRoleCount);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
/// <summary>
|
||||
/// This method runs on the HudManager.Update method ONLY when the LOCAL player has this role.
|
||||
/// </summary>
|
||||
|
||||
@@ -4,5 +4,5 @@ public enum ModdedRoleTeams
|
||||
{
|
||||
Crewmate,
|
||||
Impostor,
|
||||
Neutral
|
||||
}
|
||||
Neutral,
|
||||
}
|
||||
@@ -3,4 +3,4 @@
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class RegisterCustomRoleAttribute : Attribute;
|
||||
public class RegisterCustomRoleAttribute : Attribute;
|
||||
@@ -1,6 +1,4 @@
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// The type of hint style for a role to use.
|
||||
@@ -18,7 +16,7 @@ public enum RoleHintType
|
||||
TaskHint,
|
||||
|
||||
/// <summary>
|
||||
/// Use custom role tab.
|
||||
/// Use Mira's custom role tab.
|
||||
/// </summary>
|
||||
RoleTab,
|
||||
}
|
||||
@@ -13,7 +13,7 @@ public static class RoleId
|
||||
|
||||
return roleId;
|
||||
}
|
||||
|
||||
|
||||
public static ushort Get(Type type)
|
||||
{
|
||||
if (!CustomRoleManager.RoleIds.TryGetValue(type, out var roleId))
|
||||
@@ -23,4 +23,4 @@ public static class RoleId
|
||||
|
||||
return roleId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,17 +5,17 @@
|
||||
/// Mira uses the <see cref="LoadableAsset{T}"/> pattern in various locations.
|
||||
/// You can create your own implementation of this class to load assets in different ways.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the asset to be loaded</typeparam>
|
||||
/// <typeparam name="T">The type of the asset to be loaded.</typeparam>
|
||||
public abstract class LoadableAsset<T> where T : UnityEngine.Object
|
||||
{
|
||||
/// <summary>
|
||||
/// A reference to the loaded asset. Intended to be used for caching purposes.
|
||||
/// Gets or sets reference to the loaded asset. Intended to be used for caching purposes.
|
||||
/// </summary>
|
||||
protected T LoadedAsset;
|
||||
|
||||
protected T? LoadedAsset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The method that loads the asset.
|
||||
/// Loads the asset from the source.
|
||||
/// </summary>
|
||||
/// <returns>The loaded asset</returns>
|
||||
/// <returns>The loaded asset.</returns>
|
||||
public abstract T LoadAsset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Cpp2IL.Core.Extensions;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Utilities.Assets;
|
||||
|
||||
/// <summary>
|
||||
/// A utility class for loading .WAV audio assets from the DLL's embedded resources.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the wave file.</param>
|
||||
public class LoadableAudioResourceAsset(string path) : LoadableAsset<AudioClip>
|
||||
{
|
||||
private readonly Assembly _assembly = Assembly.GetCallingAssembly();
|
||||
|
||||
/// <summary>
|
||||
/// Loads the asset from embedded resources.
|
||||
/// </summary>
|
||||
/// <returns>The asset to load.</returns>
|
||||
/// <exception cref="NotSupportedException">Attempted to load an Audio file in non WAV format.</exception>
|
||||
/// <exception cref="FileNotFoundException">Stream failed to load. Check if the name of your asset was correct.</exception>
|
||||
public override AudioClip LoadAsset()
|
||||
{
|
||||
var assetStream = _assembly.GetManifestResourceStream(path) ??
|
||||
throw new FileNotFoundException(
|
||||
"Stream failed to load. Check if the name of your asset was correct.");
|
||||
var audioBytes = assetStream.ReadFully();
|
||||
|
||||
var riffHeader = Encoding.ASCII.GetString(audioBytes.SubArray(0, 4));
|
||||
var waveHeader = Encoding.ASCII.GetString(audioBytes.SubArray(8, 4));
|
||||
|
||||
if (riffHeader != "RIFF" || waveHeader != "WAVE")
|
||||
{
|
||||
throw new NotSupportedException($"Attempted to load an Audio file in non WAV format '{path}'.");
|
||||
}
|
||||
|
||||
int channels = BitConverter.ToInt16(audioBytes, 22);
|
||||
var sampleRate = BitConverter.ToInt32(audioBytes, 24);
|
||||
var dataSize = BitConverter.ToInt32(audioBytes, 40);
|
||||
|
||||
var audioData = new float[dataSize / 2];
|
||||
for (var i = 0; i < audioData.Length; i++)
|
||||
{
|
||||
audioData[i] = BitConverter.ToInt16(audioBytes, 44 + i * 2) / 32768.0f;
|
||||
}
|
||||
|
||||
var audioClip = AudioClip.Create(
|
||||
path,
|
||||
audioData.Length,
|
||||
channels,
|
||||
sampleRate,
|
||||
false
|
||||
);
|
||||
audioClip.SetData(audioData, 0);
|
||||
|
||||
return audioClip;
|
||||
}
|
||||
}
|
||||
@@ -7,30 +7,30 @@ namespace MiraAPI.Utilities.Assets;
|
||||
/// <summary>
|
||||
/// A utility class for loading assets from an asset bundle.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the asset</param>
|
||||
/// <param name="bundle">The AssetBundle that contains the asset</param>
|
||||
/// <typeparam name="T">The type of the asset to be loaded</typeparam>
|
||||
/// <param name="name">The name of the asset.</param>
|
||||
/// <param name="bundle">The AssetBundle that contains the asset.</param>
|
||||
/// <typeparam name="T">The type of the asset to be loaded.</typeparam>
|
||||
public class LoadableBundleAsset<T>(string name, AssetBundle bundle) : LoadableAsset<T> where T : UnityEngine.Object
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads the asset from the asset bundle.
|
||||
/// </summary>
|
||||
/// <returns>The asset</returns>
|
||||
/// <returns>The asset.</returns>
|
||||
/// <exception cref="Exception">The asset did not load properly.</exception>
|
||||
public override T LoadAsset()
|
||||
{
|
||||
if (LoadedAsset)
|
||||
if (LoadedAsset != null)
|
||||
{
|
||||
return LoadedAsset;
|
||||
}
|
||||
|
||||
LoadedAsset = bundle.LoadAsset<T>(name);
|
||||
|
||||
if (!LoadedAsset)
|
||||
|
||||
if (LoadedAsset == null)
|
||||
{
|
||||
throw new Exception($"INVALID ASSET: {name}");
|
||||
throw new InvalidOperationException($"INVALID ASSET: {name}");
|
||||
}
|
||||
|
||||
|
||||
return LoadedAsset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,24 @@
|
||||
using System.Reflection;
|
||||
using Reactor.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Utilities.Assets;
|
||||
|
||||
public class LoadableResourceAsset(string path) : LoadableAsset<Sprite>
|
||||
/// <summary>
|
||||
/// A utility class for loading assets from embedded resources.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the embedded resource.</param>
|
||||
public class LoadableResourceAsset(string path, float pixelsPerUnit=100f) : LoadableAsset<Sprite>
|
||||
{
|
||||
private readonly Assembly _assembly = Assembly.GetCallingAssembly();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Sprite LoadAsset()
|
||||
{
|
||||
if (LoadedAsset)
|
||||
if (LoadedAsset != null)
|
||||
{
|
||||
return LoadedAsset;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return LoadedAsset = SpriteTools.LoadSpriteFromPath(path, _assembly);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Not loading, invalid asset: {path}");
|
||||
return null;
|
||||
}
|
||||
return LoadedAsset = SpriteTools.LoadSpriteFromPath(path, _assembly, pixelsPerUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,5 @@ public static class MiraAssets
|
||||
public static readonly LoadableResourceAsset Cog = new("MiraAPI.Resources.Cog.png");
|
||||
public static readonly LoadableResourceAsset Checkmark = new("MiraAPI.Resources.Checkmark.png");
|
||||
public static readonly LoadableResourceAsset CheckmarkBox = new("MiraAPI.Resources.CheckMarkBox.png");
|
||||
|
||||
|
||||
public static readonly Color32 AcceptedTeal = new Color32(43, 233, 198, 255);
|
||||
}
|
||||
public static readonly Color32 AcceptedTeal = new(43, 233, 198, 255);
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using Il2CppInterop.Runtime;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Il2CppInterop.Runtime;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Utilities.Assets;
|
||||
@@ -13,15 +13,9 @@ namespace MiraAPI.Utilities.Assets;
|
||||
/// </summary>
|
||||
public static class SpriteTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for LoadImage.
|
||||
/// </summary>
|
||||
public delegate bool DLoadImage(IntPtr tex, IntPtr data, bool markNonReadable);
|
||||
private delegate bool DLoadImage(IntPtr tex, IntPtr data, bool markNonReadable);
|
||||
|
||||
/// <summary>
|
||||
/// The ICall for LoadImage.
|
||||
/// </summary>
|
||||
public static DLoadImage? ICallLoadImage;
|
||||
private static DLoadImage? _iCallLoadImage;
|
||||
|
||||
/// <summary>
|
||||
/// Load an image into a texture.
|
||||
@@ -32,20 +26,20 @@ public static class SpriteTools
|
||||
/// <returns>True if succeeded.</returns>
|
||||
public static bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable)
|
||||
{
|
||||
ICallLoadImage ??= IL2CPP.ResolveICall<DLoadImage>("UnityEngine.ImageConversion::LoadImage");
|
||||
_iCallLoadImage ??= IL2CPP.ResolveICall<DLoadImage>("UnityEngine.ImageConversion::LoadImage");
|
||||
|
||||
var il2CPPArray = (Il2CppStructArray<byte>)data;
|
||||
|
||||
return ICallLoadImage.Invoke(tex.Pointer, il2CPPArray.Pointer, markNonReadable);
|
||||
return _iCallLoadImage.Invoke(tex.Pointer, il2CPPArray.Pointer, markNonReadable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a sprite from a resource path.
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">The path to the resource.</param>
|
||||
/// <returns>A sprite made from the resource</returns>
|
||||
/// <returns>A sprite made from the resource.</returns>
|
||||
/// <exception cref="Exception">The resource cannot be found.</exception>
|
||||
public static Sprite LoadSpriteFromPath(string resourcePath, Assembly assembly)
|
||||
public static Sprite LoadSpriteFromPath(string resourcePath, Assembly assembly, float pixelsPerUnit)
|
||||
{
|
||||
var tex = new Texture2D(1, 1, TextureFormat.ARGB32, false);
|
||||
var myStream = assembly.GetManifestResourceStream(resourcePath);
|
||||
@@ -59,6 +53,6 @@ public static class SpriteTools
|
||||
Logger<MiraApiPlugin>.Error($"Resource not found: {resourcePath}\nReturning empty sprite!");
|
||||
}
|
||||
|
||||
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f));
|
||||
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f), pixelsPerUnit);
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,9 @@ public static class Extensions
|
||||
role.ParentMod.PluginConfig.TryGetEntry<int>(role.NumConfigDefinition, out var numEntry);
|
||||
role.ParentMod.PluginConfig.TryGetEntry<int>(role.ChanceConfigDefinition, out var chanceEntry);
|
||||
|
||||
return new NetData(RoleId.Get(role.GetType()), BitConverter.GetBytes(numEntry.Value).AddRangeToArray(BitConverter.GetBytes(chanceEntry.Value)));
|
||||
return new NetData(
|
||||
RoleId.Get(role.GetType()),
|
||||
BitConverter.GetBytes(numEntry.Value).AddRangeToArray(BitConverter.GetBytes(chanceEntry.Value)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -112,10 +114,11 @@ public static class Extensions
|
||||
/// <returns>True if the OptionBehaviour is for a custom options, false otherwise.</returns>
|
||||
public static bool IsCustom(this OptionBehaviour optionBehaviour)
|
||||
{
|
||||
return ModdedOptionsManager.ModdedOptions.Values.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour == optionBehaviour);
|
||||
return ModdedOptionsManager.ModdedOptions.Values.Any(
|
||||
opt => opt.OptionBehaviour && opt.OptionBehaviour == optionBehaviour);
|
||||
}
|
||||
|
||||
private static readonly Dictionary<PlayerControl, ModifierComponent> ModifierComponents = [];
|
||||
internal static Dictionary<PlayerControl, ModifierComponent> ModifierComponents { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ModifierComponent for a player.
|
||||
@@ -130,11 +133,12 @@ public static class Extensions
|
||||
}
|
||||
|
||||
component = player.GetComponent<ModifierComponent>();
|
||||
if (component)
|
||||
if (component == null)
|
||||
{
|
||||
ModifierComponents[player] = component;
|
||||
return null;
|
||||
}
|
||||
|
||||
ModifierComponents[player] = component;
|
||||
return component;
|
||||
}
|
||||
|
||||
@@ -154,6 +158,7 @@ public static class Extensions
|
||||
randomizedList.Add(list[index]);
|
||||
list.RemoveAt(index);
|
||||
}
|
||||
|
||||
return randomizedList;
|
||||
}
|
||||
|
||||
@@ -176,7 +181,8 @@ public static class Extensions
|
||||
/// <returns>True if the Modifier is present, false otherwise.</returns>
|
||||
public static bool HasModifier<T>(this PlayerControl? player) where T : BaseModifier
|
||||
{
|
||||
return player?.GetModifierComponent() != null && player.GetModifierComponent()!.ActiveModifiers.Exists(x => x is T);
|
||||
return player?.GetModifierComponent() != null &&
|
||||
player.GetModifierComponent()!.ActiveModifiers.Exists(x => x is T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -187,7 +193,8 @@ public static class Extensions
|
||||
/// <returns>True if the Modifier is present, false otherwise.</returns>
|
||||
public static bool HasModifier(this PlayerControl? player, uint id)
|
||||
{
|
||||
return player?.GetModifierComponent() != null && player.GetModifierComponent()!.ActiveModifiers.Exists(x => x.ModifierId == id);
|
||||
return player?.GetModifierComponent() != null &&
|
||||
player.GetModifierComponent()!.ActiveModifiers.Exists(x => x.ModifierId == id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -233,6 +240,7 @@ public static class Extensions
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier with id {modifierId} because it is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
target.GetModifierComponent()?.AddModifier(type);
|
||||
}
|
||||
|
||||
@@ -304,7 +312,9 @@ public static class Extensions
|
||||
/// <returns>The dead body if it is found, or null there is none within the radius.</returns>
|
||||
public static DeadBody? GetNearestDeadBody(this PlayerControl playerControl, float radius)
|
||||
{
|
||||
return Helpers.GetNearestDeadBodies(playerControl.GetTruePosition(), radius).Find(component => component && !component.Reported);
|
||||
return Helpers
|
||||
.GetNearestDeadBodies(playerControl.GetTruePosition(), radius, Helpers.CreateFilter(Constants.NotShipMask))
|
||||
.Find(component => component && !component.Reported);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -312,29 +322,46 @@ public static class Extensions
|
||||
/// </summary>
|
||||
/// <param name="playerControl">The player object.</param>
|
||||
/// <param name="radius">The radius to search within.</param>
|
||||
/// <param name="filter">The contact filter.</param>
|
||||
/// <param name="colliderTag">An optional collider tag.</param>
|
||||
/// <param name="predicate">Optional predicate to test if the object is valid.</param>
|
||||
/// <typeparam name="T">The type of the object.</typeparam>
|
||||
/// <returns>The object if it was found, or null if there is none within the radius.</returns>
|
||||
public static T? GetNearestObjectOfType<T>(this PlayerControl playerControl, float radius, string? colliderTag = null, Predicate<T>? predicate = null) where T : Component
|
||||
public static T? GetNearestObjectOfType<T>(
|
||||
this PlayerControl playerControl,
|
||||
float radius,
|
||||
ContactFilter2D filter,
|
||||
string? colliderTag = null,
|
||||
Predicate<T>? predicate = null) where T : Component
|
||||
{
|
||||
return Helpers.GetNearestObjectsOfType<T>(playerControl.GetTruePosition(), radius, colliderTag).Find(predicate ?? (component => component));
|
||||
return Helpers.GetNearestObjectsOfType<T>(playerControl.GetTruePosition(), radius, filter, colliderTag)
|
||||
.Find(predicate ?? (component => component));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nearest player to a player.
|
||||
/// Gets the closest player that matches the given criteria.
|
||||
/// </summary>
|
||||
/// <param name="playerControl">The player object.</param>
|
||||
/// <param name="includeImpostors">Whether impostors should be included in the search.</param>
|
||||
/// <param name="distance">The radius to search within.</param>
|
||||
/// <param name="ignoreColliders">Whether colliders should be ignored when searching.</param>
|
||||
/// <param name="predicate">Optional predicate to test if the object is valid.</param>
|
||||
/// <returns>The closest player if there is one, false otherwise.</returns>
|
||||
public static PlayerControl? GetClosestPlayer(this PlayerControl playerControl, bool includeImpostors, float distance, bool ignoreColliders = false)
|
||||
public static PlayerControl? GetClosestPlayer(
|
||||
this PlayerControl playerControl,
|
||||
bool includeImpostors,
|
||||
float distance,
|
||||
bool ignoreColliders = false,
|
||||
Predicate<PlayerControl>? predicate = null)
|
||||
{
|
||||
var filteredPlayers = Helpers.GetClosestPlayers(playerControl, distance, ignoreColliders)
|
||||
.Where(playerInfo => !playerInfo.Data.Disconnected && playerInfo.PlayerId != playerControl.PlayerId && !playerInfo.Data.IsDead &&
|
||||
(includeImpostors || !playerInfo.Data.Role.IsImpostor));
|
||||
.Where(
|
||||
playerInfo => !playerInfo.Data.Disconnected &&
|
||||
playerInfo.PlayerId != playerControl.PlayerId &&
|
||||
!playerInfo.Data.IsDead &&
|
||||
(includeImpostors || !playerInfo.Data.Role.IsImpostor))
|
||||
.ToList();
|
||||
|
||||
return filteredPlayers.FirstOrDefault();
|
||||
return predicate != null ? filteredPlayers.Find(predicate) : filteredPlayers.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,14 @@ namespace MiraAPI.Utilities;
|
||||
public static class Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// NotShipMask filter for collisions.
|
||||
/// Creates a ContactFilter2D from a layer mask.
|
||||
/// </summary>
|
||||
public static readonly ContactFilter2D Filter = ContactFilter2D.CreateLegacyFilter(
|
||||
Constants.NotShipMask,
|
||||
float.MinValue,
|
||||
float.MaxValue);
|
||||
/// <param name="layerMask">The layer mask.</param>
|
||||
/// <returns>A new ContactFilter2D that represents the layer mask.</returns>
|
||||
public static ContactFilter2D CreateFilter(int layerMask)
|
||||
{
|
||||
return ContactFilter2D.CreateLegacyFilter(layerMask, float.MinValue, float.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the room at a specific position.
|
||||
@@ -37,11 +39,12 @@ public static class Helpers
|
||||
/// </summary>
|
||||
/// <param name="source">The source location.</param>
|
||||
/// <param name="radius">The radius to search in.</param>
|
||||
/// <param name="filter">The contact filter.</param>
|
||||
/// <returns>A list of dead bodies.</returns>
|
||||
public static List<DeadBody> GetNearestDeadBodies(Vector2 source, float radius)
|
||||
public static List<DeadBody> GetNearestDeadBodies(Vector2 source, float radius, ContactFilter2D filter)
|
||||
{
|
||||
var results = new Il2CppSystem.Collections.Generic.List<Collider2D>();
|
||||
Physics2D.OverlapCircle(source, radius, Filter, results);
|
||||
Physics2D.OverlapCircle(source, radius, filter, results);
|
||||
return results.ToArray()
|
||||
.Where(collider2D => collider2D.CompareTag("DeadBody"))
|
||||
.Select(collider2D => collider2D.GetComponent<DeadBody>()).ToList();
|
||||
@@ -52,14 +55,15 @@ public static class Helpers
|
||||
/// </summary>
|
||||
/// <param name="source">The source point.</param>
|
||||
/// <param name="radius">The radius to search in.</param>
|
||||
/// <param name="filter">The contact filter.</param>
|
||||
/// <param name="colliderTag">An optional collider tag.</param>
|
||||
/// <typeparam name="T">The type of the object.</typeparam>
|
||||
/// <returns>A list of objects of type T.</returns>
|
||||
public static List<T> GetNearestObjectsOfType<T>(Vector2 source, float radius, string? colliderTag = null)
|
||||
public static List<T> GetNearestObjectsOfType<T>(Vector2 source, float radius, ContactFilter2D filter, string? colliderTag = null)
|
||||
where T : Component
|
||||
{
|
||||
var results = new Il2CppSystem.Collections.Generic.List<Collider2D>();
|
||||
Physics2D.OverlapCircle(source, radius, Filter, results);
|
||||
Physics2D.OverlapCircle(source, radius, filter, results);
|
||||
return results.ToArray()
|
||||
.Where(collider2D => colliderTag == null || collider2D.CompareTag(colliderTag))
|
||||
.Select(collider2D => collider2D.GetComponent<T>()).ToList();
|
||||
@@ -77,7 +81,7 @@ public static class Helpers
|
||||
float radius,
|
||||
bool ignoreColliders = true)
|
||||
{
|
||||
var newList = GetNearestObjectsOfType<PlayerControl>(source, radius);
|
||||
var newList = GetNearestObjectsOfType<PlayerControl>(source, radius, CreateFilter(Constants.NotShipMask));
|
||||
|
||||
if (!ignoreColliders)
|
||||
{
|
||||
|
||||
@@ -4,5 +4,5 @@ public enum MiraNumberSuffixes
|
||||
None,
|
||||
Multiplier,
|
||||
Seconds,
|
||||
Percent
|
||||
}
|
||||
Percent,
|
||||
}
|
||||
@@ -5,8 +5,8 @@ namespace MiraAPI.Utilities;
|
||||
|
||||
public static class ShaderID
|
||||
{
|
||||
private static readonly Dictionary<string, int> Cache = new();
|
||||
|
||||
private static readonly Dictionary<string, int> Cache = [];
|
||||
|
||||
public static int Get(string name)
|
||||
{
|
||||
if (Cache.TryGetValue(name, out var id))
|
||||
@@ -18,74 +18,74 @@ public static class ShaderID
|
||||
Cache[name] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
// For player shader
|
||||
public static readonly int BodyColor = Shader.PropertyToID("_BodyColor");
|
||||
public static readonly int BackColor = Shader.PropertyToID("_BackColor");
|
||||
public static readonly int VisorColor = Shader.PropertyToID("_VisorColor");
|
||||
|
||||
|
||||
// Main texture, very obviously used in any shader with a texture
|
||||
public static readonly int MainTex = Shader.PropertyToID("_MainTex");
|
||||
|
||||
|
||||
// Any masking stuff, like MeetingHud bubbles
|
||||
public static readonly int Mask = Shader.PropertyToID("_Mask");
|
||||
public static readonly int MaskComp = Shader.PropertyToID("_MaskComp");
|
||||
public static readonly int MaskLayer = Shader.PropertyToID("_MaskLayer");
|
||||
public static readonly int Stencil = Shader.PropertyToID("_Stencil");
|
||||
public static readonly int StencilComp = Shader.PropertyToID("_StencilComp");
|
||||
|
||||
|
||||
// Used in many tasks
|
||||
public static readonly int Color = Shader.PropertyToID("_Color");
|
||||
|
||||
|
||||
// Has 2 uses in the game, provided for convenience
|
||||
public static readonly int Opacity = Shader.PropertyToID("_Opacity");
|
||||
|
||||
|
||||
// Used once in CooldownHelpers, once in PowerBarMining
|
||||
public static readonly int NormalizedUvs = Shader.PropertyToID("_NormalizedUvs");
|
||||
|
||||
|
||||
// Used in many consoles
|
||||
public static readonly int Outline = Shader.PropertyToID("_Outline");
|
||||
public static readonly int OutlineColor = Shader.PropertyToID("_OutlineColor");
|
||||
|
||||
|
||||
// Used in some consoles
|
||||
public static readonly int AddColor = Shader.PropertyToID("_AddColor");
|
||||
|
||||
|
||||
// Has some uses in various locations
|
||||
public static readonly int Percent = Shader.PropertyToID("_Percent");
|
||||
public static readonly int PercentY = Shader.PropertyToID("_PercentY");
|
||||
public static readonly int Desat = Shader.PropertyToID("_Desat");
|
||||
|
||||
|
||||
// Used in LightSource
|
||||
public static readonly int PlayerRadius = Shader.PropertyToID("_PlayerRadius");
|
||||
public static readonly int LightRadius = Shader.PropertyToID("_LightRadius");
|
||||
public static readonly int LightOffset = Shader.PropertyToID("_LightOffset");
|
||||
public static readonly int FlashlightSize = Shader.PropertyToID("_FlashlightSize");
|
||||
public static readonly int FlashlightAngle = Shader.PropertyToID("_FlashlightAngle");
|
||||
|
||||
|
||||
// Used once for LightSourceGpuRenderer
|
||||
public static readonly int DepthCompressionValue = Shader.PropertyToID("_DepthCompressionValue");
|
||||
|
||||
|
||||
// Used in ProgressTracker
|
||||
public static readonly int Buckets = Shader.PropertyToID("_Buckets");
|
||||
public static readonly int FullBuckets = Shader.PropertyToID("_FullBuckets");
|
||||
|
||||
|
||||
// Used once in IntroCutscene and EndGameManager
|
||||
public static readonly int Rad = Shader.PropertyToID("_Rad");
|
||||
|
||||
|
||||
// Used only once for Quick Chat
|
||||
public static readonly int FaceColor = Shader.PropertyToID("_FaceColor");
|
||||
|
||||
|
||||
// Used for NavigationMinigame
|
||||
public static readonly int CrossHair = Shader.PropertyToID("_CrossHair");
|
||||
public static readonly int CrossColor = Shader.PropertyToID("_CrossColor");
|
||||
|
||||
|
||||
// Used for both SurveillanceMinigame (Planet and Normal)
|
||||
public static readonly int Center = Shader.PropertyToID("_Center");
|
||||
public static readonly int Color2 = Shader.PropertyToID("_Color2");
|
||||
|
||||
|
||||
// Used in ReactorShipRoom
|
||||
public static readonly int Speed = Shader.PropertyToID("_Speed");
|
||||
|
||||
|
||||
// Used only once in CourseMinigame
|
||||
public static readonly int AltTex = Shader.PropertyToID("_AltTex");
|
||||
public static readonly int Perc = Shader.PropertyToID("_Perc");
|
||||
@@ -93,8 +93,7 @@ public static class ShaderID
|
||||
// Used once in TextMarquee
|
||||
public static readonly int VertexOffsetX = Shader.PropertyToID("_VertexOffsetX");
|
||||
public static readonly int VertexOffsetY = Shader.PropertyToID("_VertexOffsetY");
|
||||
|
||||
|
||||
// Used in VertLineBehaviour
|
||||
public static readonly int Fade = Shader.PropertyToID("_Fade");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ To start using Mira API, you need to:
|
||||
2. Add a BepInDependency on your plugin class like this: `[BepInDependency(MiraApiPlugin.Id)]`
|
||||
3. Implement the IMiraPlugin interface on your plugin class.
|
||||
|
||||
Mira API also depends on [Reactor](https://github.com/NuclearPowered/Reactor) in order to function properly! Do not forget to include it as a reference and `BepInDependency`!
|
||||
|
||||
For a full example, see [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/ExamplePlugin.cs).
|
||||
|
||||
## Recommended Project Structure
|
||||
|
||||
Reference in new issue
Block a user