Compare commits
10 Commits
71712b834c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 906a4d5a74 | |||
| c0b6816ab0 | |||
| 5a9f306f3f | |||
| 4251a29794 | |||
| 81bf38b472 | |||
| 4285ae5258 | |||
| b589d39ccd | |||
| 2f874a89e4 | |||
| a6fa90375e | |||
| 8f162dc103 |
@@ -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.
|
||||
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"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,28 +7,24 @@ namespace HomuraHimeAudioMod.Patches
|
||||
{
|
||||
public class AudioDuckingPatch
|
||||
{
|
||||
public const float COMBAT_DUCK_AMOUNT = 0.25f;
|
||||
public const float COMBAT_DUCK_FADE_IN = 0.03f;
|
||||
public const float COMBAT_DUCK_FADE_OUT = 0.15f;
|
||||
public const float VOICE_DUCK_AMOUNT = 0.35f;
|
||||
public const float SFX_DUCK_AMOUNT = 0.4f;
|
||||
|
||||
public static void Apply(ref Harmony harmony)
|
||||
{
|
||||
if (!ModConfig.EnableDuckingFix.Value)
|
||||
{
|
||||
Plugin.Log.LogInfo("AudioDucking patches disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin.Log.LogInfo("Applying AudioDucking patches...");
|
||||
|
||||
var originalType = AccessTools.TypeByName("Andy.SnapshotManager");
|
||||
if (originalType != null)
|
||||
var snapshotManagerType = FindSnapshotManagerType();
|
||||
if (snapshotManagerType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found SnapshotManager type: {originalType.FullName}");
|
||||
PatchSnapshotApply(harmony, originalType);
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Log.LogWarning("SnapshotManager type not found");
|
||||
Plugin.Log.LogInfo($"Found SnapshotManager type: {snapshotManagerType.FullName}");
|
||||
PatchSnapshotApply(harmony, snapshotManagerType);
|
||||
}
|
||||
|
||||
var fadeManagerType = AccessTools.TypeByName("Andy.DEAudioFadeManager");
|
||||
var fadeManagerType = FindFadeManagerType();
|
||||
if (fadeManagerType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found DEAudioFadeManager type: {fadeManagerType.FullName}");
|
||||
@@ -38,6 +34,60 @@ namespace HomuraHimeAudioMod.Patches
|
||||
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
|
||||
@@ -91,14 +141,23 @@ namespace HomuraHimeAudioMod.Patches
|
||||
|
||||
private static bool ApplySnapshotPrefix(ref string snapshotName, ref float fadeTime)
|
||||
{
|
||||
if (fadeTime < COMBAT_DUCK_FADE_IN)
|
||||
float configuredFadeTime = ModConfig.DuckFadeTime.Value;
|
||||
|
||||
if (fadeTime < configuredFadeTime)
|
||||
{
|
||||
fadeTime = COMBAT_DUCK_FADE_IN;
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"Adjusting fade time from {fadeTime} to {configuredFadeTime}");
|
||||
}
|
||||
fadeTime = configuredFadeTime;
|
||||
}
|
||||
|
||||
if (snapshotName != null && snapshotName.Contains("Battle"))
|
||||
if (ModConfig.EnableDebugLogging.Value && !string.IsNullOrEmpty(snapshotName))
|
||||
{
|
||||
Plugin.Log.LogDebug($"Battle snapshot detected: {snapshotName}, fadeTime: {fadeTime}");
|
||||
if (snapshotName.Contains("Battle") || snapshotName.Contains("Combat"))
|
||||
{
|
||||
Plugin.Log.LogDebug($"Battle/Combat snapshot: {snapshotName}");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -106,18 +165,28 @@ namespace HomuraHimeAudioMod.Patches
|
||||
|
||||
private static bool FadeInPrefix(ref float fadeTime)
|
||||
{
|
||||
if (fadeTime < 0.01f)
|
||||
const float minFadeIn = 0.01f;
|
||||
if (fadeTime < minFadeIn)
|
||||
{
|
||||
fadeTime = 0.01f;
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"FadeIn adjusted: {fadeTime} -> {minFadeIn}");
|
||||
}
|
||||
fadeTime = minFadeIn;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool FadeOutPrefix(ref float fadeTime)
|
||||
{
|
||||
if (fadeTime < 0.05f)
|
||||
const float minFadeOut = 0.05f;
|
||||
if (fadeTime < minFadeOut)
|
||||
{
|
||||
fadeTime = 0.05f;
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"FadeOut adjusted: {fadeTime} -> {minFadeOut}");
|
||||
}
|
||||
fadeTime = minFadeOut;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
using System;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HomuraHimeAudioMod.Patches
|
||||
{
|
||||
public class EnemyAudioPatch
|
||||
{
|
||||
public const float ENEMY_INDICATOR_VOLUME_BOOST = 1.2f;
|
||||
public const float ALERT_SOUND_VOLUME_BOOST = 1.3f;
|
||||
public const float ATTACK_SOUND_VOLUME_BOOST = 1.15f;
|
||||
|
||||
public static void Apply(ref Harmony harmony)
|
||||
{
|
||||
if (!ModConfig.EnableEnemyAudioBoost.Value)
|
||||
{
|
||||
Plugin.Log.LogInfo("EnemyAudio patches disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin.Log.LogInfo("Applying EnemyAudio patches...");
|
||||
|
||||
PatchEnemyAttackBase(harmony);
|
||||
@@ -25,12 +26,12 @@ namespace HomuraHimeAudioMod.Patches
|
||||
{
|
||||
try
|
||||
{
|
||||
var enemyAttackBaseType = AccessTools.TypeByName("EnemyAttackBase");
|
||||
var enemyAttackBaseType = FindEnemyAttackBaseType();
|
||||
if (enemyAttackBaseType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found EnemyAttackBase type: {enemyAttackBaseType.FullName}");
|
||||
|
||||
var attackMethods = new[] { "Attack", "OnAttack", "DoAttack", "AlertAction" };
|
||||
string[] attackMethods = new[] { "Attack", "OnAttack", "DoAttack", "AlertAction", "OnEnter", "StartAttack" };
|
||||
foreach (var methodName in attackMethods)
|
||||
{
|
||||
var method = AccessTools.Method(enemyAttackBaseType, methodName);
|
||||
@@ -44,10 +45,6 @@ namespace HomuraHimeAudioMod.Patches
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Log.LogWarning("EnemyAttackBase type not found");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -59,7 +56,7 @@ namespace HomuraHimeAudioMod.Patches
|
||||
{
|
||||
try
|
||||
{
|
||||
var cfVoiceType = AccessTools.TypeByName("CFVoiceEventUtility");
|
||||
var cfVoiceType = FindCFVoiceEventUtilityType();
|
||||
if (cfVoiceType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found CFVoiceEventUtility type: {cfVoiceType.FullName}");
|
||||
@@ -74,10 +71,6 @@ namespace HomuraHimeAudioMod.Patches
|
||||
Plugin.Log.LogInfo("Patched PlayCFVoice");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Log.LogWarning("CFVoiceEventUtility type not found");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -89,7 +82,7 @@ namespace HomuraHimeAudioMod.Patches
|
||||
{
|
||||
try
|
||||
{
|
||||
var handlerType = AccessTools.TypeByName("EnemyDamageVoiceSFXHandler");
|
||||
var handlerType = FindEnemyDamageVoiceSFXHandlerType();
|
||||
if (handlerType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found EnemyDamageVoiceSFXHandler type: {handlerType.FullName}");
|
||||
@@ -97,7 +90,7 @@ namespace HomuraHimeAudioMod.Patches
|
||||
var feedbackVoiceField = AccessTools.Field(handlerType, "FeedbackVoice");
|
||||
if (feedbackVoiceField != null)
|
||||
{
|
||||
Plugin.Log.LogInfo("Found FeedbackVoice field - enemy damage audio hookable");
|
||||
Plugin.Log.LogInfo("EnemyDamageVoiceSFXHandler.FeedbackVoice field found - injectable");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,32 +100,141 @@ namespace HomuraHimeAudioMod.Patches
|
||||
}
|
||||
}
|
||||
|
||||
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 (!string.IsNullOrEmpty(eventKey))
|
||||
{
|
||||
Plugin.Log.LogDebug($"Enemy attack event: {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 sound detected: {eventKey}");
|
||||
Plugin.Log.LogDebug($"Enemy alert: {eventKey}");
|
||||
}
|
||||
else if (eventKey.Contains("Attack"))
|
||||
{
|
||||
Plugin.Log.LogDebug($"Enemy attack sound: {eventKey}");
|
||||
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"))
|
||||
{
|
||||
Plugin.Log.LogDebug($"Enemy voice event triggered: {eventKey}");
|
||||
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;
|
||||
|
||||
@@ -7,42 +7,69 @@ namespace HomuraHimeAudioMod.Patches
|
||||
{
|
||||
public class VoiceManagerPatch
|
||||
{
|
||||
public const int MAX_VOICE_COUNT = 128;
|
||||
public const float DEFAULT_DUCK_FADE_TIME = 0.05f;
|
||||
public const float DEFAULT_DUCK_VOLUME = 0.3f;
|
||||
|
||||
public static void Apply(ref Harmony harmony)
|
||||
{
|
||||
if (!ModConfig.EnableVoiceLimitFix.Value && !ModConfig.EnableDuckingFix.Value)
|
||||
{
|
||||
Plugin.Log.LogInfo("VoiceManager patches disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
Plugin.Log.LogInfo("Applying VoiceManager patches...");
|
||||
|
||||
var originalType = AccessTools.TypeByName("Andy.UtageFmodVoiceManager");
|
||||
var originalType = FindFmodVoiceManagerType();
|
||||
if (originalType != null)
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found UtageFmodVoiceManager type: {originalType.FullName}");
|
||||
Plugin.Log.LogInfo($"Found voice manager type: {originalType.FullName}");
|
||||
|
||||
if (ModConfig.EnableDuckingFix.Value)
|
||||
{
|
||||
PatchUpdateDucking(harmony, originalType);
|
||||
PatchVoiceCount(harmony, originalType);
|
||||
PatchDuckVolume(harmony, originalType);
|
||||
}
|
||||
|
||||
if (ModConfig.EnableVoiceLimitFix.Value)
|
||||
{
|
||||
PatchVoiceCount(harmony, originalType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Log.LogWarning("UtageFmodVoiceManager type not found by name, searching...");
|
||||
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"))
|
||||
if (type.Name.Contains("FmodVoiceManager") || type.Name.Contains("UtageFmodVoice"))
|
||||
{
|
||||
Plugin.Log.LogInfo($"Found alternative: {type.FullName}");
|
||||
PatchUpdateDucking(harmony, type);
|
||||
PatchVoiceCount(harmony, type);
|
||||
break;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Plugin.Log.LogInfo("VoiceManager patches applied");
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void PatchUpdateDucking(Harmony harmony, Type targetType)
|
||||
@@ -58,10 +85,6 @@ namespace HomuraHimeAudioMod.Patches
|
||||
);
|
||||
Plugin.Log.LogInfo("Patched UpdateDucking");
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Log.LogWarning("UpdateDucking method not found");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -86,10 +109,6 @@ namespace HomuraHimeAudioMod.Patches
|
||||
Plugin.Log.LogInfo("Patched CountVoice getter");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Log.LogWarning("CountVoice property not found");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -121,37 +140,47 @@ namespace HomuraHimeAudioMod.Patches
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateDuckingPostfix(IEnumerator self, UtageFmodVoiceManager __instance)
|
||||
private static void UpdateDuckingPostfix(IEnumerator self, object __instance)
|
||||
{
|
||||
if (__instance != null)
|
||||
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, DEFAULT_DUCK_FADE_TIME);
|
||||
}
|
||||
duckFadeTimeField.SetValue(__instance, ModConfig.DuckFadeTime.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CountVoicePostfix(ref int __result)
|
||||
{
|
||||
if (__result > MAX_VOICE_COUNT)
|
||||
int maxVoices = ModConfig.MaxVoiceCount.Value;
|
||||
if (__result > maxVoices)
|
||||
{
|
||||
__result = MAX_VOICE_COUNT;
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"Voice count {__result} exceeds limit {maxVoices}, capping");
|
||||
}
|
||||
__result = maxVoices;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool DuckVolumePrefix(ref float value)
|
||||
{
|
||||
if (value < DEFAULT_DUCK_VOLUME)
|
||||
float minDuck = ModConfig.DuckVolume.Value;
|
||||
if (value < minDuck)
|
||||
{
|
||||
value = DEFAULT_DUCK_VOLUME;
|
||||
if (ModConfig.EnableDebugLogging.Value)
|
||||
{
|
||||
Plugin.Log.LogDebug($"DuckVolume {value} below minimum {minDuck}, adjusting");
|
||||
}
|
||||
value = minDuck;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class UtageFmodVoiceManager : MonoBehaviour
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +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);
|
||||
|
||||
Log.LogInfo($"HomuraHime Audio Mod v{PluginInfo.PLUGIN_VERSION} initializing...");
|
||||
|
||||
ApplyPatches();
|
||||
|
||||
Log.LogInfo("HomuraHime Audio Mod initialized successfully");
|
||||
}
|
||||
|
||||
private void ApplyPatches()
|
||||
{
|
||||
Log.LogInfo("Applying audio patches...");
|
||||
|
||||
VoiceManagerPatch.Apply(ref harmony);
|
||||
AudioDuckingPatch.Apply(ref harmony);
|
||||
EnemyAudioPatch.Apply(ref harmony);
|
||||
|
||||
Log.LogInfo("All patches applied successfully");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
harmony?.UnpatchAll(PluginInfo.PLUGIN_GUID);
|
||||
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