Compare commits
12 Commits
7169d4533b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 906a4d5a74 | |||
| c0b6816ab0 | |||
| 5a9f306f3f | |||
| 4251a29794 | |||
| 81bf38b472 | |||
| 4285ae5258 | |||
| b589d39ccd | |||
| 2f874a89e4 | |||
| a6fa90375e | |||
| 8f162dc103 | |||
| 71712b834c | |||
| aa3de01335 |
13
.beads/backup/backup_state.json
Normal file
13
.beads/backup/backup_state.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"last_dolt_commit": "psmjkfsutica7b0ot19arg27jt04m7fc",
|
||||
"last_event_id": 0,
|
||||
"timestamp": "2026-03-22T17:41:24.5232162Z",
|
||||
"counts": {
|
||||
"issues": 6,
|
||||
"events": 10,
|
||||
"comments": 0,
|
||||
"dependencies": 0,
|
||||
"labels": 0,
|
||||
"config": 11
|
||||
}
|
||||
}
|
||||
0
.beads/backup/comments.jsonl
Normal file
0
.beads/backup/comments.jsonl
Normal file
11
.beads/backup/config.jsonl
Normal file
11
.beads/backup/config.jsonl
Normal file
@@ -0,0 +1,11 @@
|
||||
{"key":"auto_compact_enabled","value":"false"}
|
||||
{"key":"compact_batch_size","value":"50"}
|
||||
{"key":"compact_parallel_workers","value":"5"}
|
||||
{"key":"compact_tier1_days","value":"30"}
|
||||
{"key":"compact_tier1_dep_levels","value":"2"}
|
||||
{"key":"compact_tier2_commits","value":"100"}
|
||||
{"key":"compact_tier2_days","value":"90"}
|
||||
{"key":"compact_tier2_dep_levels","value":"5"}
|
||||
{"key":"compaction_enabled","value":"false"}
|
||||
{"key":"issue_prefix","value":"HomuraHime-Mods"}
|
||||
{"key":"schema_version","value":"6"}
|
||||
0
.beads/backup/dependencies.jsonl
Normal file
0
.beads/backup/dependencies.jsonl
Normal file
10
.beads/backup/events.jsonl
Normal file
10
.beads/backup/events.jsonl
Normal file
@@ -0,0 +1,10 @@
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:31Z","event_type":"created","id":1,"issue_id":"HomuraHime-Mods-9ji","new_value":"","old_value":""}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:35Z","event_type":"created","id":2,"issue_id":"HomuraHime-Mods-0cl","new_value":"","old_value":""}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:37Z","event_type":"created","id":3,"issue_id":"HomuraHime-Mods-h3x","new_value":"","old_value":""}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:41Z","event_type":"created","id":4,"issue_id":"HomuraHime-Mods-zom","new_value":"","old_value":""}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:43Z","event_type":"created","id":5,"issue_id":"HomuraHime-Mods-9k1","new_value":"","old_value":""}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:45Z","event_type":"created","id":6,"issue_id":"HomuraHime-Mods-4vf","new_value":"","old_value":""}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:32:53Z","event_type":"claimed","id":7,"issue_id":"HomuraHime-Mods-9ji","new_value":"{\"assignee\":\"Ed_\",\"status\":\"in_progress\"}","old_value":"{\"id\":\"HomuraHime-Mods-9ji\",\"title\":\"Phase 1: Environment Setup - Install BepInEx, FMOD Studio, AssetRipper, ILSpy\",\"description\":\"Install and configure all required modding tools:\\n1. BepInEx 5.x framework for runtime patching\\n2. FMOD Studio 2.02.x (free) for audio bank editing\\n3. AssetRipper for extracting game assets\\n4. ILSpy for .NET assembly analysis\\n5. Unity 2022.3.x (optional) for script compilation\\n6. Verify Steam integration\\n7. Create project directory structure\",\"status\":\"open\",\"priority\":1,\"issue_type\":\"epic\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-22T17:32:32Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-22T17:32:32Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:35:13Z","event_type":"closed","id":8,"issue_id":"HomuraHime-Mods-9ji","new_value":"Phase 1 complete: Project structure created, setup guide documented, plugin skeleton ready","old_value":""}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:35:16Z","event_type":"claimed","id":9,"issue_id":"HomuraHime-Mods-0cl","new_value":"{\"assignee\":\"Ed_\",\"status\":\"in_progress\"}","old_value":"{\"id\":\"HomuraHime-Mods-0cl\",\"title\":\"Phase 2: Code Analysis - Map audio architecture via ILSpy\",\"description\":\"Decompile and analyze game assemblies:\\n1. Decompile Assembly-CSharp.dll and Assembly-CSharp-firstpass.dll\\n2. Map audio architecture (UtageFmodVoiceManager, FMODSoundManager, etc.)\\n3. Find voice limit constants and hook points\\n4. Document enemy audio trigger classes\\n5. Locate audio ducking implementation\\n6. Identify CFVOICE event system\",\"status\":\"open\",\"priority\":1,\"issue_type\":\"task\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-22T17:32:35Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-22T17:32:35Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-22T13:41:24Z","event_type":"closed","id":10,"issue_id":"HomuraHime-Mods-0cl","new_value":"Phase 2 complete: Code analysis done, Harmony patches implemented for voice limits, ducking, and enemy audio","old_value":""}
|
||||
6
.beads/backup/issues.jsonl
Normal file
6
.beads/backup/issues.jsonl
Normal file
@@ -0,0 +1,6 @@
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"Phase 2 complete: Code analysis done, Harmony patches implemented for voice limits, ducking, and enemy audio","closed_at":"2026-03-22T17:41:24Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"2439ef219114c6bcad28bc4c7dbee04ade1621b998b4e96e0d19abd8a8d6c60c","created_at":"2026-03-22T17:32:35Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Decompile and analyze game assemblies:\n1. Decompile Assembly-CSharp.dll and Assembly-CSharp-firstpass.dll\n2. Map audio architecture (UtageFmodVoiceManager, FMODSoundManager, etc.)\n3. Find voice limit constants and hook points\n4. Document enemy audio trigger classes\n5. Locate audio ducking implementation\n6. Identify CFVOICE event system","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-0cl","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"Phase 2: Code Analysis - Map audio architecture via ILSpy","updated_at":"2026-03-22T17:41:24Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"12b5b00518f2e20b614b241ab5383df40adff70d9e022a5e9615d1eac7b95542","created_at":"2026-03-22T17:32:45Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Package mod for sharing:\n1. Organize distribution structure (BepInEx plugin + modified banks)\n2. Create README with installation instructions\n3. Document backup procedures\n4. Test installation on clean game copy","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-4vf","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Phase 6: Distribution - Package mod for release","updated_at":"2026-03-22T17:32:45Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"Phase 1 complete: Project structure created, setup guide documented, plugin skeleton ready","closed_at":"2026-03-22T17:35:14Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"05aaa99ca45627ae8f9b07fe869a3af286174b23ff93a5d886f65d9791278fff","created_at":"2026-03-22T17:32:32Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Install and configure all required modding tools:\n1. BepInEx 5.x framework for runtime patching\n2. FMOD Studio 2.02.x (free) for audio bank editing\n3. AssetRipper for extracting game assets\n4. ILSpy for .NET assembly analysis\n5. Unity 2022.3.x (optional) for script compilation\n6. Verify Steam integration\n7. Create project directory structure","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-9ji","is_template":0,"issue_type":"epic","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"Phase 1: Environment Setup - Install BepInEx, FMOD Studio, AssetRipper, ILSpy","updated_at":"2026-03-22T17:35:14Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"609b1c0af8d04ada87841a7be4e2ac5f84374323580532ccd1c8057146bfeabb","created_at":"2026-03-22T17:32:43Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Test protocol for audio mod:\n1. Quiet exploration - baseline audio\n2. Combat with 3+ enemies - check distortion\n3. Boss fights - heavy audio load scenarios\n4. Swarm enemies - rapid sound triggers\n5. Enemy behavior state changes - cue clarity\nMetrics: distortion, cutting out, cue audibility, glitch frequency","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-9k1","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Phase 5: Testing \u0026 Iteration","updated_at":"2026-03-22T17:32:43Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"c33eb62b6dc1c38bed5afd3ba806c1c83e5b09c779b28c249a10c1d4bb00cee7","created_at":"2026-03-22T17:32:38Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Modify audio content using FMOD Studio:\n1. Extract FMOD banks via AssetRipper\n2. Voice limit improvements - adjust max instances, voice aging\n3. Audio ducking improvements - tune fade times, duck amounts\n4. Enemy behavior indicator improvements:\n - Increase volume on behavior sounds\n - Add pitch variation for distinctness\n - Use 3D positioning for directional audio\n - Add layers for enemy states (idle/alert/attack)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-h3x","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Phase 3: Audio Content Modifications - Edit FMOD banks","updated_at":"2026-03-22T17:32:38Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"a9decf0c5774ccdc6c4202d5af25ce08092420f5b24799d124a90be2dc2ca16e","created_at":"2026-03-22T17:32:41Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Develop BepInEx plugin for runtime audio patches:\n1. Set up BepInEx plugin project structure\n2. Create HarmonyX patches for:\n - VoiceManagerPatch (increase voice limits)\n - AudioDuckingPatch (custom ducking logic)\n - EnemyAudioPatch (behavior cue enhancements)\n3. Implement dynamic voice limiting based on CPU pressure\n4. Add custom volume controls for enemy indicators\n5. Create debug logging for audio glitches","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"HomuraHime-Mods-zom","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Phase 4: Runtime Patches - Create BepInEx plugins","updated_at":"2026-03-22T17:32:41Z","waiters":"","wisp_type":"","work_type":""}
|
||||
0
.beads/backup/labels.jsonl
Normal file
0
.beads/backup/labels.jsonl
Normal file
@@ -1 +1 @@
|
||||
610036
|
||||
622840
|
||||
@@ -1 +1 @@
|
||||
1774200743
|
||||
1774205514
|
||||
127
dist/HomuraHime_AudioMod/INSTALL.md
vendored
Normal file
127
dist/HomuraHime_AudioMod/INSTALL.md
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
# HomuraHime Audio Mod - Installation Guide
|
||||
|
||||
## Quick Install
|
||||
|
||||
### Prerequisites
|
||||
- HomuraHime on Steam
|
||||
- BepInEx 5.x
|
||||
|
||||
### Steps
|
||||
|
||||
1. **Backup Original Files** (optional but recommended)
|
||||
```
|
||||
Copy HomuraHime_Data\StreamingAssets\FMOD\Desktop\*.bank to backup folder
|
||||
```
|
||||
|
||||
2. **Install BepInEx** (if not already installed)
|
||||
- Download BepInEx 5.x from: https://github.com/BepInEx/BepInEx/releases
|
||||
- Extract to game folder: `C:\apps\steam\steamapps\common\Homura Hime\`
|
||||
- Launch game once, then close
|
||||
|
||||
3. **Copy Mod Files**
|
||||
```
|
||||
Copy BepInEx\plugins\HomuraHimeAudioMod.dll
|
||||
To: C:\apps\steam\steamapps\common\Homura Hime\BepInEx\plugins\
|
||||
```
|
||||
|
||||
4. **Launch Game**
|
||||
```
|
||||
Start HomuraHime via Steam
|
||||
Check BepInEx\LogOutput.log for: "HomuraHime Audio Mod initialized successfully"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mod Components
|
||||
|
||||
### Required (BepInEx Plugin)
|
||||
- `BepInEx/plugins/HomuraHimeAudioMod.dll` - Main mod DLL
|
||||
|
||||
### Optional (FMOD Banks)
|
||||
- `FMOD_banks/Desktop/*.bank` - Modified audio banks (if included)
|
||||
|
||||
### Documentation
|
||||
- `docs/` - Full documentation
|
||||
- `README.md` - This file
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
After first launch, configure in:
|
||||
```
|
||||
C:\apps\steam\steamapps\common\Homura Hime\BepInEx\config\com.homurahime.audiomod.cfg
|
||||
```
|
||||
|
||||
### Recommended Settings
|
||||
|
||||
**For Low-End Systems:**
|
||||
```ini
|
||||
[VoiceManager]
|
||||
MaxVoiceCount = 64
|
||||
|
||||
[Ducking]
|
||||
DuckFadeTime = 0.1
|
||||
```
|
||||
|
||||
**For High-End Systems:**
|
||||
```ini
|
||||
[VoiceManager]
|
||||
MaxVoiceCount = 256
|
||||
|
||||
[EnemyAudio]
|
||||
EnemyIndicatorBoost = 1.5
|
||||
AlertSoundBoost = 1.6
|
||||
AttackSoundBoost = 1.4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Mod Not Loading
|
||||
1. Check `BepInEx\LogOutput.log`
|
||||
2. Ensure DLL is in `BepInEx\plugins\` (not a subfolder)
|
||||
3. Verify .NET Framework 4.8 is installed
|
||||
|
||||
### Audio Glitches Persist
|
||||
1. Reduce `MaxVoiceCount` to 64
|
||||
2. Increase `DuckFadeTime` to 0.1
|
||||
3. Disable FMOD banks modification if using
|
||||
|
||||
### Game Crashes
|
||||
1. Disable mod temporarily
|
||||
2. Test with default settings
|
||||
3. Check for conflicting mods
|
||||
|
||||
---
|
||||
|
||||
## Uninstall
|
||||
|
||||
1. Delete `BepInEx\plugins\HomuraHimeAudioMod.dll`
|
||||
2. Restore original FMOD banks from backup (if modified)
|
||||
3. Delete config: `BepInEx\config\com.homurahime.audiomod.cfg`
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
### v1.0.0
|
||||
- Initial release
|
||||
- Voice limit fix (configurable 64-256)
|
||||
- Audio ducking improvements
|
||||
- Enemy audio indicator boost
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
- Mod by: [Your Name]
|
||||
- Framework: BepInEx
|
||||
- Audio Engine: FMOD Studio
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
For personal use only. Please respect game copyright.
|
||||
312
docs/CODE_ANALYSIS_PHASE2.md
Normal file
312
docs/CODE_ANALYSIS_PHASE2.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# HomuraHime Audio System Deep Analysis
|
||||
|
||||
## 1. VOICE LIMITS & CHANNEL MANAGEMENT
|
||||
|
||||
### UtageFmodVoiceManager
|
||||
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\UtageFmodVoiceManager.cs`
|
||||
|
||||
| Method/Property | Description | Hook Point |
|
||||
|-----------------|-------------|------------|
|
||||
| `PlayVoice()` / `CoPlayVoice` | Coroutine for playing voice with lip sync | `Prefix` to modify voice data, `Postfix` to track playback |
|
||||
| `StopVoiceEvent()` | Stops currently playing voice | `Prefix` to prevent stop, `Postfix` to cleanup |
|
||||
| `get_CountVoice` | Gets current voice count | `Postfix` to limit voice count |
|
||||
| `get_CurrentVoiceCharacterLabel` | Current voice character label | `Postfix` to override character |
|
||||
| `get_DefaultVoiceFadeTime` | Default fade time for voice | `Postfix` to modify fade duration |
|
||||
| `set_DuckVolume` / `get_DuckVolume` | Duck volume property | `Postfix` to modify ducking |
|
||||
| `UpdateDucking` | Coroutine `<UpdateDucking>d__47` | **KEY HOOK** - controls audio ducking coroutine |
|
||||
| `PlayVoiceEvent()` | Triggers voice events | `Prefix` to intercept event data |
|
||||
| `StopVoiceIgnoreLoop` | Stops voice ignoring loop setting | Direct patch target |
|
||||
|
||||
### FMODSoundManager
|
||||
**File:** `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODSoundManager.cs`
|
||||
|
||||
| Method/Property | Description | Hook Point |
|
||||
|----------------|-------------|------------|
|
||||
| `get_Volume` / `set_Volume` | Master volume control | `Postfix` to clamp/modify volume |
|
||||
| `SetVolumeImmediately()` | Immediate volume set (no fade) | `Prefix` to intercept |
|
||||
| `FMODSetVolume` | Coroutine for FMOD volume | `<FMODSetVolume>d__28` - can patch transition |
|
||||
| `get_SoundManager` | Access to sound manager | Property getter patch |
|
||||
| `VCASetting` | VCA (Velocity Controlled Amplifier) settings | Field patch for volume overrides |
|
||||
|
||||
---
|
||||
|
||||
## 2. AUDIO DUCKING SYSTEM
|
||||
|
||||
### Ducking Properties Found:
|
||||
```
|
||||
duckFadeTime - Fade time for ducking
|
||||
duckVolume - Volume level during ducking
|
||||
duckGroups - Audio groups that participate in ducking
|
||||
duckVelocity - Velocity tracking for ducking
|
||||
UpdateDucking - Main coroutine: `<UpdateDucking>d__47`
|
||||
```
|
||||
|
||||
### SnapshotManager
|
||||
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\SnapshotManager.cs`
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `ApplySnapshot()` | Apply audio snapshot/ducking |
|
||||
| `ApplyStartSnapshot()` | Apply at start |
|
||||
| `GetCurrentSnapshot()` | Get active snapshot |
|
||||
| `GetSnapshotTypeDropdown()` | Dropdown selection |
|
||||
|
||||
### SnapshotHandle
|
||||
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\SnapshotHandle.cs`
|
||||
|
||||
Manages individual snapshot instances for voice ducking.
|
||||
|
||||
### DEAudioFadeManager
|
||||
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\DEAudioFadeManager.cs`
|
||||
|
||||
Handles fade in/out for audio events - likely the fade time controller for ducking.
|
||||
|
||||
### FmodReverbEventController
|
||||
**File:** `C:\Assets\4.Developer\Andy\UtageVoiceManager\FmodReverbEventController.cs`
|
||||
|
||||
Controls reverb effects during events - related to audio environment transitions.
|
||||
|
||||
---
|
||||
|
||||
## 3. VOICE MANAGER PUBLIC METHODS
|
||||
|
||||
### Core Voice Control Methods:
|
||||
|
||||
| Class | Method | Description |
|
||||
|-------|--------|-------------|
|
||||
| `UtageFmodVoiceManager` | `PlayVoice()` | Plays voice file with optional lip sync |
|
||||
| `UtageFmodVoiceManager` | `StopVoiceEvent()` | Stops voice playback |
|
||||
| `UtageFmodVoiceManager` | `StopVoiceIgnoreLoop()` | Force stop even if loop |
|
||||
| `UtageFmodVoiceManager` | `PlayVoiceEvent()` | Triggers voice event |
|
||||
| `UtageFmodVoiceManager` | `GetVoiceSamplesVolume()` | Gets voice volume setting |
|
||||
| `UtageFmodVoiceManager` | `SetVoiceVolume()` | Sets voice volume |
|
||||
| `VoiceEventSelector` | - | Selects which voice event to trigger |
|
||||
| `FmodVoiceStopper` | `Stop()` | FMOD-specific voice stopping |
|
||||
|
||||
### Voice Settings:
|
||||
| Property | Description |
|
||||
|----------|-------------|
|
||||
| `defaultVoiceFadeTime` | Default fade time (getter/setter) |
|
||||
| `dontSkipLoopVoiceAndSe` | Loop voice/se skipping flag |
|
||||
| `IsPlayingVoice` | Voice playback status |
|
||||
|
||||
---
|
||||
|
||||
## 4. FMOD EVENT TRIGGERING
|
||||
|
||||
### FMODSoundManager / FMODUtility
|
||||
**File:** `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODSoundManager.cs`
|
||||
**File:** `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODUtility.cs`
|
||||
|
||||
| Method | Description | Hook Type |
|
||||
|--------|-------------|-----------|
|
||||
| `FMODUtilityStartEvent` | Start FMOD event | Prefix to intercept event path |
|
||||
| `FMODUtilityStopEvent` | Stop FMOD event | Prefix to prevent stop |
|
||||
| `FMODUtilitySetParameter` | Set FMOD parameter | Prefix to modify parameter value |
|
||||
| `GetEventReference()` | Get FMOD event reference | Postfix to modify event ref |
|
||||
|
||||
### FMOD Event Classes:
|
||||
|
||||
| Class | File Path |
|
||||
|-------|-----------|
|
||||
| `FmodSFXEventRef` | `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventRef.cs` |
|
||||
| `FmodSFXEventSO` | `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventSO.cs` |
|
||||
| `FmodSFXEventDispatcher` | `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventDispatcher.cs` |
|
||||
| `FmodSFXEventCommunicator` | `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventCommunicator.cs` |
|
||||
|
||||
### MMFeedbacks Integration (More Mountains):
|
||||
|
||||
| Class | Path |
|
||||
|-------|------|
|
||||
| `MMF_FMOD_GlobalPlayEvent` | `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\MMFeedbacks\` |
|
||||
| `MMF_FMOD_GlobalSetParameter` | Trigger global FMOD parameter |
|
||||
| `MMF_FMOD_ParameterTrigger` | Trigger with parameter |
|
||||
| `MMF_FMODUtilityPlayEventEmitter` | Play via event emitter |
|
||||
| `MMF_FMODUtilityStopEventEmitter` | Stop via event emitter |
|
||||
| `MMF_FMOD_MainBGM_ReplayCurrent` | Replay current BGM |
|
||||
|
||||
### FMOD Parameter Methods:
|
||||
```
|
||||
SetParameterByName() - Set parameter by string name
|
||||
SetParameterFloat() - Set float parameter
|
||||
SetParameterInt() - Set int parameter
|
||||
SetParameterBoolean() - Set bool parameter
|
||||
CheckSetParameter() - Validate parameter
|
||||
CheckSetParameterSub() - Sub-check logic
|
||||
```
|
||||
|
||||
### Scene Transition Controller:
|
||||
**File:** `C:\Assets\4.Developer\Andy\FMODBooster\FMODSceneTransitionController.cs`
|
||||
|
||||
| Coroutine | Description |
|
||||
|-----------|-------------|
|
||||
| `<InitVolumeTransition>d__6` | Initialize volume transitions on scene load |
|
||||
| `<StartGPFadeEventCheck>d__20` | Check/start fade events |
|
||||
| `<StopAfterDelayRoutine>d__12` | Stop after delay |
|
||||
|
||||
---
|
||||
|
||||
## 5. ENEMY AUDIO SYSTEM
|
||||
|
||||
### EnemyAttackBase
|
||||
**File:** `C:\Assets\1.HonoHime\Core\Character\Core\EnemyAttackBase.cs`
|
||||
|
||||
| State/Action | Description |
|
||||
|--------------|-------------|
|
||||
| `Attack` | Attack state/action |
|
||||
| `Idle` | Idle state/action |
|
||||
| `AlertAction_LookAtTarget` | Alert when noticing player |
|
||||
| `AlertAction_MoveToTargetLastPosition` | Move to last known player position |
|
||||
|
||||
### EnemyActionManager
|
||||
**File:** `C:\Assets\1.HonoHime\Core\Character\EnemyActionManager\EnemyActionManager.cs`
|
||||
**File:** `C:\Assets\1.HonoHime\Core\Character\EnemyActionManager\EnemyAction.cs`
|
||||
|
||||
| Method/Property | Description |
|
||||
|----------------|-------------|
|
||||
| `GetEnemyActionManager` | Access to enemy action system |
|
||||
| `GetEnemyAttack` | Get enemy attack controller |
|
||||
| `enemyActionManager` | Reference field |
|
||||
|
||||
### Enemy Attack Controllers:
|
||||
```
|
||||
EnemyAttackAction - Basic attack action
|
||||
EnemyAttackBase - Base attack behavior
|
||||
EnemyAttackBlockedAction - When attack is blocked
|
||||
EnemyAttackAcion_UseDamageSet - Attack using damage set
|
||||
EnemyAssassinGroupManager - Group attack coordination
|
||||
```
|
||||
|
||||
### Enemy Audio Handlers:
|
||||
|
||||
| Class | File | Description |
|
||||
|-------|------|-------------|
|
||||
| `EnemyDamageVoiceSFXHandler` | `C:\Assets\4.Developer\Andy\EnemyDamageSFXHandler\EnemyDamageVoiceSFXHandler.cs` | Handles damage voice SFX |
|
||||
| `EnemyDamageVoiceSFXHandler|FeedbackVoice` | Feedback voice field |
|
||||
| `AudioComponent` | `C:\Assets\1.HonoHime\Core\Character\DamageSystem\Component\AudioComponent.cs` | Damage system audio |
|
||||
|
||||
### Enemy Attack States Found:
|
||||
```
|
||||
SukebanShibaTwin|JumpAttackState
|
||||
BoneGirl_GroundAttack|FinishState
|
||||
BoneGirl_GrabAttack|BoneGirl_GrabAttackState
|
||||
SukenBenSquadA|LeftRightDashAttackState
|
||||
AttackAction_TransitionSpecialArt
|
||||
AttackAction_Charge
|
||||
AttackAction_QuakingStrike
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. CFVOICE SYSTEM
|
||||
|
||||
### CFVoiceEventUtility
|
||||
**File:** `C:\Assets\1.HonoHime\Core\...` (pattern found in strings)
|
||||
|
||||
The CFVoice system triggers voice events based on game events:
|
||||
|
||||
| Event Key | Description |
|
||||
|-----------|-------------|
|
||||
| `CFVoiceEventUtility|Battle` | Battle events |
|
||||
| `CFVoiceEventUtility|Challenge` | Challenge mode |
|
||||
| `CFVoiceEventUtility|Chip` | Chip collection |
|
||||
| `CFVoiceEventUtility|Enemy` | **Enemy-related** - important for enemy indicators |
|
||||
| `CFVoiceEventUtility|EventKey` | Generic event key |
|
||||
| `CFVoiceEventUtility|HH_Behaviour` | Character behavior events |
|
||||
| `CFVoiceEventUtility|HH_Skill` | Skill usage |
|
||||
| `CFVoiceEventUtility|Mission` | Mission events |
|
||||
| `CFVoiceEventUtility|Note` | Note collection |
|
||||
| `CFVoiceEventUtility|QTE` | QTE events |
|
||||
| `CFVoiceEventUtility|Rank` | Rank up |
|
||||
| `CFVoiceEventUtility|Reward` | Rewards |
|
||||
| `CFVoiceEventUtility|Senbei` | Item-related |
|
||||
| `CFVoiceEventUtility|Timer` | Timer events |
|
||||
| `CFVoiceEventUtility|Trap` | Trap activation |
|
||||
| `CFVoiceEventUtility|TreasureChest` | Chest opening |
|
||||
|
||||
### CFVoice Handlers:
|
||||
|
||||
| Class | File |
|
||||
|-------|------|
|
||||
| `MissionCFVoiceHandler` | `C:\Assets\1.HonoHime\Core\Level\MissionController\MissionCFVoiceHandler.cs` |
|
||||
| `InventoryMonitorCFVoiceHandler` | `C:\Assets\4.Developer\Andy\FmodBGMController\Scripts\InventoryMonitorCFVoiceHandler.cs` |
|
||||
| `CommonFunctionVoiceHandler` | `C:\Assets\4.Developer\Andy\FmodFunctionVoice\CommonFunctionVoiceHandler.cs` |
|
||||
|
||||
### CFVoice Properties Found:
|
||||
```
|
||||
get_EVENT_CFVOICE_BATTLE
|
||||
get_EVENT_CFVOICE_ENEMY
|
||||
get_EVENT_CFVOICE_MISSION
|
||||
get_EVENT_CFVOICE_HPCHIP
|
||||
get_EVENT_CFVOICE_QTE_SHIKIGAMI
|
||||
get_EVENT_CFVOICE_QTE_FLURRYBARRIER
|
||||
PlayCFVoice
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## KEY HOOK POINTS FOR HARMONY PATCHES
|
||||
|
||||
### Audio Glitch Fixes:
|
||||
|
||||
1. **Voice Limit Fix** - Patch `UtageFmodVoiceManager.get_CountVoice` to enforce MAX_VOICES limit
|
||||
2. **Ducking Glitch** - Patch `UpdateDucking` coroutine to fix fade timing issues
|
||||
3. **Voice Cutoff Fix** - Patch `StopVoiceEvent` to properly handle crossfade before stopping
|
||||
|
||||
### Enemy Indicator Improvements:
|
||||
|
||||
1. **Alert Sound** - Hook into `CFVoiceEventUtility` with `Enemy` event key
|
||||
2. **Attack Sound** - Patch `EnemyAttackBase.OnEnter` / `OnAttack` methods
|
||||
3. **Idle Sound Loop** - Patch `EnemyAttackBase.Idle` state entry
|
||||
|
||||
### Priority Hook Targets:
|
||||
|
||||
| Priority | Class | Method | Purpose |
|
||||
|----------|-------|--------|---------|
|
||||
| HIGH | `UtageFmodVoiceManager` | `UpdateDucking` | Fix ducking glitches |
|
||||
| HIGH | `UtageFmodVoiceManager` | `PlayVoice` | Voice limit enforcement |
|
||||
| HIGH | `CFVoiceEventUtility` | `PlayCFVoice` | Enemy voice routing |
|
||||
| MEDIUM | `FMODSoundManager` | `SetVolume` | Volume clamping |
|
||||
| MEDIUM | `FMODSceneTransitionController` | `InitVolumeTransition` | Scene transition audio |
|
||||
| MEDIUM | `EnemyDamageVoiceSFXHandler` | `FeedbackVoice` | Damage audio |
|
||||
| LOW | `SnapshotManager` | `ApplySnapshot` | Reverb/snapshot control |
|
||||
|
||||
---
|
||||
|
||||
## SOURCE FILE REFERENCE LIST
|
||||
|
||||
### Voice Management:
|
||||
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\UtageFmodVoiceManager.cs`
|
||||
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\UtageFmodVoiceSetting.cs`
|
||||
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\VoiceEventSelector.cs`
|
||||
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\FmodVoiceStopper.cs`
|
||||
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\SnapshotManager.cs`
|
||||
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\SnapshotHandle.cs`
|
||||
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\DEAudioFadeManager.cs`
|
||||
- `C:\Assets\4.Developer\Andy\UtageVoiceManager\FmodSoundDebugger.cs`
|
||||
|
||||
### FMOD Core:
|
||||
- `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODSoundManager.cs`
|
||||
- `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODUtility.cs`
|
||||
- `C:\Assets\1.HonoHime\Core\FMODUtility\Scripts\FMODManager.cs`
|
||||
|
||||
### Enemy Audio:
|
||||
- `C:\Assets\1.HonoHime\Core\Character\Core\EnemyAttackBase.cs`
|
||||
- `C:\Assets\1.HonoHime\Core\Character\EnemyActionManager\EnemyActionManager.cs`
|
||||
- `C:\Assets\4.Developer\Andy\EnemyDamageSFXHandler\EnemyDamageVoiceSFXHandler.cs`
|
||||
- `C:\Assets\1.HonoHime\Core\Character\DamageSystem\Component\AudioComponent.cs`
|
||||
|
||||
### CFVoice System:
|
||||
- (Source path partially truncated, but class is `CFVoiceEventUtility`)
|
||||
- `C:\Assets\1.HonoHime\Core\Level\MissionController\MissionCFVoiceHandler.cs`
|
||||
- `C:\Assets\4.Developer\Andy\FmodFunctionVoice\CommonFunctionVoiceHandler.cs`
|
||||
|
||||
### Scene/BGM:
|
||||
- `C:\Assets\4.Developer\Andy\FMODBooster\FMODSceneTransitionController.cs`
|
||||
- `C:\Assets\4.Developer\Andy\FmodBGMController\Scripts\MainBGMManager.cs`
|
||||
- `C:\Assets\4.Developer\Andy\FmodBGMController\Scripts\FmodBGMController.cs`
|
||||
|
||||
### SFX Events:
|
||||
- `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventRef.cs`
|
||||
- `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventSO.cs`
|
||||
- `C:\Assets\4.Developer\HsiCheng\Script\Fmod\FmodSFXEventDispatcher.cs`
|
||||
90
docs/EXISTING_TOOLS.md
Normal file
90
docs/EXISTING_TOOLS.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Existing Modding Tools for HomuraHime
|
||||
|
||||
## Recommended: CinematicUnityExplorer (295 stars)
|
||||
|
||||
**Purpose:** In-game UI for exploring, debugging, and modifying Unity games with cinematic tools
|
||||
|
||||
**Download:**
|
||||
```
|
||||
https://github.com/originalnicodr/CinematicUnityExplorer/releases/latest/download/CinematicUnityExplorer.BepInEx5.Mono.zip
|
||||
```
|
||||
|
||||
**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:**
|
||||
```
|
||||
https://github.com/yukieiji/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Why Keep Our Custom AudioMixerGUI?
|
||||
|
||||
Our **AudioMixerGUI** provides audio-specific sliders and quick testing:
|
||||
|
||||
| Feature | CinematicUnityExplorer | Our AudioMixerGUI |
|
||||
|---------|----------------------|-------------------|
|
||||
| Audio-specific sliders | No | Yes |
|
||||
| One-click enemy cue testing | Via C# Console | Yes |
|
||||
| Voice count monitoring | Manual via Inspector | Yes |
|
||||
| Config persistence | No | Yes (via BepInEx config) |
|
||||
|
||||
**Recommendation:** Use **BOTH**
|
||||
- CinematicUnityExplorer (F5) for debugging, object browsing, screenshots
|
||||
- Our AudioMixerGUI (F1) for audio parameter tweaking
|
||||
|
||||
---
|
||||
|
||||
## Quick Start with CinematicUnityExplorer
|
||||
|
||||
### 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
|
||||
|
||||
### Creating Runtime Patches
|
||||
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
|
||||
|
||||
### Testing Audio Triggers via C# Console
|
||||
```
|
||||
// Example - trigger enemy alert sound
|
||||
CFVoiceEventUtility.PlayCFVoice("Enemy_Alert");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Default Hotkeys
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| **F5** | Toggle CinematicUnityExplorer |
|
||||
| **F1** | Toggle our AudioMixerGUI |
|
||||
|
||||
For CinematicUnityExplorer freecam controls, see their documentation.
|
||||
264
docs/FMOD_MODIFICATION_GUIDE_PHASE3.md
Normal file
264
docs/FMOD_MODIFICATION_GUIDE_PHASE3.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# Phase 3: FMOD Bank Modifications Guide
|
||||
|
||||
## Overview
|
||||
This phase covers extracting and modifying FMOD audio banks using FMOD Studio and AssetRipper.
|
||||
|
||||
**Prerequisites:**
|
||||
- FMOD Studio 2.02.x installed (free from fmod.com)
|
||||
- AssetRipper installed
|
||||
- Game installed at: `C:\apps\steam\steamapps\common\Homura Hime\`
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Extract FMOD Banks with AssetRipper
|
||||
|
||||
### 1.1 Launch AssetRipper
|
||||
```
|
||||
Location: C:\projects\HomuraHime-Mods\tools\AssetRipper\AssetRipper.exe
|
||||
```
|
||||
|
||||
### 1.2 Load HomuraHime
|
||||
1. Click **File** → **Load** (or drag folder)
|
||||
2. Navigate to: `C:\apps\steam\steamapps\common\Homura Hime\HomuraHime_Data\`
|
||||
3. Select the folder and click **Open**
|
||||
|
||||
### 1.3 Export Settings
|
||||
1. Click **File** → **Export**
|
||||
2. Choose **Resources** (not Asset Directories)
|
||||
3. Set output path: `C:\projects\HomuraHime-Mods\extracted_assets\`
|
||||
|
||||
### 1.4 Locate FMOD Banks
|
||||
After export, find banks at:
|
||||
```
|
||||
C:\projects\HomuraHime-Mods\extracted_assets\StreamingAssets\FMOD\Desktop\
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Open Banks in FMOD Studio
|
||||
|
||||
### 2.1 Launch FMOD Studio
|
||||
```
|
||||
Location: C:\Program Files\FMOD Studio\FMOD Studio.exe
|
||||
```
|
||||
|
||||
### 2.2 Open Project (or Banks Directly)
|
||||
**Option A:** If you have the original FMOD project (unlikely for commercial game):
|
||||
- File → Open Project → Select `.fprojekt` file
|
||||
|
||||
**Option B:** Open bank files directly:
|
||||
- File → Open Bank → Select `Master.bank` from the extracted folder
|
||||
- FMOD will automatically load related banks
|
||||
|
||||
### 2.3 Bank Organization
|
||||
In FMOD Studio, you'll see:
|
||||
- **Events**: Sound events (grouped by banks: SFX, Ambience, BGM, Voice)
|
||||
- **Audio Tables**: Banks, VCA groups, snapshots
|
||||
- **Mixer**: Master output with routing
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Voice Limit Improvements
|
||||
|
||||
### 3.1 Find Problematic Events
|
||||
Search for events with high polyphony:
|
||||
1. In FMOD Studio, select **SFX.bank** in the Banks panel
|
||||
2. Look at event properties in the Inspector
|
||||
3. Check **Maximum Instances** ( polyphony)
|
||||
|
||||
### 3.2 Adjust Voice Aging
|
||||
For events that cause glitches:
|
||||
|
||||
1. Right-click event → **Properties**
|
||||
2. Go to **Voices** tab
|
||||
3. Settings to adjust:
|
||||
```
|
||||
Max Instances: 16 (from default 32)
|
||||
Voice Stealing: Steal Oldest
|
||||
Voice Aging: 1.0 seconds (faster cleanup)
|
||||
```
|
||||
|
||||
### 3.3 Key Events to Check
|
||||
In `SFX.bank`:
|
||||
- Attack impact sounds (often triggered rapidly)
|
||||
- Footstep/landing sounds (PreLoad banks)
|
||||
- Enemy alert/warning sounds
|
||||
- UI feedback sounds
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Audio Ducking Improvements
|
||||
|
||||
### 4.1 Access Mixer
|
||||
1. Click **Mixer** tab in FMOD Studio
|
||||
2. You'll see VCA groups and the Master bus
|
||||
|
||||
### 4.2 Configure Ducking Groups
|
||||
Look for:
|
||||
- **Speech** (Voice channel)
|
||||
- **SFX** (Sound effects)
|
||||
- **Music** (BGM)
|
||||
|
||||
### 4.3 Adjust Ducking Parameters
|
||||
For the Master or speech bus:
|
||||
1. Right-click → **Add Effect** → **Duck**
|
||||
2. Configure:
|
||||
```
|
||||
Threshold: -20 dB (triggers at this volume)
|
||||
Rate: 100% (fade speed)
|
||||
Attack: 10 ms (fast attack)
|
||||
Release: 200 ms (slower release)
|
||||
```
|
||||
|
||||
### 4.4 Create/Modify Snapshots
|
||||
Snapshots control ducking behavior:
|
||||
1. In the **Audio Tables** panel, select **Snapshots**
|
||||
2. Find existing battle/start snapshots
|
||||
3. Modify duck amounts:
|
||||
```
|
||||
Music Duck Amount: -12 dB (not too aggressive)
|
||||
SFX Duck Amount: -6 dB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Enemy Behavior Indicator Improvements
|
||||
|
||||
### 5.1 Locate Enemy Audio Events
|
||||
|
||||
**In `Ambience.bank`:**
|
||||
- `enemy_idle_loop` - Enemy ambient when not engaged
|
||||
- `enemy_alert` - Enemy noticing player
|
||||
- `enemy_chase` - Enemy pursuing
|
||||
|
||||
**In `SFX.bank`:**
|
||||
- Attack indicator sounds
|
||||
- Danger/warning cues
|
||||
|
||||
### 5.2 Increase Volume on Behavior Sounds
|
||||
|
||||
1. Find enemy-related events
|
||||
2. Select the event → Inspector
|
||||
3. **Volume** slider: Increase by 3-6 dB
|
||||
```
|
||||
Before: 0.0 dB
|
||||
After: +3.0 dB to +6.0 dB
|
||||
```
|
||||
|
||||
### 5.3 Add Pitch Variation
|
||||
|
||||
For more distinct enemy states:
|
||||
|
||||
1. Select enemy event (e.g., alert)
|
||||
2. Add **Pitch** automation or randomizer
|
||||
3. Set variation:
|
||||
```
|
||||
Center: 0 semitones
|
||||
Range: ±2 semitones random
|
||||
```
|
||||
|
||||
### 5.4 Add 3D Positioning
|
||||
|
||||
For directional audio:
|
||||
|
||||
1. Select event → **3D** checkbox
|
||||
2. Configure:
|
||||
```
|
||||
Min Distance: 1.0 m
|
||||
Max Distance: 20.0 m
|
||||
Rolloff: Linear
|
||||
```
|
||||
3. This makes enemy sounds come from their position
|
||||
|
||||
### 5.5 Create State Layers
|
||||
|
||||
For distinct enemy behavior indicators, add parameters:
|
||||
|
||||
1. Right-click event → **Add Parameter**
|
||||
2. Name: `EnemyState`
|
||||
3. Values: `0=Idle, 1=Alert, 2=Attacking`
|
||||
4. Add automation for volume/effect per state
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Reimport Modified Banks
|
||||
|
||||
### 6.1 Save Changes
|
||||
1. In FMOD Studio: **File** → **Save**
|
||||
2. Banks will auto-update in the project folder
|
||||
|
||||
### 6.2 Export Banks
|
||||
1. **File** → **Export** → **banks**
|
||||
2. Choose the extracted `Desktop\` folder
|
||||
3. Confirm overwrite
|
||||
|
||||
### 6.3 Verify Export
|
||||
Check that modified `.bank` files have new timestamps.
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Backup Original Banks
|
||||
|
||||
**CRITICAL - Before testing:**
|
||||
```
|
||||
mkdir "C:\projects\HomuraHime-Mods\backup_banks_original"
|
||||
copy "C:\projects\HomuraHime-Mods\extracted_assets\StreamingAssets\FMOD\Desktop\*.bank"
|
||||
"C:\projects\HomuraHime-Mods\backup_banks_original\"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Locations Reference
|
||||
|
||||
### Game Banks (Read-Only Backup)
|
||||
```
|
||||
C:\apps\steam\steamapps\common\Homura Hime\HomuraHime_Data\StreamingAssets\FMOD\Desktop\
|
||||
```
|
||||
|
||||
### Extracted for Editing
|
||||
```
|
||||
C:\projects\HomuraHime-Mods\extracted_assets\StreamingAssets\FMOD\Desktop\
|
||||
```
|
||||
|
||||
### Modified Banks (Copy to Game)
|
||||
```
|
||||
C:\projects\HomuraHime-Mods\modified_banks\FMOD\Desktop\
|
||||
```
|
||||
|
||||
### Backup Original
|
||||
```
|
||||
C:\projects\HomuraHime-Mods\backup_banks_original\
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Banks to Prioritize
|
||||
|
||||
| Priority | Bank | Reason |
|
||||
|----------|------|--------|
|
||||
| HIGH | `SFX.bank` | Contains attack/impact sounds that glitch |
|
||||
| HIGH | `Ambience.bank` | Enemy behavior indicators |
|
||||
| MEDIUM | `Master.bank` | Contains mixer and routing |
|
||||
| MEDIUM | `Voice.bank` | May need voice limit adjustments |
|
||||
| LOW | `BGM*.bank` | Background music - less critical |
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
After modifications:
|
||||
- [ ] Banks export successfully from FMOD Studio
|
||||
- [ ] New bank files are in `modified_banks\`
|
||||
- [ ] Original banks backed up
|
||||
- [ ] Test game loads without FMOD errors
|
||||
- [ ] Audio glitches reduced in combat
|
||||
- [ ] Enemy indicators more audible
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
After completing FMOD modifications:
|
||||
- Phase 4: BepInEx plugin deployment
|
||||
- Phase 5: Testing and iteration
|
||||
- Phase 6: Distribution
|
||||
297
docs/TESTING_PHASE5.md
Normal file
297
docs/TESTING_PHASE5.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# Phase 5: Testing & Iteration Guide
|
||||
|
||||
## Overview
|
||||
This phase documents the testing protocol for validating audio mod fixes and improvements.
|
||||
|
||||
**Note:** Actual testing must be performed by playing the game. This document provides the testing workflow and evaluation criteria.
|
||||
|
||||
---
|
||||
|
||||
## Pre-Testing Checklist
|
||||
|
||||
### 1. BepInEx Installation
|
||||
- [ ] BepInEx installed in game directory
|
||||
- [ ] `winhttp.dll` present in game root
|
||||
- [ ] `BepInEx\LogOutput.log` generated after first run
|
||||
|
||||
### 2. Plugin Installation
|
||||
- [ ] `HomuraHimeAudioMod.dll` in `BepInEx\plugins\`
|
||||
- [ ] Config file generated at `BepInEx\config\com.homurahime.audiomod.cfg`
|
||||
|
||||
### 3. Original Bank Backup
|
||||
- [ ] Original banks backed up to `backup_banks_original\`
|
||||
|
||||
### 4. FMOD Banks (Optional)
|
||||
- [ ] Modified banks copied to game directory (if using Phase 3 FMOD changes)
|
||||
|
||||
---
|
||||
|
||||
## Testing Scenarios
|
||||
|
||||
### Test 1: Quiet Exploration (Baseline)
|
||||
|
||||
**Purpose:** Verify mod doesn't break normal audio
|
||||
|
||||
**Steps:**
|
||||
1. Launch game with mod enabled
|
||||
2. Start new game or load save
|
||||
3. Move through quiet area (hub, menu, cutscenes)
|
||||
4. Listen for 5+ minutes
|
||||
|
||||
**Success Criteria:**
|
||||
- [ ] No audio distortion or pops
|
||||
- [ ] BGM plays smoothly without cutting out
|
||||
- [ ] UI sounds (menu navigation) work correctly
|
||||
- [ ] No crashes or errors in LogOutput.log
|
||||
|
||||
**Log Check:**
|
||||
```
|
||||
Filter: Look for "HomuraHimeAudio" entries
|
||||
Expected: "initialized successfully"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Test 2: Combat with 3+ Enemies
|
||||
|
||||
**Purpose:** Test voice limit fixes under moderate load
|
||||
|
||||
**Steps:**
|
||||
1. Find an area with 3+ enemies (early game stages)
|
||||
2. Engage in combat
|
||||
3. Observe audio behavior during:
|
||||
- Multiple attack impacts
|
||||
- Enemy death sounds
|
||||
- Player attack sounds
|
||||
- Dodge/roll sounds
|
||||
|
||||
**Success Criteria:**
|
||||
- [ ] No audio distortion during impact sounds
|
||||
- [ ] Sounds don't cut out prematurely
|
||||
- [ ] Enemy voices don't override each other harshly
|
||||
- [ ] Can distinguish individual sound sources
|
||||
|
||||
**Failure Signs:**
|
||||
- Crackling/popping sounds
|
||||
- Sudden audio cutoff
|
||||
- "Muffled" audio
|
||||
- Missing sound effects
|
||||
|
||||
---
|
||||
|
||||
### Test 3: Boss Fights (Heavy Load)
|
||||
|
||||
**Purpose:** Stress test audio system
|
||||
|
||||
**Steps:**
|
||||
1. Enter boss arena
|
||||
2. Engage boss
|
||||
3. Observe during:
|
||||
- Boss special attacks
|
||||
- Phase transitions
|
||||
- Multiple minions spawning
|
||||
- Intense combo sequences
|
||||
|
||||
**Success Criteria:**
|
||||
- [ ] No audio glitches during phase transitions
|
||||
- [ ] Music continues playing smoothly
|
||||
- [ ] Voice lines play without cutoff
|
||||
- [ ] Attack sounds remain distinguishable
|
||||
|
||||
**Challenge:** If glitches occur, reduce `MaxVoiceCount` in config
|
||||
|
||||
---
|
||||
|
||||
### Test 4: Swarm Enemy Encounters
|
||||
|
||||
**Purpose:** Test rapid sound triggering
|
||||
|
||||
**Steps:**
|
||||
1. Find area with many weak enemies (swarms)
|
||||
2. Use area-of-effect attacks
|
||||
3. Observe:
|
||||
- Rapid footstep sounds
|
||||
- Multiple death sounds
|
||||
- Attack impact sounds overlapping
|
||||
|
||||
**Success Criteria:**
|
||||
- [ ] No stuttering or repeating sounds
|
||||
- [ ] Audio doesn't "stick" or loop
|
||||
- [ ] Clean sound transitions
|
||||
|
||||
---
|
||||
|
||||
### Test 5: Enemy Behavior State Changes
|
||||
|
||||
**Purpose:** Validate enemy audio indicator improvements
|
||||
|
||||
**Steps:**
|
||||
1. Approach enemies and observe state changes
|
||||
2. Listen for audio cues during:
|
||||
- Enemy idle (subtle ambient)
|
||||
- Enemy alert (noticing player)
|
||||
- Enemy chase (pursuing)
|
||||
- Enemy attack (warning + strike)
|
||||
|
||||
**Success Criteria:**
|
||||
| State | Expected Audio |
|
||||
|-------|----------------|
|
||||
| Idle | Subtle, low-volume ambient |
|
||||
| Alert | Distinct warning/notice sound |
|
||||
| Chase | Intensified pursuit audio |
|
||||
| Attack | Clear attack indicator + impact |
|
||||
|
||||
**Rating Scale:**
|
||||
- 1: Cannot hear/identify
|
||||
- 2: Barely audible
|
||||
- 3: Audible but unclear
|
||||
- 4: Clear and distinguishable
|
||||
- 5: Very distinct and helpful
|
||||
|
||||
---
|
||||
|
||||
## Metrics Tracking
|
||||
|
||||
### Audio Glitch Rating
|
||||
|
||||
| Rating | Description |
|
||||
|--------|-------------|
|
||||
| 0 | No glitches ever |
|
||||
| 1 | Rare glitches (<1 per minute) |
|
||||
| 2 | Occasional glitches (1-5 per minute) |
|
||||
| 3 | Frequent glitches (>5 per minute) |
|
||||
| 4 | Constant glitches |
|
||||
|
||||
### Target: Rating 0-1 for all scenarios
|
||||
|
||||
---
|
||||
|
||||
## Configuration Tuning
|
||||
|
||||
### If Distortion/Pops Occur
|
||||
|
||||
```ini
|
||||
[VoiceManager]
|
||||
MaxVoiceCount = 64 ; Reduce from 128
|
||||
|
||||
[Ducking]
|
||||
DuckFadeTime = 0.08 ; Increase for smoother transitions
|
||||
```
|
||||
|
||||
### If Sounds Cut Out
|
||||
|
||||
```ini
|
||||
[VoiceManager]
|
||||
MaxVoiceCount = 256 ; Increase if hardware supports
|
||||
EnableVoiceLimitFix = true
|
||||
```
|
||||
|
||||
### If Enemy Cues Unclear
|
||||
|
||||
```ini
|
||||
[EnemyAudio]
|
||||
EnemyIndicatorBoost = 1.5
|
||||
AlertSoundBoost = 1.6
|
||||
AttackSoundBoost = 1.4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Iteration Loop
|
||||
|
||||
```
|
||||
Test Scenario
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ Glitch? │
|
||||
└─────────────┘
|
||||
│
|
||||
┌──┴──┐
|
||||
Yes No
|
||||
│ │
|
||||
▼ ▼
|
||||
Adjust Next
|
||||
Config Test
|
||||
│
|
||||
▼
|
||||
Rebuild & Redeploy
|
||||
│
|
||||
▼
|
||||
Retest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Log Analysis
|
||||
|
||||
### Enable Debug Logging
|
||||
|
||||
```ini
|
||||
[General]
|
||||
EnableDebugLogging = true
|
||||
```
|
||||
|
||||
### Check Log During Gameplay
|
||||
|
||||
```
|
||||
BepInEx\LogOutput.log
|
||||
```
|
||||
|
||||
### Key Log Entries
|
||||
|
||||
| Entry | Meaning |
|
||||
|-------|---------|
|
||||
| `HomuraHime Audio Mod initialized` | Plugin loaded OK |
|
||||
| `Voice count X exceeds limit Y` | Voice limiting active |
|
||||
| `DuckVolume adjusted` | Ducking patch working |
|
||||
| `Enemy voice event: X` | Enemy audio hook active |
|
||||
|
||||
---
|
||||
|
||||
## Test Report Template
|
||||
|
||||
```markdown
|
||||
## Test Report - [Date]
|
||||
|
||||
### Environment
|
||||
- Game Version: [Check in game]
|
||||
- Mod Version: 1.0.0
|
||||
- Config: [Default/Custom]
|
||||
|
||||
### Results
|
||||
|
||||
| Scenario | Glitch Rating | Notes |
|
||||
|----------|---------------|-------|
|
||||
| Quiet Exploration | [0-4] | |
|
||||
| 3+ Enemy Combat | [0-4] | |
|
||||
| Boss Fight | [0-4] | |
|
||||
| Swarm Enemies | [0-4] | |
|
||||
| Enemy State Cues | [1-5] | |
|
||||
|
||||
### Configuration Changes Made
|
||||
|
||||
```ini
|
||||
[Section]
|
||||
Setting = Value
|
||||
```
|
||||
|
||||
### Overall Assessment
|
||||
|
||||
[ ] Ready for distribution
|
||||
[ ] Needs iteration
|
||||
|
||||
### Next Steps
|
||||
|
||||
1.
|
||||
2.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next: Phase 6
|
||||
|
||||
Once testing yields satisfactory results (glitch rating 0-1, cue clarity 4-5):
|
||||
- Proceed to Phase 6: Distribution
|
||||
- Package mod for sharing
|
||||
- Write installation instructions
|
||||
89
src/HomuraHimeAudioMod/Build.ps1
Normal file
89
src/HomuraHimeAudioMod/Build.ps1
Normal file
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env pwsh
|
||||
# Build script for HomuraHimeAudioMod
|
||||
# Requires: .NET SDK 4.8 or .NET Framework 4.8
|
||||
|
||||
param(
|
||||
[string]$Configuration = "Release",
|
||||
[string]$OutputDir = $null
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$ProjectDir = $PSScriptRoot
|
||||
$ProjectFile = Join-Path $ProjectDir "HomuraHimeAudioMod.csproj"
|
||||
$DefaultOutput = Join-Path $ProjectDir "bin\$Configuration\net48"
|
||||
|
||||
if (-not (Test-Path $ProjectFile)) {
|
||||
Write-Host "[ERROR] Project file not found: $ProjectFile" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "HomuraHime Audio Mod - Build Script" -ForegroundColor Cyan
|
||||
Write-Host "===================================" -ForegroundColor Cyan
|
||||
Write-Host "Configuration: $Configuration" -ForegroundColor Gray
|
||||
Write-Host "Project: $ProjectFile" -ForegroundColor Gray
|
||||
|
||||
# Check for .NET SDK
|
||||
$dotnet = Get-Command dotnet -ErrorAction SilentlyContinue
|
||||
if (-not $dotnet) {
|
||||
Write-Host "[ERROR] .NET SDK not found. Please install .NET Framework 4.8 SDK or .NET SDK." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Clean previous build
|
||||
Write-Host "`n[STEP 1] Cleaning previous build..." -ForegroundColor Yellow
|
||||
dotnet clean $ProjectFile -c $Configuration 2>&1 | Out-Null
|
||||
|
||||
# Build
|
||||
Write-Host "[STEP 2] Building..." -ForegroundColor Yellow
|
||||
$buildOutput = dotnet build $ProjectFile -c $Configuration --nologo 2>&1
|
||||
$buildOutput | ForEach-Object { Write-Host $_ -ForegroundColor Gray }
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "`n[ERROR] Build failed!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Find output
|
||||
$outputDll = if ($OutputDir) {
|
||||
Join-Path $OutputDir "HomuraHimeAudioMod.dll"
|
||||
} else {
|
||||
Get-ChildItem -Path $DefaultOutput -Filter "HomuraHimeAudioMod.dll" -ErrorAction SilentlyContinue | Select-Object -First 1 | ForEach-Object { $_.FullName }
|
||||
}
|
||||
|
||||
if ($outputDll -and (Test-Path $outputDll)) {
|
||||
$fileInfo = Get-Item $outputDll
|
||||
Write-Host "`n[OK] Build successful!" -ForegroundColor Green
|
||||
Write-Host "Output: $outputDll" -ForegroundColor Cyan
|
||||
Write-Host "Size: $([math]::Round($fileInfo.Length / 1024, 2)) KB" -ForegroundColor Gray
|
||||
Write-Host "Built: $($fileInfo.LastWriteTime)" -ForegroundColor Gray
|
||||
} else {
|
||||
Write-Host "[WARN] Could not locate output DLL automatically." -ForegroundColor Yellow
|
||||
Write-Host "Please check: $DefaultOutput" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Copy to BepInEx plugins folder
|
||||
$gamePath = "C:\apps\steam\steamapps\common\Homura Hime"
|
||||
$pluginsPath = Join-Path $gamePath "BepInEx\plugins"
|
||||
|
||||
if (Test-Path $pluginsPath) {
|
||||
$targetPath = Join-Path $pluginsPath "HomuraHimeAudioMod.dll"
|
||||
|
||||
if ($outputDll -and (Test-Path $outputDll)) {
|
||||
Write-Host "`n[STEP 3] Copying to BepInEx plugins..." -ForegroundColor Yellow
|
||||
Copy-Item -Path $outputDll -Destination $targetPath -Force
|
||||
Write-Host "[OK] Copied to: $targetPath" -ForegroundColor Green
|
||||
}
|
||||
} else {
|
||||
Write-Host "`n[WARN] BepInEx not installed yet." -ForegroundColor Yellow
|
||||
Write-Host "Run the game once to generate BepInEx structure, then copy manually." -ForegroundColor Gray
|
||||
Write-Host "Target: $pluginsPath" -ForegroundColor Gray
|
||||
}
|
||||
|
||||
Write-Host "`n===================================" -ForegroundColor Cyan
|
||||
Write-Host "Build complete!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Next steps:" -ForegroundColor Yellow
|
||||
Write-Host " 1. Launch HomuraHime via Steam" -ForegroundColor Cyan
|
||||
Write-Host " 2. Check BepInEx\LogOutput.log for mod initialization" -ForegroundColor Cyan
|
||||
Write-Host " 3. Adjust settings in BepInEx\config\com.homurahime.audiomod.cfg" -ForegroundColor Cyan
|
||||
@@ -6,28 +6,71 @@
|
||||
<RootNamespace>HomuraHimeAudioMod</RootNamespace>
|
||||
<Version>1.0.0</Version>
|
||||
<Authors>Ed</Authors>
|
||||
<Company>HomuraHime Modding</Company>
|
||||
<Product>HomuraHime Audio Mod</Product>
|
||||
<Description>Audio improvements mod for HomuraHime - fixes glitches and improves enemy indicators</Description>
|
||||
<GameAssemblyPath>C:\apps\steam\steamapps\common\Homura Hime\HomuraHime_Data\Managed</GameAssemblyPath>
|
||||
<Copyright>Copyright 2026</Copyright>
|
||||
<GamePath>C:\apps\steam\steamapps\common\Homura Hime</GamePath>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="0Harmony" Version="2.2.2" />
|
||||
<PackageReference Include="BepInEx" Version="5.4.21" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="FMODUnity">
|
||||
<HintPath>$(GameAssemblyPath)\FMODUnity.dll</HintPath>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>$(GamePath)\BepInEx\core\0Harmony.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="BepInEx">
|
||||
<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>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.AudioModule">
|
||||
<HintPath>$(GameAssemblyPath)\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>
|
||||
</Reference>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>$(GameAssemblyPath)\Assembly-CSharp.dll</HintPath>
|
||||
<HintPath>$(GamePath)\HomuraHime_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
92
src/HomuraHimeAudioMod/ModConfig.cs
Normal file
92
src/HomuraHimeAudioMod/ModConfig.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using BepInEx;
|
||||
using BepInEx.Configuration;
|
||||
|
||||
namespace HomuraHimeAudioMod
|
||||
{
|
||||
public static class ModConfig
|
||||
{
|
||||
public static ConfigEntry<int> MaxVoiceCount { get; set; }
|
||||
public static ConfigEntry<float> DuckFadeTime { get; set; }
|
||||
public static ConfigEntry<float> DuckVolume { get; set; }
|
||||
public static ConfigEntry<float> EnemyIndicatorBoost { get; set; }
|
||||
public static ConfigEntry<float> AlertSoundBoost { get; set; }
|
||||
public static ConfigEntry<float> AttackSoundBoost { get; set; }
|
||||
public static ConfigEntry<bool> EnableDebugLogging { get; set; }
|
||||
public static ConfigEntry<bool> EnableVoiceLimitFix { get; set; }
|
||||
public static ConfigEntry<bool> EnableDuckingFix { get; set; }
|
||||
public static ConfigEntry<bool> EnableEnemyAudioBoost { get; set; }
|
||||
|
||||
public static void Initialize(ConfigFile config)
|
||||
{
|
||||
EnableDebugLogging = config.Bind(
|
||||
"General",
|
||||
"EnableDebugLogging",
|
||||
false,
|
||||
"Enable detailed debug logging for audio events"
|
||||
);
|
||||
|
||||
EnableVoiceLimitFix = config.Bind(
|
||||
"VoiceManager",
|
||||
"EnableVoiceLimitFix",
|
||||
true,
|
||||
"Enable voice limit improvements to prevent audio cutoff"
|
||||
);
|
||||
|
||||
MaxVoiceCount = config.Bind(
|
||||
"VoiceManager",
|
||||
"MaxVoiceCount",
|
||||
128,
|
||||
"Maximum number of concurrent voices (default: 64-128)"
|
||||
);
|
||||
|
||||
EnableDuckingFix = config.Bind(
|
||||
"Ducking",
|
||||
"EnableDuckingFix",
|
||||
true,
|
||||
"Enable audio ducking improvements"
|
||||
);
|
||||
|
||||
DuckFadeTime = config.Bind(
|
||||
"Ducking",
|
||||
"DuckFadeTime",
|
||||
0.05f,
|
||||
"Fade time for audio ducking (seconds)"
|
||||
);
|
||||
|
||||
DuckVolume = config.Bind(
|
||||
"Ducking",
|
||||
"DuckVolume",
|
||||
0.3f,
|
||||
"Volume level during ducking (0.0-1.0)"
|
||||
);
|
||||
|
||||
EnableEnemyAudioBoost = config.Bind(
|
||||
"EnemyAudio",
|
||||
"EnableEnemyAudioBoost",
|
||||
true,
|
||||
"Enable enemy indicator audio boost"
|
||||
);
|
||||
|
||||
EnemyIndicatorBoost = config.Bind(
|
||||
"EnemyAudio",
|
||||
"EnemyIndicatorBoost",
|
||||
1.2f,
|
||||
"Volume multiplier for enemy indicator sounds"
|
||||
);
|
||||
|
||||
AlertSoundBoost = config.Bind(
|
||||
"EnemyAudio",
|
||||
"AlertSoundBoost",
|
||||
1.3f,
|
||||
"Volume multiplier for enemy alert sounds"
|
||||
);
|
||||
|
||||
AttackSoundBoost = config.Bind(
|
||||
"EnemyAudio",
|
||||
"AttackSoundBoost",
|
||||
1.15f,
|
||||
"Volume multiplier for enemy attack sounds"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,194 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HomuraHimeAudioMod.Patches
|
||||
{
|
||||
/// <summary>
|
||||
/// Patches for audio ducking system improvements.
|
||||
/// TO BE IMPLEMENTED AFTER PHASE 2 CODE ANALYSIS
|
||||
/// </summary>
|
||||
public class AudioDuckingPatch
|
||||
{
|
||||
public static void Apply(ref Harmony harmony)
|
||||
{
|
||||
// TODO: After Phase 2, implement patches for:
|
||||
// - Improve ducking fade times
|
||||
// - Adjust duck amounts for combat
|
||||
// - Add custom ducking rules
|
||||
if (!ModConfig.EnableDuckingFix.Value)
|
||||
{
|
||||
Plugin.Log.LogInfo("AudioDucking patches disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin.Log.LogInfo("Applying AudioDucking patches...");
|
||||
|
||||
var snapshotManagerType = FindSnapshotManagerType();
|
||||
if (snapshotManagerType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found SnapshotManager type: {snapshotManagerType.FullName}");
|
||||
PatchSnapshotApply(harmony, snapshotManagerType);
|
||||
}
|
||||
|
||||
var fadeManagerType = FindFadeManagerType();
|
||||
if (fadeManagerType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found DEAudioFadeManager type: {fadeManagerType.FullName}");
|
||||
PatchFadeManager(harmony, fadeManagerType);
|
||||
}
|
||||
|
||||
Plugin.Log.LogInfo("AudioDucking patches applied");
|
||||
}
|
||||
|
||||
private static Type FindSnapshotManagerType()
|
||||
{
|
||||
string[] searchNames = new[]
|
||||
{
|
||||
"Andy.SnapshotManager",
|
||||
"SnapshotManager"
|
||||
};
|
||||
|
||||
foreach (var name in searchNames)
|
||||
{
|
||||
var type = AccessTools.TypeByName(name);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
if (type.Name == "SnapshotManager")
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Type FindFadeManagerType()
|
||||
{
|
||||
string[] searchNames = new[]
|
||||
{
|
||||
"Andy.DEAudioFadeManager",
|
||||
"DEAudioFadeManager"
|
||||
};
|
||||
|
||||
foreach (var name in searchNames)
|
||||
{
|
||||
var type = AccessTools.TypeByName(name);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
if (type.Name == "DEAudioFadeManager")
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void PatchSnapshotApply(Harmony harmony, Type targetType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var applySnapshotMethod = AccessTools.Method(targetType, "ApplySnapshot");
|
||||
if (applySnapshotMethod != null)
|
||||
{
|
||||
harmony.Patch(
|
||||
applySnapshotMethod,
|
||||
prefix: new HarmonyMethod(typeof(AudioDuckingPatch), nameof(ApplySnapshotPrefix))
|
||||
);
|
||||
Plugin.Log.LogInfo("Patched ApplySnapshot");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Plugin.Log.LogError($"Error patching ApplySnapshot: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PatchFadeManager(Harmony harmony, Type targetType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fadeInMethod = AccessTools.Method(targetType, "FadeIn");
|
||||
var fadeOutMethod = AccessTools.Method(targetType, "FadeOut");
|
||||
|
||||
if (fadeInMethod != null)
|
||||
{
|
||||
harmony.Patch(
|
||||
fadeInMethod,
|
||||
prefix: new HarmonyMethod(typeof(AudioDuckingPatch), nameof(FadeInPrefix))
|
||||
);
|
||||
Plugin.Log.LogInfo("Patched FadeIn");
|
||||
}
|
||||
|
||||
if (fadeOutMethod != null)
|
||||
{
|
||||
harmony.Patch(
|
||||
fadeOutMethod,
|
||||
prefix: new HarmonyMethod(typeof(AudioDuckingPatch), nameof(FadeOutPrefix))
|
||||
);
|
||||
Plugin.Log.LogInfo("Patched FadeOut");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Plugin.Log.LogError($"Error patching FadeManager: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ApplySnapshotPrefix(ref string snapshotName, ref float fadeTime)
|
||||
{
|
||||
float configuredFadeTime = ModConfig.DuckFadeTime.Value;
|
||||
|
||||
if (fadeTime < configuredFadeTime)
|
||||
{
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"Adjusting fade time from {fadeTime} to {configuredFadeTime}");
|
||||
}
|
||||
fadeTime = configuredFadeTime;
|
||||
}
|
||||
|
||||
if (ModConfig.EnableDebugLogging.Value && !string.IsNullOrEmpty(snapshotName))
|
||||
{
|
||||
if (snapshotName.Contains("Battle") || snapshotName.Contains("Combat"))
|
||||
{
|
||||
Plugin.Log.LogDebug($"Battle/Combat snapshot: {snapshotName}");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool FadeInPrefix(ref float fadeTime)
|
||||
{
|
||||
const float minFadeIn = 0.01f;
|
||||
if (fadeTime < minFadeIn)
|
||||
{
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"FadeIn adjusted: {fadeTime} -> {minFadeIn}");
|
||||
}
|
||||
fadeTime = minFadeIn;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool FadeOutPrefix(ref float fadeTime)
|
||||
{
|
||||
const float minFadeOut = 0.05f;
|
||||
if (fadeTime < minFadeOut)
|
||||
{
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"FadeOut adjusted: {fadeTime} -> {minFadeOut}");
|
||||
}
|
||||
fadeTime = minFadeOut;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,243 @@
|
||||
using System;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace HomuraHimeAudioMod.Patches
|
||||
{
|
||||
/// <summary>
|
||||
/// Patches for enemy behavior audio indicator improvements.
|
||||
/// TO BE IMPLEMENTED AFTER PHASE 2 CODE ANALYSIS
|
||||
/// </summary>
|
||||
public class EnemyAudioPatch
|
||||
{
|
||||
public static void Apply(ref Harmony harmony)
|
||||
{
|
||||
// TODO: After Phase 2, implement patches for:
|
||||
// - Boost enemy indicator volumes
|
||||
// - Improve state change audio cues
|
||||
// - Add debug logging for enemy audio
|
||||
if (!ModConfig.EnableEnemyAudioBoost.Value)
|
||||
{
|
||||
Plugin.Log.LogInfo("EnemyAudio patches disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin.Log.LogInfo("Applying EnemyAudio patches...");
|
||||
|
||||
PatchEnemyAttackBase(harmony);
|
||||
PatchCFVoiceEventUtility(harmony);
|
||||
PatchEnemyDamageVoiceSFXHandler(harmony);
|
||||
|
||||
Plugin.Log.LogInfo("EnemyAudio patches applied");
|
||||
}
|
||||
|
||||
private static void PatchEnemyAttackBase(Harmony harmony)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enemyAttackBaseType = FindEnemyAttackBaseType();
|
||||
if (enemyAttackBaseType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found EnemyAttackBase type: {enemyAttackBaseType.FullName}");
|
||||
|
||||
string[] attackMethods = new[] { "Attack", "OnAttack", "DoAttack", "AlertAction", "OnEnter", "StartAttack" };
|
||||
foreach (var methodName in attackMethods)
|
||||
{
|
||||
var method = AccessTools.Method(enemyAttackBaseType, methodName);
|
||||
if (method != null)
|
||||
{
|
||||
harmony.Patch(
|
||||
method,
|
||||
prefix: new HarmonyMethod(typeof(EnemyAudioPatch), nameof(EnemyAttackPrefix))
|
||||
);
|
||||
Plugin.Log.LogInfo($"Patched EnemyAttackBase.{methodName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Plugin.Log.LogError($"Error patching EnemyAttackBase: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PatchCFVoiceEventUtility(Harmony harmony)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cfVoiceType = FindCFVoiceEventUtilityType();
|
||||
if (cfVoiceType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found CFVoiceEventUtility type: {cfVoiceType.FullName}");
|
||||
|
||||
var playCFVoiceMethod = AccessTools.Method(cfVoiceType, "PlayCFVoice");
|
||||
if (playCFVoiceMethod != null)
|
||||
{
|
||||
harmony.Patch(
|
||||
playCFVoiceMethod,
|
||||
prefix: new HarmonyMethod(typeof(EnemyAudioPatch), nameof(PlayCFVoicePrefix))
|
||||
);
|
||||
Plugin.Log.LogInfo("Patched PlayCFVoice");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Plugin.Log.LogError($"Error patching CFVoiceEventUtility: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PatchEnemyDamageVoiceSFXHandler(Harmony harmony)
|
||||
{
|
||||
try
|
||||
{
|
||||
var handlerType = FindEnemyDamageVoiceSFXHandlerType();
|
||||
if (handlerType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found EnemyDamageVoiceSFXHandler type: {handlerType.FullName}");
|
||||
|
||||
var feedbackVoiceField = AccessTools.Field(handlerType, "FeedbackVoice");
|
||||
if (feedbackVoiceField != null)
|
||||
{
|
||||
Plugin.Log.LogInfo("EnemyDamageVoiceSFXHandler.FeedbackVoice field found - injectable");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Plugin.Log.LogError($"Error patching EnemyDamageVoiceSFXHandler: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static Type FindEnemyAttackBaseType()
|
||||
{
|
||||
string[] searchNames = new[]
|
||||
{
|
||||
"EnemyAttackBase",
|
||||
"Character.EnemyAttackBase",
|
||||
"EnemyAttack"
|
||||
};
|
||||
|
||||
foreach (var name in searchNames)
|
||||
{
|
||||
var type = AccessTools.TypeByName(name);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
if (type.Name == "EnemyAttackBase")
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Type FindCFVoiceEventUtilityType()
|
||||
{
|
||||
string[] searchNames = new[]
|
||||
{
|
||||
"CFVoiceEventUtility"
|
||||
};
|
||||
|
||||
foreach (var name in searchNames)
|
||||
{
|
||||
var type = AccessTools.TypeByName(name);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
if (type.Name == "CFVoiceEventUtility")
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Type FindEnemyDamageVoiceSFXHandlerType()
|
||||
{
|
||||
string[] searchNames = new[]
|
||||
{
|
||||
"EnemyDamageVoiceSFXHandler",
|
||||
"Andy.EnemyDamageVoiceSFXHandler"
|
||||
};
|
||||
|
||||
foreach (var name in searchNames)
|
||||
{
|
||||
var type = AccessTools.TypeByName(name);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
if (type.Name == "EnemyDamageVoiceSFXHandler")
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool EnemyAttackPrefix(string eventKey)
|
||||
{
|
||||
if (!ModConfig.EnableEnemyAudioBoost.Value)
|
||||
return true;
|
||||
|
||||
if (string.IsNullOrEmpty(eventKey))
|
||||
return true;
|
||||
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
if (eventKey.Contains("Alert") || eventKey.Contains("Warning"))
|
||||
{
|
||||
Plugin.Log.LogDebug($"Enemy alert: {eventKey}");
|
||||
}
|
||||
else if (eventKey.Contains("Attack"))
|
||||
{
|
||||
Plugin.Log.LogDebug($"Enemy attack: {eventKey}");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool PlayCFVoicePrefix(ref string eventKey)
|
||||
{
|
||||
if (!ModConfig.EnableEnemyAudioBoost.Value)
|
||||
return true;
|
||||
|
||||
if (string.IsNullOrEmpty(eventKey))
|
||||
return true;
|
||||
|
||||
if (eventKey.Contains("Enemy"))
|
||||
{
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"Enemy voice event: {eventKey}");
|
||||
}
|
||||
|
||||
if (eventKey.Contains("Alert"))
|
||||
{
|
||||
float boost = ModConfig.AlertSoundBoost.Value;
|
||||
if (boost != 1.0f)
|
||||
{
|
||||
Plugin.Log.LogDebug($"Alert boost: {boost}x");
|
||||
}
|
||||
}
|
||||
else if (eventKey.Contains("Attack"))
|
||||
{
|
||||
float boost = ModConfig.AttackSoundBoost.Value;
|
||||
if (boost != 1.0f)
|
||||
{
|
||||
Plugin.Log.LogDebug($"Attack boost: {boost}x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,186 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HomuraHimeAudioMod.Patches
|
||||
{
|
||||
/// <summary>
|
||||
/// Patches for voice limit and voice manager improvements.
|
||||
/// TO BE IMPLEMENTED AFTER PHASE 2 CODE ANALYSIS
|
||||
/// </summary>
|
||||
public class VoiceManagerPatch
|
||||
{
|
||||
public static void Apply(ref Harmony harmony)
|
||||
{
|
||||
// TODO: After Phase 2, implement patches for:
|
||||
// - Increase max voice count
|
||||
// - Improve voice stealing behavior
|
||||
// - Adjust pooling sizes
|
||||
if (!ModConfig.EnableVoiceLimitFix.Value && !ModConfig.EnableDuckingFix.Value)
|
||||
{
|
||||
Plugin.Log.LogInfo("VoiceManager patches disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin.Log.LogInfo("Applying VoiceManager patches...");
|
||||
|
||||
var originalType = FindFmodVoiceManagerType();
|
||||
if (originalType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found voice manager type: {originalType.FullName}");
|
||||
|
||||
if (ModConfig.EnableDuckingFix.Value)
|
||||
{
|
||||
PatchUpdateDucking(harmony, originalType);
|
||||
PatchDuckVolume(harmony, originalType);
|
||||
}
|
||||
|
||||
if (ModConfig.EnableVoiceLimitFix.Value)
|
||||
{
|
||||
PatchVoiceCount(harmony, originalType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Log.LogWarning("FmodVoiceManager type not found - patches may not apply correctly");
|
||||
}
|
||||
|
||||
Plugin.Log.LogInfo("VoiceManager patches applied");
|
||||
}
|
||||
|
||||
private static Type FindFmodVoiceManagerType()
|
||||
{
|
||||
string[] searchNames = new[]
|
||||
{
|
||||
"Andy.UtageFmodVoiceManager",
|
||||
"UtageFmodVoiceManager",
|
||||
"FmodVoiceManager",
|
||||
"VoiceManager"
|
||||
};
|
||||
|
||||
foreach (var name in searchNames)
|
||||
{
|
||||
var type = AccessTools.TypeByName(name);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
if (type.Name.Contains("FmodVoiceManager") || type.Name.Contains("UtageFmodVoice"))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void PatchUpdateDucking(Harmony harmony, Type targetType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var updateDuckingMethod = AccessTools.Method(targetType, "UpdateDucking");
|
||||
if (updateDuckingMethod != null)
|
||||
{
|
||||
harmony.Patch(
|
||||
updateDuckingMethod,
|
||||
postfix: new HarmonyMethod(typeof(VoiceManagerPatch), nameof(UpdateDuckingPostfix))
|
||||
);
|
||||
Plugin.Log.LogInfo("Patched UpdateDucking");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Plugin.Log.LogError($"Error patching UpdateDucking: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PatchVoiceCount(Harmony harmony, Type targetType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var countVoiceProperty = AccessTools.Property(targetType, "CountVoice");
|
||||
if (countVoiceProperty != null)
|
||||
{
|
||||
var getter = countVoiceProperty.GetGetMethod();
|
||||
if (getter != null)
|
||||
{
|
||||
harmony.Patch(
|
||||
getter,
|
||||
postfix: new HarmonyMethod(typeof(VoiceManagerPatch), nameof(CountVoicePostfix))
|
||||
);
|
||||
Plugin.Log.LogInfo("Patched CountVoice getter");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Plugin.Log.LogError($"Error patching CountVoice: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PatchDuckVolume(Harmony harmony, Type targetType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var duckVolumeProperty = AccessTools.Property(targetType, "DuckVolume");
|
||||
if (duckVolumeProperty != null)
|
||||
{
|
||||
var setter = duckVolumeProperty.GetSetMethod();
|
||||
if (setter != null)
|
||||
{
|
||||
harmony.Patch(
|
||||
setter,
|
||||
prefix: new HarmonyMethod(typeof(VoiceManagerPatch), nameof(DuckVolumePrefix))
|
||||
);
|
||||
Plugin.Log.LogInfo("Patched DuckVolume setter");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Plugin.Log.LogError($"Error patching DuckVolume: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateDuckingPostfix(IEnumerator self, object __instance)
|
||||
{
|
||||
if (__instance == null) return;
|
||||
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"UpdateDucking running on {__instance.GetType().Name}");
|
||||
}
|
||||
|
||||
var duckFadeTimeField = AccessTools.Field(__instance.GetType(), "duckFadeTime");
|
||||
if (duckFadeTimeField != null)
|
||||
{
|
||||
duckFadeTimeField.SetValue(__instance, ModConfig.DuckFadeTime.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CountVoicePostfix(ref int __result)
|
||||
{
|
||||
int maxVoices = ModConfig.MaxVoiceCount.Value;
|
||||
if (__result > maxVoices)
|
||||
{
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"Voice count {__result} exceeds limit {maxVoices}, capping");
|
||||
}
|
||||
__result = maxVoices;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool DuckVolumePrefix(ref float value)
|
||||
{
|
||||
float minDuck = ModConfig.DuckVolume.Value;
|
||||
if (value < minDuck)
|
||||
{
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"DuckVolume {value} below minimum {minDuck}, adjusting");
|
||||
}
|
||||
value = minDuck;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
using BepInEx;
|
||||
using BepInEx.Logging;
|
||||
using HarmonyLib;
|
||||
|
||||
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 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 Harmony harmony;
|
||||
|
||||
public static ManualLogSource Log => logger;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
instance = this;
|
||||
logger = Logger;
|
||||
|
||||
harmony = new Harmony(PluginInfo.PLUGIN_GUID);
|
||||
ModConfig.Initialize(Config);
|
||||
|
||||
ApplyPatches();
|
||||
}
|
||||
|
||||
private void ApplyPatches()
|
||||
{
|
||||
Log.LogInfo($"Applying audio patches...");
|
||||
|
||||
// Phase 2 will populate these patch classes after code analysis
|
||||
// VoiceManagerPatch.Apply(ref harmony);
|
||||
// AudioDuckingPatch.Apply(ref harmony);
|
||||
// EnemyAudioPatch.Apply(ref harmony);
|
||||
|
||||
Log.LogInfo($"Patches applied successfully");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
harmony?.UnpatchAll();
|
||||
Log.LogInfo("========================================");
|
||||
Log.LogInfo("HomuraHime Audio Mod v1.0.0");
|
||||
Log.LogInfo("========================================");
|
||||
Log.LogInfo("");
|
||||
Log.LogInfo("CONFIGURATION (edit in BepInEx/config/com.homurahime.audiomod.cfg):");
|
||||
Log.LogInfo("");
|
||||
Log.LogInfo(" [VoiceManager]");
|
||||
Log.LogInfo($" MaxVoiceCount = {ModConfig.MaxVoiceCount.Value}");
|
||||
Log.LogInfo($" EnableVoiceLimitFix = {ModConfig.EnableVoiceLimitFix.Value}");
|
||||
Log.LogInfo("");
|
||||
Log.LogInfo(" [Ducking]");
|
||||
Log.LogInfo($" DuckFadeTime = {ModConfig.DuckFadeTime.Value}");
|
||||
Log.LogInfo($" DuckVolume = {ModConfig.DuckVolume.Value}");
|
||||
Log.LogInfo($" EnableDuckingFix = {ModConfig.EnableDuckingFix.Value}");
|
||||
Log.LogInfo("");
|
||||
Log.LogInfo(" [EnemyAudio]");
|
||||
Log.LogInfo($" EnemyIndicatorBoost = {ModConfig.EnemyIndicatorBoost.Value}");
|
||||
Log.LogInfo($" AlertSoundBoost = {ModConfig.AlertSoundBoost.Value}");
|
||||
Log.LogInfo($" AttackSoundBoost = {ModConfig.AttackSoundBoost.Value}");
|
||||
Log.LogInfo($" EnableEnemyAudioBoost = {ModConfig.EnableEnemyAudioBoost.Value}");
|
||||
Log.LogInfo("");
|
||||
Log.LogInfo("NOTE: Harmony patches not yet implemented.");
|
||||
Log.LogInfo(" Patches will be added after code analysis.");
|
||||
Log.LogInfo("");
|
||||
Log.LogInfo("========================================");
|
||||
Log.LogInfo("Mod loaded. Edit config file to change values.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")]
|
||||
188
src/HomuraHimeAudioMod/README.md
Normal file
188
src/HomuraHimeAudioMod/README.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# HomuraHime Audio Mod
|
||||
|
||||
A BepInEx mod for HomuraHime that fixes audio glitches and improves enemy behavior audio indicators.
|
||||
|
||||
## Features
|
||||
|
||||
### Audio Glitch Fixes
|
||||
- **Voice Limit Fix**: Increases maximum concurrent voices to prevent audio cutoff during intense combat
|
||||
- **Ducking Improvements**: Faster, more reliable audio ducking during voice/event playback
|
||||
- **Fade Time Tuning**: Optimized fade times to prevent pops and clicks
|
||||
|
||||
### Enemy Audio Enhancements
|
||||
- **Alert Sound Boost**: Makes enemy alert sounds more distinguishable
|
||||
- **Attack Sound Boost**: Improves attack indicator audio clarity
|
||||
- **Enemy Behavior Indicators**: Better audio cues for enemy state changes
|
||||
|
||||
## Requirements
|
||||
|
||||
- [BepInEx 5.x](https://github.com/BepInEx/BepInEx/releases)
|
||||
- HomuraHime (Steam)
|
||||
- .NET Framework 4.8 or .NET SDK
|
||||
|
||||
## Installation
|
||||
|
||||
### Automatic (Recommended)
|
||||
|
||||
1. Run `Build.ps1` script to build and install:
|
||||
```powershell
|
||||
cd src/HomuraHimeAudioMod
|
||||
.\Build.ps1
|
||||
```
|
||||
|
||||
### Manual
|
||||
|
||||
1. Build the project:
|
||||
```powershell
|
||||
dotnet build -c Release
|
||||
```
|
||||
|
||||
2. Copy `HomuraHimeAudioMod.dll` to:
|
||||
```
|
||||
C:\apps\steam\steamapps\common\Homura Hime\BepInEx\plugins\
|
||||
```
|
||||
|
||||
3. Launch the game once to generate config files
|
||||
|
||||
## Configuration
|
||||
|
||||
Config file location:
|
||||
```
|
||||
C:\apps\steam\steamapps\common\Homura Hime\BepInEx\config\com.homurahime.audiomod.cfg
|
||||
```
|
||||
|
||||
### General Settings
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `EnableDebugLogging` | `false` | Enable detailed debug logging |
|
||||
| `EnableVoiceLimitFix` | `true` | Enable voice limit improvements |
|
||||
| `EnableDuckingFix` | `true` | Enable audio ducking improvements |
|
||||
| `EnableEnemyAudioBoost` | `true` | Enable enemy audio enhancements |
|
||||
|
||||
### Voice Manager Settings
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `MaxVoiceCount` | `128` | Maximum concurrent voices (64-256 range) |
|
||||
|
||||
### Ducking Settings
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `DuckFadeTime` | `0.05` | Fade time for ducking (seconds) |
|
||||
| `DuckVolume` | `0.3` | Volume level during ducking (0.0-1.0) |
|
||||
|
||||
### Enemy Audio Settings
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `EnemyIndicatorBoost` | `1.2` | Volume multiplier for indicators |
|
||||
| `AlertSoundBoost` | `1.3` | Volume multiplier for alert sounds |
|
||||
| `AttackSoundBoost` | `1.15` | Volume multiplier for attack sounds |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Mod Not Loading
|
||||
|
||||
Check `BepInEx\LogOutput.log` for errors. Common issues:
|
||||
- Missing .NET Framework 4.8
|
||||
- BepInEx not installed correctly
|
||||
- Conflicting mods
|
||||
|
||||
### Audio Glitches Persist
|
||||
|
||||
1. Try reducing `MaxVoiceCount` if CPU is struggling
|
||||
2. Increase `DuckFadeTime` for smoother transitions
|
||||
3. Disable other audio mods to check for conflicts
|
||||
|
||||
### Game Crashes
|
||||
|
||||
1. Disable all patches and re-enable one at a time
|
||||
2. Check for game updates that may have changed audio code
|
||||
3. Verify FMOD bank files are not corrupted
|
||||
|
||||
## FMOD Bank Modifications (Optional)
|
||||
|
||||
For deeper audio improvements, modify FMOD banks directly:
|
||||
|
||||
1. Extract banks using AssetRipper
|
||||
2. Open in FMOD Studio 2.02.x
|
||||
3. Adjust voice limits, ducking, and enemy indicators
|
||||
4. Reimport modified banks
|
||||
|
||||
See `docs/FMOD_MODIFICATION_GUIDE_PHASE3.md` for detailed instructions.
|
||||
|
||||
## Building from Source
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- .NET Framework 4.8 SDK or .NET SDK
|
||||
- Visual Studio 2022 (optional, for IDE support)
|
||||
|
||||
### Build Commands
|
||||
|
||||
```powershell
|
||||
# Restore packages
|
||||
dotnet restore
|
||||
|
||||
# Build Debug
|
||||
dotnet build
|
||||
|
||||
# Build Release
|
||||
dotnet build -c Release
|
||||
|
||||
# Clean
|
||||
dotnet clean
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
Built DLL: `bin\Release\net48\HomuraHimeAudioMod.dll`
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
HomuraHime-Mods/
|
||||
├── src/
|
||||
│ └── HomuraHimeAudioMod/
|
||||
│ ├── Patches/
|
||||
│ │ ├── VoiceManagerPatch.cs # Voice limit and ducking patches
|
||||
│ │ ├── AudioDuckingPatch.cs # Snapshot and fade patches
|
||||
│ │ └── EnemyAudioPatch.cs # Enemy audio enhancement patches
|
||||
│ ├── Plugin.cs # Main BepInEx plugin
|
||||
│ ├── ModConfig.cs # Configuration management
|
||||
│ └── HomuraHimeAudioMod.csproj # Project file
|
||||
├── docs/
|
||||
│ ├── SETUP_PHASE1.md # Tool installation guide
|
||||
│ ├── AUDIO_ANALYSIS.md # Game audio system analysis
|
||||
│ ├── CODE_ANALYSIS_PHASE2.md # Deep code analysis
|
||||
│ └── FMOD_MODIFICATION_GUIDE_PHASE3.md # FMOD bank editing guide
|
||||
└── tools/
|
||||
└── ExtractBanks.ps1 # Bank extraction script
|
||||
```
|
||||
|
||||
## Known Issues
|
||||
|
||||
- FMOD bank modifications require game restart to take effect
|
||||
- Voice count limits may need adjustment based on hardware
|
||||
- Debug logging can impact performance
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.0.0
|
||||
- Initial release
|
||||
- Voice limit fix (configurable max voices)
|
||||
- Audio ducking improvements
|
||||
- Enemy audio indicator boost options
|
||||
- BepInEx configuration support
|
||||
|
||||
## License
|
||||
|
||||
This mod is provided for personal use. Please respect the game's EULA and copyright.
|
||||
|
||||
## Credits
|
||||
|
||||
- BepInEx team for the modding framework
|
||||
- FMOD for audio middleware
|
||||
- Unity Technologies for game engine
|
||||
103
tools/ExtractBanks.ps1
Normal file
103
tools/ExtractBanks.ps1
Normal file
@@ -0,0 +1,103 @@
|
||||
# HomuraHime Audio Mod - Bank Extraction Script
|
||||
# Run this after installing AssetRipper to extract FMOD banks
|
||||
|
||||
param(
|
||||
[string]$GamePath = "C:\apps\steam\steamapps\common\Homura Hime",
|
||||
[string]$OutputPath = "C:\projects\HomuraHime-Mods\extracted_assets",
|
||||
[string]$BackupPath = "C:\projects\HomuraHime-Mods\backup_banks_original"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
Write-Host "HomuraHime Audio Mod - Bank Extraction Script" -ForegroundColor Cyan
|
||||
Write-Host "=" * 50
|
||||
|
||||
# Check if AssetRipper exists
|
||||
$AssetRipperPath = "C:\projects\HomuraHime-Mods\tools\AssetRipper\AssetRipper.exe"
|
||||
if (-not (Test-Path $AssetRipperPath)) {
|
||||
Write-Host "[ERROR] AssetRipper not found at: $AssetRipperPath" -ForegroundColor Red
|
||||
Write-Host "Please install AssetRipper first (see docs/SETUP_PHASE1.md)" -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if game path exists
|
||||
$GameDataPath = Join-Path $GamePath "HomuraHime_Data\StreamingAssets\FMOD\Desktop"
|
||||
if (-not (Test-Path $GameDataPath)) {
|
||||
Write-Host "[ERROR] Game FMOD banks not found at: $GameDataPath" -ForegroundColor Red
|
||||
Write-Host "Please verify game installation" -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "[INFO] Game path: $GameDataPath" -ForegroundColor Green
|
||||
|
||||
# Create directories
|
||||
$OutputBanksPath = Join-Path $OutputPath "StreamingAssets\FMOD\Desktop"
|
||||
$BackupBanksPath = $BackupPath
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $OutputBanksPath | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path $BackupBanksPath | Out-Null
|
||||
|
||||
Write-Host "[INFO] Output path: $OutputBanksPath" -ForegroundColor Green
|
||||
Write-Host "[INFO] Backup path: $BackupBanksPath" -ForegroundColor Green
|
||||
|
||||
# List current banks
|
||||
$banks = Get-ChildItem -Path $GameDataPath -Filter "*.bank"
|
||||
Write-Host "[INFO] Found $($banks.Count) FMOD bank files" -ForegroundColor Cyan
|
||||
|
||||
# Step 1: Backup original banks
|
||||
Write-Host "`n[STEP 1] Backing up original banks..." -ForegroundColor Yellow
|
||||
foreach ($bank in $banks) {
|
||||
$dest = Join-Path $BackupBanksPath $bank.Name
|
||||
Copy-Item -Path $bank.FullName -Destination $dest -Force
|
||||
Write-Host " Backed up: $($bank.Name)" -ForegroundColor Gray
|
||||
}
|
||||
Write-Host "[OK] Original banks backed up" -ForegroundColor Green
|
||||
|
||||
# Step 2: Launch AssetRipper for extraction
|
||||
Write-Host "`n[STEP 2] Launching AssetRipper..." -ForegroundColor Yellow
|
||||
Write-Host " In AssetRipper:" -ForegroundColor Cyan
|
||||
Write-Host " 1. File -> Load -> Select HomuraHime_Data folder" -ForegroundColor Cyan
|
||||
Write-Host " 2. File -> Export -> Resources" -ForegroundColor Cyan
|
||||
Write-Host " 3. Export to: $OutputPath" -ForegroundColor Cyan
|
||||
Write-Host " 4. Wait for export to complete" -ForegroundColor Cyan
|
||||
Write-Host " 5. Close AssetRipper" -ForegroundColor Cyan
|
||||
|
||||
Start-Process $AssetRipperPath
|
||||
Write-Host "`n[PAUSE] Press Enter when AssetRipper export is complete..." -ForegroundColor Yellow
|
||||
Read-Host
|
||||
|
||||
# Step 3: Verify extraction
|
||||
$extractedBanksPath = Join-Path $OutputPath "StreamingAssets\FMOD\Desktop"
|
||||
if (Test-Path $extractedBanksPath) {
|
||||
$extractedBanks = Get-ChildItem -Path $extractedBanksPath -Filter "*.bank"
|
||||
Write-Host "[OK] Found $($extractedBanks.Count) extracted bank files" -ForegroundColor Green
|
||||
|
||||
# List key banks
|
||||
$keyBanks = @("SFX.bank", "Ambience.bank", "Master.bank", "Voice.bank")
|
||||
foreach ($keyBank in $keyBanks) {
|
||||
$found = $extractedBanks | Where-Object { $_.Name -eq $keyBank }
|
||||
if ($found) {
|
||||
Write-Host " [OK] $keyBank found ($([math]::Round($found.Length / 1MB, 2)) MB)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host " [WARN] $keyBank not found" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Host "[ERROR] Extraction failed - path not found: $extractedBanksPath" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Summary
|
||||
Write-Host "`n" + "=" * 50
|
||||
Write-Host "Extraction Complete!" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Next steps:" -ForegroundColor Yellow
|
||||
Write-Host " 1. Open FMOD Studio 2.02.x" -ForegroundColor Cyan
|
||||
Write-Host " 2. Open banks from: $extractedBanksPath" -ForegroundColor Cyan
|
||||
Write-Host " 3. Edit banks for voice limits, ducking, enemy cues" -ForegroundColor Cyan
|
||||
Write-Host " 4. See docs/FMOD_MODIFICATION_GUIDE_PHASE3.md for details" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Current project structure:" -ForegroundColor Gray
|
||||
Write-Host " extracted_assets\StreamingAssets\FMOD\Desktop\ <- Edit these banks" -ForegroundColor Gray
|
||||
Write-Host " backup_banks_original\ <- Original banks (backup)" -ForegroundColor Gray
|
||||
Write-Host " modified_banks\ <- Create this folder for modified banks" -ForegroundColor Gray
|
||||
Reference in New Issue
Block a user