[SM] Add support for Remote Items (#317)

This commit is contained in:
lordlou 2022-03-21 00:34:47 -04:00 committed by GitHub
parent 9ba70951d5
commit 7df12930ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 18 additions and 3 deletions

View File

@ -201,7 +201,8 @@ async def deathlink_kill_player(ctx: Context):
snes_buffered_write(ctx, WRAM_START + 0x0373, snes_buffered_write(ctx, WRAM_START + 0x0373,
bytes([8])) # deal 1 full heart of damage at next opportunity bytes([8])) # deal 1 full heart of damage at next opportunity
elif ctx.game == GAME_SM: elif ctx.game == GAME_SM:
snes_buffered_write(ctx, WRAM_START + 0x09C2, bytes([0, 0])) # set current health to 0 snes_buffered_write(ctx, WRAM_START + 0x09C2, bytes([1, 0])) # set current health to 1 (to prevent saving with 0 energy)
snes_buffered_write(ctx, WRAM_START + 0x0A50, bytes([255])) # deal 255 of damage at next opportunity
if not ctx.death_link_allow_survive: if not ctx.death_link_allow_survive:
snes_buffered_write(ctx, WRAM_START + 0x09D6, bytes([0, 0])) # set current reserve to 0 snes_buffered_write(ctx, WRAM_START + 0x09D6, bytes([0, 0])) # set current reserve to 0
await snes_flush_writes(ctx) await snes_flush_writes(ctx)
@ -266,6 +267,7 @@ SM_RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2 # 1 byte
SM_RECV_ITEM_PLAYER_ADDR = SAVEDATA_START + 0x4D3 # 1 byte SM_RECV_ITEM_PLAYER_ADDR = SAVEDATA_START + 0x4D3 # 1 byte
SM_DEATH_LINK_ACTIVE_ADDR = ROM_START + 0x277f04 # 1 byte SM_DEATH_LINK_ACTIVE_ADDR = ROM_START + 0x277f04 # 1 byte
SM_REMOTE_ITEM_FLAG_ADDR = ROM_START + 0x277f06 # 1 byte
# SMZ3 # SMZ3
SMZ3_ROMNAME_START = 0x00FFC0 SMZ3_ROMNAME_START = 0x00FFC0
@ -983,7 +985,8 @@ async def game_watcher(ctx: Context):
continue continue
elif game_name[:2] == b"SM": elif game_name[:2] == b"SM":
ctx.game = GAME_SM ctx.game = GAME_SM
ctx.items_handling = 0b001 # full local item_handling = await snes_read(ctx, SM_REMOTE_ITEM_FLAG_ADDR, 1)
ctx.items_handling = 0b001 if item_handling is None else item_handling[0]
else: else:
game_name = await snes_read(ctx, SMZ3_ROMNAME_START, 3) game_name = await snes_read(ctx, SMZ3_ROMNAME_START, 3)
if game_name == b"ZSM": if game_name == b"ZSM":
@ -1133,13 +1136,15 @@ async def game_watcher(ctx: Context):
itemOutPtr = data[2] | (data[3] << 8) itemOutPtr = data[2] | (data[3] << 8)
from worlds.sm.Items import items_start_id from worlds.sm.Items import items_start_id
from worlds.sm.Locations import locations_start_id
if itemOutPtr < len(ctx.items_received): if itemOutPtr < len(ctx.items_received):
item = ctx.items_received[itemOutPtr] item = ctx.items_received[itemOutPtr]
itemId = item.item - items_start_id itemId = item.item - items_start_id
locationId = (item.location - locations_start_id) if item.location >= 0 else 0x00
playerID = item.player if item.player <= SM_ROM_PLAYER_LIMIT else 0 playerID = item.player if item.player <= SM_ROM_PLAYER_LIMIT else 0
snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + itemOutPtr * 4, bytes( snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + itemOutPtr * 4, bytes(
[playerID & 0xFF, (playerID >> 8) & 0xFF, itemId & 0xFF, (itemId >> 8) & 0xFF])) [playerID & 0xFF, (playerID >> 8) & 0xFF, itemId & 0xFF, locationId & 0xFF]))
itemOutPtr += 1 itemOutPtr += 1
snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + 0x602, snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + 0x602,
bytes([itemOutPtr & 0xFF, (itemOutPtr >> 8) & 0xFF])) bytes([itemOutPtr & 0xFF, (itemOutPtr >> 8) & 0xFF]))

View File

@ -52,6 +52,10 @@ class DeathLink(Choice):
alias_true = 1 alias_true = 1
default = 0 default = 0
class RemoteItems(Toggle):
"""Indicates you get items sent from your own world. This allows coop play of a world."""
display_name = "Remote Items"
class MaxDifficulty(Choice): class MaxDifficulty(Choice):
"""Depending on the perceived difficulties of the techniques, bosses, hell runs etc. from the preset, it will prevent the Randomizer from placing an item in a location too difficult to reach with the current items.""" """Depending on the perceived difficulties of the techniques, bosses, hell runs etc. from the preset, it will prevent the Randomizer from placing an item in a location too difficult to reach with the current items."""
display_name = "Maximum Difficulty" display_name = "Maximum Difficulty"
@ -225,6 +229,7 @@ sm_options: typing.Dict[str, type(Option)] = {
"start_inventory_removes_from_pool": StartItemsRemovesFromPool, "start_inventory_removes_from_pool": StartItemsRemovesFromPool,
"preset": Preset, "preset": Preset,
"start_location": StartLocation, "start_location": StartLocation,
"remote_items": RemoteItems,
"death_link": DeathLink, "death_link": DeathLink,
#"majors_split": "Full", #"majors_split": "Full",
#"scav_num_locs": "10", #"scav_num_locs": "10",

View File

@ -91,6 +91,8 @@ class SMWorld(World):
if (self.variaRando.args.morphPlacement == "early"): if (self.variaRando.args.morphPlacement == "early"):
self.world.local_items[self.player].value.add('Morph') self.world.local_items[self.player].value.add('Morph')
self.remote_items = self.world.remote_items[self.player]
if (len(self.variaRando.randoExec.setup.restrictedLocs) > 0): if (len(self.variaRando.randoExec.setup.restrictedLocs) > 0):
self.world.accessibility[self.player] = self.world.accessibility[self.player].from_text("items") self.world.accessibility[self.player] = self.world.accessibility[self.player].from_text("items")
logger.warning(f"accessibility forced to 'items' for player {self.world.get_player_name(self.player)} because of 'fun' settings") logger.warning(f"accessibility forced to 'items' for player {self.world.get_player_name(self.player)} because of 'fun' settings")
@ -285,6 +287,7 @@ class SMWorld(World):
openTourianGreyDoors = {0x07C823 + 5: [0x0C], 0x07C831 + 5: [0x0C]} openTourianGreyDoors = {0x07C823 + 5: [0x0C], 0x07C831 + 5: [0x0C]}
deathLink = {0x277f04: [self.world.death_link[self.player].value]} deathLink = {0x277f04: [self.world.death_link[self.player].value]}
remoteItem = {0x277f06: self.getWordArray(0b001 + (0b010 if self.remote_items else 0b000))}
playerNames = {} playerNames = {}
playerNameIDMap = {} playerNameIDMap = {}
@ -299,6 +302,7 @@ class SMWorld(World):
'offworldSprites': offworldSprites, 'offworldSprites': offworldSprites,
'openTourianGreyDoors': openTourianGreyDoors, 'openTourianGreyDoors': openTourianGreyDoors,
'deathLink': deathLink, 'deathLink': deathLink,
'remoteItem': remoteItem,
'PlayerName': playerNames, 'PlayerName': playerNames,
'PlayerNameIDMap': playerNameIDMap} 'PlayerNameIDMap': playerNameIDMap}
romPatcher.applyIPSPatchDict(patchDict) romPatcher.applyIPSPatchDict(patchDict)
@ -310,6 +314,7 @@ class SMWorld(World):
self.romName.extend([0] * (21 - len(self.romName))) self.romName.extend([0] * (21 - len(self.romName)))
romPatcher.applyIPSPatch('ROMName', { 'ROMName': {0x1C4F00 : self.romName, 0x007FC0 : self.romName} }) romPatcher.applyIPSPatch('ROMName', { 'ROMName': {0x1C4F00 : self.romName, 0x007FC0 : self.romName} })
startItemROMAddressBase = 0x2FD8B9 startItemROMAddressBase = 0x2FD8B9
# current, base value or bitmask, max, base value or bitmask # current, base value or bitmask, max, base value or bitmask