Creating mods
A mod is a folder containing a Ruby file. That's the entire format.
Folder structure
The loader scans BLACK SOULS II/Mods/ at startup. Every direct subfolder
that contains a main.rb file is treated as a mod and its main.rb
is eval'd at the top level.
Mods/
└── my_first_mod/
├── main.rb required - the mod's entry point
├── manifest.txt optional - pure metadata
└── assets/ optional - graphic / audio / data overrides
├── Graphics/
│ └── Faces/Custom.png
└── Audio/
└── BGM/title.ogg
The mod's folder name is what shows up in logs and load order - keep it descriptive
and ASCII-safe. Folders starting with _ or . are skipped.
The smallest possible mod
One line of Ruby is enough to be a mod:
# Mods/hello_world/main.rb
ModLoader.log("hello from my first mod")
That writes hello from my first mod to %TEMP%/bs_modloader.log at every
game launch. Boring, but it confirms your mod is being loaded.
The alias_method pattern
The most common pattern: monkey-patch an existing game class without breaking its
behavior. Use alias_method to keep the original method around, redefine the
method, call the original, then add your own logic.
class Game_Player
alias_method :_my_orig_increase_steps, :increase_steps
def increase_steps
_my_orig_increase_steps # call the original
if $game_party
$game_party.gain_gold(5) rescue nil # add 5 souls per step
end
end
end
ModLoader.log("[my_mod] hooked Game_Player#increase_steps") rescue nil
Three principles to follow:
- Use a unique alias prefix (e.g.
_my_) so you don't collide with another mod that hooks the same method. - Always call the aliased original - otherwise you replace base behavior, not extend it.
- Wrap risky operations in
rescue nil. If your mod throws, the loader catches it, but the rest of the game shouldn't be left in a half-modified state.
Tweaking the database
The game's data tables are exposed as global arrays - $data_items,
$data_weapons, $data_armors, $data_skills,
$data_enemies, $data_classes, $data_states,
$data_actors, $data_troops. They're all 1-indexed (entry 0 is nil).
# Mods/strong_potions/main.rb
($data_items || []).each do |item|
next unless item && item.itype_id == 1 # 1 = Regular item, 2 = Key item
item.price = 1
# Replace effects with "recover all HP"
item.effects = [RPG::UsableItem::Effect.new(11, 0, 1.0, 0)]
end
ModLoader.log("[strong_potions] juiced every consumable") rescue nil
These changes are in-memory. They take effect immediately, last until the game closes, and don't touch your save file.
Hooking scenes
Scenes (Scene_Map, Scene_Battle, Scene_Menu, …)
have start, update, and terminate lifecycle methods
you can hook for overlays, custom HUDs, hotkeys, etc.
# Mods/coord_hud/main.rb
class Scene_Map
alias_method :_my_orig_start, :start
alias_method :_my_orig_update, :update
def start
_my_orig_start
@_my_sprite = Sprite.new
@_my_sprite.bitmap = Bitmap.new(220, 28)
@_my_sprite.x = 8; @_my_sprite.y = 8; @_my_sprite.z = 9999
end
def update
_my_orig_update
return unless @_my_sprite && @_my_sprite.bitmap && $game_player
@_my_sprite.bitmap.clear
@_my_sprite.bitmap.draw_text(0, 0, 220, 28,
"Map #{$game_map.map_id} (#{$game_player.x},#{$game_player.y})")
end
end
For a more complete example see the 05_scene_hook template - it adds a
cleanup step in terminate so the sprite is disposed properly when leaving
the map.
Replacing assets
For graphic / audio / data file replacement, you don't need any Ruby code at all.
See the Asset Overrides page - drop a PNG into
assets/Graphics/Faces/ and you're done.
Where to find class and method names
Black Souls II ships with 224 scripts; many of them are heavily customized vanilla RPG Maker scripts plus BS2-specific systems (covenants, symbol enemies, region passing, etc.). To hook the right method you need to know what's actually defined.
Run the recon tool that comes with the source repo:
cd "Mod Loader/src"
python recon.py
It dumps every script under docs/recon/scripts/ as readable Ruby and
auto-generates a DOCUMENTATION.md indexing every class, module, method,
and constant the live game defines. That's the canonical reference for naming things.
Logging
Use ModLoader.log("[your_mod] message") to write to the loader's log files.
Wrap calls in rescue nil so a missing loader (e.g. when running outside the
patched game) doesn't break your mod.
Log files written:
BLACK SOULS II/Mods/_loader.logBLACK SOULS II/bs_modloader.log%TEMP%/bs_modloader.log