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

108 lines
4.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using FMOD.Studio;
using HarmonyLib;
using NitroxClient.Communication.Abstract;
using NitroxClient.GameLogic;
using NitroxClient.GameLogic.FMOD;
using NitroxModel.GameLogic.FMOD;
using NitroxModel.Helper;
using NitroxModel.Packets;
using UnityEngine;
namespace NitroxPatcher.Patches.Dynamic;
public sealed partial class FootstepSounds_OnStep_Patch : NitroxPatch, IDynamicPatch
{
private const string PRECURSOR_STEP_SOUND_PATH = "event:/player/footstep_precursor_base";
private const string METAL_STEP_SOUND_PATH = "event:/player/footstep_metal";
private const string LAND_STEP_SOUND_PATH = "event:/player/footstep_dirt";
internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((FootstepSounds t) => t.OnStep(default));
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
/*
From:
evt.setVolume(volume);
To:
evt.setVolume(CalculateVolume(volume, this, asset, xform));
From:
event.release();
To:
event.release();
SendFootstepPacket(asset);
*/
return new CodeMatcher(instructions)
.MatchEndForward(
new CodeMatch(OpCodes.Ldloc_1),
new CodeMatch(OpCodes.Call, Reflect.Method((EventInstance evt) => evt.setVolume(default)))
)
.Insert(
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldloc_0),
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Call, Reflect.Method(() => CalculateVolume(default, default, default, default)))
)
.MatchEndForward(
new CodeMatch(OpCodes.Call, Reflect.Method((EventInstance evt) => evt.release())),
new CodeMatch(OpCodes.Pop)
)
.Advance(1)
.InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_0))
.Insert(new CodeInstruction(OpCodes.Call, Reflect.Method(() => SendFootstepPacket(default))))
.InstructionEnumeration();
}
// This method is called very often and should therefore be performant
private static float CalculateVolume(float originalVolume, FootstepSounds instance, FMODAsset asset, Transform xform)
{
// For exosuits only "event:/sub/exo/step" is used inside FootstepSounds
if (asset.path.Equals(LAND_STEP_SOUND_PATH, StringComparison.Ordinal) && (!Player.main.currentMountedVehicle || Player.main.currentMountedVehicle.gameObject != instance.gameObject))
{
// Origin is Exosuit which is controlled by remote player
return FMODSystem.CalculateVolume(xform.position, Player.main.transform.position, stepSoundRadius.Value, originalVolume);
}
return originalVolume;
}
private static readonly Lazy<float> stepSoundRadius = new(() =>
{
Resolve<FMODWhitelist>().TryGetSoundData(LAND_STEP_SOUND_PATH, out SoundData soundData);
return soundData.Radius;
});
private static void SendFootstepPacket(FMODAsset asset)
{
if (!Resolve<LocalPlayer>().PlayerId.HasValue)
{
return;
}
FootstepPacket.StepSounds assetIndex;
switch (asset.path)
{
case PRECURSOR_STEP_SOUND_PATH:
assetIndex = FootstepPacket.StepSounds.PRECURSOR;
break;
case METAL_STEP_SOUND_PATH:
assetIndex = FootstepPacket.StepSounds.METAL;
break;
case LAND_STEP_SOUND_PATH:
assetIndex = FootstepPacket.StepSounds.LAND;
break;
default:
return;
}
FootstepPacket footstepPacket = new(Resolve<LocalPlayer>().PlayerId.Value, assetIndex);
Resolve<IPacketSender>().Send(footstepPacket);
}
}