General Guide - Replacing Sounds
Updated a month agoBepInEx Plugin Guide: Custom Audio within Abraxis Audio using CustomWwiseAPI
This guide shows you how to create BepInEx 5 plugins for Necropolis that add custom sounds, replace existing sounds, and replace environment-specific music using the CustomWwiseAPI system.
Table of Contents
- Prerequisites
- Project Setup
- Basic Plugin Structure
- Playing Custom Sounds
- Replacing Existing Sounds
- Replacing Music (Environment-Aware)
- Advanced: 3D Positioned Sounds
- Distribution and Installation
- Troubleshooting
Prerequisites
Required Software:
- Visual Studio 2019 or later (Community Edition works)
- BepInEx 5 installed in Necropolis
- Wwise 2015.1.9 (for creating soundbanks - see Wwise guide)
- .NET Framework 3.5
Required Knowledge:
- Basic C# programming
- Understanding of Unity GameObject system
- Familiarity with BepInEx plugin structure
- If you are replacing sounds, you'll need to know Sound Event Names in Necropolis
Required Files/Dependencies:
You'll need these DLL references in your project:
Assembly-CSharp.dll(from Necropolis game folder:Necropolis_Data/Managed/)UnityEngine.dll(from Necropolis game folder:Necropolis_Data/Managed/)BepInEx.dll(from BepInEx installation:BepInEx/core/)0Harmony.dll(from BepInEx installation:BepInEx/core/)AbraxisAudio.dll(from CustomWwiseSoundbanks mod)
Required Mods:
- CustomWwiseSoundbanks plugin (provides the API)
Sound Event Names in Necropolis
You need to know the exact event name from the game. Two methods:
Method 1: Check Wwise_IDs.h
- Go to
Necropolis\Necropolis_Data\StreamingAssets\Audio\GeneratedSoundBanks - In that folder, there is a file called Wwise_IDs.h, open it in a text editor
- This file will give you every single sound event in the game
Look in the game's Wwise_IDs.h file for event names:
static const AkUniqueID BRUTE_PLAY_FOOT_STONE_01 = 123456789U;
Method 2: Enable Debug Logging
- Run the game with BepInEx console open
- Enable verbose logging in CustomWwiseSoundbanks config
- Play the game and watch console for event names being posted
You may need to experiment to find out what sounds are from where!
Project Setup
Step 1: Create New C# Class Library Project
- Open Visual Studio
- Create New Project → Class Library (.NET Framework)
- Name:
YourModName - Framework: .NET Framework 3.5
- Click Create
Step 2: Add References
- Right-click References in Solution Explorer
- Click Add Reference → Browse
- Add all required DLLs listed in prerequisites
Step 3: Configure Build Output
- Right-click your project → Properties
- Go to Build tab
- Output path: Set to your BepInEx plugins folder for easy testing
- Example:
C:\Games\Necropolis\BepInEx\plugins\YourModName\
- Example:
- Target framework: .NET Framework 3.5
Step 4: Add Your Soundbank File
-
In your project folder, create a folder structure:
YourModName/ YourModName.csproj YourPlugin.cs soundbanks/ YourModName.bnk ← Your Wwise soundbank -
In Visual Studio:
- Right-click project → Add → Existing Item
- Browse to your
.bnkfile - Add it to project
- Right-click the
.bnkfile → Properties - Build Action: Content
- Copy to Output Directory: Copy if newer
Basic Plugin Structure
Minimal Plugin Template
Create a new file YourPlugin.cs:
using BepInEx;
using BepInEx.Logging;
using CustomWwiseAPI;
namespace YourModName
{
[BepInPlugin(GUID, NAME, VERSION)]
[BepInDependency("com.customwwise.soundbanks")] // Require CustomWwiseSoundbanks
public class YourPlugin : BaseUnityPlugin
{
// Plugin metadata
public const string GUID = "com.yourname.yourmod";
public const string NAME = "Your Mod Name";
public const string VERSION = "1.0.0";
// Logger for debugging
private static ManualLogSource logger;
void Awake()
{
logger = Logger;
logger.LogInfo($"{NAME} v{VERSION} is loading...");
// Your initialization code here
InitializeAudio();
logger.LogInfo($"{NAME} loaded successfully!");
}
private void InitializeAudio()
{
// Audio setup code goes here
}
}
}
Key Components:
[BepInPlugin]: Marks this as a BepInEx plugin with unique GUID[BepInDependency]: Ensures CustomWwiseSoundbanks loads firstAwake(): Called when plugin loads - do initialization herelogger: For debug logging (visible in BepInEx console)
Playing Custom Sounds
Be sure you've gone through the "Setting Up Your Sounds & Music in Wwise 2015.1" Guide First
Method 1: Simple Sound Playback
Play a sound from your custom soundbank:
using CustomWwiseAPI;
using UnityEngine;
void PlayCustomSound()
{
// Get the player GameObject
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
// Play event on player
CustomAudioManager.PlayCustomEvent("Play_MyMod_Footstep", player);
logger.LogInfo("Played custom footstep sound!");
}
else
{
logger.LogWarning("Could not find player GameObject!");
}
}
Method 2: Play on Specific Position (3D Sound)
For sounds that should come from a specific location:
void PlaySoundAtPosition(Vector3 worldPosition)
{
// Create temporary GameObject at position
GameObject soundSource = new GameObject("CustomSoundSource");
soundSource.transform.position = worldPosition;
// Play sound
CustomAudioManager.PlayEvent("Play_MyMod_ExplosionSound", soundSource);
// Clean up after sound duration (adjust time as needed)
Destroy(soundSource, 3.0f);
}
Fun Example: Play with Delay
Heres an example of something you could do when playing custom sounds:
void PlayDelayedSound(string eventName, GameObject source, float delay)
{
StartCoroutine(PlaySoundAfterDelay(eventName, source, delay));
}
IEnumerator PlaySoundAfterDelay(string eventName, GameObject source, float delay)
{
yield return new WaitForSeconds(delay);
CustomAudioManager.PlayEvent(eventName, source);
}
This is just one of many things though, think outside the box, the possibilities are endless!
Best Practices:
✅ Always check if GameObject exists before playing sound ✅ Use descriptive event names with your mod prefix (avoid conflicts) ✅ Log errors for debugging ✅ Clean up temporary GameObjects used as sound sources
Replacing Existing Sounds
Replace game sounds with your custom audio using event name mapping.
How Sound Replacement Works:
The CustomWwiseSoundbanks plugin uses Harmony to intercept PostEvent calls. When the game tries to play an event, the system checks if you've registered a replacement. If found, your custom event plays instead.
Finding Event Names to Replace
Please check out Sound Event Names In Necropolis
Basic Replacement Setup
using CustomWwiseAPI;
void InitializeAudio()
{
// Replace a specific footstep sound
CustomAudioManager.RegisterEventReplacement(
"BRUTE_PLAY_FOOT_STONE_01", // Original game event
"Play_MyMod_Footstep" // Your custom event
);
logger.LogInfo("Registered footstep replacement");
}
Multiple Replacements Example
void InitializeAudio()
{
// Replace multiple footstep variations
string[] footstepEvents = new string[]
{
"BRUTE_PLAY_FOOT_STONE_01",
"BRUTE_PLAY_FOOT_STONE_02",
"BRUTE_PLAY_FOOT_STONE_03",
"BRUTE_PLAY_FOOT_WOOD_01"
};
foreach (string eventName in footstepEvents)
{
CustomAudioManager.RegisterEventReplacement(
eventName,
"Play_MyMod_Footstep" // Same custom sound for all
);
logger.LogInfo($"Replaced: {eventName}");
}
}
Conditional Replacement
Only replace sounds under certain conditions:
using HarmonyLib;
[HarmonyPatch(typeof(AudioBankManager), "PostEvent", typeof(string), typeof(GameObject))]
class AudioReplacementPatch
{
static bool Prefix(string eventName, GameObject sourceObject)
{
// Only replace if player is wearing specific item
if (eventName.Contains("FOOTSTEP") && PlayerHasCustomSkin())
{
CustomAudioManager.PlayCustomEvent("Play_MyMod_MetalFootstep", sourceObject);
return false; // Skip original event
}
return true; // Allow original event
}
static bool PlayerHasCustomSkin()
{
// Your logic here
return true;
}
}
Important Notes:
⚠️ Event names are case-sensitive! ⚠️ Your custom event must exist in your soundbank ⚠️ Registering the same replacement twice will overwrite the first
Replacing Music (Environment-Aware)
Necropolis uses an environment-based music system. Different areas trigger different music based on AudioState_EnvironmentSet values.
Understanding Environment Music System
The game sets environment states like:
Forest- Forest areasFoundry- Foundry areasTemple- Temple areasHellscape- Hell areasIce_Cave- Just the ice caves, not the ice areasPalace- Either an old/unused or very specific area that Im not aware ofSwamp- Swamp areasThunder- Also not sure on this one yet.Schism- Most of the default areas of the game.No_Set- UNSUREReport_Window- UNSURE
Music events are triggered based on combat state:
- Explore mode: Background exploration music
- Combat mode: Intense combat music
Available Music Switch Values
From the Music_Switch:
Title- Title screenExplore- Exploration modeCombat- Combat modeCredits- End credits
Basic Music Replacement
using CustomWwiseAPI;
void InitializeAudio()
{
// Replace exploration music for Black Forest environment
CustomAudioManager.RegisterEnvironmentMusic(
"Forest", // Environment name
"Explore", // Music switch value
"Play_MyMod_ForestExploreMusic" // Your custom event
);
// Replace combat music for same environment
CustomAudioManager.RegisterEnvironmentMusic(
"Forest",
"Combat",
"Play_MyMod_ForestCombatMusic"
);
logger.LogInfo("Registered custom Black Forest music");
}
Complete Environment Music Setup
Replace music for all states in an environment:
void InitializeAudio()
{
string environment = "Foundry";
// Map all music states for this environment
var musicMap = new Dictionary<string, string>()
{
{ "Explore", "Play_MyMod_FoundryExplore" },
{ "Combat", "Play_MyMod_FoundryCombat" }
};
foreach (var kvp in musicMap)
{
CustomAudioManager.RegisterEnvironmentMusic(
environment,
kvp.Key,
kvp.Value
);
logger.LogInfo($"Registered {environment} - {kvp.Key} music");
}
}
Dynamic Music Example
Change music based on player health:
using UnityEngine;
void Update()
{
if (Input.GetKeyDown(KeyCode.M)) // Toggle music (for testing)
{
CheckPlayerHealthAndAdjustMusic();
}
}
void CheckPlayerHealthAndAdjustMusic()
{
// Get player's current health (simplified example)
float healthPercent = GetPlayerHealthPercent();
string currentEnvironment = GetCurrentEnvironment();
if (healthPercent < 0.25f)
{
// Play intense music when health is low
CustomAudioManager.RegisterEnvironmentMusic(
currentEnvironment,
"Explore",
"Play_MyMod_DangerMusic"
);
logger.LogInfo("Switched to danger music!");
}
else
{
// Normal music
CustomAudioManager.RegisterEnvironmentMusic(
currentEnvironment,
"Explore",
"Play_MyMod_NormalMusic"
);
}
}
float GetPlayerHealthPercent()
{
// Your implementation here
return 1.0f;
}
string GetCurrentEnvironment()
{
// Your implementation here
return "Forest";
}
Music Replacement Notes:
✅ Music events should be set up in a Music Switch Container in Wwise ✅ Use stereo audio files for music ✅ Consider music loop points and transitions in Wwise ✅ Test in different environments to ensure proper replacement ⚠️ Music changes happen when the environment state changes, not instantly
Advanced: 3D Positioned Sounds
Playing 3D Sound at World Position
For sounds that should come from a specific location with distance falloff:
void PlayExplosionAt(Vector3 position)
{
GameObject soundEmitter = new GameObject("ExplosionSound");
soundEmitter.transform.position = position;
// Play 3D positioned event (must be configured in Wwise!)
CustomAudioManager.PlayEvent("Play_MyMod_Explosion", soundEmitter);
// Destroy after sound duration
Destroy(soundEmitter, 5.0f);
}
Attaching Sound to Moving Object
For sounds that follow a moving object (like a projectile):
void AttachSoundToProjectile(GameObject projectile)
{
// Play looping sound
CustomAudioManager.PlayEvent("Play_MyMod_ProjectileLoop", projectile);
// Stop sound when projectile is destroyed
StartCoroutine(StopSoundOnDestroy(projectile));
}
IEnumerator StopSoundOnDestroy(GameObject obj)
{
// Wait until object is destroyed
while (obj != null)
{
yield return null;
}
// Stop the sound (if you created a Stop event)
CustomAudioManager.PlayCustomEvent("Stop_MyMod_ProjectileLoop", obj);
}
3D Sound Requirements:
⚠️ Your Wwise event must be configured for 3D positioning ⚠️ Audio source should be mono (not stereo) ⚠️ Attenuation profile must be set up in Wwise (see Wwise guide) ⚠️ GameObject position must be valid in world space
Distribution and Installation
File Structure for Distribution
Your mod should be distributed with this structure:
YourModName_v1.0.zip
├── BepInEx/
│ └── plugins/
│ └── YourModName/
│ ├── YourModName.dll ← Your compiled plugin
│ └── soundbanks/
│ └── YourModName.bnk ← Your Wwise soundbank
Why This Structure?
The CustomWwiseSoundbanks plugin recursively scans the BepInEx plugins folder for .bnk files. By placing your soundbank in your plugin's folder, it will be automatically discovered and loaded.
Installation Instructions for Users
Create a README.txt for users:
YourModName Installation Instructions:
1. Install BepInEx 5 if not already installed
2. Install CustomWwiseSoundbanks plugin (dependency)
3. Extract this ZIP file to your Necropolis game folder
4. File structure should look like:
Necropolis/
BepInEx/
plugins/
YourModName/
YourModName.dll
soundbanks/
YourModName.bnk
5. Run the game!
The soundbank will be automatically loaded when the game starts.
Check BepInEx/LogOutput.log for any errors.
Build Process Automation
Add to your .csproj file to automate copying files:
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<ItemGroup>
<SoundbankFiles Include="$(ProjectDir)soundbanks\*.bnk" />
</ItemGroup>
<Copy SourceFiles="@(SoundbankFiles)"
DestinationFolder="$(OutDir)soundbanks\" />
</Target>
Troubleshooting
Sound Not Playing
Check 1: Is the soundbank loaded?
- Look in BepInEx console/log for:
"Loaded custom soundbank: YourModName.bnk" - If not found, check file path and structure
Check 2: Is the event name correct?
// Add debug logging
CustomAudioManager.PlayEvent("Play_MyMod_Sound", gameObject);
logger.LogInfo("Attempted to play: Play_MyMod_Sound");
Check 3: Does the event exist in your soundbank?
- Open your
.bnk.txtfile (generated with soundbank) - Search for your event name
- Verify exact spelling and capitalization
Check 4: Is the GameObject valid?
if (gameObject == null)
{
logger.LogError("GameObject is null! Cannot play sound.");
return;
}
Sound Replacement Not Working
Issue: Game sound still plays instead of custom sound
Fix 1: Verify event name exactly matches
// Wrong - case mismatch
CustomAudioManager.RegisterEventReplacement("brute_play_foot", "Play_Custom");
// Correct
CustomAudioManager.RegisterEventReplacement("BRUTE_PLAY_FOOT_STONE_01", "Play_Custom");
Fix 2: Check registration happens in Awake()
void Awake()
{
InitializeAudio(); // Make sure this is called!
}
Fix 3: Verify your custom event exists
- Check Wwise project
- Regenerate soundbank
- Check
.bnk.txtfor event list
3D Sound Not Attenuating
Issue: Sound doesn't get quieter with distance
Fix: Check Wwise project
- Sound must have Positioning enabled
- Sound must have Attenuation assigned
- Attenuation must have distance curve defined
- See Wwise Audio Creation Guide for complete setup
Plugin Not Loading
Issue: Plugin doesn't appear in BepInEx log
Check 1: Dependencies installed
- CustomWwiseSoundbanks plugin must be present
- Check
BepInEx/plugins/folder
Check 2: Plugin metadata correct
[BepInPlugin(GUID, NAME, VERSION)]
[BepInDependency("com.customwwise.soundbanks")] // Required!
Check 3: .NET Framework version
- Project must target .NET Framework 4.6 or higher
- Check project properties → Application → Target framework
Check 4: Check log file
- Open
BepInEx/LogOutput.log - Search for your plugin name
- Look for error messages or load failures
Common Error Messages
Error: "Could not load soundbank: YourMod.bnk"
- Cause: Soundbank file not found in expected location
- Fix: Verify
.bnkfile is inplugins/YourModName/soundbanks/folder
Error: "Event 'Play_Custom_Sound' not found in loaded banks"
- Cause: Event doesn't exist or soundbank not loaded
- Fix: Check event exists in Wwise project and soundbank generated correctly
Error: "Dependency plugin 'com.customwwise.soundbanks' not found"
- Cause: CustomWwiseSoundbanks plugin not installed
- Fix: Install dependency plugin first
Complete Example: Custom Weapon Sound Mod
Here's a complete, working example that replaces sword swing sounds:
using BepInEx;
using BepInEx.Logging;
using CustomWwiseAPI;
using UnityEngine;
using System.Collections.Generic;
namespace CustomSwordSounds
{
[BepInPlugin(GUID, NAME, VERSION)]
[BepInDependency("com.customwwise.soundbanks")]
public class CustomSwordSoundsMod : BaseUnityPlugin
{
public const string GUID = "com.example.customswordsounds";
public const string NAME = "Custom Sword Sounds";
public const string VERSION = "1.0.0";
private static ManualLogSource logger;
void Awake()
{
logger = Logger;
logger.LogInfo($"{NAME} v{VERSION} is loading...");
InitializeAudio();
logger.LogInfo($"{NAME} loaded successfully!");
}
private void InitializeAudio()
{
// Replace all sword swing sounds with custom sounds
List<string> swordSwingSounds = new List<string>()
{
"BRUTE_ATTACK_GREATSWORD_HEAVY_ATTACK01",
"BRUTE_ATTACK_GREATSWORD_HEAVY_ATTACK02",
"BRUTE_ATTACK_GREATSWORD_HEAVY_ATTACK03",
"BRUTE_ATTACK_SHORTSWORD_LIGHT_ATTACK01",
"BRUTE_ATTACK_SHORTSWORD_LIGHT_ATTACK02"
};
foreach (string soundEvent in swordSwingSounds)
{
CustomAudioManager.RegisterEventReplacement(
soundEvent,
"Play_CustomSword_Swing" // Your custom event from Wwise
);
logger.LogInfo($"Replaced sword sound: {soundEvent}");
}
// Replace weapon impact sounds
CustomAudioManager.RegisterEventReplacement(
"WEAPON_PLAY_SWORD_ONHIT",
"Play_CustomSword_Impact"
);
logger.LogInfo("Sword sound replacements registered!");
}
}
}
Required Wwise Setup:
- Create events
Play_CustomSword_SwingandPlay_CustomSword_Impact - Import your custom audio files
- Generate soundbank named
CustomSwordSounds.bnk - Place in
plugins/CustomSwordSounds/soundbanks/CustomSwordSounds.bnk
Best Practices Summary
✅ Always log important operations for debugging ✅ Use unique event names with your mod prefix to avoid conflicts ✅ Test thoroughly in different game scenarios ✅ Check null references before accessing GameObjects ✅ Follow the file structure for automatic soundbank loading ✅ Document your mod with clear installation instructions ✅ Version your releases and track changes ✅ List dependencies clearly (CustomWwiseSoundbanks required!)
Next Steps
- Create your Wwise project following the Wwise Audio Creation Guide
- Generate your soundbanks with unique event names
- Write your plugin using the examples above
- Test in-game and iterate
- Package for distribution following the structure above
- Share with the community!
Happy modding! 🎮🎵