using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using HarmonyLib; using NitroxClient.Communication; using NitroxClient.GameLogic; using NitroxClient.MonoBehaviours; using NitroxModel.DataStructures; using NitroxModel.DataStructures.GameLogic.Entities; using NitroxModel.Helper; using NitroxModel.Packets; using NitroxModel_Subnautica.DataStructures; using NitroxPatcher.PatternMatching; using UnityEngine; namespace NitroxPatcher.Patches.Dynamic; public sealed partial class CyclopsDestructionEvent_SpawnLootAsync_Patch : NitroxPatch, IDynamicPatch { public static readonly MethodInfo TARGET_METHOD = AccessTools.EnumeratorMoveNext(Reflect.Method((CyclopsDestructionEvent t) => t.SpawnLootAsync())); // Matches twice, once for scrap metal and once for computer chips public static readonly InstructionsPattern PATTERN = new(expectedMatches: 2) { { Reflect.Method(() => UnityEngine.Object.Instantiate(default(GameObject), default(Vector3), default(Quaternion))), "SpawnObject" } }; public static IEnumerable Transpiler(MethodBase original, IEnumerable instructions) { return new CodeMatcher(instructions) .MatchStartForward(new CodeMatch(OpCodes.Switch)) .InsertAndAdvance(new CodeInstruction(OpCodes.Call, Reflect.Method(() => TrampolineCallback(default)))) .InstructionEnumeration() .InsertAfterMarker(PATTERN, "SpawnObject", [ new(OpCodes.Dup), new(OpCodes.Ldloc_1), new(OpCodes.Call, ((Action)SpawnObjectCallback).Method) ]); } public static void SpawnObjectCallback(GameObject gameObject, CyclopsDestructionEvent __instance) { NitroxId lootId = NitroxEntity.GenerateNewId(gameObject); LargeWorldEntity largeWorldEntity = gameObject.GetComponent(); PrefabIdentifier prefabIdentifier = gameObject.GetComponent(); Pickupable pickupable = gameObject.GetComponent(); WorldEntity lootEntity = new(gameObject.transform.ToWorldDto(), (int)largeWorldEntity.cellLevel, prefabIdentifier.classId, false, lootId, pickupable.GetTechType().ToDto(), null, null, []); Resolve().BroadcastEntitySpawnedByClient(lootEntity); } public static int TrampolineCallback(int originalIndex) { // Immediately return from iterator block if called from within CyclopsMetadataProcessor return PacketSuppressor.IsSuppressed ? int.MaxValue : originalIndex; } }