diff --git a/EXILED/Exiled.Events/EventArgs/Map/SpawningRoomConnectorEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/SpawningRoomConnectorEventArgs.cs new file mode 100644 index 0000000000..179c244e83 --- /dev/null +++ b/EXILED/Exiled.Events/EventArgs/Map/SpawningRoomConnectorEventArgs.cs @@ -0,0 +1,58 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.EventArgs.Map +{ + using Exiled.API.Features; + using Exiled.Events.EventArgs.Interfaces; + using MapGeneration.RoomConnectors; + using MapGeneration.RoomConnectors.Spawners; + + /// + /// Contains all information before spawning the connector between rooms. + /// + public class SpawningRoomConnectorEventArgs : IDeniableEvent + { + /// + /// Initializes a new instance of the class. + /// + /// The RoomConnectorSpawnpointBase. + /// The connector type the game is trying to spawn. + public SpawningRoomConnectorEventArgs(RoomConnectorSpawnpointBase roomConnectorSpawnpointBase, SpawnableRoomConnectorType connectorType) + { + RoomConnectorSpawnpoint = roomConnectorSpawnpointBase; + ConnectorType = connectorType; + RoomForward = Room.Get(RoomConnectorSpawnpoint._parentRoom); + RoomBackward = Room.Get(RoomConnectorSpawnpoint.transform.position + (RoomConnectorSpawnpoint.transform.forward * -1)); + } + + /// + /// Gets the RoomConnectorSpawnpointBase. + /// + public RoomConnectorSpawnpointBase RoomConnectorSpawnpoint { get; } + + /// + /// Gets the Room forward of the Connector. + /// + public Room RoomForward { get; } + + /// + /// Gets the Room Backward of the Connector. + /// + public Room RoomBackward { get; } + + /// + /// Gets or sets which Connector the game should spawn. + /// + public SpawnableRoomConnectorType ConnectorType { get; set; } + + /// + /// Gets or sets a value indicating whether the connector can be spawned. + /// + public bool IsAllowed { get; set; } = true; + } +} diff --git a/EXILED/Exiled.Events/Handlers/Map.cs b/EXILED/Exiled.Events/Handlers/Map.cs index 00d73e9ba0..4db303b36d 100644 --- a/EXILED/Exiled.Events/Handlers/Map.cs +++ b/EXILED/Exiled.Events/Handlers/Map.cs @@ -105,6 +105,11 @@ public static class Map /// public static Event PickupDestroyed { get; set; } = new(); + /// + /// Invoked before a room connector spawns. + /// + public static Event SpawningRoomConnector { get; set; } = new(); + /// /// Invoked before a team vehicle is spawned. /// @@ -221,6 +226,12 @@ public static class Map /// The instance. public static void OnPickupDestroyed(PickupDestroyedEventArgs ev) => PickupDestroyed.InvokeSafely(ev); + /// + /// Invoked before a room connector spawns. + /// + /// The instance. + public static void OnSpawningRoomConnector(SpawningRoomConnectorEventArgs ev) => SpawningRoomConnector.InvokeSafely(ev); + /// /// Invoked before a team vehicle is spawned. /// diff --git a/EXILED/Exiled.Events/Patches/Events/Map/SpawningRoomConnector.cs b/EXILED/Exiled.Events/Patches/Events/Map/SpawningRoomConnector.cs new file mode 100644 index 0000000000..7b11934f9d --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Events/Map/SpawningRoomConnector.cs @@ -0,0 +1,106 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Events.Map +{ + using MapGeneration.RoomConnectors; + +#pragma warning disable SA1402 // File may only contain a single type + + using System.Collections.Generic; + using System.Reflection; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using Exiled.Events.Attributes; + using Exiled.Events.EventArgs.Map; + using HarmonyLib; + using MapGeneration; + using MapGeneration.RoomConnectors.Spawners; + + using static HarmonyLib.AccessTools; + + /// + /// Patches . + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Map), nameof(Handlers.Map.SpawningRoomConnector))] + [HarmonyPatch(typeof(RoomConnectorSpawnpointBase), nameof(RoomConnectorSpawnpointBase.Spawn))] + internal static class SpawningRoomConnector + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + Label retLabel = generator.DefineLabel(); + + newInstructions.InsertRange(0, new CodeInstruction[] + { + // this + new(OpCodes.Ldarg_0), + + // type + new(OpCodes.Ldarg_1), + + // SpawningRoomConnectorEventArgs ev = new SpawningRoomConnectorEventArgs(RoomConnectorSpawnpointBase, SpawnableRoomConnectorType) + new(OpCodes.Newobj, Constructor( + typeof(SpawningRoomConnectorEventArgs), + new[] { typeof(RoomConnectorSpawnpointBase), typeof(SpawnableRoomConnectorType) })), + new(OpCodes.Dup), + new(OpCodes.Dup), + + // Handlers.Map.OnSpawningRoomConnector(ev); + new(OpCodes.Call, Method(typeof(Handlers.Map), nameof(Handlers.Map.OnSpawningRoomConnector))), + + // type = ev.ConnectorType + new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningRoomConnectorEventArgs), nameof(SpawningRoomConnectorEventArgs.ConnectorType))), + new(OpCodes.Starg_S, 1), + + // if (!ev.IsAllowed) + // return; + new(OpCodes.Callvirt, PropertyGetter(typeof(SpawningRoomConnectorEventArgs), nameof(SpawningRoomConnectorEventArgs.IsAllowed))), + new(OpCodes.Brfalse_S, retLabel), + }); + + int earlyRetIndex = newInstructions.FindIndex(i => i.opcode == OpCodes.Ret); + newInstructions[earlyRetIndex].WithLabels(retLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } + + /// + /// Patches . + /// Adds the event. + /// + [EventPatch(typeof(Handlers.Map), nameof(Handlers.Map.SpawningRoomConnector))] + [HarmonyPatch(typeof(SeedSynchronizer), nameof(SeedSynchronizer.GenerateLevel))] + internal static class SpawningRoomConnectorFix + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int offset = 0; + int index = newInstructions.FindIndex(i => i.Calls(Method(typeof(RoomConnectorSpawnpointBase), nameof(RoomConnectorSpawnpointBase.SetupAllRoomConnectors)))) + offset; + + newInstructions.RemoveAt(index); + + offset = -1; + index = newInstructions.FindIndex(i => i.opcode == OpCodes.Newobj && (ConstructorInfo)i.operand == GetDeclaredConstructors(typeof(PluginAPI.Events.MapGeneratedEvent))[0]) + offset; + newInstructions.Insert(index, new CodeInstruction(OpCodes.Call, Method(typeof(RoomConnectorSpawnpointBase), nameof(RoomConnectorSpawnpointBase.SetupAllRoomConnectors))).MoveLabelsFrom(newInstructions[index])); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file