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