using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using HarmonyLib; using NitroxClient.GameLogic; using NitroxClient.MonoBehaviours; using NitroxModel.DataStructures; using NitroxModel.Helper; namespace NitroxPatcher.Patches.Dynamic; /// /// Prevents fishing from being born in a WaterPark if the said WaterPark is not simulated by the local player. /// Syncs fish birth. /// public sealed partial class WaterParkCreature_BornAsync_Patch : NitroxPatch, IDynamicPatch { public static readonly MethodInfo TARGET_METHOD = AccessTools.EnumeratorMoveNext(Reflect.Method(() => WaterParkCreature.BornAsync(default, default, default))); /* * MODIFIED: * if (creaturePrefabReference == null || !creaturePrefabReference.RuntimeKeyIsValid()) * BECOMES * if (creaturePrefabReference == null || !creaturePrefabReference.RuntimeKeyIsValid() || !WaterParkCreature_BornAsync_Patch.CanBeBorn(waterPark)) * * INSERTED: * result.SetActive(true); * waterPark.AddItem(pickupable); * WaterParkCreature_BornAsync_Patch.Callback(pickupable); <--- INSERTED LINE */ public static IEnumerable Transpiler(IEnumerable instructions) { // The "waterPark" variable (which is a parameter of the BornAsync IEnumerator) is not accessible because IEnumerators are specials // Finds the waterPark parameter reference so that we can duplicate it CodeMatcher waterParkMatcher = new CodeMatcher(instructions).MatchEndForward([ new CodeMatch(OpCodes.Ldloc_1), new CodeMatch(OpCodes.Ldc_I4_1), new CodeMatch(OpCodes.Callvirt), new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld), ]); CodeInstruction ldfldWaterPark = waterParkMatcher.Instruction.Clone(); CodeMatcher matcher = new CodeMatcher(instructions).MatchEndForward([ new CodeMatch(OpCodes.Stfld), new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld), new CodeMatch(OpCodes.Brfalse) ]); CodeInstruction brFalseInstruction = matcher.Instruction.Clone(); // 1st injection return matcher.Advance(1) .Insert([ new CodeInstruction(OpCodes.Ldarg_0), ldfldWaterPark, new CodeInstruction(OpCodes.Call, Reflect.Method(() => CanBeBorn(default))), brFalseInstruction ]) // 2nd injection .MatchEndForward([ new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld), new CodeMatch(OpCodes.Ldloc_3), new CodeMatch(OpCodes.Callvirt) ]) .Advance(1) .InsertAndAdvance([ new CodeInstruction(OpCodes.Ldloc_3), new CodeInstruction(OpCodes.Call, Reflect.Method(() => Callback(default))) ]) .InstructionEnumeration(); } public static bool CanBeBorn(WaterPark waterPark) { if (waterPark.TryGetNitroxId(out NitroxId waterParkId)) { return Resolve().HasAnyLockType(waterParkId); } return true; } private static void Callback(Pickupable pickupable) { NitroxEntity.GenerateNewId(pickupable.gameObject); Resolve().Dropped(pickupable.gameObject); } }