diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs index 779982d2fb..53bb0e6833 100644 --- a/EXILED/Exiled.API/Features/Player.cs +++ b/EXILED/Exiled.API/Features/Player.cs @@ -75,6 +75,11 @@ namespace Exiled.API.Features public class Player : TypeCastObject, IEntity, IWorldSpace { #pragma warning disable SA1401 + /// + /// A list of the player's CustomRole Ids. + /// + public readonly List CustomRoleIds = new(); + /// /// A list of the player's items. /// diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomArmor.cs b/EXILED/Exiled.CustomItems/API/Features/CustomArmor.cs index e6f1932905..7c881b4391 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomArmor.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomArmor.cs @@ -87,7 +87,7 @@ public override void Give(Player player, bool displayMessage = true) player.AddItem(armor); - TrackedSerials.Add(armor.Serial); + SerialLookupTable.Add(armor.Serial); Timing.CallDelayed(0.05f, () => OnAcquired(player, armor, displayMessage)); @@ -121,7 +121,7 @@ private void OnInternalPickingUpItem(PickingUpItemEventArgs ev) ev.IsAllowed = false; - TrackedSerials.Remove(ev.Pickup.Serial); + SerialLookupTable.Remove(ev.Pickup.Serial); ev.Pickup.Destroy(); Give(ev.Player); diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomGrenade.cs b/EXILED/Exiled.CustomItems/API/Features/CustomGrenade.cs index 48d725ce7e..1e0117d152 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomGrenade.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomGrenade.cs @@ -105,7 +105,7 @@ public virtual Pickup Throw(Vector3 position, float force, float weight, float f /// /// The grenade to check. /// True if it is a custom grenade. - public virtual bool Check(Projectile grenade) => grenade is not null && TrackedSerials.Contains(grenade.Serial); + public virtual bool Check(Projectile grenade) => grenade is not null && SerialLookupTable.Contains(grenade.Serial); /// protected override void SubscribeEvents() diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs b/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs index cf6d937b8b..6376dea578 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomItem.cs @@ -34,10 +34,8 @@ namespace Exiled.CustomItems.API.Features using static CustomItems; - using BaseFirearmPickup = InventorySystem.Items.Firearms.FirearmPickup; using Firearm = Exiled.API.Features.Items.Firearm; using Item = Exiled.API.Features.Items.Item; - using Map = Exiled.API.Features.Map; using Player = Exiled.API.Features.Player; using UpgradingPickupEventArgs = Exiled.Events.EventArgs.Scp914.UpgradingPickupEventArgs; @@ -46,8 +44,9 @@ namespace Exiled.CustomItems.API.Features /// public abstract class CustomItem { - private static Dictionary stringLookupTable = new(); - private static Dictionary idLookupTable = new(); + private static readonly Dictionary StringLookupTable = new(); + private static readonly Dictionary IdLookupTable = new(); + private static readonly Dictionary SerialLookupTable = new(); private ItemType type = ItemType.None; @@ -101,12 +100,6 @@ public virtual ItemType Type } } - /// - /// Gets the list of custom items inside players' inventory being tracked as the current item. - /// - [YamlIgnore] - public HashSet TrackedSerials { get; } = new(); - /// /// Gets a value indicating whether this item causes things to happen that may be considered hacks, and thus be shown to global moderators as being present in a player's inventory when they gban them. /// @@ -120,9 +113,9 @@ public virtual ItemType Type /// The matching the search, if not registered. public static CustomItem? Get(uint id) { - if (!idLookupTable.ContainsKey(id)) - idLookupTable.Add(id, Registered.FirstOrDefault(i => i.Id == id)); - return idLookupTable[id]; + if (!IdLookupTable.ContainsKey(id)) + IdLookupTable.Add(id, Registered.FirstOrDefault(i => i.Id == id)); + return IdLookupTable[id]; } /// @@ -132,9 +125,9 @@ public virtual ItemType Type /// The matching the search, if not registered. public static CustomItem? Get(string name) { - if (!stringLookupTable.ContainsKey(name)) - stringLookupTable.Add(name, Registered.FirstOrDefault(i => i.Name == name)); - return stringLookupTable[name]; + if (!StringLookupTable.ContainsKey(name)) + StringLookupTable.Add(name, Registered.FirstOrDefault(i => i.Name == name)); + return StringLookupTable[name]; } /// @@ -221,18 +214,22 @@ public static bool TryGet(Player player, out IEnumerable? customItem return customItems.Any(); } + /// + /// Checks if this serial is a custom item. + /// + /// The serial to check. + /// The this item is. + /// True if the serial is a custom item. + public static bool TryGet(ushort serial, out CustomItem? customItem) => SerialLookupTable.TryGetValue(serial, out customItem); + /// /// Checks to see if this item is a custom item. /// /// The to check. /// The this item is. /// True if the item is a custom item. - public static bool TryGet(Item item, out CustomItem? customItem) - { - customItem = item == null ? null : Registered?.FirstOrDefault(tempCustomItem => tempCustomItem.TrackedSerials.Contains(item.Serial)); - - return customItem is not null; - } + /// Thrown if is . + public static bool TryGet(Item item, out CustomItem? customItem) => TryGet(item.Serial, out customItem); /// /// Checks if this pickup is a custom item. @@ -240,12 +237,17 @@ public static bool TryGet(Item item, out CustomItem? customItem) /// The to check. /// The this pickup is. /// True if the pickup is a custom item. - public static bool TryGet(Pickup pickup, out CustomItem? customItem) - { - customItem = Registered?.FirstOrDefault(tempCustomItem => tempCustomItem.TrackedSerials.Contains(pickup.Serial)); + /// Thrown if is . + public static bool TryGet(Pickup pickup, out CustomItem? customItem) => TryGet(pickup.Serial, out customItem); - return customItem is not null; - } + /// + /// Tries to spawn a specific at a specific position. + /// + /// The ID of the to spawn. + /// The location to spawn the item. + /// The instance of the . + /// Returns a value indicating whether the was spawned. + public static bool TrySpawn(uint id, Vector3 position, out Pickup? pickup) => TrySpawn(id, position, out pickup, out _); /// /// Tries to spawn a specific at a specific position. @@ -253,15 +255,16 @@ public static bool TryGet(Pickup pickup, out CustomItem? customItem) /// The ID of the to spawn. /// The location to spawn the item. /// The instance of the . + /// The . /// Returns a value indicating whether the was spawned. - public static bool TrySpawn(uint id, Vector3 position, out Pickup? pickup) + public static bool TrySpawn(uint id, Vector3 position, out Pickup? pickup, out CustomItem? customItem) { pickup = default; - if (!TryGet(id, out CustomItem? item)) + if (!TryGet(id, out customItem)) return false; - pickup = item?.Spawn(position); + pickup = customItem?.Spawn(position); return true; } @@ -552,7 +555,7 @@ public static IEnumerable UnregisterItems(IEnumerable targetTy if (previousOwner is not null) pickup.PreviousOwner = previousOwner; - TrackedSerials.Add(pickup.Serial); + SerialLookupTable[pickup.Serial] = this; return pickup; } @@ -648,8 +651,7 @@ public virtual void Give(Player player, Item item, bool displayMessage = true) player.AddItem(item); Log.Debug($"{nameof(Give)}: Adding {item.Serial} to tracker."); - if (!TrackedSerials.Contains(item.Serial)) - TrackedSerials.Add(item.Serial); + SerialLookupTable[item.Serial] = this; Timing.CallDelayed(0.05f, () => OnAcquired(player, item, displayMessage)); } @@ -679,8 +681,8 @@ public virtual void Give(Player player, Item item, bool displayMessage = true) /// public virtual void Init() { - stringLookupTable.Add(Name, this); - idLookupTable.Add(Id, this); + StringLookupTable.Add(Name, this); + IdLookupTable.Add(Id, this); SubscribeEvents(); } @@ -692,8 +694,8 @@ public virtual void Destroy() { UnsubscribeEvents(); - stringLookupTable.Remove(Name); - idLookupTable.Remove(Id); + StringLookupTable.Remove(Name); + IdLookupTable.Remove(Id); } /// @@ -701,14 +703,14 @@ public virtual void Destroy() /// /// The to check. /// True if it is a custom item. - public virtual bool Check(Pickup? pickup) => pickup is not null && TrackedSerials.Contains(pickup.Serial); + public virtual bool Check(Pickup? pickup) => pickup is not null && Check(pickup.Serial); /// /// Checks the specified inventory item to see if it is a custom item. /// /// The to check. /// True if it is a custom item. - public virtual bool Check(Item? item) => item is not null && TrackedSerials.Contains(item.Serial); + public virtual bool Check(Item? item) => item is not null && Check(item.Serial); /// /// Checks the specified player's current item to see if it is a custom item. @@ -717,6 +719,13 @@ public virtual void Destroy() /// True if it is a custom item. public virtual bool Check(Player? player) => Check(player?.CurrentItem); + /// + /// Checks the specified player's current item to see if it is a custom item. + /// + /// The serial of the / to check. + /// True if it is a custom item. + public virtual bool Check(ushort serial) => SerialLookupTable.TryGetValue(serial, out CustomItem? customItem) && customItem!.Id == Id; + /// public override string ToString() => $"[{Name} ({Type}) | {Id}] {Description}"; @@ -912,7 +921,7 @@ protected virtual void OnAcquired(Player player, Item item, bool displayMessage) /// protected virtual void OnWaitingForPlayers() { - TrackedSerials.Clear(); + SerialLookupTable.Clear(); } /// @@ -937,91 +946,37 @@ protected virtual void ShowSelectedMessage(Player player) private void OnInternalOwnerChangingRole(ChangingRoleEventArgs ev) { - if (ev.Reason is SpawnReason.Escaped or SpawnReason.Destroyed) - return; - - foreach (Item item in ev.Player.Items.ToList()) + foreach (Item item in ev.Player.Items) { - if (!Check(item)) - continue; - - OnOwnerChangingRole(new OwnerChangingRoleEventArgs(item.Base, ev)); - - TrackedSerials.Remove(item.Serial); - - ev.Player.RemoveItem(item); - - Spawn(ev.Player, item, ev.Player); + if (Check(item)) + OnOwnerChangingRole(new OwnerChangingRoleEventArgs(item.Base, ev)); } - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); } private void OnInternalOwnerDying(DyingEventArgs ev) { - foreach (Item item in ev.Player.Items.ToList()) + foreach (Item item in ev.Player.Items) { - if (!Check(item)) - continue; - - OnOwnerDying(new OwnerDyingEventArgs(item, ev)); - - if (!ev.IsAllowed) - continue; - - ev.Player.RemoveItem(item); - - TrackedSerials.Remove(item.Serial); - - Spawn(ev.Player, item, ev.Player); - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); + if (Check(item)) + OnOwnerDying(new OwnerDyingEventArgs(item, ev)); } - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); } private void OnInternalOwnerEscaping(EscapingEventArgs ev) { - foreach (Item item in ev.Player.Items.ToList()) + foreach (Item item in ev.Player.Items) { - if (!Check(item)) - continue; - - OnOwnerEscaping(new OwnerEscapingEventArgs(item, ev)); - - if (!ev.IsAllowed) - continue; - - ev.Player.RemoveItem(item); - - TrackedSerials.Remove(item.Serial); - - Timing.CallDelayed(1.5f, () => Spawn(ev.Player.Position, item, null)); - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); + if (Check(item)) + OnOwnerEscaping(new OwnerEscapingEventArgs(item, ev)); } - - MirrorExtensions.ResyncSyncVar(ev.Player.ReferenceHub.networkIdentity, typeof(NicknameSync), nameof(NicknameSync.Network_myNickSync)); } private void OnInternalOwnerHandcuffing(HandcuffingEventArgs ev) { - foreach (Item item in ev.Target.Items.ToList()) + foreach (Item item in ev.Target.Items) { - if (!Check(item)) - continue; - - OnOwnerHandcuffing(new OwnerHandcuffingEventArgs(item, ev)); - - if (!ev.IsAllowed) - continue; - - ev.Target.RemoveItem(item); - - TrackedSerials.Remove(item.Serial); - - Spawn(ev.Target, item, ev.Target); + if (Check(item)) + OnOwnerHandcuffing(new OwnerHandcuffingEventArgs(item, ev)); } } diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs b/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs index 277037494e..9f338d5e7d 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs @@ -94,7 +94,7 @@ public override ItemType Type if (previousOwner is not null) pickup.PreviousOwner = previousOwner; - TrackedSerials.Add(pickup.Serial); + SerialLookupTable.Add(pickup.Serial); return pickup; } @@ -116,7 +116,7 @@ public override ItemType Type if (previousOwner is not null) pickup.PreviousOwner = previousOwner; - TrackedSerials.Add(pickup.Serial); + SerialLookupTable.Add(pickup.Serial); return pickup; } @@ -138,7 +138,7 @@ public override void Give(Player player, bool displayMessage = true) } Log.Debug($"{nameof(Give)}: Adding {item.Serial} to tracker."); - TrackedSerials.Add(item.Serial); + SerialLookupTable.Add(item.Serial); OnAcquired(player, item, displayMessage); } diff --git a/EXILED/Exiled.CustomItems/Commands/List/Tracked.cs b/EXILED/Exiled.CustomItems/Commands/List/Tracked.cs index c04e50fe64..511cd8a5ba 100644 --- a/EXILED/Exiled.CustomItems/Commands/List/Tracked.cs +++ b/EXILED/Exiled.CustomItems/Commands/List/Tracked.cs @@ -71,7 +71,7 @@ public bool Execute(ArraySegment arguments, ICommandSender sender, out s count += customItem.TrackedSerials.Count; - foreach (int insideInventory in customItem.TrackedSerials) + foreach (int insideInventory in customItem.SerialLookupTable) { Player owner = Player.List.FirstOrDefault(player => player.Inventory.UserInventory.Items.Any(item => item.Key == insideInventory)); diff --git a/EXILED/Exiled.CustomRoles/API/Extensions.cs b/EXILED/Exiled.CustomRoles/API/Extensions.cs index 8a0821216d..96099d5b4e 100644 --- a/EXILED/Exiled.CustomRoles/API/Extensions.cs +++ b/EXILED/Exiled.CustomRoles/API/Extensions.cs @@ -46,19 +46,8 @@ public static ReadOnlyCollection GetCustomRoles(this Player player) /// /// The to check. /// true if the player has at least one custom role; otherwise, false. - public static bool HasAnyCustomRole(this Player player) - { - if (player == null) - return false; - - foreach (CustomRole role in CustomRole.Registered) - { - if (role.Check(player)) - return true; - } - - return false; - } + /// Thrown when is . + public static bool HasAnyCustomRole(this Player player) => player.CustomRoleIds.Count > 0; /// /// Registers an of s. diff --git a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs index 895b483b2e..1f5746ac6b 100644 --- a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs +++ b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs @@ -37,13 +37,16 @@ namespace Exiled.CustomRoles.API.Features /// public abstract class CustomRole { - private const float AddRoleDelay = 0.25f; + /// + /// The delay after which ammo and items are added to the player. + /// + public const float AddRoleItemAndAmmoDelay = 0.25f; - private static Dictionary typeLookupTable = new(); + private static readonly Dictionary TypeLookupTable = new(); - private static Dictionary stringLookupTable = new(); + private static readonly Dictionary StringLookupTable = new(); - private static Dictionary idLookupTable = new(); + private static readonly Dictionary IdLookupTable = new(); /// /// Gets a list of all registered custom roles. @@ -86,6 +89,11 @@ public abstract class CustomRole /// public virtual RoleTypeId Role { get; set; } + /// + /// Gets or sets the valid to get this . + /// + public virtual SpawnReason[] ValidSpawnReasons { get; set; } = new[] { SpawnReason.RoundStart, SpawnReason.LateJoin, SpawnReason.Respawn, SpawnReason.Escaped }; + /// /// Gets or sets a list of the roles custom abilities. /// @@ -144,7 +152,7 @@ public abstract class CustomRole /// /// Gets or sets a value indicating broadcast that will be shown to the player. /// - public virtual Broadcast Broadcast { get; set; } = new Broadcast(); + public virtual Broadcast? Broadcast { get; set; } = new Broadcast(); /// /// 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. @@ -189,9 +197,9 @@ public abstract class CustomRole /// The role, or if it doesn't exist. public static CustomRole? Get(uint id) { - if (!idLookupTable.ContainsKey(id)) - idLookupTable.Add(id, Registered?.FirstOrDefault(r => r.Id == id)); - return idLookupTable[id]; + if (!IdLookupTable.ContainsKey(id)) + IdLookupTable.Add(id, Registered?.FirstOrDefault(r => r.Id == id)); + return IdLookupTable[id]; } /// @@ -201,9 +209,9 @@ public abstract class CustomRole /// The role, or if it doesn't exist. public static CustomRole? Get(Type t) { - if (!typeLookupTable.ContainsKey(t)) - typeLookupTable.Add(t, Registered?.FirstOrDefault(r => r.GetType() == t)); - return typeLookupTable[t]; + if (!TypeLookupTable.ContainsKey(t)) + TypeLookupTable.Add(t, Registered?.FirstOrDefault(r => r.GetType() == t)); + return TypeLookupTable[t]; } /// @@ -213,9 +221,9 @@ public abstract class CustomRole /// The role, or if it doesn't exist. public static CustomRole? Get(string name) { - if (!stringLookupTable.ContainsKey(name)) - stringLookupTable.Add(name, Registered?.FirstOrDefault(r => r.Name == name)); - return stringLookupTable[name]; + if (!StringLookupTable.ContainsKey(name)) + StringLookupTable.Add(name, Registered?.FirstOrDefault(r => r.Name == name)); + return StringLookupTable[name]; } /// @@ -487,9 +495,9 @@ public static void ForceSyncSetPlayerFriendlyFire(CustomRole roleToSync, Player /// public virtual void Init() { - idLookupTable.Add(Id, this); - typeLookupTable.Add(GetType(), this); - stringLookupTable.Add(Name, this); + IdLookupTable.Add(Id, this); + TypeLookupTable.Add(GetType(), this); + StringLookupTable.Add(Name, this); SubscribeEvents(); } @@ -498,9 +506,9 @@ public virtual void Init() /// public virtual void Destroy() { - idLookupTable.Remove(Id); - typeLookupTable.Remove(GetType()); - stringLookupTable.Remove(Name); + IdLookupTable.Remove(Id); + TypeLookupTable.Remove(GetType()); + StringLookupTable.Remove(Name); UnsubscribeEvents(); } @@ -508,58 +516,65 @@ public virtual void Destroy() /// Handles setup of the role, including spawn location, inventory and registering event handlers and add FF rules. /// /// The to add the role to. - public virtual void AddRole(Player player) + public virtual void AddRole(Player player) => AddRole(player, false, RoleSpawnFlags.All); + + /// + /// Handles setup of the role, including spawn location, inventory and registering event handlers and add FF rules. + /// + /// The to add the role to. + /// Whether it should use or not. + /// The to apply if is . + public virtual void AddRole(Player player, bool overrideFlags = false, RoleSpawnFlags overrideSpawnFlags = RoleSpawnFlags.All) { Log.Debug($"{Name}: Adding role to {player.Nickname}."); player.UniqueRole = Name; + player.CustomRoleIds.Add(Id); TrackedPlayers.Add(player); - if (Role != RoleTypeId.None) + RoleSpawnFlags keptSpawnFlags = overrideSpawnFlags; + + if (Role == RoleTypeId.None) { - if (KeepPositionOnSpawn) - { - if (KeepInventoryOnSpawn) - player.Role.Set(Role, SpawnReason.CustomRole, RoleSpawnFlags.None); - else - player.Role.Set(Role, SpawnReason.CustomRole, RoleSpawnFlags.AssignInventory); - } - else + keptSpawnFlags = RoleSpawnFlags.None; + } + else + { + if (!overrideFlags) { - if (KeepInventoryOnSpawn && player.IsAlive) - player.Role.Set(Role, SpawnReason.CustomRole, RoleSpawnFlags.UseSpawnpoint); + if (KeepPositionOnSpawn) + keptSpawnFlags = KeepInventoryOnSpawn ? RoleSpawnFlags.None : RoleSpawnFlags.AssignInventory; else - player.Role.Set(Role, SpawnReason.CustomRole, RoleSpawnFlags.All); + keptSpawnFlags = KeepInventoryOnSpawn && player.IsAlive ? RoleSpawnFlags.UseSpawnpoint : RoleSpawnFlags.All; } + + player.Role.Set(Role, SpawnReason.CustomRole, keptSpawnFlags); } player.UniqueRole = Name; - 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.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; @@ -569,9 +584,7 @@ public virtual void AddRole(Player player) fpcRole.Gravity = Gravity.Value; Vector3 position = GetSpawnPosition(); if (position != Vector3.zero) - { player.Position = position; - } Log.Debug($"{Name}: Setting player info"); @@ -588,7 +601,7 @@ public virtual void AddRole(Player player) ShowMessage(player); ShowBroadcast(player); - RoleAdded(player); + RoleAdded(player, keptSpawnFlags); player.TryAddCustomRoleFriendlyFire(Name, CustomRoleFFMultiplier); if (!string.IsNullOrEmpty(ConsoleMessage)) @@ -619,28 +632,33 @@ public virtual void AddRole(Player player) /// Removes the role from a specific player and FF rules. /// /// The to remove the role from. - public virtual void RemoveRole(Player player) + public virtual void RemoveRole(Player player) => RemoveRole(player, RoleSpawnFlags.All); + + /// + /// Removes the role from a specific player and FF rules. + /// + /// The to remove the role from. + /// The to apply. + public virtual void RemoveRole(Player player, RoleSpawnFlags roleSpawnFlags = RoleSpawnFlags.All) { - if (!TrackedPlayers.Contains(player)) + if (!player.CustomRoleIds.Remove(Id) || !TrackedPlayers.Remove(player)) return; + Log.Debug($"{Name}: Removing role from {player.Nickname}"); - TrackedPlayers.Remove(player); player.CustomInfo = string.Empty; player.InfoArea |= PlayerInfoArea.Role | PlayerInfoArea.Nickname; if (CustomAbilities is not null) { foreach (CustomAbility ability in CustomAbilities) - { ability.RemoveAbility(player); - } } - RoleRemoved(player); + RoleRemoved(player, roleSpawnFlags); player.UniqueRole = string.Empty; player.TryRemoveCustomeRoleFriendlyFire(Name); if (RemovalKillsPlayer) - player.Role.Set(RoleTypeId.Spectator); + player.Role.Set(RoleTypeId.Spectator, SpawnReason.CustomRole, roleSpawnFlags); } /// @@ -648,26 +666,13 @@ public virtual void RemoveRole(Player player) /// /// Role to add. /// Friendly fire multiplier. - public void SetFriendlyFire(RoleTypeId roleToAdd, float ffMult) - { - if (CustomRoleFFMultiplier.ContainsKey(roleToAdd)) - { - CustomRoleFFMultiplier[roleToAdd] = ffMult; - } - else - { - CustomRoleFFMultiplier.Add(roleToAdd, ffMult); - } - } + public void SetFriendlyFire(RoleTypeId roleToAdd, float ffMult) => CustomRoleFFMultiplier[roleToAdd] = ffMult; /// /// Wrapper to call . /// /// Role with FF to add even if it exists. - public void SetFriendlyFire(KeyValuePair roleFF) - { - SetFriendlyFire(roleFF.Key, roleFF.Value); - } + public void SetFriendlyFire(KeyValuePair roleFF) => SetFriendlyFire(roleFF.Key, roleFF.Value); /// /// Tries to add to CustomRole FriendlyFire rules. @@ -678,9 +683,7 @@ public void SetFriendlyFire(KeyValuePair roleFF) public bool TryAddFriendlyFire(RoleTypeId roleToAdd, float ffMult) { if (CustomRoleFFMultiplier.ContainsKey(roleToAdd)) - { return false; - } CustomRoleFFMultiplier.Add(roleToAdd, ffMult); return true; @@ -726,9 +729,7 @@ public bool TryAddFriendlyFire(Dictionary ffRules, bool overw if (!overwrite) { foreach (KeyValuePair roleFF in temporaryFriendlyFireRules) - { TryAddFriendlyFire(roleFF); - } } DictionaryPool.Pool.Return(temporaryFriendlyFireRules); @@ -925,12 +926,29 @@ protected virtual void UnsubscribeEvents() /// Shows the spawn broadcast to the player. /// /// The to show the message to. - 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); + } + + /// + /// Called after the role has been added to the player. + /// + /// The the role was added to. + /// The to apply. + protected virtual void RoleAdded(Player player, RoleSpawnFlags roleSpawnFlags) + { +#pragma warning disable CS0618 + RoleAdded(player); +#pragma warning restore CS0618 + } /// /// Called after the role has been added to the player. /// /// The the role was added to. + [Obsolete("Use RoleAdded(Player, SpawnReason, RoleSpawnFlags) instead.")] protected virtual void RoleAdded(Player player) { } @@ -939,6 +957,19 @@ protected virtual void RoleAdded(Player player) /// Called 1 frame before the role is removed from the player. /// /// The the role was removed from. + /// The to apply. + protected virtual void RoleRemoved(Player player, RoleSpawnFlags roleSpawnFlags) + { +#pragma warning disable CS0618 + RoleRemoved(player); +#pragma warning restore CS0618 + } + + /// + /// Called 1 frame before the role is removed from the player. + /// + /// The the role was removed from. + [Obsolete("Use RoleRemoved(Player, SpawnReason, RoleSpawnFlags) instead.")] protected virtual void RoleRemoved(Player player) { } @@ -952,7 +983,7 @@ private void OnInternalChangingNickname(ChangingNicknameEventArgs ev) private void OnInternalChangingRole(ChangingRoleEventArgs ev) { if (ev.IsAllowed && ev.Reason is not(SpawnReason.Destroyed or SpawnReason.CustomRole) && Check(ev.Player) && ((ev.NewRole == RoleTypeId.Spectator && !KeepRoleOnDeath) || (ev.NewRole != RoleTypeId.Spectator && !KeepRoleOnChangingRole))) - RemoveRole(ev.Player); + RemoveRole(ev.Player, ev.SpawnFlags); } private void OnSpawningRagdoll(SpawningRagdollEventArgs ev) @@ -963,4 +994,4 @@ private void OnSpawningRagdoll(SpawningRagdollEventArgs ev) private void OnDestroying(DestroyingEventArgs ev) => RemoveRole(ev.Player); } -} +} \ No newline at end of file diff --git a/EXILED/Exiled.CustomRoles/CustomRoles.cs b/EXILED/Exiled.CustomRoles/CustomRoles.cs index a66457b3bd..db0cd49a06 100644 --- a/EXILED/Exiled.CustomRoles/CustomRoles.cs +++ b/EXILED/Exiled.CustomRoles/CustomRoles.cs @@ -15,7 +15,6 @@ namespace Exiled.CustomRoles using Exiled.CustomRoles.Events; using Exiled.Loader; using Exiled.Loader.Features.Configs.CustomConverters; - using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; @@ -25,7 +24,7 @@ namespace Exiled.CustomRoles /// public class CustomRoles : Plugin { - private PlayerHandlers? playerHandlers; + private PlayerHandler? playerHandlers; private KeypressActivator? keypressActivator; /// @@ -58,25 +57,21 @@ public CustomRoles() public override void OnEnabled() { Instance = this; - playerHandlers = new PlayerHandlers(this); + playerHandlers = new PlayerHandler(this); if (Config.UseKeypressActivation) keypressActivator = new(); - Exiled.Events.Handlers.Player.Spawned += playerHandlers.OnSpawned; - Exiled.Events.Handlers.Player.SpawningRagdoll += playerHandlers.OnSpawningRagdoll; + playerHandlers.Register(); - Exiled.Events.Handlers.Server.WaitingForPlayers += playerHandlers.OnWaitingForPlayers; base.OnEnabled(); } /// public override void OnDisabled() { - Exiled.Events.Handlers.Player.Spawned -= playerHandlers!.OnSpawned; - Exiled.Events.Handlers.Player.SpawningRagdoll -= playerHandlers!.OnSpawningRagdoll; - - Exiled.Events.Handlers.Server.WaitingForPlayers -= playerHandlers!.OnWaitingForPlayers; + playerHandlers!.Unregister(); + playerHandlers = null; keypressActivator = null; base.OnDisabled(); diff --git a/EXILED/Exiled.CustomRoles/Events/PlayerHandlers.cs b/EXILED/Exiled.CustomRoles/Events/PlayerHandler.cs similarity index 64% rename from EXILED/Exiled.CustomRoles/Events/PlayerHandlers.cs rename to EXILED/Exiled.CustomRoles/Events/PlayerHandler.cs index 1a10e38dcf..e90b159de8 100644 --- a/EXILED/Exiled.CustomRoles/Events/PlayerHandlers.cs +++ b/EXILED/Exiled.CustomRoles/Events/PlayerHandler.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright (c) ExMod Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. // @@ -9,10 +9,8 @@ namespace Exiled.CustomRoles.Events { using System; using System.Collections.Generic; - using System.Threading; using Exiled.API.Enums; - using Exiled.API.Features; using Exiled.CustomRoles.API; using Exiled.CustomRoles.API.Features; using Exiled.Events.EventArgs.Player; @@ -20,7 +18,7 @@ namespace Exiled.CustomRoles.Events /// /// Handles general events for players. /// - public class PlayerHandlers + internal sealed class PlayerHandler { private static readonly HashSet ValidSpawnReasons = new() { @@ -35,47 +33,62 @@ public class PlayerHandlers private readonly CustomRoles plugin; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The plugin instance. - public PlayerHandlers(CustomRoles plugin) + internal PlayerHandler(CustomRoles plugin) { this.plugin = plugin; } + /// + /// Registers the events. + /// + internal void Register() + { + Exiled.Events.Handlers.Player.Spawned += OnSpawned; + Exiled.Events.Handlers.Player.SpawningRagdoll += OnSpawningRagdoll; + + Exiled.Events.Handlers.Server.WaitingForPlayers += OnWaitingForPlayers; + } + + /// + /// Unregisters the events. + /// + internal void Unregister() + { + Exiled.Events.Handlers.Player.Spawned -= OnSpawned; + Exiled.Events.Handlers.Player.SpawningRagdoll -= OnSpawningRagdoll; + + Exiled.Events.Handlers.Server.WaitingForPlayers -= OnWaitingForPlayers; + } + /// - internal void OnWaitingForPlayers() + private void OnWaitingForPlayers() { foreach (CustomRole role in CustomRole.Registered) - { role.SpawnedPlayers = 0; - } } /// - internal void OnSpawningRagdoll(SpawningRagdollEventArgs ev) + private void OnSpawningRagdoll(SpawningRagdollEventArgs ev) { - if (plugin.StopRagdollPlayers.Contains(ev.Player)) - { + if (plugin.StopRagdollPlayers.Remove(ev.Player)) ev.IsAllowed = false; - plugin.StopRagdollPlayers.Remove(ev.Player); - } } /// - internal void OnSpawned(SpawnedEventArgs ev) + private void OnSpawned(SpawnedEventArgs ev) { - if (!ValidSpawnReasons.Contains(ev.Reason) || ev.Player.HasAnyCustomRole()) - { + if (ev.Player == null || !ValidSpawnReasons.Contains(ev.Reason) || ev.Player.HasAnyCustomRole()) return; - } float totalChance = 0f; List eligibleRoles = new(8); foreach (CustomRole role in CustomRole.Registered) { - if (role.Role == ev.Player.Role.Type && !role.IgnoreSpawnSystem && role.SpawnChance > 0 && !role.Check(ev.Player) && (role.SpawnProperties is null || role.SpawnedPlayers < role.SpawnProperties.Limit)) + if (!role.IgnoreSpawnSystem && role.Role == ev.Player.Role.Type && role.ValidSpawnReasons.Contains(ev.Reason) && role.SpawnChance > 0 && !role.Check(ev.Player) && (role.SpawnProperties is null || role.SpawnedPlayers < role.SpawnProperties.Limit)) { eligibleRoles.Add(role); totalChance += role.SpawnChance; @@ -83,17 +96,13 @@ internal void OnSpawned(SpawnedEventArgs ev) } if (eligibleRoles.Count == 0) - { return; - } float lotterySize = Math.Max(100f, totalChance); float randomRoll = (float)Loader.Loader.Random.NextDouble() * lotterySize; if (randomRoll >= totalChance) - { return; - } foreach (CustomRole candidateRole in eligibleRoles) { @@ -105,14 +114,14 @@ internal void OnSpawned(SpawnedEventArgs ev) if (candidateRole.SpawnProperties is null) { - candidateRole.AddRole(ev.Player); + candidateRole.AddRole(ev.Player, false, ev.SpawnFlags); break; } int newSpawnCount = candidateRole.SpawnedPlayers++; if (newSpawnCount <= candidateRole.SpawnProperties.Limit) { - candidateRole.AddRole(ev.Player); + candidateRole.AddRole(ev.Player, false, ev.SpawnFlags); break; } else