Files
MiraAPI/MiraAPI/Roles/ICustomRole.cs
T

315 lines
11 KiB
C#

using System;
using System.Text;
using BepInEx.Configuration;
using MiraAPI.GameModes;
using MiraAPI.GameOptions;
using MiraAPI.Modifiers;
using MiraAPI.PluginLoading;
using MiraAPI.Translation;
using UnityEngine;
namespace MiraAPI.Roles;
/// <summary>
/// Interface for custom roles.
/// </summary>
public interface ICustomRole : IOptionable
{
/// <summary>
/// Gets the id part used to build the role's translation keys. It is recommended to simply call it what the role is, as other mods may utilize it.
/// </summary>
string IdPart => GetType().Name;
/// <summary>
/// Gets the id part used to build the role's translation keys. It is recommended to call it ModGuid.Role.Faction.
/// </summary>
string IdPrefix => GetType().Namespace!;
/// <summary>
/// Gets the name of the role.
/// </summary>
string RoleName => MiraLocaleManager.Get(RoleNameLocale);
/// <summary>
/// Gets the role's name id for localization.
/// </summary>
string RoleNameLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart);
/// <summary>
/// Gets the description of the role. Used in the Intro Cutscene.
/// </summary>
string RoleDescription => MiraLocaleManager.Get(RoleDescriptionLocale);
/// <summary>
/// Gets the role's intro blurb id for localization.
/// </summary>
string RoleDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "IntroBlurb");
/// <summary>
/// Gets the medium description of the role. Used in the role guide and options menu.
/// </summary>
string RoleMedDescription => MiraLocaleManager.Get(RoleMedDescriptionLocale);
/// <summary>
/// Gets the role's medium description id for localization.
/// </summary>
string RoleMedDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "MedDescription");
/// <summary>
/// Gets the long description of the role. Used in the Role Tab.
/// </summary>
string RoleLongDescription => MiraLocaleManager.Get(RoleLongDescriptionLocale);
/// <summary>
/// Gets the role's long description id for localization.
/// </summary>
string RoleLongDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "TabDescription");
/// <summary>
/// Gets the wiki description of the role. Used in the wiki, but not currently required.
/// </summary>
string RoleWikiDescription => MiraLocaleManager.Get(RoleLongDescriptionLocale);
/// <summary>
/// Gets the role's wiki description id for localization.
/// </summary>
string RoleWikiDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "WikiDescription");
/// <summary>
/// Gets the faction / alignment of the role. Used in the role guide.
/// </summary>
string RoleFactionTitle => Team switch
{
ModdedRoleTeams.Crewmate => MiraLocaleManager.Get("MiraApi.RoleTeam.Crewmate"),
ModdedRoleTeams.Impostor => MiraLocaleManager.Get("MiraApi.RoleTeam.Impostor"),
ModdedRoleTeams.Custom => MiraLocaleManager.Get("MiraApi.RoleTeam.Neutral"),
_ => MiraLocaleManager.Get("MiraApi.RoleTeam.Other"),
};
/// <summary>
/// Gets the <see cref="Color"/> of the role.
/// </summary>
Color RoleColor { get; }
/// <summary>
/// Gets the <see cref="Color"/> that should be used in the options menu.
/// </summary>
Color OptionsMenuColor => RoleColor;
/// <summary>
/// Gets the <see cref="ModdedRoleTeams"/> of the role.
/// </summary>
ModdedRoleTeams Team { get; }
/// <summary>
/// Gets advanced settings of the role.
/// </summary>
CustomRoleConfiguration Configuration { get; }
/// <summary>
/// Gets the <see cref="RoleOptionsGroup"/>.
/// </summary>
RoleOptionsGroup RoleOptionsGroup => Team switch
{
ModdedRoleTeams.Crewmate => RoleOptionsGroup.Crewmate,
ModdedRoleTeams.Impostor => RoleOptionsGroup.Impostor,
ModdedRoleTeams.Custom => RoleOptionsGroup.Neutral,
_ => new RoleOptionsGroup(RoleName, RoleColor),
};
/// <summary>
/// Gets the role's TeamIntroCutscene configuration.
/// </summary>
TeamIntroConfiguration? IntroConfiguration => Team switch
{
ModdedRoleTeams.Custom => TeamIntroConfiguration.Neutral,
_ => null,
};
/// <summary>
/// Gets the parent mod of this role.
/// </summary>
MiraPluginInfo ParentMod => CustomRoleManager.FindParentMod(this);
internal ConfigDefinition NumConfigDefinition => new("Roles", $"Num {GetType().FullName}");
internal ConfigDefinition ChanceConfigDefinition => new("Roles", $"Chance {GetType().FullName}");
/// <summary>
/// Binds the configuration options for this role to the provided ConfigFile.
/// </summary>
/// <param name="config">The ConfigFile to bind the options to.</param>
void BindConfig(ConfigFile config)
{
config.Bind(NumConfigDefinition, Configuration.DefaultRoleCount);
config.Bind(ChanceConfigDefinition, Configuration.DefaultChance);
}
/// <summary>
/// Saves the role's configuration to a preset <see cref="ConfigFile"/>.
/// </summary>
/// <param name="presetConfig">The <see cref="ConfigFile"/> to save the preset configuration to.</param>
/// <param name="useDefault">Whether to use the default values for the configuration.</param>
void SaveToPreset(ConfigFile presetConfig, bool useDefault = false)
{
BindConfig(presetConfig);
presetConfig[NumConfigDefinition].BoxedValue = useDefault ? Configuration.DefaultRoleCount : GetCount();
presetConfig[ChanceConfigDefinition].BoxedValue = useDefault ? Configuration.DefaultChance : GetChance();
}
/// <summary>
/// Loads the role's configuration from a preset <see cref="ConfigFile"/>.
/// </summary>
/// <param name="presetConfig">The <see cref="ConfigFile"/> containing the preset configuration.</param>
void LoadFromPreset(ConfigFile presetConfig)
{
if (presetConfig.TryGetEntry(NumConfigDefinition, out ConfigEntry<int> numEntry))
{
SetCount(numEntry.Value);
}
if (presetConfig.TryGetEntry(ChanceConfigDefinition, out ConfigEntry<int> chanceEntry))
{
SetChance(chanceEntry.Value);
}
}
/// <summary>
/// Gets the role chance option.
/// </summary>
/// <returns>The role chance option.</returns>
int? GetChance()
{
return !Configuration.CanModifyChance
? Configuration.DefaultChance
: ParentMod.PluginConfig.TryGetEntry(ChanceConfigDefinition, out ConfigEntry<int> entry)
? Mathf.Clamp(entry.Value, 0, 100)
: null;
}
/// <summary>
/// Gets the role count option.
/// </summary>
/// <returns>The role count option.</returns>
int? GetCount()
{
return ParentMod.PluginConfig.TryGetEntry(NumConfigDefinition, out ConfigEntry<int> entry)
? Mathf.Clamp(entry.Value, 0, Configuration.MaxRoleCount)
: null;
}
/// <summary>
/// Sets the role chance option.
/// </summary>
/// <param name="chance">The chance between 0 and 100.</param>
void SetChance(int chance)
{
if (!Configuration.CanModifyChance)
{
Error($"Cannot modify chance for role: {RoleName}");
return;
}
if (ParentMod.PluginConfig.TryGetEntry(ChanceConfigDefinition, out ConfigEntry<int> entry))
{
entry.Value = Mathf.Clamp(chance, 0, 100);
return;
}
Error($"Error getting chance configuration for role: {RoleName}");
}
/// <summary>
/// Sets the role count option.
/// </summary>
/// <param name="count">The amount of this role between zero and its MaxRoleCount in the Configuration.</param>
void SetCount(int count)
{
if (ParentMod.PluginConfig.TryGetEntry(NumConfigDefinition, out ConfigEntry<int> entry))
{
entry.Value = Mathf.Clamp(count, 0, Configuration.MaxRoleCount);
return;
}
Error($"Error getting count configuration for role: {RoleName}");
}
/// <summary>
/// Whether the <see cref="PlayerControl.LocalPlayer"/> can see this role.
/// </summary>
/// <param name="player">The player with the role.</param>
/// <returns>Whether they can see the role (name color) or not.</returns>
bool CanLocalPlayerSeeRole(PlayerControl player)
{
return (PlayerControl.LocalPlayer.Data.Role.IsImpostor && player.Data.Role.IsImpostor) || PlayerControl.LocalPlayer.Data.IsDead;
}
/// <summary>
/// Allows the role to specify who is shown on the intro team screen.
/// </summary>
/// <param name="instance">The intro cutscene instance.</param>
/// <param name="yourTeam">The reference to the list of player in the team.</param>
/// <returns><see langword="true"/> to use the original team intro code, <see langword="false"/> to skip.</returns>
bool SetupIntroTeam(IntroCutscene instance, ref CppCollections.List<PlayerControl> yourTeam)
{
if (Team == ModdedRoleTeams.Custom)
{
var team = new CppCollections.List<PlayerControl>();
team.Add(PlayerControl.LocalPlayer);
yourTeam = team;
}
return true;
}
/// <summary>
/// Gets a custom ejection message for the role. Return <see langword="null"/> to use the default message.
/// </summary>
/// <param name="player">The NetworkedPlayerInfo object for this player.</param>
/// <returns>A string with a custom ejection message or <see langword="null"/>.</returns>
string? GetCustomEjectionMessage(NetworkedPlayerInfo player)
{
return Team == ModdedRoleTeams.Impostor ? $"{player.PlayerName} was The {RoleName}" : null;
}
/// <summary>
/// Get the custom Role Tab text for this role.
/// </summary>
/// <returns>A <see cref="StringBuilder"/> with the role tab text.</returns>
StringBuilder SetTabText()
{
return CustomRoleUtils.CreateForRole(this);
}
/// <summary>
/// Determine whether a given <see cref="BaseModifier"/> can be applied to this role.
/// </summary>
/// <param name="modifier">The modifier to be tested.</param>
/// <returns><see langword="true"/> if the <see cref="BaseModifier"/> is valid on this role, <see langword="false"/> otherwise.</returns>
bool IsModifierApplicable(BaseModifier modifier)
{
return true;
}
/// <summary>
/// Determines whether the role can spawn in general, accounting for gamemodes and everything else.
/// </summary>
/// <returns><see langword="true"/> if the role is able to spawn, otherwise <see langword="false"/>.</returns>
bool CanSpawnOnCurrentMode()
{
return Configuration.AssociatedGameMode.IsInstanceOfType(CustomGameModeManager.ActiveMode);
}
/// <summary>
/// Gets whether the role is forcibly shown or disabled in the wiki screen.
/// </summary>
/// <returns><see langword="true"/> if the role is always displayed, otherwise <see langword="false"/> if it is never displayable, or <see langword="null"/> if it is dictated by amount and chance.</returns>
bool? ForceShowRoleOnWiki => null;
/// <summary>
/// Gets the function that determines whether the role should be toggled on or off in the game settings.
/// </summary>
Func<bool> VisibleInSettings => () => true;
}