Skip to content

Commit

Permalink
Merge branch 'master' into docs
Browse files Browse the repository at this point in the history
  • Loading branch information
LeeTwentyThree committed Nov 16, 2024
2 parents 7b2b79c + 198fcc7 commit 39a24dd
Show file tree
Hide file tree
Showing 21 changed files with 3,935 additions and 181 deletions.
2,972 changes: 2,972 additions & 0 deletions Nautilus/Documentation/resources/BZ-FMODEvents.txt

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions Nautilus/Extensions/FModExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using FMOD;
using FMODUnity;
using Nautilus.Patchers;
using Nautilus.Utility;

namespace Nautilus.Extensions;

/// <summary>
/// Contains extension methods for the FMOD system.
/// </summary>
public static class FModExtensions
{
/// <summary>
/// Adds a fade-out point for the specified sound.
/// </summary>
/// <param name="sound">The sound to add a fade-out to</param>
/// <param name="seconds">The duration of the fade-out.</param>
/// <remarks>Fades are only triggered when an emitter respects them. E.G: when calling <c>FMOD_CustomEmitter.Stop(STOP_MODE.ALLOWFADEOUT)</c>.</remarks>
public static void AddFadeOut(this Sound sound, float seconds)
{
if (!sound.hasHandle())
{
InternalLogger.Error("AddFadeOut: Sound object is missing. Please provide a valid sound object.");
return;
}

CustomSoundPatcher.FadeOuts[sound.handle] = new CustomSoundPatcher.FadeInfo(sound, seconds);
}

/// <summary>
/// Adds a fade-out point for the specified channel.
/// </summary>
/// <param name="channel">The channel to add a fade-out to</param>
/// <param name="seconds">The duration of the fade-out. The fade-out starts at the current time.</param>
/// <param name="dspClock">The DSP clock at the point where the fade was added.<br/>
/// DSP clock consists of 48_000 ticks per second. For more information, please refer to the <see href="https://documentation.help/fmod-studio-api/FMOD_Channel_GetDSPClock.html">FMOD docs</see>.</param>
/// <remarks>This method only applies the fade-out one time. If you want the fade to stay everytime the sound is played, consider using <see cref="AddFadeOut(FMOD.Sound,float)"/>.</remarks>
public static void AddFadeOut(this Channel channel, float seconds, out ulong dspClock)
{
if (!channel.hasHandle())
{
InternalLogger.Error("AddFadeOut: Channel object is invalid. Fade operation is cancelled.");
dspClock = 0;
return;
}

RuntimeManager.CoreSystem.getSoftwareFormat(out int samplesRate, out _, out _);

channel.getDSPClock(out _, out ulong parentClock);
channel.addFadePoint(parentClock, 1f);
channel.addFadePoint(parentClock + (ulong)(samplesRate * seconds), 0f);
dspClock = parentClock;
}
}
105 changes: 59 additions & 46 deletions Nautilus/Handlers/CraftTreeHandler.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
namespace Nautilus.Handlers;

using System;
using System.Collections.Generic;
using System.Linq;
using Nautilus.Crafting;
using Nautilus.Patchers;
using Nautilus.Utility;

/// <summary>
/// A handler class for creating and modifying crafting trees.
Expand All @@ -29,25 +32,27 @@ public static void AddCraftingNode(CraftTree.Type craftTree, TechType craftingIt

nodes.Add(new CraftingNode(stepsToTab, craftTree, craftingItem));
CraftTreePatcher.CraftingNodes[craftTree] = nodes;
CraftTreePatcher.CachedTrees.Remove(craftTree);

// If this node had previously been slated for removal, undo that instruction.
if (CraftTreePatcher.NodesToRemove.TryGetValue(craftTree, out List<Node> queuedNodes))
{
var fullPath = stepsToTab.Append(craftingItem.AsString(false));
int removedNodes = queuedNodes.RemoveAll(node => node.Path.SequenceEqual(fullPath));
if (removedNodes > 0)
{
InternalLogger.Debug($"Removal of CraftNode at {string.Join("/", fullPath)} overwritten by new custom CraftNode.");
}
}
}

/// <summary>
/// Adds a new crafting node to the root of the specified crafting tree
/// </summary>
/// <param name="craftTree">The target craft tree to edit.</param>
/// <param name="craftingItem">The item to craft.</param>

public static void AddCraftingNode(CraftTree.Type craftTree, TechType craftingItem)
{
if (!CraftTreePatcher.CraftingNodes.TryGetValue(craftTree, out var nodes))
{
nodes = new List<CraftingNode>();
}

nodes.Add(new CraftingNode(new string[0], craftTree, craftingItem));
CraftTreePatcher.CraftingNodes[craftTree] = nodes;
CraftTreePatcher.CachedTrees.Remove(craftTree);
AddCraftingNode(craftTree, craftingItem, Array.Empty<string>());
}

#if SUBNAUTICA
Expand All @@ -60,14 +65,7 @@ public static void AddCraftingNode(CraftTree.Type craftTree, TechType craftingIt
/// <param name="sprite">The sprite of the tab.</param>
public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, Atlas.Sprite sprite)
{
if (!CraftTreePatcher.TabNodes.TryGetValue(craftTree, out var craftTreeTabNodes))
{
craftTreeTabNodes = new List<TabNode>();
}

craftTreeTabNodes.Add(new TabNode(new string[0], craftTree, sprite, name, displayName));
CraftTreePatcher.TabNodes[craftTree] = craftTreeTabNodes;
CraftTreePatcher.CachedTrees.Remove(craftTree);
AddTabNode(craftTree, name, displayName, sprite, Array.Empty<string>());
}

/// <summary>
Expand All @@ -77,17 +75,9 @@ public static void AddTabNode(CraftTree.Type craftTree, string name, string disp
/// <param name="name">The ID of the tab node. Must be unique!</param>
/// <param name="displayName">The display name of the tab, which will show up when you hover your mouse on the tab. If null or empty, this will use the language line "{craftTreeName}_{tabName}" instead.</param>
/// <param name="sprite">The sprite of the tab.</param>

public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, UnityEngine.Sprite sprite)
{
if (!CraftTreePatcher.TabNodes.TryGetValue(craftTree, out var craftTreeTabNodes))
{
craftTreeTabNodes = new List<TabNode>();
}

craftTreeTabNodes.Add(new TabNode(new string[0], craftTree, new Atlas.Sprite(sprite), name, displayName));
CraftTreePatcher.TabNodes[craftTree] = craftTreeTabNodes;
CraftTreePatcher.CachedTrees.Remove(craftTree);
AddTabNode(craftTree, name, displayName, new Atlas.Sprite(sprite), Array.Empty<string>());
}

/// <summary>
Expand All @@ -112,7 +102,17 @@ public static void AddTabNode(CraftTree.Type craftTree, string name, string disp

craftTreeTabNodes.Add(new TabNode(stepsToTab, craftTree, sprite, name, displayName));
CraftTreePatcher.TabNodes[craftTree] = craftTreeTabNodes;
CraftTreePatcher.CachedTrees.Remove(craftTree);

// If this node had previously been slated for removal, undo that instruction.
if (CraftTreePatcher.NodesToRemove.TryGetValue(craftTree, out List<Node> queuedNodes))
{
var fullPath = stepsToTab.Append(name);
int removedNodes = queuedNodes.RemoveAll(node => node.Path.SequenceEqual(fullPath));
if (removedNodes > 0)
{
InternalLogger.Debug($"Removal of TabNode at {string.Join("/", fullPath)} overwritten by new custom TabNode.");
}
}
}

/// <summary>
Expand All @@ -130,14 +130,7 @@ public static void AddTabNode(CraftTree.Type craftTree, string name, string disp
/// </param>
public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, UnityEngine.Sprite sprite, params string[] stepsToTab)
{
if (!CraftTreePatcher.TabNodes.TryGetValue(craftTree, out var craftTreeTabNodes))
{
craftTreeTabNodes = new List<TabNode>();
}

craftTreeTabNodes.Add(new TabNode(stepsToTab, craftTree, new Atlas.Sprite(sprite), name, displayName));
CraftTreePatcher.TabNodes[craftTree] = craftTreeTabNodes;
CraftTreePatcher.CachedTrees.Remove(craftTree);
AddTabNode(craftTree, name, displayName, new Atlas.Sprite(sprite), stepsToTab);
}

#elif BELOWZERO
Expand All @@ -150,14 +143,7 @@ public static void AddTabNode(CraftTree.Type craftTree, string name, string disp
/// <param name="sprite">The sprite of the tab.</param>
public static void AddTabNode(CraftTree.Type craftTree, string name, string displayName, UnityEngine.Sprite sprite)
{
if (!CraftTreePatcher.TabNodes.TryGetValue(craftTree, out var craftTreeTabNodes))
{
craftTreeTabNodes = new List<TabNode>();
}

craftTreeTabNodes.Add(new TabNode(new string[0], craftTree, sprite, name, displayName));
CraftTreePatcher.TabNodes[craftTree] = craftTreeTabNodes;
CraftTreePatcher.CachedTrees.Remove(craftTree);
AddTabNode(craftTree, name, displayName, sprite, Array.Empty<string>());
}

/// <summary>
Expand All @@ -182,7 +168,17 @@ public static void AddTabNode(CraftTree.Type craftTree, string name, string disp

craftTreeTabNodes.Add(new TabNode(stepsToTab, craftTree, sprite, name, displayName));
CraftTreePatcher.TabNodes[craftTree] = craftTreeTabNodes;
CraftTreePatcher.CachedTrees.Remove(craftTree);

// If this node had previously been slated for removal, undo that instruction.
if (CraftTreePatcher.NodesToRemove.TryGetValue(craftTree, out List<Node> queuedNodes))
{
var fullPath = stepsToTab.Append(name);
int removedNodes = queuedNodes.RemoveAll(node => node.Path.SequenceEqual(fullPath));
if (removedNodes > 0)
{
InternalLogger.Debug($"Removal of TabNode at {string.Join("/", fullPath)} overwritten by new custom TabNode.");
}
}
}

#endif
Expand All @@ -209,7 +205,24 @@ public static void RemoveNode(CraftTree.Type craftTree, params string[] stepsToN

nodesToRemove.Add(new Node(stepsToNode, craftTree));
CraftTreePatcher.NodesToRemove[craftTree] = nodesToRemove;
CraftTreePatcher.CachedTrees.Remove(craftTree);

// If this is a previously registered custom node, undo that instruction.
// This avoids accumulation of instructions that cancel each other out.
int removedNodes = 0;
if (CraftTreePatcher.CraftingNodes.TryGetValue(craftTree, out List<CraftingNode> craftingNodes))
{
removedNodes += craftingNodes.RemoveAll(node => node.Path.Append(node.TechType.ToString()).SequenceEqual(stepsToNode));
}

if (CraftTreePatcher.TabNodes.TryGetValue(craftTree, out List<TabNode> tabNodes))
{
removedNodes += tabNodes.RemoveAll(node => node.Path.Append(node.Id).SequenceEqual(stepsToNode));
}

if (removedNodes > 0)
{
InternalLogger.Debug($"Removed another mod's custom node at {string.Join("/", stepsToNode)} from future craft trees.");
}
}

/// <summary>
Expand Down
37 changes: 37 additions & 0 deletions Nautilus/Handlers/CustomSoundHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,41 @@ public static bool TryGetCustomSoundChannel(int id, out Channel channel)
{
return CustomSoundPatcher.EmitterPlayedChannels.TryGetValue(id, out channel);
}


/// <summary>
/// Attaches the specified channel to the given transform. This results in the sound position following the <paramref name="transform"/>.
/// </summary>
/// <param name="channel">The channel to attach.</param>
/// <param name="transform">The transform which the channel will follow.</param>
public static void AttachChannelToGameObject(Channel channel, Transform transform)
{
var index = CustomSoundPatcher.AttachedChannels.FindIndex(x => x.Channel.handle == channel.handle);
var attachedChannel = new CustomSoundPatcher.AttachedChannel(channel, transform);
if (index == -1)
{
CustomSoundPatcher.AttachedChannels.Add(attachedChannel);
}
else
{
CustomSoundPatcher.AttachedChannels[index] = attachedChannel;
}

CustomSoundPatcher.SetChannel3DAttributes(channel, transform);
}

/// <summary>
/// Detaches the specified channel from any game object.
/// </summary>
/// <param name="channel">The channel to detach.</param>
public static void DetachChannelFromGameObject(Channel channel)
{
var index = CustomSoundPatcher.AttachedChannels.FindIndex(x => x.Channel.handle == channel.handle);
if (index == -1)
{
InternalLogger.Warn($"{nameof(CustomSoundHandler)}: The specified channel is not attached to any game object.");
}

CustomSoundPatcher.AttachedChannels.RemoveAt(index);
}
}
4 changes: 3 additions & 1 deletion Nautilus/Handlers/KnownTechHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ public static void SetAnalysisTechEntry(KnownTech.AnalysisTech analysisTech)
AddAnalysisTech(analysisTech);
}

#if SUBNAUTICA
/// <summary>
/// Allows you to define which TechTypes are unlocked when a certain TechType is unlocked, i.e., "analysed".
/// If there is already an existing AnalysisTech entry for a TechType, all the TechTypes in "techTypesToUnlock" will be
Expand All @@ -329,8 +330,9 @@ public static void SetAnalysisTechEntry(KnownTech.AnalysisTech analysisTech)
/// <param name="storyGoals">The story goals that will be triggered when you unlock the blueprint.</param>
public static void SetAnalysisTechEntry(TechType techTypeToBeAnalysed, IEnumerable<TechType> techTypesToUnlock, FMODAsset unlockSound, Sprite unlockSprite, List<StoryGoal> storyGoals)
{
AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock, "NotificationBlueprintUnlocked", unlockSound, unlockSprite);
AddAnalysisTech(techTypeToBeAnalysed, techTypesToUnlock, "NotificationBlueprintUnlocked", unlockSound, unlockSprite, storyGoals);
}
#endif

/// <summary>
/// Allows you to set up a custom Compound Unlock requiring multiple techtypes to be unlocked before 1 is.
Expand Down
7 changes: 6 additions & 1 deletion Nautilus/Initializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ namespace Nautilus;
/// <summary>
/// WARNING: This class is for use only by BepInEx.
/// </summary>
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
#if BELOWZERO
[BepInProcess("SubnauticaZero.exe")]
#else
[BepInProcess("Subnautica.exe")]
#endif
public class Initializer : BaseUnityPlugin
{
private static readonly Harmony _harmony = new(PluginInfo.PLUGIN_GUID);
Expand Down
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
39 changes: 39 additions & 0 deletions Nautilus/Patchers/CraftDataPatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,45 @@ internal static void Patch(Harmony harmony)

InternalLogger.Log("CraftDataPatcher is done.", LogLevel.Debug);
}

[HarmonyPatch(typeof(uGUI_CraftingMenu), nameof(uGUI_CraftingMenu.IsGrid))]
[HarmonyPrefix]
private static bool ShouldGridPostfix(uGUI_CraftingMenu.Node node, ref bool __result)
{
__result = ShouldGrid();
return false;

bool ShouldGrid()
{
var craftings = 0;
var tabs = 0;

foreach (var child in node)
{
if (child.action == TreeAction.Expand)
{
tabs++;
}
else if (child.action == TreeAction.Craft)
{
craftings++;
}
}

return craftings > tabs;
}
}

[HarmonyPatch(typeof(uGUI_CraftingMenu), nameof(uGUI_CraftingMenu.Collapse))]
[HarmonyPostfix]
private static void CollapsePostfix(uGUI_CraftingMenu.Node parent)
{
if (parent == null) return;

if (parent.action != TreeAction.Craft) return;

parent.icon.SetActive(false);
}

[HarmonyPrefix]
[HarmonyPatch(typeof(CraftData), nameof(CraftData.GetTechType), new Type[] { typeof(GameObject), typeof(GameObject) }, argumentVariations: new ArgumentType[] { ArgumentType.Normal, ArgumentType.Out })]
Expand Down
Loading

0 comments on commit 39a24dd

Please sign in to comment.