### .NET SDK Version Output Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/dev-setup.md Example output displayed after running the dotnet version command. ```bash 7.0.102 ``` -------------------------------- ### Build and serve documentation Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/README.md Builds the documentation site using the specified configuration file and starts a local web server. ```powershell docfx Nautilus/docfx.json --serve ``` -------------------------------- ### Verify .NET SDK Installation Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/dev-setup.md Use this command in your terminal to confirm that the .NET SDK is correctly installed and to check the current version. ```bash dotnet --version ``` -------------------------------- ### Install docfx tool Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/README.md Installs the docfx tool globally using the .NET CLI. ```powershell dotnet tool update -g docfx ``` -------------------------------- ### English JSON Localization Example Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/localization.md Example of an English localization JSON file. Keys are identifiers, and values are the English translations. ```json // English.json { "TitaniumClone": "Titanium Clone", "Tooltip_TitaniumClone": "Titanium clone that makes me go yes." } ``` -------------------------------- ### Create and Register Custom Prefab with Nautilus Source: https://context7.com/subnauticamodding/nautilus/llms.txt This snippet demonstrates how to create a custom prefab by cloning an existing game object, modifying its appearance, setting biome spawns, and registering it with Nautilus. Ensure BepInEx and Nautilus are installed. ```csharp using BepInEx; using Nautilus.Assets; using Nautilus.Assets.Gadgets; using Nautilus.Assets.PrefabTemplates; using Nautilus.Crafting; using UnityEngine; using BiomeData = LootDistributionData.BiomeData; [BepInPlugin("com.example.mymod", "My Mod", "1.0.0")] [BepInDependency("com.snmodding.nautilus")] public class MyMod : BaseUnityPlugin { private void Awake() { // Create prefab info with TechType, display name, and description PrefabInfo copperCloneInfo = PrefabInfo.WithTechType("CopperClone", "Red Copper Ore", "A copper ore with a red tint.") .WithIcon(SpriteManager.Get(TechType.Copper)); // Create the custom prefab CustomPrefab copperClone = new CustomPrefab(copperCloneInfo); // Clone an existing game object and modify it CloneTemplate cloneTemplate = new CloneTemplate(copperCloneInfo, TechType.Copper) { ModifyPrefab = prefab => prefab.GetComponentsInChildren() .ForEach(r => r.materials.ForEach(m => m.color = Color.red)) }; copperClone.SetGameObject(cloneTemplate); // Add biome spawns for the item copperClone.SetSpawns( new BiomeData { biome = BiomeType.SafeShallows_Grass, count = 4, probability = 0.1f }, new BiomeData { biome = BiomeType.SafeShallows_CaveFloor, count = 1, probability = 0.4f } ); // Register to the game copperClone.Register(); } } ``` -------------------------------- ### Spanish JSON Localization Example Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/localization.md Example of a Spanish localization JSON file. Keys are identifiers, and values are the Spanish translations. ```json // Spanish.json { "TitaniumClone": "Clon de Titanio", "Tooltip_TitaniumClone": "Clon de Titanio que me hace decir que sí" } ``` -------------------------------- ### Add NuGet Source Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/dev-setup.md If you encounter errors related to NuGet sources, run this command to add the official NuGet source. This is a prerequisite for installing templates. ```bash dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org ``` -------------------------------- ### Create and Register Custom Prefab with Asset Bundle Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/assetbundles.md This example shows how to create a full custom prefab using Nautilus, load a GameObject from an Asset Bundle, apply necessary components and shaders, and register it with a recipe and PDA entry. Ensure all required namespaces are imported. ```csharp using Nautilus.Assets; using Nautilus.Assets.Gadgets; using Nautilus.Crafting; using Nautilus.Utility; using UnityEngine; using Ingredient = CraftData.Ingredient; namespace ExamplePrefab { internal static class MyCoolPrefab { public static PrefabInfo MyPrefabInfo { get; private set; } public static void Patch() { PrefabInfo prefabInfo = PrefabInfo.WithTechType("MyCoolPrefab", "My Cool Prefab", "Pretty cool, right!") .WithIcon(SpriteManager.Get(TechType.Titanium)); // Just using the Titanium sprite as a placeholder // Cache the tech type for use in other places MyPrefabInfo = prefabInfo; var prefab = new CustomPrefab(prefabInfo); // Create the recipe RecipeData recipe = new RecipeData { craftAmount = 1, Ingredients = { new Ingredient(TechType.Titanium, 2), new Ingredient(TechType.CopperWire, 2), }, }; // Set the prefab GamrObject to the result of the GetAssetBundlePrefab method prefab.SetGameObject(GetAssetBundlePrefab()); // Using the Seaglide as a placeholder unlock prefab.SetUnlock(TechType.Seaglide); // Set the recipe prefab.SetRecipe(recipe) .WithCraftingTime(6f); // Add the prefab to the Miscellaneous tab of the blueprints in the PDA prefab.SetPdaGroupCategory(TechGroup.Miscellaneous, TechCategory.Misc); // Register the prefab to the Nautilus prefab database prefab.Register(); } private static GameObject GetAssetBundlePrefab() { GameObject myCoolPrefab = assetBundle.LoadAsset("myCoolPrefab"); // The classID is the same as the one we put into the PrefabInfo.WithTechType up above // The LargeWorldEntity.CellLevel determines how far away the object will be loaded from the player PrefabUtils.AddBasicComponents(myCoolPrefab, MyPrefabInfo.ClassID, MyPrefabInfo.TechType, LargeWorldEntity.CellLevel.Medium); // Makes the GameObject have the correct shaders // You can use the optional inputs here to change the look of your object MaterialUtils.ApplySNShaders(myCoolPrefab); // Allows the object to be picked up myCoolPrefab.AddComponent(); // Return the GameObject with all the components added return myCoolPrefab; } } } ``` -------------------------------- ### Install Subnautica Modding Templates Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/dev-setup.md Run this command in your terminal to install the Subnautica Modding templates. Ensure you have .NET SDK installed. ```bash dotnet new install Subnautica.Templates ``` -------------------------------- ### List Installed Templates Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/dev-setup.md This output displays the available Subnautica modding templates after installation, including their short names, languages, and tags. ```bash Template Name Short Name Language Tags ---------------------------------------- -------------- -------- --------------------------------------------- Subnautica Empty Template snmod_empty [C#] SNModding/Subnautica/Mod Subnautica Nautilus Template snmod_nautilus [C#] SNModding/Subnautica/Mod/Nautilus Subnautica Template snmod [C#] SNModding/Subnautica/Mod Subnautica: Below Zero Empty Template bzmod_empty [C#] SNModding/Subnautica: Below Zero/Mod Subnautica: Below Zero Nautilus Template bzmod_nautilus [C#] SNModding/Subnautica: Below Zero/Mod/Nautilus Subnautica: Below Zero Template bzmod [C#] SNModding/Subnautica: Below Zero/Mod ``` -------------------------------- ### Create Custom Fabricator Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/sml2-to-nautilus.md This example shows how to create a custom fabricator with a unique model, texture, blueprint recipe, and PDA categorization. It extends the CustomFabricator class and overrides methods to define its behavior and appearance. ```csharp public class AbyssFabricator : CustomFabricator { private static Texture2D texture; public override Models Model { get; } = Models.Fabricator; public AbyssFabricator() : base("AbyssFabricator", "Abyss Fabricator", "Abyss Batteries Fabricator") { OnStartedPatching += () => { texture = ImageUtils.LoadTextureFromFile(Path.Combine(Main.AssetsFolder, "AbyssFabricatorskin.png")); }; } public override GameObject GetGameObject() { GameObject prefab = base.GetGameObject(); if (texture != null) { SkinnedMeshRenderer skinnedMeshRenderer = prefab.GetComponentInChildren(); skinnedMeshRenderer.material.mainTexture = texture; } return prefab; } protected override TechData GetBlueprintRecipe() { return new TechData { craftAmount = 1, Ingredients = new List { new Ingredient(TechType.Titanium, 2), new Ingredient(TechType.Quartz, 2), new Ingredient(TechType.JeweledDiskPiece, 1), } }; } protected override Atlas.Sprite GetItemSprite() { return SpriteManager.Get(TechType.Fabricator); } public override TechCategory CategoryForPDA { get; } = TechCategory.InteriorModule; public override TechGroup GroupForPDA { get; } = TechGroup.InteriorModules; } ``` -------------------------------- ### Configure Crafting Tree Paths Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/craft-tree-paths.md Examples of setting crafting tree paths for fabricators using the CraftingGadget API. ```csharp // EX1: Add the prefab to the Advanced Materials tab. craftingGadget.WithStepsToFabricatorTab("Resources", "AdvancedMaterials"); // EX2: Add the prefab to the root of the fabricator. craftingGadget.WithStepsToFabricatorTab(); // EX3: Add the prefab to the Vehicles tab of the mobile vehicle bay. craftingGadget.WithFabricatorType(CraftTree.Type.Constructor) .WithStepsToFabricatorTab("Vehicles"); ``` -------------------------------- ### Configure Game Directory for Post-Build Events Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/csproj-tutorials.md Create a 'GameDir.targets' file to specify your Subnautica installation directory. This file is used by post-build scripts to locate the plugins folder. ```xml C:\ Program Files (x86)\Steam\steamapps\common\Subnautica ``` -------------------------------- ### Add Databank Entry with Custom Path Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/databank-entries.md Register a new Databank entry using `PDAHandler.AddEncyclopediaEntry`. This example shows how to add entries to custom paths like 'Lifeforms/Fauna/Pets' and its subcategory 'Robotic Pets'. ```csharp // Adds "Doggo" to the "Pets" category: PDAHandler.AddEncyclopediaEntry("Doggo", "Lifeforms/Fauna/Pets" ...); // Adds "Robot Dog" to the "Robotic" category which is a subcategory of "Pets": PDAHandler.AddEncyclopediaEntry("Robot Dog", "Lifeforms/Fauna/Pets/Robotic" ...); ``` -------------------------------- ### Configure Crafting Recipes with Nautilus Source: https://context7.com/subnauticamodding/nautilus/llms.txt This snippet shows how to set crafting recipes using Nautilus. It covers simple recipes, recipes yielding multiple items, and configuring recipes for custom prefabs with fabricator type and crafting time. Ensure Nautilus is installed. ```csharp using Nautilus.Crafting; using Nautilus.Handlers; // Set a simple recipe for Titanium Ingot RecipeData titaniumIngotRecipe = new RecipeData(new CraftData.Ingredient(TechType.Titanium, 2)); CraftDataHandler.SetRecipeData(TechType.TitaniumIngot, titaniumIngotRecipe); // Create a recipe that yields multiple linked items RecipeData scrapMetalRecipe = new RecipeData { craftAmount = 0, // Don't yield the main item Ingredients = { new CraftData.Ingredient(TechType.ScrapMetal) }, LinkedItems = Enumerable.Repeat(TechType.Titanium, 10).ToList() // Yield 10 titaniums }; CraftDataHandler.SetRecipeData(TechType.ScrapMetal, scrapMetalRecipe); // For custom prefabs, use the fluent API CustomPrefab myItem = new CustomPrefab(prefabInfo); myItem.SetRecipe(new RecipeData( new CraftData.Ingredient(TechType.Titanium, 3), new CraftData.Ingredient(TechType.Copper, 2))) .WithFabricatorType(CraftTree.Type.Fabricator) .WithStepsToFabricatorTab("Resources", "BasicMaterials") .WithCraftingTime(3f); ``` -------------------------------- ### Implement Instant Message Sender Module Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/vehicle-module.md Sets up a vehicle upgrade module that displays a "Hello world!" message when activated. Uses 'EquipmentType.VehicleModule' and 'QuickSlotType.Selectable'. The charge parameters are unused in this configuration. ```csharp prefab.SetVehicleUpgradeModule(EquipmentType.VehicleModule, QuickSlotType.Selectable) .WithOnModuleUsed((Vehicle inst, int slotID, float _charge, float _chargeScalar) => // charge and chargeScalar are always 0f here. { Subtitles.Add("Hello world!"); }); ``` -------------------------------- ### Initialize Prefabs in Plugin Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/simple-mod.md Registering custom items within the main plugin entry point. ```csharp // Plugin.cs private void InitializePrefabs() { Coal.Register(); YeetKnifePrefab.Register(); } ``` -------------------------------- ### Spawn a Peeper using CraftData.GetPrefabForTechTypeAsync Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/loading-and-instantiating-prefabs.md Demonstrates the full workflow of fetching, awaiting, and instantiating a prefab in front of the player camera. ```csharp private static IEnumerator SpawnPeeper() { // Fetch the prefab: CoroutineTask task = CraftData.GetPrefabForTechTypeAsync(TechType.Peeper); // Wait for the prefab task to complete: yield return task; // Get the prefab: GameObject prefab = task.GetResult(); // Instantiate the prefab with a random rotation 2 meters in front of the player camera: Instantiate(prefab, MainCamera.camera.transform.position + (MainCamera.camera.transform.forward * 2), Random.rotation); } ``` ```csharp private static IEnumerator SpawnPeeper() { var task = CraftData.GetPrefabForTechTypeAsync(TechType.Peeper); yield return task; var prefab = task.GetResult(); } ``` -------------------------------- ### Create MusicTitleAddon Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/title-addons.md Instantiates a music addon using an asset from a loaded bundle. ```cs private MusicTitleAddon GetMusicAddon() { return new MusicTitleAddon(MyAssetBundle.LoadAsset("MyMusic")); } ``` -------------------------------- ### Define Prefab Info for a Custom Module Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/vehicle-module.md Initializes the metadata for a new custom module, including its TechType, display name, description, and icon. ```csharp var prefabInfo = PrefabInfo.WithTechType("SeamothDepthUpgrade", "Seamoth Depth Module MK.4", "Dive down to 1700 meters!!! Let's meet the Sea Dragon!") .WithIcon(SpriteManager.Get(TechType.HullReinforcementModule3)); CustomPrefab prefab = new CustomPrefab(prefabInfo); ``` -------------------------------- ### Migrate Equipable to Nautilus Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/sml2-to-nautilus.md Example of an SML 2.0 Equipable class that would be migrated to the Nautilus CustomPrefab and EquipmentGadget system. ```csharp public class SeamothBrineResistanceModule : Equipable { public static TechType TechTypeID { get; protected set; } public SeamothBrineResistanceModule() : base("SeamothBrineResistModule", "Seamoth brine resistant coating", "Makes the Seamoth resistant to corrosive brine pools, by means of a protective coating.") { OnFinishedPatching += () => { TechTypeID = this.TechType; }; } public override EquipmentType EquipmentType => EquipmentType.SeamothModule; public override TechType RequiredForUnlock => TechType.BaseUpgradeConsole; public override TechGroup GroupForPDA => TechGroup.VehicleUpgrades; public override TechCategory CategoryForPDA => TechCategory.VehicleUpgrades; public override CraftTree.Type FabricatorType => CraftTree.Type.SeamothUpgrades; public override string[] StepsToFabricatorTab => new string[] { "SeamothModules" }; public override QuickSlotType QuickSlotType => QuickSlotType.Passive; public override GameObject GetGameObject() { var prefab = CraftData.GetPrefabForTechType(TechType.SeamothElectricalDefense); var obj = GameObject.Instantiate(prefab); return obj; } protected override TechData GetBlueprintRecipe() { return new TechData() { craftAmount = 1, Ingredients = { new Ingredient(TechType.Polyaniline, 1), new Ingredient(TechType.CopperWire, 2), new Ingredient(TechType.AluminumOxide, 2), new Ingredient(TechType.Nickel, 1), }, }; } } ``` -------------------------------- ### Create Mod Options Menus Source: https://context7.com/subnauticamodding/nautilus/llms.txt Implement configuration menus using ConfigFile attributes for simple settings or the ModOptions class for advanced UI control. ```csharp using Nautilus.Json; using Nautilus.Options; using Nautilus.Options.Attributes; using Nautilus.Handlers; // Simple approach using ConfigFile with attributes [Menu("My Mod Settings")] public class MyConfig : ConfigFile { [Slider("Damage Multiplier", 0.5f, 3.0f, DefaultValue = 1.0f, Format = "{0:F1}x")] public float DamageMultiplier = 1.0f; [Toggle("Enable Feature")] public bool FeatureEnabled = true; [Choice("Difficulty", "Easy", "Normal", "Hard")] public string Difficulty = "Normal"; [Keybind("Activate Hotkey")] public KeyCode ActivateKey = KeyCode.F5; } // Register in your plugin public class MyPlugin : BaseUnityPlugin { public static MyConfig Config { get; private set; } private void Awake() { Config = OptionsPanelHandler.RegisterModOptions(); } } // Advanced approach using ModOptions for more control public class AdvancedModOptions : ModOptions { public AdvancedModOptions() : base("Advanced Mod Options") { var slider = ModSliderOption.Create("speed", "Movement Speed", 0, 100, 50, 50); slider.OnChanged += (sender, args) => { /* Handle change */ }; AddItem(slider); AddItem(ModToggleOption.Create("godmode", "God Mode", false)); AddItem(ModChoiceOption.Create("mode", "Game Mode", new string[] { "Survival", "Creative", "Hardcore" }, 0)); } } ``` -------------------------------- ### Change Plugin GUID in csproj Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/csproj-tutorials.md Add the 'BepInExPluginGuid' tag to your csproj file to set your mod's unique identifier. Use reverse-DNS naming for consistency. ```xml com.snmodding.projectneptune ``` -------------------------------- ### Initialize FModSoundBuilder Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/audio.md Create a builder instance using a previously defined CustomSoundSource. ```csharp FModSoundBuilder builder = new FModSoundBuilder(soundSource); ``` -------------------------------- ### Configure Custom Tool Recipe Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/simple-mod.md Setting up a recipe for a custom tool that requires specific ingredients and a fabricator type. ```csharp // YeetKnifePrefab.cs ... public static void Register() { var customPrefab = new CustomPrefab(Info); var yeetKnifeObj = new CloneTemplate(Info, TechType.HeatBlade); yeetKnifeObj.ModifyPrefab += obj => { var heatBlade = obj.GetComponent(); var yeetKnife = obj.AddComponent().CopyComponent(heatBlade); Object.DestroyImmediate(heatBlade); yeetKnife.damage *= 2f; }; customPrefab.SetGameObject(yeetKnifeObj); // Recipe requires 1 Heat blade and 4 Coal. var recipe = new RecipeData(new Ingredient(TechType.HeatBlade), new Ingredient(Coal.Info.TechType, 4)); customPrefab.SetRecipe(recipe) .WithFabricatorType(CraftTree.Type.Workbench); customPrefab.SetEquipment(EquipmentType.Hand); customPrefab.Register(); } ``` -------------------------------- ### Initialize Mod Project Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/simple-mod.md Commands to create a new mod project directory and generate the project files for Subnautica or Subnautica Below Zero. ```powershell mkdir YourProjectName ``` ```powershell dotnet new snmod_nautilus ``` ```powershell dotnet new bzmod_nautilus ``` -------------------------------- ### Define Audio Source Implementations Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/audio.md Use AssetBundleSoundSource for bundled assets or ModFolderSoundSource for direct file loading from the mod directory. ```csharp // Load the asset bundle (keep in mind, you should cache the AssetBundle in an actual project, and never load one twice) AssetBundle bundle = AssetBundleLoadingUtils.LoadFromAssetsFolder(Assembly, "assetbundlename"); // Tell the system to load clips from the given asset bundle by their name CustomSoundSourceBase soundSource = new AssetBundleSoundSource(bundle); ``` ```csharp // Tell the system to load clips from a folder called "SoundsFolder", which is directly inside your mod folder CustomSoundSourceBase soundSource = new ModFolderSoundSource("SoundsFolder"); ``` -------------------------------- ### Import GameDir.targets into csproj Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/csproj-tutorials.md Import the 'GameDir.targets' file into your csproj to make the 'GameDir' variable available for use in build events. ```xml ``` -------------------------------- ### Create a ConfigFile with Slider Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/options.md Defines a configuration class inheriting from ConfigFile, using attributes to automatically generate a slider in the game's options menu. ```csharp using Nautilus.Json; using Nautilus.Options.Attributes; /// The Menu attribute allows us to set the title of our section within the "Mods" tab of the options menu. [Menu("My Options Menu")] public class MyConfig : ConfigFile { /// A Slider attribute is used to represent a numeric value as a slider in the options menu with a /// minimum and maximum value. By default, the minimum value is 0 and maximum is 100. /// /// In this example we are setting a minimum value of 0 and a maximum of 50, with a /// DefaultValue of 25 which will be represented by a notch on the slider. [Slider("My slider", 0, 50, DefaultValue = 25)] /// This is the actual definition for the value which should be saved. Its value controls the /// initial value that our option will have upon opening the game for the first time. public int SliderValue = 15; } ``` -------------------------------- ### Create Custom Prefab from Existing Template Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/vehicle-module.md Uses a CloneTemplate to base the new custom module on an existing game item, such as the Reinforced Hull module. ```csharp var clone = new CloneTemplate(prefabInfo, TechType.HullReinforcementModule3); prefab.SetGameObject(clone); ``` -------------------------------- ### Set Custom Prefab Spawns using ICustomPrefab Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/spawns.md Shows how to create a custom prefab, modify its appearance, and define its spawn locations and probabilities using the SetSpawns method. This is recommended for custom prefabs over direct LootDistributionHandler manipulation. ```csharp // Set the vanilla titanium icon for our item CustomPrefab titaniumClone = new CustomPrefab("TitaniumClone", "Titanium Clone", "Titanium clone that makes me go yes.", SpriteManager.Get(TechType.Titanium)); // Creates a clone of the Titanium prefab and colors it red, then set the new prefab as our Titanium Clone's game object. PrefabTemplate cloneTemplate = new CloneTemplate(titaniumClone.Info, TechType.Titanium) { // Callback to change all material colors of this clone to red. ModifyPrefab = prefab => prefab.GetComponentsInChildren().ForEach(r => r.materials.ForEach(m => m.color = Color.red)) }; titaniumClone.SetGameObject(cloneTemplate); titaniumClone.SetSpawns( // Adds a chance for our titanium clone to spawn in Safe shallows grass, x4 each time. new BiomeData { biome = BiomeType.SafeShallows_Grass, count = 4, probability = 0.1f }, // Adds a chance for our titanium clone to spawn in Safe shallows caves, once each time. new BiomeData { biome = BiomeType.SafeShallows_CaveFloor, count = 1, probability = 0.4f }); // Register the Titanium Clone to the game. titaniumClone.Register(); ``` -------------------------------- ### Configure Post-Build Event for DLL Copying Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/csproj-tutorials.md Add a post-build target to your csproj to automatically copy your mod's DLL to the BepInEx plugins folder after a successful build. ```xml ``` -------------------------------- ### Create SkyChangeTitleAddon Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/title-addons.md Configures a sky change addon with a specific fade duration and custom sky settings. ```csharp var skyChangeAddon = new SkyChangeTitleAddon(2f, new SkyChangeTitleAddon.Settings(0f, fogDensity: 0.001f)); ``` -------------------------------- ### Spawn Peeper using Filename Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/loading-and-instantiating-prefabs.md Fetches and instantiates a Peeper prefab using its filename. Requires waiting for the async task to complete before accessing the prefab. ```csharp using UWE; // ... private static IEnumerator SpawnPeeper() { // Fetch the prefab: IPrefabRequest task = UWE.PrefabDatabase.GetPrefabForFilenameAsync("WorldEntities/Creatures/Peeper.prefab"); // Wait for the prefab task to complete: yield return task; // Get the prefab: task.TryGetPrefab(out GameObject prefab); // Instantiate the prefab with a random rotation 2 meters above the player camera: Instantiate(prefab, MainCamera.camera.transform.position + (MainCamera.camera.transform.up * 2), Random.rotation); } ``` ```csharp private static IEnumerator SpawnPeeper() { var task = UWE.PrefabDatabase.GetPrefabForFilenameAsync("WorldEntities/Creatures/Peeper.prefab"); yield return task; task.TryGetPrefab(out var prefab); } ``` -------------------------------- ### Migrate PDAEncyclopedia and PDALog Handlers Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/sml2-to-nautilus.md Encyclopedia and Log entries are now managed through the PDAHandler class. ```diff PDAEncyclopedia.EntryData entry = new PDAEncyclopedia.EntryData() { key = "SomeEncy", path = "Tech/Tools", nodes = new[] { "Tech", "Tools" } }; - PDAEncyclopediaHandler.AddCustomEntry(entry); + PDAHandler.AddEncyclopediaEntry(entry); - PDALogHandler.AddCustomEntry("SomeLog", "SomeLanguageKey"); + PDAHandler.AddLogEntry("SomeLog", "SomeLanguageKey"); ``` -------------------------------- ### Implement Mod Options (Nautilus) Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/sml2-to-nautilus.md Modern implementation using the unified AddItem method and specific event handlers for individual options. ```csharp public class ModOptionsV3 : ModOptions { public ModOptionsV3() : base("My Mod Options") { OptionsPanelHandler.RegisterModOptions(this); OnChanged += GlobalOptions_Changed; var sliderWithChange = ModSliderOption.Create(id: "Fancy", label: "Slider", minValue: 0, maxValue: 100, value: 50); sliderWithChange.OnChanged += specific_OnChanged; AddItem(sliderWithChange); AddItem(ModSliderOption.Create(id: "Foo", label: "Bar", minValue: 0, maxValue: 100, value: 50)); AddItem(ModChoiceOption.Create(id: "Baz", label: "Qux", options: new[] { "ABC", "DEF", "XYZ" }, index: 0)); } private void specific_OnChanged(object sender, SliderChangedEventArgs e) { // Do onChange here } private void GlobalOptions_Changed(object sender, OptionEventArgs e) { switch (e) { case SliderChangedEventArgs sliderArgs: switch (sliderArgs.Id) { case "Foo": // Do stuff here break; } break; case ChoiceChangedEventArgs choiceArgs: switch (choiceArgs.Id) { case "Baz": // Do stuff here break; } break; } } } ``` -------------------------------- ### Include PDB File in Post-Build Copy Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/csproj-tutorials.md Add this section within the post-build target to also copy the PDB file to the plugins folder, which is useful for debugging. ```xml ``` -------------------------------- ### Define Project Structure Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/simple-mod.md The expected file layout for a Nautilus mod project. ```bash MyAwesomeMod/Items/Equipment/YeetKnifePrefab.cs MyAwesomeMod/Plugin.cs ``` -------------------------------- ### Create Custom Seamoth Module Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/sml2-to-nautilus.md This snippet demonstrates how to create a custom Seamoth module, including setting its properties, defining its appearance by cloning another item, specifying equipment type, setting unlock requirements, and defining its crafting recipe and fabrication location. ```csharp var seamothBrineResistanceModule = new CustomPrefab( "SeamothBrineResistModule", "Seamoth brine resistant coating", "Makes the Seamoth resistant to corrosive brine pools, by means of a protective coating."); seamothBrineResistanceModule.SetGameObject(new CloneTemplate(seamothBrineResistanceModule.Info, TechType.SeamothElectricalDefense)); seamothBrineResistanceModule.SetEquipment(EquipmentType.SeamothModule) .WithQuickSlotType(QuickSlotType.Passive); ScanningGadget scanning = seamothBrineResistanceModule.SetUnlock(TechType.BaseUpgradeConsole); scanning.WithPdaGroupCategory(TechGroup.VehicleUpgrades, TechCategory.VehicleUpgrades); var recipe = new RecipeData() { craftAmount = 1, Ingredients = { new CraftData.Ingredient(TechType.Polyaniline, 1), new CraftData.Ingredient(TechType.CopperWire, 2), new CraftData.Ingredient(TechType.AluminumOxide, 2), new CraftData.Ingredient(TechType.Nickel, 1), }, }; seamothBrineResistanceModule.SetRecipe(recipe) .WithFabricatorType(CraftTree.Type.SeamothUpgrades) .WithStepsToFabricatorTab("SeamothModules"); seamothBrineResistanceModule.Register(); ``` -------------------------------- ### Load multiple non-bundle files concurrently Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/async-mod-loading.md Demonstrates how to initiate multiple file loading tasks simultaneously and wait for all of them to complete within a single coroutine. ```csharp // Define the filenames of each file that needs to be loaded. private string[] _fileNames = new[] { "recipeChanges.json", "fragmentChanges.json", "databankChanges.json" }; private void Awake() { ... // We use an async WaitScreenTask for file loading. WaitScreenHandler.RegisterAsyncLoadTask("ExampleMod", LoadFilesAsync); } // This method is a coroutine wrapped around the method that *actually* loads the file. private IEnumerator LoadFilesAsync(WaitScreenHandler.WaitScreenTask task) { task.Status = "Loading very important files..."; // Keep a list of all active async Tasks. var fileTasks = new List(); // Start loading each file and keep track of its Task. foreach (string fileName of _fileNames) { fileTasks.Add(LoadImportantFileAsync(fileName)); } // Wait until all files have finished loading. yield return new WaitUntil(fileTasks.TrueForAll(fileTask => fileTask.IsCompleted)); // Do something with the file contents. ... } // This method is what actually loads the file. Unfortunately, Unity has poor async support // so it cannot be used directly. private async Task LoadImportantFileAsync(string filePath) { // This time, we pass in the file path as an argument for reusability. using StreamReader reader = new StreamReader(File.OpenRead(filePath)); // Read everything and return it when done. string fileContents = await reader.ReadToEndAsync(); return fileContents; } ``` -------------------------------- ### Configure Custom TechCategory and TechGroup Objects Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/sml2-to-nautilus.md Register custom tech groups and categories, linking them using the builder's fluent interface. ```csharp - TechGroup customGroup = TechGroupHandler.AddTechCategory("CustomGroup", "Custom Group"); + TechGroup customGroup = EnumHandler.AddEntry("CustomGroup").WithPdaInfo("Custom Group"); - TechCategory customCategory = TechCategoryHandler.AddTechCategory("CustomCategory", "Custom Category"); - TechCategoryHandler.TryRegisterTechCategoryToTechGroup(customGroup, customCategory); + TechCategory customCategory = EnumHandler.AddEntry("CustomCategory").WithPdaInfo("Custom Group") + .RegisterToTechGroup(customGroup); ``` -------------------------------- ### Set Spawns for Custom Prefabs Source: https://context7.com/subnauticamodding/nautilus/llms.txt Set spawn locations directly for custom prefabs using SpawnLocation. ```csharp using Nautilus.Assets; // For custom prefabs, use SetSpawns directly customPrefab.SetSpawns(new SpawnLocation(100f, -50f, 200f)); ``` -------------------------------- ### Create a Custom Fabricator Prefab Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/sml2-to-nautilus.md Defines a new fabricator, configures its crafting tree, applies a custom texture, and registers it with the game. ```csharp // Create a custom prefab instance and set the class ID, friendly name, description and icon respectively var abyssFabricator = new CustomPrefab( "AbyssFabricator", "Abyss Fabricator", "Abyss Batteries Fabricator", SpriteManager.Get(TechType.Fabricator)); // Create a custom crafting tree for this tech type. This method returns a FabricatorGadget, which we can use to customize our crafting tree. For example, to add a new tab or crafting node. var abyssFabCraftTree = abyssFabricator.CreateFabricator(out CraftTree.Type abyssFabType); // Load up the custom main (diffuse) texture from disk. var mainTexture = ImageUtils.LoadTextureFromFile(Path.Combine(AssetsFolder, "AbyssFabricatorskin.png")); // Create our fabricator game object. The fabricator game object will use the vanilla Fabricator model, then set the main texture to the texture we loaded earlier. var abyssFabricatorModel = new FabricatorTemplate(abyssFabricator.Info, abyssFabType) { FabricatorModel = FabricatorTemplate.Model.Fabricator, ModifyPrefab = obj => obj.GetComponentInChildren().material.mainTexture = mainTexture }; // Sets this prefab's game object to the model we created earlier. abyssFabricator.SetGameObject(abyssFabricatorModel); // Sets the recipe for the fabricator. abyssFabricator.SetRecipe(new RecipeData(new Ingredient(TechType.Titanium, 2), new Ingredient(TechType.Quartz, 2), new Ingredient(TechType.JeweledDiskPiece, 1))); // Adds the fabricator item to the Interior Modules group. This also makes our object buildable. abyssFabricator.SetPdaGroupCategory(TechGroup.InteriorModules, TechCategory.InteriorModule); // Register our item to the game. abyssFabricator.Register(); ``` -------------------------------- ### Spawn Peeper using Class ID Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/loading-and-instantiating-prefabs.md Fetches and instantiates a Peeper prefab using its Class ID. Requires waiting for the async task to complete before accessing the prefab. ```csharp using UWE; // ... private static IEnumerator SpawnPeeper() { // Fetch the prefab (3fcd548b-781f-46ba-b076-7412608deeef is the Class ID of the Peeper): IPrefabRequest task = UWE.PrefabDatabase.GetPrefabAsync("3fcd548b-781f-46ba-b076-7412608deeef"); // Wait for the prefab task to complete: yield return task; // Get the prefab: task.TryGetPrefab(out GameObject prefab); // Instantiate the prefab with a random rotation 2 meters behind the player camera: Instantiate(prefab, MainCamera.camera.transform.position - (MainCamera.camera.transform.forward * 2), Random.rotation); } ``` ```csharp private static IEnumerator SpawnPeeper() { var task = UWE.PrefabDatabase.GetPrefabAsync("3fcd548b-781f-46ba-b076-7412608deeef"); yield return task; task.TryGetPrefab(out var prefab); } ``` -------------------------------- ### Initialize CustomTitleData Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/title-addons.md Groups multiple title addons into a single data object using a localization key. ```csharp var customData = new TitleScreenHandler.CustomTitleData("MyModLocalizationKey", objectAddon, musicAddon, skyChangeAddon); ``` -------------------------------- ### Implement Custom Audio with FMOD Source: https://context7.com/subnauticamodding/nautilus/llms.txt Use FModSoundBuilder to register 3D, 2D, or music events. Audio sources can be loaded from asset bundles or mod folders. ```csharp using Nautilus.FMod; using Nautilus.Handlers; using Nautilus.Utility; // Define the audio source (from asset bundle or mod folder) AssetBundle bundle = AssetBundleLoadingUtils.LoadFromAssetsFolder(Assembly, "sounds"); CustomSoundSourceBase soundSource = new AssetBundleSoundSource(bundle); // Or load from a folder in your mod directory // CustomSoundSourceBase soundSource = new ModFolderSoundSource("SoundsFolder"); FModSoundBuilder builder = new FModSoundBuilder(soundSource); // Register a 3D underwater sound builder.CreateNewEvent("ExplosionSound", AudioUtils.BusPaths.UnderwaterAmbient) .SetMode3D(3, 70) // Min distance 3m, max distance 70m .SetSound("ExplosionSound") // Loads ExplosionSound.mp3/.wav from source .Register(); // Register a 2D UI sound with random variations builder.CreateNewEvent("ButtonClick", "bus:/master/SFX_for_pause/PDA_pause/all/SFX") .SetMode2D() .SetSounds(true, "Click1", "Click2", "Click3") // Random selection .Register(); // Register custom music builder.CreateNewEvent("BiomeMusic", AudioUtils.BusPaths.Music) .SetModeMusic() .SetFadeDuration(2) // 2 second fade out .SetSounds(true, "Track1", "Track2") .Register(); // Play a registered sound Utils.PlayFMODAsset(AudioUtils.GetFmodAsset("ExplosionSound"), Player.main.transform.position); ``` -------------------------------- ### Registering a delegate as a console command Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/console-commands.md Uses a delegate to define a command callback. Note that optional parameters are not supported with this method. ```csharp using BepInEx; using Nautilus.Handlers; [BepInPlugin(PluginInfo.GUID, PluginInfo.MOD_NAME, PluginInfo.VERSION)] public class MyPlugin : BaseUnityPlugin { private void Start() { ConsoleCommandsHandler.RegisterConsoleCommand("delegatecommand", (myString, myInt, myBool) => { return $"Parameters: {myString} {myInt} {myBool}"; }); } private delegate string MyCommand(string myString, int myInt, bool myBool); } ``` -------------------------------- ### UWE.PrefabDatabase.GetPrefabForFilenameAsync Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/loading-and-instantiating-prefabs.md Loads a prefab using its filename (path). This is similar to Resources.Load and useful when the Class ID is unknown but the file path is available. ```APIDOC ## GET UWE.PrefabDatabase.GetPrefabForFilenameAsync ### Description Retrieves a prefab asynchronously using its filename or path. This method is analogous to `Resources.Load` and is useful when you know the file path but not necessarily the Class ID. Ensure the `.prefab` extension is included and the `Assets/AddressableResources/` prefix is excluded. ### Method GET (asynchronous operation) ### Endpoint UWE.PrefabDatabase.GetPrefabForFilenameAsync(string filename) ### Parameters #### Path Parameters - **filename** (string) - Required - The filename or path of the prefab to retrieve (e.g., "WorldEntities/Creatures/Peeper.prefab"). ### Request Example ```csharp // Example of spawning a Peeper using its filename private static IEnumerator SpawnPeeper() { IPrefabRequest task = UWE.PrefabDatabase.GetPrefabForFilenameAsync("WorldEntities/Creatures/Peeper.prefab"); yield return task; task.TryGetPrefab(out GameObject prefab); Instantiate(prefab, MainCamera.camera.transform.position + (MainCamera.camera.transform.up * 2), Random.rotation); } ``` ### Response #### Success Response (IPrefabRequest) - **IPrefabRequest** - An object that can be awaited to retrieve the prefab. #### Response Example ```csharp // After awaiting the task: IPrefabRequest task = UWE.PrefabDatabase.GetPrefabForFilenameAsync("WorldEntities/Creatures/Peeper.prefab"); yield return task; if (task.IsDone) { task.TryGetPrefab(out GameObject prefab); // Use the prefab } ``` ``` -------------------------------- ### Implement Mod Options (SML 2.0) Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/sml2-to-nautilus.md Legacy implementation of mod options using individual AddSliderOption and AddChoiceOption methods. ```csharp public class ModOptionsV2 : ModOptions { public ModOptionsV2() : base("My Mod Options") { OptionsPanelHandler.RegisterModOptions(this); SliderChanged += Options_SliderChanged; ChoiceChanged += Options_ChoiceChagned; } public override void BuildModOptions() { AddSliderOption(id: "Foo", label: "Bar", minValue: 0, maxValue: 100, value: 50); AddChoiceOption(id: "Baz", label: "Qux", options: new[] { "ABC", "DEF", "XYZ" }, index: 0); } private void Options_SliderChanged(object sender, SliderChangedEventArgs e) { switch (e.Id) { case "Foo": // Do stuff here break; } } private void Options_ChoiceChagned(object sender, ChoiceChangedEventArgs e) { switch (e.Id) { case "Baz": // Do stuff here break; } } } ``` -------------------------------- ### Registering a LateLoadTask Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/async-mod-loading.md Demonstrates registering a task to modify game objects during the Late loading stage, ensuring vanilla objects are initialized. ```csharp private void Awake() { ... // Other plugin setup code // First, register your task, for example during your plugin Awake(). // We choose the Late stage for this task because we need access to the life pod. WaitScreenHandler.RegisterLateLoadTask("ExampleMod", ExpandPodInventory); } // This function will be called by Nautilus as part of the task during the loading screen. private void ExpandPodInventory(WaitScreenHandler.WaitScreenTask task) { // Change the life pod's inventory to be 10x10. EscapePod.main.storageContainer.Resize(10, 10); } ``` -------------------------------- ### Create Subnautica: Below Zero Mod Project Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/guides/dev-setup.md Use this command to create a new Subnautica: Below Zero mod project with the 'bzmod' template. Replace 'MyBeautifulMod' with your desired project name. ```bash dotnet new bzmod -n MyBeautifulMod ``` -------------------------------- ### Implement Chargeable Self-Destruct Module Source: https://github.com/subnauticamodding/nautilus/blob/master/Nautilus/Documentation/tutorials/vehicle-module.md Configures a vehicle upgrade module that self-destructs the vehicle after a cooldown period when fully charged. Requires 'EquipmentType.VehicleModule' and 'QuickSlotType.SelectableChargeable'. ```csharp var maxCharge = 50f; var cooldown = 10f; var energyCost = 6.9f; prefab.SetVehicleUpgradeModule(EquipmentType.VehicleModule, QuickSlotType.SelectableChargeable) .WithMaxCharge(maxCharge) .WithCooldown(cooldown) .WithEnergyCost(energyCost) .WithOnModuleAdded((Vehicle inst, int slotId) => { Subtitles.Add("Self-destruct module installed. The module needs to be charged fully to detonate."); }) .WithOnModuleRemoved((Vehicle inst, int slotId) => { Subtitles.Add("Self-destruct module uninstalled."); }) .WithOnModuleUsed((Vehicle inst, int slotID, float charge, float chargeScalar) => { if (charge < maxCharge) { Subtitles.Add("Self-destruction sequence disengaged.") return; } else { Subtitles.Add("Self-destruction sequence engaged.") UWE.CoroutineHost.StartCoroutine(EngageSelfDestruct(inst, countdown)); } }); static IEnumerator EngageSelfDestruct(Vehicle instance, float countdown) { var startTime = Time.time while(Time.time < (startTime + countdown)) { yield return null; } instance.liveMixin.Kill(DamageType.Explosive); } ```