Skip to content

Developers World

FoxWorn3365 edited this page Aug 23, 2024 · 16 revisions

The UncomplicatedCustomRoles APIs are easy to use, and with v4.0.0 they become even easier!
This is not to say that ease takes away from quality, in fact you will be able to customize your custom Custom Roles even more!

Introduction

Our APIs are really logical to use and manage Custom Roles on multiple levels, so they are easy and intuitive. There are mainly two levels:

  • When the Custom Role is registered and uploaded by UCR.
  • When the Custom Role is applied to a player

Custom Role as Registered

UncomplicatedCustomRoles treats all roles that are registered as instances of a class that implements the ICustomRole interface: this then allows API users to be able to create custom classes that implement the interface!
When a role is loaded its instance is single: it is saved in a Dictionary<TKey, TValue> and thus can be retrieved-and modified-at any time: however, this modification is not applied instantaneously to players who have already obtained the role before the modification.

Tip

As how C# works, you can edit a role from the source dictionary, and statistics that are updated in real time (such as Hume Shield regeneration) will be automatically updated without the need to give the player the custom role again.

Custom Role as applied to a Player

Instead, UCR handles a role differently when it is assigned to a player.
This is where the SummonedCustomRole class comes in, which represents just such a swept player with a certain Custom Role.
This class also handles many aspects of the role, such as mechanics, Custom Modules, and event handling.
This class also saves the player's original information (such as any public tags, custom nicknames, and the like) so that these values are reassigned when the Custom Role is removed (either by the plugin then forcedly or by plugin events).
Each instance of SummonedCustomRole (which thus fully represents a player with a Custom Role) is saved in a list in the static variable SummonedCustomRole.List.

Extension

For faster management of players (and their related Custom Roles), UCR implements an extension that allows you to perform some quick actions by having the Player.

Function name Args Return Description Note
HasCustomRole bool Check if a Player is currently a ICustomRole
SetCustomRoleSync ICustomRole role void Set a ICustomRole to a Player without a coroutine Can cause small lag
SetCustomRole ICustomRole role void Set a ICustomRole to a Player
SetCustomRoleAttributes ICustomRole role void Set a ICustomRole to a player, skipping the spawn-related thing(s) You shouldn't use it
TryGetSummonedInstance out SummonedCustomRole role bool Try to gets the SummonedCustomRole instance for the given player: if false then the player is not a Custom Role
GetSummonedInstance bool Gets the SummonedCustomRole instance for the given player: if null then the player is not a Custom Role
TryRemoveCustomRole bool doResetRole = false bool Tries to remove the current Custom Role from a player.

So for example if you want to spawn a Player:

player.SetCustomRole(myBeautifulCustomRole);

If you want to try to remove the Custom Role:

// You can do both from the extension or from the SummonedCustomRole
// SummonedCustomRole
if (player.TryGetSummonedInstance(out SummonedCustomRole role))
  role.Destroy();

// Extension
player.TryRemoveCustomRole();

If you want to check if a player has a Custom Role

// You can use two methods
// If you don't need the Custom Role info
if (player.HasCustomRole())
  Log.Info("Yeah");

// If you need Custom Role info
if (player.TryGetSummonedInstance(out SummonedCustomRole role))
  // Your code...

Create a Custom Role

Through your own plugin you can create your own Custom Roles and import them within UCR.
However, we must first distinguish the types of Custom Roles that you can manage with UCR.

  • ICustomRole: the basic interface of a Custom Role: you will have to implement all members of the interface yourself
  • CustomRole: the UCR class that already implements the ICustomRole interface: every member is virtual so you can override them.
  • EventCustomRole: the UCR class that already implements the CustomRole and also allow to handle every event by overriding them.

ICustomRole

  • Fully customizable implementation
  • Fast and easy implementation
  • Event implementation Perfect for those who, in addition to already knowing C# well, want to customize the role in every way.
    Example:
namespace UCRExample
{
    internal class MyCustomRole : ICustomRole
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool OverrideRoleName { get; set; }
        public string Nickname { get; set; }
        public string CustomInfo { get; set; }
        public string BadgeName { get; set; }
        public string BadgeColor { get; set; }
        public RoleTypeId Role { get; set; }
        public Team? Team { get; set; }
        public RoleTypeId RoleAppearance { get; set; }
        public List<Team> IsFriendOf { get; set; }
        public HealthBehaviour Health { get; set; }
        public AhpBehaviour Ahp { get; set; }
        public List<Effect> Effects { get; set; }
        public StaminaBehaviour Stamina { get; set; }
        public int MaxScp330Candies { get; set; }
        public bool CanEscape { get; set; }
        public Dictionary<string, string> RoleAfterEscape { get; set; }
        public Vector3 Scale { get; set; }
        public string SpawnBroadcast { get; set; }
        public ushort SpawnBroadcastDuration { get; set; }
        public string SpawnHint { get; set; }
        public float SpawnHintDuration { get; set; }
        public Dictionary<ItemCategory, sbyte> CustomInventoryLimits { get; set; }
        public List<ItemType> Inventory { get; set; }
        public List<uint> CustomItemsInventory { get; set; }
        public Dictionary<AmmoType, ushort> Ammo { get; set; }
        public float DamageMultiplier { get; set; }
        public SpawnBehaviour SpawnSettings { get; set; }
        public CustomFlags? CustomFlags { get; set; }
        public bool IgnoreSpawnSystem { get; set; }
    }
}

Note

I have not set any values as they are unnecessary for the purpose of the wiki. However, it remains mandatory to definiri as there is no default fallback!

CustomRole

  • Fully customizable implementation
  • Fast and easy implementation
  • Event implementation Perfect if you want to create a Custom Role by changing only a few default values and leaving unnecessary ones as such.
    Example:
namespace UCRExample
{
    internal class AnotherCustomRole : CustomRole
    {
        public override int Id { get; set; } = 69;

        public override string Name { get; set; } = "AnotherRole";
    }
}

As you can see we have modified only the values that we wanted to edit, without the need to import the whole parameters as if not overrided here the default ones will be used!

EventCustomRole

  • Fully customizable implementation
  • Fast and easy implementation
  • Event implementation This can be seen more as an extension of CustomRole in that it implements it and also implements a built-in event system: in fact, with this class you can handle events directly from here and without even having to register them!

Important

The class event will obviously be invoked when the player with the custom role activates it. Example:

namespace UCRExample
{
    internal class MyEventCustomRole : EventCustomRole, ICoroutineRole
    {
        public override int Id { get; set; } = 99;

        public override void OnDroppedItem(DroppedItemEventArgs ev)
        {
            ev.Player.ShowHint("You just dropped an item!", 2.5f);
        }
    }
}

As you can see we are handling the DroppedItem event by overriding the basic virtual method with our function!

Register a Custom Role

It is possible to register a Custom Role in two different ways, depending on your needs:

  • Via an Attribute to be assigned to the Custom Role class.
  • Via a call to the CustomRole.Register(ICustomRole) static function.

Warning

Registering a Custom Role with both methods “just to be sure” although it may sound very clever is quite stupid: the role will be de facto loaded twice.

Register with the Attribute

To automatically register a Custom Role without having to do anything outside of its class just use the PluginCustomRole attribute to apply to the class.
Example:

namespace UCRExample
{
    [PluginCustomRole]
    internal class AnotherCustomRole : CustomRole
    {
        public override int Id { get; set; } = 69;

        public override string Name { get; set; } = "AnotherRole";
    }
}

In this way when every plugin will finish the loading this role will be registered inside UCR.

Register with CustomRole.Register

If, on the other hand, you prefer to load the role in the traditional way - or the plugin must be able to decide whether to load it or not - you can use the classic static function to register the CustomRole.
Example:

namespace UCRExample
{
    internal class AnotherCustomRole : CustomRole
    {
        public override int Id { get; set; } = 69;

        public override string Name { get; set; } = "AnotherRole";
    }
}
// You can remove the first part if you do use UncomplicatedCustomRoles.API.Features - you should already know that btw
UncomplicatedCustomRoles.API.Features.CustomRole.Register(new AnotherCustomRole());

Coroutine Custom Role

Through UCR, it is possible to already set up a role with a Coroutine within it, that is, an action that repeats from when a Player becomes that role until it is removed from him.
In order to use this very cool feature you need to implement the ICoroutineRole interface within your Custom Role.
Example:

namespace UCRExample
{
    [PluginCustomRole]
    internal class MyCoroutineCustomRole : CustomRole, ICoroutineRole
    {
        public override int Id { get; set; } = 56;

        public CoroutineHandle CoroutineHandler { get; set; }

        public float TickRate { get; set; } = 5f;

        public long Frame { get; set; } = -1;

        public void Tick(SummonedCustomRole roleInstance)
        {
            roleInstance.Player.ShowHint("UWU", 1f);
        }
    }
}

In this example, when the player receives this custom role it will display a hint containing “UWU” for 1 second: this is every 5 seconds.

  • float TickRate: Every how many seconds should the Tick() function invoked
  • float Frame: Every iteration (Invoke of Tick()) 1 is added to this variable ( so Frame++ )
  • CoroutineHandle CoroutineHandler: the handler of the coroutine: in this way you can manage it by yourself!

Tip

ICoroutineRole can be also used with EventCustomRole!

Custom Modules

Version v4.0.0 also brought Custom Modules.
These we can see as hard-coded “skills” in the plugin.
The official Custom Modules have a CustomFlag assigned to them by the system so that the plugin can infer which Custom Modules to load for a certain role.

Custom Modules can also be seen as “disks” that are inserted and removed at will from an instance or more of the SummonedCustomRoles class: these disks contain additional functionality.
And just think, you too can create and insert these disks!

Creating a Custom Module

All Custom Modules must implement the abstract class CustomModule in order to function.
That class has two constructors: one with no arguments and the other with a single argument: SummonedCustomRole. Depending on your requirements:

  • Choose the empty constructor (you can also not define it) if the functionality you create does not require an instance of the SummonedCustomRole class with which it is associated
  • Choose the constructor with the class SummonedCustomRole if the functionality you create does not require an instance of the class with which it is associated Example:
namespace UCRExample
{
    internal class AutoDoorBypassModule : CustomModule
    { }
}
namespace UCRExample
{
    internal class PetModule : CustomModule
    {
        public PetModule(SummonedCustomRole role) : base(role) 
        { }
    }
}

Adding a Custom Module to a SummonedCustomRole

Adding the Custom Module to the SummonedCustomRole instance you want is critical to enabling its functionality. For this you can interface to the UncomplicatedCustomRoles.AddModule<T>() of the class.

role.AddModule<PetModule>();

Managing a Custom Module

There are also functions dedicated to managing the Custom Module(s) present.

// Check if the instance has the PetModule
if (role.HasModule<PetModule>())
  // Present!

// Try to get the instance of the module PetModule
if (role.Get(out PetModule module))
  module.Execute();

// Get a list of modules with the same base
role.GetModules<ReallyNumerousModule>().Length; // omg 13!

// Get a single Custom Module
role.GetModule<PetModule>();

// Remove the module
role.RemoveModule<PetModule>();