Skip to content
This repository was archived by the owner on Jan 19, 2025. It is now read-only.

Commit b4ee458

Browse files
OnShot event now supports shooting into the sky (#2781)
1 parent 4243def commit b4ee458

File tree

1 file changed

+63
-0
lines changed
  • Exiled.Events/Patches/Events/Player

1 file changed

+63
-0
lines changed

Exiled.Events/Patches/Events/Player/Shot.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,69 @@ private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstructi
142142
}
143143
}
144144

145+
/// <summary>
146+
/// Patches the "else" branch of ServerPerformShot raycast attempt to fire OnShot even if the raycast didn't hit anything.
147+
/// </summary>
148+
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Shot))]
149+
[HarmonyPatch(typeof(SingleBulletHitreg), nameof(SingleBulletHitreg.ServerPerformShot))]
150+
internal static class Miss
151+
{
152+
/// <summary>
153+
/// Method to fire the OnShot event when raycast fails.
154+
/// </summary>
155+
private static void ProcessMiss(ReferenceHub player, Firearm firearm, Ray ray)
156+
{
157+
RaycastHit hit = new();
158+
hit.distance = firearm.BaseStats.MaxDistance(); // Assign artificial values to raycast hit
159+
hit.point = ray.GetPoint(hit.distance);
160+
hit.normal = -ray.direction;
161+
162+
ShotEventArgs shotEvent = new(Player.Get(player), Item.Get(firearm) as API.Features.Items.Firearm, hit, null, 0f);
163+
164+
Handlers.Player.OnShot(shotEvent);
165+
}
166+
167+
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
168+
{
169+
List<CodeInstruction> newInstructions = ListPool<CodeInstruction>.Pool.Get(instructions);
170+
171+
int firstGetDebugModeIndex = newInstructions.FindIndex(instruction => instruction.Calls(PropertyGetter(typeof(StandardHitregBase), nameof(StandardHitregBase.DebugMode))));
172+
173+
int secondGetDebugModeIndex = newInstructions.FindIndex( // There are two calls to get_DebugMode, we need the second one
174+
firstGetDebugModeIndex + 1,
175+
instruction => instruction.Calls(PropertyGetter(typeof(StandardHitregBase), nameof(StandardHitregBase.DebugMode))));
176+
177+
/*
178+
call bool InventorySystem.Items.Firearms.Modules.StandardHitregBase::get_DebugMode()
179+
[] <---- we insert instructions here so they fire only in else branch
180+
brfalse.s IL_00c1
181+
*/
182+
int falseReturnIndex = secondGetDebugModeIndex + 1;
183+
184+
newInstructions.InsertRange(falseReturnIndex, new CodeInstruction[]
185+
{
186+
// this.Hub
187+
new(OpCodes.Ldarg_0),
188+
new(OpCodes.Callvirt, PropertyGetter(typeof(StandardHitregBase), nameof(StandardHitregBase.Hub))),
189+
190+
// this.Firearm
191+
new(OpCodes.Ldarg_0),
192+
new(OpCodes.Callvirt, PropertyGetter(typeof(StandardHitregBase), nameof(StandardHitregBase.Firearm))),
193+
194+
// ray
195+
new(OpCodes.Ldarg_1),
196+
197+
// ProcessMiss
198+
new(OpCodes.Call, Method(typeof(Miss), nameof(ProcessMiss), new[] { typeof(ReferenceHub), typeof(Firearm), typeof(Ray) })),
199+
});
200+
201+
for (int i = 0; i < newInstructions.Count; i++)
202+
yield return newInstructions[i];
203+
204+
ListPool<CodeInstruction>.Pool.Return(newInstructions);
205+
}
206+
}
207+
145208
/// <summary>
146209
/// Patches <see cref="BuckshotHitreg.ShootPellet" />.
147210
/// Adds the <see cref="Handlers.Player.Shot" /> events.

0 commit comments

Comments
 (0)