diff --git a/EXILED/Exiled.API/Enums/LockerType.cs b/EXILED/Exiled.API/Enums/LockerType.cs index d960514ace..6b21f699c7 100644 --- a/EXILED/Exiled.API/Enums/LockerType.cs +++ b/EXILED/Exiled.API/Enums/LockerType.cs @@ -8,7 +8,7 @@ namespace Exiled.API.Enums { /// - /// Unique identifier for different types of s. + /// Unique identifier for different types of s. /// public enum LockerType { diff --git a/EXILED/Exiled.API/Features/Lockers/Chamber.cs b/EXILED/Exiled.API/Features/Lockers/Chamber.cs new file mode 100644 index 0000000000..c6bae8c8cb --- /dev/null +++ b/EXILED/Exiled.API/Features/Lockers/Chamber.cs @@ -0,0 +1,173 @@ +// ----------------------------------------------------------------------- +// +// 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.Pickups; + using Exiled.API.Interfaces; + using MapGeneration.Distributors; + using UnityEngine; + + /// + /// A wrapper for . + /// + public class Chamber : IWrapper, IWorldSpace + { + /// + /// 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; + Locker = locker; + + Chambers.Add(chamber, this); + } + + /// + /// Gets a of which contains all the instances. + /// + public static IReadOnlyCollection List => Chambers.Values; + + /// + public LockerChamber Base { get; } + + /// + /// Gets the where this chamber is located at. + /// + public Locker Locker { get; } + + /// + public Vector3 Position => Base.transform.position; + + /// + public Quaternion Rotation => Base.transform.rotation; + + /// + /// 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 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 one chamber is opened. + /// + 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; + + /// + /// 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()); + } +} diff --git a/EXILED/Exiled.API/Features/SupplyLocker.cs b/EXILED/Exiled.API/Features/Lockers/Locker.cs similarity index 50% rename from EXILED/Exiled.API/Features/SupplyLocker.cs rename to EXILED/Exiled.API/Features/Lockers/Locker.cs index b3bdd538ad..15fb2ccaec 100644 --- a/EXILED/Exiled.API/Features/SupplyLocker.cs +++ b/EXILED/Exiled.API/Features/Lockers/Locker.cs @@ -1,79 +1,78 @@ // ----------------------------------------------------------------------- -// +// // Copyright (c) Exiled Team. All rights reserved. // Licensed under the CC BY-SA 3.0 license. // // ----------------------------------------------------------------------- -namespace Exiled.API.Features +namespace Exiled.API.Features.Lockers { using System; using System.Collections.Generic; using System.Linq; - using System.Reflection; using Exiled.API.Enums; using Exiled.API.Extensions; + using Exiled.API.Features; + using Exiled.API.Features.Pickups; using Exiled.API.Interfaces; + using InventorySystem.Items.Pickups; using MapGeneration.Distributors; + using Mirror; - using PluginAPI.Core.Items; using UnityEngine; + using BaseLocker = MapGeneration.Distributors.Locker; #nullable enable /// - /// The in-game SupplyLocker. + /// The in-game Locker. /// - public class SupplyLocker : IWrapper, IWorldSpace + public class Locker : IWrapper, IWorldSpace { /// - /// A containing all known s and their corresponding . + /// A containing all known s and their corresponding . /// - internal static readonly Dictionary LockerToSupplyLocker = new(250, new ComponentsEqualityComparer()); + internal static readonly Dictionary BaseToExiledLockers = new(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The encapsulated . - internal SupplyLocker(Locker locker) + /// The encapsulated . + public Locker(BaseLocker locker) { Base = locker; - LockerToSupplyLocker.Add(locker, this); + BaseToExiledLockers.Add(locker, this); + + Chambers = locker.Chambers.Select(x => new Chamber(x, this)).ToList(); Type = locker.GetLockerType(); } /// - /// Gets a of which contains all the instances. + /// Gets a of which contains all the instances. /// - public static IReadOnlyCollection List => LockerToSupplyLocker.Values; + public static IReadOnlyCollection List => BaseToExiledLockers.Values; - /// - /// Gets the instance of the supply locker. - /// - public Locker Base { get; } + /// + public BaseLocker Base { get; } /// - /// Gets the of the . + /// Gets the of the . /// public LockerType Type { get; } /// - /// Gets the . + /// Gets the . /// public Transform Transform => Base.transform; - /// - /// Gets the position. - /// + /// public Vector3 Position => Base.transform.position; - /// - /// Gets the rotation. - /// + /// public Quaternion Rotation => Base.transform.rotation; /// - /// Gets the in which the is located. + /// Gets the in which the is located. /// public Room? Room => Room.Get(Position); @@ -83,14 +82,18 @@ internal SupplyLocker(Locker locker) public ZoneType Zone => Room?.Zone ?? ZoneType.Unspecified; /// - /// Gets the 's in this . + /// Gets the all in this locker. /// - public IEnumerable Chambers => Base.Chambers; + public IReadOnlyCollection Chambers { get; } /// - /// Gets the who has been open. + /// Gets or sets an id for manipulating opened chambers. /// - public IEnumerable OpenedChambers => Chambers.Where(c => c.IsOpen).ToArray(); + public ushort OpenedChambers + { + get => Base.OpenedChambers; + set => Base.NetworkOpenedChambers = value; + } /// /// Gets a random position from one of the . @@ -99,51 +102,51 @@ public Vector3 RandomChamberPosition { get { - LockerChamber randomChamber = Chambers.GetRandomValue(); + Chamber randomChamber = Chambers.GetRandomValue(); // Determine if the chamber uses multiple spawn points and has at least one available spawn point. - if (randomChamber._useMultipleSpawnpoints && randomChamber._spawnpoints.Length > 0) + if (randomChamber.UseMultipleSpawnpoints && randomChamber.Spawnpoints.Count() > 0) { // Return the position of a random spawn point within the chamber. - return randomChamber._spawnpoints.RandomItem().position; + return randomChamber.Spawnpoints.GetRandomValue().position; } // Return the position of the main spawn point for the chamber. - return randomChamber._spawnpoint.position; + return randomChamber.Spawnpoint.position; } } /// - /// Gets the belonging to the , if any. + /// Gets the belonging to the , if any. /// - /// The to get. - /// A or if not found. - public static SupplyLocker? Get(Locker locker) => locker == null ? null : - LockerToSupplyLocker.TryGetValue(locker, out SupplyLocker supply) ? supply : new SupplyLocker(locker); + /// The to get. + /// A or if not found. + public static Locker? Get(BaseLocker locker) => locker == null ? null : + BaseToExiledLockers.TryGetValue(locker, out Locker supply) ? supply : new Locker(locker); /// - /// Gets a of given the specified . + /// Gets a of given the specified . /// /// The to search for. - /// The with the given or if not found. - public static IEnumerable Get(ZoneType zoneType) => Get(room => room.Zone.HasFlag(zoneType)); + /// The with the given or if not found. + public static IEnumerable Get(ZoneType zoneType) => Get(room => room.Zone.HasFlag(zoneType)); /// - /// Gets a of filtered based on a predicate. + /// Gets a of filtered based on a predicate. /// /// The condition to satify. - /// A of which contains elements that satify the condition. - public static IEnumerable Get(Func predicate) => List.Where(predicate); + /// A of which contains elements that satify the condition. + public static IEnumerable Get(Func predicate) => List.Where(predicate); /// - /// Gets a random based on the specified filters. + /// Gets a random based on the specified filters. /// /// The to filter by. If unspecified, all zones are considered. /// The to filter by. If unspecified, all locker types are considered. - /// A random object, or null if no matching locker is found. - public static SupplyLocker? Random(ZoneType zone = ZoneType.Unspecified, LockerType lockerType = LockerType.Unknow) + /// A random object, or null if no matching locker is found. + public static Locker? Random(ZoneType zone = ZoneType.Unspecified, LockerType lockerType = LockerType.Unknow) { - IEnumerable filteredLockers = List; + IEnumerable filteredLockers = List; if (lockerType != LockerType.Unknow) filteredLockers = filteredLockers.Where(l => l.Type == lockerType); @@ -157,19 +160,19 @@ public Vector3 RandomChamberPosition /// /// Adds an item to a randomly selected locker chamber. /// - /// The to be added to the locker chamber. - public void AddItem(ItemPickup item) + /// The to be added to the locker chamber. + public void AddItem(Pickup item) { // Select a random chamber from the available locker chambers. - LockerChamber chamber = Chambers.GetRandomValue(); + Chamber chamber = Chambers.GetRandomValue(); // Determine the parent transform where the item will be placed. - Transform parentTransform = chamber._useMultipleSpawnpoints && chamber._spawnpoints.Length > 0 - ? chamber._spawnpoints.RandomItem() - : chamber._spawnpoint; + Transform parentTransform = chamber.UseMultipleSpawnpoints && chamber.Spawnpoints.Count() > 0 + ? chamber.Spawnpoints.GetRandomValue() + : chamber.Spawnpoint; // If the chamber is open, immediately set the item's parent and spawn it. - if (chamber.IsOpen) + if (chamber.Base.IsOpen) { item.Transform.SetParent(parentTransform); item.Spawn(); @@ -177,7 +180,7 @@ public void AddItem(ItemPickup item) else { // If the item is already spawned on the network, unspawn it before proceeding. - if (NetworkServer.spawned.ContainsKey(item.OriginalObject.netId)) + if (NetworkServer.spawned.ContainsKey(item.Base.netId)) NetworkServer.UnSpawn(item.GameObject); // Set the item's parent transform. @@ -187,7 +190,7 @@ public void AddItem(ItemPickup item) item.IsLocked = true; // Notify any pickup distributor triggers. - (item.OriginalObject as IPickupDistributorTrigger)?.OnDistributed(); + (item.Base as IPickupDistributorTrigger)?.OnDistributed(); // If the item has a Rigidbody component, make it kinematic and reset its position and rotation. if (item.Rigidbody != null) @@ -202,36 +205,25 @@ public void AddItem(ItemPickup item) // If the chamber is configured to spawn items on the first opening, add the item to the list of items to be spawned. // Otherwise, spawn the item immediately. - if (chamber._spawnOnFirstChamberOpening) - chamber._toBeSpawned.Add(item.OriginalObject); + if (chamber.InitiallySpawn) + chamber.Base._toBeSpawned.Add(item.Base); else - ItemDistributor.SpawnPickup(item.OriginalObject); + ItemDistributor.SpawnPickup(item.Base); } } /// - /// Spawns an item of the specified to the locker by creating a new . + /// Spawns an item of the specified to the locker by creating a new . /// /// The type of item to be added. - public void AddItem(ItemType type) => AddItem(ItemPickup.Create(type, default, default)); + public void AddItem(ItemType type) => AddItem(Pickup.Create(type)); /// - /// Clears the cached lockers in the dictionary that have become invalid. - /// This method identifies and removes all entries where either the key (a instance) - /// or the value (a instance) is null, ensuring that only valid references - /// are kept in the cache. + /// Clears the cached lockers in the dictionary. /// internal static void ClearCache() { - List keysToRemove = LockerToSupplyLocker - .Where(kv => kv.Key == null || kv.Value == null) - .Select(kv => kv.Key) - .ToList(); - - foreach (Locker key in keysToRemove) - { - LockerToSupplyLocker.Remove(key); - } + BaseToExiledLockers.Clear(); } } } diff --git a/EXILED/Exiled.API/Features/Map.cs b/EXILED/Exiled.API/Features/Map.cs index 05e5007a43..13d55fc987 100644 --- a/EXILED/Exiled.API/Features/Map.cs +++ b/EXILED/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.Toys; using global::Hazards; @@ -79,9 +80,9 @@ DecontaminationController.Singleton.NetworkDecontaminationOverride is Decontamin public static ReadOnlyCollection PocketDimensionTeleports { get; } = TeleportsValue.AsReadOnly(); /// - /// Gets all objects. + /// Gets all objects. /// - public static ReadOnlyCollection Lockers { get; } = SupplyLocker.LockerToSupplyLocker.Keys.ToList().AsReadOnly(); + public static ReadOnlyCollection Lockers { get; } = Features.Lockers.Locker.BaseToExiledLockers.Keys.ToList().AsReadOnly(); /// /// Gets all objects. @@ -207,10 +208,10 @@ public static void ResetLightsColor() } /// - /// Gets a random . + /// Gets a random . /// - /// object. - public static Locker GetRandomLocker() => Lockers.GetRandomValue(); + /// object. + public static MapGeneration.Distributors.Locker GetRandomLocker() => Lockers.GetRandomValue(); /// /// Gets a random . diff --git a/EXILED/Exiled.API/Features/Spawn/LockerSpawnPoint.cs b/EXILED/Exiled.API/Features/Spawn/LockerSpawnPoint.cs index 84579e2272..ecc4341279 100644 --- a/EXILED/Exiled.API/Features/Spawn/LockerSpawnPoint.cs +++ b/EXILED/Exiled.API/Features/Spawn/LockerSpawnPoint.cs @@ -10,6 +10,7 @@ namespace Exiled.API.Features.Spawn using System.Linq; using Exiled.API.Enums; + using Exiled.API.Features.Lockers; using UnityEngine; using YamlDotNet.Serialization; @@ -35,7 +36,7 @@ public class LockerSpawnPoint : SpawnPoint public Vector3 Offset { get; set; } = Vector3.zero; /// - /// Gets or sets the type of the . + /// Gets or sets the type of the . /// public LockerType Type { get; set; } = LockerType.Unknow; @@ -56,7 +57,7 @@ public override Vector3 Position { get { - SupplyLocker foundLocker = SupplyLocker.Random(Zone, Type) ?? throw new NullReferenceException("No locker found in the specified zone."); + Locker foundLocker = Locker.Random(Zone, Type) ?? throw new NullReferenceException("No locker found in the specified zone."); // If UseChamber is true, use a random chamber's position. if (UseChamber) diff --git a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs index 1bbcc5ddb4..30ad47c9f0 100644 --- a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs +++ b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs @@ -18,7 +18,7 @@ namespace Exiled.Events.Handlers.Internal using Exiled.API.Enums; using Exiled.API.Extensions; - + using Exiled.API.Features.Lockers; using InventorySystem.Items.Firearms.Attachments; using InventorySystem.Items.Firearms.Attachments.Components; @@ -47,7 +47,7 @@ public static void OnMapGenerated() { Map.ClearCache(); PrefabHelper.LoadPrefabs(); - SupplyLocker.ClearCache(); + Locker.ClearCache(); // TODO: Fix For (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/377) PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.ChaosInsurgency] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.ChaosConscript); diff --git a/EXILED/Exiled.Events/Patches/Generic/LockerList.cs b/EXILED/Exiled.Events/Patches/Generic/LockerList.cs index a4491f5c39..7dadd8817a 100644 --- a/EXILED/Exiled.Events/Patches/Generic/LockerList.cs +++ b/EXILED/Exiled.Events/Patches/Generic/LockerList.cs @@ -12,7 +12,7 @@ namespace Exiled.Events.Patches.Generic using API.Features; using API.Features.Pools; - + using Exiled.API.Features.Lockers; using HarmonyLib; using MapGeneration.Distributors; @@ -20,22 +20,22 @@ namespace Exiled.Events.Patches.Generic using static HarmonyLib.AccessTools; /// - /// Patches . + /// Patches . /// - [HarmonyPatch(typeof(Locker), nameof(Locker.Start))] + [HarmonyPatch(typeof(MapGeneration.Distributors.Locker), nameof(MapGeneration.Distributors.Locker.Start))] internal class LockerList { private static IEnumerable Transpiler(IEnumerable codeInstructions) { List newInstructions = ListPool.Pool.Get(codeInstructions); - // new SupplyLocker(this) + // new Locker(this) newInstructions.InsertRange( 0, new CodeInstruction[] { new(OpCodes.Ldarg_0), - new(OpCodes.Newobj, GetDeclaredConstructors(typeof(SupplyLocker))[0]), + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(API.Features.Lockers.Locker))[0]), new(OpCodes.Pop), });