@@ -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