Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
349b432
refactor!: update to 9.6.1 (#547)
BoltonDev Jun 13, 2025
bcd6121
Update release.yml
BoltonDev Jun 13, 2025
daaa1eb
Update release.yml
BoltonDev Jun 13, 2025
b8387ee
added spawnreason and flags
Bonjemus Jun 13, 2025
5e34e8a
Update release.yml
Bonjemus Jun 16, 2025
dfe4eb3
scale fix first attempt
Bonjemus Jun 16, 2025
0ddde3e
Merge pull request #1 from Bonjemus/scale-fix
Bonjemus Jun 16, 2025
98871ea
2 attempt
Bonjemus Jun 16, 2025
fec43de
Merge pull request #2 from Bonjemus/scale-fix
Bonjemus Jun 16, 2025
baf95b1
3 attempt
Bonjemus Jun 16, 2025
bd0393a
Merge pull request #3 from Bonjemus/scale-fix
Bonjemus Jun 16, 2025
6fc0ffe
4 attempt
Bonjemus Jun 16, 2025
b3bd38a
Merge pull request #4 from Bonjemus/scale-fix
Bonjemus Jun 16, 2025
680013d
Merge pull request #5 from ExMod-Team/dev
Bonjemus Jul 5, 2025
2f93296
Merge branch 'custolrole-spawnreasonflags' into dev
Bonjemus Jul 5, 2025
98dce59
Merge pull request #7 from Bonjemus/dev
Bonjemus Jul 5, 2025
a3a2377
the animatronic characters here do get a bit quirky at night
Bonjemus Jul 5, 2025
26505a6
Merge pull request #8 from Bonjemus/custolrole-spawnreasonflags
Bonjemus Jul 6, 2025
bca9f44
lil trolling
Bonjemus Jul 6, 2025
0a6b5d3
fixed all the funny
Bonjemus Jul 6, 2025
204816b
Merge branch 'master' into customitems-refactor
Bonjemus Jul 6, 2025
519f6c9
Revert "fixed all the funny"
Bonjemus Jul 6, 2025
1b7c164
Merge branch 'customitems-refactor' of https://github.com/Bonjemus/EX…
Bonjemus Jul 6, 2025
a79dd7c
Revert "fixed all the funny"
Bonjemus Jul 6, 2025
8c003bd
Fix Conflict for now
louis1706 Jul 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions EXILED/Exiled.API/Features/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ namespace Exiled.API.Features
public class Player : TypeCastObject<Player>, IEntity, IWorldSpace
{
#pragma warning disable SA1401
/// <summary>
/// A list of the player's CustomRole Ids.
/// </summary>
public readonly List<uint> CustomRoleIds = new();

/// <summary>
/// A list of the player's items.
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions EXILED/Exiled.CustomItems/API/Features/CustomArmor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.CustomItems/API/Features/CustomGrenade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public virtual Pickup Throw(Vector3 position, float force, float weight, float f
/// </summary>
/// <param name="grenade">The <see cref="Projectile">grenade</see> to check.</param>
/// <returns>True if it is a custom grenade.</returns>
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);

/// <inheritdoc/>
protected override void SubscribeEvents()
Expand Down
167 changes: 61 additions & 106 deletions EXILED/Exiled.CustomItems/API/Features/CustomItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -46,8 +44,9 @@ namespace Exiled.CustomItems.API.Features
/// </summary>
public abstract class CustomItem
{
private static Dictionary<string, CustomItem?> stringLookupTable = new();
private static Dictionary<uint, CustomItem?> idLookupTable = new();
private static readonly Dictionary<string, CustomItem?> StringLookupTable = new();
private static readonly Dictionary<uint, CustomItem?> IdLookupTable = new();
private static readonly Dictionary<ushort, CustomItem?> SerialLookupTable = new();

private ItemType type = ItemType.None;

Expand Down Expand Up @@ -101,12 +100,6 @@ public virtual ItemType Type
}
}

/// <summary>
/// Gets the list of custom items inside players' inventory being tracked as the current item.
/// </summary>
[YamlIgnore]
public HashSet<int> TrackedSerials { get; } = new();

/// <summary>
/// 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.
/// </summary>
Expand All @@ -120,9 +113,9 @@ public virtual ItemType Type
/// <returns>The <see cref="CustomItem"/> matching the search, <see langword="null"/> if not registered.</returns>
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];
}

/// <summary>
Expand All @@ -132,9 +125,9 @@ public virtual ItemType Type
/// <returns>The <see cref="CustomItem"/> matching the search, <see langword="null"/> if not registered.</returns>
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];
}

/// <summary>
Expand Down Expand Up @@ -221,47 +214,57 @@ public static bool TryGet(Player player, out IEnumerable<CustomItem>? customItem
return customItems.Any();
}

/// <summary>
/// Checks if this serial is a custom item.
/// </summary>
/// <param name="serial">The serial to check.</param>
/// <param name="customItem">The <see cref="CustomItem"/> this item is.</param>
/// <returns>True if the serial is a custom item.</returns>
public static bool TryGet(ushort serial, out CustomItem? customItem) => SerialLookupTable.TryGetValue(serial, out customItem);

/// <summary>
/// Checks to see if this item is a custom item.
/// </summary>
/// <param name="item">The <see cref="Item"/> to check.</param>
/// <param name="customItem">The <see cref="CustomItem"/> this item is.</param>
/// <returns>True if the item is a custom item.</returns>
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;
}
/// <exception cref="ArgumentNullException">Thrown if <paramref name="item"/> is <see langword="null"/>.</exception>
public static bool TryGet(Item item, out CustomItem? customItem) => TryGet(item.Serial, out customItem);

/// <summary>
/// Checks if this pickup is a custom item.
/// </summary>
/// <param name="pickup">The <see cref="ItemPickupBase"/> to check.</param>
/// <param name="customItem">The <see cref="CustomItem"/> this pickup is.</param>
/// <returns>True if the pickup is a custom item.</returns>
public static bool TryGet(Pickup pickup, out CustomItem? customItem)
{
customItem = Registered?.FirstOrDefault(tempCustomItem => tempCustomItem.TrackedSerials.Contains(pickup.Serial));
/// <exception cref="ArgumentNullException">Thrown if <paramref name="pickup"/> is <see langword="null"/>.</exception>
public static bool TryGet(Pickup pickup, out CustomItem? customItem) => TryGet(pickup.Serial, out customItem);

return customItem is not null;
}
/// <summary>
/// Tries to spawn a specific <see cref="CustomItem"/> at a specific <see cref="Vector3"/> position.
/// </summary>
/// <param name="id">The ID of the <see cref="CustomItem"/> to spawn.</param>
/// <param name="position">The <see cref="Vector3"/> location to spawn the item.</param>
/// <param name="pickup">The <see cref="ItemPickupBase"/> instance of the <see cref="CustomItem"/>.</param>
/// <returns>Returns a value indicating whether the <see cref="CustomItem"/> was spawned.</returns>
public static bool TrySpawn(uint id, Vector3 position, out Pickup? pickup) => TrySpawn(id, position, out pickup, out _);

/// <summary>
/// Tries to spawn a specific <see cref="CustomItem"/> at a specific <see cref="Vector3"/> position.
/// </summary>
/// <param name="id">The ID of the <see cref="CustomItem"/> to spawn.</param>
/// <param name="position">The <see cref="Vector3"/> location to spawn the item.</param>
/// <param name="pickup">The <see cref="ItemPickupBase"/> instance of the <see cref="CustomItem"/>.</param>
/// <param name="customItem">The <see cref="CustomItem"/>.</param>
/// <returns>Returns a value indicating whether the <see cref="CustomItem"/> was spawned.</returns>
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;
}
Expand Down Expand Up @@ -552,7 +555,7 @@ public static IEnumerable<CustomItem> UnregisterItems(IEnumerable<Type> targetTy
if (previousOwner is not null)
pickup.PreviousOwner = previousOwner;

TrackedSerials.Add(pickup.Serial);
SerialLookupTable[pickup.Serial] = this;

return pickup;
}
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -679,8 +681,8 @@ public virtual void Give(Player player, Item item, bool displayMessage = true)
/// </summary>
public virtual void Init()
{
stringLookupTable.Add(Name, this);
idLookupTable.Add(Id, this);
StringLookupTable.Add(Name, this);
IdLookupTable.Add(Id, this);

SubscribeEvents();
}
Expand All @@ -692,23 +694,23 @@ public virtual void Destroy()
{
UnsubscribeEvents();

stringLookupTable.Remove(Name);
idLookupTable.Remove(Id);
StringLookupTable.Remove(Name);
IdLookupTable.Remove(Id);
}

/// <summary>
/// Checks the specified pickup to see if it is a custom item.
/// </summary>
/// <param name="pickup">The <see cref="Pickup"/> to check.</param>
/// <returns>True if it is a custom item.</returns>
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);

/// <summary>
/// Checks the specified inventory item to see if it is a custom item.
/// </summary>
/// <param name="item">The <see cref="Item"/> to check.</param>
/// <returns>True if it is a custom item.</returns>
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);

/// <summary>
/// Checks the specified player's current item to see if it is a custom item.
Expand All @@ -717,6 +719,13 @@ public virtual void Destroy()
/// <returns>True if it is a custom item.</returns>
public virtual bool Check(Player? player) => Check(player?.CurrentItem);

/// <summary>
/// Checks the specified player's current item to see if it is a custom item.
/// </summary>
/// <param name="serial">The serial of the <see cref="Item"/>/<see cref="Pickup"/> to check.</param>
/// <returns>True if it is a custom item.</returns>
public virtual bool Check(ushort serial) => SerialLookupTable.TryGetValue(serial, out CustomItem? customItem) && customItem!.Id == Id;

/// <inheritdoc/>
public override string ToString() => $"[{Name} ({Type}) | {Id}] {Description}";

Expand Down Expand Up @@ -912,7 +921,7 @@ protected virtual void OnAcquired(Player player, Item item, bool displayMessage)
/// </summary>
protected virtual void OnWaitingForPlayers()
{
TrackedSerials.Clear();
SerialLookupTable.Clear();
}

/// <summary>
Expand All @@ -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));
}
}

Expand Down
6 changes: 3 additions & 3 deletions EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
if (previousOwner is not null)
pickup.PreviousOwner = previousOwner;

TrackedSerials.Add(pickup.Serial);
SerialLookupTable.Add(pickup.Serial);

Check failure on line 97 in EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs

View workflow job for this annotation

GitHub Actions / build

There is no argument given that corresponds to the required parameter 'value' of 'Dictionary<ushort, CustomItem?>.Add(ushort, CustomItem?)'

Check failure on line 97 in EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs

View workflow job for this annotation

GitHub Actions / build

'CustomItem.SerialLookupTable' is inaccessible due to its protection level
return pickup;
}

Expand All @@ -116,7 +116,7 @@
if (previousOwner is not null)
pickup.PreviousOwner = previousOwner;

TrackedSerials.Add(pickup.Serial);
SerialLookupTable.Add(pickup.Serial);

Check failure on line 119 in EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs

View workflow job for this annotation

GitHub Actions / build

There is no argument given that corresponds to the required parameter 'value' of 'Dictionary<ushort, CustomItem?>.Add(ushort, CustomItem?)'

Check failure on line 119 in EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs

View workflow job for this annotation

GitHub Actions / build

'CustomItem.SerialLookupTable' is inaccessible due to its protection level
return pickup;
}

Expand All @@ -138,7 +138,7 @@
}

Log.Debug($"{nameof(Give)}: Adding {item.Serial} to tracker.");
TrackedSerials.Add(item.Serial);
SerialLookupTable.Add(item.Serial);

Check failure on line 141 in EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs

View workflow job for this annotation

GitHub Actions / build

There is no argument given that corresponds to the required parameter 'value' of 'Dictionary<ushort, CustomItem?>.Add(ushort, CustomItem?)'

Check failure on line 141 in EXILED/Exiled.CustomItems/API/Features/CustomWeapon.cs

View workflow job for this annotation

GitHub Actions / build

'CustomItem.SerialLookupTable' is inaccessible due to its protection level

OnAcquired(player, item, displayMessage);
}
Expand Down
2 changes: 1 addition & 1 deletion EXILED/Exiled.CustomItems/Commands/List/Tracked.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,16 @@

foreach (CustomItem customItem in CustomItem.Registered)
{
if (customItem.TrackedSerials.Count == 0)

Check failure on line 65 in EXILED/Exiled.CustomItems/Commands/List/Tracked.cs

View workflow job for this annotation

GitHub Actions / build

'CustomItem' does not contain a definition for 'TrackedSerials' and no accessible extension method 'TrackedSerials' accepting a first argument of type 'CustomItem' could be found (are you missing a using directive or an assembly reference?)
continue;

message.AppendLine()
.Append('[').Append(customItem.Id).Append(". ").Append(customItem.Name).Append(" (").Append(customItem.Type).Append(')')
.Append(" {").Append(customItem.TrackedSerials.Count).AppendLine("}]").AppendLine();

Check failure on line 70 in EXILED/Exiled.CustomItems/Commands/List/Tracked.cs

View workflow job for this annotation

GitHub Actions / build

'CustomItem' does not contain a definition for 'TrackedSerials' and no accessible extension method 'TrackedSerials' accepting a first argument of type 'CustomItem' could be found (are you missing a using directive or an assembly reference?)

count += customItem.TrackedSerials.Count;

Check failure on line 72 in EXILED/Exiled.CustomItems/Commands/List/Tracked.cs

View workflow job for this annotation

GitHub Actions / build

'CustomItem' does not contain a definition for 'TrackedSerials' and no accessible extension method 'TrackedSerials' accepting a first argument of type 'CustomItem' could be found (are you missing a using directive or an assembly reference?)

foreach (int insideInventory in customItem.TrackedSerials)
foreach (int insideInventory in customItem.SerialLookupTable)

Check failure on line 74 in EXILED/Exiled.CustomItems/Commands/List/Tracked.cs

View workflow job for this annotation

GitHub Actions / build

'CustomItem.SerialLookupTable' is inaccessible due to its protection level
{
Player owner = Player.List.FirstOrDefault(player => player.Inventory.UserInventory.Items.Any(item => item.Key == insideInventory));

Expand Down
Loading