Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// -----------------------------------------------------------------------
// <copyright file="SpawningRoomConnectorEventArgs.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.Events.EventArgs.Map
{
using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;
using MapGeneration.RoomConnectors;
using MapGeneration.RoomConnectors.Spawners;

/// <summary>
/// Contains all information before spawning the connector between rooms.
/// </summary>
public class SpawningRoomConnectorEventArgs : IDeniableEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="SpawningRoomConnectorEventArgs" /> class.
/// </summary>
/// <param name="roomConnectorSpawnpointBase">The RoomConnectorSpawnpointBase.</param>
/// <param name="connectorType">The connector type the game is trying to spawn.</param>
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));
}

/// <summary>
/// Gets the RoomConnectorSpawnpointBase.
/// </summary>
public RoomConnectorSpawnpointBase RoomConnectorSpawnpoint { get; }

/// <summary>
/// Gets the Room forward of the Connector.
/// </summary>
public Room RoomForward { get; }

/// <summary>
/// Gets the Room Backward of the Connector.
/// </summary>
public Room RoomBackward { get; }

/// <summary>
/// Gets or sets which Connector the game should spawn.
/// </summary>
public SpawnableRoomConnectorType ConnectorType { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the connector can be spawned.
/// </summary>
public bool IsAllowed { get; set; } = true;
}
}
11 changes: 11 additions & 0 deletions EXILED/Exiled.Events/Handlers/Map.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ public static class Map
/// </summary>
public static Event<PickupDestroyedEventArgs> PickupDestroyed { get; set; } = new();

/// <summary>
/// Invoked before a room connector spawns.
/// </summary>
public static Event<SpawningRoomConnectorEventArgs> SpawningRoomConnector { get; set; } = new();

/// <summary>
/// Invoked before a team vehicle is spawned.
/// </summary>
Expand Down Expand Up @@ -221,6 +226,12 @@ public static class Map
/// <param name="ev">The <see cref="PickupDestroyedEventArgs"/> instance.</param>
public static void OnPickupDestroyed(PickupDestroyedEventArgs ev) => PickupDestroyed.InvokeSafely(ev);

/// <summary>
/// Invoked before a room connector spawns.
/// </summary>
/// <param name="ev">The <see cref="SpawningRoomConnectorEventArgs"/> instance.</param>
public static void OnSpawningRoomConnector(SpawningRoomConnectorEventArgs ev) => SpawningRoomConnector.InvokeSafely(ev);

/// <summary>
/// Invoked before a team vehicle is spawned.
/// </summary>
Expand Down
106 changes: 106 additions & 0 deletions EXILED/Exiled.Events/Patches/Events/Map/SpawningRoomConnector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// -----------------------------------------------------------------------
// <copyright file="SpawningRoomConnector.cs" company="ExMod Team">
// Copyright (c) ExMod Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

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;

/// <summary>
/// Patches <see cref="RoomConnectorSpawnpointBase.Spawn"/>.
/// Adds the <see cref="Handlers.Map.OnSpawningRoomConnector"/> event.
/// </summary>
[EventPatch(typeof(Handlers.Map), nameof(Handlers.Map.SpawningRoomConnector))]
[HarmonyPatch(typeof(RoomConnectorSpawnpointBase), nameof(RoomConnectorSpawnpointBase.Spawn))]
internal static class SpawningRoomConnector
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.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<CodeInstruction>.Pool.Return(newInstructions);
}
}

/// <summary>
/// Patches <see cref="SeedSynchronizer.GenerateLevel"/>.
/// Adds the <see cref="Handlers.Map.OnSpawningRoomConnector"/> event.
/// </summary>
[EventPatch(typeof(Handlers.Map), nameof(Handlers.Map.SpawningRoomConnector))]
[HarmonyPatch(typeof(SeedSynchronizer), nameof(SeedSynchronizer.GenerateLevel))]
internal static class SpawningRoomConnectorFix
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.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<CodeInstruction>.Pool.Return(newInstructions);
}
}
}