mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
ALTTP: Add "oof" sound customization option (#709)
Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com> Co-authored-by: Fabian Dill <fabian.dill@web.de> Co-authored-by: Zach Parks <zach@alliware.com>
This commit is contained in:
@@ -189,7 +189,7 @@ def check_enemizer(enemizercli):
|
||||
# some time may have passed since the lock was acquired, as such a quick re-check doesn't hurt
|
||||
if getattr(check_enemizer, "done", None):
|
||||
return
|
||||
wanted_version = (7, 0, 1)
|
||||
wanted_version = (7, 1, 0)
|
||||
# version info is saved on the lib, for some reason
|
||||
library_info = os.path.join(os.path.dirname(enemizercli), "EnemizerCLI.Core.deps.json")
|
||||
with open(library_info) as f:
|
||||
@@ -1775,8 +1775,57 @@ def hud_format_text(text):
|
||||
output += b'\x7f\x00'
|
||||
return output[:32]
|
||||
|
||||
def apply_oof_sfx(rom, oof: str):
|
||||
with open(oof, 'rb') as stream:
|
||||
oof_bytes = bytearray(stream.read())
|
||||
|
||||
def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, sprite: str, palettes_options,
|
||||
oof_len_bytes = len(oof_bytes).to_bytes(2, byteorder='little')
|
||||
|
||||
# Credit to kan for this method, and Nyx for initial C# implementation
|
||||
# this is ported from, with both of their permission for use by AP
|
||||
# Original C# implementation:
|
||||
# https://github.com/Nyx-Edelstein/The-Unachievable-Ideal-of-Chibi-Elf-Grunting-Noises-When-They-Get-Punched-A-Z3-Rom-Patcher
|
||||
|
||||
# Jump execution from the SPC load routine to new code
|
||||
rom.write_bytes(0x8CF, [0x5C, 0x00, 0x80, 0x25])
|
||||
|
||||
# Change the pointer for instrument 9 in SPC memory to point to the new data we'll be inserting:
|
||||
rom.write_bytes(0x1A006C, [0x88, 0x31, 0x00, 0x00])
|
||||
|
||||
# Insert a sigil so we can branch on it later
|
||||
# We will recover the value it overwrites after we're done with insertion
|
||||
rom.write_bytes(0x1AD38C, [0xBE, 0xBE])
|
||||
|
||||
# Change the "oof" sound effect to use instrument 9:
|
||||
rom.write_byte(0x1A9C4E, 0x09)
|
||||
|
||||
# Correct the pitch shift value:
|
||||
rom.write_byte(0x1A9C51, 0xB6)
|
||||
|
||||
# Modify parameters of instrument 9
|
||||
# (I don't actually understand this part, they're just magic values to me)
|
||||
rom.write_bytes(0x1A9CAE, [0x7F, 0x7F, 0x00, 0x10, 0x1A, 0x00, 0x00, 0x7F, 0x01])
|
||||
|
||||
# Hook from SPC load routine:
|
||||
# * Check for the read of the sigil
|
||||
# * Once we find it, change the SPC load routine's data pointer to read from the location containing the new sample
|
||||
# * Note: XXXX in the string below is a placeholder for the number of bytes in the .brr sample (little endian)
|
||||
# * Another sigil "$EBEB" is inserted at the end of the data
|
||||
# * When the second sigil is read, we know we're done inserting our data so we can change the data pointer back
|
||||
# * Effect: The new data gets loaded into SPC memory without having to relocate the SPC load routine
|
||||
# Slight variation from VT-compatible algorithm: We need to change the data pointer to $00 00 35 and load 538E into Y to pick back up where we left off
|
||||
rom.write_bytes(0x128000, [0xB7, 0x00, 0xC8, 0xC8, 0xC9, 0xBE, 0xBE, 0xF0, 0x09, 0xC9, 0xEB, 0xEB, 0xF0, 0x1B, 0x5C, 0xD3, 0x88, 0x00, 0xA2, oof_len_bytes[0], oof_len_bytes[1], 0xA9, 0x80, 0x25, 0x85, 0x01, 0xA9, 0x3A, 0x80, 0x85, 0x00, 0xA0, 0x00, 0x00, 0xA9, 0x88, 0x31, 0x5C, 0xD8, 0x88, 0x00, 0xA9, 0x80, 0x35, 0x64, 0x00, 0x85, 0x01, 0xA2, 0x00, 0x00, 0xA0, 0x8E, 0x53, 0x5C, 0xD4, 0x88, 0x00])
|
||||
|
||||
# The new sample data
|
||||
# (We need to insert the second sigil at the end)
|
||||
rom.write_bytes(0x12803A, oof_bytes)
|
||||
rom.write_bytes(0x12803A + len(oof_bytes), [0xEB, 0xEB])
|
||||
|
||||
#Enemizer patch: prevent Enemizer from overwriting $3188 in SPC memory with an unused sound effect ("WHAT")
|
||||
rom.write_bytes(0x13000D, [0x00, 0x00, 0x00, 0x08])
|
||||
|
||||
|
||||
def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, sprite: str, oof: str, palettes_options,
|
||||
world=None, player=1, allow_random_on_event=False, reduceflashing=False,
|
||||
triforcehud: str = None, deathlink: bool = False, allowcollect: bool = False):
|
||||
local_random = random if not world else world.per_slot_randoms[player]
|
||||
@@ -1918,6 +1967,10 @@ def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, spri
|
||||
|
||||
apply_random_sprite_on_event(rom, sprite, local_random, allow_random_on_event,
|
||||
world.sprite_pool[player] if world else [])
|
||||
|
||||
if oof is not None:
|
||||
apply_oof_sfx(rom, oof)
|
||||
|
||||
if isinstance(rom, LocalRom):
|
||||
rom.write_crc()
|
||||
|
||||
|
||||
@@ -103,7 +103,16 @@ class ALTTPWeb(WebWorld):
|
||||
["Berserker"]
|
||||
)
|
||||
|
||||
tutorials = [setup_en, setup_de, setup_es, setup_fr, msu, msu_es, msu_fr, plando]
|
||||
oof_sound = Tutorial(
|
||||
"'OOF' Sound Replacement",
|
||||
"A guide to customizing Link's 'oof' sound",
|
||||
"English",
|
||||
"oof_sound_en.md",
|
||||
"oof_sound/en",
|
||||
["Nyx Edelstein"]
|
||||
)
|
||||
|
||||
tutorials = [setup_en, setup_de, setup_es, setup_fr, msu, msu_es, msu_fr, plando, oof_sound]
|
||||
|
||||
|
||||
class ALTTPWorld(World):
|
||||
@@ -485,6 +494,7 @@ class ALTTPWorld(World):
|
||||
world.menuspeed[player].current_key,
|
||||
world.music[player],
|
||||
world.sprite[player],
|
||||
None,
|
||||
palettes_options, world, player, True,
|
||||
reduceflashing=world.reduceflashing[player] or world.is_race,
|
||||
triforcehud=world.triforcehud[player].current_key,
|
||||
|
||||
20
worlds/alttp/docs/oof_sound_en.md
Normal file
20
worlds/alttp/docs/oof_sound_en.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# "OOF" sound customization guide
|
||||
|
||||
## What does this feature do?
|
||||
|
||||
It replaces the sound effect when Link takes damage. The intended use case for this is custom sprites, but you can use it with any sprite, including the default one.
|
||||
|
||||
Due to technical restrictions resulting from limited available memory, there is a limit to how long the sound can be. Using the current method, this limit is **0.394 seconds**. This means that many ideas won't work, and any intelligible speech or anything other than a grunt or simple noise will be too long.
|
||||
|
||||
Some examples of what is possible: https://www.youtube.com/watch?v=TYs322kHlc0
|
||||
|
||||
## How do I create my own custom sound?
|
||||
|
||||
1. Obtain a .wav file with the following specifications: 16-bit signed PCM at 12khz, no longer than 0.394 seconds. You can do this by editing an existing sample using a program like Audacity, or by recording your own. Note that samples can be shrinked or truncated to meet the length requirement, at the expense of sound quality.
|
||||
2. Use the `--encode` function of the snesbrr tool (https://github.com/boldowa/snesbrr) to encode your .wav file in the proper format (.brr). The .brr file **cannot** exceed 2673 bytes. As long as the input file meets the above specifications, the .brr file should be this size or smaller. If your file is too large, go back to step 1 and make the sample shorter.
|
||||
3. When running the adjuster GUI, simply select the .brr file you wish to use after clicking the `"OOF" Sound` menu option.
|
||||
4. You can also do the patch via command line: `python .\LttPAdjuster.py --baserom .\baserom.sfc --oof .\oof.brr .\romtobeadjusted.sfc`, replacing the file names with your files.
|
||||
|
||||
## Can I use multiple sounds for composite sprites?
|
||||
|
||||
No, this is not technically feasible. You can only use one sound.
|
||||
Reference in New Issue
Block a user