Skip to content
Open
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
66 changes: 66 additions & 0 deletions EXILED/Exiled.Events/EventArgs/Player/ShowingHitMarkerEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// -----------------------------------------------------------------------
// <copyright file="ShowingHitMarkerEventArgs.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 API.Features;
using Interfaces;

/// <summary>
/// Contains all information before a hitmarker is show to player.
/// </summary>
Comment on lines +14 to +15
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XML doc summary has grammatical errors ("is show to player"); please correct to something like "...before a hitmarker is shown to a player." so generated docs/readers are clear.

Copilot uses AI. Check for mistakes.
public class ShowingHitMarkerEventArgs : IPlayerEvent, IDeniableEvent
{
/// <summary>
/// Initializes a new instance of the <see cref="ShowingHitMarkerEventArgs" /> class.
/// </summary>
/// <param name="hub">
/// <inheritdoc cref="Player" />
/// </param>
/// <param name="size">
/// <inheritdoc cref="Size" />
/// </param>
/// <param name="shouldPlayAudio">
/// <inheritdoc cref="ShouldPlayAudio" />
/// </param>
/// <param name="hitmarkerType">
/// <inheritdoc cref="HitmarkerType" />
/// </param>
public ShowingHitMarkerEventArgs(ReferenceHub hub, float size, bool shouldPlayAudio, HitmarkerType hitmarkerType)
{
Player = Player.Get(hub);
Size = size;
ShouldPlayAudio = shouldPlayAudio;
HitmarkerType = hitmarkerType;
}

/// <summary>
/// Gets or sets the player that the hitmarker is being shown to.
/// </summary>
public Player Player { get; set; }
Comment on lines +42 to +44
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Player is settable here, but the patch only uses the original ReferenceHub argument and does not read back ev.Player, so changing ev.Player in handlers will have no effect and may mislead API consumers. Consider making Player get-only (like most other IPlayerEvent args, e.g. ChangedItemEventArgs / HitEventArgs) or updating the patch/API to support redirecting if that is intended.

Suggested change
/// Gets or sets the player that the hitmarker is being shown to.
/// </summary>
public Player Player { get; set; }
/// Gets the player that the hitmarker is being shown to.
/// </summary>
public Player Player { get; private set; }

Copilot uses AI. Check for mistakes.

/// <summary>
/// Gets or sets the target size multiplier.
/// </summary>
public float Size { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the hitmarker sound effect should play.
/// </summary>
public bool ShouldPlayAudio { get; set; }

/// <summary>
/// Gets or sets a the type of the hitmarker.
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor grammar issue in the property XML doc ("a the type"). Please fix to "the type" to avoid typos in generated documentation.

Suggested change
/// Gets or sets a the type of the hitmarker.
/// Gets or sets the type of the hitmarker.

Copilot uses AI. Check for mistakes.
/// </summary>
public HitmarkerType HitmarkerType { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the hitmarker should be shown.
/// </summary>
public bool IsAllowed { get; set; } = true;
}
}
11 changes: 11 additions & 0 deletions EXILED/Exiled.Events/Handlers/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public class Player
/// </summary>
public static Event<HitEventArgs> Hit { get; set; } = new ();

/// <summary>
/// Invoked before a player is shown a hitmarker.
/// </summary>
public static Event<ShowingHitMarkerEventArgs> ShowingHitMarker { get; set; } = new ();

/// <summary>
/// Invoked before authenticating a <see cref="API.Features.Player"/>.
/// </summary>
Expand Down Expand Up @@ -1423,6 +1428,12 @@ public static void OnItemRemoved(ReferenceHub referenceHub, InventorySystem.Item
/// <param name="ev">The <see cref="HitEventArgs"/> instance.</param>
public static void OnHit(HitEventArgs ev) => Hit.InvokeSafely(ev);

/// <summary>
/// Called before a player is shown a hitmarker.
/// </summary>
/// <param name="ev">The <see cref="ShowingHitMarkerEventArgs"/> instance.</param>
public static void OnShowingHitMarker(ShowingHitMarkerEventArgs ev) => ShowingHitMarker.InvokeSafely(ev);

/// <summary>
/// Called before Emergency Release Button is pressed.
/// </summary>
Expand Down
81 changes: 81 additions & 0 deletions EXILED/Exiled.Events/Patches/Events/Player/ShowingHitMarker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// -----------------------------------------------------------------------
// <copyright file="ShowingHitMarker.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 Exiled.API.Features.Pools;
using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Player;
using HarmonyLib;

using static HarmonyLib.AccessTools;

/// <summary>
/// Patch the <see cref="Hitmarker.SendHitmarkerDirectly(ReferenceHub, float, bool, HitmarkerType)"/> method.
/// Adds the <see cref="Handlers.Player.ShowingHitMarker"/> event.
/// </summary>
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.ShowingHitMarker))]
[HarmonyPatch(typeof(Hitmarker), nameof(Hitmarker.SendHitmarkerDirectly), typeof(ReferenceHub), typeof(float), typeof(bool), typeof(HitmarkerType))]
internal static class ShowingHitMarker
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);

LocalBuilder ev = generator.DeclareLocal(typeof(ShowingHitMarkerEventArgs));
Label continueLabel = generator.DefineLabel();

int offset = 1;

int index = newInstructions.FindIndex(instruction => instruction.opcode == OpCodes.Ret) + offset;

newInstructions.InsertRange(index, new[]
{
// ShowingHitMarkerEventArgs ev = new(hub, size, playAudio, hitmarkerType)
new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]),
new(OpCodes.Ldarg_1),
new(OpCodes.Ldarg_2),
new(OpCodes.Ldarg_3),
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ShowingHitMarkerEventArgs))[0]),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, ev.LocalIndex),

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

// if (!ev.IsAllowed) return;
new(OpCodes.Ldloc_S, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(ShowingHitMarkerEventArgs), nameof(ShowingHitMarkerEventArgs.IsAllowed))),
new(OpCodes.Brtrue_S, continueLabel),
new(OpCodes.Ret),

// size = ev.Size;
new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex).WithLabels(continueLabel),
new(OpCodes.Callvirt, PropertyGetter(typeof(ShowingHitMarkerEventArgs), nameof(ShowingHitMarkerEventArgs.Size))),
new(OpCodes.Starg_S, 1),

// playAudio = ev.ShouldPlayAudio;
new(OpCodes.Ldloc_S, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(ShowingHitMarkerEventArgs), nameof(ShowingHitMarkerEventArgs.ShouldPlayAudio))),
new(OpCodes.Starg_S, 2),

// hitmarkerType = ev.HitmarkerType;
new(OpCodes.Ldloc_S, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(ShowingHitMarkerEventArgs), nameof(ShowingHitMarkerEventArgs.HitmarkerType))),
new(OpCodes.Starg_S, 3),
});

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

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