diff --git a/Exiled.API/Features/Map.cs b/Exiled.API/Features/Map.cs index ed6af529..0f827b30 100644 --- a/Exiled.API/Features/Map.cs +++ b/Exiled.API/Features/Map.cs @@ -51,11 +51,6 @@ public static class Map /// internal static readonly List TeleportsValue = new(8); - /// - /// A list of s on the map. - /// - internal static readonly List RagdollsValue = new(); - /// /// A list of s on the map. /// @@ -63,7 +58,6 @@ public static class Map private static readonly ReadOnlyCollection ReadOnlyTeleportsValue = TeleportsValue.AsReadOnly(); private static readonly ReadOnlyCollection ReadOnlyLockersValue = LockersValue.AsReadOnly(); - private static readonly ReadOnlyCollection ReadOnlyRagdollsValue = RagdollsValue.AsReadOnly(); private static readonly ReadOnlyCollection ReadOnlyToysValue = ToysValue.AsReadOnly(); private static TantrumEnvironmentalHazard tantrumPrefab; @@ -122,11 +116,6 @@ public static Scp939AmnesticCloudInstance AmnesticCloudPrefab /// public static ReadOnlyCollection Lockers => ReadOnlyLockersValue; - /// - /// Gets all objects. - /// - public static ReadOnlyCollection Ragdolls => ReadOnlyRagdollsValue; - /// /// Gets all objects. /// @@ -354,7 +343,7 @@ internal static void ClearCache() Item.BaseToItem.Clear(); TeleportsValue.Clear(); LockersValue.Clear(); - RagdollsValue.Clear(); + Ragdoll.BasicRagdollToRagdoll.Clear(); Firearm.ItemTypeToFirearmInstance.Clear(); Firearm.BaseCodesValue.Clear(); Firearm.AvailableAttachmentsValue.Clear(); diff --git a/Exiled.API/Features/Player.cs b/Exiled.API/Features/Player.cs index a7db2f98..2ef68a68 100644 --- a/Exiled.API/Features/Player.cs +++ b/Exiled.API/Features/Player.cs @@ -2853,7 +2853,7 @@ public void RandomTeleport(Type type) nameof(TeslaGate) => TeslaGate.List.ElementAt(Random.Range(0, TeslaGate.BaseTeslaGateToTeslaGate.Count)), nameof(Player) => Dictionary.Values.ElementAt(Random.Range(0, Dictionary.Count)), nameof(Pickup) => Pickup.BaseToPickup.ElementAt(Random.Range(0, Pickup.BaseToPickup.Count)).Value, - nameof(Ragdoll) => Map.RagdollsValue[Random.Range(0, Map.RagdollsValue.Count)], + nameof(Ragdoll) => Ragdoll.List.ElementAt(Random.Range(0, Ragdoll.BasicRagdollToRagdoll.Count)), nameof(Locker) => Map.GetRandomLocker(), nameof(Generator) => Generator.List.ElementAt(Random.Range(0, Generator.Scp079GeneratorToGenerator.Count)), nameof(Window) => Window.List.ElementAt(Random.Range(0, Window.BreakableWindowToWindow.Count)), diff --git a/Exiled.API/Features/Ragdoll.cs b/Exiled.API/Features/Ragdoll.cs index dfad949d..87004e85 100644 --- a/Exiled.API/Features/Ragdoll.cs +++ b/Exiled.API/Features/Ragdoll.cs @@ -15,6 +15,8 @@ namespace Exiled.API.Features using Enums; using Exiled.API.Extensions; + using Interactables.Interobjects.DoorUtils; + using MapGeneration; using Mirror; using PlayerRoles; using PlayerRoles.PlayableScps.Scp049.Zombies; @@ -31,61 +33,24 @@ namespace Exiled.API.Features public class Ragdoll { /// - /// Initializes a new instance of the class. + /// A containing all known s and their corresponding . /// - /// The ragdoll's owner. - /// The player's . - /// A value that represents whether the ragdoll can be spawned. - public Ragdoll(Player player, DamageHandlerBase handler, bool canBeSpawned = false) - { - if (player.Role.Base is not IRagdollRole ragdollRole) - return; - - GameObject modelRagdoll = ragdollRole.Ragdoll.gameObject; - - if (modelRagdoll == null || !Object.Instantiate(modelRagdoll).TryGetComponent(out BasicRagdoll ragdoll)) - return; - - ragdoll.NetworkInfo = new RagdollData(player.ReferenceHub, handler, modelRagdoll.transform.localPosition, modelRagdoll.transform.localRotation); - - Base = ragdoll; - - Map.RagdollsValue.Add(this); - - if (canBeSpawned) - Spawn(); - } + internal static readonly Dictionary BasicRagdollToRagdoll = new(250); /// /// Initializes a new instance of the class. /// - /// The ragdoll's . - /// A value that represents whether the ragdoll can be spawned. - public Ragdoll(RagdollData networkInfo, bool canBeSpawned = false) + /// The encapsulated . + internal Ragdoll(BasicRagdoll ragdoll) { - if (networkInfo.RoleType.GetRoleBase() is not IRagdollRole ragdollRole) - return; - - GameObject modelRagdoll = ragdollRole.Ragdoll.gameObject; - - if (modelRagdoll == null || !Object.Instantiate(modelRagdoll).TryGetComponent(out BasicRagdoll ragdoll)) - return; - - ragdoll.NetworkInfo = networkInfo; - Base = ragdoll; - - Map.RagdollsValue.Add(this); - - if (canBeSpawned) - Spawn(); + BasicRagdollToRagdoll.Add(ragdoll, this); } /// - /// Initializes a new instance of the class. + /// Gets a of which contains all the instances. /// - /// The encapsulated . - internal Ragdoll(BasicRagdoll ragdoll) => Base = ragdoll; + public static IEnumerable List => BasicRagdollToRagdoll.Values; /// /// Gets or sets the s clean up time. @@ -268,43 +233,120 @@ public Vector3 Scale /// internal static HashSet IgnoredRagdolls { get; set; } = new(); + /// + /// Creates a new ragdoll. + /// + /// The data associated with the ragdoll. + /// The ragdoll. + /// Provided RoleType is not a valid ragdoll role (Spectator, Scp079, etc). + /// Unable to create a ragdoll. + public static Ragdoll Create(RagdollData networkInfo) + { + if (networkInfo.RoleType.GetRoleBase() is not IRagdollRole ragdollRole) + throw new ArgumentException($"Provided RoleType '{networkInfo.RoleType}' is not a valid ragdoll role."); + + GameObject modelRagdoll = ragdollRole.Ragdoll.gameObject; + + if (modelRagdoll == null || !Object.Instantiate(modelRagdoll).TryGetComponent(out BasicRagdoll ragdoll)) + throw new InvalidOperationException($"Unable to create a ragdoll of type {networkInfo.RoleType}."); + + ragdoll.NetworkInfo = networkInfo; + + return new(ragdoll) + { + Position = networkInfo.StartPosition, + Rotation = networkInfo.StartRotation, + }; + } + + /// + /// Creates a new ragdoll. + /// + /// The of the ragdoll. + /// The name of the ragdoll. + /// The damage handler responsible for the ragdoll's death. + /// The optional owner of the ragdoll. + /// The ragdoll. + public static Ragdoll Create(RoleTypeId roleType, string name, DamageHandlerBase damageHandler, Player owner = null) + => Create(new(owner?.ReferenceHub ?? Server.Host.ReferenceHub, damageHandler, roleType, default, default, name, NetworkTime.time)); + + /// + /// Creates a new ragdoll. + /// + /// The of the ragdoll. + /// The name of the ragdoll. + /// The reason the ragdoll died. + /// The optional owner of the ragdoll. + /// The ragdoll. + public static Ragdoll Create(RoleTypeId roleType, string name, string deathReason, Player owner = null) + => Create(roleType, name, new CustomReasonDamageHandler(deathReason), owner); + + /// + /// Creates and spawns a new ragdoll. + /// + /// The data associated with the ragdoll. + /// The ragdoll. + public static Ragdoll CreateAndSpawn(RagdollData networkInfo) + { + Ragdoll doll = Create(networkInfo); + doll.Spawn(); + + return doll; + } + + /// + /// Creates and spawns a new ragdoll. + /// + /// The of the ragdoll. + /// The name of the ragdoll. + /// The damage handler responsible for the ragdoll's death. + /// The position of the ragdoll. + /// The rotation of the ragdoll. + /// The optional owner of the ragdoll. + /// The ragdoll. + public static Ragdoll CreateAndSpawn(RoleTypeId roleType, string name, DamageHandlerBase damageHandler, Vector3 position, Quaternion rotation, Player owner = null) + => CreateAndSpawn(new(owner?.ReferenceHub ?? Server.Host.ReferenceHub, damageHandler, roleType, position, rotation, name, NetworkTime.time)); + + /// + /// Creates and spawns a new ragdoll. + /// + /// The of the ragdoll. + /// The name of the ragdoll. + /// The reason the ragdoll died. + /// The position of the ragdoll. + /// The rotation of the ragdoll. + /// The optional owner of the ragdoll. + /// The ragdoll. + public static Ragdoll CreateAndSpawn(RoleTypeId roleType, string name, string deathReason, Vector3 position, Quaternion rotation, Player owner = null) + => CreateAndSpawn(roleType, name, new CustomReasonDamageHandler(deathReason), position, rotation, owner); + /// /// Gets the belonging to the , if any. /// /// The to get. /// A or if not found. - public static Ragdoll Get(BasicRagdoll ragdoll) => Map.Ragdolls.FirstOrDefault(rd => rd.Base == ragdoll); + public static Ragdoll Get(BasicRagdoll ragdoll) => BasicRagdollToRagdoll.TryGetValue(ragdoll, out Ragdoll doll) + ? doll + : new Ragdoll(ragdoll); /// /// Gets the of belonging to the , if any. /// /// The to get. /// A of . - public static IEnumerable Get(Player player) => Map.Ragdolls.Where(rd => rd.Owner == player); + public static IEnumerable Get(Player player) => Ragdoll.List.Where(rd => rd.Owner == player); /// /// Gets the of belonging to the of , if any. /// /// The s to get. /// A of . - public static IEnumerable Get(IEnumerable players) => players.SelectMany(pl => Map.Ragdolls.Where(rd => rd.Owner == pl)); + public static IEnumerable Get(IEnumerable players) => players.SelectMany(pl => Ragdoll.List.Where(rd => rd.Owner == pl)); /// - /// Spawns a on the map. + /// Destroys the ragdoll. /// - /// The ragdoll's owner. - /// The ragdoll's . - /// The created . - public static Ragdoll Spawn(Player player, DamageHandlers.DamageHandlerBase handler) => new(player, handler, true); - - /// - /// Deletes the ragdoll. - /// - public void Delete() - { - Object.Destroy(GameObject); - Map.RagdollsValue.Remove(this); - } + public void Destroy() => Object.Destroy(GameObject); /// /// Spawns the ragdoll. diff --git a/Exiled.CustomRoles/API/Features/CustomRole.cs b/Exiled.CustomRoles/API/Features/CustomRole.cs index 6ad7e51a..086ef313 100644 --- a/Exiled.CustomRoles/API/Features/CustomRole.cs +++ b/Exiled.CustomRoles/API/Features/CustomRole.cs @@ -751,7 +751,7 @@ private void OnInternalDying(DyingEventArgs ev) if (Check(ev.Player)) { CustomRoles.Instance.StopRagdollPlayers.Add(ev.Player); - _ = new Ragdoll(new RagdollData(ev.Player.ReferenceHub, ev.DamageHandler, Role, ev.Player.Position, Quaternion.Euler(ev.Player.Rotation), ev.Player.DisplayNickname, NetworkTime.time), true); + Ragdoll.CreateAndSpawn(new RagdollData(ev.Player.ReferenceHub, ev.DamageHandler, Role, ev.Player.Position, Quaternion.Euler(ev.Player.Rotation), ev.Player.DisplayNickname, NetworkTime.time)); } } } diff --git a/Exiled.Events/Events.cs b/Exiled.Events/Events.cs index dc7f6a5f..dabf37a4 100644 --- a/Exiled.Events/Events.cs +++ b/Exiled.Events/Events.cs @@ -18,6 +18,7 @@ namespace Exiled.Events using HarmonyLib; using PlayerRoles; using PlayerRoles.FirstPersonControl.Thirdperson; + using PlayerRoles.Ragdolls; using PluginAPI.Events; using UnityEngine.SceneManagement; @@ -94,6 +95,9 @@ public override void OnEnabled() AnimatedCharacterModel.OnFootstepPlayed += Handlers.Player.OnMakingNoise; + RagdollManager.OnRagdollSpawned += Handlers.Internal.RagdollList.OnSpawnedRagdoll; + RagdollManager.OnRagdollRemoved += Handlers.Internal.RagdollList.OnRemovedRagdoll; + ServerConsole.ReloadServerName(); EventManager.RegisterEvents(this); @@ -125,6 +129,9 @@ public override void OnDisabled() AnimatedCharacterModel.OnFootstepPlayed -= Handlers.Player.OnMakingNoise; + RagdollManager.OnRagdollSpawned -= Handlers.Internal.RagdollList.OnSpawnedRagdoll; + RagdollManager.OnRagdollRemoved -= Handlers.Internal.RagdollList.OnRemovedRagdoll; + EventManager.UnregisterEvents(this); EventManager.UnregisterEvents(this); } diff --git a/Exiled.Events/Handlers/Internal/RagdollList.cs b/Exiled.Events/Handlers/Internal/RagdollList.cs new file mode 100644 index 00000000..b0916379 --- /dev/null +++ b/Exiled.Events/Handlers/Internal/RagdollList.cs @@ -0,0 +1,30 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Exiled Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Handlers.Internal +{ + using Exiled.API.Features; + using PlayerRoles.Ragdolls; + + /// + /// Handles adding and removing from . + /// + internal static class RagdollList + { + /// + /// Called after a ragdoll is spawned. Hooked to . + /// + /// The spawned ragdoll. + public static void OnSpawnedRagdoll(BasicRagdoll ragdoll) => Ragdoll.Get(ragdoll); + + /// + /// Called before a ragdoll is destroyed. Hooked to . + /// + /// The destroyed ragdoll. + public static void OnRemovedRagdoll(BasicRagdoll ragdoll) => Ragdoll.BasicRagdollToRagdoll.Remove(ragdoll); + } +} diff --git a/Exiled.Events/Patches/Events/Player/SpawningRagdoll.cs b/Exiled.Events/Patches/Events/Player/SpawningRagdoll.cs index 74abd222..fd96f346 100644 --- a/Exiled.Events/Patches/Events/Player/SpawningRagdoll.cs +++ b/Exiled.Events/Patches/Events/Player/SpawningRagdoll.cs @@ -105,36 +105,6 @@ private static IEnumerable Transpiler(IEnumerable instruction.opcode == OpCodes.Ldnull); - - List