Files
Nitrox/NitroxPatcher/Patches/Dynamic/CreatureDeath_OnKillAsync_Patch.cs
2025-07-06 00:23:46 +02:00

115 lines
5.8 KiB
C#

using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using HarmonyLib;
using NitroxClient.Communication.Abstract;
using NitroxClient.GameLogic;
using NitroxClient.GameLogic.Spawning.Metadata;
using NitroxClient.MonoBehaviours;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.DataStructures.Util;
using NitroxModel.Helper;
using NitroxModel.Packets;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
namespace NitroxPatcher.Patches.Dynamic;
/// <summary>
/// Broadcasts creature death consequences:<br/>
/// - Converted to a cooked item<br/>
/// - Dead but still has its corpse floating in the water<br/>
/// - Eatable decomposition metadata
/// </summary>
public sealed partial class CreatureDeath_OnKillAsync_Patch : NitroxPatch, IDynamicPatch
{
internal static readonly MethodInfo TARGET_METHOD = AccessTools.EnumeratorMoveNext(Reflect.Method((CreatureDeath t) => t.OnKillAsync()));
/*
* 1st injection:
* gameObject.GetComponent<Rigidbody>().angularDrag = base.gameObject.GetComponent<Rigidbody>().angularDrag * 3f;
* UnityEngine.Object.Destroy(base.gameObject);
* result = null;
* CreatureDeath_OnKillAsync_Patch.BroadcastCookedSpawned(this, gameObject, cookedData); <---- INSERTED LINE
*
* 2nd injection:
* base.Invoke("RemoveCorpse", this.removeCorpseAfterSeconds);
* CreatureDeath_OnKillAsync_Patch.BroadcastRemoveCorpse(this); <---- INSERTED LINE
*
* 3rd injection:
* this.eatable.SetDecomposes(true);
* CreatureDeath_OnKillAsync_Patch.BroadcastCookedSpawned(this.eatable); <---- INSERTED LINE
*/
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
// First injection
return new CodeMatcher(instructions).MatchEndForward([
new CodeMatch(OpCodes.Ldarg_0),
new CodeMatch(OpCodes.Ldnull),
new CodeMatch(OpCodes.Stfld),
new CodeMatch(OpCodes.Br),
])
.Advance(1)
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_2))
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_3))
.Insert(new CodeInstruction(OpCodes.Call, Reflect.Method(() => BroadcastCookedSpawned(default, default, default))))
// Second injection
.MatchEndForward([
new CodeMatch(OpCodes.Ldloc_1),
new CodeMatch(OpCodes.Ldfld, Reflect.Field((CreatureDeath t) => t.removeCorpseAfterSeconds)),
new CodeMatch(OpCodes.Call),
])
.Advance(1)
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
.Insert(new CodeInstruction(OpCodes.Call, Reflect.Method(() => BroadcastRemoveCorpse(default))))
// Third injection
.MatchEndForward([
new CodeMatch(OpCodes.Ldloc_1),
new CodeMatch(OpCodes.Ldfld, Reflect.Field((CreatureDeath t) => t.eatable)),
new CodeMatch(OpCodes.Ldc_I4_1),
new CodeMatch(OpCodes.Callvirt),
])
.Advance(1)
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_1))
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldfld, Reflect.Field((CreatureDeath t) => t.eatable)))
.Insert(new CodeInstruction(OpCodes.Call, Reflect.Method(() => BroadcastEatableMetadata(default))))
.InstructionEnumeration();
}
public static void BroadcastCookedSpawned(CreatureDeath creatureDeath, GameObject gameObject, TechType cookedTechType)
{
if (creatureDeath.TryGetNitroxId(out NitroxId creatureId))
{
NitroxEntity.SetNewId(gameObject, creatureId);
}
Resolve<Items>().Dropped(gameObject, cookedTechType);
}
public static void BroadcastRemoveCorpse(CreatureDeath creatureDeath)
{
if (creatureDeath.TryGetNitroxId(out NitroxId creatureId))
{
Resolve<SimulationOwnership>().StopSimulatingEntity(creatureId);
EntityPositionBroadcaster.RemoveEntityMovementControl(creatureDeath.gameObject, creatureId);
Resolve<IPacketSender>().Send(new RemoveCreatureCorpse(creatureId, creatureDeath.transform.localPosition.ToDto(), creatureDeath.transform.localRotation.ToDto()));
}
}
public static void BroadcastEatableMetadata(Eatable eatable)
{
if (!eatable.TryGetNitroxId(out NitroxId eatableId))
{
return;
}
Optional<EntityMetadata> metadata = Resolve<EntityMetadataManager>().Extract(eatable);
if (metadata.HasValue)
{
Resolve<Entities>().BroadcastMetadataUpdate(eatableId, metadata.Value);
}
}
}