using System; using System.Collections.Generic; using System.Text; using UnityEngine; using HarmonyLib; namespace HomuraHimeAudioMod.GUI { public class AudioMixerGUI : MonoBehaviour { private bool isVisible = false; private bool showConsole = true; private Vector2 scrollPosition; private Vector2 consoleScrollPosition; private float margin = 10f; private float labelWidth = 140f; private float sliderWidth = 200f; private float buttonHeight = 25f; private float lineHeight = 30f; private float windowWidth = 450f; private float windowHeight = 580f; private Rect windowRect = new Rect(10, 10, 450, 580); private const int MAX_CONSOLE_LINES = 100; private List consoleLines = new List(); private string lastEventContext = ""; 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 Type cachedVoiceManagerType; private Type cachedCFVoiceType; private bool typesCached = false; private int cachedVoiceCount = -1; private float lastVoiceCountUpdate = 0f; private const float VOICE_COUNT_UPDATE_INTERVAL = 0.5f; private void Start() { AddConsoleLine("AudioMixerGUI started"); AddConsoleLine("Press F1 to toggle GUI"); AddConsoleLine("Press F2 to close"); } private void OnDestroy() { RestoreCursorState(); } private void Update() { if (!isVisible) { if (Cursor.lockState != CursorLockMode.Locked) { return; } } if (UnityEngine.Input.GetKeyDown(KeyCode.F1)) { isVisible = !isVisible; UpdateCursorState(); AddConsoleLine(isVisible ? "GUI Opened" : "GUI Closed"); } if (UnityEngine.Input.GetKeyDown(KeyCode.F2) && isVisible) { isVisible = false; UpdateCursorState(); AddConsoleLine("GUI Closed"); } if (Time.time - lastVoiceCountUpdate >= VOICE_COUNT_UPDATE_INTERVAL) { int newCount = GetCurrentVoiceCount(); if (newCount != cachedVoiceCount) { cachedVoiceCount = newCount; if (cachedVoiceCount >= 0) { AddConsoleLine($"Voice Count: {cachedVoiceCount}"); } } lastVoiceCountUpdate = Time.time; } } private void UpdateCursorState() { if (isVisible) { Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } else { RestoreCursorState(); } } private void RestoreCursorState() { Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; } private void AddConsoleLine(string line) { if (!showConsole) return; string timestamp = Time.time.ToString("F2"); consoleLines.Add($"[{timestamp}] {line}"); while (consoleLines.Count > MAX_CONSOLE_LINES) { consoleLines.RemoveAt(0); } } private void CacheTypes() { if (typesCached) return; cachedVoiceManagerType = FindTypeByName("FmodVoiceManager"); cachedCFVoiceType = FindTypeByName("CFVoiceEventUtility"); typesCached = true; AddConsoleLine($"Types cached: VoiceManager={cachedVoiceManagerType?.Name}, CFVoice={cachedCFVoiceType?.Name}"); } private void OnGUI() { if (!isVisible) return; windowRect.x = Mathf.Clamp(windowRect.x, 0, Screen.width - windowWidth - 10); windowRect.y = Mathf.Clamp(windowRect.y, 0, Screen.height - windowHeight - 10); windowRect = UnityEngine.GUI.Window(0, windowRect, DrawMainWindow, "HomuraHime Audio Mixer"); } private void DrawMainWindow(int windowID) { UnityEngine.GUI.DragWindow(new Rect(0, 0, windowWidth - 20, 25)); GUILayout.BeginHorizontal(); showConsole = GUILayout.Toggle(showConsole, "Show Console", GUILayout.Width(100)); GUILayout.FlexibleSpace(); if (GUILayout.Button("Clear Log", GUILayout.Width(70))) { consoleLines.Clear(); AddConsoleLine("Log cleared"); } GUILayout.EndHorizontal(); scrollPosition = GUILayout.BeginScrollView(scrollPosition); 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(); if (showConsole) { DrawConsole(); } GUILayout.BeginHorizontal(); GUILayout.Label($"v1.0.0 | F1: Toggle | F2: Close", GUILayout.Height(20)); GUILayout.EndHorizontal(); } private void DrawConsole() { GUILayout.Box("CONSOLE LOG", GUILayout.Height(20)); consoleScrollPosition = GUILayout.BeginScrollView(consoleScrollPosition, GUILayout.Height(120)); GUIStyle logStyle = new GUIStyle(UnityEngine.GUI.skin.label) { fontSize = 10, richText = true }; Color normalColor = logStyle.normal.textColor; Color warningColor = new Color(1f, 0.7f, 0.3f); Color errorColor = new Color(1f, 0.4f, 0.4f); Color infoColor = new Color(0.7f, 0.9f, 1f); for (int i = 0; i < consoleLines.Count; i++) { string line = consoleLines[i]; GUIStyle lineStyle = new GUIStyle(logStyle); if (line.Contains("[ERROR]") || line.Contains("Error")) { lineStyle.normal.textColor = errorColor; } else if (line.Contains("[WARN]") || line.Contains("Warning")) { lineStyle.normal.textColor = warningColor; } else if (line.Contains("[GUI]") || line.Contains("Triggered") || line.Contains("Testing")) { lineStyle.normal.textColor = infoColor; } else { lineStyle.normal.textColor = normalColor; } GUILayout.Label(line, lineStyle); } GUILayout.EndScrollView(); } private void DrawHeader() { GUILayout.BeginVertical("box"); GUILayout.Label("=== HOMURAHIME AUDIO MIXER ===", GUILayout.Height(25)); int voiceCount = GetCurrentVoiceCount(); string voiceStatus = voiceCount >= 0 ? $"{voiceCount}" : "N/A"; string voiceWarning = voiceCount >= ModConfig.MaxVoiceCount.Value * 0.9f ? " (HIGH!)" : ""; GUILayout.BeginHorizontal(); GUILayout.Label($"Voice Count: {voiceStatus} / {ModConfig.MaxVoiceCount.Value}{voiceWarning}", GUILayout.Height(20)); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } 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", 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", 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 { if (!typesCached) CacheTypes(); if (cachedVoiceManagerType != null) { var instance = AccessTools.Property(cachedVoiceManagerType, "Instance")?.GetValue(null); if (instance != null) { var countProp = AccessTools.Property(cachedVoiceManagerType, "CountVoice"); if (countProp != null) { return (int)countProp.GetValue(instance); } } } } catch { } return -1; } private Type FindTypeByName(string name) { foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { try { foreach (var type in assembly.GetTypes()) { if (type.Name.Contains(name)) return type; } } catch { } } return null; } private void TestEnemyCue(string cueName) { AddConsoleLine($"[GUI] Testing enemy cue: {cueName}"); Plugin.Log.LogDebug($"[GUI] Testing enemy cue: {cueName}"); try { if (!typesCached) CacheTypes(); if (cachedCFVoiceType != null) { var playMethod = AccessTools.Method(cachedCFVoiceType, "PlayCFVoice"); if (playMethod != null) { playMethod.Invoke(null, new object[] { cueName }); AddConsoleLine($"[GUI] Triggered: {cueName}"); } else { AddConsoleLine($"[GUI] Method not found: PlayCFVoice"); } } else { AddConsoleLine($"[GUI] CFVoiceEventUtility not found"); } } catch (Exception ex) { AddConsoleLine($"[GUI] Error: {ex.Message}"); Plugin.Log.LogWarning($"[GUI] Could not trigger enemy cue: {ex.Message}"); } } private void TestBattleCue(string cueName) { AddConsoleLine($"[GUI] Testing battle cue: {cueName}"); Plugin.Log.LogDebug($"[GUI] Testing battle cue: {cueName}"); try { if (!typesCached) CacheTypes(); if (cachedCFVoiceType != null) { var playMethod = AccessTools.Method(cachedCFVoiceType, "PlayCFVoice"); if (playMethod != null) { playMethod.Invoke(null, new object[] { cueName }); AddConsoleLine($"[GUI] Triggered: {cueName}"); } else { AddConsoleLine($"[GUI] Method not found: PlayCFVoice"); } } else { AddConsoleLine($"[GUI] CFVoiceEventUtility not found"); } } catch (Exception ex) { AddConsoleLine($"[GUI] Error: {ex.Message}"); Plugin.Log.LogWarning($"[GUI] Could not trigger battle cue: {ex.Message}"); } } private void LogCurrentConfig() { AddConsoleLine("=== CONFIG DUMP ==="); AddConsoleLine($"MaxVoiceCount: {ModConfig.MaxVoiceCount.Value}"); AddConsoleLine($"DuckFadeTime: {ModConfig.DuckFadeTime.Value}"); AddConsoleLine($"DuckVolume: {ModConfig.DuckVolume.Value}"); AddConsoleLine($"EnemyIndicatorBoost: {ModConfig.EnemyIndicatorBoost.Value}"); AddConsoleLine($"AlertSoundBoost: {ModConfig.AlertSoundBoost.Value}"); AddConsoleLine($"AttackSoundBoost: {ModConfig.AttackSoundBoost.Value}"); AddConsoleLine($"VoiceLimitFix: {ModConfig.EnableVoiceLimitFix.Value}"); AddConsoleLine($"DuckingFix: {ModConfig.EnableDuckingFix.Value}"); AddConsoleLine($"EnemyAudioBoost: {ModConfig.EnableEnemyAudioBoost.Value}"); AddConsoleLine($"DebugLogging: {ModConfig.EnableDebugLogging.Value}"); AddConsoleLine("==================="); 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; AddConsoleLine("[GUI] Config reset to defaults"); Plugin.Log.LogInfo("[GUI] Config reset to defaults"); } } }