Skip to content

Commit

Permalink
Added FMOD support for playable behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
Metious committed Jul 22, 2024
1 parent 09d1d4e commit ae520f2
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 0 deletions.
1 change: 1 addition & 0 deletions Nautilus/Nautilus.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<PackageReference Include="PolySharp" Version="1.13.1" PrivateAssets="all" />

<Publicize Include="Newtonsoft.Json" />
<Publicize Include="FMODUnity" />
</ItemGroup>

<ItemGroup>
Expand Down
174 changes: 174 additions & 0 deletions Nautilus/Patchers/CustomSoundPatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal record struct AttachedChannel(Channel Channel, Transform Transform);
internal static List<AttachedChannel> AttachedChannels = new();

private static readonly Dictionary<string, Channel> PlayedChannels = new();
private static readonly Dictionary<FMODEventPlayableBehavior, Channel> PlayableBehaviorChannels = new();
private static readonly List<AttachedChannel> _attachedChannelsToRemove = new();

internal static void Patch(Harmony harmony)
Expand Down Expand Up @@ -74,7 +75,180 @@ public static bool FMODExtension_GetLength_Prefix(string path, ref int __result)
__result = 0;
return false;
}

[HarmonyPatch(typeof(FMODEventPlayableBehavior), nameof(FMODEventPlayableBehavior.PerformSeek))]
[HarmonyPrefix]
public static bool FMODEventPlayableBehavior_PerformSeek_Prefix(FMODEventPlayableBehavior __instance)
{
if (!PlayableBehaviorChannels.TryGetValue(__instance, out var channel))
{
return true;
}

if (__instance.seek < 0)
{
return true;
}

channel.setPosition((uint)__instance.seek, TIMEUNIT.MS);
__instance.seek = -1;
return false;
}

[HarmonyPatch(typeof(FMODEventPlayableBehavior), nameof(FMODEventPlayableBehavior.PlayEvent))]
[HarmonyPrefix]
public static bool FMODEventPlayableBehavior_PlayEvent_Prefix(FMODEventPlayableBehavior __instance)
{
if (string.IsNullOrEmpty(__instance.eventName))
{
return true;
}

if (string.IsNullOrEmpty(__instance.eventName) || !CustomSounds.TryGetValue(__instance.eventName, out Sound soundEvent)
&& !CustomFModSounds.ContainsKey(__instance.eventName)) return true;

Channel channel;
if (CustomFModSounds.TryGetValue(__instance.eventName, out var fModSound))
{
if (!fModSound.TryPlaySound(out channel))
return false;
}
else if (CustomSoundBuses.TryGetValue(__instance.eventName, out Bus bus))
{
if (!AudioUtils.TryPlaySound(soundEvent, bus, out channel))
return false;
}
else
{
return false;
}

PlayableBehaviorChannels[__instance] = channel;

channel.setPaused(true);

__instance.PerformSeek();

if (__instance.TrackTargetObject)
{
CustomSoundHandler.AttachChannelToGameObject(channel, __instance.TrackTargetObject.transform);
}
else
{
SetChannel3DAttributes(channel, Vector3.zero);
}

channel.setVolume(__instance.currentVolume);

channel.setPaused(false);

return false;
}

[HarmonyPatch(typeof(FMODEventPlayableBehavior), nameof(FMODEventPlayableBehavior.OnExit))]
[HarmonyPrefix]
public static bool FMODEventPlayableBehavior_OnExit_Prefix(FMODEventPlayableBehavior __instance)
{
if (!PlayableBehaviorChannels.TryGetValue(__instance, out var channel))
{
return true;
}

if (!__instance.isPlayheadInside)
{
return false;
}

if (__instance.stopType != FMODUnity.STOP_MODE.None)
{
channel.stop();
}

PlayableBehaviorChannels.Remove(__instance);
__instance.isPlayheadInside = false;

return false;
}

[HarmonyPatch(typeof(FMODEventPlayableBehavior), nameof(FMODEventPlayableBehavior.ProcessFrame))]
[HarmonyPrefix]
public static bool FMODEventPlayableBehavior_ProcessFrame_Prefix(FMODEventPlayableBehavior __instance)
{
return !PlayableBehaviorChannels.ContainsKey(__instance);
}

[HarmonyPatch(typeof(FMODEventPlayableBehavior), nameof(FMODEventPlayableBehavior.UpdateBehavior))]
[HarmonyPrefix]
public static bool FMODEventPlayableBehavior_UpdateBehavior_Prefix(FMODEventPlayableBehavior __instance, float time, float volume)
{
if (!PlayableBehaviorChannels.TryGetValue(__instance, out var channel))
{
return true;
}

if (volume != __instance.currentVolume)
{
__instance.currentVolume = volume;
channel.setVolume(volume);
}

if (time >= __instance.OwningClip.start && time < __instance.OwningClip.end)
{
__instance.OnEnter();
}
else
{
__instance.OnExit();
}

return false;
}

[HarmonyPatch(typeof(FMODEventPlayableBehavior), nameof(FMODEventPlayableBehavior.OnGraphStop))]
[HarmonyPostfix]
public static void FMODEventPlayableBehavior_OnGraphStop_Postfix(FMODEventPlayableBehavior __instance)
{
if (!PlayableBehaviorChannels.TryGetValue(__instance, out var channel))
{
channel.stop();
PlayableBehaviorChannels.Remove(__instance);
}
}

[HarmonyPatch(typeof(FMODEventPlayableBehavior), nameof(FMODEventPlayableBehavior.Evaluate))]
[HarmonyPrefix]
public static bool FMODEventPlayableBehavior_Evaluate_Postfix(FMODEventPlayableBehavior __instance, double time, FrameData info, bool evaluate)
{
if (!PlayableBehaviorChannels.TryGetValue(__instance, out var channel))
{
return true;
}

if (!info.timeHeld && time >= __instance.OwningClip.start && time < __instance.OwningClip.end)
{
if (!__instance.isPlayheadInside)
{
if (time - __instance.OwningClip.start > 0.1)
{
__instance.seek = __instance.GetPosition(time);
}
__instance.OnEnter();
return false;
}
if ((evaluate || info.seekOccurred || info.timeLooped || info.evaluationType == FrameData.EvaluationType.Evaluate))
{
__instance.seek = __instance.GetPosition(time);
__instance.PerformSeek();
return false;
}
}
else
{
__instance.OnExit();
}

return false;
}

[HarmonyPatch(typeof(RuntimeManager), nameof(RuntimeManager.Update))]
[HarmonyPostfix]
Expand Down

0 comments on commit ae520f2

Please sign in to comment.