Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions ChapterMaster.yyp
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@
"isEcma":false,
"LibraryEmitters":[],
"MetaData":{
"IDEVersion":"2026.100.0.1083",
"IDEVersion":"2026.0.0.16",
},
"name":"ChapterMaster",
"resources":[
Expand Down Expand Up @@ -713,6 +713,7 @@
{"id":{"name":"__gml_pragma_global","path":"scripts/__gml_pragma_global/__gml_pragma_global.yy",},},
{"id":{"name":"__init","path":"scripts/__init/__init.yy",},},
{"id":{"name":"Armamentarium","path":"scripts/Armamentarium/Armamentarium.yy",},},
{"id":{"name":"AudioManager","path":"scripts/AudioManager/AudioManager.yy",},},
{"id":{"name":"ColourItem","path":"scripts/ColourItem/ColourItem.yy",},},
{"id":{"name":"ColourPicker","path":"scripts/ColourPicker/ColourPicker.yy",},},
{"id":{"name":"CombatDebugger","path":"scripts/CombatDebugger/CombatDebugger.yy",},},
Expand Down Expand Up @@ -861,7 +862,6 @@
{"id":{"name":"scr_mission_functions","path":"scripts/scr_mission_functions/scr_mission_functions.yy",},},
{"id":{"name":"scr_mission_reward","path":"scripts/scr_mission_reward/scr_mission_reward.yy",},},
{"id":{"name":"scr_move_unit_info","path":"scripts/scr_move_unit_info/scr_move_unit_info.yy",},},
{"id":{"name":"scr_music","path":"scripts/scr_music/scr_music.yy",},},
{"id":{"name":"scr_necron_tombs","path":"scripts/scr_necron_tombs/scr_necron_tombs.yy",},},
{"id":{"name":"scr_ork_fleet_functions","path":"scripts/scr_ork_fleet_functions/scr_ork_fleet_functions.yy",},},
{"id":{"name":"scr_ork_planet_functions","path":"scripts/scr_ork_planet_functions/scr_ork_planet_functions.yy",},},
Expand All @@ -879,6 +879,7 @@
{"id":{"name":"scr_popup","path":"scripts/scr_popup/scr_popup.yy",},},
{"id":{"name":"scr_post_battle_events","path":"scripts/scr_post_battle_events/scr_post_battle_events.yy",},},
{"id":{"name":"scr_powers","path":"scripts/scr_powers/scr_powers.yy",},},
{"id":{"name":"scr_predicate","path":"scripts/scr_predicate/scr_predicate.yy",},},
{"id":{"name":"scr_promote","path":"scripts/scr_promote/scr_promote.yy",},},
{"id":{"name":"scr_punit_combat_heplers","path":"scripts/scr_punit_combat_heplers/scr_punit_combat_heplers.yy",},},
{"id":{"name":"scr_purge_world","path":"scripts/scr_purge_world/scr_purge_world.yy",},},
Expand Down
110 changes: 110 additions & 0 deletions datafiles/audio_modding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Audio Modding Guide

## Overview

- You can add your own music and sound effects (SFX) to Chapter Master.
- The game supports **.ogg** files only.
- Newly added files are **lazy-loaded** on the next play, you don't need to relaunch the game.

## Where To Put Files

There are two places where audio files live:

1. **Shipped** (inside the game's install folder)
2. **User** (your personal override folder)

**User files always override shipped files** if they share the same name. This means you never need to modify the game's installation.

```
AppData/Local/ChapterMaster/Custom Files/Audio/
Music/
menu/
sector/
battle/
creation/
defeat/
diplomacy/
postbattle/
SFX/
```

> If a folder doesn't exist, just create it. The game will read it automatically.

## Music

- Music is organized by **context** (what is happening in the game).
- Each context is a folder.
- Any `.ogg` file you put in that folder becomes part of that context's playlist.
- The game picks a random track from the folder and avoids playing the same track twice in a row.

### Context Folders

| Context | Folder Name | When It Plays |
|---|---|---|
| Menu | `menu/` | Main Menu |
| Sector | `sector/` | Star Map |
| Battle | `battle/` | Combat |
| Creation | `creation/` | Chapter Creation |
| Defeat | `defeat/` | Defeat |
| Diplomacy | `diplomacy/` | Diplomacy Events |
| Post-Battle | `postbattle/` | After Combat Results |

### Example

To add your own battle music:
1. Go to your **User** folder: `<AppData>/Local/ChapterMaster/Custom Files/Audio/Music/battle/`
2. Drop `.ogg` files in there (e.g. `my_battle_track.ogg`, `epic_fight.ogg`)
3. Next time you enter combat, the game will randomly pick from your tracks

### Built-in Music

- The game comes with built-in tracks.
- They play only when no external `.ogg` files exist in a context folder.
- You don't need to worry about them, just adding your own files automatically replaces them.

## Sound Effects (SFX)

SFX files go into the `SFX/` folder (not inside a context subfolder).

### Overridable SFX Names

Place a file named exactly like one of these in `SFX/` to replace that sound:

| Name | Plays When... |
|---|---|
| `click` | Button click |
| `click_small` | Small button click (???) |
| `error` | Error (duh) |
| `buzz` | Buzzing on the creation screen |
| `end_turn` | Ending your turn |
| `identify` | Identifying an artifact |
| `stc` | Identifying an STC fragment |

### Example

To replace the click sound:

1. Create an `.ogg` file named `click.ogg`
2. Put it in `<AppData>/Local/ChapterMaster/Custom Files/Audio/SFX/`
3. The game will now use your file instead of the built-in one

## File Priority (How The Game Decides Which File To Play)

1. User folder (already cached from a previous play)
2. User folder (loads fresh from disk)
3. Shipped folder
4. Built-in asset (compiled into the game)

The game checks these in order and plays the first one it finds. This means:
- Put your custom files in the **User folder** to override everything
- Add new names (not just override existing ones) to expand playlists

## Quick Start Checklist

1. Find your user audio folder: `<AppData>/Local/ChapterMaster/Custom Files/Audio/`
2. Pick SFX or Music, and the context: `Music/battle/` (or whatever)
3. Convert your audio to **.ogg** format
4. Drop the `.ogg` files in the correct folder
5. Launch the game, your audio will play automatically

That's it. No config files, no mod loader, no editing game files.
4 changes: 1 addition & 3 deletions objects/obj_controller/Alarm_7.gml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Player defeat screen
LOGGER.info("Player Defeated; Exited to Defeat Screen");

audio_stop_sound(snd_royal);
audio_play_sound(snd_defeat, 0, true, 0.1);
audio_sound_gain(snd_defeat, 1, 5000);
global.audio_manager.play_playlist(CONTEXT_DEFEAT, 5000);
Comment thread
EttyKitty marked this conversation as resolved.

if ((marines + command <= 50) && (global.defeat != 2)) {
global.defeat = 0;
Expand Down
3 changes: 0 additions & 3 deletions objects/obj_controller/Create_0.gml
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,8 @@ LOGGER.info("Creating Controller");
scr_colors_initialize();
is_test_map = false;
target_navy_number = 5;
global.sound_playing = undefined;
global.defeat = 0;
tutorial = 0;
sound_in = 0;
sound_to = "";
fix_right = 0;
text_bar = 0;
bar_fix = false;
Expand Down
20 changes: 2 additions & 18 deletions objects/obj_controller/Step_0.gml
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,6 @@ try {
}
formating = 0;
}
// Sounds
if (sound_in >= 0) {
sound_in -= 1;
}
if ((sound_in == 0) && (sound_to != "")) {
audio_stop_all();
var nope = false;
if (sound_to == "blood") {
global.sound_playing = audio_play_sound(snd_blood, 0, true);
audio_sound_gain(global.sound_playing, 1, 5000);
}
if (sound_to == "royal") {
global.sound_playing = audio_play_sound(snd_royal, 0, true);
audio_sound_gain(global.sound_playing, 1, 5000);
}
}
// Cheat codes
if (cheatcode != "") {
cheatyface = 1;
Expand Down Expand Up @@ -380,11 +364,11 @@ try {
// Sound controls
if (click > 0) {
click = -1;
audio_play_sound(snd_click, -80, false);
global.audio_manager.play_sfx(SFX_CLICK);
}
if (click2 > 0) {
click2 = -1;
audio_play_sound(snd_click_small, -80, 0);
global.audio_manager.play_sfx(SFX_CLICK_SMALL);
}
// Return artifact
if (qsfx == 1) {
Expand Down
4 changes: 1 addition & 3 deletions objects/obj_creation/Create_0.gml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ custom_icon = 0;
/// Stores the chapter icon in one spot so we dont have to keep checking whether we're using a custom image or not every time we wanna display it somewhere
global.chapter_id = 0;

audio_stop_all();
audio_play_sound(snd_diboz, 0, true, 0.1);
audio_sound_gain(snd_diboz, 1, 5000);
global.audio_manager.play_playlist(CONTEXT_CREATION, 5000);

global.load = -1;
planet_types = global.planet_types;
Expand Down
2 changes: 1 addition & 1 deletion objects/obj_creation/Step_0.gml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ if (slide == 1) {
// Play audio
if ((slate5 == 1) || (slate6 == 1)) {
if ((global.settings.master_volume > 0) && (global.settings.sfx_volume > 0)) {
audio_play_sound(snd_buzz, 0, false);
global.audio_manager.play_sfx(SFX_BUZZ);
}
}

Expand Down
2 changes: 1 addition & 1 deletion objects/obj_defeat/Step_0.gml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if (goodbye > 0) {
}

if (fadeout == 1) {
audio_sound_gain(snd_defeat, 0, 2000);
global.audio_manager.stop_music(2000)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 The Roast: Missing semicolon on stop_music(2000). The old code had it, the new code dropped it. This is the coding equivalent of forgetting your keys — you might still get in the house, but it's awkward.

🩹 The Fix:

Suggested change
global.audio_manager.stop_music(2000)
global.audio_manager.stop_music(2000);

📏 Severity: nitpick


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

}

if (fadeout == 60) {
Expand Down
2 changes: 1 addition & 1 deletion objects/obj_formation_bar/Create_0.gml
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ mouse_release = function() {
obj_cursor.dragging = 0;
obj_cursor.image_index = 0;
if ((global.settings.master_volume > 0) && (global.settings.sfx_volume > 0)) {
audio_play_sound(snd_error, -80, false);
global.audio_manager.play_sfx(SFX_ERROR);
}
}

Expand Down
4 changes: 1 addition & 3 deletions objects/obj_main_menu/Create_0.gml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ title_alpha = 0;
cooldown = 0;
update_blink_visible = false;

audio_stop_all();
global.current_music = audio_play_sound(snd_prologue, 10, true, 0.1);
audio_sound_gain(global.current_music, 1, (global.returned > 0) ? 0 : 5000);
global.audio_manager.play_playlist(CONTEXT_MENU, (global.returned > 0) ? 0 : 5000);
Comment thread
EttyKitty marked this conversation as resolved.

if (instance_exists(obj_cursor)) {
obj_cursor.image_alpha = (global.returned > 0) ? 1 : 0;
Expand Down
4 changes: 2 additions & 2 deletions objects/obj_main_menu/Draw_0.gml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ if (global.game_version != "unknown version") {

if (point_and_click([1400, 830, 1600, 900])) {
clipboard_set_text($"{_build_date_line}\n{_version_line}");
audio_play_sound(snd_click_small, 0, false);
global.audio_manager.play_sfx(SFX_CLICK_SMALL);
}

// Update notification
Expand All @@ -35,7 +35,7 @@ if (UPDATE_CHECKER.update_available) {

if (point_and_click([1400, 780, 1600, 830])) {
url_open(UPDATE_CHECKER.latest_release_url);
audio_play_sound(snd_click_small, 0, false);
global.audio_manager.play_sfx(SFX_CLICK_SMALL);
}
}

Expand Down
4 changes: 1 addition & 3 deletions objects/obj_ncombat/Alarm_7.gml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
try {
audio_stop_sound(snd_battle);
audio_play_sound(snd_royal, 0, true);
audio_sound_gain(snd_royal, 1, 5000);
global.audio_manager.play_playlist(CONTEXT_POSTBATTLE, 5000);

// Execute the cleaning scripts
// Check for any more battles
Expand Down
4 changes: 1 addition & 3 deletions objects/obj_ncombat/Create_0.gml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ if (instance_number(obj_ncombat) > 1) {
set_zoom_to_default();
LOGGER.info("Ground Combat Started");

audio_stop_sound(snd_royal);
audio_play_sound(snd_battle, 0, true);
audio_sound_gain(snd_battle, 1, 5000);
global.audio_manager.play_playlist(CONTEXT_BATTLE, 5000);

hue = 0;
turn_count = 0;
Expand Down
10 changes: 7 additions & 3 deletions objects/obj_persistent/Create_0.gml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
randomize();

audio_group_load(audiogroup_sfx);
audio_group_load(audiogroup_music);

global.audio_manager = new AudioManager();
global.audio_manager.discover();

global.settings = new SettingsManager();
global.settings.load();
global.settings.apply_video();
Expand All @@ -11,7 +17,6 @@ USERNAME_PROMPT.prompt();
global.save_version = 0;
global.returned = 0;
global.debug = false;
global.current_music = -1;
global.load = 0;
global.cheat_req = false;
global.cheat_gene = false;
Expand All @@ -22,5 +27,4 @@ global.language = "en";
instance_create_depth(0, 0, 0, obj_garbage_collector);
instance_create_depth(0, 0, 0, obj_img);

audio_group_load(audiogroup_sfx);
audio_group_load(audiogroup_music);

5 changes: 2 additions & 3 deletions objects/obj_saveload/Destroy_0.gml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
scr_image("loading", -666, 0, 0, 0, 0);

if ((!audio_is_playing(snd_royal)) && instance_exists(obj_controller)) {
audio_play_sound(snd_royal, 0, true);
audio_sound_gain(snd_royal, 1, 5000);
if (instance_exists(obj_controller)) {
global.audio_manager.play_playlist(CONTEXT_SECTOR, 5000);
}
2 changes: 1 addition & 1 deletion objects/obj_turn_end/Alarm_1.gml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if (array_length(audience_stack) > 0) {
obj_controller.audience_data = current_audience.audience_data;

if ((obj_controller.diplomacy == 10) && (obj_controller.faction_gender[10] == 2)) {
scr_music("blood", 60);
global.audio_manager.play_playlist(CONTEXT_DIPLOMACY, 60);
}

if (string_count("intro", current_audience.topic) > 0) {
Expand Down
12 changes: 6 additions & 6 deletions scripts/Armamentarium/Armamentarium.gml
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ function STCResearchPanel(_controller_ref, _on_change_callback) constructor {

Comment thread
EttyKitty marked this conversation as resolved.
if (mouse_button_clicked()) {
controller.stc_research.research_focus = _cat;
audio_play_sound(snd_click, 10, false);
global.audio_manager.play_sfx(SFX_CLICK);
refresh_eta();
}
}
Expand Down Expand Up @@ -430,7 +430,7 @@ function STCResearchPanel(_controller_ref, _on_change_callback) constructor {

advance_stc_research(_target);

audio_play_sound(snd_stc, -500, false);
global.audio_manager.play_sfx(SFX_STC);

if (is_method(on_change)) {
on_change();
Expand Down Expand Up @@ -867,7 +867,7 @@ function Armamentarium(_controller) constructor {
_item.stocked -= _sold_count;
controller.requisition += _sell_price;

audio_play_sound(snd_click, 10, false);
global.audio_manager.play_sfx(SFX_CLICK);

return true;
};
Expand All @@ -882,7 +882,7 @@ function Armamentarium(_controller) constructor {
// 1. Warships
if (shop_type == "ships") {
add_event({e_id: "ship_construction", ship_class: _item.name, duration: _item.request_duration});
audio_play_sound(snd_click, 10, false);
global.audio_manager.play_sfx(SFX_CLICK);
return;
}

Expand All @@ -900,7 +900,7 @@ function Armamentarium(_controller) constructor {
}

_item.stocked += _count;
audio_play_sound(snd_click, 10, false);
global.audio_manager.play_sfx(SFX_CLICK);
};

/// @desc Switches the current shop category.
Expand Down Expand Up @@ -1051,7 +1051,7 @@ function Armamentarium(_controller) constructor {
if (array_length(_queue) < FORGE_QUEUE_MAX) {
array_push(_queue, {item: _item, count: _count, forge_points: _cost, ordered: controller.turn});
} else {
audio_play_sound(snd_error, 10, false);
global.audio_manager.play_sfx(SFX_ERROR);
}
}

Expand Down
Loading
Loading