diff --git a/src/mars_patcher/common_types.py b/src/mars_patcher/common_types.py index 0a09c67..10f352b 100644 --- a/src/mars_patcher/common_types.py +++ b/src/mars_patcher/common_types.py @@ -11,3 +11,5 @@ AreaRoomPair = tuple[AreaId, RoomId] MinimapId: TypeAlias = Annotated[int, "0 <= value < 10"] + +MusicMapping: TypeAlias = types_mf.Musicmapping | types_zm.Musicmapping diff --git a/src/mars_patcher/mf/auto_generated_types.py b/src/mars_patcher/mf/auto_generated_types.py index 523ebd3..221e13d 100644 --- a/src/mars_patcher/mf/auto_generated_types.py +++ b/src/mars_patcher/mf/auto_generated_types.py @@ -189,6 +189,68 @@ 'Italian', 'Spanish' ] +Validmusictracks = typ.Literal[ + 'UNUSED_1', + 'AFTER_EVENT', + 'SECTOR_1', + 'SECTOR_2', + 'SECTOR_3', + 'SECTOR_5', + 'SECTOR_4', + 'SECTOR_6', + 'NAVIGATION_ROOM', + 'SECURITY_DATA_ROOM', + 'ITEM_JINGLE', + 'LOADING_SAVE', + 'MESSAGE_POPUP', + 'SA_X_APPEARANCE', + 'SA_X_CHASE', + 'BOSS_TENSION', + 'ARACHNUS_BATTLE', + 'ZAZABI_BATTLE', + 'BOX_BATTLE', + 'MAIN_DECK_AMBIENCE', + 'UNUSED_1F', + 'UNUSED_20', + 'SILENCE_1_SHIP', + 'TENSION', + 'MAIN_DECK_LIVELY', + 'OMEGA_METROID_DEFEATED', + 'OPERATIONS_DECK', + 'OPERATIONS_DECK_ELEVATOR_OFFLINE_SOUND_AND_AMBIENCE', + 'X_INVASION_DETECTION', + 'SA_X_ELEVATOR', + 'HEADING_TO_NIGHTMARE_RIDLEY', + 'OPERATIONS_DECK_ELEVATOR_OFFLINE_SOUND', + 'OPERATIONS_DECK_ELEVATOR_OFFLINE_AMBIENCE', + 'MAIN_BOILER_COOLDOWN_MISSION', + 'STATION_ESCAPE', + 'OBJECTIVE_COMPLETE', + 'SECTOR_4_UNDERWATER', + 'SECTOR_4_UNDERWATER_UNUSED', + 'SERRIS_YAKUZA_BATTLE', + 'VARIA_CORE_X_BATTLE', + 'NIGHTMARE_BATTLE', + 'NEO_RIDLEY_BATTLE', + 'CHOZO_STATUE_CORE_X_BATTLE', + 'NETTORI_BATTLE', + 'PRE_TITLE_END', + 'TITLE', + 'SA_X_BATTLE', + 'EPILOGUE_END', + 'ENDING', + 'EPILOGUE', + 'DISQUIETING', + 'SHOCK', + 'SILENCE_1', + 'MAIN_BOILER_OVERHEATING', + 'FINAL_ORDER', + 'SILENCE_2', + 'INTRIGUE', + 'UNUSED_5E', + 'UNEASE' +] +Musicmapping: typ.TypeAlias = dict[Validmusictracks, Validmusictracks] Messagelanguages: typ.TypeAlias = dict[Validlanguages, str] class Itemmessages(typ.TypedDict, total=False): @@ -572,6 +634,9 @@ class Marsschemamf(typ.TypedDict, total=False): SeedHash: typ.Required[typ.Annotated[str, '/^[0-9A-Z]{8}$/']] """A seed hash that will be displayed on the file select screen.""" + MusicReplacement: Musicmapping + """Shuffles the in-game music.""" + Locations: typ.Required[MarsschemamfLocations] """Specifies how the item locations in the game should be changed.""" diff --git a/src/mars_patcher/mf/constants/music_library.py b/src/mars_patcher/mf/constants/music_library.py new file mode 100644 index 0000000..19df627 --- /dev/null +++ b/src/mars_patcher/mf/constants/music_library.py @@ -0,0 +1,78 @@ +from enum import IntEnum + + +# In-between spaces are null values +class MusicLibrary(IntEnum): + UNUSED_1 = 0x1 # Short track with mainly percussion + + AFTER_EVENT = 0x3 + SECTOR_1 = 0x4 + + SECTOR_2 = 0x6 + SECTOR_3 = 0x7 + SECTOR_5 = 0x8 + SECTOR_4 = 0x9 + SECTOR_6 = 0xA + NAVIGATION_ROOM = 0xB + + SECURITY_DATA_ROOM = 0xF + ITEM_JINGLE = 0x10 + LOADING_SAVE = 0x11 + + MESSAGE_POPUP = 0x14 + SA_X_APPEARANCE = 0x15 + + SA_X_CHASE = 0x17 + BOSS_TENSION = 0x18 + ARACHNUS_BATTLE = 0x19 + ZAZABI_BATTLE = 0x1A + BOX_BATTLE = 0x1B + + MAIN_DECK_AMBIENCE = 0x1E + UNUSED_1F = 0x1F # Combination of MAIN_DECK_AMBIENCE and TENSION + UNUSED_20 = 0x20 # Dramatic track combined with MAIN_DECK_AMBIENCE + + SILENCE_1_SHIP = 0x2A + TENSION = 0x2B + MAIN_DECK_LIVELY = 0x2C + OMEGA_METROID_DEFEATED = 0x2D + OPERATIONS_DECK = 0x2E + OPERATIONS_DECK_ELEVATOR_OFFLINE_SOUND_AND_AMBIENCE = 0x2F + X_INVASION_DETECTION = 0x30 + SA_X_ELEVATOR = 0x31 + HEADING_TO_NIGHTMARE_RIDLEY = 0x32 + OPERATIONS_DECK_ELEVATOR_OFFLINE_SOUND = 0x33 + OPERATIONS_DECK_ELEVATOR_OFFLINE_AMBIENCE = 0x34 + MAIN_BOILER_COOLDOWN_MISSION = 0x35 + + STATION_ESCAPE = 0x38 + + OBJECTIVE_COMPLETE = 0x3B + SECTOR_4_UNDERWATER = 0x3C + + SECTOR_4_UNDERWATER_UNUSED = 0x3E + SERRIS_YAKUZA_BATTLE = 0x3F + VARIA_CORE_X_BATTLE = 0x40 + NIGHTMARE_BATTLE = 0x41 + NEO_RIDLEY_BATTLE = 0x42 + CHOZO_STATUE_CORE_X_BATTLE = 0x43 + NETTORI_BATTLE = 0x44 + + PRE_TITLE_END = 0x49 + TITLE = 0x4A + + SA_X_BATTLE = 0x51 + + EPILOGUE_END = 0x53 + + ENDING = 0x55 + EPILOGUE = 0x56 + DISQUIETING = 0x57 + SHOCK = 0x58 + SILENCE_1 = 0x59 + MAIN_BOILER_OVERHEATING = 0x5A + FINAL_ORDER = 0x5B + SILENCE_2 = 0x5C + INTRIGUE = 0x5D + UNUSED_5E = 0x5E # Suspenseful track + UNEASE = 0x5F diff --git a/src/mars_patcher/mf/data/schema.json b/src/mars_patcher/mf/data/schema.json index 82d7582..f87519e 100644 --- a/src/mars_patcher/mf/data/schema.json +++ b/src/mars_patcher/mf/data/schema.json @@ -9,6 +9,10 @@ "type": "string", "pattern": "^[0-9A-Z]{8}$" }, + "MusicReplacement": { + "description": "Shuffles the in-game music.", + "$ref": "#/$defs/MusicMapping" + }, "Locations": { "type": "object", "description": "Specifies how the item locations in the game should be changed.", @@ -465,7 +469,7 @@ "default": null }, "TitleText": { - "type":"array", + "type": "array", "description": "Lines of ascii text to write to the title screen.", "items": { "type": "object", @@ -701,7 +705,11 @@ "maxLength": 112 } }, - "required": ["Area", "Room", "Name"] + "required": [ + "Area", + "Room", + "Name" + ] } }, "RevealHiddenTiles": { @@ -899,7 +907,6 @@ "Randovania", "ArchipelagoColor", "ArchipelagoMonochrome" - ] }, "ValidAbilities": { @@ -971,12 +978,89 @@ "Spanish" ] }, + "ValidMusicTracks": { + "type": "string", + "description": "Valid music tracks supported by the game.", + "enum": [ + "UNUSED_1", + "AFTER_EVENT", + "SECTOR_1", + "SECTOR_2", + "SECTOR_3", + "SECTOR_5", + "SECTOR_4", + "SECTOR_6", + "NAVIGATION_ROOM", + "SECURITY_DATA_ROOM", + "ITEM_JINGLE", + "LOADING_SAVE", + "MESSAGE_POPUP", + "SA_X_APPEARANCE", + "SA_X_CHASE", + "BOSS_TENSION", + "ARACHNUS_BATTLE", + "ZAZABI_BATTLE", + "BOX_BATTLE", + "MAIN_DECK_AMBIENCE", + "UNUSED_1F", + "UNUSED_20", + "SILENCE_1_SHIP", + "TENSION", + "MAIN_DECK_LIVELY", + "OMEGA_METROID_DEFEATED", + "OPERATIONS_DECK", + "OPERATIONS_DECK_ELEVATOR_OFFLINE_SOUND_AND_AMBIENCE", + "X_INVASION_DETECTION", + "SA_X_ELEVATOR", + "HEADING_TO_NIGHTMARE_RIDLEY", + "OPERATIONS_DECK_ELEVATOR_OFFLINE_SOUND", + "OPERATIONS_DECK_ELEVATOR_OFFLINE_AMBIENCE", + "MAIN_BOILER_COOLDOWN_MISSION", + "STATION_ESCAPE", + "OBJECTIVE_COMPLETE", + "SECTOR_4_UNDERWATER", + "SECTOR_4_UNDERWATER_UNUSED", + "SERRIS_YAKUZA_BATTLE", + "VARIA_CORE_X_BATTLE", + "NIGHTMARE_BATTLE", + "NEO_RIDLEY_BATTLE", + "CHOZO_STATUE_CORE_X_BATTLE", + "NETTORI_BATTLE", + "PRE_TITLE_END", + "TITLE", + "SA_X_BATTLE", + "EPILOGUE_END", + "ENDING", + "EPILOGUE", + "DISQUIETING", + "SHOCK", + "SILENCE_1", + "MAIN_BOILER_OVERHEATING", + "FINAL_ORDER", + "SILENCE_2", + "INTRIGUE", + "UNUSED_5E", + "UNEASE" + ] + }, + "MusicMapping": { + "description": "Maps music tracks to each other", + "type": "object", + "propertyNames": { + "$ref": "#/$defs/ValidMusicTracks" + }, + "additionalProperties": { + "$ref": "#/$defs/ValidMusicTracks" + } + }, "MessageLanguages": { "type": "object", "propertyNames": { "$ref": "#/$defs/ValidLanguages" }, - "required": ["English"], + "required": [ + "English" + ], "additionalProperties": { "type": "string", "description": "Specifies what text should appear for a 2 line message. Text will auto-wrap if the next word doesn't fit on the line. If the text is too long, it will be truncated. Use \n to force a line break. If not provided, a message based on the Item will be shown. If a language is not provided, it will use the provided English message." @@ -989,10 +1073,14 @@ "$ref": "#/$defs/ItemMessagesKind" } }, - "required": ["Kind"], + "required": [ + "Kind" + ], "if": { "properties": { - "Kind": {"const": "CustomMessage" } + "Kind": { + "const": "CustomMessage" + } } }, "then": { @@ -1008,7 +1096,9 @@ "default": true } }, - "required": ["Languages"], + "required": [ + "Languages" + ], "additionalProperties": false }, "else": { @@ -1023,17 +1113,25 @@ "description": "The Message ID, will display one of the predefined messages in the ROM" } }, - "required": ["MessageID"], + "required": [ + "MessageID" + ], "additionalProperties": false } }, - "ItemMessagesKind":{ + "ItemMessagesKind": { "type": "string", - "enum": ["CustomMessage", "MessageID"] + "enum": [ + "CustomMessage", + "MessageID" + ] }, - "Jingle":{ + "Jingle": { "type": "string", - "enum": ["Minor", "Major"] + "enum": [ + "Minor", + "Major" + ] }, "BlockLayer": { "type": "array", @@ -1070,4 +1168,4 @@ "default": "OPEN" } } -} +} \ No newline at end of file diff --git a/src/mars_patcher/mf/patcher.py b/src/mars_patcher/mf/patcher.py index 1a4b99e..f01e0e2 100644 --- a/src/mars_patcher/mf/patcher.py +++ b/src/mars_patcher/mf/patcher.py @@ -36,6 +36,7 @@ from mars_patcher.minimap import apply_minimap_edits from mars_patcher.random_palettes import PaletteRandomizer, PaletteSettings from mars_patcher.rom import Rom +from mars_patcher.sounds import set_sounds from mars_patcher.text import write_seed_hash from mars_patcher.titlescreen_text import write_title_text @@ -80,6 +81,11 @@ def patch_mf( # Required metroid count set_required_metroid_count(rom, patch_data["RequiredMetroidCount"]) + # Music + if "MusicReplacement" in patch_data: + status_update("Writing music...", -1) + set_sounds(rom, patch_data["MusicReplacement"]) + # Starting location if "StartingLocation" in patch_data: status_update("Writing starting location...", -1) diff --git a/src/mars_patcher/sounds.py b/src/mars_patcher/sounds.py new file mode 100644 index 0000000..ebc515d --- /dev/null +++ b/src/mars_patcher/sounds.py @@ -0,0 +1,27 @@ +from mars_patcher.common_types import MusicMapping +from mars_patcher.constants.game_data import sound_data_entries +from mars_patcher.mf.constants.music_library import MusicLibrary as MusicLibraryMF +from mars_patcher.rom import Rom +from mars_patcher.zm.constants.music_library import MusicLibrary as MusicLibraryZM + +SOUND_SIZE = 8 + + +def set_sounds(rom: Rom, data: MusicMapping) -> None: + read_data_entries = [] + + MusicLibrary: type[MusicLibraryMF] | type[MusicLibraryZM] + if rom.is_mf(): + MusicLibrary = MusicLibraryMF + elif rom.is_zm(): + MusicLibrary = MusicLibraryZM + + # Read new data + for new in data.values(): + read_location = sound_data_entries(rom) + SOUND_SIZE * MusicLibrary[new].value + read_data_entries.append(rom.read_bytes(read_location, SOUND_SIZE)) + + # Write to rom + for original, sound_data in zip(data.keys(), read_data_entries): + write_location = sound_data_entries(rom) + SOUND_SIZE * MusicLibrary[original].value + rom.write_bytes(write_location, sound_data) diff --git a/src/mars_patcher/zm/auto_generated_types.py b/src/mars_patcher/zm/auto_generated_types.py index b9b8e50..5aebd8f 100644 --- a/src/mars_patcher/zm/auto_generated_types.py +++ b/src/mars_patcher/zm/auto_generated_types.py @@ -145,6 +145,71 @@ 'ITALIAN', 'SPANISH' ] +Validmusictracks = typ.Literal[ + 'BRINSTAR', + 'TITLE_SCREEN', + 'SAVE_ELEVATOR_ROOM', + 'INTRO', + 'CHOZO_STATUE_HINT', + 'NORFAIR', + 'KRAID', + 'ESCAPE', + 'FILE_SELECT', + 'STATUE_ROOM', + 'BOSS_KILLED', + 'MAP_ROOM', + 'CHOZO_RUINS_DEPTH', + 'CHOZO_RUINS', + 'CHOZO_RUINS_LIGHT', + 'RIDLEY_IN_SPACE', + 'RIDLEY_LANDING', + 'CHOZO_STATUE_HINT_DELAY', + 'GETTING_FULLY_POWERED_SUIT_CUTSCENE', + 'ESCAPING_ZEBES_CUTSCENE', + 'CHOZO_VOICE_1', + 'CHOZO_VOICE_2', + 'BEFORE_RUINS_TEST_UNUSED', + 'ELEVATOR_ROOM', + 'BRINSTAR_REMIX', + 'ESCAPE_SUCCESFUL', + 'CREDITS', + 'STATUE_ROOM_OPENED', + 'RIDLEY', + 'KRAID_BATTLE_WITH_INTRO', + 'RIDLEY_BATTLE', + 'LOADING_JINGLE', + 'GETTING_ITEM_JINGLE', + 'INTRO_MOTHER_BRAIN', + 'GETTING_TANK_JINGLE', + 'TOURIAN', + 'WORMS_BATTLE', + 'MOTHER_BRAIN_BATTLE', + 'CATTERPILLARS_BATTLE', + 'IMAGO_COCOON_BATTLE', + 'IMAGO_BATTLE', + 'MECHA_RIDLEY_BATTLE', + 'GETTING_UNKNOWN_ITEM_JINGLE', + 'RUINS_TEST_BATTLE_WITH_INTRO', + 'ENTERING_TOURIAN_CUTSCENE', + 'ALARM_ACTIVATED', + 'STEALTH', + 'ENTERING_NORFAIR_CUTSCENE', + 'CHOZODIA_DETECTED', + 'GETTING_FULLY_POWERED_SUIT_JINGLE', + 'KRAID_BATTLE', + 'RIDLEY_BATTLE_2', + 'MECHA_RIDLEY_BATTLE_2', + 'RUINS_TEST_BATTLE', + 'CATTERPILLARS_BATTLE_2', + 'CRATERIA', + 'GAME_OVER', + 'CHOZODIA_SURFACE', + 'MAP_ROOM_2', + 'SAVE_ELEVATOR_ROOM_2', + 'BEFORE_RUINS_TEST_ROOM', + 'STEALTH_2' +] +Musicmapping: typ.TypeAlias = dict[Validmusictracks, Validmusictracks] MessageLanguages: typ.TypeAlias = dict[ValidLanguages, str] class ItemMessages(typ.TypedDict, total=False): @@ -482,6 +547,9 @@ class Marsschemazm(typ.TypedDict, total=False): palettes: MarsschemazmPalettes = None """Properties for randomized in-game palettes.""" + MusicReplacement: Musicmapping + """Shuffles the in-game music.""" + intro_text: dict[ValidLanguages, str] = None """Specifies what text should appear during the new game intro.""" diff --git a/src/mars_patcher/zm/constants/music_library.py b/src/mars_patcher/zm/constants/music_library.py new file mode 100644 index 0000000..501a945 --- /dev/null +++ b/src/mars_patcher/zm/constants/music_library.py @@ -0,0 +1,73 @@ +from enum import IntEnum + + +# In-between spaces are null values +class MusicLibrary(IntEnum): + BRINSTAR = 0x1 + TITLE_SCREEN = 0x2 + SAVE_ELEVATOR_ROOM = 0x3 + INTRO = 0x4 + CHOZO_STATUE_HINT = 0x5 + NORFAIR = 0x6 + KRAID = 0x7 + ESCAPE = 0x8 + FILE_SELECT = 0x9 + STATUE_ROOM = 0xA + BOSS_KILLED = 0xB + MAP_ROOM = 0xC + CHOZO_RUINS_DEPTH = 0xD + CHOZO_RUINS = 0xE + CHOZO_RUINS_LIGHT = 0xF + RIDLEY_IN_SPACE = 0x10 + RIDLEY_LANDING = 0x11 + CHOZO_STATUE_HINT_DELAY = 0x12 + GETTING_FULLY_POWERED_SUIT_CUTSCENE = 0x13 + ESCAPING_ZEBES_CUTSCENE = 0x14 + CHOZO_VOICE_1 = 0x15 + CHOZO_VOICE_2 = 0x16 + BEFORE_RUINS_TEST_UNUSED = 0x17 + ELEVATOR_ROOM = 0x18 + BRINSTAR_REMIX = 0x19 + ESCAPE_SUCCESFUL = 0x1A + CREDITS = 0x1B + STATUE_ROOM_OPENED = 0x1C + + RIDLEY = 0x32 + + KRAID_BATTLE_WITH_INTRO = 0x34 + RIDLEY_BATTLE = 0x35 + LOADING_JINGLE = 0x36 + GETTING_ITEM_JINGLE = 0x37 + + INTRO_MOTHER_BRAIN = 0x39 + GETTING_TANK_JINGLE = 0x3A + TOURIAN = 0x3B + WORMS_BATTLE = 0x3C + MOTHER_BRAIN_BATTLE = 0x3D + CATTERPILLARS_BATTLE = 0x3E + IMAGO_COCOON_BATTLE = 0x3F + IMAGO_BATTLE = 0x40 + MECHA_RIDLEY_BATTLE = 0x41 + GETTING_UNKNOWN_ITEM_JINGLE = 0x42 + RUINS_TEST_BATTLE_WITH_INTRO = 0x43 + ENTERING_TOURIAN_CUTSCENE = 0x44 + ALARM_ACTIVATED = 0x45 + STEALTH = 0x46 + + ENTERING_NORFAIR_CUTSCENE = 0x48 + CHOZODIA_DETECTED = 0x49 + GETTING_FULLY_POWERED_SUIT_JINGLE = 0x4A + KRAID_BATTLE = 0x4B + RIDLEY_BATTLE_2 = 0x4C + MECHA_RIDLEY_BATTLE_2 = 0x4D + RUINS_TEST_BATTLE = 0x4E + CATTERPILLARS_BATTLE_2 = 0x4F + CRATERIA = 0x50 + + GAME_OVER = 0x53 + + CHOZODIA_SURFACE = 0x5A + MAP_ROOM_2 = 0x5B + SAVE_ELEVATOR_ROOM_2 = 0x5C + BEFORE_RUINS_TEST_ROOM = 0x5D + STEALTH_2 = 0x5E diff --git a/src/mars_patcher/zm/data/schema.json b/src/mars_patcher/zm/data/schema.json index adb6547..5511f32 100644 --- a/src/mars_patcher/zm/data/schema.json +++ b/src/mars_patcher/zm/data/schema.json @@ -367,6 +367,10 @@ ], "default": null }, + "MusicReplacement": { + "description": "Shuffles the in-game music.", + "$ref": "#/$defs/MusicMapping" + }, "intro_text": { "type": "object", "description": "Specifies what text should appear during the new game intro.", @@ -572,7 +576,11 @@ "maxLength": 112 } }, - "required": ["area", "room", "name"] + "required": [ + "area", + "room", + "name" + ] } }, "reveal_hidden_tiles": { @@ -785,12 +793,92 @@ "SPANISH" ] }, + "ValidMusicTracks": { + "type": "string", + "description": "Valid music tracks supported by the game.", + "enum": [ + "BRINSTAR", + "TITLE_SCREEN", + "SAVE_ELEVATOR_ROOM", + "INTRO", + "CHOZO_STATUE_HINT", + "NORFAIR", + "KRAID", + "ESCAPE", + "FILE_SELECT", + "STATUE_ROOM", + "BOSS_KILLED", + "MAP_ROOM", + "CHOZO_RUINS_DEPTH", + "CHOZO_RUINS", + "CHOZO_RUINS_LIGHT", + "RIDLEY_IN_SPACE", + "RIDLEY_LANDING", + "CHOZO_STATUE_HINT_DELAY", + "GETTING_FULLY_POWERED_SUIT_CUTSCENE", + "ESCAPING_ZEBES_CUTSCENE", + "CHOZO_VOICE_1", + "CHOZO_VOICE_2", + "BEFORE_RUINS_TEST_UNUSED", + "ELEVATOR_ROOM", + "BRINSTAR_REMIX", + "ESCAPE_SUCCESFUL", + "CREDITS", + "STATUE_ROOM_OPENED", + "RIDLEY", + "KRAID_BATTLE_WITH_INTRO", + "RIDLEY_BATTLE", + "LOADING_JINGLE", + "GETTING_ITEM_JINGLE", + "INTRO_MOTHER_BRAIN", + "GETTING_TANK_JINGLE", + "TOURIAN", + "WORMS_BATTLE", + "MOTHER_BRAIN_BATTLE", + "CATTERPILLARS_BATTLE", + "IMAGO_COCOON_BATTLE", + "IMAGO_BATTLE", + "MECHA_RIDLEY_BATTLE", + "GETTING_UNKNOWN_ITEM_JINGLE", + "RUINS_TEST_BATTLE_WITH_INTRO", + "ENTERING_TOURIAN_CUTSCENE", + "ALARM_ACTIVATED", + "STEALTH", + "ENTERING_NORFAIR_CUTSCENE", + "CHOZODIA_DETECTED", + "GETTING_FULLY_POWERED_SUIT_JINGLE", + "KRAID_BATTLE", + "RIDLEY_BATTLE_2", + "MECHA_RIDLEY_BATTLE_2", + "RUINS_TEST_BATTLE", + "CATTERPILLARS_BATTLE_2", + "CRATERIA", + "GAME_OVER", + "CHOZODIA_SURFACE", + "MAP_ROOM_2", + "SAVE_ELEVATOR_ROOM_2", + "BEFORE_RUINS_TEST_ROOM", + "STEALTH_2" + ] + }, + "MusicMapping": { + "description": "Maps music tracks to each other", + "type": "object", + "propertyNames": { + "$ref": "#/$defs/ValidMusicTracks" + }, + "additionalProperties": { + "$ref": "#/$defs/ValidMusicTracks" + } + }, "message_languages": { "type": "object", "propertyNames": { "$ref": "#/$defs/valid_languages" }, - "required": ["ENGLISH"], + "required": [ + "ENGLISH" + ], "additionalProperties": { "type": "string", "description": "Specifies what text should appear for a 2 line message. Text will auto-wrap if the next word doesn't fit on the line. If the text is too long, it will be truncated. Use \n to force a line break. If not provided, a message based on the Item will be shown. If a language is not provided, it will use the provided English message." @@ -803,10 +891,14 @@ "$ref": "#/$defs/item_messages_kind" } }, - "required": ["kind"], + "required": [ + "kind" + ], "if": { "properties": { - "kind": {"const": "CUSTOM_MESSAGE" } + "kind": { + "const": "CUSTOM_MESSAGE" + } } }, "then": { @@ -822,7 +914,9 @@ "default": true } }, - "required": ["languages"], + "required": [ + "languages" + ], "additionalProperties": false }, "else": { @@ -837,18 +931,29 @@ "description": "The Message ID, will display one of the predefined messages in the ROM" } }, - "required": ["message_id"], + "required": [ + "message_id" + ], "additionalProperties": false } }, "item_messages_kind": { "type": "string", - "enum": ["CUSTOM_MESSAGE", "MESSAGE_ID"] + "enum": [ + "CUSTOM_MESSAGE", + "MESSAGE_ID" + ] }, "jingle": { "type": "string", "description": "The sound that plays when an item is collected", - "enum": ["DEFAULT", "MINOR", "MAJOR", "UNKNOWN", "FULLY_POWERED"] + "enum": [ + "DEFAULT", + "MINOR", + "MAJOR", + "UNKNOWN", + "FULLY_POWERED" + ] }, "hint_locations": { "type": "string", @@ -886,4 +991,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/mars_patcher/zm/patcher.py b/src/mars_patcher/zm/patcher.py index e41a0b1..03dec62 100644 --- a/src/mars_patcher/zm/patcher.py +++ b/src/mars_patcher/zm/patcher.py @@ -2,6 +2,7 @@ from os import PathLike from mars_patcher.rom import Rom +from mars_patcher.sounds import set_sounds from mars_patcher.zm.auto_generated_types import MarsSchemaZM from mars_patcher.zm.constants.game_data import skip_door_transitions_addr from mars_patcher.zm.item_patcher import ItemPatcher, set_tank_increments @@ -55,6 +56,11 @@ def patch_zm( # status_update("Writing starting items...", -1) # set_starting_items(rom, patch_data["StartingItems"]) + # Music + if "MusicReplacement" in patch_data: + status_update("Writing music...", -1) + set_sounds(rom, patch_data["MusicReplacement"]) + # Tank increments if "tank_increments" in patch_data: status_update("Writing tank increments...", -1)