Compare commits

..

6 Commits

Author SHA1 Message Date
Ed_
906a4d5a74 Strip broken GUI - minimal working version
Removed AudioMixerGUI.cs entirely
Plugin.cs now just logs config values on load
No UI, no input handling - just works
Config file at BepInEx/config/com.homurahime.audiomod.cfg

User can edit config values manually.
2026-03-22 14:57:14 -04:00
Ed_
c0b6816ab0 Rewrite AudioMixerGUI with Unity Canvas system
- Switched from OnGUI to Unity Canvas/UI system
- Added VerticalLayoutGroup for flexible layout
- Added ContentSizeFitter for auto-sizing
- Added resizable panel with drag handle in corner
- Fixed TMPro and UnityEngine.UI references in csproj
- Panel is now draggable and resizable (350x400 to 800x900)
2026-03-22 14:51:52 -04:00
Ed_
5a9f306f3f Update EXISTING_TOOLS.md with correct CinematicUnityExplorer links
- Changed from sinai-dev to originalnicodr/CinematicUnityExplorer (295 stars, v1.4.0)
- Updated download URL to correct latest release
- Added feature comparison table
- Added quick start guide for finding audio types
- Documented Hook Manager and C# Console usage
2026-03-22 14:25:35 -04:00
Ed_
4251a29794 Strip failing patches - rely on UnityExplorer for audio method discovery
Removed all Harmony patches that were failing due to incorrect method names.
Mod now loads without errors.
GUI shows config sliders but no actual patches are applied.
Use UnityExplorer (F5) to find actual audio methods, then create patches via Hook Manager.
2026-03-22 14:21:21 -04:00
Ed_
81bf38b472 Add console log window to AudioMixerGUI
- Added in-game console log showing background events
- Voice count updates shown in real-time
- Cue triggers logged with timestamps
- Errors/warnings highlighted in different colors
- Show/Hide console toggle
- Clear log button
- Fixed GUI.skin namespace conflict
- Cursor now unlocks when GUI is visible
2026-03-22 14:10:35 -04:00
Ed_
4285ae5258 Fix build issues: csproj references, Unity modules, Harmony calls
- Fixed csproj to reference all Unity modules (Core, Audio, IMG, Input, TextRendering, UI)
- Fixed AudioMixerGUI namespace conflicts (GUI.Window vs UnityEngine.GUI.Window)
- Fixed Harmony UnpatchAll -> UnpatchSelf
- Removed duplicate AssemblyInfo.cs
- Build now succeeds and DLL deployed to BepInEx/plugins
2026-03-22 13:59:39 -04:00
8 changed files with 135 additions and 577 deletions

View File

@@ -1 +1 @@
610036 622840

View File

@@ -1 +1 @@
1774200743 1774205514

View File

@@ -1,74 +1,90 @@
# Existing Modding Tools for HomuraHime # Existing Modding Tools for HomuraHime
## Recommended Existing Tools ## Recommended: CinematicUnityExplorer (295 stars)
### 1. UnityExplorer (3k stars) **Purpose:** In-game UI for exploring, debugging, and modifying Unity games with cinematic tools
**Purpose:** In-game UI for exploring, debugging, and modifying Unity games
**Features:**
- Object Explorer - browse scenes, find audio objects
- Inspector - view/edit component values in real-time
- Hook Manager - create Harmony patches at runtime (no rebuild needed)
- C# Console - execute code live
- Mouse Inspect - click objects to inspect them
**Download:** **Download:**
- BepInEx 5.x Mono: https://github.com/sinai-dev/UnityExplorer/releases/download/UnityExplorer.BepInEx5.Mono.zip ```
https://github.com/originalnicodr/CinematicUnityExplorer/releases/latest/download/CinematicUnityExplorer.BepInEx5.Mono.zip
```
**Installation:** **Installation:**
1. Download `CinematicUnityExplorer.BepInEx5.Mono.zip`
2. Extract to: `C:\apps\steam\steamapps\common\Homura Hime\BepInEx\plugins\`
3. Launch game - press **F5** to open
**Features:**
| Feature | Description |
|---------|-------------|
| Object Search | Find audio manager types (FmodVoiceManager, CFVoiceEventUtility, etc.) |
| Inspector | View/edit component values in real-time |
| Hook Manager | Create Harmony patches at runtime (no rebuild needed) |
| C# Console | Execute code live to test audio triggers |
| Mouse Inspect | Click objects to inspect them |
| Freecam | Cinematic camera for screenshots/videos |
| Lights Manager | Add/modify scene lighting |
| Camera Paths | Create cinematic camera sequences |
| Animator Control | Pose characters for screenshots |
---
## Alternative: Yukieiji UnityExplorer (809 stars)
**Vanilla UnityExplorer** - if you don't need cinematic features
**Download:**
``` ```
1. Download UnityExplorer.BepInEx5.Mono.zip https://github.com/yukieiji/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip
2. Extract to: C:\apps\steam\steamapps\common\Homura Hime\BepInEx\plugins\
3. Launch game - press F5 to open UnityExplorer menu
``` ```
### 2. UniverseLib (129 stars)
**Purpose:** UI library for Unity mod plugins
**NuGet:**
- Mono: `rainbowblood.UniverseLib.Mono`
- IL2CPP: `rainbowblood.UniverseLib.IL2CPP`
Note: UniverseLib is included with UnityExplorer, no separate install needed.
--- ---
## Why Keep Our Custom AudioMixerGUI? ## Why Keep Our Custom AudioMixerGUI?
While UnityExplorer is excellent for general debugging, our **AudioMixerGUI** provides: Our **AudioMixerGUI** provides audio-specific sliders and quick testing:
| Feature | UnityExplorer | Our AudioMixerGUI | | Feature | CinematicUnityExplorer | Our AudioMixerGUI |
|---------|---------------|-------------------| |---------|----------------------|-------------------|
| Audio-specific sliders | No | Yes | | Audio-specific sliders | No | Yes |
| One-click enemy cue testing | No | Yes | | One-click enemy cue testing | Via C# Console | Yes |
| Battle cue testing | No | Yes | | Voice count monitoring | Manual via Inspector | Yes |
| Voice count monitoring | Manual | Automatic | | Config persistence | No | Yes (via BepInEx config) |
| Audio presets | No | Yes |
**Recommendation:** Use **BOTH** **Recommendation:** Use **BOTH**
- UnityExplorer for general debugging and object browsing - CinematicUnityExplorer (F5) for debugging, object browsing, screenshots
- Our AudioMixerGUI for focused audio parameter tweaking and cue testing - Our AudioMixerGUI (F1) for audio parameter tweaking
--- ---
## Alternative: Dear ImGui (Future Option) ## Quick Start with CinematicUnityExplorer
If you prefer Dear ImGui-style UI, consider: ### Finding Audio Types
1. Press **F5** to open CinematicUnityExplorer
2. Go to **Object Search** tab
3. Search for: `FmodVoiceManager`, `CFVoiceEventUtility`, `VoiceManager`, `SnapshotManager`
4. Click results to inspect
1. **MelonImGui** - https://github.com/plusno69/MelonImGui ### Creating Runtime Patches
2. **ui_imgui** - https://github.com/nk-mermaid/ui_imgui 1. Go to **Hook Manager** tab
2. Enter class name (e.g., `CFVoiceEventUtility`)
3. Select method (e.g., `PlayCFVoice`)
4. Click to generate patch code
5. Edit and apply at runtime
These require additional dependencies but provide modern ImGui-style UI. ### Testing Audio Triggers via C# Console
```
**Trade-off:** More work to integrate, but looks more professional. // Example - trigger enemy alert sound
CFVoiceEventUtility.PlayCFVoice("Enemy_Alert");
```
--- ---
## Recommended Setup ## Default Hotkeys
1. **Install UnityExplorer** (for general modding/debugging) | Key | Action |
2. **Keep our AudioMixerGUI** (for audio-specific controls) |-----|--------|
3. **Press F1** to toggle our audio mixer | **F5** | Toggle CinematicUnityExplorer |
4. **Press F5** to toggle UnityExplorer | **F1** | Toggle our AudioMixerGUI |
This gives you the best of both worlds - professional debugging tools + dedicated audio mixer. For CinematicUnityExplorer freecam controls, see their documentation.

View File

@@ -9,7 +9,7 @@ param(
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$ProjectDir = Split-Path -Parent $PSScriptRoot $ProjectDir = $PSScriptRoot
$ProjectFile = Join-Path $ProjectDir "HomuraHimeAudioMod.csproj" $ProjectFile = Join-Path $ProjectDir "HomuraHimeAudioMod.csproj"
$DefaultOutput = Join-Path $ProjectDir "bin\$Configuration\net48" $DefaultOutput = Join-Path $ProjectDir "bin\$Configuration\net48"

View File

@@ -1,428 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace HomuraHimeAudioMod.GUI
{
public class AudioMixerGUI : MonoBehaviour
{
private static AudioMixerGUI instance;
public static AudioMixerGUI Instance => instance;
private bool isVisible = false;
private Vector2 scrollPosition;
private float margin = 10f;
private float labelWidth = 140f;
private float sliderWidth = 200f;
private float buttonHeight = 25f;
private float lineHeight = 30f;
private float windowWidth = 380f;
private float windowHeight = 520f;
private string[] enemyCueNames = new string[]
{
"Enemy_Idle",
"Enemy_Alert",
"Enemy_Attack",
"Enemy_Chase",
"Enemy_Death",
"Enemy_Damage_Taken",
"Enemy_Special"
};
private string[] battleCueNames = new string[]
{
"Battle_Start",
"Battle_Victory",
"Player_Attack",
"Player_Dodge",
"Player_Block",
"Player_Critical"
};
private Rect windowRect
{
get => new Rect(margin, margin, windowWidth, windowHeight);
}
private void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
Plugin.Log.LogInfo("AudioMixerGUI initialized");
}
else
{
Destroy(gameObject);
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.F1))
{
isVisible = !isVisible;
Plugin.Log.LogDebug($"Audio Mixer GUI: {(isVisible ? "VISIBLE" : "HIDDEN")}");
}
if (Input.GetKeyDown(KeyCode.F2))
{
isVisible = false;
}
}
private void OnGUI()
{
if (!isVisible) return;
GUI.skin = CreateCustomSkin();
windowRect.x = Mathf.Clamp(windowRect.x, 0, Screen.width - windowWidth - 10);
windowRect.y = Mathf.Clamp(windowRect.y, 0, Screen.height - windowHeight - 10);
windowRect = GUI.Window(0, windowRect, DrawMainWindow, "HomuraHime Audio Mixer (F1: Toggle | F2: Close)");
}
private GUISkin CreateCustomSkin()
{
GUISkin skin = GUI.skin;
GUIStyle headerStyle = new GUIStyle(GUI.skin.label)
{
fontSize = 14,
fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter
};
GUIStyle sectionStyle = new GUIStyle(GUI.skin.label)
{
fontSize = 12,
fontStyle = FontStyle.Bold,
normal = { textColor = new Color(0.9f, 0.7f, 0.3f) }
};
GUIStyle buttonStyle = new GUIStyle(GUI.skin.button)
{
fontSize = 11,
fontStyle = FontStyle.Normal
};
return skin;
}
private void DrawMainWindow(int windowID)
{
GUI.DragWindow(new Rect(0, 0, windowWidth - 20, 25));
scrollPosition = GUILayout.BeginScrollView(scrollPosition);
GUILayout.Space(margin);
DrawHeader();
GUILayout.Space(margin);
DrawVoiceControls();
GUILayout.Space(10);
DrawDuckingControls();
GUILayout.Space(10);
DrawEnemyAudioControls();
GUILayout.Space(10);
DrawEnemyCueTestButtons();
GUILayout.Space(10);
DrawBattleCueTestButtons();
GUILayout.Space(10);
DrawDebugControls();
GUILayout.Space(margin);
GUILayout.EndScrollView();
GUILayout.BeginHorizontal();
GUILayout.Label($"v1.0.0 | F1: Toggle | F2: Close", GUILayout.Height(20));
GUILayout.EndHorizontal();
}
private void DrawHeader()
{
GUILayout.BeginHorizontal();
GUILayout.Label("=== HOMURAHIME AUDIO MIXER ===", GUILayout.Height(25));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label($"Voice Count: {GetCurrentVoiceCount()} | Max: {ModConfig.MaxVoiceCount.Value}", GUILayout.Height(20));
GUILayout.EndHorizontal();
}
private void DrawVoiceControls()
{
GUILayout.BeginVertical("box");
GUILayout.Label("VOICE MANAGER", GUILayout.Height(20));
GUILayout.BeginHorizontal();
GUILayout.Label("Max Voices:", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.MaxVoiceCount.Value = (int)GUILayout.HorizontalSlider(
ModConfig.MaxVoiceCount.Value, 32, 256,
GUILayout.Width(sliderWidth), GUILayout.Height(lineHeight));
GUILayout.Label($"{ModConfig.MaxVoiceCount.Value}", GUILayout.Width(40));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label($"Voice Limit Fix: ", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.EnableVoiceLimitFix.Value = GUILayout.Toggle(
ModConfig.EnableVoiceLimitFix.Value, "Enabled");
GUILayout.EndHorizontal();
GUILayout.EndVertical();
}
private void DrawDuckingControls()
{
GUILayout.BeginVertical("box");
GUILayout.Label("AUDIO DUCKING", GUILayout.Height(20));
GUILayout.BeginHorizontal();
GUILayout.Label("Duck Fade Time:", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.DuckFadeTime.Value = GUILayout.HorizontalSlider(
ModConfig.DuckFadeTime.Value, 0.01f, 0.3f,
GUILayout.Width(sliderWidth), GUILayout.Height(lineHeight));
GUILayout.Label($"{ModConfig.DuckFadeTime.Value:F3}s", GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Duck Volume:", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.DuckVolume.Value = GUILayout.HorizontalSlider(
ModConfig.DuckVolume.Value, 0.1f, 0.8f,
GUILayout.Width(sliderWidth), GUILayout.Height(lineHeight));
GUILayout.Label($"{ModConfig.DuckVolume.Value:F2}", GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label($"Ducking Fix: ", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.EnableDuckingFix.Value = GUILayout.Toggle(
ModConfig.EnableDuckingFix.Value, "Enabled");
GUILayout.EndHorizontal();
GUILayout.EndVertical();
}
private void DrawEnemyAudioControls()
{
GUILayout.BeginVertical("box");
GUILayout.Label("ENEMY AUDIO INDICATORS", GUILayout.Height(20));
GUILayout.BeginHorizontal();
GUILayout.Label("Indicator Boost:", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.EnemyIndicatorBoost.Value = GUILayout.HorizontalSlider(
ModConfig.EnemyIndicatorBoost.Value, 0.5f, 2.0f,
GUILayout.Width(sliderWidth), GUILayout.Height(lineHeight));
GUILayout.Label($"{ModConfig.EnemyIndicatorBoost.Value:F2}x", GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Alert Boost:", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.AlertSoundBoost.Value = GUILayout.HorizontalSlider(
ModConfig.AlertSoundBoost.Value, 0.5f, 2.0f,
GUILayout.Width(sliderWidth), GUILayout.Height(lineHeight));
GUILayout.Label($"{ModConfig.AlertSoundBoost.Value:F2}x", GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Attack Boost:", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.AttackSoundBoost.Value = GUILayout.HorizontalSlider(
ModConfig.AttackSoundBoost.Value, 0.5f, 2.0f,
GUILayout.Width(sliderWidth), GUILayout.Height(lineHeight));
GUILayout.Label($"{ModConfig.AttackSoundBoost.Value:F2}x", GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label($"Enemy Audio: ", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.EnableEnemyAudioBoost.Value = GUILayout.Toggle(
ModConfig.EnableEnemyAudioBoost.Value, "Enabled");
GUILayout.EndHorizontal();
GUILayout.EndVertical();
}
private void DrawEnemyCueTestButtons()
{
GUILayout.BeginVertical("box");
GUILayout.Label("TEST ENEMY CUES (Debug)", GUILayout.Height(20));
GUILayout.BeginHorizontal();
for (int i = 0; i < enemyCueNames.Length; i++)
{
string cueName = enemyCueNames[i];
if (GUILayout.Button(cueName, GUILayout.Height(buttonHeight)))
{
TestEnemyCue(cueName);
}
if ((i + 1) % 2 == 0) GUILayout.EndHorizontal();
if ((i + 1) % 2 != 0 && i < enemyCueNames.Length - 1) GUILayout.BeginHorizontal();
}
if (enemyCueNames.Length % 2 != 0) GUILayout.EndHorizontal();
GUILayout.EndVertical();
}
private void DrawBattleCueTestButtons()
{
GUILayout.BeginVertical("box");
GUILayout.Label("TEST BATTLE CUES (Debug)", GUILayout.Height(20));
GUILayout.BeginHorizontal();
for (int i = 0; i < battleCueNames.Length; i++)
{
string cueName = battleCueNames[i];
if (GUILayout.Button(cueName, GUILayout.Height(buttonHeight)))
{
TestBattleCue(cueName);
}
if ((i + 1) % 2 == 0) GUILayout.EndHorizontal();
if ((i + 1) % 2 != 0 && i < battleCueNames.Length - 1) GUILayout.BeginHorizontal();
}
if (battleCueNames.Length % 2 != 0) GUILayout.EndHorizontal();
GUILayout.EndVertical();
}
private void DrawDebugControls()
{
GUILayout.BeginVertical("box");
GUILayout.Label("DEBUG OPTIONS", GUILayout.Height(20));
GUILayout.BeginHorizontal();
GUILayout.Label($"Debug Logging: ", GUILayout.Width(labelWidth), GUILayout.Height(lineHeight));
ModConfig.EnableDebugLogging.Value = GUILayout.Toggle(
ModConfig.EnableDebugLogging.Value, "Enabled");
GUILayout.EndHorizontal();
if (GUILayout.Button("Log Current Config", GUILayout.Height(buttonHeight)))
{
LogCurrentConfig();
}
if (GUILayout.Button("Reset to Defaults", GUILayout.Height(buttonHeight)))
{
ResetToDefaults();
}
GUILayout.EndVertical();
}
private int GetCurrentVoiceCount()
{
try
{
var voiceManagerType = FindTypeByName("FmodVoiceManager");
if (voiceManagerType != null)
{
var instance = AccessTools.Property(voiceManagerType, "Instance")?.GetValue(null);
if (instance != null)
{
var countProp = AccessTools.Property(voiceManagerType, "CountVoice");
if (countProp != null)
{
return (int)countProp.GetValue(instance);
}
}
}
}
catch { }
return -1;
}
private Type FindTypeByName(string name)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
if (type.Name.Contains(name))
return type;
}
}
return null;
}
private void TestEnemyCue(string cueName)
{
Plugin.Log.LogDebug($"[GUI] Testing enemy cue: {cueName}");
try
{
var cfVoiceType = FindTypeByName("CFVoiceEventUtility");
if (cfVoiceType != null)
{
var playMethod = AccessTools.Method(cfVoiceType, "PlayCFVoice");
if (playMethod != null)
{
playMethod.Invoke(null, new object[] { cueName });
Plugin.Log.LogDebug($"[GUI] Triggered enemy cue: {cueName}");
}
}
}
catch (Exception ex)
{
Plugin.Log.LogWarning($"[GUI] Could not trigger enemy cue: {ex.Message}");
}
}
private void TestBattleCue(string cueName)
{
Plugin.Log.LogDebug($"[GUI] Testing battle cue: {cueName}");
try
{
var cfVoiceType = FindTypeByName("CFVoiceEventUtility");
if (cfVoiceType != null)
{
var playMethod = AccessTools.Method(cfVoiceType, "PlayCFVoice");
if (playMethod != null)
{
playMethod.Invoke(null, new object[] { cueName });
Plugin.Log.LogDebug($"[GUI] Triggered battle cue: {cueName}");
}
}
}
catch (Exception ex)
{
Plugin.Log.LogWarning($"[GUI] Could not trigger battle cue: {ex.Message}");
}
}
private void LogCurrentConfig()
{
Plugin.Log.LogInfo("=== CURRENT AUDIO MOD CONFIG ===");
Plugin.Log.LogInfo($"MaxVoiceCount: {ModConfig.MaxVoiceCount.Value}");
Plugin.Log.LogInfo($"DuckFadeTime: {ModConfig.DuckFadeTime.Value}");
Plugin.Log.LogInfo($"DuckVolume: {ModConfig.DuckVolume.Value}");
Plugin.Log.LogInfo($"EnemyIndicatorBoost: {ModConfig.EnemyIndicatorBoost.Value}");
Plugin.Log.LogInfo($"AlertSoundBoost: {ModConfig.AlertSoundBoost.Value}");
Plugin.Log.LogInfo($"AttackSoundBoost: {ModConfig.AttackSoundBoost.Value}");
Plugin.Log.LogInfo($"EnableVoiceLimitFix: {ModConfig.EnableVoiceLimitFix.Value}");
Plugin.Log.LogInfo($"EnableDuckingFix: {ModConfig.EnableDuckingFix.Value}");
Plugin.Log.LogInfo($"EnableEnemyAudioBoost: {ModConfig.EnableEnemyAudioBoost.Value}");
Plugin.Log.LogInfo($"EnableDebugLogging: {ModConfig.EnableDebugLogging.Value}");
Plugin.Log.LogInfo("===============================");
}
private void ResetToDefaults()
{
ModConfig.MaxVoiceCount.Value = 128;
ModConfig.DuckFadeTime.Value = 0.05f;
ModConfig.DuckVolume.Value = 0.3f;
ModConfig.EnemyIndicatorBoost.Value = 1.2f;
ModConfig.AlertSoundBoost.Value = 1.3f;
ModConfig.AttackSoundBoost.Value = 1.15f;
ModConfig.EnableVoiceLimitFix.Value = true;
ModConfig.EnableDuckingFix.Value = true;
ModConfig.EnableEnemyAudioBoost.Value = true;
ModConfig.EnableDebugLogging.Value = false;
Plugin.Log.LogInfo("[GUI] Config reset to defaults");
}
}
}

View File

@@ -10,34 +10,61 @@
<Product>HomuraHime Audio Mod</Product> <Product>HomuraHime Audio Mod</Product>
<Description>Audio improvements mod for HomuraHime - fixes glitches and improves enemy indicators</Description> <Description>Audio improvements mod for HomuraHime - fixes glitches and improves enemy indicators</Description>
<Copyright>Copyright 2026</Copyright> <Copyright>Copyright 2026</Copyright>
<GamePath>C:\apps\steam\steamapps\common\Homura Hime\HomuraHime_Data\Managed</GamePath> <GamePath>C:\apps\steam\steamapps\common\Homura Hime</GamePath>
</PropertyGroup> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<PropertyGroup Condition="Exists('$(GamePath)')">
<DefineConstants>GAME_PATH_EXISTS</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="0Harmony" Version="2.2.2"> <Reference Include="0Harmony">
<ExcludeAssets>runtime</ExcludeAssets> <HintPath>$(GamePath)\BepInEx\core\0Harmony.dll</HintPath>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="Exists('$(GamePath)')">
<Reference Include="BepInEx">
<HintPath>$(GamePath)\..\BepInEx\core\BepInEx.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="FMODUnity"> <Reference Include="BepInEx">
<HintPath>$(GamePath)\FMODUnity.dll</HintPath> <HintPath>$(GamePath)\BepInEx\core\BepInEx.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="UnityEngine.AudioModule"> <Reference Include="UnityEngine.AudioModule">
<HintPath>$(GamePath)\UnityEngine.AudioModule.dll</HintPath> <HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.AudioModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.UI">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.UI.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.UIModule">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\UnityEngine.UIModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="TMPro">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\Unity.TextMeshPro.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="FMODUnity">
<HintPath>$(GamePath)\HomuraHime_Data\Managed\FMODUnity.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
<Reference Include="Assembly-CSharp"> <Reference Include="Assembly-CSharp">
<HintPath>$(GamePath)\Assembly-CSharp.dll</HintPath> <HintPath>$(GamePath)\HomuraHime_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>false</Private> <Private>false</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>

View File

@@ -1,85 +1,47 @@
using BepInEx; using BepInEx;
using BepInEx.Logging; using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
namespace HomuraHimeAudioMod namespace HomuraHimeAudioMod
{ {
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] [BepInPlugin("com.homurahime.audiomod", "HomuraHime Audio Mod", "1.0.0")]
public class Plugin : BaseUnityPlugin public class Plugin : BaseUnityPlugin
{ {
public const string PLUGIN_GUID = "com.homurahime.audiomod";
public const string PLUGIN_NAME = "HomuraHime Audio Mod";
public const string PLUGIN_VERSION = "1.0.0";
private static Plugin instance;
private static ManualLogSource logger; private static ManualLogSource logger;
private Harmony harmony;
public static ManualLogSource Log => logger; public static ManualLogSource Log => logger;
private void Awake() private void Awake()
{ {
instance = this;
logger = Logger; logger = Logger;
ModConfig.Initialize(Config); ModConfig.Initialize(Config);
Log.LogInfo($"HomuraHime Audio Mod v{PluginInfo.PLUGIN_VERSION} initializing..."); Log.LogInfo("========================================");
Log.LogInfo($"Voice Limit Fix: {(ModConfig.EnableVoiceLimitFix.Value ? "ENABLED" : "DISABLED")}"); Log.LogInfo("HomuraHime Audio Mod v1.0.0");
Log.LogInfo($"Ducking Fix: {(ModConfig.EnableDuckingFix.Value ? "ENABLED" : "DISABLED")}"); Log.LogInfo("========================================");
Log.LogInfo($"Enemy Audio Boost: {(ModConfig.EnableEnemyAudioBoost.Value ? "ENABLED" : "DISABLED")}"); Log.LogInfo("");
Log.LogInfo($"Max Voices: {ModConfig.MaxVoiceCount.Value}"); Log.LogInfo("CONFIGURATION (edit in BepInEx/config/com.homurahime.audiomod.cfg):");
Log.LogInfo("");
harmony = new Harmony(PluginInfo.PLUGIN_GUID); Log.LogInfo(" [VoiceManager]");
Log.LogInfo($" MaxVoiceCount = {ModConfig.MaxVoiceCount.Value}");
ApplyPatches(); Log.LogInfo($" EnableVoiceLimitFix = {ModConfig.EnableVoiceLimitFix.Value}");
Log.LogInfo("");
InitializeGUI(); Log.LogInfo(" [Ducking]");
Log.LogInfo($" DuckFadeTime = {ModConfig.DuckFadeTime.Value}");
Log.LogInfo("HomuraHime Audio Mod initialized successfully"); Log.LogInfo($" DuckVolume = {ModConfig.DuckVolume.Value}");
Log.LogInfo("Press F1 to open Audio Mixer GUI"); Log.LogInfo($" EnableDuckingFix = {ModConfig.EnableDuckingFix.Value}");
} Log.LogInfo("");
Log.LogInfo(" [EnemyAudio]");
private void ApplyPatches() Log.LogInfo($" EnemyIndicatorBoost = {ModConfig.EnemyIndicatorBoost.Value}");
{ Log.LogInfo($" AlertSoundBoost = {ModConfig.AlertSoundBoost.Value}");
if (ModConfig.EnableVoiceLimitFix.Value || ModConfig.EnableDuckingFix.Value) Log.LogInfo($" AttackSoundBoost = {ModConfig.AttackSoundBoost.Value}");
{ Log.LogInfo($" EnableEnemyAudioBoost = {ModConfig.EnableEnemyAudioBoost.Value}");
Log.LogInfo("Applying VoiceManager patches..."); Log.LogInfo("");
VoiceManagerPatch.Apply(ref harmony); Log.LogInfo("NOTE: Harmony patches not yet implemented.");
} Log.LogInfo(" Patches will be added after code analysis.");
Log.LogInfo("");
if (ModConfig.EnableDuckingFix.Value) Log.LogInfo("========================================");
{ Log.LogInfo("Mod loaded. Edit config file to change values.");
Log.LogInfo("Applying AudioDucking patches...");
AudioDuckingPatch.Apply(ref harmony);
}
if (ModConfig.EnableEnemyAudioBoost.Value)
{
Log.LogInfo("Applying EnemyAudio patches...");
EnemyAudioPatch.Apply(ref harmony);
}
Log.LogInfo("All patches applied successfully");
}
private void InitializeGUI()
{
GameObject guiObject = new GameObject("HomuraHimeAudioMixer");
guiObject.AddComponent<GUI.AudioMixerGUI>();
DontDestroyOnLoad(guiObject);
Log.LogInfo("Audio Mixer GUI initialized");
}
private void Update()
{
}
private void OnDestroy()
{
harmony?.UnpatchAll(PluginInfo.PLUGIN_GUID);
Log.LogInfo("HomuraHime Audio Mod unloaded");
} }
} }
} }

View File

@@ -1,19 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("HomuraHimeAudioMod")]
[assembly: AssemblyDescription("Audio improvements mod for HomuraHime")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HomuraHimeAudioMod")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]