diff --git a/Exiled.API/Features/Lockers/Chamber.cs b/Exiled.API/Features/Lockers/Chamber.cs
new file mode 100644
index 0000000000..02a128d356
--- /dev/null
+++ b/Exiled.API/Features/Lockers/Chamber.cs
@@ -0,0 +1,185 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Features.Lockers
+{
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+
+ using Exiled.API.Enums;
+ using Exiled.API.Features.Core;
+ using Exiled.API.Features.Pickups;
+ using Exiled.API.Interfaces;
+ using MapGeneration.Distributors;
+ using UnityEngine;
+
+ ///
+ /// A wrapper for .
+ ///
+ public class Chamber : GameEntity, IWrapper
+ {
+ ///
+ /// with and .
+ ///
+ internal static readonly Dictionary Chambers = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// instance.
+ /// where this chamber is located.
+ public Chamber(LockerChamber chamber, Locker locker)
+ : base(chamber.gameObject)
+ {
+ Base = chamber;
+ Locker = locker;
+
+ Chambers.Add(chamber, this);
+ }
+
+ ///
+ /// Gets all chambers.
+ ///
+ public static new IReadOnlyCollection List => Chambers.Values;
+
+ ///
+ public LockerChamber Base { get; }
+
+ ///
+ /// Gets or sets all pickups that should be spawned when the door is initially opened.
+ ///
+ public IEnumerable ToBeSpawned
+ {
+ get => Base._toBeSpawned.Select(Pickup.Get);
+ set
+ {
+ Base._toBeSpawned.Clear();
+
+ foreach (Pickup pickup in value)
+ Base._toBeSpawned.Add(pickup.Base);
+ }
+ }
+
+ ///
+ /// Gets or sets all pickups in the chamber.
+ ///
+ public IEnumerable AllPickups
+ {
+ get => Base._content.Select(Pickup.Get);
+ set
+ {
+ Base._content.Clear();
+
+ foreach (Pickup pickup in value)
+ Base._content.Add(pickup.Base);
+ }
+ }
+
+ ///
+ /// Gets or sets all spawn points.
+ ///
+ ///
+ /// Used if is set to .
+ ///
+ public IEnumerable Spawnpoints
+ {
+ get => Base._spawnpoints;
+ set => Base._spawnpoints = value.ToArray();
+ }
+
+ ///
+ /// Gets or sets all the acceptable items which can be spawned in this chamber.
+ ///
+ public IEnumerable AcceptableTypes
+ {
+ get => Base.AcceptableItems;
+ set => Base.AcceptableItems = value.ToArray();
+ }
+
+ ///
+ /// Gets or sets required permissions to open this chamber.
+ ///
+ public KeycardPermissions RequiredPermissions
+ {
+ get => (KeycardPermissions)Base.RequiredPermissions;
+ set => Base.RequiredPermissions = (Interactables.Interobjects.DoorUtils.KeycardPermissions)value;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether multiple spawn points should be used.
+ ///
+ ///
+ /// If , will be used over .
+ ///
+ public bool UseMultipleSpawnpoints
+ {
+ get => Base._useMultipleSpawnpoints;
+ set => Base._useMultipleSpawnpoints = value;
+ }
+
+ ///
+ /// Gets or sets a spawn point for the items in the chamber.
+ ///
+ ///
+ /// Used if is set to .
+ ///
+ public Transform Spawnpoint
+ {
+ get => Base._spawnpoint;
+ set => Base._spawnpoint = value;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether or not items should be spawned as soon as they are initialized.
+ ///
+ public bool InitiallySpawn
+ {
+ get => Base._spawnOnFirstChamberOpening;
+ set => Base._spawnOnFirstChamberOpening = value;
+ }
+
+ ///
+ /// Gets or sets the amount of time before a player can interact with the chamber again.
+ ///
+ public float Cooldown
+ {
+ get => Base._targetCooldown;
+ set => Base._targetCooldown = value;
+ }
+
+ ///
+ /// Gets the of current cooldown.
+ ///
+ /// Used in check.
+ public Stopwatch CurrentCooldown => Base._stopwatch;
+
+ ///
+ /// Gets a value indicating whether the chamber is interactable.
+ ///
+ public bool CanInteract => Base.CanInteract;
+
+ ///
+ /// Gets the locker where this chamber is located at.
+ ///
+ public Locker Locker { get; }
+
+ ///
+ /// Spawns a specified item from .
+ ///
+ /// from .
+ /// Amount of items that should be spawned.
+ public void SpawnItem(ItemType type, int amount) => Base.SpawnItem(type, amount);
+
+ ///
+ /// Gets the chamber by its .
+ ///
+ /// .
+ /// .
+ internal static Chamber Get(LockerChamber chamber) => Chambers.TryGetValue(chamber, out Chamber chmb) ? chmb : new(chamber, Locker.Get(x => x.Chambers.Any(x => x.Base == chamber)).FirstOrDefault());
+ }
+}
\ No newline at end of file
diff --git a/Exiled.API/Features/Lockers/Locker.cs b/Exiled.API/Features/Lockers/Locker.cs
new file mode 100644
index 0000000000..9e78366147
--- /dev/null
+++ b/Exiled.API/Features/Lockers/Locker.cs
@@ -0,0 +1,112 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Features.Lockers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+
+ using Exiled.API.Extensions;
+ using Exiled.API.Features.Core;
+ using Exiled.API.Interfaces;
+ using MapGeneration.Distributors;
+
+ using BaseLocker = MapGeneration.Distributors.Locker;
+
+ ///
+ /// Represents a basic locker.
+ ///
+ public class Locker : GameEntity, IWrapper
+ {
+ ///
+ /// with and .
+ ///
+ internal static readonly Dictionary BaseToExiledLockers = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The instance.
+ public Locker(BaseLocker locker)
+ : base(locker.gameObject)
+ {
+ Base = locker;
+ Chambers = locker.Chambers.Select(x => new Chamber(x, this)).ToList();
+
+ BaseToExiledLockers.Add(locker, this);
+ }
+
+ ///
+ /// Gets the all instances.
+ ///
+ public static new IReadOnlyCollection List => BaseToExiledLockers.Values;
+
+ ///
+ public BaseLocker Base { get; }
+
+ ///
+ /// Gets or sets all instances in this locker.
+ ///
+ public IEnumerable Loot
+ {
+ get => Base.Loot;
+ set => Base.Loot = value.ToArray();
+ }
+
+ ///
+ /// Gets the all in this locker.
+ ///
+ public IReadOnlyCollection Chambers { get; }
+
+ ///
+ /// Gets or sets an id for manipulating opened chambers.
+ ///
+ public ushort OpenedChambers
+ {
+ get => Base.OpenedChambers;
+ set => Base.NetworkOpenedChambers = value;
+ }
+
+ ///
+ /// Gets the given the instance.
+ ///
+ /// instance.
+ /// instance.
+ public static Locker Get(BaseLocker locker) => BaseToExiledLockers.TryGetValue(locker, out Locker lk)
+ ? lk
+ : locker switch
+ {
+ PedestalScpLocker psl => new PedestalLocker(psl),
+ _ => new Locker(locker)
+ };
+
+ ///
+ /// Gets the all instances matching the predicate.
+ ///
+ /// Predicate to match.
+ /// All instances matching the predicate.
+ public static IEnumerable Get(System.Func predicate) => List.Where(predicate);
+
+ ///
+ /// Interacts with a specific chamber.
+ ///
+ /// If , the interaction will be randomized.
+ /// The player who interacts.
+ public void Interact(Chamber chamber = null, Player player = null)
+ {
+ chamber ??= Chambers.Random();
+
+ Base.ServerInteract(player?.ReferenceHub, (byte)Chambers.ToList().IndexOf(chamber));
+ }
+
+ ///
+ /// Fills the chamber.
+ ///
+ /// Chamber to fill.
+ public void FillChamber(Chamber chamber) => Base.FillChamber(chamber.Base);
+ }
+}
diff --git a/Exiled.API/Features/Lockers/PedestalLocker.cs b/Exiled.API/Features/Lockers/PedestalLocker.cs
new file mode 100644
index 0000000000..a01293ff2b
--- /dev/null
+++ b/Exiled.API/Features/Lockers/PedestalLocker.cs
@@ -0,0 +1,31 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Features.Lockers
+{
+ using Exiled.API.Interfaces;
+ using MapGeneration.Distributors;
+
+ ///
+ /// Represents a pedestal.
+ ///
+ public class PedestalLocker : Locker, IWrapper
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// instance.
+ public PedestalLocker(PedestalScpLocker locker)
+ : base(locker)
+ {
+ Base = locker;
+ }
+
+ ///
+ public new PedestalScpLocker Base { get; }
+ }
+}
\ No newline at end of file
diff --git a/Exiled.API/Features/Map.cs b/Exiled.API/Features/Map.cs
index 27856d6d58..03a32857c3 100644
--- a/Exiled.API/Features/Map.cs
+++ b/Exiled.API/Features/Map.cs
@@ -16,6 +16,7 @@ namespace Exiled.API.Features
using Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Hazards;
+ using Exiled.API.Features.Lockers;
using Exiled.API.Features.Pickups;
using Exiled.API.Features.Scp914Processors;
using Exiled.API.Features.Toys;
@@ -27,7 +28,6 @@ namespace Exiled.API.Features
using Items;
using LightContainmentZoneDecontamination;
using MapGeneration;
- using MapGeneration.Distributors;
using Mirror;
using PlayerRoles;
using PlayerRoles.PlayableScps.Scp173;
@@ -45,11 +45,6 @@ namespace Exiled.API.Features
///
public static class Map
{
- ///
- /// A list of s on the map.
- ///
- internal static readonly List LockersValue = new(35);
-
///
/// A list of s on the map.
///
@@ -86,11 +81,6 @@ public static DecontaminationController.DecontaminationStatus DecontaminationOve
///
public static ReadOnlyCollection PocketDimensionTeleports { get; } = TeleportsValue.AsReadOnly();
- ///
- /// Gets all objects.
- ///
- public static ReadOnlyCollection Lockers { get; } = LockersValue.AsReadOnly();
-
///
/// Gets all objects.
///
@@ -218,7 +208,7 @@ public static void ResetLightsColor()
/// Gets a random .
///
/// object.
- public static Locker GetRandomLocker() => Lockers.Random();
+ public static Locker GetRandomLocker() => Locker.List.Random();
///
/// Gets a random .
@@ -355,15 +345,18 @@ internal static void ClearCache()
{
Item.BaseToItem.Clear();
- LockersValue.RemoveAll(locker => locker == null);
-
Ragdoll.BasicRagdollToRagdoll.Clear();
Firearm.ItemTypeToFirearmInstance.Clear();
Firearm.BaseCodesValue.Clear();
Firearm.AvailableAttachmentsValue.Clear();
+ Locker.BaseToExiledLockers.Clear();
+
+ Chamber.Chambers.Clear();
+
Scp914Processor.ProcessorToWrapper.Clear();
+
Workstation.BaseToWrapper.Clear();
}
}
diff --git a/Exiled.API/Features/Player.cs b/Exiled.API/Features/Player.cs
index e22ac73544..b7eab17237 100644
--- a/Exiled.API/Features/Player.cs
+++ b/Exiled.API/Features/Player.cs
@@ -22,6 +22,7 @@ namespace Exiled.API.Features
using Exiled.API.Features.Doors;
using Exiled.API.Features.Hazards;
using Exiled.API.Features.Items;
+ using Exiled.API.Features.Lockers;
using Exiled.API.Features.Pickups;
using Exiled.API.Features.Roles;
using Exiled.API.Interfaces;
@@ -40,7 +41,6 @@ namespace Exiled.API.Features
using InventorySystem.Items.Firearms.BasicMessages;
using InventorySystem.Items.Usables;
using InventorySystem.Items.Usables.Scp330;
- using MapGeneration.Distributors;
using MEC;
using Mirror;
using Mirror.LiteNetLib4Mirror;
@@ -3407,6 +3407,9 @@ public void Teleport(object obj, Vector3 offset)
? new Vector3(3, 0, 0)
: new Vector3(0, 0, 3)));
break;
+ case Chamber chamber:
+ Teleport(chamber.UseMultipleSpawnpoints ? chamber.Spawnpoints.Random().transform.position : chamber.Spawnpoint.transform.position + Vector3.up + offset);
+ break;
case Role role:
if (role.Owner is not null)
Teleport(role.Owner.Position + offset);
@@ -3443,12 +3446,6 @@ public void Teleport(object obj, Vector3 offset)
case Scp914Controller scp914:
Teleport(scp914._knobTransform.position + Vector3.up + offset);
break;
- case Locker locker:
- Teleport(locker.transform.position + Vector3.up + offset);
- break;
- case LockerChamber chamber:
- Teleport(chamber._spawnpoint.position + Vector3.up + offset);
- break;
case ElevatorChamber elevator:
Teleport(elevator.transform.position + Vector3.up + offset);
break;
@@ -3485,11 +3482,11 @@ public void RandomTeleport(Type type)
nameof(Player) => Random,
nameof(Pickup) => Pickup.Random,
nameof(Ragdoll) => Ragdoll.Random,
- nameof(Locker) => Map.GetRandomLocker(),
+ nameof(Lockers.Locker) => Map.GetRandomLocker(),
nameof(Generator) => Generator.Random,
nameof(Window) => Window.Random,
nameof(Scp914) => Scp914.Scp914Controller,
- nameof(LockerChamber) => Map.GetRandomLocker().Chambers.Random(),
+ nameof(Chamber) => Map.GetRandomLocker().Chambers.Random(),
_ => null,
};
diff --git a/Exiled.CustomModules/API/Features/CustomItems/CustomItem.cs b/Exiled.CustomModules/API/Features/CustomItems/CustomItem.cs
index 1dd9a4015b..57bc64e885 100644
--- a/Exiled.CustomModules/API/Features/CustomItems/CustomItem.cs
+++ b/Exiled.CustomModules/API/Features/CustomItems/CustomItem.cs
@@ -18,13 +18,12 @@ namespace Exiled.CustomModules.API.Features.CustomItems
using Exiled.API.Features.Core;
using Exiled.API.Features.Core.Interfaces;
using Exiled.API.Features.Items;
+ using Exiled.API.Features.Lockers;
using Exiled.API.Features.Pickups;
using Exiled.API.Features.Spawn;
using Exiled.CustomModules.API.Enums;
using Exiled.CustomModules.API.Features.Attributes;
- using Exiled.CustomModules.API.Features.CustomEscapes;
using Exiled.CustomModules.API.Features.CustomItems.Items;
- using MapGeneration.Distributors;
using UnityEngine;
///
@@ -521,16 +520,16 @@ public virtual uint Spawn(IEnumerable spawnPoints, uint limit)
{
for (int i = 0; i < 50; i++)
{
- if (Map.Lockers is null)
+ if (Locker.List is null)
continue;
- Locker locker = Map.Lockers[Loader.Loader.Random.Next(Map.Lockers.Count)];
+ Locker locker = Locker.List.Random();
if (locker is null || locker.Loot is null || locker.Chambers is null)
continue;
- LockerChamber chamber = locker.Chambers[Loader.Loader.Random.Next(Mathf.Max(0, locker.Chambers.Length - 1))];
- Vector3 position = chamber._spawnpoint.transform.position;
+ Chamber chamber = locker.Chambers.Random();
+ Vector3 position = chamber.Spawnpoint.position;
Spawn(position, null);
Log.Debug($"Spawned {Name} at {position} ({spawnPoint.Name})", true);
diff --git a/Exiled.Events/EventArgs/Map/FillingLockerEventArgs.cs b/Exiled.Events/EventArgs/Map/FillingLockerEventArgs.cs
index f50a87182f..00fa6b64df 100644
--- a/Exiled.Events/EventArgs/Map/FillingLockerEventArgs.cs
+++ b/Exiled.Events/EventArgs/Map/FillingLockerEventArgs.cs
@@ -7,6 +7,7 @@
namespace Exiled.Events.EventArgs.Map
{
+ using Exiled.API.Features.Lockers;
using Exiled.API.Features.Pickups;
using Exiled.Events.EventArgs.Interfaces;
@@ -31,7 +32,7 @@ public class FillingLockerEventArgs : IDeniableEvent, IPickupEvent
public FillingLockerEventArgs(ItemPickupBase pickupBase, LockerChamber lockerChamber)
{
Pickup = Pickup.Get(pickupBase);
- LockerChamber = lockerChamber;
+ Chamber = Chamber.Get(lockerChamber);
}
///
@@ -42,7 +43,7 @@ public FillingLockerEventArgs(ItemPickupBase pickupBase, LockerChamber lockerCha
///
/// Gets a value indicating the target locker chamber.
///
- public LockerChamber LockerChamber { get; }
+ public Chamber Chamber { get; }
///
/// Gets or sets a value indicating whether or not the item can be spawned.
diff --git a/Exiled.Events/EventArgs/Player/InteractingLockerEventArgs.cs b/Exiled.Events/EventArgs/Player/InteractingLockerEventArgs.cs
index 2eec0c06b9..d6dd2e4015 100644
--- a/Exiled.Events/EventArgs/Player/InteractingLockerEventArgs.cs
+++ b/Exiled.Events/EventArgs/Player/InteractingLockerEventArgs.cs
@@ -7,10 +7,11 @@
namespace Exiled.Events.EventArgs.Player
{
- using API.Features;
+ using System;
+ using API.Features;
+ using Exiled.API.Features.Lockers;
using Interfaces;
-
using MapGeneration.Distributors;
///
@@ -24,9 +25,6 @@ public class InteractingLockerEventArgs : IPlayerEvent, IDeniableEvent
///
///
///
- ///
- ///
- ///
///
///
///
@@ -36,24 +34,18 @@ public class InteractingLockerEventArgs : IPlayerEvent, IDeniableEvent
///
///
///
- public InteractingLockerEventArgs(Player player, Locker locker, LockerChamber lockerChamber, byte chamberId, bool isAllowed)
+ public InteractingLockerEventArgs(Player player, LockerChamber lockerChamber, byte chamberId, bool isAllowed)
{
Player = player;
- Locker = locker;
- Chamber = lockerChamber;
+ LockerChamber = Chamber.Get(lockerChamber);
ChamberId = chamberId;
IsAllowed = isAllowed;
}
- ///
- /// Gets the instance.
- ///
- public Locker Locker { get; }
-
///
/// Gets the interacting chamber.
///
- public LockerChamber Chamber { get; }
+ public Chamber LockerChamber { get; }
///
/// Gets the chamber id.
diff --git a/Exiled.Events/Patches/Events/Player/InteractingLocker.cs b/Exiled.Events/Patches/Events/Player/InteractingLocker.cs
index e615eb98fc..3a99317bf1 100644
--- a/Exiled.Events/Patches/Events/Player/InteractingLocker.cs
+++ b/Exiled.Events/Patches/Events/Player/InteractingLocker.cs
@@ -43,9 +43,6 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable
/// Patches .
///
[HarmonyPatch(typeof(Locker), nameof(Locker.Start))]
internal class LockerList
{
- private static IEnumerable Transpiler(IEnumerable codeInstructions)
- {
- List newInstructions = ListPool.Pool.Get(codeInstructions);
-
- // Map.LockersValue.Add(this);
- newInstructions.InsertRange(
- 0,
- new CodeInstruction[]
- {
- new(OpCodes.Ldsfld, Field(typeof(Map), nameof(Map.LockersValue))),
- new(OpCodes.Ldarg_0),
- new(OpCodes.Callvirt, Method(typeof(List), nameof(List.Add), new[] { typeof(Locker) })),
- });
-
- for (int z = 0; z < newInstructions.Count; z++)
- yield return newInstructions[z];
-
- ListPool.Pool.Return(newInstructions);
- }
+ private static void Postfix(Locker __instance) => API.Features.Lockers.Locker.Get(__instance);
}
}
\ No newline at end of file