Skip to content
This repository was archived by the owner on Jul 8, 2025. It is now read-only.
Closed
118 changes: 80 additions & 38 deletions EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ namespace Exiled.CustomRoles.API.Features
/// </summary>
public abstract class CustomRole
{
private const float AddRoleDelay = 0.25f;
/// <summary>
/// The delay after which ammo and items are added to the player.
/// </summary>
public const float AddRoleItemAndAmmoDelay = 0.25f;

private static Dictionary<Type, CustomRole?> typeLookupTable = new();

Expand Down Expand Up @@ -143,7 +146,7 @@ public abstract class CustomRole
/// <summary>
/// Gets or sets a value indicating broadcast that will be shown to the player.
/// </summary>
public virtual Broadcast Broadcast { get; set; } = new Broadcast();
public virtual Broadcast? Broadcast { get; set; } = new Broadcast();

/// <summary>
/// Gets or sets a value indicating whether players will receive a message for getting a custom item, when gaining it through the inventory config for this role.
Expand Down Expand Up @@ -496,58 +499,65 @@ public virtual void Destroy()
/// Handles setup of the role, including spawn location, inventory and registering event handlers and add FF rules.
/// </summary>
/// <param name="player">The <see cref="Player"/> to add the role to.</param>
public virtual void AddRole(Player player)
public virtual void AddRole(Player player) => AddRole(player, SpawnReason.ForceClass, false, RoleSpawnFlags.All);

/// <summary>
/// Handles setup of the role, including spawn location, inventory and registering event handlers and add FF rules.
/// </summary>
/// <param name="player">The <see cref="Player"/> to add the role to.</param>
/// <param name="spawnReason">The <see cref="SpawnReason"/>.</param>
/// <param name="overrideFlags">Whether it should use <paramref name="overrideSpawnFlags"/> or not.</param>
/// <param name="overrideSpawnFlags">The <see cref="RoleSpawnFlags"/> to apply if <paramref name="overrideFlags"/> is <see langword="true"/>.</param>
public virtual void AddRole(Player player, SpawnReason spawnReason = SpawnReason.ForceClass, bool overrideFlags = false, RoleSpawnFlags overrideSpawnFlags = RoleSpawnFlags.All)
{
Log.Debug($"{Name}: Adding role to {player.Nickname}.");
player.UniqueRole = Name;

if (Role != RoleTypeId.None)
RoleSpawnFlags keptSpawnFlags = overrideSpawnFlags;

if (Role == RoleTypeId.None)
{
if (KeepPositionOnSpawn)
{
if (KeepInventoryOnSpawn)
player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.None);
else
player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.AssignInventory);
}
else
keptSpawnFlags = RoleSpawnFlags.None;
}
else
{
if (!overrideFlags)
{
if (KeepInventoryOnSpawn && player.IsAlive)
player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.UseSpawnpoint);
if (KeepPositionOnSpawn)
keptSpawnFlags = KeepInventoryOnSpawn ? RoleSpawnFlags.None : RoleSpawnFlags.AssignInventory;
else
player.Role.Set(Role, SpawnReason.ForceClass, RoleSpawnFlags.All);
keptSpawnFlags = KeepInventoryOnSpawn && player.IsAlive ? RoleSpawnFlags.UseSpawnpoint : RoleSpawnFlags.All;
}

player.Role.Set(Role, spawnReason, keptSpawnFlags);
}

player.UniqueRole = Name;
TrackedPlayers.Add(player);

Timing.CallDelayed(
AddRoleDelay,
() =>
{
if (!KeepInventoryOnSpawn)
if (keptSpawnFlags.HasFlag(RoleSpawnFlags.AssignInventory))
{
Timing.CallDelayed(
AddRoleItemAndAmmoDelay,
() =>
{
Log.Debug($"{Name}: Clearing {player.Nickname}'s inventory.");
player.ClearInventory();
}

foreach (string itemName in Inventory)
{
Log.Debug($"{Name}: Adding {itemName} to inventory.");
TryAddItem(player, itemName);
}
foreach (string itemName in Inventory)
{
Log.Debug($"{Name}: Adding {itemName} to inventory.");
TryAddItem(player, itemName);
}

if (Ammo.Count > 0)
{
Log.Debug($"{Name}: Adding Ammo to {player.Nickname} inventory.");
foreach (AmmoType type in EnumUtils<AmmoType>.Values)
{
if (type != AmmoType.None)
player.SetAmmo(type, Ammo.ContainsKey(type) ? Ammo[type] == ushort.MaxValue ? InventoryLimits.GetAmmoLimit(type.GetItemType(), player.ReferenceHub) : Ammo[type] : (ushort)0);
}
}
});
});
}

Log.Debug($"{Name}: Setting health values.");
player.Health = MaxHealth;
Expand All @@ -556,9 +566,7 @@ public virtual void AddRole(Player player)

Vector3 position = GetSpawnPosition();
if (position != Vector3.zero)
{
player.Position = position;
}

Log.Debug($"{Name}: Setting player info");

Expand All @@ -573,7 +581,7 @@ public virtual void AddRole(Player player)

ShowMessage(player);
ShowBroadcast(player);
RoleAdded(player);
RoleAdded(player, spawnReason, keptSpawnFlags);
player.TryAddCustomRoleFriendlyFire(Name, CustomRoleFFMultiplier);

if (!string.IsNullOrEmpty(ConsoleMessage))
Expand Down Expand Up @@ -604,7 +612,9 @@ public virtual void AddRole(Player player)
/// Removes the role from a specific player and FF rules.
/// </summary>
/// <param name="player">The <see cref="Player"/> to remove the role from.</param>
public virtual void RemoveRole(Player player)
/// <param name="spawnReason">The <see cref="SpawnReason"/>.</param>
/// <param name="roleSpawnFlags">The <see cref="RoleSpawnFlags"/> to apply.</param>
public virtual void RemoveRole(Player player, SpawnReason spawnReason = SpawnReason.ForceClass, RoleSpawnFlags roleSpawnFlags = RoleSpawnFlags.All)
{
if (!TrackedPlayers.Contains(player))
return;
Expand All @@ -621,12 +631,12 @@ public virtual void RemoveRole(Player player)
}
}

RoleRemoved(player);
RoleRemoved(player, spawnReason, roleSpawnFlags);
player.UniqueRole = string.Empty;
player.TryRemoveCustomeRoleFriendlyFire(Name);

if (RemovalKillsPlayer)
player.Role.Set(RoleTypeId.Spectator);
player.Role.Set(RoleTypeId.Spectator, spawnReason, roleSpawnFlags);
}

/// <summary>
Expand Down Expand Up @@ -891,12 +901,30 @@ protected virtual void UnsubscribeEvents()
/// Shows the spawn broadcast to the player.
/// </summary>
/// <param name="player">The <see cref="Player"/> to show the message to.</param>
protected virtual void ShowBroadcast(Player player) => player.Broadcast(Broadcast);
protected virtual void ShowBroadcast(Player player)
{
if (Broadcast != null && Broadcast.Duration > 0 && !string.IsNullOrEmpty(Broadcast.Content))
player.Broadcast(Broadcast);
}

/// <summary>
/// Called after the role has been added to the player.
/// </summary>
/// <param name="player">The <see cref="Player"/> the role was added to.</param>
/// <param name="spawnReason">The <see cref="SpawnReason"/>.</param>
/// <param name="roleSpawnFlags">The <see cref="RoleSpawnFlags"/> to apply.</param>
protected virtual void RoleAdded(Player player, SpawnReason spawnReason, RoleSpawnFlags roleSpawnFlags)
{
#pragma warning disable CS0618
RoleAdded(player);
#pragma warning restore CS0618
}

/// <summary>
/// Called after the role has been added to the player.
/// </summary>
/// <param name="player">The <see cref="Player"/> the role was added to.</param>
[Obsolete("Use RoleAdded(Player, SpawnReason, RoleSpawnFlags) instead.")]
protected virtual void RoleAdded(Player player)
{
}
Expand All @@ -905,6 +933,20 @@ protected virtual void RoleAdded(Player player)
/// Called 1 frame before the role is removed from the player.
/// </summary>
/// <param name="player">The <see cref="Player"/> the role was removed from.</param>
/// <param name="spawnReason">The <see cref="SpawnReason"/>.</param>
/// <param name="roleSpawnFlags">The <see cref="RoleSpawnFlags"/> to apply.</param>
protected virtual void RoleRemoved(Player player, SpawnReason spawnReason, RoleSpawnFlags roleSpawnFlags)
{
#pragma warning disable CS0618
RoleRemoved(player);
#pragma warning restore CS0618
}

/// <summary>
/// Called 1 frame before the role is removed from the player.
/// </summary>
/// <param name="player">The <see cref="Player"/> the role was removed from.</param>
[Obsolete("Use RoleRemoved(Player, SpawnReason, RoleSpawnFlags) instead.")]
protected virtual void RoleRemoved(Player player)
{
}
Expand All @@ -924,7 +966,7 @@ private void OnInternalSpawned(SpawnedEventArgs ev)
private void OnInternalChangingRole(ChangingRoleEventArgs ev)
{
if (ev.IsAllowed && ev.Reason != SpawnReason.Destroyed && Check(ev.Player) && ((ev.NewRole == RoleTypeId.Spectator && !KeepRoleOnDeath) || (ev.NewRole != RoleTypeId.Spectator && !KeepRoleOnChangingRole)))
RemoveRole(ev.Player);
RemoveRole(ev.Player, ev.Reason, ev.SpawnFlags);
}

private void OnSpawningRagdoll(SpawningRagdollEventArgs ev)
Expand Down
1 change: 0 additions & 1 deletion EXILED/Exiled.Events/Patches/Events/Player/Aiming.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace Exiled.Events.Patches.Events.Player
using System.Collections.Generic;
using System.Reflection.Emit;

using Exiled.API.Extensions;
using Exiled.API.Features.Pools;

using Exiled.Events.Attributes;
Expand Down
Loading