1+ // -----------------------------------------------------------------------
2+ // <copyright file="RemovingTarget.cs" company="ExMod Team">
3+ // Copyright (c) ExMod Team. All rights reserved.
4+ // Licensed under the CC BY-SA 3.0 license.
5+ // </copyright>
6+ // -----------------------------------------------------------------------
7+
8+ namespace Exiled . Events . Patches . Events . Scp096
9+ {
10+ using System ;
11+ using System . Collections . Generic ;
12+ using System . Diagnostics ;
13+ using System . Reflection . Emit ;
14+
15+ using API . Features ;
16+ using API . Features . Pools ;
17+ using Exiled . Events . Attributes ;
18+ using Exiled . Events . EventArgs . Scp096 ;
19+ using HarmonyLib ;
20+ using PlayerRoles . PlayableScps . Scp096 ;
21+ using PlayerRoles . Subroutines ;
22+
23+ using static HarmonyLib . AccessTools ;
24+
25+ /// <summary>
26+ /// Patches <see cref="Scp096TargetsTracker.RemoveTarget(ReferenceHub)" /> and <see cref="Scp096TargetsTracker.ClearAllTargets()" />.
27+ /// Adds the <see cref="Scp096.RemovingTarget" /> event.
28+ /// </summary>
29+ [ EventPatch ( typeof ( Handlers . Scp096 ) , nameof ( Handlers . Scp096 . RemovingTarget ) ) ]
30+ [ HarmonyPatch ( typeof ( Scp096TargetsTracker ) ) ]
31+ internal static class RemovingTarget
32+ {
33+ [ HarmonyPatch ( nameof ( Scp096TargetsTracker . RemoveTarget ) ) ]
34+ private static IEnumerable < CodeInstruction > Transpiler ( IEnumerable < CodeInstruction > instructions , ILGenerator generator )
35+ {
36+ List < CodeInstruction > newInstructions = ListPool < CodeInstruction > . Pool . Get ( instructions ) ;
37+
38+ Label retFalseLabel = generator . DefineLabel ( ) ;
39+ Label runLabel = generator . DefineLabel ( ) ;
40+
41+ // make game check contains instead of removing then forcibly running our event (so no spam event calls and is still deniable)
42+ newInstructions . Find ( instruction => instruction . Calls ( Method ( typeof ( HashSet < ReferenceHub > ) , nameof ( HashSet < ReferenceHub > . Remove ) ) ) ) . operand = Method ( typeof ( HashSet < ReferenceHub > ) , nameof ( HashSet < ReferenceHub > . Contains ) ) ;
43+
44+ int index = newInstructions . FindIndex ( instruction => instruction . opcode == OpCodes . Ret ) - 1 ;
45+
46+ newInstructions [ index ] . WithLabels ( retFalseLabel ) ;
47+
48+ index -= 1 ;
49+
50+ // integrate our condition into first if statement
51+ newInstructions . RemoveAt ( index ) ;
52+
53+ newInstructions . InsertRange (
54+ index ,
55+ new CodeInstruction [ ]
56+ {
57+ // integrate our condition into first if statement
58+ new ( OpCodes . Brfalse , retFalseLabel ) ,
59+
60+ // Player.Get(base.Owner)
61+ new ( OpCodes . Ldarg_0 ) ,
62+ new ( OpCodes . Call , PropertyGetter ( typeof ( StandardSubroutine < Scp096Role > ) , nameof ( StandardSubroutine < Scp096Role > . Owner ) ) ) ,
63+ new ( OpCodes . Call , Method ( typeof ( Player ) , nameof ( Player . Get ) , new [ ] { typeof ( ReferenceHub ) } ) ) ,
64+
65+ // Player.Get(target)
66+ new ( OpCodes . Ldarg_1 ) ,
67+ new ( OpCodes . Call , Method ( typeof ( Player ) , nameof ( Player . Get ) , new [ ] { typeof ( ReferenceHub ) } ) ) ,
68+
69+ // true
70+ new ( OpCodes . Ldc_I4_1 ) ,
71+
72+ // RemovingTargetEventArgs ev = new(scp096, target, isAllowed)
73+ new ( OpCodes . Newobj , GetDeclaredConstructors ( typeof ( RemovingTargetEventArgs ) ) [ 0 ] ) ,
74+ new ( OpCodes . Dup ) ,
75+
76+ // Handlers.Scp096.OnRemovingTarget(ev)
77+ new ( OpCodes . Call , Method ( typeof ( Handlers . Scp096 ) , nameof ( Handlers . Scp096 . OnRemovingTarget ) ) ) ,
78+
79+ // if (!ev.IsAllowed)
80+ // return;
81+ new ( OpCodes . Callvirt , PropertyGetter ( typeof ( RemovingTargetEventArgs ) , nameof ( RemovingTargetEventArgs . IsAllowed ) ) ) ,
82+ new ( OpCodes . Brtrue , runLabel ) ,
83+ } ) ;
84+
85+ index = newInstructions . FindIndex ( instruction => instruction . opcode == OpCodes . Ret ) + 1 ;
86+
87+ // if allowed, remove target
88+ newInstructions . InsertRange ( index , new [ ]
89+ {
90+ new CodeInstruction ( OpCodes . Ldarg_0 ) . MoveLabelsFrom ( newInstructions [ index ] ) . WithLabels ( runLabel ) ,
91+ new ( OpCodes . Ldfld , Field ( typeof ( Scp096TargetsTracker ) , nameof ( Scp096TargetsTracker . Targets ) ) ) ,
92+ new ( OpCodes . Ldarg_1 ) ,
93+ new ( OpCodes . Callvirt , Method ( typeof ( HashSet < ReferenceHub > ) , nameof ( HashSet < ReferenceHub > . Remove ) ) ) ,
94+ new ( OpCodes . Pop ) ,
95+ } ) ;
96+
97+ for ( int z = 0 ; z < newInstructions . Count ; z ++ )
98+ yield return newInstructions [ z ] ;
99+
100+ ListPool < CodeInstruction > . Pool . Return ( newInstructions ) ;
101+ }
102+
103+ [ HarmonyPatch ( nameof ( Scp096TargetsTracker . ClearAllTargets ) ) ]
104+ [ HarmonyTranspiler ]
105+ private static IEnumerable < CodeInstruction > Transpiler2 ( IEnumerable < CodeInstruction > instructions )
106+ {
107+ List < CodeInstruction > newInstructions = ListPool < CodeInstruction > . Pool . Get ( instructions ) ;
108+
109+ object continueLabel = newInstructions . Find ( instruction => instruction . opcode == OpCodes . Br_S ) . operand ;
110+
111+ const int offset = 1 ;
112+ int index = newInstructions . FindIndex ( instruction => instruction . opcode == OpCodes . Stloc_1 ) + offset ;
113+
114+ newInstructions . InsertRange (
115+ index ,
116+ new [ ]
117+ {
118+ // Player.Get(base.Owner)
119+ new CodeInstruction ( OpCodes . Ldarg_0 ) . MoveLabelsFrom ( newInstructions [ index ] ) ,
120+ new ( OpCodes . Call , PropertyGetter ( typeof ( StandardSubroutine < Scp096Role > ) , nameof ( StandardSubroutine < Scp096Role > . Owner ) ) ) ,
121+ new ( OpCodes . Call , Method ( typeof ( Player ) , nameof ( Player . Get ) , new [ ] { typeof ( ReferenceHub ) } ) ) ,
122+
123+ // Player.Get(target)
124+ new ( OpCodes . Ldloc_1 ) ,
125+ new ( OpCodes . Call , Method ( typeof ( Player ) , nameof ( Player . Get ) , new [ ] { typeof ( ReferenceHub ) } ) ) ,
126+
127+ // true
128+ new ( OpCodes . Ldc_I4_1 ) ,
129+
130+ // RemovingTargetEventArgs ev = new(scp096, target, isAllowed)
131+ new ( OpCodes . Newobj , GetDeclaredConstructors ( typeof ( RemovingTargetEventArgs ) ) [ 0 ] ) ,
132+ new ( OpCodes . Dup ) ,
133+
134+ // Handlers.Scp096.OnRemovingTarget(ev)
135+ new ( OpCodes . Call , Method ( typeof ( Handlers . Scp096 ) , nameof ( Handlers . Scp096 . OnRemovingTarget ) ) ) ,
136+
137+ // if (!ev.IsAllowed)
138+ // continue;
139+ new ( OpCodes . Callvirt , PropertyGetter ( typeof ( RemovingTargetEventArgs ) , nameof ( RemovingTargetEventArgs . IsAllowed ) ) ) ,
140+ new ( OpCodes . Brfalse , continueLabel ) ,
141+ } ) ;
142+
143+ for ( int z = 0 ; z < newInstructions . Count ; z ++ )
144+ yield return newInstructions [ z ] ;
145+
146+ ListPool < CodeInstruction > . Pool . Return ( newInstructions ) ;
147+ }
148+ }
149+ }
0 commit comments