General Guide - Replacing Sounds

Updated a month ago

BepInEx 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

  1. Prerequisites
  2. Project Setup
  3. Basic Plugin Structure
  4. Playing Custom Sounds
  5. Replacing Existing Sounds
  6. Replacing Music (Environment-Aware)
  7. Advanced: 3D Positioned Sounds
  8. Distribution and Installation
  9. 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

  1. Go to Necropolis\Necropolis_Data\StreamingAssets\Audio\GeneratedSoundBanks
  2. In that folder, there is a file called Wwise_IDs.h, open it in a text editor
  3. 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

  1. Run the game with BepInEx console open
  2. Enable verbose logging in CustomWwiseSoundbanks config
  3. 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

  1. Open Visual Studio
  2. Create New Project → Class Library (.NET Framework)
  3. Name: YourModName
  4. Framework: .NET Framework 3.5
  5. Click Create

Step 2: Add References

  1. Right-click References in Solution Explorer
  2. Click Add ReferenceBrowse
  3. Add all required DLLs listed in prerequisites

Step 3: Configure Build Output

  1. Right-click your project → Properties
  2. Go to Build tab
  3. Output path: Set to your BepInEx plugins folder for easy testing
    • Example: C:\Games\Necropolis\BepInEx\plugins\YourModName\
  4. Target framework: .NET Framework 3.5

Step 4: Add Your Soundbank File

  1. In your project folder, create a folder structure:

    YourModName/
      YourModName.csproj
      YourPlugin.cs
      soundbanks/
        YourModName.bnk  ← Your Wwise soundbank
    
  2. In Visual Studio:

    • Right-click project → Add → Existing Item
    • Browse to your .bnk file
    • Add it to project
    • Right-click the .bnk file → 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 first
  • Awake(): Called when plugin loads - do initialization here
  • logger: 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 areas
  • Foundry - Foundry areas
  • Temple - Temple areas
  • Hellscape - Hell areas
  • Ice_Cave - Just the ice caves, not the ice areas
  • Palace - Either an old/unused or very specific area that Im not aware of
  • Swamp - Swamp areas
  • Thunder - Also not sure on this one yet.
  • Schism - Most of the default areas of the game.
  • No_Set - UNSURE
  • Report_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 screen
  • Explore - Exploration mode
  • Combat - Combat mode
  • Credits - 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.txt file (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.txt for 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 .bnk file is in plugins/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:

  1. Create events Play_CustomSword_Swing and Play_CustomSword_Impact
  2. Import your custom audio files
  3. Generate soundbank named CustomSwordSounds.bnk
  4. 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

  1. Create your Wwise project following the Wwise Audio Creation Guide
  2. Generate your soundbanks with unique event names
  3. Write your plugin using the examples above
  4. Test in-game and iterate
  5. Package for distribution following the structure above
  6. Share with the community!

Happy modding! 🎮🎵