diff --git a/src/mars_patcher/auto_generated_types.py b/src/mars_patcher/auto_generated_types.py index 9d08ef0..8b004a2 100644 --- a/src/mars_patcher/auto_generated_types.py +++ b/src/mars_patcher/auto_generated_types.py @@ -182,8 +182,16 @@ Messagelanguages: typ.TypeAlias = dict[Validlanguages, str] class Itemmessages(typ.TypedDict, total=False): + Kind: typ.Required[Itemmessageskind] Languages: Messagelanguages Centered: bool = True + MessageID: typ.Annotated[int, '0 <= value <= 56'] + """The Message ID, will display one of the predefined messages in the ROM""" + +Itemmessageskind = typ.Literal[ + 'CustomMessage', + 'MessageID' +] class BlocklayerItem(typ.TypedDict, total=False): X: Typeu8 diff --git a/src/mars_patcher/constants/items.py b/src/mars_patcher/constants/items.py index 83a69d9..8fcb4c0 100644 --- a/src/mars_patcher/constants/items.py +++ b/src/mars_patcher/constants/items.py @@ -111,8 +111,15 @@ class ItemSprite(Enum): KEY_ITEM: Final = "Item" KEY_ITEM_SPRITE: Final = "ItemSprite" KEY_ITEM_MESSAGES: Final = "ItemMessages" +KEY_ITEM_MESSAGES_KIND: Final = "Kind" KEY_LANGUAGES: Final = "Languages" KEY_CENTERED: Final = "Centered" +KEY_MESSAGE_ID: Final = "MessageID" + + +class ItemMessagesKind(Enum): + CUSTOM_MESSAGE = 0 + MESSAGE_ID = 1 SOURCE_ENUMS = { diff --git a/src/mars_patcher/constants/reserved_space.py b/src/mars_patcher/constants/reserved_space.py index 0abed21..6a82763 100644 --- a/src/mars_patcher/constants/reserved_space.py +++ b/src/mars_patcher/constants/reserved_space.py @@ -9,7 +9,7 @@ class ReservedConstants: # Pointers, offset by language value, that store the message table location MESSAGE_TABLE_LOOKUP_ADDR = 0x79CDF4 - FIRST_CUSTOM_MESSAGE_ID = 0x38 # The first 0x37 messages are reserved for standard messages + FIRST_CUSTOM_MESSAGE_ID = 0x39 # The first 0x38 messages are reserved for standard messages PATCHER_FREE_SPACE_ADDR = 0x7D0000 PATCHER_FREE_SPACE_END = PATCHER_FREE_SPACE_ADDR + 0x20000 diff --git a/src/mars_patcher/data/schema.json b/src/mars_patcher/data/schema.json index cd20c78..14bb9df 100644 --- a/src/mars_patcher/data/schema.json +++ b/src/mars_patcher/data/schema.json @@ -922,15 +922,52 @@ "ItemMessages": { "type": "object", "properties": { - "Languages": { - "$ref": "#/$defs/MessageLanguages" - }, - "Centered": { - "type": "boolean", - "default": true + "Kind": { + "$ref": "#/$defs/ItemMessagesKind" } + }, + "required": ["Kind"], + "if": { + "properties": { + "Kind": {"const": "CustomMessage" } + } + }, + "then": { + "properties": { + "Kind": { + "$ref": "#/$defs/ItemMessagesKind" + }, + "Languages": { + "$ref": "#/$defs/MessageLanguages" + }, + "Centered": { + "type": "boolean", + "default": true + } + }, + "required": ["Languages"], + "additionalProperties": false + }, + "else": { + "properties": { + "Kind": { + "$ref": "#/$defs/ItemMessagesKind" + }, + "MessageID": { + "type": "integer", + "minimum": 0, + "maximum": 56, + "description": "The Message ID, will display one of the predefined messages in the ROM" + } + }, + "required": ["MessageID"], + "additionalProperties": false } }, + "ItemMessagesKind":{ + "type": "string", + "enum": ["CustomMessage", "MessageID"] + }, "BlockLayer": { "type": "array", "uniqueItems": true, diff --git a/src/mars_patcher/item_patcher.py b/src/mars_patcher/item_patcher.py index 4c257bb..2684c8f 100644 --- a/src/mars_patcher/item_patcher.py +++ b/src/mars_patcher/item_patcher.py @@ -1,6 +1,12 @@ from mars_patcher.auto_generated_types import MarsschemaTankincrements from mars_patcher.constants.reserved_space import ReservedConstants -from mars_patcher.locations import ItemMessages, ItemSprite, ItemType, LocationSettings +from mars_patcher.locations import ( + ItemMessages, + ItemMessagesKind, + ItemSprite, + ItemType, + LocationSettings, +) from mars_patcher.rom import Rom from mars_patcher.room_entry import RoomEntry from mars_patcher.text import Language, MessageType, encode_text @@ -130,23 +136,27 @@ def write_items(self) -> None: rom.write_8(item_addr + 5, min_loc.new_item.value) if min_loc.item_sprite != ItemSprite.UNCHANGED: rom.write_8(item_addr + 6, min_loc.item_sprite.value) - # Handle custom messages + # Handle item messages if min_loc.item_messages is not None: - # If we already encountered the message before, just write the message id. messages = min_loc.item_messages - if messages in item_messages_to_custom_id: - rom.write_8(item_addr + 7, item_messages_to_custom_id[messages]) + # If the kind is Custom Message, write the message to free space, and set the ID + if messages.kind == ItemMessagesKind.CUSTOM_MESSAGE: + # If we already encountered the message before, write the existing message id. + if messages in item_messages_to_custom_id: + rom.write_8(item_addr + 7, item_messages_to_custom_id[messages]) + else: + self.write_custom_message( + custom_message_id, + message_table_addrs, + item_addr, + min_loc.item_messages, + False, + ) + item_messages_to_custom_id[messages] = custom_message_id + custom_message_id += 1 + # If the kind is Message ID, write that ID else: - self.write_custom_message( - custom_message_id, - message_table_addrs, - item_addr, - min_loc.item_messages, - False, - ) - item_messages_to_custom_id[messages] = custom_message_id - custom_message_id += 1 - + rom.write_8(item_addr + 7, messages.message_id) # Handle major locations for maj_loc in self.settings.major_locs: # Write to majors table @@ -155,22 +165,27 @@ def write_items(self) -> None: total_metroids += 1 addr = MAJOR_LOCS_ADDR + (maj_loc.major_src.value * MAJOR_LOC_SIZE) rom.write_8(addr, maj_loc.new_item.value) - # Handle custom messages + # Handle item messages if maj_loc.item_messages is not None: - # If we already encountered the message before, just write the message id. messages = maj_loc.item_messages - if messages in item_messages_to_custom_id: - rom.write_8(addr + 1, item_messages_to_custom_id[messages]) + # If the kind is Custom Message, write the message to free space, and set the ID + if messages.kind == ItemMessagesKind.CUSTOM_MESSAGE: + # If we already encountered the message before, write the existing id. + if messages in item_messages_to_custom_id: + rom.write_8(addr + 1, item_messages_to_custom_id[messages]) + else: + self.write_custom_message( + custom_message_id, + message_table_addrs, + addr, + maj_loc.item_messages, + True, + ) + item_messages_to_custom_id[messages] = custom_message_id + custom_message_id += 1 + # If the kind is Message ID, write that ID else: - self.write_custom_message( - custom_message_id, - message_table_addrs, - addr, - maj_loc.item_messages, - True, - ) - item_messages_to_custom_id[messages] = custom_message_id - custom_message_id += 1 + rom.write_8(addr + 1, messages.message_id) # Write total metroid count rom.write_8(TOTAL_METROID_COUNT_ADDR, total_metroids) diff --git a/src/mars_patcher/locations.py b/src/mars_patcher/locations.py index 244bf27..4dc1fbc 100644 --- a/src/mars_patcher/locations.py +++ b/src/mars_patcher/locations.py @@ -16,14 +16,17 @@ KEY_HIDDEN, KEY_ITEM, KEY_ITEM_MESSAGES, + KEY_ITEM_MESSAGES_KIND, KEY_ITEM_SPRITE, KEY_LANGUAGES, KEY_MAJOR_LOCS, + KEY_MESSAGE_ID, KEY_MINOR_LOCS, KEY_ORIGINAL, KEY_ROOM, KEY_SOURCE, SOURCE_ENUMS, + ItemMessagesKind, ItemSprite, ItemType, MajorSource, @@ -91,8 +94,10 @@ def __init__( @dataclass(frozen=True) class ItemMessages: + kind: ItemMessagesKind item_messages: frozendict[Language, str] centered: bool + message_id: int LANG_ENUMS: ClassVar[dict[str, Language]] = { "JapaneseKanji": Language.JAPANESE_KANJI, @@ -104,14 +109,26 @@ class ItemMessages: "Spanish": Language.SPANISH, } + KIND_ENUMS: ClassVar[dict[str, ItemMessagesKind]] = { + "CustomMessage": ItemMessagesKind.CUSTOM_MESSAGE, + "MessageID": ItemMessagesKind.MESSAGE_ID, + } + @classmethod def from_json(cls, data: Itemmessages) -> ItemMessages: item_messages: dict[Language, str] = {} - for lang_name, message in data[KEY_LANGUAGES].items(): - lang = cls.LANG_ENUMS[lang_name] - item_messages[lang] = message - centered = data.get(KEY_CENTERED, True) - return cls(frozendict(item_messages), centered) + centered = True + kind: ItemMessagesKind = cls.KIND_ENUMS[data[KEY_ITEM_MESSAGES_KIND]] + message_id = 0 + if kind == ItemMessagesKind.CUSTOM_MESSAGE: + for lang_name, message in data[KEY_LANGUAGES].items(): + lang = cls.LANG_ENUMS[lang_name] + item_messages[lang] = message + centered = data.get(KEY_CENTERED, True) + else: + message_id = data[KEY_MESSAGE_ID] + + return cls(kind, frozendict(item_messages), centered, message_id) class LocationSettings: