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,74 @@
// -----------------------------------------------------------------------
// <copyright file="SavingByAntiScp207EventArgs.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.Player
{
using CustomPlayerEffects;
using Exiled.API.Features;
using Exiled.API.Features.DamageHandlers;
using Exiled.Events.EventArgs.Interfaces;

/// <summary>
/// Contains all information before a player is saved from death by the Anti-SCP-207 effect.
/// </summary>
public class SavingByAntiScp207EventArgs : IPlayerEvent, IDeniableEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="SavingByAntiScp207EventArgs"/> class.
/// </summary>
/// <param name="player">The player who is being saved.</param>
/// <param name="damageAmount">The amount of damage that would have been applied.</param>
/// <param name="handler">The damage handler that describes the damage.</param>
/// <param name="hitboxType">The hitbox that was hit.</param>
public SavingByAntiScp207EventArgs(ReferenceHub player, float damageAmount, DamageHandlerBase handler, HitboxType hitboxType)
{
Player = Player.Get(player);

Handler = handler;
HitboxType = hitboxType;
DamageAmount = damageAmount;
DamageMultiplier = (Player.Health + Player.ArtificialHealth - AntiScp207.DeathSaveHealth) / damageAmount;
IsAllowed = true;
}

/// <summary>
/// Gets the player who is being saved.
/// </summary>
public Player Player { get; }

/// <summary>
/// Gets the amount of damage that would have been applied.
/// </summary>
public float DamageAmount { get; }

/// <summary>
/// Gets or sets the multiplier for the damage that is applied when the event is allowed.
/// </summary>
public float DamageMultiplier { get; set; }

/// <summary>
/// Gets or sets the multiplier for the damage that if event denied.
/// </summary>
public float DeniedDamageMultiplier { get; set; } = 1;

/// <summary>
/// Gets the damage handler that describes the incoming damage.
/// </summary>
public DamageHandlerBase Handler { get; }

/// <summary>
/// Gets the hitbox that was hit.
/// </summary>
public HitboxType HitboxType { get; }

/// <summary>
/// Gets or sets a value indicating whether the event is allowed.
/// If set to <c>false</c>, the event will be denied.
/// </summary>
public bool IsAllowed { get; set; }
}
}
11 changes: 11 additions & 0 deletions EXILED/Exiled.Events/Handlers/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ public class Player
/// </summary>
public static Event<InteractedEventArgs> Interacted { get; set; } = new();

/// <summary>
/// Invoked before a <see cref="API.Features.Player"/> is saved from death by the Anti-SCP-207 effect.
/// </summary>
public static Event<SavingByAntiScp207EventArgs> SavingByAntiScp207 { get; set; } = new();

/// <summary>
/// Invoked before spawning a <see cref="API.Features.Player"/> <see cref="API.Features.Ragdoll"/>.
/// </summary>
Expand Down Expand Up @@ -1239,6 +1244,12 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item
/// <param name="ev">The <see cref="HealedEventArgs"/> instance. </param>
public static void OnHealed(HealedEventArgs ev) => Healed.InvokeSafely(ev);

/// <summary>
/// Called before a <see cref="API.Features.Player"/> is saved from death by the Anti-SCP-207 effect.
/// </summary>
/// <param name="ev">The <see cref="SavingByAntiScp207EventArgs"/> instance.</param>
public static void OnSavingByAntiScp207(SavingByAntiScp207EventArgs ev) => SavingByAntiScp207.InvokeSafely(ev);

/// <summary>
/// Called before a <see cref="API.Features.Player"/> dies.
/// </summary>
Expand Down
96 changes: 96 additions & 0 deletions EXILED/Exiled.Events/Patches/Events/Player/SavingByAntiScp207.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// -----------------------------------------------------------------------
// <copyright file="SavingByAntiScp207.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.Player
{
using System.Collections.Generic;
using System.Reflection.Emit;

using CustomPlayerEffects;
using Exiled.API.Features.Pools;
using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Player;

using HarmonyLib;

using static HarmonyLib.AccessTools;

/// <summary>
/// Patches <see cref="AntiScp207.GetDamageModifier"/>.
/// Adds the <see cref="Handlers.Player.SavingByAntiScp207"/> event before the player is saved from Anti-SCP-207 damage.
/// </summary>
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.SavingByAntiScp207))]
[HarmonyPatch(typeof(AntiScp207), nameof(AntiScp207.GetDamageModifier))]
internal class SavingByAntiScp207
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);

LocalBuilder ev = generator.DeclareLocal(typeof(SavingByAntiScp207EventArgs));

int offset = -1;
int index = newInstructions.FindLastIndex(i => i.Calls(Method(typeof(StatusEffectBase), nameof(StatusEffectBase.DisableEffect)))) + offset;

Label skipLabel = generator.DefineLabel();
Label gotoEventLabel = newInstructions[index].labels[0];

newInstructions[index].labels = new List<Label> { skipLabel };

newInstructions.InsertRange(index, new CodeInstruction[]
{
// this.Hub
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(gotoEventLabel),
new(OpCodes.Call, PropertyGetter(typeof(StatusEffectBase), nameof(StatusEffectBase.Hub))),

// damageAmount
new(OpCodes.Ldarg_1),

// handler
new(OpCodes.Ldarg_2),

// hitboxType
new(OpCodes.Ldarg_3),

// SavingByAntiScp207EventArgs ev = new SavingByAntiScp207EventArgs(ReferenceHub, float, DamageHandlerBase, HitboxType)
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(SavingByAntiScp207EventArgs))[0]),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, ev.LocalIndex),

// Handlers.Player.OnSavingByAntiScp207(ev);
new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnSavingByAntiScp207))),

// if (!ev.IsAllowed)
// return ev.DeniedDamageMultiplier;
new(OpCodes.Ldloc_S, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(SavingByAntiScp207EventArgs), nameof(SavingByAntiScp207EventArgs.IsAllowed))),
new(OpCodes.Brtrue_S, skipLabel),

new(OpCodes.Ldloc_S, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(SavingByAntiScp207EventArgs), nameof(SavingByAntiScp207EventArgs.DeniedDamageMultiplier))),
new(OpCodes.Ret),
});

index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldloc_2);

newInstructions.InsertRange(index, new CodeInstruction[]
{
// return ev.DamageMultiplier;
new(OpCodes.Ldloc_S, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(SavingByAntiScp207EventArgs), nameof(SavingByAntiScp207EventArgs.DamageMultiplier))),
new(OpCodes.Ret),
});

for (int i = 0; i < newInstructions.Count; i++)
{
yield return newInstructions[i];
}

ListPool<CodeInstruction>.Pool.Return(newInstructions);
}
}
}
Loading