using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; using HarmonyLib; using NitroxClient.MonoBehaviours.Cyclops; using NitroxClient.MonoBehaviours.Vehicles; using NitroxModel.Helper; namespace NitroxPatcher.Patches.Dynamic; /// /// Prevents sonar from turning off automatically for the player that isn't currently piloting the Cyclops. /// public sealed partial class CyclopsSonarButton_Update_Patch : NitroxPatch, IDynamicPatch { public static readonly MethodInfo TARGET_METHOD = Reflect.Method((CyclopsSonarButton t) => t.Update()); internal static readonly OpCode INJECTION_OPCODE = OpCodes.Ldsfld; internal static readonly object INJECTION_OPERAND = Reflect.Field(() => Player.main); public static IEnumerable Transpiler(MethodBase methodBase, IEnumerable instructions, ILGenerator il) { /* Normally in the Update() * if (Player.main.GetMode() == Player.Mode.Normal && this.sonarActive) * { * this.TurnOffSonar(); * } * this part will be changed into: * if (CyclopsSonarButton_Update_Patch.ShouldTurnOff(this) && Player.main.GetMode() == Player.Mode.Normal && this.sonarActive) */ List codeInstructions = new(instructions); Label brLabel = il.DefineLabel(); CodeInstruction loadInstruction = new(OpCodes.Ldarg_0); CodeInstruction callInstruction = new(OpCodes.Call, Reflect.Method(() => ShouldTurnoff(default))); CodeInstruction brInstruction = new(OpCodes.Brfalse, brLabel); codeInstructions.Last().labels.Add(brLabel); for (int i = 0; i < codeInstructions.Count; i++) { CodeInstruction instruction = codeInstructions[i]; if (instruction.opcode.Equals(INJECTION_OPCODE) && instruction.operand.Equals(INJECTION_OPERAND)) { // The second line after the current instruction should be a Brtrue, we need its operand to have the same jump label for our brfalse CodeInstruction nextBr = codeInstructions[i + 2]; // The new instruction will be the first of the if statement, so it should take the jump labels that the former first part of the statement had instruction.MoveLabelsTo(loadInstruction); yield return loadInstruction; yield return callInstruction; yield return brInstruction; } yield return instruction; } } /// true (sonar should be turned off) if local player is simulating the cyclops (there's no replicator in this case) public static bool ShouldTurnoff(CyclopsSonarButton cyclopsSonarButton) { return !cyclopsSonarButton.subRoot.GetComponent(); } }