using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using HarmonyLib; using NitroxClient.GameLogic; using NitroxModel.Core; using NitroxModel.DataStructures; using NitroxModel.Helper; namespace NitroxPatcher.Patches.Dynamic; public sealed partial class Welder_Weld_Patch : NitroxPatch, IDynamicPatch { private static readonly MethodInfo TARGET_METHOD = Reflect.Method((Welder t) => t.Weld()); private static readonly OpCode INJECTION_OPCODE = OpCodes.Call; private static readonly object INJECTION_OPERAND = Reflect.Method(() => AddHealthOverride(default(LiveMixin), default(float), default(Welder))); private static readonly OpCode SWAP_INSTRUCTION_OPCODE = OpCodes.Callvirt; private static readonly MethodInfo SWAP_INSTRUCTION_OPERAND = Reflect.Method((LiveMixin t) => t.AddHealth(default(float))); private static readonly Welder RESPONSE_WELDER = null; public static bool Prefix() { return RESPONSE_WELDER == null; } public static IEnumerable Transpiler(MethodBase original, IEnumerable instructions) { Validate.NotNull(INJECTION_OPERAND); foreach (CodeInstruction instruction in instructions) { if (instruction.opcode.Equals(SWAP_INSTRUCTION_OPCODE) && instruction.operand.Equals(SWAP_INSTRUCTION_OPERAND)) { /* * Swap * this.activeWeldTarget.AddHealth(this.healthPerWeld) * with * AddHealthOverride(Welder welder, float addHealth) */ yield return new CodeInstruction(OpCodes.Ldarg_0, null); yield return new CodeInstruction(INJECTION_OPCODE, INJECTION_OPERAND); } else { yield return instruction; } } } public static float AddHealthOverride(LiveMixin live, float addHealth, Welder welder) { float result = 0f; if ((live.IsAlive() || live.canResurrect) && live.health < live.maxHealth) { float num = live.health; float newHealth = Math.Min(live.health + addHealth, live.maxHealth); result = newHealth - num; // For now, we only control the LiveMixin for vehicles (not even repair nodes at a cyclops) // If we change that, this if should be removed! Vehicle vehicle = live.GetComponent(); if (vehicle && live.TryGetIdOrWarn(out NitroxId id)) { if (Resolve().HasAnyLockType(id)) { result = live.AddHealth(addHealth); } else { // Another player simulates this entity. Send the weld info Log.Debug($"Broadcast weld action for {id}"); NitroxServiceLocator.LocateService().BroadcastWeld(id, addHealth); } } else { result = live.AddHealth(addHealth); } } return result; } }