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