SM: Fix unobtainable items in remote items+item links combo (#1151)
* SM: fix using item links together with remote items * SM: write 0 index for excess player ids * some style and minor fixes (strotlog/Archipelago#1) * more typing in SM patching Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com>
This commit is contained in:
parent
802119502d
commit
655f287d42
|
@ -4,7 +4,7 @@ import time
|
|||
|
||||
from NetUtils import ClientStatus, color
|
||||
from worlds.AutoSNIClient import SNIClient
|
||||
from .Rom import ROM_PLAYER_LIMIT as SM_ROM_PLAYER_LIMIT
|
||||
from .Rom import SM_ROM_MAX_PLAYERID
|
||||
|
||||
snes_logger = logging.getLogger("SNES")
|
||||
|
||||
|
@ -143,7 +143,7 @@ class SMSNIClient(SNIClient):
|
|||
else:
|
||||
location_id = 0x00 #backward compat
|
||||
|
||||
player_id = item.player if item.player <= SM_ROM_PLAYER_LIMIT else 0
|
||||
player_id = item.player if item.player <= SM_ROM_MAX_PLAYERID else 0
|
||||
snes_buffered_write(ctx, SM_RECV_QUEUE_START + item_out_ptr * 4, bytes(
|
||||
[player_id & 0xFF, (player_id >> 8) & 0xFF, item_id & 0xFF, location_id & 0xFF]))
|
||||
item_out_ptr += 1
|
||||
|
|
|
@ -7,8 +7,8 @@ from Utils import read_snes_rom
|
|||
from worlds.Files import APDeltaPatch
|
||||
|
||||
SMJUHASH = '21f3e98df4780ee1c667b84e57d88675'
|
||||
ROM_PLAYER_LIMIT = 65535 # max archipelago player ID. note, SM ROM itself will only store 201 names+ids max
|
||||
|
||||
SM_ROM_MAX_PLAYERID = 65535
|
||||
SM_ROM_PLAYERDATA_COUNT = 202
|
||||
|
||||
class SMDeltaPatch(APDeltaPatch):
|
||||
hash = SMJUHASH
|
||||
|
|
|
@ -5,7 +5,7 @@ import copy
|
|||
import os
|
||||
import threading
|
||||
import base64
|
||||
from typing import Set, TextIO
|
||||
from typing import Any, Dict, Iterable, List, Set, TextIO, TypedDict
|
||||
|
||||
from worlds.sm.variaRandomizer.graph.graph_utils import GraphUtils
|
||||
|
||||
|
@ -15,7 +15,7 @@ from .Regions import create_regions
|
|||
from .Rules import set_rules, add_entrance_rule
|
||||
from .Options import sm_options
|
||||
from .Client import SMSNIClient
|
||||
from .Rom import get_base_rom_path, ROM_PLAYER_LIMIT, SMDeltaPatch, get_sm_symbols
|
||||
from .Rom import get_base_rom_path, SM_ROM_MAX_PLAYERID, SM_ROM_PLAYERDATA_COUNT, SMDeltaPatch, get_sm_symbols
|
||||
import Utils
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, ItemClassification, RegionType, CollectionState, Tutorial
|
||||
|
@ -67,6 +67,13 @@ class SMWeb(WebWorld):
|
|||
["Farrak Kilhn"]
|
||||
)]
|
||||
|
||||
|
||||
class ByteEdit(TypedDict):
|
||||
sym: Dict[str, Any]
|
||||
offset: int
|
||||
values: Iterable[int]
|
||||
|
||||
|
||||
locations_start_id = 82000
|
||||
items_start_id = 83000
|
||||
|
||||
|
@ -201,7 +208,8 @@ class SMWorld(World):
|
|||
create_locations(self, self.player)
|
||||
create_regions(self, self.multiworld, self.player)
|
||||
|
||||
def getWordArray(self, w): # little-endian convert a 16-bit number to an array of numbers <= 255 each
|
||||
def getWordArray(self, w: int) -> List[int]:
|
||||
""" little-endian convert a 16-bit number to an array of numbers <= 255 each """
|
||||
return [w & 0x00FF, (w & 0xFF00) >> 8]
|
||||
|
||||
# used for remote location Credits Spoiler of local items
|
||||
|
@ -283,46 +291,85 @@ class SMWorld(World):
|
|||
def APPostPatchRom(self, romPatcher):
|
||||
symbols = get_sm_symbols(os.path.join(os.path.dirname(__file__),
|
||||
"data", "SMBasepatch_prebuilt", "sm-basepatch-symbols.json"))
|
||||
multiWorldLocations = []
|
||||
multiWorldItems = []
|
||||
|
||||
# gather all player ids and names relevant to this rom, then write player name and player id data tables
|
||||
playerIdSet: Set[int] = {0} # 0 is for "Archipelago" server
|
||||
for itemLoc in self.multiworld.get_locations():
|
||||
assert itemLoc.item, f"World of player '{self.multiworld.player_name[itemLoc.player]}' has a loc.item " + \
|
||||
f"that is {itemLoc.item} during generate_output"
|
||||
# add each playerid who has a location containing an item to send to us *or* to an item_link we're part of
|
||||
if itemLoc.item.player == self.player or \
|
||||
(itemLoc.item.player in self.multiworld.groups and
|
||||
self.player in self.multiworld.groups[itemLoc.item.player]['players']):
|
||||
playerIdSet |= {itemLoc.player}
|
||||
# add each playerid, including item link ids, that we'll be sending items to
|
||||
if itemLoc.player == self.player:
|
||||
playerIdSet |= {itemLoc.item.player}
|
||||
if len(playerIdSet) > SM_ROM_PLAYERDATA_COUNT:
|
||||
# max 202 entries, but it's possible for item links to add enough replacement items for us, that are placed
|
||||
# in worlds that otherwise have no relation to us, that the 2*location count limit is exceeded
|
||||
logger.warning("SM is interacting with too many players to fit in ROM. "
|
||||
f"Removing the highest {len(playerIdSet) - SM_ROM_PLAYERDATA_COUNT} ids to fit")
|
||||
playerIdSet = set(sorted(playerIdSet)[:SM_ROM_PLAYERDATA_COUNT])
|
||||
otherPlayerIndex: Dict[int, int] = {} # ap player id -> rom-local player index
|
||||
playerNameData: List[ByteEdit] = []
|
||||
playerIdData: List[ByteEdit] = []
|
||||
# sort all player data by player id so that the game can look up a player's data reasonably quickly when
|
||||
# the client sends an ap playerid to the game
|
||||
for i, playerid in enumerate(sorted(playerIdSet)):
|
||||
playername = self.multiworld.player_name[playerid] if playerid != 0 else "Archipelago"
|
||||
playerIdForRom = playerid
|
||||
if playerid > SM_ROM_MAX_PLAYERID:
|
||||
# note, playerIdForRom = 0 is not unique so the game cannot look it up.
|
||||
# instead it will display the player received-from as "Archipelago"
|
||||
playerIdForRom = 0
|
||||
if playerid == self.player:
|
||||
raise Exception(f"SM rom cannot fit enough bits to represent self player id {playerid}")
|
||||
else:
|
||||
logger.warning(f"SM rom cannot fit enough bits to represent player id {playerid}, setting to 0 in rom")
|
||||
otherPlayerIndex[playerid] = i
|
||||
playerNameData.append({"sym": symbols["rando_player_name_table"],
|
||||
"offset": i * 16,
|
||||
"values": playername[:16].upper().center(16).encode()})
|
||||
playerIdData.append({"sym": symbols["rando_player_id_table"],
|
||||
"offset": i * 2,
|
||||
"values": self.getWordArray(playerIdForRom)})
|
||||
|
||||
multiWorldLocations: List[ByteEdit] = []
|
||||
multiWorldItems: List[ByteEdit] = []
|
||||
idx = 0
|
||||
self.playerIDMap = {}
|
||||
playerIDCount = 0 # 0 is for "Archipelago" server; highest possible = 200 (201 entries)
|
||||
vanillaItemTypesCount = 21
|
||||
for itemLoc in self.multiworld.get_locations():
|
||||
if itemLoc.player == self.player and locationsDict[itemLoc.name].Id != None:
|
||||
# this SM world can find this item: write full item data to tables and assign player data for writing
|
||||
romPlayerID = itemLoc.item.player if itemLoc.item.player <= ROM_PLAYER_LIMIT else 0
|
||||
# item to place in this SM world: write full item data to tables
|
||||
if isinstance(itemLoc.item, SMItem) and itemLoc.item.type in ItemManager.Items:
|
||||
itemId = ItemManager.Items[itemLoc.item.type].Id
|
||||
else:
|
||||
itemId = ItemManager.Items['ArchipelagoItem'].Id + idx
|
||||
itemId = ItemManager.Items["ArchipelagoItem"].Id + idx
|
||||
multiWorldItems.append({"sym": symbols["message_item_names"],
|
||||
"offset": (vanillaItemTypesCount + idx)*64,
|
||||
"values": self.convertToROMItemName(itemLoc.item.name)})
|
||||
idx += 1
|
||||
|
||||
if (romPlayerID > 0 and romPlayerID not in self.playerIDMap.keys()):
|
||||
playerIDCount += 1
|
||||
self.playerIDMap[romPlayerID] = playerIDCount
|
||||
if itemLoc.item.player == self.player:
|
||||
itemDestinationType = 0 # dest type 0 means 'regular old SM item' per itemtable.asm
|
||||
elif itemLoc.item.player in self.multiworld.groups and \
|
||||
self.player in self.multiworld.groups[itemLoc.item.player]['players']:
|
||||
# dest type 2 means 'SM item link item that sends to the current player and others'
|
||||
# per itemtable.asm (groups are synonymous with item_links, currently)
|
||||
itemDestinationType = 2
|
||||
else:
|
||||
itemDestinationType = 1 # dest type 1 means 'item for entirely someone else' per itemtable.asm
|
||||
|
||||
[w0, w1] = self.getWordArray(0 if itemLoc.item.player == self.player else 1)
|
||||
[w0, w1] = self.getWordArray(itemDestinationType)
|
||||
[w2, w3] = self.getWordArray(itemId)
|
||||
[w4, w5] = self.getWordArray(romPlayerID)
|
||||
[w4, w5] = self.getWordArray(otherPlayerIndex[itemLoc.item.player] if itemLoc.item.player in
|
||||
otherPlayerIndex else 0)
|
||||
[w6, w7] = self.getWordArray(0 if itemLoc.item.advancement else 1)
|
||||
multiWorldLocations.append({"sym": symbols["rando_item_table"],
|
||||
"offset": locationsDict[itemLoc.name].Id*8,
|
||||
"values": [w0, w1, w2, w3, w4, w5, w6, w7]})
|
||||
|
||||
elif itemLoc.item.player == self.player:
|
||||
# this SM world owns the item: so in case the sending player might not have anything placed in this
|
||||
# world to receive from it, assign them space in playerIDMap so that the ROM can display their name
|
||||
# (SM item name not needed, as SM item type id will be in the message they send to this world live)
|
||||
romPlayerID = itemLoc.player if itemLoc.player <= ROM_PLAYER_LIMIT else 0
|
||||
if (romPlayerID > 0 and romPlayerID not in self.playerIDMap.keys()):
|
||||
playerIDCount += 1
|
||||
self.playerIDMap[romPlayerID] = playerIDCount
|
||||
|
||||
itemSprites = [{"fileName": "off_world_prog_item.bin",
|
||||
"paletteSymbolName": "prog_item_eight_palette_indices",
|
||||
"dataSymbolName": "offworld_graphics_data_progression_item"},
|
||||
|
@ -331,7 +378,7 @@ class SMWorld(World):
|
|||
"paletteSymbolName": "nonprog_item_eight_palette_indices",
|
||||
"dataSymbolName": "offworld_graphics_data_item"}]
|
||||
idx = 0
|
||||
offworldSprites = []
|
||||
offworldSprites: List[ByteEdit] = []
|
||||
for itemSprite in itemSprites:
|
||||
with open(os.path.join(os.path.dirname(__file__), "data", "custom_sprite", itemSprite["fileName"]), 'rb') as stream:
|
||||
buffer = bytearray(stream.read())
|
||||
|
@ -343,31 +390,21 @@ class SMWorld(World):
|
|||
"values": buffer[8:264]})
|
||||
idx += 1
|
||||
|
||||
deathLink = [{"sym": symbols["config_deathlink"],
|
||||
deathLink: List[ByteEdit] = [{
|
||||
"sym": symbols["config_deathlink"],
|
||||
"offset": 0,
|
||||
"values": [self.multiworld.death_link[self.player].value]}]
|
||||
remoteItem = [{"sym": symbols["config_remote_items"],
|
||||
"values": [self.multiworld.death_link[self.player].value]
|
||||
}]
|
||||
remoteItem: List[ByteEdit] = [{
|
||||
"sym": symbols["config_remote_items"],
|
||||
"offset": 0,
|
||||
"values": self.getWordArray(0b001 + (0b010 if self.remote_items else 0b000))}]
|
||||
ownPlayerId = [{"sym": symbols["config_player_id"],
|
||||
"values": self.getWordArray(0b001 + (0b010 if self.remote_items else 0b000))
|
||||
}]
|
||||
ownPlayerId: List[ByteEdit] = [{
|
||||
"sym": symbols["config_player_id"],
|
||||
"offset": 0,
|
||||
"values": self.getWordArray(self.player)}]
|
||||
|
||||
playerNames = []
|
||||
playerNameIDMap = []
|
||||
playerNames.append({"sym": symbols["rando_player_table"],
|
||||
"offset": 0,
|
||||
"values": "Archipelago".upper().center(16).encode()})
|
||||
playerNameIDMap.append({"sym": symbols["rando_player_id_table"],
|
||||
"offset": 0,
|
||||
"values": self.getWordArray(0)})
|
||||
for key,value in self.playerIDMap.items():
|
||||
playerNames.append({"sym": symbols["rando_player_table"],
|
||||
"offset": value * 16,
|
||||
"values": self.multiworld.player_name[key][:16].upper().center(16).encode()})
|
||||
playerNameIDMap.append({"sym": symbols["rando_player_id_table"],
|
||||
"offset": value * 2,
|
||||
"values": self.getWordArray(key)})
|
||||
"values": self.getWordArray(self.player)
|
||||
}]
|
||||
|
||||
patchDict = { 'MultiWorldLocations': multiWorldLocations,
|
||||
'MultiWorldItems': multiWorldItems,
|
||||
|
@ -375,15 +412,15 @@ class SMWorld(World):
|
|||
'deathLink': deathLink,
|
||||
'remoteItem': remoteItem,
|
||||
'ownPlayerId': ownPlayerId,
|
||||
'PlayerName': playerNames,
|
||||
'PlayerNameIDMap': playerNameIDMap}
|
||||
'playerNameData': playerNameData,
|
||||
'playerIdData': playerIdData}
|
||||
|
||||
# convert an array of symbolic byte_edit dicts like {"sym": symobj, "offset": 0, "values": [1, 0]}
|
||||
# to a single rom patch dict like {0x438c: [1, 0], 0xa4a5: [0, 0, 0]} which varia will understand and apply
|
||||
def resolve_symbols_to_file_offset_based_dict(byte_edits_arr) -> dict:
|
||||
this_patch_as_dict = {}
|
||||
def resolve_symbols_to_file_offset_based_dict(byte_edits_arr: List[ByteEdit]) -> Dict[int, Iterable[int]]:
|
||||
this_patch_as_dict: Dict[int, Iterable[int]] = {}
|
||||
for byte_edit in byte_edits_arr:
|
||||
offset_within_rom_file = byte_edit["sym"]["offset_within_rom_file"] + byte_edit["offset"]
|
||||
offset_within_rom_file: int = byte_edit["sym"]["offset_within_rom_file"] + byte_edit["offset"]
|
||||
this_patch_as_dict[offset_within_rom_file] = byte_edit["values"]
|
||||
return this_patch_as_dict
|
||||
|
||||
|
@ -499,7 +536,7 @@ class SMWorld(World):
|
|||
|
||||
itemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.multiworld.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.multiworld.get_locations() if itemLoc.item.player == self.player]
|
||||
progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.multiworld.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.multiworld.get_locations() if itemLoc.item.player == self.player and itemLoc.item.advancement == True]
|
||||
# progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type if itemLoc.item.type in ItemManager.Items else 'ArchipelagoItem'], locationsDict[itemLoc.name], True) for itemLoc in self.world.get_locations() if itemLoc.player == self.player and itemLoc.item.player == self.player and itemLoc.item.advancement == True]
|
||||
# progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type if itemLoc.item.type in ItemManager.Items else 'ArchipelagoItem'], locationsDict[itemLoc.name], True) for itemLoc in self.multiworld.get_locations() if itemLoc.player == self.player and itemLoc.item.player == self.player and itemLoc.item.advancement == True]
|
||||
|
||||
# romPatcher.writeSplitLocs(self.variaRando.args.majorsSplit, itemLocs, progItemLocs)
|
||||
romPatcher.writeSpoiler(itemLocs, progItemLocs)
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
|||
"CLIPLEN_end": "85:990F",
|
||||
"CLIPLEN_no_multi": "85:990C",
|
||||
"CLIPSET": "85:FF1D",
|
||||
"COLLECTTANK": "B8:84E8",
|
||||
"COLLECTTANK": "B8:8503",
|
||||
"MISCFX": "85:FF45",
|
||||
"NORMAL": "84:8BF2",
|
||||
"SETFX": "85:FF4E",
|
||||
|
@ -12,6 +12,11 @@
|
|||
"SOUNDFX_84": "84:F9E0",
|
||||
"SPECIALFX": "85:FF3C",
|
||||
"ammo_loop_table": "84:F896",
|
||||
"ap_playerid_to_rom_other_player_index": "B8:85BA",
|
||||
"ap_playerid_to_rom_other_player_index_checklastrow": "B8:85DD",
|
||||
"ap_playerid_to_rom_other_player_index_correctindex": "B8:85F8",
|
||||
"ap_playerid_to_rom_other_player_index_do_search_stage_1": "B8:85C0",
|
||||
"ap_playerid_to_rom_other_player_index_notfound": "B8:85F5",
|
||||
"archipelago_chozo_item_plm": "84:F874",
|
||||
"archipelago_hidden_item_plm": "84:F878",
|
||||
"archipelago_visible_item_plm": "84:F870",
|
||||
|
@ -35,11 +40,13 @@
|
|||
"i_item_setup_shared_all_items": "B8:8878",
|
||||
"i_item_setup_shared_alwaysloaded": "B8:8883",
|
||||
"i_live_pickup": "84:FA79",
|
||||
"i_live_pickup_multiworld": "B8:8578",
|
||||
"i_live_pickup_multiworld_end": "B8:85BD",
|
||||
"i_live_pickup_multiworld_local_item_or_offworld": "B8:8594",
|
||||
"i_live_pickup_multiworld_own_item": "B8:85A9",
|
||||
"i_live_pickup_multiworld_own_item1": "B8:85B5",
|
||||
"i_live_pickup_multiworld": "B8:85FD",
|
||||
"i_live_pickup_multiworld_end": "B8:8679",
|
||||
"i_live_pickup_multiworld_item_link_item": "B8:8659",
|
||||
"i_live_pickup_multiworld_otherplayers_item": "B8:8649",
|
||||
"i_live_pickup_multiworld_own_item": "B8:8635",
|
||||
"i_live_pickup_multiworld_own_item1": "B8:8641",
|
||||
"i_live_pickup_multiworld_send_network": "B8:8620",
|
||||
"i_load_custom_graphics": "84:FA1E",
|
||||
"i_load_custom_graphics_all_items": "84:FA39",
|
||||
"i_load_custom_graphics_alwaysloaded": "84:FA49",
|
||||
|
@ -52,36 +59,41 @@
|
|||
"i_start_draw_loop_visible_or_chozo": "84:F9E5",
|
||||
"i_visible_item": "84:F8A6",
|
||||
"i_visible_item_setup": "84:FA53",
|
||||
"message_PlaceholderBig": "85:BA8A",
|
||||
"message_char_table": "85:BA0A",
|
||||
"message_hook_tilemap_calc": "85:BABC",
|
||||
"message_hook_tilemap_calc_msgbox_mwrecv": "85:BADC",
|
||||
"message_hook_tilemap_calc_msgbox_mwsend": "85:BACE",
|
||||
"message_PlaceholderBig": "85:BB73",
|
||||
"message_char_table": "85:BAF3",
|
||||
"message_hook_tilemap_calc": "85:BBAA",
|
||||
"message_hook_tilemap_calc_msgbox_mw_item_link": "85:BBDD",
|
||||
"message_hook_tilemap_calc_msgbox_mwrecv": "85:BBCF",
|
||||
"message_hook_tilemap_calc_msgbox_mwsend": "85:BBC1",
|
||||
"message_hook_tilemap_calc_normal": "85:824C",
|
||||
"message_hook_tilemap_calc_vanilla": "85:BAC9",
|
||||
"message_hook_tilemap_calc_vanilla": "85:BBBC",
|
||||
"message_item_link_distributed": "85:B9A3",
|
||||
"message_item_link_distributed_end": "85:BAA3",
|
||||
"message_item_names": "85:9963",
|
||||
"message_item_received": "85:B8A3",
|
||||
"message_item_received_end": "85:B9A3",
|
||||
"message_item_sent": "85:B7A3",
|
||||
"message_item_sent_end": "85:B8A3",
|
||||
"message_multiworld_init_new_messagebox_if_needed": "85:BA95",
|
||||
"message_multiworld_init_new_messagebox_if_needed_msgbox_mwrecv": "85:BAB1",
|
||||
"message_multiworld_init_new_messagebox_if_needed_msgbox_mwsend": "85:BAB1",
|
||||
"message_multiworld_init_new_messagebox_if_needed_vanilla": "85:BAA9",
|
||||
"message_write_placeholders": "85:B9A3",
|
||||
"message_write_placeholders_adjust": "85:B9A5",
|
||||
"message_write_placeholders_end": "85:BA04",
|
||||
"message_write_placeholders_loop": "85:B9CA",
|
||||
"message_write_placeholders_notfound": "85:B9DC",
|
||||
"message_write_placeholders_value_ok": "85:B9DF",
|
||||
"message_multiworld_init_new_messagebox_if_needed": "85:BB7E",
|
||||
"message_multiworld_init_new_messagebox_if_needed_msgbox_mw_item_link": "85:BB9F",
|
||||
"message_multiworld_init_new_messagebox_if_needed_msgbox_mwrecv": "85:BB9F",
|
||||
"message_multiworld_init_new_messagebox_if_needed_msgbox_mwsend": "85:BB9F",
|
||||
"message_multiworld_init_new_messagebox_if_needed_vanilla": "85:BB97",
|
||||
"message_write_placeholders": "85:BAA3",
|
||||
"message_write_placeholders_adjust": "85:BAA5",
|
||||
"message_write_placeholders_end": "85:BAED",
|
||||
"mw_cleanup_item_link_messagebox": "B8:84BB",
|
||||
"mw_display_item_sent": "B8:848B",
|
||||
"mw_handle_queue": "B8:84F8",
|
||||
"mw_handle_queue_end": "B8:8571",
|
||||
"mw_handle_queue_loop": "B8:84FA",
|
||||
"mw_handle_queue_new_remote_item": "B8:854A",
|
||||
"mw_handle_queue_next": "B8:8566",
|
||||
"mw_handle_queue_perform_receive": "B8:855C",
|
||||
"mw_hook_main_game": "B8:85C1",
|
||||
"mw_handle_queue": "B8:8513",
|
||||
"mw_handle_queue_collect_item_if_present": "B8:8562",
|
||||
"mw_handle_queue_end": "B8:85B3",
|
||||
"mw_handle_queue_found": "B8:859B",
|
||||
"mw_handle_queue_lookup_player": "B8:8522",
|
||||
"mw_handle_queue_loop": "B8:8515",
|
||||
"mw_handle_queue_new_remote_item": "B8:857C",
|
||||
"mw_handle_queue_next": "B8:85A7",
|
||||
"mw_handle_queue_perform_receive": "B8:858E",
|
||||
"mw_hook_main_game": "B8:867D",
|
||||
"mw_init": "B8:8311",
|
||||
"mw_init_continuereset": "B8:8366",
|
||||
"mw_init_end": "B8:83EA",
|
||||
|
@ -91,8 +103,9 @@
|
|||
"mw_load_sram": "B8:8474",
|
||||
"mw_load_sram_done": "B8:8482",
|
||||
"mw_load_sram_setnewgame": "B8:8485",
|
||||
"mw_receive_item": "B8:84A9",
|
||||
"mw_receive_item_end": "B8:84E1",
|
||||
"mw_prep_item_link_messagebox": "B8:84A9",
|
||||
"mw_receive_item": "B8:84C4",
|
||||
"mw_receive_item_end": "B8:84FC",
|
||||
"mw_save_sram": "B8:8469",
|
||||
"mw_write_message": "B8:8442",
|
||||
"nonprog_item_eight_palette_indices": "84:F888",
|
||||
|
@ -119,16 +132,16 @@
|
|||
"p_visible_item_end": "84:F96E",
|
||||
"p_visible_item_loop": "84:F95B",
|
||||
"p_visible_item_trigger": "84:F967",
|
||||
"patch_load_multiworld": "B8:85D8",
|
||||
"patch_load_multiworld": "B8:8694",
|
||||
"perform_item_pickup": "84:FA7E",
|
||||
"plm_graphics_entry_offworld_item": "84:F886",
|
||||
"plm_graphics_entry_offworld_progression_item": "84:F87C",
|
||||
"plm_sequence_generic_item_0_bitmask": "84:FA90",
|
||||
"prog_item_eight_palette_indices": "84:F87E",
|
||||
"rando_item_table": "B8:E000",
|
||||
"rando_player_id_table": "B8:DC90",
|
||||
"rando_player_id_table_end": "B8:DE22",
|
||||
"rando_player_table": "B8:D000",
|
||||
"rando_player_id_table": "B8:DCA0",
|
||||
"rando_player_id_table_end": "B8:DE34",
|
||||
"rando_player_name_table": "B8:D000",
|
||||
"rando_seed_data": "B8:CF00",
|
||||
"sm_item_graphics": "B8:8800",
|
||||
"sm_item_plm_pickup_sequence_pointers": "B8:882E",
|
||||
|
|
Loading…
Reference in New Issue