mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 21:35:49 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d54c47c835 | ||
|
|
5d4f9eb06a | ||
|
|
d153f10f0b | ||
|
|
f8af852286 | ||
|
|
9fb25542e7 | ||
|
|
8a9300425a | ||
|
|
4277193aed | ||
|
|
a9015a3535 | ||
|
|
67f0513605 | ||
|
|
9c751bcd39 | ||
|
|
10b8999f3e | ||
|
|
76c39d9bdc | ||
|
|
016d21075f | ||
|
|
36a419d721 | ||
|
|
87a1be7410 | ||
|
|
892c5c7c70 | ||
|
|
625cd2ca4b | ||
|
|
39851c6269 | ||
|
|
013c94d064 | ||
|
|
2dadf08df4 | ||
|
|
74592b2558 | ||
|
|
157c5af280 | ||
|
|
1cc41052e3 | ||
|
|
8a8fb7ced4 | ||
|
|
99d5efca1a | ||
|
|
37aee8712e | ||
|
|
f39580e858 | ||
|
|
201826dc62 | ||
|
|
4cd8aa0bd7 | ||
|
|
cd1529b4bf | ||
|
|
564f524dd6 | ||
|
|
5e9c592aab | ||
|
|
d96b146e59 | ||
|
|
a2b1e9ddbf | ||
|
|
dfa5591188 | ||
|
|
a88646465b | ||
|
|
f4f2cf9182 | ||
|
|
c77664936f | ||
|
|
55b7174be2 | ||
|
|
af471f0232 | ||
|
|
e4e3a10ed8 | ||
|
|
d0bbb191c5 | ||
|
|
6de53fb761 | ||
|
|
0e39612325 | ||
|
|
b62d1b6565 | ||
|
|
70320d7c19 | ||
|
|
2eb2567db9 | ||
|
|
3226d9fea5 | ||
|
|
a9ff1c7f63 | ||
|
|
9b173a4298 | ||
|
|
26284852c9 | ||
|
|
140d9ed353 | ||
|
|
81f9fa728b | ||
|
|
5c24488049 | ||
|
|
a0a2e9a6ab | ||
|
|
a077cabea1 |
No files matched your search
@@ -0,0 +1,26 @@
|
||||
name: CI
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.x
|
||||
|
||||
- name: Run the Cake script
|
||||
uses: cake-build/cake-action@v2
|
||||
with:
|
||||
verbosity: Diagnostic
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: MiraAPI.dll
|
||||
path: MiraAPI/bin/Release/net6.0/MiraAPI.dll
|
||||
@@ -1,4 +1,6 @@
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Example.Modifiers;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Utilities;
|
||||
using UnityEngine;
|
||||
@@ -12,8 +14,9 @@ public class ExampleButton : CustomActionButton
|
||||
public override float Cooldown => 5f;
|
||||
public override float EffectDuration => 0f;
|
||||
public override int MaxUses => 5;
|
||||
public override LoadableAsset<Sprite> Sprite { get; } = ExampleAssets.ExampleButton;
|
||||
|
||||
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
||||
public override ButtonLocation Location => ButtonLocation.BottomRight;
|
||||
|
||||
protected override void OnClick()
|
||||
{
|
||||
Logger<ExamplePlugin>.Info("Example button clicked!");
|
||||
@@ -21,6 +24,6 @@ public class ExampleButton : CustomActionButton
|
||||
|
||||
public override bool Enabled(RoleBehaviour role)
|
||||
{
|
||||
return true;
|
||||
return PlayerControl.LocalPlayer.HasModifier<GameModifierExample>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example.Buttons;
|
||||
|
||||
public class ExampleTargetButton : CustomActionButton<PlayerControl>
|
||||
{
|
||||
public override string Name => "Shoot";
|
||||
public override float Cooldown => 0f;
|
||||
public override float EffectDuration => 0f;
|
||||
public override int MaxUses => 0;
|
||||
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
|
||||
|
||||
protected override void OnClick()
|
||||
{
|
||||
PlayerControl.LocalPlayer.CmdCheckMurder(Target);
|
||||
}
|
||||
|
||||
public override PlayerControl GetTarget()
|
||||
{
|
||||
return PlayerControl.LocalPlayer.Data.Role.FindClosestTarget();
|
||||
}
|
||||
|
||||
public override void SetOutline(bool active)
|
||||
{
|
||||
Target?.cosmetics.SetOutline(active, new Il2CppSystem.Nullable<Color>(Color.red));
|
||||
}
|
||||
|
||||
public override bool IsTargetValid(PlayerControl target)
|
||||
{
|
||||
return PlayerControl.LocalPlayer.Data.Role.IsValidTarget(target.Data);
|
||||
}
|
||||
|
||||
public override bool Enabled(RoleBehaviour role)
|
||||
{
|
||||
return role.IsImpostor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using MiraAPI.Colors;
|
||||
using UnityEngine;
|
||||
|
||||
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 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));
|
||||
}
|
||||
@@ -9,7 +9,7 @@ using Reactor.Networking.Attributes;
|
||||
|
||||
namespace MiraAPI.Example;
|
||||
|
||||
[BepInAutoPlugin("mira.example", "MiraAPIExample")]
|
||||
[BepInAutoPlugin("mira.example", "MiraExampleMod")]
|
||||
[BepInProcess("Among Us.exe")]
|
||||
[BepInDependency(ReactorPlugin.Id)]
|
||||
[BepInDependency(MiraApiPlugin.Id)]
|
||||
@@ -17,9 +17,11 @@ namespace MiraAPI.Example;
|
||||
public partial class ExamplePlugin : BasePlugin, IMiraPlugin
|
||||
{
|
||||
public Harmony Harmony { get; } = new(Id);
|
||||
public string OptionsTitleText => "Mira API\nExample Mod";
|
||||
public ConfigFile GetConfigFile() => Config;
|
||||
public override void Load()
|
||||
{
|
||||
Harmony.PatchAll();
|
||||
AddComponent<MiraDebugWindow>();
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Reactor" Version="2.3.0" />
|
||||
<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="Iced" Version="1.21.0" />
|
||||
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2024.8.13" PrivateAssets="all" />
|
||||
|
||||
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" />
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using Reactor.Utilities.ImGui;
|
||||
using System;
|
||||
using MiraAPI.Example.Modifiers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example;
|
||||
[RegisterInIl2Cpp]
|
||||
public class MiraDebugWindow(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
{
|
||||
public readonly DragWindow DebuggingWindow = new(new Rect(10, 10, 0, 0), "MIRA API DEBUGGING", () =>
|
||||
{
|
||||
if (GUILayout.Button("Test modifier"))
|
||||
{
|
||||
PlayerControl.LocalPlayer.AddModifier<ModifierTimerExample>();
|
||||
}
|
||||
if (GUILayout.Button("Remove modifier"))
|
||||
{
|
||||
PlayerControl.LocalPlayer.RemoveModifier<ModifierTimerExample>();
|
||||
}
|
||||
})
|
||||
{
|
||||
Enabled = true
|
||||
};
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
DebuggingWindow.OnGUI();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace MiraAPI.Example;
|
||||
|
||||
public class ModifierExample
|
||||
{
|
||||
//name, role specific, etc
|
||||
|
||||
// mono behaviour stuff here (ModdedPlayerModifier inherits monobehaviour)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
|
||||
namespace MiraAPI.Example.Modifiers;
|
||||
|
||||
[RegisterModifier]
|
||||
public class GameModifierExample : GameModifier
|
||||
{
|
||||
public override string ModifierName => "Example Game Modifier";
|
||||
|
||||
public override bool CanVent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetAmountPerGame()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public override int GetAssignmentChance()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Modifiers.Types;
|
||||
|
||||
namespace MiraAPI.Example.Modifiers;
|
||||
|
||||
[RegisterModifier]
|
||||
public class ModifierTimerExample : TimedModifier
|
||||
{
|
||||
public override string ModifierName => "Color Changer";
|
||||
public override bool HideOnUi => true;
|
||||
public override float Duration => 10f;
|
||||
public override bool AutoStart => true;
|
||||
|
||||
private readonly System.Random _rand = new();
|
||||
|
||||
public override void OnTimerComplete()
|
||||
{
|
||||
Player.SetColor((byte)_rand.Next(0, Palette.PlayerColors.Count));
|
||||
StartTimer();
|
||||
}
|
||||
}
|
||||
@@ -6,28 +6,28 @@ namespace MiraAPI.Example.Options;
|
||||
|
||||
public class ExampleOptions : AbstractOptionGroup
|
||||
{
|
||||
public override string GroupName => "General Group";
|
||||
public override Color GroupColor => Color.red;
|
||||
public override string GroupName => "Example Options 1";
|
||||
public override Color GroupColor => Color.green;
|
||||
|
||||
[ModdedToggleOption("Use Thing")]
|
||||
public bool UseThing { get; set; } = false;
|
||||
[ModdedToggleOption("Toggle Opt 1")]
|
||||
public bool ToggleOpt { get; set; } = false;
|
||||
|
||||
[ModdedToggleOption("Use another thing")]
|
||||
public bool UseAnotherThing { get; set; } = true;
|
||||
[ModdedToggleOption("Toggle Opt 2")]
|
||||
public bool ToggleOpt2 { get; set; } = true;
|
||||
|
||||
[ModdedNumberOption("Sussy level", min: 0, max: 10)]
|
||||
public float SussyLevel { get; set; } = 4f;
|
||||
[ModdedNumberOption("Number Opt", min: 0, max: 10)]
|
||||
public float NumberOpt { get; set; } = 4f;
|
||||
|
||||
[ModdedStringOption("Sus choices", ["hello", "hello 2", "hello 3"])]
|
||||
public int SusChoices { get; set; } = 2;
|
||||
[ModdedStringOption("String Opt", ["Hello", "Hey", "Bye"])]
|
||||
public int StringOpt { get; set; } = 2;
|
||||
|
||||
[ModdedEnumOption("Enum test", typeof(TestEnum))]
|
||||
public TestEnum EnumTest { get; set; } = TestEnum.Bye;
|
||||
[ModdedEnumOption("Enum Opt", typeof(TestEnum))]
|
||||
public TestEnum EnumOpt { get; set; } = TestEnum.Mira;
|
||||
}
|
||||
|
||||
public enum TestEnum
|
||||
{
|
||||
Hello,
|
||||
Hi,
|
||||
Bye
|
||||
Welcome,
|
||||
To,
|
||||
Mira
|
||||
}
|
||||
@@ -5,23 +5,23 @@ namespace MiraAPI.Example.Options;
|
||||
|
||||
public class ExampleOptions2 : AbstractOptionGroup
|
||||
{
|
||||
public override string GroupName => "Example 2";
|
||||
|
||||
public ModdedToggleOption UseThing { get; } = new("Use Thingy 5", false);
|
||||
|
||||
public ModdedToggleOption UseThing2 { get; } = new("Use Thingy 6", false);
|
||||
|
||||
public ModdedStringOption Sussy { get; } = new("Sussy", 0, ["Sussy 1", "Sussy 2", "Sussy 3"]);
|
||||
|
||||
public ModdedEnumOption SussyEnum { get; } = new("Sussy Enum", 0, typeof(SussyEnum));
|
||||
|
||||
public ModdedNumberOption YeezyLevel { get; } = new("Yeezy Level", 50, 0, 100, 25, NumberSuffixes.Multiplier);
|
||||
public override string GroupName => "Example Options 2";
|
||||
|
||||
public ModdedToggleOption ToggleOpt1 { get; } = new("Toggle Option 1", false);
|
||||
|
||||
public ModdedToggleOption ToggleOpt2 { get; } = new("Toggle Option 2", false)
|
||||
{
|
||||
Visible = () => OptionGroupSingleton<ExampleOptions2>.Instance.ToggleOpt1.Value
|
||||
};
|
||||
|
||||
public ModdedStringOption StringOpt { get; } = new("String Opt", 0, ["Choice 1", "Choice 2", "Choice 3"]);
|
||||
|
||||
public ModdedEnumOption EnumOpt { get; } = new("Enum Opt", 0, typeof(TestingEnum));
|
||||
}
|
||||
|
||||
public enum SussyEnum
|
||||
public enum TestingEnum
|
||||
{
|
||||
Sussy,
|
||||
Baka,
|
||||
Imposter,
|
||||
Kanye
|
||||
Happy,
|
||||
Sad,
|
||||
Neutral
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Example.Options.Roles;
|
||||
|
||||
@@ -10,7 +10,7 @@ public class CustomRole2Settings : AbstractOptionGroup
|
||||
public override string GroupName => "Custom Role";
|
||||
|
||||
public override Type AdvancedRole => typeof(CustomRole2);
|
||||
|
||||
|
||||
[ModdedToggleOption("Teleportation ability")] public bool Teleport { get; set; } = true;
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.Example.Roles;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Example.Options.Roles;
|
||||
|
||||
@@ -10,7 +10,7 @@ public class CustomRoleSettings : AbstractOptionGroup
|
||||
public override string GroupName => "Custom Role";
|
||||
|
||||
public override Type AdvancedRole => typeof(CustomRole);
|
||||
|
||||
[ModdedNumberOption("Testing level", min: 3, max: 9)] public float TestingLevel { get; set; } = 4;
|
||||
|
||||
[ModdedNumberOption("Number Opt", min: 3, max: 9)] public float NumberOpt { get; set; } = 4;
|
||||
|
||||
}
|
||||
@@ -7,11 +7,11 @@ namespace MiraAPI.Example.Roles;
|
||||
[RegisterCustomRole]
|
||||
public class CustomRole : ImpostorRole, ICustomRole
|
||||
{
|
||||
public string RoleName => "fortnite killer";
|
||||
public string RoleLongDescription => "ok so your objective is to eliminate everyone with your fortnite tactical shotgun, good luck";
|
||||
public string RoleName => "Custom Role";
|
||||
public string RoleLongDescription => "Eliminate everyone with your special ability!";
|
||||
public string RoleDescription => RoleLongDescription;
|
||||
public Color RoleColor => Palette.Black;
|
||||
public ModdedRoleTeams Team => ModdedRoleTeams.Impostor;
|
||||
public LoadableAsset<Sprite> OptionsScreenshot => ExampleAssets.Banner;
|
||||
|
||||
|
||||
}
|
||||
@@ -7,11 +7,11 @@ namespace MiraAPI.Example.Roles;
|
||||
[RegisterCustomRole]
|
||||
public class CustomRole2 : CrewmateRole, ICustomRole
|
||||
{
|
||||
public string RoleName => "fortnite defender";
|
||||
public string RoleLongDescription => "ok so your objective is to save everyone from the fortnite killer";
|
||||
public string RoleName => "Custom Role 2";
|
||||
public string RoleLongDescription => "Save everyone from dying!";
|
||||
public string RoleDescription => RoleLongDescription;
|
||||
public Color RoleColor => Palette.AcceptedGreen;
|
||||
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
|
||||
public LoadableAsset<Sprite> OptionsScreenshot => ExampleAssets.Banner;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Example.Roles;
|
||||
|
||||
[RegisterCustomRole]
|
||||
public class CustomRole3 : CrewmateRole, ICustomRole
|
||||
{
|
||||
public string RoleName => "fortnite helper";
|
||||
public string RoleLongDescription => "ok so your objective is to help";
|
||||
public string RoleDescription => RoleLongDescription;
|
||||
public Color RoleColor => Palette.Brown;
|
||||
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
|
||||
public LoadableAsset<Sprite> OptionsScreenshot => ExampleAssets.Banner;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Localization.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Colors;
|
||||
public sealed class CustomColor(StringNames name, Color32 mainColor, Color32 shadowColor)
|
||||
{
|
||||
public Color32 MainColor { get; } = mainColor;
|
||||
public Color32 ShadowColor { get; } = shadowColor;
|
||||
public StringNames Name { get; } = name;
|
||||
|
||||
public CustomColor(StringNames name, Color32 mainColor) : this(name, mainColor, mainColor.GetShadowColor(60))
|
||||
{
|
||||
}
|
||||
|
||||
public CustomColor(string name, Color32 mainColor) : this(name, mainColor, mainColor.GetShadowColor(60))
|
||||
{
|
||||
}
|
||||
|
||||
public CustomColor(string name, Color32 mainColor, Color32 shadowColor) : this(CustomStringName.CreateAndRegister(name), mainColor, shadowColor)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace MiraAPI.Colors;
|
||||
|
||||
internal static class PaletteManager
|
||||
{
|
||||
public static readonly List<CustomColor> CustomColors = [];
|
||||
|
||||
public static void RegisterAllColors()
|
||||
{
|
||||
var colors = CustomColors.Select(x => x.MainColor);
|
||||
var shadowColors = CustomColors.Select(x => x.ShadowColor);
|
||||
var stringNames = CustomColors.Select(x => x.Name);
|
||||
|
||||
Palette.PlayerColors = Palette.PlayerColors.ToArray().AddRangeToArray(colors.ToArray());
|
||||
Palette.ShadowColors = Palette.ShadowColors.ToArray().AddRangeToArray(shadowColors.ToArray());
|
||||
Palette.ColorNames = Palette.ColorNames.ToArray().AddRangeToArray(stringNames.ToArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Colors;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class RegisterCustomColorsAttribute : Attribute;
|
||||
@@ -14,7 +14,7 @@ public static class CustomGameModeManager
|
||||
/// <summary>
|
||||
/// List of registered gamemodes
|
||||
/// </summary>
|
||||
public static readonly Dictionary<int, CustomGameMode> GameModes = [];
|
||||
internal static readonly Dictionary<int, CustomGameMode> GameModes = [];
|
||||
|
||||
public static bool IsDefault()
|
||||
{
|
||||
@@ -24,7 +24,7 @@ public static class CustomGameModeManager
|
||||
/// <summary>
|
||||
/// Current gamemode
|
||||
/// </summary>
|
||||
public static CustomGameMode? ActiveMode = new DefaultMode();
|
||||
public static CustomGameMode? ActiveMode { get; internal set; } = new DefaultMode();
|
||||
|
||||
/// <summary>
|
||||
/// Set current gamemode
|
||||
|
||||
@@ -7,24 +7,28 @@ namespace MiraAPI.GameOptions;
|
||||
public abstract class AbstractOptionGroup
|
||||
{
|
||||
public List<IModdedOption> Options { get; } = [];
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The name of the group. Visible in options menu.
|
||||
/// </summary>
|
||||
public abstract string GroupName { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A function that returns whether the group should be visible or not.
|
||||
/// </summary>
|
||||
public virtual Func<bool> GroupVisible => () => true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The group color. This is used to color the group in the options menu.
|
||||
/// </summary>
|
||||
public virtual Color GroupColor => Color.clear;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The role the group is associated with. This is used for the advanced role options menu.
|
||||
/// </summary>
|
||||
public virtual Type AdvancedRole => null;
|
||||
|
||||
internal bool AllOptionsHidden { get; set; }
|
||||
|
||||
internal CategoryHeaderMasked Header { get; set; }
|
||||
}
|
||||
@@ -15,13 +15,13 @@ public class ModdedEnumOptionAttribute(string title, Type enumType, Type roleTyp
|
||||
|
||||
public override void SetValue(object value)
|
||||
{
|
||||
ModdedEnumOption opt = HolderOption as ModdedEnumOption;
|
||||
var opt = HolderOption as ModdedEnumOption;
|
||||
opt.SetValue((int)value);
|
||||
}
|
||||
|
||||
public override object GetValue()
|
||||
{
|
||||
ModdedEnumOption opt = HolderOption as ModdedEnumOption;
|
||||
var opt = HolderOption as ModdedEnumOption;
|
||||
return Enum.ToObject(enumType, opt.Value);
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,13 @@ public class ModdedNumberOptionAttribute(
|
||||
|
||||
public override void SetValue(object value)
|
||||
{
|
||||
ModdedNumberOption toggleOpt = HolderOption as ModdedNumberOption;
|
||||
var toggleOpt = HolderOption as ModdedNumberOption;
|
||||
toggleOpt.SetValue((float)value);
|
||||
}
|
||||
|
||||
public override object GetValue()
|
||||
{
|
||||
ModdedNumberOption toggleOpt = HolderOption as ModdedNumberOption;
|
||||
var toggleOpt = HolderOption as ModdedNumberOption;
|
||||
return toggleOpt.Value;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace MiraAPI.GameOptions.Attributes;
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public abstract class ModdedOptionAttribute(string title, Type roleType = null) : Attribute
|
||||
{
|
||||
public IModdedOption HolderOption { get; set; }
|
||||
public IModdedOption HolderOption { get; internal set; }
|
||||
|
||||
public string Title { get; private set; } = title;
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ public class ModdedStringOptionAttribute(string title, string[] values, Type rol
|
||||
|
||||
public override void SetValue(object value)
|
||||
{
|
||||
ModdedStringOption opt = HolderOption as ModdedStringOption;
|
||||
var opt = HolderOption as ModdedStringOption;
|
||||
opt.SetValue((int)value);
|
||||
}
|
||||
|
||||
public override object GetValue()
|
||||
{
|
||||
ModdedStringOption opt = HolderOption as ModdedStringOption;
|
||||
var opt = HolderOption as ModdedStringOption;
|
||||
return opt.Value;
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,13 @@ public class ModdedToggleOptionAttribute(string title, Type roleType = null) : M
|
||||
|
||||
public override void SetValue(object value)
|
||||
{
|
||||
ModdedToggleOption toggleOpt = HolderOption as ModdedToggleOption;
|
||||
var toggleOpt = HolderOption as ModdedToggleOption;
|
||||
toggleOpt.SetValue((bool)value);
|
||||
}
|
||||
|
||||
public override object GetValue()
|
||||
{
|
||||
ModdedToggleOption toggleOpt = HolderOption as ModdedToggleOption;
|
||||
var toggleOpt = HolderOption as ModdedToggleOption;
|
||||
return toggleOpt.Value;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ public interface IModdedOption
|
||||
public ConfigDefinition ConfigDefinition { get; }
|
||||
public void ValueChanged(OptionBehaviour optionBehaviour);
|
||||
public OptionBehaviour CreateOption(ToggleOption toggleOpt, NumberOption numberOpt, StringOption stringOpt, Transform container);
|
||||
public abstract float GetFloatData();
|
||||
public NetData GetNetData();
|
||||
public void HandleNetData(byte[] data);
|
||||
}
|
||||
@@ -7,19 +7,20 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using MiraAPI.Networking;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking.Rpc;
|
||||
|
||||
namespace MiraAPI.GameOptions;
|
||||
|
||||
public class ModdedOptionsManager
|
||||
public static class ModdedOptionsManager
|
||||
{
|
||||
private static readonly Dictionary<PropertyInfo, ModdedOptionAttribute> OptionAttributes = new();
|
||||
private static readonly Dictionary<Type, AbstractOptionGroup> TypeToGroup = new();
|
||||
|
||||
public static readonly Dictionary<uint, IModdedOption> ModdedOptions = new();
|
||||
public static readonly List<AbstractOptionGroup> Groups = [];
|
||||
internal static readonly Dictionary<uint, IModdedOption> ModdedOptions = new();
|
||||
internal static readonly List<AbstractOptionGroup> Groups = [];
|
||||
|
||||
public static uint NextId => _nextId++;
|
||||
internal static uint NextId => _nextId++;
|
||||
private static uint _nextId = 1;
|
||||
|
||||
internal static bool RegisterGroup(Type type, MiraPluginInfo pluginInfo)
|
||||
@@ -109,31 +110,14 @@ public class ModdedOptionsManager
|
||||
|
||||
internal static void SyncAllOptions(int targetId=-1)
|
||||
{
|
||||
List<NetData> data = [];
|
||||
var chunks = ModdedOptions.Values.Select(option => option.GetNetData()).ChunkNetData(1000);
|
||||
|
||||
var count = 0;
|
||||
foreach (var netData in ModdedOptions.Values.Select(option => option.GetNetData()))
|
||||
while (chunks.Count > 0)
|
||||
{
|
||||
data.Add(netData);
|
||||
count += netData.GetLength();
|
||||
|
||||
if (count <= 1000)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Rpc<SyncOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
|
||||
data.Clear();
|
||||
count = 0;
|
||||
}
|
||||
|
||||
if (data.Count > 0)
|
||||
{
|
||||
Rpc<SyncOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
|
||||
Rpc<SyncOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, chunks.Dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal static void HandleSyncOptions(NetData[] data)
|
||||
{
|
||||
foreach (var netData in data)
|
||||
@@ -143,7 +127,6 @@ public class ModdedOptionsManager
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger<MiraApiPlugin>.Error("Handling option " + option.Title);
|
||||
option.HandleNetData(netData.Data);
|
||||
}
|
||||
}
|
||||
@@ -151,14 +134,14 @@ public class ModdedOptionsManager
|
||||
|
||||
public static void PropertySetterPatch(MethodBase __originalMethod, object value)
|
||||
{
|
||||
ModdedOptionAttribute attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod().Equals(__originalMethod)).Value;
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetSetMethod().Equals(__originalMethod)).Value;
|
||||
|
||||
attribute?.SetValue(value);
|
||||
}
|
||||
|
||||
public static bool PropertyGetterPatch(MethodBase __originalMethod, ref object __result)
|
||||
{
|
||||
ModdedOptionAttribute attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod().Equals(__originalMethod)).Value;
|
||||
var attribute = OptionAttributes.First(pair => pair.Key.GetGetMethod().Equals(__originalMethod)).Value;
|
||||
|
||||
if (attribute == null)
|
||||
{
|
||||
|
||||
@@ -39,7 +39,12 @@ public class ModdedEnumOption : ModdedOption<int>
|
||||
|
||||
return stringOption;
|
||||
}
|
||||
|
||||
|
||||
public override float GetFloatData()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public override NetData GetNetData()
|
||||
{
|
||||
return new NetData(Id, BitConverter.GetBytes(Value));
|
||||
|
||||
@@ -65,6 +65,11 @@ public class ModdedNumberOption : ModdedOption<float>
|
||||
return numberOption;
|
||||
}
|
||||
|
||||
public override float GetFloatData()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public override NetData GetNetData()
|
||||
{
|
||||
return new NetData(Id, BitConverter.GetBytes(Value));
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using MiraAPI.PluginLoading;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.Networking;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Localization.Utilities;
|
||||
using System;
|
||||
using BepInEx.Configuration;
|
||||
using MiraAPI.Networking;
|
||||
using Reactor.Networking.Rpc;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.GameOptions.OptionTypes;
|
||||
@@ -12,7 +12,7 @@ namespace MiraAPI.GameOptions.OptionTypes;
|
||||
public abstract class ModdedOption<T> : IModdedOption
|
||||
{
|
||||
private IMiraPlugin _parentMod;
|
||||
|
||||
|
||||
public uint Id { get; }
|
||||
public BaseGameSetting Data { get; protected init; }
|
||||
public IMiraPlugin ParentMod
|
||||
@@ -26,14 +26,14 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
}
|
||||
}
|
||||
public T Value { get; protected set; }
|
||||
public T DefaultValue { get; init; }
|
||||
public T DefaultValue { get; }
|
||||
public Action<T> ChangedEvent { get; set; }
|
||||
public string Title { get; }
|
||||
public StringNames StringName { get; }
|
||||
public Func<bool> Visible { get; set; }
|
||||
public Type AdvancedRole { get; set; }
|
||||
public OptionBehaviour OptionBehaviour { get; set; }
|
||||
public ConfigDefinition ConfigDefinition => new ("Options", Title);
|
||||
public ConfigDefinition ConfigDefinition => new("Options", Title);
|
||||
|
||||
protected ModdedOption(string title, T defaultValue, Type roleType)
|
||||
{
|
||||
@@ -68,18 +68,19 @@ public abstract class ModdedOption<T> : IModdedOption
|
||||
{
|
||||
Rpc<SyncOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [GetNetData()], true);
|
||||
}
|
||||
|
||||
if (ParentMod is not null &&
|
||||
ParentMod.GetConfigFile().TryGetEntry<T>(ConfigDefinition, out var entry))
|
||||
|
||||
if (ParentMod?.GetConfigFile().TryGetEntry<T>(ConfigDefinition, out var entry) == true)
|
||||
{
|
||||
entry.Value = Value;
|
||||
}
|
||||
|
||||
|
||||
OnValueChanged(newValue);
|
||||
}
|
||||
|
||||
public abstract float GetFloatData();
|
||||
|
||||
public abstract NetData GetNetData();
|
||||
|
||||
|
||||
public abstract void HandleNetData(byte[] data);
|
||||
|
||||
public abstract void OnValueChanged(T newValue);
|
||||
|
||||
@@ -40,6 +40,11 @@ public class ModdedStringOption : ModdedOption<int>
|
||||
return stringOption;
|
||||
}
|
||||
|
||||
public override float GetFloatData()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public override NetData GetNetData()
|
||||
{
|
||||
return new NetData(Id, BitConverter.GetBytes(Value));
|
||||
|
||||
@@ -32,6 +32,11 @@ public class ModdedToggleOption : ModdedOption<bool>
|
||||
return toggleOption;
|
||||
}
|
||||
|
||||
public override float GetFloatData()
|
||||
{
|
||||
return Value ? 1 : 0;
|
||||
}
|
||||
|
||||
public override NetData GetNetData()
|
||||
{
|
||||
return new NetData(Id, BitConverter.GetBytes(Value));
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace MiraAPI.Hud;
|
||||
|
||||
public enum ButtonLocation
|
||||
{
|
||||
BottomLeft,
|
||||
BottomRight
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
using MiraAPI.Utilities.Assets;
|
||||
#nullable enable
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace MiraAPI.Hud;
|
||||
|
||||
/// <summary>
|
||||
/// Class for making custom action buttons. More customizable than the default Action/Ability buttons in the base game
|
||||
/// </summary>
|
||||
public abstract class CustomActionButton
|
||||
{
|
||||
/// <summary>
|
||||
@@ -31,6 +36,11 @@ public abstract class CustomActionButton
|
||||
/// </summary>
|
||||
public abstract LoadableAsset<Sprite> Sprite { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The location of the button on the screen
|
||||
/// </summary>
|
||||
public virtual ButtonLocation Location => ButtonLocation.BottomLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the button has an effect ability.
|
||||
/// </summary>
|
||||
@@ -59,7 +69,7 @@ public abstract class CustomActionButton
|
||||
/// <summary>
|
||||
/// The button object in game. This is created by Mira API automatically.
|
||||
/// </summary>
|
||||
protected ActionButton Button { get; private set; }
|
||||
protected ActionButton? Button { get; private set; }
|
||||
|
||||
internal void CreateButton(Transform parent)
|
||||
{
|
||||
@@ -109,7 +119,10 @@ public abstract class CustomActionButton
|
||||
/// <param name="sprite">The new sprite to override with</param>
|
||||
public void OverrideSprite(Sprite sprite)
|
||||
{
|
||||
Button.graphic.sprite = sprite;
|
||||
if (Button != null)
|
||||
{
|
||||
Button.graphic.sprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -118,7 +131,7 @@ public abstract class CustomActionButton
|
||||
/// <param name="name">The new name to override with</param>
|
||||
public void OverrideName(string name)
|
||||
{
|
||||
Button.OverrideText(name);
|
||||
Button?.OverrideText(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -137,7 +150,7 @@ public abstract class CustomActionButton
|
||||
/// True means the button is active, false means the button is disabled.
|
||||
/// </summary>
|
||||
/// <param name="role">The role of the local player.</param>
|
||||
public abstract bool Enabled(RoleBehaviour role);
|
||||
public abstract bool Enabled(RoleBehaviour? role);
|
||||
|
||||
/// <summary>
|
||||
/// Given that there is an effect, this method that runs when the effect ends.
|
||||
@@ -164,7 +177,7 @@ public abstract class CustomActionButton
|
||||
/// <param name="role">Passed in from HudManager.SetHudActive, the current role of the player.</param>
|
||||
public virtual void SetActive(bool visible, RoleBehaviour role)
|
||||
{
|
||||
Button.ToggleVisible(visible && Enabled(role));
|
||||
Button?.ToggleVisible(visible && Enabled(role));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -172,7 +185,7 @@ public abstract class CustomActionButton
|
||||
/// This method takes into account cooldowns, effects, and uses, before calling the <see cref="OnClick"/> method.
|
||||
/// It can be overridden for custom behavior.
|
||||
/// </summary>
|
||||
public virtual void ClickHandler()
|
||||
protected virtual void ClickHandler()
|
||||
{
|
||||
if (!CanUse())
|
||||
{
|
||||
@@ -182,11 +195,11 @@ public abstract class CustomActionButton
|
||||
if (LimitedUses)
|
||||
{
|
||||
UsesLeft--;
|
||||
Button.SetUsesRemaining(UsesLeft);
|
||||
Button?.SetUsesRemaining(UsesLeft);
|
||||
}
|
||||
|
||||
OnClick();
|
||||
Button.SetDisabled();
|
||||
Button?.SetDisabled();
|
||||
if (HasEffect)
|
||||
{
|
||||
EffectActive = true;
|
||||
@@ -219,15 +232,74 @@ public abstract class CustomActionButton
|
||||
|
||||
if (CanUse())
|
||||
{
|
||||
Button.SetEnabled();
|
||||
Button?.SetEnabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
Button.SetDisabled();
|
||||
Button?.SetDisabled();
|
||||
}
|
||||
|
||||
Button.SetCoolDown(Timer, EffectActive ? EffectDuration : Cooldown);
|
||||
Button?.SetCoolDown(Timer, EffectActive ? EffectDuration : Cooldown);
|
||||
|
||||
FixedUpdate(playerControl);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom action button that has a target object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the target object.</typeparam>
|
||||
public abstract class CustomActionButton<T> : CustomActionButton where T : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// The target object of the button.
|
||||
/// </summary>
|
||||
public T? Target { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The distance the player must be from the target object to use the button.
|
||||
/// </summary>
|
||||
public virtual float Distance => PlayerControl.LocalPlayer.Data.Role.GetAbilityDistance();
|
||||
|
||||
/// <summary>
|
||||
/// An optional collider tag to filter the target object by.
|
||||
/// </summary>
|
||||
public virtual string? ColliderTag => null;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the target object is valid.
|
||||
/// </summary>
|
||||
public virtual bool IsTargetValid(T target)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method used to get the target object.
|
||||
/// </summary>
|
||||
public virtual T? GetTarget()
|
||||
{
|
||||
return PlayerControl.LocalPlayer.GetNearestObjectOfType<T>(Distance, ColliderTag, IsTargetValid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the outline of the target object.
|
||||
/// </summary>
|
||||
/// <param name="active">Should the outline be active</param>
|
||||
public abstract void SetOutline(bool active);
|
||||
|
||||
|
||||
public override bool CanUse()
|
||||
{
|
||||
var newTarget = GetTarget();
|
||||
if (newTarget != Target)
|
||||
{
|
||||
SetOutline(false);
|
||||
}
|
||||
|
||||
Target = newTarget;
|
||||
SetOutline(true);
|
||||
|
||||
return base.CanUse() && Target;
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ using Reactor.Utilities;
|
||||
|
||||
namespace MiraAPI.Hud;
|
||||
|
||||
public static class CustomButtonManager
|
||||
internal static class CustomButtonManager
|
||||
{
|
||||
public static readonly List<CustomActionButton> CustomButtons = [];
|
||||
internal static readonly List<CustomActionButton> CustomButtons = [];
|
||||
|
||||
internal static void RegisterButton(Type buttonType)
|
||||
{
|
||||
|
||||
+12
-5
@@ -3,14 +3,22 @@
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<DebugType>embedded</DebugType>
|
||||
|
||||
<VersionPrefix>1.0.0</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
<PackageId>AllOfUs.MiraAPI</PackageId>
|
||||
<Version>0.1.0</Version>
|
||||
<Description>Simple and easy to use Among Us modding API.</Description>
|
||||
<Authors>Angxl, XtraCube</Authors>
|
||||
<Company>All Of Us</Company>
|
||||
<PackageLicenseExpression>LGPL-2.1-or-later</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Checkmark.png" />
|
||||
<EmbeddedResource Include="Resources\CheckMarkBox.png" />
|
||||
<EmbeddedResource Include="Resources\Cog.png" />
|
||||
<EmbeddedResource Include="Resources\CogActive.png" />
|
||||
<EmbeddedResource Include="Resources\NextButton.png" />
|
||||
@@ -19,9 +27,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Reactor" Version="2.3.0" />
|
||||
<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="Iced" Version="1.21.0" />
|
||||
<PackageReference Include="AmongUs.GameLibs.Steam" Version="2024.8.13" PrivateAssets="all" />
|
||||
|
||||
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" />
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
using BepInEx.Unity.IL2CPP;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor;
|
||||
using Reactor.Patches;
|
||||
using System;
|
||||
using System.Text;
|
||||
using Reactor.Networking;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI;
|
||||
@@ -20,30 +19,16 @@ public partial class MiraApiPlugin : BasePlugin
|
||||
{
|
||||
public Harmony Harmony { get; } = new(Id);
|
||||
private static MiraPluginManager PluginManager { get; set; }
|
||||
|
||||
|
||||
public static Color MiraColor = new Color32(238, 154, 112, 255);
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
Harmony.PatchAll();
|
||||
|
||||
ReactorCredits.Register("Mira API", Version.Truncate(11, "") ?? Version, true, ReactorCredits.AlwaysShow);
|
||||
|
||||
PluginManager = new MiraPluginManager();
|
||||
PluginManager.Initialize();
|
||||
|
||||
ReactorVersionShower.TextUpdated += text =>
|
||||
{
|
||||
text.text = new StringBuilder($"{MiraColor.ToTextColor()}Mira API</color> ")
|
||||
.Append(GetShortHashVersion(Version))
|
||||
.Append('\n')
|
||||
.Append(text.text)
|
||||
.ToString();
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetShortHashVersion(string version)
|
||||
{
|
||||
var index = version.IndexOf("+", StringComparison.Ordinal);
|
||||
|
||||
return index < 0 ? version : version[..(index + 3)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace MiraAPI.Modifiers;
|
||||
public abstract class BaseModifier
|
||||
{
|
||||
public abstract string ModifierName { get; }
|
||||
public virtual bool HideOnUi => false;
|
||||
public PlayerControl Player { get; internal set; }
|
||||
public uint ModifierId { get; internal set; }
|
||||
public virtual void OnActivate() { }
|
||||
public virtual void OnDeactivate() { }
|
||||
public virtual void Update() { }
|
||||
public virtual void FixedUpdate() { }
|
||||
public virtual void OnDeath() { }
|
||||
public virtual bool CanVent() => Player.Data.Role.CanVent;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.Networking;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Modifiers;
|
||||
|
||||
[RegisterInIl2Cpp]
|
||||
public class ModifierComponent(IntPtr ptr) : MonoBehaviour(ptr)
|
||||
{
|
||||
public List<BaseModifier> ActiveModifiers { get; private set; }
|
||||
|
||||
public PlayerControl player;
|
||||
|
||||
private TextMeshPro _modifierText;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
player = GetComponent<PlayerControl>();
|
||||
ActiveModifiers = [];
|
||||
|
||||
if (!player.AmOwner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_modifierText = Helpers.CreateTextLabel("ModifierText", HudManager.Instance.transform, AspectPosition.EdgeAlignments.RightTop, new Vector3(10.1f, 3.5f, 0), textAlignment: TextAlignmentOptions.Right);
|
||||
_modifierText.verticalAlignment = VerticalAlignmentOptions.Top;
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
foreach (var modifier in ActiveModifiers)
|
||||
{
|
||||
modifier.FixedUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
foreach (var modifier in ActiveModifiers)
|
||||
{
|
||||
modifier.Update();
|
||||
}
|
||||
|
||||
var filteredModifiers = ActiveModifiers.Where(mod => !mod.HideOnUi);
|
||||
|
||||
var baseModifiers = filteredModifiers as BaseModifier[] ?? filteredModifiers.ToArray();
|
||||
|
||||
if (player.AmOwner && baseModifiers.Any())
|
||||
{
|
||||
var stringBuild = new StringBuilder();
|
||||
foreach (var mod in baseModifiers)
|
||||
{
|
||||
stringBuild.Append($"\n{mod.ModifierName}");
|
||||
if (mod is TimedModifier timer)
|
||||
{
|
||||
stringBuild.Append($" <size=70%>({Math.Round(timer.Duration - timer.TimeRemaining, 0)}s/{timer.Duration}s)</size>");
|
||||
}
|
||||
}
|
||||
_modifierText.text = $"<b><size=130%>Modifiers:</b></size>{stringBuild}";
|
||||
}
|
||||
else if (player.AmOwner && !baseModifiers.Any() && _modifierText.text != string.Empty)
|
||||
{
|
||||
_modifierText.text = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveModifier(PlayerControl target, uint modifierId)
|
||||
{
|
||||
if (!ModifierManager.IdToTypeModifiers.TryGetValue(modifierId, out var type))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot remove modifier with id {modifierId} because it is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
var modifierComponent = target.GetModifierComponent();
|
||||
|
||||
var modifier = modifierComponent.ActiveModifiers.Find(x => x.GetType() == type);
|
||||
|
||||
if (modifier is null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot remove modifier {type.Name} because it is not active.");
|
||||
return;
|
||||
}
|
||||
|
||||
modifier.OnDeactivate();
|
||||
modifierComponent.ActiveModifiers.Remove(modifier);
|
||||
|
||||
if (target.AmOwner)
|
||||
{
|
||||
HudManager.Instance.SetHudActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddModifier(PlayerControl target, uint modifierId)
|
||||
{
|
||||
if (!ModifierManager.IdToTypeModifiers.TryGetValue(modifierId, out var type))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier with id {modifierId} because it is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.HasModifier(modifierId))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Player already has modifier with id {modifierId}!");
|
||||
return;
|
||||
}
|
||||
|
||||
var modifier = (BaseModifier)Activator.CreateInstance(type);
|
||||
|
||||
var modifierComponent = target.GetModifierComponent();
|
||||
|
||||
if (modifier is null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier {type.Name} because it is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
modifierComponent.ActiveModifiers.Add(modifier);
|
||||
modifier.Player = modifierComponent.player;
|
||||
modifier.ModifierId = modifierId;
|
||||
modifier.OnActivate();
|
||||
|
||||
if (target.AmOwner)
|
||||
{
|
||||
if (modifier is TimedModifier { AutoStart: true } timer)
|
||||
{
|
||||
timer.StartTimer();
|
||||
}
|
||||
|
||||
HudManager.Instance.SetHudActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodRpc((uint)MiraRpc.RemoveModifier)]
|
||||
public static void RpcRemoveModifier(PlayerControl target, uint modifierId) => RemoveModifier(target, modifierId);
|
||||
|
||||
[MethodRpc((uint)MiraRpc.AddModifier)]
|
||||
public static void RpcAddModifier(PlayerControl target, uint modifierId) => AddModifier(target, modifierId);
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using MiraAPI.Modifiers.Types;
|
||||
using MiraAPI.Networking;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking.Rpc;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Random = System.Random;
|
||||
|
||||
namespace MiraAPI.Modifiers;
|
||||
|
||||
public static class ModifierManager
|
||||
{
|
||||
public static readonly Dictionary<uint, Type> IdToTypeModifiers = [];
|
||||
public static readonly Dictionary<Type, uint> TypeToIdModifiers = [];
|
||||
|
||||
private static uint _nextId;
|
||||
|
||||
private static uint GetNextId()
|
||||
{
|
||||
_nextId++;
|
||||
return _nextId;
|
||||
}
|
||||
|
||||
internal static void RegisterModifier(Type modifierType)
|
||||
{
|
||||
if (!typeof(BaseModifier).IsAssignableFrom(modifierType))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IdToTypeModifiers.Add(GetNextId(), modifierType);
|
||||
TypeToIdModifiers.Add(modifierType, _nextId);
|
||||
}
|
||||
|
||||
internal static void AssignModifiers(List<PlayerControl> plrs)
|
||||
{
|
||||
var rand = new Random();
|
||||
|
||||
List<uint> filteredModifiers = [];
|
||||
|
||||
foreach (var modifier in IdToTypeModifiers)
|
||||
{
|
||||
var mod = (GameModifier)Activator.CreateInstance(modifier.Value);
|
||||
var num = mod.GetAmountPerGame();
|
||||
var chance = mod.GetAssignmentChance();
|
||||
|
||||
for (var i = 0; i < num; i++)
|
||||
{
|
||||
var randomNum = rand.Next(100);
|
||||
|
||||
if (randomNum < Math.Clamp(chance, 0, 100))
|
||||
{
|
||||
filteredModifiers.Add(TypeToIdModifiers[modifier.Value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var shuffledModifiers = filteredModifiers.Randomize();
|
||||
if (shuffledModifiers.Count > plrs.Count)
|
||||
{
|
||||
shuffledModifiers = shuffledModifiers.GetRange(0, plrs.Count);
|
||||
}
|
||||
|
||||
while (shuffledModifiers.Count > 0)
|
||||
{
|
||||
var id = shuffledModifiers[0];
|
||||
|
||||
var mod = (GameModifier)Activator.CreateInstance(IdToTypeModifiers[id]);
|
||||
var plr = plrs.Random();
|
||||
|
||||
if (plr.Data.Role is ICustomRole modRole)
|
||||
{
|
||||
if (!modRole.IsModifierApplicable(mod))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (plr.HasModifier(id) || mod.IsModifierValidOn(plr.Data.Role))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
shuffledModifiers.RemoveAt(0);
|
||||
ModifierComponent.RpcAddModifier(plr, id);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SyncAllModifiers(int targetId = -1)
|
||||
{
|
||||
var data = new List<NetData>();
|
||||
|
||||
if (targetId == -1)
|
||||
{
|
||||
foreach (var player in GameData.Instance.AllPlayers)
|
||||
{
|
||||
data.Add(GetPlayerModifiers(player.Object));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var player = GameData.Instance.GetPlayerById((byte)targetId).Object;
|
||||
|
||||
data.Add(GetPlayerModifiers(player));
|
||||
}
|
||||
|
||||
|
||||
Rpc<SyncModifiersRpc>.Instance.Send(PlayerControl.LocalPlayer, data.ToArray(), true);
|
||||
}
|
||||
|
||||
internal static void HandleSyncModifiers(NetData[] data)
|
||||
{
|
||||
foreach (var netData in data)
|
||||
{
|
||||
var ids = new uint[netData.Data.Length / 4];
|
||||
Buffer.BlockCopy(netData.Data, 0, ids, 0, netData.Data.Length);
|
||||
|
||||
var plr = GameData.Instance.GetPlayerById((byte)netData.Id).Object;
|
||||
var modifierComponent = plr.GetComponent<ModifierComponent>();
|
||||
|
||||
if (!modifierComponent)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var modifier in modifierComponent.ActiveModifiers)
|
||||
{
|
||||
modifier.OnDeactivate();
|
||||
}
|
||||
modifierComponent.ActiveModifiers.Clear();
|
||||
|
||||
foreach (var id in ids)
|
||||
{
|
||||
ModifierComponent.AddModifier(plr, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static NetData GetPlayerModifiers(PlayerControl player)
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
var modifierComponent = player.GetComponent<ModifierComponent>();
|
||||
if (!modifierComponent)
|
||||
{
|
||||
return new NetData(player.PlayerId, []);
|
||||
}
|
||||
|
||||
foreach (var modifier in modifierComponent.ActiveModifiers)
|
||||
{
|
||||
bytes.AddRange(BitConverter.GetBytes(TypeToIdModifiers[modifier.GetType()]));
|
||||
}
|
||||
|
||||
return new NetData(player.PlayerId, bytes.ToArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Modifiers;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class RegisterModifierAttribute : Attribute;
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace MiraAPI.Modifiers.Types;
|
||||
public abstract class GameModifier : BaseModifier
|
||||
{
|
||||
public abstract int GetAssignmentChance();
|
||||
public abstract int GetAmountPerGame();
|
||||
public virtual bool IsModifierValidOn(RoleBehaviour role) => true;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Reactor.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Modifiers.Types;
|
||||
public abstract class TimedModifier : BaseModifier
|
||||
{
|
||||
public abstract float Duration { get; }
|
||||
public virtual bool AutoStart => false;
|
||||
public abstract void OnTimerComplete();
|
||||
|
||||
public bool TimerActive { get; protected set; }
|
||||
|
||||
public float TimeRemaining { get; protected set; }
|
||||
|
||||
public override void FixedUpdate()
|
||||
{
|
||||
if (!Player.AmOwner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (TimeRemaining > 0 && TimerActive)
|
||||
{
|
||||
TimeRemaining -= Time.fixedDeltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
TimerActive = false;
|
||||
TimeRemaining = Duration;
|
||||
OnTimerComplete();
|
||||
}
|
||||
}
|
||||
|
||||
public void StartTimer()
|
||||
{
|
||||
if (TimerActive)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("Can't start a timer that has already been started.");
|
||||
return;
|
||||
}
|
||||
|
||||
TimerActive = true;
|
||||
TimeRemaining = Duration;
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,8 @@
|
||||
public enum MiraRpc : uint
|
||||
{
|
||||
SyncGameOptions,
|
||||
SyncRoleOptions
|
||||
SyncRoleOptions,
|
||||
AddModifier,
|
||||
RemoveModifier,
|
||||
SyncModifiers
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Networking;
|
||||
namespace MiraAPI.Networking;
|
||||
|
||||
public readonly struct NetData(uint id, byte[] data)
|
||||
{
|
||||
public uint Id { get; } = id;
|
||||
public byte[] Data { get; } = data;
|
||||
public int GetLength() => BitConverter.GetBytes(Id).Length + Data.Length;
|
||||
public int GetLength() => 4 + Data.Length;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Hazel;
|
||||
using MiraAPI.Modifiers;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Networking.Rpc;
|
||||
|
||||
namespace MiraAPI.Networking;
|
||||
|
||||
[RegisterCustomRpc((uint)MiraRpc.SyncModifiers)]
|
||||
internal 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)
|
||||
{
|
||||
writer.WritePacked((uint)data.Length);
|
||||
foreach (var netData in data)
|
||||
{
|
||||
writer.WritePacked(netData.Id);
|
||||
writer.WriteBytesAndSize(netData.Data);
|
||||
}
|
||||
}
|
||||
|
||||
public override NetData[] Read(MessageReader reader)
|
||||
{
|
||||
var length = reader.ReadPackedUInt32();
|
||||
var data = new NetData[length];
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var id = reader.ReadPackedUInt32();
|
||||
var bytes = reader.ReadBytesAndSize();
|
||||
data[i] = new NetData(id, bytes);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public override void Handle(PlayerControl playerControl, NetData[] data)
|
||||
{
|
||||
if (AmongUsClient.Instance.HostId != playerControl.OwnerId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ModifierManager.HandleSyncModifiers(data);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ 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)]
|
||||
public class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
internal class SyncOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
{
|
||||
public override RpcLocalHandling LocalHandling => RpcLocalHandling.None;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ using Reactor.Networking.Rpc;
|
||||
namespace MiraAPI.Networking;
|
||||
|
||||
[RegisterCustomRpc((uint)MiraRpc.SyncRoleOptions)]
|
||||
public class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
internal class SyncRoleOptionsRpc(MiraApiPlugin plugin, uint id) : PlayerCustomRpc<MiraApiPlugin, NetData[]>(plugin, id)
|
||||
{
|
||||
|
||||
public override RpcLocalHandling LocalHandling => RpcLocalHandling.None;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Roles;
|
||||
|
||||
namespace MiraAPI.Patches;
|
||||
@@ -14,7 +15,7 @@ public static class AmongUsClientSyncPatch
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (clientData.Id == AmongUsClient.Instance.HostId)
|
||||
{
|
||||
return;
|
||||
@@ -22,5 +23,6 @@ public static class AmongUsClientSyncPatch
|
||||
|
||||
ModdedOptionsManager.SyncAllOptions(clientData.Id);
|
||||
CustomRoleManager.SyncAllRoleSettings(clientData.Id);
|
||||
ModifierManager.SyncAllModifiers(clientData.Id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Colors;
|
||||
|
||||
[HarmonyPatch(typeof(PlayerTab))]
|
||||
public static class ScrollingColorsPatch
|
||||
{
|
||||
/// Collider
|
||||
private static BoxCollider2D _collider;
|
||||
|
||||
/// <summary>
|
||||
/// Add scrolling to the colors tab
|
||||
/// </summary>
|
||||
[HarmonyPostfix, HarmonyPatch("OnEnable")]
|
||||
public static void AddScrollingToColorsTabPatch(PlayerTab __instance)
|
||||
{
|
||||
if (!PlayerCustomizationMenu.Instance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var tab = PlayerCustomizationMenu.Instance.Tabs[1].Tab;
|
||||
|
||||
if (__instance.scroller == null)
|
||||
{
|
||||
var newScroller = Object.Instantiate(tab.scroller, __instance.transform, true);
|
||||
newScroller.Inner.transform.DestroyChildren();
|
||||
|
||||
var maskObj = new GameObject
|
||||
{
|
||||
layer = 5,
|
||||
name = "SpriteMask"
|
||||
};
|
||||
maskObj.transform.SetParent(__instance.transform);
|
||||
maskObj.transform.localPosition = new Vector3(0, 0, 0);
|
||||
maskObj.transform.localScale = new Vector3(500, 4.76f);
|
||||
|
||||
var mask = maskObj.AddComponent<SpriteMask>();
|
||||
mask.sprite = MiraAssets.Empty.LoadAsset();
|
||||
|
||||
_collider = maskObj.AddComponent<BoxCollider2D>();
|
||||
_collider.size = new Vector2(1f, 0.75f);
|
||||
_collider.enabled = true;
|
||||
__instance.scroller = newScroller;
|
||||
}
|
||||
|
||||
foreach (var chip in __instance.ColorChips)
|
||||
{
|
||||
chip.transform.SetParent(__instance.scroller.Inner);
|
||||
chip.Button.ClickMask = _collider;
|
||||
chip.GetComponent<SpriteRenderer>().maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
|
||||
}
|
||||
|
||||
__instance.SetScrollerBounds();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace MiraAPI.Patches;
|
||||
|
||||
@@ -78,10 +80,12 @@ public static class HudManagerPatches
|
||||
[HarmonyPatch(nameof(HudManager.Start))]
|
||||
public static void StartPostfix(HudManager __instance)
|
||||
{
|
||||
var buttons = __instance.transform.Find("Buttons");
|
||||
var bottomRight = buttons.Find("BottomRight");
|
||||
|
||||
if (!_bottomLeft)
|
||||
{
|
||||
var buttons = __instance.transform.Find("Buttons");
|
||||
_bottomLeft = Object.Instantiate(buttons.Find("BottomRight").gameObject, buttons);
|
||||
_bottomLeft = Object.Instantiate(bottomRight.gameObject, buttons);
|
||||
}
|
||||
|
||||
foreach (var t in _bottomLeft.GetComponentsInChildren<ActionButton>(true))
|
||||
@@ -98,7 +102,19 @@ public static class HudManagerPatches
|
||||
|
||||
foreach (var button in CustomButtonManager.CustomButtons)
|
||||
{
|
||||
button.CreateButton(_bottomLeft.transform);
|
||||
var location = button.Location switch
|
||||
{
|
||||
ButtonLocation.BottomLeft => _bottomLeft.transform,
|
||||
ButtonLocation.BottomRight => bottomRight,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (location is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
button.CreateButton(location);
|
||||
}
|
||||
|
||||
gridArrange.Start();
|
||||
@@ -125,7 +141,9 @@ public static class HudManagerPatches
|
||||
}
|
||||
|
||||
foreach (var button in CustomButtonManager.CustomButtons)
|
||||
{
|
||||
button.SetActive(isActive, roleBehaviour);
|
||||
}
|
||||
|
||||
if (roleBehaviour is ICustomRole role)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Utilities;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Modifiers;
|
||||
|
||||
[HarmonyPatch(typeof(Vent))]
|
||||
public static class VentPatches
|
||||
{
|
||||
[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)
|
||||
{
|
||||
var @object = pc.Object;
|
||||
var role = @object.Data.Role;
|
||||
|
||||
var modifiers = @object.GetModifierComponent()?.ActiveModifiers;
|
||||
if (modifiers is null || modifiers.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (role.CanVent)
|
||||
{
|
||||
case true when modifiers.Any(x => !x.CanVent()):
|
||||
couldUse = canUse = false;
|
||||
return;
|
||||
case false when modifiers.Any(x => x.CanVent()):
|
||||
couldUse = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var num = float.MaxValue;
|
||||
|
||||
canUse = couldUse;
|
||||
if (canUse)
|
||||
{
|
||||
var center = @object.Collider.bounds.center;
|
||||
var position = __instance.transform.position;
|
||||
num = Vector2.Distance(center, position);
|
||||
canUse &= num <= __instance.UsableDistance && !PhysicsHelpers.AnythingBetween(@object.Collider, center, position, Constants.ShipOnlyMask, false);
|
||||
}
|
||||
__result = num;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
using HarmonyLib;
|
||||
using Il2CppSystem.Collections.Generic;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using Reactor.Localization.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace MiraAPI.Patches.Options;
|
||||
|
||||
@@ -13,6 +15,59 @@ namespace MiraAPI.Patches.Options;
|
||||
[HarmonyPatch(typeof(GameOptionsMenu))]
|
||||
public static class GameOptionsMenuPatch
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.Update))]
|
||||
public static void UpdatePatch(GameOptionsMenu __instance)
|
||||
{
|
||||
if (GameSettingMenuPatches.CurrentSelectedMod == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var num = 2.1f;
|
||||
var filteredGroups = GameSettingMenuPatches.SelectedMod.OptionGroups.Where(x => x.GroupVisible.Invoke() && x.AdvancedRole is null);
|
||||
|
||||
foreach (var group in filteredGroups)
|
||||
{
|
||||
var filteredOpts = group.Options.Where(x => x.Visible.Invoke()).ToList();
|
||||
if (filteredOpts.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
group.Header.gameObject.SetActive(true);
|
||||
group.Header.transform.localScale = Vector3.one * 0.63f;
|
||||
group.Header.transform.localPosition = new Vector3(-0.903f, num, -2f);
|
||||
|
||||
num -= 0.58f;
|
||||
|
||||
foreach (var opt in group.Options)
|
||||
{
|
||||
var newOpt = opt.OptionBehaviour;
|
||||
|
||||
if (opt.Visible.Invoke() == false)
|
||||
{
|
||||
newOpt.gameObject.SetActive(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!group.AllOptionsHidden)
|
||||
{
|
||||
newOpt.gameObject.SetActive(true);
|
||||
newOpt.transform.localPosition = new Vector3(0.952f, num, -2f);
|
||||
num -= 0.45f;
|
||||
}
|
||||
else
|
||||
{
|
||||
newOpt.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__instance.scrollBar.SetYBoundsMax(-num - 1.65f);
|
||||
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.CreateSettings))]
|
||||
public static bool SettingsPatch(GameOptionsMenu __instance)
|
||||
@@ -24,52 +79,51 @@ public static class GameOptionsMenuPatch
|
||||
|
||||
__instance.MapPicker.gameObject.SetActive(false);
|
||||
|
||||
float num = 2.1f;
|
||||
var filteredGroups = GameSettingMenuPatches.SelectedMod.OptionGroups.Where(x => x.AdvancedRole is null);
|
||||
|
||||
var filteredGroups = GameSettingMenuPatches.SelectedMod.OptionGroups.Where(x => x.GroupVisible.Invoke() && x.AdvancedRole is null);
|
||||
|
||||
foreach (AbstractOptionGroup group in filteredGroups)
|
||||
foreach (var group in filteredGroups)
|
||||
{
|
||||
var filteredOpts = group.Options.Where(x => x.Visible.Invoke()).ToList();
|
||||
if (filteredOpts.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CategoryHeaderMasked 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)
|
||||
{
|
||||
categoryHeaderMasked.Background.color = group.GroupColor;
|
||||
categoryHeaderMasked.Divider.color = group.GroupColor;
|
||||
categoryHeaderMasked.Title.color = group.GroupColor.GetAlternateColor();
|
||||
}
|
||||
categoryHeaderMasked.transform.localScale = Vector3.one * 0.63f;
|
||||
categoryHeaderMasked.transform.localPosition = new Vector3(-0.903f, num, -2f);
|
||||
num -= 0.63f;
|
||||
categoryHeaderMasked.Background.size = new Vector2(categoryHeaderMasked.Background.size.x + 1.5f, categoryHeaderMasked.Background.size.y);
|
||||
categoryHeaderMasked.gameObject.SetActive(false);
|
||||
group.Header = categoryHeaderMasked;
|
||||
|
||||
foreach (var opt in filteredOpts)
|
||||
var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform);
|
||||
newText.text = $"<size=70%>(Click to open)</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));
|
||||
|
||||
foreach (var newOpt in options)
|
||||
{
|
||||
OptionBehaviour newOpt = opt.CreateOption(__instance.checkboxOrigin, __instance.numberOptionOrigin, __instance.stringOptionOrigin, __instance.settingsContainer);
|
||||
newOpt.transform.localPosition = new Vector3(0.952f, num, -2f);
|
||||
newOpt.SetClickMask(__instance.ButtonClickMask);
|
||||
|
||||
SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren<SpriteRenderer>(true);
|
||||
for (int i = 0; i < componentsInChildren.Length; i++)
|
||||
foreach (var renderer in componentsInChildren)
|
||||
{
|
||||
if (group.GroupColor != Color.clear)
|
||||
{
|
||||
componentsInChildren[i].color = group.GroupColor.GetAlternateColor();
|
||||
if (componentsInChildren[i].transform.parent.TryGetComponent<GameOptionButton>(out var btn))
|
||||
renderer.color = group.GroupColor.GetAlternateColor();
|
||||
if (renderer.transform.parent.TryGetComponent<GameOptionButton>(out var btn))
|
||||
{
|
||||
btn.interactableColor = group.GroupColor.GetAlternateColor();
|
||||
btn.interactableHoveredColor = Color.white;
|
||||
}
|
||||
}
|
||||
|
||||
componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20);
|
||||
renderer.material.SetInt(PlayerMaterial.MaskLayer, 20);
|
||||
}
|
||||
|
||||
foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
|
||||
foreach (var textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
|
||||
{
|
||||
if (group.GroupColor != Color.clear)
|
||||
{
|
||||
@@ -80,14 +134,37 @@ public static class GameOptionsMenuPatch
|
||||
textMeshPro.fontMaterial.SetFloat(ShaderID.Stencil, 20);
|
||||
}
|
||||
|
||||
if (newOpt is ToggleOption toggle)
|
||||
{
|
||||
toggle.CheckMark.sprite = MiraAssets.Checkmark.LoadAsset();
|
||||
|
||||
var rend = toggle.CheckMark.transform.parent.FindChild("ActiveSprite").GetComponent<SpriteRenderer>();
|
||||
rend.sprite = MiraAssets.CheckmarkBox.LoadAsset();
|
||||
rend.color = group.GroupColor;
|
||||
}
|
||||
|
||||
__instance.Children.Add(newOpt);
|
||||
|
||||
num -= 0.45f;
|
||||
newOpt.Initialize();
|
||||
newOpt.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
var boxCol = categoryHeaderMasked.gameObject.AddComponent<BoxCollider2D>();
|
||||
boxCol.size = new Vector2(7, 0.7f);
|
||||
boxCol.offset = new Vector2(1.5f, -0.3f);
|
||||
|
||||
var headerBtn = categoryHeaderMasked.gameObject.AddComponent<PassiveButton>();
|
||||
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.SetButtonEnableState(true);
|
||||
}
|
||||
|
||||
__instance.scrollBar.SetYBoundsMax(-num - 1.65f);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -95,30 +172,32 @@ public static class GameOptionsMenuPatch
|
||||
[HarmonyPatch(nameof(GameOptionsMenu.Initialize))]
|
||||
public static bool InitPatch(GameOptionsMenu __instance)
|
||||
{
|
||||
if (__instance.Children == null || __instance.Children.Count == 0)
|
||||
if (__instance.Children != null && __instance.Children.Count != 0)
|
||||
{
|
||||
__instance.MapPicker.gameObject.SetActive(true);
|
||||
__instance.MapPicker.Initialize(20);
|
||||
BaseGameSetting mapNameSetting = GameManager.Instance.GameSettingsList.MapNameSetting;
|
||||
__instance.MapPicker.SetUpFromData(mapNameSetting, 20);
|
||||
__instance.Children = new List<OptionBehaviour>();
|
||||
__instance.Children.Add(__instance.MapPicker);
|
||||
__instance.CreateSettings();
|
||||
__instance.cachedData = GameOptionsManager.Instance.CurrentGameOptions;
|
||||
foreach (var optionBehaviour in __instance.Children)
|
||||
return false;
|
||||
}
|
||||
|
||||
__instance.MapPicker.gameObject.SetActive(true);
|
||||
__instance.MapPicker.Initialize(20);
|
||||
var mapNameSetting = GameManager.Instance.GameSettingsList.MapNameSetting;
|
||||
__instance.MapPicker.SetUpFromData(mapNameSetting, 20);
|
||||
__instance.Children = new Il2CppSystem.Collections.Generic.List<OptionBehaviour>();
|
||||
__instance.Children.Add(__instance.MapPicker);
|
||||
__instance.CreateSettings();
|
||||
__instance.cachedData = GameOptionsManager.Instance.CurrentGameOptions;
|
||||
foreach (var optionBehaviour in __instance.Children)
|
||||
{
|
||||
if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
if (AmongUsClient.Instance && !AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
optionBehaviour.SetAsPlayer();
|
||||
}
|
||||
|
||||
if (optionBehaviour.IsCustom())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
optionBehaviour.OnValueChanged = new System.Action<OptionBehaviour>(__instance.ValueChanged);
|
||||
optionBehaviour.SetAsPlayer();
|
||||
}
|
||||
|
||||
if (optionBehaviour.IsCustom())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
optionBehaviour.OnValueChanged = new System.Action<OptionBehaviour>(__instance.ValueChanged);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -21,14 +21,17 @@ public static class GameSettingMenuPatches
|
||||
|
||||
private static TextMeshPro _text;
|
||||
|
||||
private static Vector3 _roleBtnOgPos;
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(GameSettingMenu.Start))]
|
||||
public static void StartPrefix(GameSettingMenu __instance)
|
||||
{
|
||||
_roleBtnOgPos = __instance.RoleSettingsButton.transform.localPosition;
|
||||
__instance.transform.FindChild("GameSettingsLabel").gameObject.SetActive(false);
|
||||
|
||||
Transform helpThing = __instance.transform.FindChild("What Is This?");
|
||||
GameObject tmpText = Object.Instantiate(helpThing.transform.FindChild("InfoText"), helpThing.parent).gameObject;
|
||||
var helpThing = __instance.transform.FindChild("What Is This?");
|
||||
var tmpText = Object.Instantiate(helpThing.transform.FindChild("InfoText"), helpThing.parent).gameObject;
|
||||
|
||||
tmpText.GetComponent<TextTranslatorTMP>().Destroy();
|
||||
tmpText.name = "SelectedMod";
|
||||
@@ -36,29 +39,31 @@ public static class GameSettingMenuPatches
|
||||
|
||||
_text = tmpText.GetComponent<TextMeshPro>();
|
||||
_text.fontSizeMax = 3.2f;
|
||||
UpdateText(__instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
_text.overflowMode = TextOverflowModes.Overflow;
|
||||
|
||||
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
|
||||
_text.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
GameObject nextButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
|
||||
var nextButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
|
||||
nextButton.transform.localPosition = new Vector3(-2.2663f, 1.5272f, -25f);
|
||||
nextButton.name = "RightArrowButton";
|
||||
nextButton.transform.FindChild("Inactive").gameObject.GetComponent<SpriteRenderer>().sprite = MiraAssets.NextButton.LoadAsset();
|
||||
nextButton.transform.FindChild("Active").gameObject.GetComponent<SpriteRenderer>().sprite = MiraAssets.NextButtonActive.LoadAsset();
|
||||
nextButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().DestroyImmediate();
|
||||
|
||||
PassiveButton passiveButton = nextButton.gameObject.GetComponent<PassiveButton>();
|
||||
var passiveButton = nextButton.gameObject.GetComponent<PassiveButton>();
|
||||
passiveButton.OnClick = new ButtonClickedEvent();
|
||||
passiveButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
if (CurrentSelectedMod != MiraPluginManager.Instance.RegisteredPlugins.Count)
|
||||
{
|
||||
CurrentSelectedMod += 1;
|
||||
UpdateText(__instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
}
|
||||
}));
|
||||
|
||||
GameObject backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject;
|
||||
var backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject;
|
||||
backButton.transform.localPosition = new Vector3(-4.4209f, 1.5272f, -25f);
|
||||
backButton.name = "LeftArrowButton";
|
||||
backButton.gameObject.GetComponent<CloseButtonConsoleBehaviour>().Destroy();
|
||||
@@ -68,12 +73,12 @@ public static class GameSettingMenuPatches
|
||||
if (CurrentSelectedMod != 0)
|
||||
{
|
||||
CurrentSelectedMod -= 1;
|
||||
UpdateText(__instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
UpdateText(__instance, __instance.GameSettingsTab, __instance.RoleSettingsTab);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static void UpdateText(GameOptionsMenu settings, RolesSettingsMenu roles)
|
||||
private static void UpdateText(GameSettingMenu menu, GameOptionsMenu settings, RolesSettingsMenu roles)
|
||||
{
|
||||
if (CurrentSelectedMod == 0)
|
||||
{
|
||||
@@ -82,19 +87,41 @@ public static class GameSettingMenuPatches
|
||||
}
|
||||
else
|
||||
{
|
||||
_text.fontSizeMax = 2f;
|
||||
_text.fontSizeMax = 2.3f;
|
||||
SelectedMod = MiraPluginManager.Instance.RegisteredPlugins.ElementAt(CurrentSelectedMod - 1).Value;
|
||||
if (SelectedMod == null)
|
||||
{
|
||||
CurrentSelectedMod = 0;
|
||||
UpdateText(settings, roles);
|
||||
UpdateText(menu, settings, roles);
|
||||
}
|
||||
|
||||
string name = SelectedMod?.PluginInfo.Metadata.Name;
|
||||
_text.text = name?[..Math.Min(name.Length, 15)];
|
||||
var name = SelectedMod?.MiraPlugin.OptionsTitleText;
|
||||
_text.text = name?[..Math.Min(name.Length, 25)];
|
||||
}
|
||||
|
||||
if (roles?.roleChances != null)
|
||||
menu.RoleSettingsButton.transform.localPosition = _roleBtnOgPos;
|
||||
menu.GameSettingsButton.gameObject.SetActive(true);
|
||||
menu.RoleSettingsButton.gameObject.SetActive(true);
|
||||
|
||||
if (CurrentSelectedMod != 0)
|
||||
{
|
||||
if (SelectedMod?.Options.Where(x=>x.AdvancedRole==null).ToList().Count == 0)
|
||||
{
|
||||
menu.GameSettingsButton.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (SelectedMod?.CustomRoles.Count == 0)
|
||||
{
|
||||
menu.RoleSettingsButton.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (!menu.GameSettingsButton.gameObject.active && menu.RoleSettingsButton.gameObject.active)
|
||||
{
|
||||
menu.RoleSettingsButton.transform.localPosition = menu.GameSettingsButton.transform.localPosition;
|
||||
}
|
||||
}
|
||||
|
||||
if (roles?.roleChances != null && SelectedMod != null && SelectedMod.CustomRoles.Count != 0)
|
||||
{
|
||||
if (roles.advancedSettingChildren is not null)
|
||||
{
|
||||
@@ -126,7 +153,7 @@ public static class GameSettingMenuPatches
|
||||
roles.scrollBar.ScrollToTop();
|
||||
}
|
||||
|
||||
if (settings?.Children != null)
|
||||
if (settings?.Children != null && SelectedMod?.OptionGroups.Count != 0)
|
||||
{
|
||||
foreach (var child in settings.Children)
|
||||
{
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
using HarmonyLib;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
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.ElementAt(CurrentSelectedMod-1).Value;
|
||||
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.Awake))]
|
||||
public static void AwakePatch(LobbyViewSettingsPane __instance)
|
||||
@@ -16,7 +30,7 @@ public static class LobbyViewPanePatches
|
||||
__instance.gameModeText.transform.localPosition = new Vector3(-2.3f, 2.4f, -2);
|
||||
|
||||
// Create the next button
|
||||
GameObject nextButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
|
||||
var nextButton = Object.Instantiate(__instance.BackButton, __instance.BackButton.transform.parent).gameObject;
|
||||
nextButton.GetComponent<BoxCollider2D>().size = new Vector2(0.2f, 0.3f);
|
||||
nextButton.transform.localPosition = new Vector3(-5.4f, 2.4f, -2f);
|
||||
nextButton.transform.localScale = new Vector3(3, 3, 2);
|
||||
@@ -30,24 +44,343 @@ public static class LobbyViewPanePatches
|
||||
hover.transform.localPosition = new Vector3(0, 0f, 0.3f);
|
||||
hover.sprite = MiraAssets.NextButtonActive.LoadAsset();
|
||||
|
||||
PassiveButton passiveButton = nextButton.gameObject.GetComponent<PassiveButton>();
|
||||
var passiveButton = nextButton.gameObject.GetComponent<PassiveButton>();
|
||||
passiveButton.OnClick = new Button.ButtonClickedEvent();
|
||||
passiveButton.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
CurrentSelectedMod += 1;
|
||||
if (CurrentSelectedMod > MiraPluginManager.Instance.RegisteredPlugins.Count)
|
||||
{
|
||||
CurrentSelectedMod = 0;
|
||||
}
|
||||
__instance.RefreshTab();
|
||||
__instance.scrollBar.ScrollToTop();
|
||||
}));
|
||||
|
||||
// Create the back button
|
||||
GameObject backButton = Object.Instantiate(nextButton, __instance.BackButton.transform.parent).gameObject;
|
||||
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
|
||||
= true;
|
||||
|
||||
PassiveButton passiveButton2 = backButton.gameObject.GetComponent<PassiveButton>();
|
||||
var passiveButton2 = backButton.gameObject.GetComponent<PassiveButton>();
|
||||
passiveButton2.OnClick = new Button.ButtonClickedEvent();
|
||||
passiveButton2.OnClick.AddListener((UnityAction)(() =>
|
||||
{
|
||||
CurrentSelectedMod -= 1;
|
||||
if (CurrentSelectedMod < 0)
|
||||
{
|
||||
CurrentSelectedMod = MiraPluginManager.Instance.RegisteredPlugins.Count;
|
||||
}
|
||||
__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;
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(LobbyViewSettingsPane.DrawRolesTab))]
|
||||
public static bool DrawRolesTabPatch(LobbyViewSettingsPane __instance)
|
||||
{
|
||||
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);
|
||||
categoryHeaderMasked.SetHeader(StringNames.Name, 61);
|
||||
categoryHeaderMasked.Title.text = group.GroupName;
|
||||
categoryHeaderMasked.transform.localScale = Vector3.one;
|
||||
categoryHeaderMasked.transform.localPosition = new Vector3(-9.77f, num, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderMasked.gameObject);
|
||||
num -= 0.85f;
|
||||
|
||||
var i = 0;
|
||||
|
||||
foreach (var option in group.Options)
|
||||
{
|
||||
if (!option.Visible.Invoke())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var viewSettingsInfoPanel = Object.Instantiate(instance.infoPanelOrigin, instance.settingsContainer, true);
|
||||
viewSettingsInfoPanel.transform.localScale = Vector3.one;
|
||||
float num2;
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
num2 = -8.95f;
|
||||
if (i > 0)
|
||||
{
|
||||
num -= 0.59f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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));
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
private static void DrawRolesTab(LobbyViewSettingsPane instance)
|
||||
{
|
||||
if (SelectedMod == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var num = 0.95f;
|
||||
var num2 = -6.53f;
|
||||
var categoryHeaderMasked =
|
||||
Object.Instantiate(instance.categoryHeaderOrigin, instance.settingsContainer, true);
|
||||
categoryHeaderMasked.SetHeader(StringNames.RoleQuotaLabel, 61);
|
||||
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();
|
||||
|
||||
if (filteredRoles.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var categoryHeaderRoleVariant =
|
||||
Object.Instantiate(instance.categoryHeaderRoleOrigin, instance.settingsContainer, true);
|
||||
|
||||
switch (team)
|
||||
{
|
||||
case ModdedRoleTeams.Crewmate:
|
||||
categoryHeaderRoleVariant.SetHeader(StringNames.CrewmateRolesHeader, 61);
|
||||
break;
|
||||
case ModdedRoleTeams.Impostor:
|
||||
categoryHeaderRoleVariant.SetHeader(StringNames.ImpostorRolesHeader, 61);
|
||||
break;
|
||||
case ModdedRoleTeams.Neutral:
|
||||
default:
|
||||
categoryHeaderRoleVariant.SetHeader(StringNames.CrewmateRolesHeader, 61);
|
||||
categoryHeaderRoleVariant.Title.text = team + " Roles";
|
||||
categoryHeaderRoleVariant.Background.color = Color.gray;
|
||||
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);
|
||||
num -= 0.696f;
|
||||
|
||||
foreach (var customRole in filteredRoles)
|
||||
{
|
||||
var roleBehaviour = customRole as RoleBehaviour;
|
||||
if (roleBehaviour == null)
|
||||
{
|
||||
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);
|
||||
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 =
|
||||
customRole.RoleColor.GetAlternateColor();
|
||||
instance.settingsInfo.Add(viewSettingsInfoPanelRoleVariant.gameObject);
|
||||
num -= 0.664f;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
var categoryHeaderMasked2 =
|
||||
Object.Instantiate(instance.categoryHeaderOrigin, instance.settingsContainer, true);
|
||||
categoryHeaderMasked2.SetHeader(StringNames.RoleSettingsLabel, 61);
|
||||
categoryHeaderMasked2.transform.localScale = Vector3.one;
|
||||
categoryHeaderMasked2.transform.localPosition = new Vector3(-9.77f, num, -2f);
|
||||
instance.settingsInfo.Add(categoryHeaderMasked2.gameObject);
|
||||
num -= 1.7f;
|
||||
var num3 = 0f;
|
||||
for (var k = 0; k < list.Count; k++)
|
||||
{
|
||||
float num4;
|
||||
if (k % 2 == 0)
|
||||
{
|
||||
num4 = -5.8f;
|
||||
if (k > 0)
|
||||
{
|
||||
num -= num3 + 0.59f;
|
||||
num3 = 0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
num4 = 0.14999962f;
|
||||
}
|
||||
|
||||
var advancedRoleViewPanel =
|
||||
Object.Instantiate(instance.advancedRolePanelOrigin, instance.settingsContainer, true);
|
||||
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;
|
||||
}
|
||||
|
||||
instance.settingsInfo.Add(advancedRoleViewPanel.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
instance.scrollBar.SetYBoundsMax(-num);
|
||||
}
|
||||
|
||||
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 (role == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
@@ -41,7 +42,7 @@ public static class RoleSettingMenuPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
float num = 0.522f;
|
||||
var num = 0.522f;
|
||||
|
||||
__instance.AllButton.transform.parent.gameObject.SetActive(false);
|
||||
__instance.AllButton.gameObject.SetActive(false);
|
||||
@@ -51,7 +52,7 @@ public static class RoleSettingMenuPatches
|
||||
hitbox.localPosition = new Vector3(0.3297f, -.6333f, 4f);
|
||||
hitbox.localScale = new Vector3(1, 1.2f, 1);
|
||||
|
||||
int num3 = 0;
|
||||
var num3 = 0;
|
||||
|
||||
var crewRoles = GameSettingMenuPatches.SelectedMod.CustomRoles.Values
|
||||
.OfType<ICustomRole>()
|
||||
@@ -61,7 +62,7 @@ public static class RoleSettingMenuPatches
|
||||
if (crewRoles.Count > 0)
|
||||
{
|
||||
|
||||
CategoryHeaderEditRole 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;
|
||||
@@ -81,7 +82,7 @@ public static class RoleSettingMenuPatches
|
||||
if (impRoles.Count > 0)
|
||||
{
|
||||
num -= 0.4f;
|
||||
CategoryHeaderEditRole categoryHeaderEditRole2 = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin,
|
||||
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);
|
||||
@@ -103,10 +104,12 @@ public static class RoleSettingMenuPatches
|
||||
if (neutRoles.Count > 0)
|
||||
{
|
||||
num -= 0.4f;
|
||||
CategoryHeaderEditRole categoryHeaderEditRole3 = Object.Instantiate(__instance.categoryHeaderEditRoleOrigin,
|
||||
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;
|
||||
categoryHeaderEditRole3.Title.color = Color.white;
|
||||
categoryHeaderEditRole3.transform.localPosition = new Vector3(4.986f, num, -2f);
|
||||
num -= 0.522f;
|
||||
|
||||
@@ -124,12 +127,7 @@ public static class RoleSettingMenuPatches
|
||||
[HarmonyPatch(nameof(RolesSettingsMenu.Update))]
|
||||
public static bool UpdatePatch()
|
||||
{
|
||||
if (GameSettingMenuPatches.CurrentSelectedMod == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return GameSettingMenuPatches.CurrentSelectedMod == 0;
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
@@ -167,7 +165,7 @@ public static class RoleSettingMenuPatches
|
||||
|
||||
if (AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
Rpc<SyncRoleOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [role.GetNetData(roleSetting.Role)], true);
|
||||
Rpc<SyncRoleOptionsRpc>.Instance.Send(PlayerControl.LocalPlayer, [role.GetNetData()], true);
|
||||
}
|
||||
GameOptionsManager.Instance.GameHostOptions = GameOptionsManager.Instance.CurrentGameOptions;
|
||||
}
|
||||
@@ -181,7 +179,7 @@ public static class RoleSettingMenuPatches
|
||||
|
||||
__instance.advancedSettingChildren.Clear();
|
||||
|
||||
float num = -0.872f;
|
||||
var num = -0.872f;
|
||||
|
||||
var filteredOptions = GameSettingMenuPatches.SelectedMod.Options.Where(x => x.AdvancedRole == role.GetType());
|
||||
|
||||
@@ -192,16 +190,16 @@ public static class RoleSettingMenuPatches
|
||||
continue;
|
||||
}
|
||||
|
||||
OptionBehaviour 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);
|
||||
|
||||
SpriteRenderer[] componentsInChildren = newOpt.GetComponentsInChildren<SpriteRenderer>(true);
|
||||
for (int i = 0; i < componentsInChildren.Length; i++)
|
||||
foreach (var renderer in componentsInChildren)
|
||||
{
|
||||
componentsInChildren[i].material.SetInt(PlayerMaterial.MaskLayer, 20);
|
||||
renderer.material.SetInt(PlayerMaterial.MaskLayer, 20);
|
||||
}
|
||||
foreach (TextMeshPro textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
|
||||
foreach (var textMeshPro in newOpt.GetComponentsInChildren<TextMeshPro>(true))
|
||||
{
|
||||
textMeshPro.fontMaterial.SetFloat(ShaderID.StencilComp, 3f);
|
||||
textMeshPro.fontMaterial.SetFloat(ShaderID.Stencil, 20);
|
||||
@@ -219,9 +217,9 @@ public static class RoleSettingMenuPatches
|
||||
}
|
||||
private static void ChangeTab(RoleBehaviour role, RolesSettingsMenu __instance)
|
||||
{
|
||||
ICustomRole customRole = role as ICustomRole;
|
||||
var customRole = role as ICustomRole;
|
||||
__instance.roleDescriptionText.text = customRole.RoleLongDescription;
|
||||
__instance.roleTitleText.text = DestroyableSingleton<TranslationController>.Instance.GetString(role.StringName, Array.Empty<Il2CppSystem.Object>());
|
||||
__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.roleScreenshot.drawMode = SpriteDrawMode.Sliced;
|
||||
__instance.roleHeaderSprite.color = customRole.RoleColor;
|
||||
@@ -244,8 +242,8 @@ public static class RoleSettingMenuPatches
|
||||
|
||||
public static void CreateQuotaOption(RolesSettingsMenu __instance, RoleBehaviour role, ref float yPos, int index)
|
||||
{
|
||||
ICustomRole customRole = role as ICustomRole;
|
||||
RoleOptionSetting roleOptionSetting = Object.Instantiate(__instance.roleOptionSettingOrigin, Vector3.zero, Quaternion.identity, __instance.RoleChancesSettings.transform);
|
||||
var customRole = role as ICustomRole;
|
||||
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;
|
||||
@@ -257,16 +255,16 @@ public static class RoleSettingMenuPatches
|
||||
roleOptionSetting.titleText.color = customRole.RoleColor.GetAlternateColor();
|
||||
roleOptionSetting.titleText.horizontalAlignment = HorizontalAlignmentOptions.Left;
|
||||
|
||||
PassiveButton newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
|
||||
var newButton = Object.Instantiate(roleOptionSetting.buttons[0], roleOptionSetting.transform);
|
||||
newButton.name = "ConfigButton";
|
||||
newButton.transform.localPosition = new Vector3(0.4473f, -0.3f, -2f);
|
||||
newButton.transform.FindChild("Text_TMP").gameObject.DestroyImmediate();
|
||||
newButton.activeSprites.Destroy();
|
||||
|
||||
SpriteRenderer btnRend = newButton.transform.FindChild("ButtonSprite").GetComponent<SpriteRenderer>();
|
||||
var btnRend = newButton.transform.FindChild("ButtonSprite").GetComponent<SpriteRenderer>();
|
||||
btnRend.sprite = MiraAssets.Cog.LoadAsset();
|
||||
|
||||
GameOptionButton passiveButton = newButton.GetComponent<GameOptionButton>();
|
||||
var passiveButton = newButton.GetComponent<GameOptionButton>();
|
||||
passiveButton.OnClick = new ButtonClickedEvent();
|
||||
passiveButton.interactableColor = btnRend.color = customRole.RoleColor.GetAlternateColor();
|
||||
passiveButton.interactableHoveredColor = Color.white;
|
||||
|
||||
@@ -1,29 +1,47 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Modifiers;
|
||||
using Reactor.Utilities.Extensions;
|
||||
|
||||
namespace MiraAPI.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(PlayerControl))]
|
||||
public static class PlayerControlPatches
|
||||
{
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(PlayerControl.Start))]
|
||||
public static void PlayerControlStartPostfix(PlayerControl __instance)
|
||||
{
|
||||
if (__instance.gameObject.TryGetComponent<ModifierComponent>(out var comp))
|
||||
{
|
||||
comp.DestroyImmediate();
|
||||
}
|
||||
|
||||
__instance.gameObject.AddComponent<ModifierComponent>();
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(PlayerControl.FixedUpdate))]
|
||||
public static void PlayerControlFixedUpdatePostfix(PlayerControl __instance)
|
||||
{
|
||||
if (!__instance.AmOwner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (__instance.Data?.Role is ICustomRole customRole)
|
||||
{
|
||||
customRole.PlayerControlFixedUpdate(__instance);
|
||||
}
|
||||
|
||||
if (!__instance.AmOwner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var button in CustomButtonManager.CustomButtons)
|
||||
{
|
||||
if (__instance.Data?.Role != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!button.Enabled(__instance.Data?.Role))
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Colors;
|
||||
using MiraAPI.Roles;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
@@ -22,5 +23,6 @@ public static class GameStartupPatch
|
||||
|
||||
_runOnce = true;
|
||||
CustomRoleManager.RegisterInRoleManager();
|
||||
PaletteManager.RegisterAllColors();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Roles;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Patches.Roles;
|
||||
@@ -8,6 +10,18 @@ namespace MiraAPI.Patches.Roles;
|
||||
[HarmonyPatch(typeof(RoleManager))]
|
||||
public static class RoleManagerPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(nameof(RoleManager.SelectRoles))]
|
||||
public static void ModifierSelectionPatches(RoleManager __instance)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ModifierManager.AssignModifiers(PlayerControl.AllPlayerControls.ToArray().Where(plr => !plr.Data.IsDead && !plr.Data.Disconnected).ToList());
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(nameof(RoleManager.AssignRoleOnDeath))]
|
||||
public static bool AssignRoleOnDeath(RoleManager __instance, [HarmonyArgument(0)] PlayerControl plr)
|
||||
@@ -17,16 +31,18 @@ public static class RoleManagerPatches
|
||||
return false;
|
||||
}
|
||||
|
||||
if (plr.Data.Role is ICustomRole role)
|
||||
if (plr.Data.Role is not ICustomRole role)
|
||||
{
|
||||
Debug.Log(role.RoleName);
|
||||
if (role.GhostRole != RoleTypes.CrewmateGhost && role.GhostRole != RoleTypes.ImpostorGhost)
|
||||
{
|
||||
plr.RpcSetRole(role.GhostRole);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (role.GhostRole is RoleTypes.CrewmateGhost or RoleTypes.ImpostorGhost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
plr.RpcSetRole(role.GhostRole);
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -14,19 +14,20 @@ 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))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (customRole.HideSettings)
|
||||
{
|
||||
if (customRole.HideSettings)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
customRole.ParentMod.PluginConfig.TryGetEntry<int>(customRole.ChanceConfigDefinition, out var entry);
|
||||
__result = entry.Value;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
customRole.ParentMod.PluginConfig.TryGetEntry<int>(customRole.ChanceConfigDefinition, out var entry);
|
||||
__result = entry.Value;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -36,18 +37,18 @@ 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))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (customRole.HideSettings)
|
||||
{
|
||||
if (customRole.HideSettings)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
customRole.ParentMod.PluginConfig.TryGetEntry<int>(customRole.NumConfigDefinition, out var entry);
|
||||
__result = entry.Value;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
customRole.ParentMod.PluginConfig.TryGetEntry<int>(customRole.NumConfigDefinition, out var entry);
|
||||
__result = entry.Value;
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@ using Il2CppSystem.Collections.Generic;
|
||||
using Il2CppSystem.Text;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
@@ -14,23 +16,59 @@ namespace MiraAPI.Patches.Roles;
|
||||
[HarmonyPatch]
|
||||
public static class TaskAdderPatch
|
||||
{
|
||||
public static TaskFolder RolesFolder;
|
||||
public static System.Collections.Generic.Dictionary<string, string> ModsFolders = new System.Collections.Generic.Dictionary<string, string>();
|
||||
private static TaskFolder _rolesFolder;
|
||||
private static readonly System.Collections.Generic.Dictionary<string, string> ModsFolders = new();
|
||||
private static Scroller _scroller;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(TaskAdderGame), nameof(TaskAdderGame.Begin))]
|
||||
public static void AddRolesFolder(TaskAdderGame __instance)
|
||||
{
|
||||
RolesFolder = Object.Instantiate(__instance.RootFolderPrefab, __instance.transform);
|
||||
RolesFolder.gameObject.SetActive(false);
|
||||
RolesFolder.FolderName = "Roles";
|
||||
RolesFolder.name = "RolesFolder";
|
||||
GameObject inner = new("Inner");
|
||||
inner.transform.SetParent(__instance.TaskParent.transform, false);
|
||||
|
||||
_scroller = __instance.TaskParent.gameObject.AddComponent<Scroller>();
|
||||
_scroller.allowX = false;
|
||||
_scroller.allowY = true;
|
||||
_scroller.Inner = inner.transform;
|
||||
|
||||
GameObject hitbox = new("Hitbox")
|
||||
{
|
||||
layer = 5
|
||||
};
|
||||
hitbox.transform.SetParent(__instance.TaskParent.transform, false);
|
||||
hitbox.transform.localScale = new Vector3(7.5f, 6.5f, 1);
|
||||
hitbox.transform.localPosition = new Vector3(2.8f, -2.2f, 0);
|
||||
|
||||
SpriteMask mask = hitbox.AddComponent<SpriteMask>();
|
||||
mask.sprite = MiraAssets.NextButton.LoadAsset();
|
||||
mask.alphaCutoff = 0.0f;
|
||||
|
||||
BoxCollider2D collider = hitbox.AddComponent<BoxCollider2D>();
|
||||
collider.size = new Vector2(1f, 1f);
|
||||
collider.enabled = true;
|
||||
|
||||
_scroller.ClickMask = collider;
|
||||
|
||||
__instance.TaskPrefab.GetComponent<PassiveButton>().ClickMask = collider;
|
||||
__instance.RoleButton.GetComponent<PassiveButton>().ClickMask = collider;
|
||||
__instance.RootFolderPrefab.GetComponent<PassiveButton>().ClickMask = collider;
|
||||
__instance.RootFolderPrefab.gameObject.SetActive(false);
|
||||
|
||||
__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";
|
||||
|
||||
foreach (var plugin in MiraPluginManager.Instance.RegisteredPlugins.Values)
|
||||
{
|
||||
var newFolder = Object.Instantiate(__instance.RootFolderPrefab, __instance.transform);
|
||||
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))
|
||||
{
|
||||
@@ -38,7 +76,7 @@ public static class TaskAdderPatch
|
||||
}
|
||||
}
|
||||
|
||||
__instance.Root.SubFolders.Add(RolesFolder);
|
||||
__instance.Root.SubFolders.Add(_rolesFolder);
|
||||
|
||||
__instance.GoToRoot();
|
||||
}
|
||||
@@ -47,16 +85,30 @@ public static class TaskAdderPatch
|
||||
[HarmonyPatch(typeof(TaskAddButton), "Role", MethodType.Setter)]
|
||||
public static void RoleGetterPatch(TaskAddButton __instance)
|
||||
{
|
||||
if (__instance.role is ICustomRole lpRole)
|
||||
if (__instance.role is ICustomRole { IsNeutral: true })
|
||||
{
|
||||
if (lpRole.IsNeutral)
|
||||
{
|
||||
__instance.FileImage.color = Color.gray;
|
||||
}
|
||||
__instance.FileImage.color = Color.gray;
|
||||
}
|
||||
|
||||
__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)
|
||||
{
|
||||
item.transform.SetParent(instance.TaskParent);
|
||||
item.transform.localPosition = new Vector3(xCursor, yCursor, 0f);
|
||||
item.transform.localScale = Vector3.one;
|
||||
maxHeight = Mathf.Max(maxHeight, item.Text.bounds.size.y + 1.3f);
|
||||
xCursor += instance.fileWidth;
|
||||
if (xCursor > instance.lineWidth)
|
||||
{
|
||||
xCursor = 0f;
|
||||
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]
|
||||
@@ -89,7 +141,7 @@ public static class TaskAdderPatch
|
||||
|
||||
folderTransform.localPosition = new Vector3(num, num2, 0f);
|
||||
folderTransform.localScale = Vector3.one;
|
||||
num3 = Mathf.Max(num3, taskFolder2.Text.bounds.size.y + 1.1f);
|
||||
num3 = Mathf.Max(num3, taskFolder2.Text.bounds.size.y + 1.3f);
|
||||
num += __instance.folderWidth;
|
||||
if (num > __instance.lineWidth)
|
||||
{
|
||||
@@ -137,7 +189,7 @@ public static class TaskAdderPatch
|
||||
{
|
||||
taskAddButton.Text.text = DestroyableSingleton<TranslationController>.Instance.GetString(taskAddButton.MyTask.TaskType);
|
||||
}
|
||||
__instance.AddFileAsChild(taskFolder, taskAddButton, ref num, ref num2, ref num3);
|
||||
__instance.AddFileAsChildCustom(taskFolder, taskAddButton, ref num, ref num2, ref num3);
|
||||
if (taskAddButton != null && taskAddButton.Button != null)
|
||||
{
|
||||
ControllerManager.Instance.AddSelectableUiElement(taskAddButton.Button);
|
||||
@@ -155,15 +207,15 @@ public static class TaskAdderPatch
|
||||
}
|
||||
if (taskFolder.FolderName == "Roles")
|
||||
{
|
||||
for (int m = 0; m < DestroyableSingleton<RoleManager>.Instance.AllRoles.Length; m++)
|
||||
for (var m = 0; m < DestroyableSingleton<RoleManager>.Instance.AllRoles.Length; m++)
|
||||
{
|
||||
RoleBehaviour roleBehaviour = DestroyableSingleton<RoleManager>.Instance.AllRoles[m];
|
||||
var roleBehaviour = DestroyableSingleton<RoleManager>.Instance.AllRoles[m];
|
||||
if (roleBehaviour.Role != RoleTypes.ImpostorGhost && roleBehaviour.Role != RoleTypes.CrewmateGhost && !CustomRoleManager.CustomRoles.ContainsKey((ushort)roleBehaviour.Role))
|
||||
{
|
||||
TaskAddButton taskAddButton2 = Object.Instantiate(__instance.RoleButton);
|
||||
var taskAddButton2 = Object.Instantiate(__instance.RoleButton);
|
||||
taskAddButton2.SafePositionWorld = __instance.SafePositionWorld;
|
||||
taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe";
|
||||
__instance.AddFileAsChild(RolesFolder, taskAddButton2, ref num, ref num2, ref num3);
|
||||
__instance.AddFileAsChildCustom(_rolesFolder, taskAddButton2, ref num, ref num2, ref num3);
|
||||
taskAddButton2.Role = roleBehaviour;
|
||||
if (taskAddButton2 != null && taskAddButton2.Button != null)
|
||||
{
|
||||
@@ -183,9 +235,9 @@ public static class TaskAdderPatch
|
||||
}
|
||||
}
|
||||
|
||||
if (ModsFolders.TryGetValue(taskFolder.FolderName, out string guid))
|
||||
if (ModsFolders.TryGetValue(taskFolder.FolderName, out var guid))
|
||||
{
|
||||
MiraPluginInfo plugin = MiraPluginManager.Instance.GetPluginByGuid(guid);
|
||||
var plugin = MiraPluginManager.Instance.GetPluginByGuid(guid);
|
||||
for (var m = 0; m < plugin.CustomRoles.Count; m++)
|
||||
{
|
||||
var roleBehaviour = plugin.CustomRoles.ElementAt(m).Value;
|
||||
@@ -194,7 +246,7 @@ public static class TaskAdderPatch
|
||||
var taskAddButton2 = Object.Instantiate(__instance.RoleButton);
|
||||
taskAddButton2.SafePositionWorld = __instance.SafePositionWorld;
|
||||
taskAddButton2.Text.text = "Be_" + roleBehaviour.NiceName + ".exe";
|
||||
__instance.AddFileAsChild(RolesFolder, taskAddButton2, ref num, ref num2, ref num3);
|
||||
__instance.AddFileAsChildCustom(_rolesFolder, taskAddButton2, ref num, ref num2, ref num3);
|
||||
taskAddButton2.Role = roleBehaviour;
|
||||
if (taskAddButton2 != null && taskAddButton2.Button != null)
|
||||
{
|
||||
@@ -213,6 +265,19 @@ public static class TaskAdderPatch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var chip in __instance.ActiveItems)
|
||||
{
|
||||
chip.GetComponentInChildren<TextMeshPro>().EnableMasking();
|
||||
chip.GetComponent<SpriteRenderer>().maskInteraction = SpriteMaskInteraction.VisibleInsideMask;
|
||||
}
|
||||
|
||||
if (_scroller)
|
||||
{
|
||||
_scroller.CalculateAndSetYBounds(__instance.ActiveItems.Count, 6, 3, 1f);
|
||||
_scroller.SetYBoundsMin(0.0f);
|
||||
_scroller.ScrollToTop();
|
||||
}
|
||||
if (__instance.Hierarchy.Count == 1)
|
||||
{
|
||||
ControllerManager.Instance.SetBackButton(__instance.BackButton);
|
||||
|
||||
@@ -4,5 +4,6 @@ namespace MiraAPI.PluginLoading;
|
||||
|
||||
public interface IMiraPlugin
|
||||
{
|
||||
string OptionsTitleText { get; }
|
||||
public ConfigFile GetConfigFile();
|
||||
}
|
||||
@@ -6,16 +6,16 @@ using System.Collections.Generic;
|
||||
|
||||
namespace MiraAPI.PluginLoading;
|
||||
|
||||
public class MiraPluginInfo(string id, IMiraPlugin miraPlugin, PluginInfo info)
|
||||
public class MiraPluginInfo(IMiraPlugin miraPlugin, PluginInfo info)
|
||||
{
|
||||
public string PluginId { get; } = id;
|
||||
public IMiraPlugin MiraPlugin { get; } = miraPlugin;
|
||||
public PluginInfo PluginInfo { get; } = info;
|
||||
public ConfigFile PluginConfig { get; } = miraPlugin.GetConfigFile();
|
||||
public string PluginId { get; set; } = info.Metadata.GUID;
|
||||
public IMiraPlugin MiraPlugin { get; set; } = miraPlugin;
|
||||
public PluginInfo PluginInfo { get; set; } = info;
|
||||
public ConfigFile PluginConfig { get; set; } = miraPlugin.GetConfigFile();
|
||||
|
||||
public readonly List<AbstractOptionGroup> OptionGroups = [];
|
||||
public readonly List<IModdedOption> Options = [];
|
||||
|
||||
public readonly Dictionary<ushort, RoleBehaviour> CustomRoles = [];
|
||||
public readonly Dictionary<int, CustomGameMode> GameModes = [];
|
||||
internal readonly Dictionary<int, CustomGameMode> GameModes = [];
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
using BepInEx;
|
||||
using BepInEx.Unity.IL2CPP;
|
||||
using Il2CppInterop.Runtime.Injection;
|
||||
using BepInEx.Unity.IL2CPP;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.GameOptions.Attributes;
|
||||
using MiraAPI.Hud;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Roles;
|
||||
using MiraAPI.Utilities.Colors;
|
||||
using Reactor.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MiraAPI.Colors;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Networking;
|
||||
|
||||
namespace MiraAPI.PluginLoading;
|
||||
|
||||
@@ -29,26 +30,26 @@ internal class MiraPluginManager
|
||||
internal void Initialize()
|
||||
{
|
||||
Instance = this;
|
||||
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) =>
|
||||
IL2CPPChainloader.Instance.PluginLoad += (pluginInfo, assembly, plugin) =>
|
||||
{
|
||||
if (!plugin.GetType().GetInterfaces().Contains(typeof(IMiraPlugin)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var id = MetadataHelper.GetMetadata(plugin.GetType()).GUID;
|
||||
var info = new MiraPluginInfo(id, plugin as IMiraPlugin, IL2CPPChainloader.Instance.Plugins[id]);
|
||||
var info = new MiraPluginInfo(plugin as IMiraPlugin, pluginInfo);
|
||||
|
||||
RegisterModifierAttribute(assembly);
|
||||
RegisterAllOptions(assembly, info);
|
||||
|
||||
|
||||
RegisterRoleAttribute(assembly, info);
|
||||
RegisterButtonAttribute(assembly);
|
||||
|
||||
RegisterAllColors(assembly);
|
||||
RegisterColorClasses(assembly);
|
||||
|
||||
RegisteredPlugins.Add(assembly, info);
|
||||
|
||||
Logger<MiraApiPlugin>.Info($"Registering mod {id} with Mira API.");
|
||||
Logger<MiraApiPlugin>.Info($"Registering mod {pluginInfo.Metadata.GUID} with Mira API.");
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,7 +68,7 @@ internal class MiraPluginManager
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
foreach (var property in type.GetProperties())
|
||||
{
|
||||
if (property.PropertyType.IsAssignableTo(typeof(IModdedOption)))
|
||||
@@ -75,7 +76,7 @@ internal class MiraPluginManager
|
||||
ModdedOptionsManager.RegisterPropertyOption(type, property, pluginInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var attribute = property.GetCustomAttribute<ModdedOptionAttribute>();
|
||||
if (attribute == null)
|
||||
{
|
||||
@@ -89,6 +90,7 @@ internal class MiraPluginManager
|
||||
|
||||
private static void RegisterRoleAttribute(Assembly assembly, MiraPluginInfo pluginInfo)
|
||||
{
|
||||
List<Type> roles = [];
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
var attribute = type.GetCustomAttribute<RegisterCustomRoleAttribute>();
|
||||
@@ -96,48 +98,72 @@ internal class MiraPluginManager
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ClassInjector.RegisterTypeInIl2Cpp(type);
|
||||
|
||||
var role = CustomRoleManager.RegisterRole(type, attribute.RoleId, pluginInfo);
|
||||
|
||||
try
|
||||
|
||||
if (!(typeof(RoleBehaviour).IsAssignableFrom(type) && typeof(ICustomRole).IsAssignableFrom(type)))
|
||||
{
|
||||
pluginInfo.CustomRoles.Add((ushort)role.Role, role);
|
||||
Logger<MiraApiPlugin>.Error($"{type.Name} does not inherit from RoleBehaviour or ICustomRole.");
|
||||
continue;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
if (!ModList.GetById(pluginInfo.PluginId).IsRequiredOnAllClients)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error("Failed to register role: " + type.Name);
|
||||
|
||||
foreach (var (k, v) in pluginInfo.CustomRoles)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"{k}: {v}");
|
||||
}
|
||||
|
||||
Logger<MiraApiPlugin>.Error(e);
|
||||
Logger<MiraApiPlugin>.Error("Custom roles are only supported on all clients.");
|
||||
return;
|
||||
}
|
||||
|
||||
roles.Add(type);
|
||||
}
|
||||
|
||||
CustomRoleManager.RegisterRoleTypes(roles, pluginInfo);
|
||||
}
|
||||
|
||||
private static void RegisterAllColors(Assembly assembly)
|
||||
private static void RegisterColorClasses(Assembly assembly)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
foreach (FieldInfo field in type.GetFields())
|
||||
if (type.GetCustomAttribute<RegisterCustomColorsAttribute>() == null)
|
||||
{
|
||||
foreach (var attribute in field.GetCustomAttributes())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!type.IsStatic())
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Color class {type.Name} must be static.");
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var property in type.GetProperties())
|
||||
{
|
||||
if (property.PropertyType != typeof(CustomColor))
|
||||
{
|
||||
if (attribute.GetType().BaseType == typeof(RegisterColorAttribute))
|
||||
{
|
||||
/* Palette.PlayerColors = Palette.PlayerColors.ToArray().AddToArray();
|
||||
Palette.ShadowColors = Palette.ShadowColors.ToArray().AddToArray();
|
||||
Palette.ColorNames = Palette.ColorNames.ToArray().AddToArray();*/
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var color = (CustomColor)property.GetValue(null);
|
||||
if (color == null)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Color property {property.Name} in {type.Name} is null.");
|
||||
continue;
|
||||
}
|
||||
|
||||
PaletteManager.CustomColors.Add(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterModifierAttribute(Assembly assembly)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
var attribute = type.GetCustomAttribute<RegisterModifierAttribute>();
|
||||
if (attribute != null)
|
||||
{
|
||||
ModifierManager.RegisterModifier(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void RegisterButtonAttribute(Assembly assembly)
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 347 B |
Binary file not shown.
|
After Width: | Height: | Size: 370 B |
@@ -9,7 +9,8 @@ using Reactor.Utilities.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using Il2CppInterop.Runtime.Injection;
|
||||
using MiraAPI.Utilities;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
@@ -19,24 +20,40 @@ namespace MiraAPI.Roles;
|
||||
public static class CustomRoleManager
|
||||
{
|
||||
public static readonly Dictionary<ushort, RoleBehaviour> CustomRoles = new();
|
||||
|
||||
private static int GetNextRoleId(ushort requestedId)
|
||||
|
||||
public static readonly Dictionary<Type, ushort> RoleIds = new();
|
||||
|
||||
private static ushort _roleId = 100;
|
||||
|
||||
private static ushort GetNextRoleId()
|
||||
{
|
||||
while (CustomRoles.ContainsKey(requestedId) || requestedId <= Enum.GetNames<RoleTypes>().Length)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error(requestedId + ", " + (requestedId+1));
|
||||
requestedId++;
|
||||
}
|
||||
|
||||
return requestedId;
|
||||
return _roleId++;
|
||||
}
|
||||
|
||||
public static void RegisterInRoleManager()
|
||||
internal static void RegisterInRoleManager()
|
||||
{
|
||||
RoleManager.Instance.AllRoles = RoleManager.Instance.AllRoles.Concat(CustomRoles.Values).ToArray();
|
||||
}
|
||||
|
||||
internal static RoleBehaviour RegisterRole(Type roleType, ushort roleId, MiraPluginInfo parentMod)
|
||||
public static void RegisterRoleTypes(List<Type> roles, MiraPluginInfo pluginInfo)
|
||||
{
|
||||
roles.ForEach(x=>RoleIds.Add(x, GetNextRoleId()));
|
||||
|
||||
foreach (var roleType in roles)
|
||||
{
|
||||
ClassInjector.RegisterTypeInIl2Cpp(roleType);
|
||||
|
||||
var role = RegisterRole(roleType, pluginInfo);
|
||||
if (role is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pluginInfo.CustomRoles.Add((ushort)role.Role, role);
|
||||
}
|
||||
}
|
||||
|
||||
private static RoleBehaviour RegisterRole(Type roleType, MiraPluginInfo parentMod)
|
||||
{
|
||||
if (!(typeof(RoleBehaviour).IsAssignableFrom(roleType) && typeof(ICustomRole).IsAssignableFrom(roleType)))
|
||||
{
|
||||
@@ -51,8 +68,10 @@ public static class CustomRoleManager
|
||||
roleBehaviour.gameObject.Destroy();
|
||||
return null;
|
||||
}
|
||||
|
||||
var roleId = RoleIds[roleType];
|
||||
|
||||
roleBehaviour.Role = (RoleTypes)GetNextRoleId(roleId);
|
||||
roleBehaviour.Role = (RoleTypes)roleId;
|
||||
roleBehaviour.TeamType = customRole.Team == ModdedRoleTeams.Neutral ? RoleTeamTypes.Crewmate : (RoleTeamTypes)customRole.Team;
|
||||
roleBehaviour.NameColor = customRole.RoleColor;
|
||||
roleBehaviour.StringName = CustomStringName.CreateAndRegister(customRole.RoleName);
|
||||
@@ -82,7 +101,7 @@ public static class CustomRoleManager
|
||||
var config = parentMod.PluginConfig;
|
||||
config.Bind(customRole.NumConfigDefinition, 1);
|
||||
config.Bind(customRole.ChanceConfigDefinition, 100);
|
||||
|
||||
|
||||
return roleBehaviour;
|
||||
}
|
||||
|
||||
@@ -94,7 +113,7 @@ public static class CustomRoleManager
|
||||
public static bool GetCustomRoleBehaviour(RoleTypes roleType, out ICustomRole result)
|
||||
{
|
||||
CustomRoles.TryGetValue((ushort)roleType, out var temp);
|
||||
if (temp != null)
|
||||
if (temp)
|
||||
{
|
||||
result = temp as ICustomRole;
|
||||
return true;
|
||||
@@ -104,7 +123,7 @@ public static class CustomRoleManager
|
||||
return false;
|
||||
}
|
||||
|
||||
public static TaskPanelBehaviour CreateRoleTab(ICustomRole role)
|
||||
internal static TaskPanelBehaviour CreateRoleTab(ICustomRole role)
|
||||
{
|
||||
var ogPanel = HudManager.Instance.TaskStuff.transform.FindChild("TaskPanel").gameObject.GetComponent<TaskPanelBehaviour>();
|
||||
var clonePanel = Object.Instantiate(ogPanel.gameObject, ogPanel.transform.parent);
|
||||
@@ -122,7 +141,7 @@ public static class CustomRoleManager
|
||||
return newPanel;
|
||||
}
|
||||
|
||||
public static void UpdateRoleTab(TaskPanelBehaviour panel, ICustomRole role)
|
||||
internal static void UpdateRoleTab(TaskPanelBehaviour panel, ICustomRole role)
|
||||
{
|
||||
var tabText = panel.tab.gameObject.GetComponentInChildren<TextMeshPro>();
|
||||
var ogPanel = HudManager.Instance.TaskStuff.transform.FindChild("TaskPanel").gameObject.GetComponent<TaskPanelBehaviour>();
|
||||
@@ -138,40 +157,20 @@ public static class CustomRoleManager
|
||||
panel.SetTaskText(role.SetTabText().ToString());
|
||||
}
|
||||
|
||||
public static void SyncAllRoleSettings(int targetId=-1)
|
||||
internal static void SyncAllRoleSettings(int targetId=-1)
|
||||
{
|
||||
List<NetData> data = [];
|
||||
int count = 0;
|
||||
foreach (var role in CustomRoles.Values)
|
||||
var data = CustomRoles.Values
|
||||
.Where(x => x is ICustomRole { HideSettings: false })
|
||||
.Select(x => ((ICustomRole)x).GetNetData())
|
||||
.ChunkNetData(1000);
|
||||
|
||||
while (data.Count > 0)
|
||||
{
|
||||
ICustomRole customRole = role as ICustomRole;
|
||||
if (customRole is null or { HideSettings: true })
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
customRole.ParentMod.PluginConfig.TryGetEntry<int>(customRole.NumConfigDefinition, out var numEntry);
|
||||
customRole.ParentMod.PluginConfig.TryGetEntry<int>(customRole.ChanceConfigDefinition, out var chanceEntry);
|
||||
|
||||
var netData = new NetData((uint)role.Role, BitConverter.GetBytes(numEntry.Value).AddRangeToArray(BitConverter.GetBytes(chanceEntry.Value)));
|
||||
|
||||
data.Add(netData);
|
||||
count += netData.GetLength();
|
||||
|
||||
if (count > 1000)
|
||||
{
|
||||
Rpc<SyncRoleOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
|
||||
data.Clear();
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
if (data.Count > 0)
|
||||
{
|
||||
Rpc<SyncRoleOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.ToArray());
|
||||
Rpc<SyncRoleOptionsRpc>.Instance.SendTo(PlayerControl.LocalPlayer, targetId, data.Dequeue());
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleSyncRoleOptions(NetData[] data)
|
||||
internal static void HandleSyncRoleOptions(NetData[] data)
|
||||
{
|
||||
foreach (var netData in data)
|
||||
{
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using AmongUs.GameOptions;
|
||||
using BepInEx.Configuration;
|
||||
using HarmonyLib;
|
||||
using MiraAPI.Modifiers;
|
||||
using MiraAPI.Networking;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Teams.Teams;
|
||||
using MiraAPI.Utilities;
|
||||
using MiraAPI.Utilities.Assets;
|
||||
using System;
|
||||
using System.Text;
|
||||
using MiraAPI.PluginLoading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
@@ -22,7 +22,7 @@ public interface ICustomRole
|
||||
|
||||
Color RoleColor { get; }
|
||||
|
||||
Type Team { get; }
|
||||
ModdedRoleTeams Team { get; }
|
||||
|
||||
LoadableAsset<Sprite> OptionsScreenshot => MiraAssets.Empty;
|
||||
|
||||
@@ -32,35 +32,43 @@ public interface ICustomRole
|
||||
|
||||
ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance{RoleName}");
|
||||
|
||||
bool AffectedByLight => Team == typeof(CrewmateTeam);
|
||||
bool AffectedByLight => Team == ModdedRoleTeams.Crewmate;
|
||||
|
||||
bool CanGetKilled => Team == typeof(CrewmateTeam);
|
||||
bool CanGetKilled => Team == ModdedRoleTeams.Crewmate;
|
||||
|
||||
bool CanKill => Team == typeof(ImpostorTeam);
|
||||
bool IsNeutral => Team == ModdedRoleTeams.Neutral;
|
||||
|
||||
bool CanUseVent => Team == typeof(ImpostorTeam);
|
||||
bool CanKill => Team == ModdedRoleTeams.Impostor;
|
||||
|
||||
bool TasksCount => Team == typeof(CrewmateTeam);
|
||||
bool CanUseVent => Team == ModdedRoleTeams.Impostor;
|
||||
|
||||
bool TasksCount => Team == ModdedRoleTeams.Crewmate;
|
||||
|
||||
bool IsGhostRole => false;
|
||||
|
||||
bool TargetsBodies => false;
|
||||
|
||||
bool CreateCustomTab => true;
|
||||
|
||||
bool HideSettings => IsGhostRole;
|
||||
|
||||
RoleTypes GhostRole => Team == typeof(CrewmateRole) ? RoleTypes.CrewmateGhost : RoleTypes.ImpostorGhost;
|
||||
RoleTypes GhostRole => Team == ModdedRoleTeams.Crewmate ? RoleTypes.CrewmateGhost : RoleTypes.ImpostorGhost;
|
||||
|
||||
MiraPluginInfo ParentMod => CustomRoleManager.FindParentMod(this);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Runs on the PlayerControl FixedUpdate method for any player with this role
|
||||
/// </summary>
|
||||
/// <param name="playerControl">The PlayerControl that has this role</param>
|
||||
void PlayerControlFixedUpdate(PlayerControl playerControl) { }
|
||||
|
||||
/// <summary>
|
||||
/// Only runs for local player on HudManager Update method
|
||||
/// </summary>
|
||||
/// <param name="hudManager">Reference to HudManager instance</param>
|
||||
void HudUpdate(HudManager hudManager) { }
|
||||
|
||||
string GetCustomEjectionMessage(NetworkedPlayerInfo player)
|
||||
{
|
||||
return Team == typeof(ImpostorTeam) ? $"{player.PlayerName} was The {RoleName}" : null;
|
||||
return Team == ModdedRoleTeams.Impostor ? $"{player.PlayerName} was The {RoleName}" : null;
|
||||
}
|
||||
|
||||
StringBuilder SetTabText()
|
||||
@@ -68,12 +76,17 @@ public interface ICustomRole
|
||||
var taskStringBuilder = Helpers.CreateForRole(this);
|
||||
return taskStringBuilder;
|
||||
}
|
||||
|
||||
bool IsModifierApplicable(BaseModifier modifier)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
NetData GetNetData(RoleBehaviour roleBehaviour)
|
||||
NetData GetNetData()
|
||||
{
|
||||
ParentMod.PluginConfig.TryGetEntry<int>(NumConfigDefinition, out var numEntry);
|
||||
ParentMod.PluginConfig.TryGetEntry<int>(ChanceConfigDefinition, out var chanceEntry);
|
||||
|
||||
return new NetData((uint)roleBehaviour.Role, BitConverter.GetBytes(numEntry.Value).AddRangeToArray(BitConverter.GetBytes(chanceEntry.Value)));
|
||||
|
||||
return new NetData(RoleId.Get(GetType()), BitConverter.GetBytes(numEntry.Value).AddRangeToArray(BitConverter.GetBytes(chanceEntry.Value)));
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,4 @@
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class RegisterCustomRoleAttribute : Attribute
|
||||
{
|
||||
public ushort RoleId { get; }
|
||||
|
||||
public RegisterCustomRoleAttribute()
|
||||
{
|
||||
RoleId = (ushort)(20 + CustomRoleManager.CustomRoles.Count);
|
||||
}
|
||||
public RegisterCustomRoleAttribute(ushort roleId)
|
||||
{
|
||||
RoleId = roleId;
|
||||
}
|
||||
}
|
||||
public class RegisterCustomRoleAttribute : Attribute;
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Roles;
|
||||
|
||||
public static class RoleId
|
||||
{
|
||||
public static ushort Get<T>() where T : RoleBehaviour
|
||||
{
|
||||
if (!CustomRoleManager.RoleIds.TryGetValue(typeof(T), out var roleId))
|
||||
{
|
||||
throw new InvalidOperationException($"Role {typeof(T)} is not registered");
|
||||
}
|
||||
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public static ushort Get(Type type)
|
||||
{
|
||||
if (!CustomRoleManager.RoleIds.TryGetValue(type, out var roleId))
|
||||
{
|
||||
throw new InvalidOperationException($"Role {type} is not registered");
|
||||
}
|
||||
|
||||
return roleId;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Teams;
|
||||
public abstract class BaseTeam
|
||||
{
|
||||
public abstract string TeamName { get; }
|
||||
public abstract string TeamDescription { get; }
|
||||
public abstract Color TeamColor { get; }
|
||||
public abstract RoleTeamTypes BaseRole { get; }
|
||||
|
||||
public abstract bool DidWin(GameOverReason reason);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Teams.Teams;
|
||||
public class CrewmateTeam : BaseTeam
|
||||
{
|
||||
public override string TeamName => "Crewmate";
|
||||
|
||||
public override string TeamDescription => "Do your tasks";
|
||||
|
||||
public override Color TeamColor => Palette.CrewmateBlue;
|
||||
|
||||
public override RoleTeamTypes BaseRole => RoleTeamTypes.Crewmate;
|
||||
|
||||
public override bool DidWin(GameOverReason reason)
|
||||
{
|
||||
return GameManager.Instance.DidHumansWin(reason);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Teams.Teams;
|
||||
public class ImpostorTeam : BaseTeam
|
||||
{
|
||||
public override string TeamName => "Impostor";
|
||||
|
||||
public override string TeamDescription => "Kill and sabotage";
|
||||
|
||||
public override Color TeamColor => Palette.ImpostorRed;
|
||||
|
||||
public override RoleTeamTypes BaseRole => RoleTeamTypes.Impostor;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Teams.Teams;
|
||||
public class NeutralTeam : BaseTeam
|
||||
{
|
||||
public override string TeamName => "Neutral";
|
||||
|
||||
public override string TeamDescription => "You're on your own";
|
||||
|
||||
public override Color TeamColor => Palette.DisabledGrey;
|
||||
|
||||
public override RoleTeamTypes BaseRole => RoleTeamTypes.Crewmate;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using Reactor.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Utilities.Assets;
|
||||
@@ -9,7 +10,7 @@ public class LoadableResourceAsset(string path) : LoadableAsset<Sprite>
|
||||
|
||||
public override Sprite LoadAsset()
|
||||
{
|
||||
if (LoadedAsset != null)
|
||||
if (LoadedAsset)
|
||||
{
|
||||
return LoadedAsset;
|
||||
}
|
||||
@@ -20,7 +21,7 @@ public class LoadableResourceAsset(string path) : LoadableAsset<Sprite>
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.LogError($"Not loading, invalid asset: {path}");
|
||||
Logger<MiraApiPlugin>.Error($"Not loading, invalid asset: {path}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,6 @@ public static class MiraAssets
|
||||
public static readonly LoadableResourceAsset NextButton = new("MiraAPI.Resources.NextButton.png");
|
||||
public static readonly LoadableResourceAsset NextButtonActive = new("MiraAPI.Resources.NextButtonActive.png");
|
||||
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");
|
||||
}
|
||||
@@ -13,17 +13,17 @@ namespace MiraAPI.Utilities.Assets;
|
||||
/// </summary>
|
||||
public static class SpriteTools
|
||||
{
|
||||
private delegate bool DLoadImage(IntPtr tex, IntPtr data, bool markNonReadable);
|
||||
public delegate bool DLoadImage(IntPtr tex, IntPtr data, bool markNonReadable);
|
||||
|
||||
private static DLoadImage _iCallLoadImage;
|
||||
public static DLoadImage ICallLoadImage;
|
||||
|
||||
private static bool LoadImage(Texture2D tex, byte[] data, bool markNonReadable)
|
||||
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>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Utilities;
|
||||
public class CustomColor
|
||||
{
|
||||
public CustomColor(Color32 mainColor, StringNames name)
|
||||
{
|
||||
MainColor = mainColor;
|
||||
ShadowColor = GetShadowColor(mainColor, 60);
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public CustomColor(Color32 mainColor, Color32 shadowColor, StringNames name)
|
||||
{
|
||||
MainColor = mainColor;
|
||||
ShadowColor = shadowColor;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public Color32 MainColor { get; }
|
||||
public Color32 ShadowColor { get; }
|
||||
public StringNames Name { get; }
|
||||
|
||||
public static Color32 GetShadowColor(Color32 c, byte darknessAmount)
|
||||
{
|
||||
return
|
||||
new Color32((byte)Mathf.Clamp(c.r - darknessAmount, 0, 255), (byte)Mathf.Clamp(c.g - darknessAmount, 0, 255),
|
||||
(byte)Mathf.Clamp(c.b - darknessAmount, 0, 255), byte.MaxValue);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MiraAPI.Utilities.Colors;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class RegisterColorAttribute : Attribute;
|
||||
+132
-44
@@ -1,21 +1,128 @@
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Roles;
|
||||
using Reactor.Utilities.Extensions;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Modifiers;
|
||||
using Reactor.Utilities;
|
||||
using System.Linq;
|
||||
using MiraAPI.Networking;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Utilities;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public static bool IsStatic(this Type type)
|
||||
{
|
||||
return type is { IsClass: true, IsAbstract: true, IsSealed: true };
|
||||
}
|
||||
|
||||
public static Color32 GetShadowColor(this Color32 color, byte darknessAmount)
|
||||
{
|
||||
return
|
||||
new Color32((byte)Mathf.Clamp(color.r - darknessAmount, 0, 255), (byte)Mathf.Clamp(color.g - darknessAmount, 0, 255),
|
||||
(byte)Mathf.Clamp(color.b - darknessAmount, 0, 255), byte.MaxValue);
|
||||
}
|
||||
|
||||
public static string? Truncate(this string? value, int maxLength, string truncationSuffix = "…")
|
||||
{
|
||||
return value?.Length > maxLength
|
||||
? value[..maxLength] + truncationSuffix
|
||||
: value;
|
||||
}
|
||||
|
||||
public static Queue<NetData[]> ChunkNetData(this IEnumerable<NetData> dataCollection, int chunkSize)
|
||||
{
|
||||
Queue<NetData[]> chunks = [];
|
||||
List<NetData> current = [];
|
||||
|
||||
var count = 0;
|
||||
foreach (var netData in dataCollection)
|
||||
{
|
||||
var length = netData.GetLength();
|
||||
|
||||
if (length > chunkSize)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"NetData length is greater than chunk size: {length} > {chunkSize}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count + length > chunkSize)
|
||||
{
|
||||
chunks.Enqueue(current.ToArray());
|
||||
current.Clear();
|
||||
count = 0;
|
||||
}
|
||||
|
||||
current.Add(netData);
|
||||
}
|
||||
|
||||
if (current.Count > 0)
|
||||
{
|
||||
chunks.Enqueue(current.ToArray());
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
|
||||
public static bool IsCustom(this OptionBehaviour optionBehaviour)
|
||||
{
|
||||
return ModdedOptionsManager.ModdedOptions.Values.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour.Equals(optionBehaviour));
|
||||
}
|
||||
|
||||
public static ModifierComponent? GetModifierComponent(this PlayerControl player)
|
||||
{
|
||||
return player.GetComponent<ModifierComponent>();
|
||||
}
|
||||
|
||||
public static List<T> Randomize<T>(this List<T> list)
|
||||
{
|
||||
List<T> randomizedList = [];
|
||||
System.Random rnd = new();
|
||||
while (list.Count > 0)
|
||||
{
|
||||
var index = rnd.Next(0, list.Count);
|
||||
randomizedList.Add(list[index]);
|
||||
list.RemoveAt(index);
|
||||
}
|
||||
return randomizedList;
|
||||
}
|
||||
|
||||
public static bool HasModifier<T>(this PlayerControl? player) where T : BaseModifier
|
||||
{
|
||||
return player?.GetModifierComponent() != null && player.GetModifierComponent()!.ActiveModifiers.Exists(x => x is T);
|
||||
}
|
||||
public static bool HasModifier(this PlayerControl? player, uint id)
|
||||
{
|
||||
return player?.GetModifierComponent() != null && player.GetModifierComponent()!.ActiveModifiers.Exists(x => x.ModifierId == id);
|
||||
}
|
||||
|
||||
public static void AddModifier<T>(this PlayerControl player) where T : BaseModifier
|
||||
{
|
||||
if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier {typeof(T).Name} because it is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
ModifierComponent.RpcAddModifier(player, id);
|
||||
}
|
||||
|
||||
public static void RemoveModifier<T>(this PlayerControl player) where T : BaseModifier
|
||||
{
|
||||
if (!ModifierManager.TypeToIdModifiers.TryGetValue(typeof(T), out var id))
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier {typeof(T).Name} because it is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
ModifierComponent.RpcRemoveModifier(player, id);
|
||||
}
|
||||
|
||||
public static Color DarkenColor(this Color color)
|
||||
{
|
||||
return new Color(color.r - 0.3f, color.g - 0.3f, color.b - 0.3f);
|
||||
return new Color(color.r - 0.45f, color.g - 0.45f, color.b - 0.45f);
|
||||
}
|
||||
public static Color GetAlternateColor(this Color color)
|
||||
{
|
||||
@@ -24,54 +131,37 @@ public static class Extensions
|
||||
|
||||
public static Color LightenColor(this Color color)
|
||||
{
|
||||
return new Color(color.r + 0.3f, color.g + 0.3f, color.b + 0.3f);
|
||||
return new Color(color.r + 0.45f, color.g + 0.45f, color.b + 0.45f);
|
||||
}
|
||||
|
||||
public static bool IsColorDark(this Color color)
|
||||
{
|
||||
return color.r < 0.5f && color.g < 0.5f && color.b < 0.5f;
|
||||
return color.r < 0.5f && color is { g: < 0.5f, b: < 0.5f };
|
||||
}
|
||||
|
||||
public static void UpdateBodies(this PlayerControl playerControl, Color outlineColor, ref DeadBody target)
|
||||
{
|
||||
foreach (var body in Object.FindObjectsOfType<DeadBody>())
|
||||
{
|
||||
foreach (var bodyRenderer in body.bodyRenderers)
|
||||
{
|
||||
bodyRenderer.SetOutline(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (playerControl.Data.Role is not ICustomRole { TargetsBodies: true })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
target = playerControl.NearestDeadBody();
|
||||
if (!target)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var renderer in target.bodyRenderers)
|
||||
{
|
||||
renderer.SetOutline(outlineColor);
|
||||
}
|
||||
}
|
||||
|
||||
public static DeadBody NearestDeadBody(this PlayerControl playerControl)
|
||||
public static DeadBody? NearestDeadBody(this PlayerControl playerControl, float radius)
|
||||
{
|
||||
var results = new Il2CppSystem.Collections.Generic.List<Collider2D>();
|
||||
Physics2D.OverlapCircle(playerControl.GetTruePosition(), playerControl.MaxReportDistance / 4f, Helpers.Filter, results);
|
||||
Physics2D.OverlapCircle(playerControl.GetTruePosition(), radius, Helpers.Filter, results);
|
||||
return results.ToArray()
|
||||
.Where(collider2D => collider2D.CompareTag("DeadBody"))
|
||||
.Select(collider2D => collider2D.GetComponent<DeadBody>())
|
||||
.FirstOrDefault(component => component && !component.Reported);
|
||||
}
|
||||
|
||||
public static PlayerControl GetClosestPlayer(this PlayerControl playerControl, bool includeImpostors, float distance)
|
||||
public static T? GetNearestObjectOfType<T>(this PlayerControl playerControl, float radius, string? colliderTag=null, Func<T, bool>? predicate=null) where T : Component
|
||||
{
|
||||
PlayerControl result = null;
|
||||
var results = new Il2CppSystem.Collections.Generic.List<Collider2D>();
|
||||
Physics2D.OverlapCircle(playerControl.GetTruePosition(), radius, Helpers.Filter, results);
|
||||
return results.ToArray()
|
||||
.Where(collider2D => colliderTag == null || collider2D.CompareTag(colliderTag))
|
||||
.Select(collider2D => collider2D.GetComponent<T>())
|
||||
.FirstOrDefault(predicate ?? (component => component));
|
||||
}
|
||||
|
||||
public static PlayerControl? GetClosestPlayer(this PlayerControl playerControl, bool includeImpostors, float distance)
|
||||
{
|
||||
PlayerControl? result = null;
|
||||
if (!ShipStatus.Instance)
|
||||
{
|
||||
return null;
|
||||
@@ -79,14 +169,12 @@ public static class Extensions
|
||||
|
||||
var truePosition = playerControl.GetTruePosition();
|
||||
|
||||
foreach (var playerInfo in GameData.Instance.AllPlayers)
|
||||
var filteredPlayers = GameData.Instance.AllPlayers.ToArray()
|
||||
.Where(playerInfo => !playerInfo.Disconnected && playerInfo.PlayerId != playerControl.PlayerId && !playerInfo.IsDead &&
|
||||
(includeImpostors || !playerInfo.Role.IsImpostor));
|
||||
|
||||
foreach (var playerInfo in filteredPlayers)
|
||||
{
|
||||
if (playerInfo.Disconnected || playerInfo.PlayerId == playerControl.PlayerId ||
|
||||
playerInfo.IsDead || !includeImpostors && playerInfo.Role.IsImpostor)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var @object = playerInfo.Object;
|
||||
if (!@object)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MiraAPI.Roles;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Utilities;
|
||||
@@ -8,17 +9,43 @@ namespace MiraAPI.Utilities;
|
||||
public static class Helpers
|
||||
{
|
||||
public static readonly ContactFilter2D Filter = ContactFilter2D.CreateLegacyFilter(Constants.NotShipMask, float.MinValue, float.MaxValue);
|
||||
|
||||
public static PlainShipRoom GetRoom(Vector3 pos)
|
||||
{
|
||||
return ShipStatus.Instance.AllRooms.ToList().Find(room => room.roomArea.OverlapPoint(pos));
|
||||
}
|
||||
|
||||
public static TextMeshPro CreateTextLabel(string name, Transform parent,
|
||||
AspectPosition.EdgeAlignments alignment, Vector3 distance, float fontSize = 2f,
|
||||
TextAlignmentOptions textAlignment = TextAlignmentOptions.Center)
|
||||
{
|
||||
var textObj = new GameObject(name)
|
||||
{
|
||||
transform =
|
||||
{
|
||||
parent = parent
|
||||
},
|
||||
layer = LayerMask.NameToLayer("UI")
|
||||
};
|
||||
|
||||
var textMeshPro = textObj.AddComponent<TextMeshPro>();
|
||||
textMeshPro.fontSize = fontSize;
|
||||
textMeshPro.alignment = textAlignment;
|
||||
textMeshPro.font = HudManager.Instance.TaskPanel.taskText.font;
|
||||
textMeshPro.fontMaterial = HudManager.Instance.TaskPanel.taskText.fontMaterial;
|
||||
|
||||
var aspectPosition = textObj.AddComponent<AspectPosition>();
|
||||
aspectPosition.Alignment = alignment;
|
||||
aspectPosition.DistanceFromEdge = distance;
|
||||
aspectPosition.AdjustPosition();
|
||||
|
||||
return textMeshPro;
|
||||
}
|
||||
|
||||
public static DeadBody GetBodyById(byte id)
|
||||
{
|
||||
return Object.FindObjectsOfType<DeadBody>().FirstOrDefault(body => body.ParentId == id);
|
||||
}
|
||||
|
||||
|
||||
public static string GetSuffix(NumberSuffixes suffix)
|
||||
{
|
||||
return suffix switch
|
||||
|
||||
@@ -6,32 +6,80 @@ A thorough, but simple, Among Us modding API and utility library that covers:
|
||||
- Roles
|
||||
- Options
|
||||
- Modifiers
|
||||
- Game Modes
|
||||
- Buttons
|
||||
- Custom Colors
|
||||
- Assets
|
||||
- HUD Elements
|
||||
- Compatibility
|
||||
- ~~Game Modes~~ (coming soon)
|
||||
|
||||
Mira API strives to be simple and easy to use, while also using as many base game elements as possible. The result is a less intrusive, better modding API that covers general use cases.
|
||||
|
||||
**Join the [Discord](https://discord.gg/all-of-us-launchpad-794950428756410429) for support and to stay updated on the latest releases**
|
||||
|
||||
# Usage
|
||||
|
||||
To start using Mira API, you need to:
|
||||
1. Add a reference to Mira API either through a DLL, project reference, or NuGet package.
|
||||
1. Add a reference to Mira API either through a [DLL](https://github.com/All-Of-Us-Mods/MiraAPI/releases), project reference, or [NuGet package](https://www.nuget.org/packages/AllOfUs.MiraAPI).
|
||||
2. Add a BepInDependency on your plugin class like this: `[BepInDependency(MiraApiPlugin.Id)]`
|
||||
3. Implement the IMiraPlugin interface on your plugin class.
|
||||
|
||||
For a full example, see [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/ExamplePlugin.cs).
|
||||
|
||||
## Recommended Project Structure
|
||||
It is highly recommended to follow this project structure when using Mira API in order to keep your code clean and organized. You can also view the Example Mod in this repository for some guidance.
|
||||
```
|
||||
MyMiraMod/
|
||||
├── Buttons/
|
||||
│ └── MyCoolButton.cs
|
||||
├── Options/
|
||||
│ ├── Roles/
|
||||
│ │ └── CoolCustomRoleOptions.cs
|
||||
│ └── MainOptionGroup.cs
|
||||
├── Patches/
|
||||
│ ├── Roles/
|
||||
│ │ └── CoolCustomRole/
|
||||
│ │ ├── PlayerControlPatches.cs
|
||||
│ │ └── ExileControllerPatches.cs
|
||||
│ └── General/
|
||||
│ └── HudManagerPatches.cs
|
||||
├── Resources/
|
||||
│ ├── CoolButton.png
|
||||
│ └── myAssets-win-x86.bundle
|
||||
├── Roles/
|
||||
│ └── CoolCustomRole.cs
|
||||
├── MyMiraModPlugin.cs
|
||||
└── MyModAssets.cs
|
||||
```
|
||||
|
||||
## Roles
|
||||
Roles are very simple in Mira API. There are 3 things you need to do to create a custom role:
|
||||
1. Create a class that inherits from a base game role (like `RoleBehaviour`, `CrewmateRole`, `ImpostorRole`, etc).
|
||||
1. Create a class that inherits from a base game role (like `CrewmateRole`, `ImpostorRole`, etc)
|
||||
2. Implement the `ICustomRole` interface from Mira API.
|
||||
3. Add the `[RegisterCustomRole]` attribute to the class.
|
||||
|
||||
Note: For step 1, if you are making neutral roles, choose either `CrewmateRole` or `ImpostorRole` as the base depending on if it can kill or not!
|
||||
|
||||
Mira API handles everything else, from adding the proper options to the settings menu, to managing the role assignment at the start of the game. There are no extra steps on the developer's part.
|
||||
|
||||
See [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Roles/CustomRole.cs) for a code example.
|
||||
|
||||
## Modifiers
|
||||
Mira API uses a different definition of 'modifiers' than other Among Us mods. For example, in Town Of Us, a modifier is an extra "ability" that is applied on top of the base role. However, in Mira API, modifiers are very flexible. A modifier is anything that "modifiers" a player's abilities or interactions.
|
||||
|
||||
Mira provides 3 classes for working with modifiers:
|
||||
- `BaseModifier`: The base for every modifier. You MUST add and remove this modifier from a player manually!
|
||||
- `TimedModifier`: A modifier that has a time limit. This modifier has to be added manually, but will automatically remove itself after the time limit
|
||||
- `GameModifier`: This works like the typical TOU modifier, where it is automatically applied at the beginning of the game, then removed at the end.
|
||||
|
||||
Modifiers provide various overridable functions and properties for custom behaviour. They can also be used for "tagging" a player. You can check if a player has a modifier through the extension method `HasModifier` on a `PlayerControl` object.
|
||||
|
||||
To start using a modifier, pick one of the base classes above and create a class that inherits from it. Implement the properties and methods you would like, then add the `[RegisterModifier]` attribute to the class.
|
||||
|
||||
An example Game modifier can be found [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Modifiers/GameModifierExample.cs).
|
||||
An example Timer modifier can be found [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Modifiers/ModifierTimerExample.cs).
|
||||
|
||||
## Options
|
||||
Options are also very simple in Mira API. Options are split up into Groups and Options. Every Option needs to be in a Group.
|
||||
Options are also very simple in Mira API. Mira API handles all the hard work behind the scenes, so developers only have to follow a few steps to create their custom options. The Options API is split up into Groups and Options. Every Option needs to be in a Group.
|
||||
|
||||
To create a group, you need to create a class that inherits from the `AbstractOptionGroup` abstract class. Groups contain 4 properties, `GroupName`, `GroupColor`, `GroupVisible`, and `AdvancedRole`. Only the `GroupName` is required.
|
||||
|
||||
@@ -50,13 +98,15 @@ You can access any group class using the `OptionGroupSingleton` class like this:
|
||||
```csharp
|
||||
// MyOptionsGroup is a class that inherits from AbstractOptionGroup
|
||||
var myGroup = OptionGroupSingleton<MyOptionsGroup>.Instance; // gets the instance of the group
|
||||
System.Out.Console.WriteLine(myGroup.MyNumberOption); // prints the value of the option to the console
|
||||
Logger<MyPlugin>.Info(myGroup.MyNumberOption); // prints the value of the option to the console
|
||||
```
|
||||
|
||||
Once you have an options group, there are two ways to make the actual options:
|
||||
- Use an Option Attribute with a property.
|
||||
- Create a ModdedOption property.
|
||||
|
||||
### Option Attributes
|
||||
|
||||
This is an example of using an Option Attribute on a property:
|
||||
```csharp
|
||||
// The first parameter is always the name of the option. The rest are dependent on the type of option.
|
||||
@@ -64,6 +114,26 @@ This is an example of using an Option Attribute on a property:
|
||||
public float SussyLevel { get; set; } = 4f; // You can set a default value here.
|
||||
```
|
||||
|
||||
Here are the available Option Attributes and their signatures:
|
||||
```csharp
|
||||
ModdedEnumOption(string name, Type enumType, Type roleType = null)
|
||||
|
||||
ModdedNumberOption(
|
||||
string name,
|
||||
float min,
|
||||
float max,
|
||||
float increment=1
|
||||
NumberSuffixes suffixType = NumberSuffixes.None,
|
||||
bool zeroInfinity = false,
|
||||
Type roleType = null)
|
||||
|
||||
ModdedStringOption(string title, string[] values, Type roleType = null)
|
||||
|
||||
ModdedToggleOption(string name, Type roleType = null)
|
||||
```
|
||||
|
||||
### ModdedOption Properties
|
||||
|
||||
And this is an example of a ModdedOption property:
|
||||
```csharp
|
||||
public ModdedToggleOption YeezusAbility { get; } = new ModdedToggleOption("Yeezus Ability", false);
|
||||
@@ -114,10 +184,40 @@ Mira API provides a simple interface for adding ability buttons to the game. The
|
||||
1. Create a class that inherits from the `CustomActionButton` class and implement the properties and methods.
|
||||
2. Add the `[RegisterCustomButton]` attribute to the class.
|
||||
|
||||
All other tasks and logic required to add the button to the game are handled by Mira API.
|
||||
|
||||
In case you need to access your `CustomActionButton` instance from another class, you can use the `CustomButtonSingleton` class like this:
|
||||
```csharp
|
||||
var myButton = CustomButtonSingleton<MyCoolButton>.Instance;
|
||||
```
|
||||
|
||||
The button API is simple, but provides a lot of flexibility. There are various methods you can override to customize the behaviour of your button. See [this file](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI/Hud/CustomActionButton.cs) for a full list of methods you can override.
|
||||
|
||||
An example button can be found [here](https://github.com/All-Of-Us-Mods/MiraAPI/blob/master/MiraAPI.Example/Buttons/ExampleButton.cs).
|
||||
|
||||
## Custom Colors
|
||||
|
||||
Mira provides a simple Custom Color API that allows you to add custom player colors to the game.
|
||||
|
||||
Creating custom colors isn't difficult, but there are some requirements for your colors to be registered by Mira.
|
||||
|
||||
1. Create a ***STATIC*** class to house all your `CustomColor` objects.
|
||||
2. Inside this class, create a `CustomColor` property for each color you intend to add.
|
||||
3. Add the `[RegisterCustomColors]` attribute to the class.
|
||||
|
||||
Here is an example of a custom color class:
|
||||
```csharp
|
||||
[RegisterCustomColors]
|
||||
public static class MyCustomColors
|
||||
{
|
||||
public static CustomColor Cerulean { get; } = new CustomColor("Cerulean", new Color(0.0f, 0.48f, 0.65f));
|
||||
|
||||
public static CustomColor Rose { get; } = new CustomColor("Rose", new Color(0.98f, 0.26f, 0.62f));
|
||||
|
||||
public static CustomColor Gold { get; } = new CustomColor("Gold", new Color(1.0f, 0.84f, 0.0f));
|
||||
}
|
||||
```
|
||||
|
||||
## Assets
|
||||
|
||||
Mira API provides a simple, but expandable asset system. The core of the system is the `LoadableAsset<T>` class. This is a generic abstract class that provides a pattern for loading assets.
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
var target = Argument("target", "Build");
|
||||
|
||||
var workflow = BuildSystem.GitHubActions.Environment.Workflow;
|
||||
var buildId = workflow.RunNumber;
|
||||
var tag = workflow.RefType == GitHubActionsRefType.Tag ? workflow.RefName : null;
|
||||
|
||||
Task("Build")
|
||||
.Does(() =>
|
||||
{
|
||||
var settings = new DotNetBuildSettings
|
||||
{
|
||||
Configuration = "Release",
|
||||
MSBuildSettings = new DotNetMSBuildSettings()
|
||||
};
|
||||
|
||||
if (tag != null)
|
||||
{
|
||||
settings.MSBuildSettings.Version = tag;
|
||||
}
|
||||
else if (buildId != 0)
|
||||
{
|
||||
settings.MSBuildSettings.VersionSuffix = "ci." + buildId;
|
||||
}
|
||||
|
||||
DotNetBuild(".", settings);
|
||||
});
|
||||
|
||||
RunTarget(target);
|
||||
Reference in new issue
Block a user