mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-26 21:23:21 -07:00
convert KDL3 to APPP
This commit is contained in:
@@ -154,15 +154,15 @@ music_choices = [
|
||||
40, # Animal Friend 3
|
||||
]
|
||||
# extra room pointers we don't want to track other than for music
|
||||
room_pointers = [
|
||||
3079990, # Zero
|
||||
2983409, # BB Whispy
|
||||
3150688, # BB Acro
|
||||
2991071, # BB PonCon
|
||||
2998969, # BB Ado
|
||||
2980927, # BB Dedede
|
||||
2894290 # BB Zero
|
||||
]
|
||||
room_music = {
|
||||
3079990: 23, # Zero
|
||||
2983409: 2, # BB Whispy
|
||||
3150688: 2, # BB Acro
|
||||
2991071: 2, # BB PonCon
|
||||
2998969: 2, # BB Ado
|
||||
2980927: 7, # BB Dedede
|
||||
2894290: 23 # BB Zero
|
||||
}
|
||||
|
||||
enemy_remap = {
|
||||
"Waddle Dee": 0,
|
||||
@@ -281,6 +281,7 @@ class RomData:
|
||||
def get_bytes(self) -> bytes:
|
||||
return bytes(self.file)
|
||||
|
||||
|
||||
def handle_level_sprites(stages, sprites, palettes):
|
||||
palette_by_level = list()
|
||||
for palette in palettes:
|
||||
@@ -340,8 +341,9 @@ def write_consumable_sprites(rom: RomData, consumables: bool, stars: bool):
|
||||
|
||||
|
||||
class KDL3PatchExtensions(APPatchExtension):
|
||||
game = "Kirby's Dream Land 3"
|
||||
@staticmethod
|
||||
def patch(caller: APProcedurePatch, rom: bytes):
|
||||
def apply_post_patch(caller: APProcedurePatch, rom: bytes):
|
||||
rom_data = RomData(rom)
|
||||
target_language = rom_data.read_byte(0x3C020)
|
||||
rom_data.write_byte(0x7FD9, target_language)
|
||||
@@ -357,7 +359,7 @@ class KDL3PatchExtensions(APPatchExtension):
|
||||
for addr, level_sprite in zip([0x1CA000, 0x1CA920, 0x1CB230, 0x1CBB40, 0x1CC450], sprites):
|
||||
rom_data.write_bytes(addr, level_sprite)
|
||||
rom_data.write_bytes(0x460A, [0x00, 0xA0, 0x39, 0x20, 0xA9, 0x39, 0x30, 0xB2, 0x39, 0x40, 0xBB, 0x39,
|
||||
0x50, 0xC4, 0x39])
|
||||
0x50, 0xC4, 0x39])
|
||||
write_consumable_sprites(rom_data, rom_data.read_byte(0x3D018) > 0, rom_data.read_byte(0x3D01A) > 0)
|
||||
rom_name = rom_data.read_bytes(0x3C000, 21)
|
||||
rom_data.write_bytes(0x7FC0, rom_name)
|
||||
@@ -365,38 +367,32 @@ class KDL3PatchExtensions(APPatchExtension):
|
||||
return rom_data.get_bytes()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def apply_raw(caller: APProcedurePatch, rom: bytes, file: str, offset: int) -> bytes:
|
||||
# I should build this into the basepatch, but we'll wait a moment to do that
|
||||
data = caller.get_file(file)
|
||||
rom_data = bytearray(rom)
|
||||
rom_data[offset: offset + len(data)] = data
|
||||
return bytes(rom_data)
|
||||
|
||||
class KDL3ProcedurePatch(APProcedurePatch):
|
||||
class KDL3ProcedurePatch(APProcedurePatch, APTokenMixin):
|
||||
hash = [KDL3UHASH, KDL3JHASH]
|
||||
game = "Kirby's Dream Land 3"
|
||||
patch_file_ending = ".apkdl3"
|
||||
procedure = [
|
||||
("apply_bsdiff4", ["kdl3_basepatch.bsdiff4"]),
|
||||
("apply_tokens", ["tokens.bin"]),
|
||||
("apply_raw", ["APPauseIcons.dat", 0x3F000]),
|
||||
("apply_tokens", ["token_patch.bin"]),
|
||||
("apply_post_patch", []),
|
||||
]
|
||||
name: bytes # used to pass to init
|
||||
|
||||
@classmethod
|
||||
def get_source_data(cls) -> bytes:
|
||||
return get_base_rom_bytes()
|
||||
|
||||
|
||||
def patch_rom(world: "KDL3World", rom: RomData):
|
||||
rom.apply_patch(get_data(__name__, os.path.join("data", "kdl3_basepatch.bsdiff4")))
|
||||
def patch_rom(world: "KDL3World", patch: KDL3ProcedurePatch):
|
||||
patch.write_file("kdl3_basepatch.bsdiff4",
|
||||
get_data(__name__, os.path.join("data", "kdl3_basepatch.bsdiff4")))
|
||||
|
||||
tiles = get_data(__name__, os.path.join("data", "APPauseIcons.dat"))
|
||||
rom.write_bytes(0x3F000, tiles)
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3F000, tiles)
|
||||
|
||||
# Write open world patch
|
||||
if world.options.open_world:
|
||||
rom.write_bytes(0x143C7, [0xAD, 0xC1, 0x5A, 0xCD, 0xC1, 0x5A, ])
|
||||
patch.write_token(APTokenTypes.WRITE, 0x143C7, bytes([0xAD, 0xC1, 0x5A, 0xCD, 0xC1, 0x5A, ]))
|
||||
# changes the stage flag function to compare $5AC1 to $5AC1,
|
||||
# always running the "new stage" function
|
||||
# This has further checks present for bosses already, so we just
|
||||
@@ -405,13 +401,14 @@ def patch_rom(world: "KDL3World", rom: RomData):
|
||||
|
||||
if world.options.consumables:
|
||||
# reroute maxim tomatoes to use the 1-UP function, then null out the function
|
||||
rom.write_bytes(0x3002F, [0x37, 0x00])
|
||||
rom.write_bytes(0x30037, [0xA9, 0x26, 0x00, # LDA #$0026
|
||||
0x22, 0x27, 0xD9, 0x00, # JSL $00D927
|
||||
0xA4, 0xD2, # LDY $D2
|
||||
0x6B, # RTL
|
||||
0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, # NOP #10
|
||||
])
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3002F, bytes([0x37, 0x00]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x30037, bytes([0xA9, 0x26, 0x00, # LDA #$0026
|
||||
0x22, 0x27, 0xD9, 0x00, # JSL $00D927
|
||||
0xA4, 0xD2, # LDY $D2
|
||||
0x6B, # RTL
|
||||
0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA,
|
||||
0xEA, # NOP #10
|
||||
]))
|
||||
|
||||
# stars handling is built into the rom, so no changes there
|
||||
|
||||
@@ -427,136 +424,157 @@ def patch_rom(world: "KDL3World", rom: RomData):
|
||||
music_map[8] = world.random.choice(music_choices)
|
||||
for room in rooms:
|
||||
room.music = music_map[room.music]
|
||||
for room in room_pointers:
|
||||
old_music = rom.read_byte(room + 2)
|
||||
rom.write_byte(room + 2, music_map[old_music])
|
||||
for i in range(5):
|
||||
for room in room_music:
|
||||
patch.write_token(APTokenTypes.WRITE, room + 2, bytes([music_map[room_music[room]]]))
|
||||
for i, old_music in zip(range(5), [25, 26, 28, 27, 30]):
|
||||
# level themes
|
||||
old_music = rom.read_byte(0x133F2 + i)
|
||||
rom.write_byte(0x133F2 + i, music_map[old_music])
|
||||
patch.write_token(APTokenTypes.WRITE, 0x133F2 + i, bytes([music_map[old_music]]))
|
||||
# Zero
|
||||
rom.write_byte(0x9AE79, music_map[0x18])
|
||||
patch.write_token(APTokenTypes.WRITE, 0x9AE79, music_map[0x18].to_bytes(1, "little"))
|
||||
# Heart Star success and fail
|
||||
rom.write_byte(0x4A388, music_map[0x08])
|
||||
rom.write_byte(0x4A38D, music_map[0x1D])
|
||||
patch.write_token(APTokenTypes.WRITE, 0x4A388, music_map[0x08].to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x4A38D, music_map[0x1D].to_bytes(1, "little"))
|
||||
elif world.options.music_shuffle == 2:
|
||||
for room in rooms:
|
||||
room.music = world.random.choice(music_choices)
|
||||
for room in room_pointers:
|
||||
rom.write_byte(room + 2, world.random.choice(music_choices))
|
||||
for room in room_music:
|
||||
patch.write_token(APTokenTypes.WRITE, room + 2, world.random.choice(music_choices).to_bytes(1, "little"))
|
||||
for i in range(5):
|
||||
# level themes
|
||||
rom.write_byte(0x133F2 + i, world.random.choice(music_choices))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x133F2 + i, world.random.choice(music_choices).to_bytes(1, "little"))
|
||||
# Zero
|
||||
rom.write_byte(0x9AE79, world.random.choice(music_choices))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x9AE79, world.random.choice(music_choices).to_bytes(1, "little"))
|
||||
# Heart Star success and fail
|
||||
rom.write_byte(0x4A388, world.random.choice(music_choices))
|
||||
rom.write_byte(0x4A38D, world.random.choice(music_choices))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x4A388, world.random.choice(music_choices).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x4A38D, world.random.choice(music_choices).to_bytes(1, "little"))
|
||||
|
||||
for room in rooms:
|
||||
room.patch(rom)
|
||||
room.patch(patch)
|
||||
|
||||
if world.options.virtual_console in [1, 3]:
|
||||
# Flash Reduction
|
||||
rom.write_byte(0x9AE68, 0x10)
|
||||
rom.write_bytes(0x9AE8E, [0x08, 0x00, 0x22, 0x5D, 0xF7, 0x00, 0xA2, 0x08, ])
|
||||
rom.write_byte(0x9AEA1, 0x08)
|
||||
rom.write_byte(0x9AEC9, 0x01)
|
||||
rom.write_bytes(0x9AED2, [0xA9, 0x1F])
|
||||
rom.write_byte(0x9AEE1, 0x08)
|
||||
patch.write_token(APTokenTypes.WRITE, 0x9AE68, 0x10.to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x9AE8E, bytes([0x08, 0x00, 0x22, 0x5D, 0xF7, 0x00, 0xA2, 0x08, ]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x9AEA1, 0x08.to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x9AEC9, 0x01.to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x9AED2, bytes([0xA9, 0x1F]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x9AEE1, 0x08.to_bytes(1, "little"))
|
||||
|
||||
if world.options.virtual_console in [2, 3]:
|
||||
# Hyper Zone BB colors
|
||||
rom.write_bytes(0x2C5E16, [0xEE, 0x1B, 0x18, 0x5B, 0xD3, 0x4A, 0xF4, 0x3B, ])
|
||||
rom.write_bytes(0x2C8217, [0xFF, 0x1E, ])
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2C5E16, bytes([0xEE, 0x1B, 0x18, 0x5B, 0xD3, 0x4A, 0xF4, 0x3B, ]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2C8217, bytes([0xFF, 0x1E, ]))
|
||||
|
||||
# boss requirements
|
||||
rom.write_bytes(0x3D000, struct.pack("HHHHH", world.boss_requirements[0], world.boss_requirements[1],
|
||||
world.boss_requirements[2], world.boss_requirements[3],
|
||||
world.boss_requirements[4]))
|
||||
rom.write_bytes(0x3D00A, struct.pack("H", world.required_heart_stars if world.options.goal_speed == 1 else 0xFFFF))
|
||||
rom.write_byte(0x3D00C, world.options.goal_speed.value)
|
||||
rom.write_byte(0x3D00E, world.options.open_world.value)
|
||||
rom.write_byte(0x3D010, world.options.death_link.value)
|
||||
rom.write_byte(0x3D012, world.options.goal.value)
|
||||
rom.write_byte(0x3D014, world.options.stage_shuffle.value)
|
||||
rom.write_byte(0x3D016, world.options.ow_boss_requirement.value)
|
||||
rom.write_byte(0x3D018, world.options.consumables.value)
|
||||
rom.write_byte(0x3D01A, world.options.starsanity.value)
|
||||
rom.write_byte(0x3D01C, world.options.gifting.value if world.multiworld.players > 1 else 0)
|
||||
rom.write_byte(0x3D01E, world.options.strict_bosses.value)
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D000,
|
||||
struct.pack("HHHHH", world.boss_requirements[0], world.boss_requirements[1],
|
||||
world.boss_requirements[2], world.boss_requirements[3],
|
||||
world.boss_requirements[4]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D00A,
|
||||
struct.pack("H", world.required_heart_stars if world.options.goal_speed == 1 else 0xFFFF))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D00C, world.options.goal_speed.value.to_bytes(2, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D00E, world.options.open_world.value.to_bytes(2, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D010, world.options.death_link.value.to_bytes(2, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D012, world.options.goal.value.to_bytes(2, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D014, world.options.stage_shuffle.value.to_bytes(2, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D016, world.options.ow_boss_requirement.value.to_bytes(2, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D018, world.options.consumables.value.to_bytes(2, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D01A, world.options.starsanity.value.to_bytes(2, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D01C, world.options.gifting.value.to_bytes(2, "little")
|
||||
if world.multiworld.players > 1 else bytes([0]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D01E, world.options.strict_bosses.value.to_bytes(2, "little"))
|
||||
# don't write gifting for solo game, since there's no one to send anything to
|
||||
|
||||
for level in world.player_levels:
|
||||
for i in range(len(world.player_levels[level])):
|
||||
rom.write_bytes(0x3F002E + ((level - 1) * 14) + (i * 2),
|
||||
struct.pack("H", level_pointers[world.player_levels[level][i]]))
|
||||
rom.write_bytes(0x3D020 + (level - 1) * 14 + (i * 2),
|
||||
struct.pack("H", world.player_levels[level][i] & 0x00FFFF))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3F002E + ((level - 1) * 14) + (i * 2),
|
||||
struct.pack("H", level_pointers[world.player_levels[level][i]]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D020 + (level - 1) * 14 + (i * 2),
|
||||
struct.pack("H", world.player_levels[level][i] & 0x00FFFF))
|
||||
if (i == 0) or (i > 0 and i % 6 != 0):
|
||||
rom.write_bytes(0x3D080 + (level - 1) * 12 + (i * 2),
|
||||
struct.pack("H", (world.player_levels[level][i] & 0x00FFFF) % 6))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3D080 + (level - 1) * 12 + (i * 2),
|
||||
struct.pack("H", (world.player_levels[level][i] & 0x00FFFF) % 6))
|
||||
|
||||
for i in range(6):
|
||||
if world.boss_butch_bosses[i]:
|
||||
rom.write_bytes(0x3F0000 + (level_pointers[0x770200 + i]), struct.pack("I", bb_bosses[0x770200 + i]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3F0000 + (level_pointers[0x770200 + i]),
|
||||
struct.pack("I", bb_bosses[0x770200 + i]))
|
||||
|
||||
# copy ability shuffle
|
||||
if world.options.copy_ability_randomization.value > 0:
|
||||
for enemy in world.copy_abilities:
|
||||
if enemy in miniboss_remap:
|
||||
rom.write_bytes(0xB417E + (miniboss_remap[enemy] << 1),
|
||||
struct.pack("H", ability_remap[world.copy_abilities[enemy]]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0xB417E + (miniboss_remap[enemy] << 1),
|
||||
struct.pack("H", ability_remap[world.copy_abilities[enemy]]))
|
||||
else:
|
||||
rom.write_bytes(0xB3CAC + (enemy_remap[enemy] << 1),
|
||||
struct.pack("H", ability_remap[world.copy_abilities[enemy]]))
|
||||
patch.write_token(APTokenTypes.WRITE, 0xB3CAC + (enemy_remap[enemy] << 1),
|
||||
struct.pack("H", ability_remap[world.copy_abilities[enemy]]))
|
||||
# following only needs done on non-door rando
|
||||
# incredibly lucky this follows the same order (including 5E == star block)
|
||||
rom.write_byte(0x2F77EA, 0x5E + (ability_remap[world.copy_abilities["Sparky"]] << 1))
|
||||
rom.write_byte(0x2F7811, 0x5E + (ability_remap[world.copy_abilities["Sparky"]] << 1))
|
||||
rom.write_byte(0x2F9BC4, 0x5E + (ability_remap[world.copy_abilities["Blocky"]] << 1))
|
||||
rom.write_byte(0x2F9BEB, 0x5E + (ability_remap[world.copy_abilities["Blocky"]] << 1))
|
||||
rom.write_byte(0x2FAC06, 0x5E + (ability_remap[world.copy_abilities["Jumper Shoot"]] << 1))
|
||||
rom.write_byte(0x2FAC2D, 0x5E + (ability_remap[world.copy_abilities["Jumper Shoot"]] << 1))
|
||||
rom.write_byte(0x2F9E7B, 0x5E + (ability_remap[world.copy_abilities["Yuki"]] << 1))
|
||||
rom.write_byte(0x2F9EA2, 0x5E + (ability_remap[world.copy_abilities["Yuki"]] << 1))
|
||||
rom.write_byte(0x2FA951, 0x5E + (ability_remap[world.copy_abilities["Sir Kibble"]] << 1))
|
||||
rom.write_byte(0x2FA978, 0x5E + (ability_remap[world.copy_abilities["Sir Kibble"]] << 1))
|
||||
rom.write_byte(0x2FA132, 0x5E + (ability_remap[world.copy_abilities["Haboki"]] << 1))
|
||||
rom.write_byte(0x2FA159, 0x5E + (ability_remap[world.copy_abilities["Haboki"]] << 1))
|
||||
rom.write_byte(0x2FA3E8, 0x5E + (ability_remap[world.copy_abilities["Boboo"]] << 1))
|
||||
rom.write_byte(0x2FA40F, 0x5E + (ability_remap[world.copy_abilities["Boboo"]] << 1))
|
||||
rom.write_byte(0x2F90E2, 0x5E + (ability_remap[world.copy_abilities["Captain Stitch"]] << 1))
|
||||
rom.write_byte(0x2F9109, 0x5E + (ability_remap[world.copy_abilities["Captain Stitch"]] << 1))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2F77EA,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Sparky"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2F7811,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Sparky"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2F9BC4,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Blocky"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2F9BEB,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Blocky"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2FAC06,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Jumper Shoot"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2FAC2D,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Jumper Shoot"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2F9E7B,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Yuki"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2F9EA2,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Yuki"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2FA951,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Sir Kibble"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2FA978,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Sir Kibble"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2FA132,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Haboki"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2FA159,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Haboki"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2FA3E8,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Boboo"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2FA40F,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Boboo"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2F90E2,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Captain Stitch"]] << 1)).to_bytes(1, "little"))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x2F9109,
|
||||
(0x5E + (ability_remap[world.copy_abilities["Captain Stitch"]] << 1)).to_bytes(1, "little"))
|
||||
|
||||
if world.options.copy_ability_randomization == 2:
|
||||
for enemy in enemy_remap:
|
||||
# we just won't include it for minibosses
|
||||
rom.write_bytes(0xB3E40 + (enemy_remap[enemy] << 1), struct.pack("h", world.random.randint(-1, 2)))
|
||||
patch.write_token(APTokenTypes.WRITE, 0xB3E40 + (enemy_remap[enemy] << 1),
|
||||
struct.pack("h", world.random.randint(-1, 2)))
|
||||
|
||||
# write jumping goal
|
||||
rom.write_bytes(0x94F8, struct.pack("H", world.options.jumping_target))
|
||||
rom.write_bytes(0x944E, struct.pack("H", world.options.jumping_target))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x94F8, struct.pack("H", world.options.jumping_target))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x944E, struct.pack("H", world.options.jumping_target))
|
||||
|
||||
from Utils import __version__
|
||||
rom.name = bytearray(
|
||||
patch.name = bytearray(
|
||||
f'KDL3{__version__.replace(".", "")[0:3]}_{world.player}_{world.multiworld.seed:11}\0', 'utf8')[:21]
|
||||
rom.name.extend([0] * (21 - len(rom.name)))
|
||||
rom.write_bytes(0x3C000, rom.name)
|
||||
rom.write_byte(0x3C020, world.options.game_language.value)
|
||||
patch.name.extend([0] * (21 - len(patch.name)))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3C000, bytes(patch.name))
|
||||
patch.write_token(APTokenTypes.WRITE, 0x3C020, world.options.game_language.value.to_bytes(1, "little"))
|
||||
|
||||
# handle palette
|
||||
if world.options.kirby_flavor_preset.value != 0:
|
||||
for addr in kirby_target_palettes:
|
||||
target = kirby_target_palettes[addr]
|
||||
palette = get_kirby_palette(world)
|
||||
rom.write_bytes(addr, get_palette_bytes(palette, target[0], target[1], target[2]))
|
||||
patch.write_token(APTokenTypes.WRITE, addr, get_palette_bytes(palette, target[0], target[1], target[2]))
|
||||
|
||||
if world.options.gooey_flavor_preset.value != 0:
|
||||
for addr in gooey_target_palettes:
|
||||
target = gooey_target_palettes[addr]
|
||||
palette = get_gooey_palette(world)
|
||||
rom.write_bytes(addr, get_palette_bytes(palette, target[0], target[1], target[2]))
|
||||
patch.write_token(APTokenTypes.WRITE, addr, get_palette_bytes(palette, target[0], target[1], target[2]))
|
||||
|
||||
patch.write_file("token_patch.bin", patch.get_token_binary())
|
||||
|
||||
|
||||
def get_base_rom_bytes() -> bytes:
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import struct
|
||||
import typing
|
||||
from BaseClasses import Region, ItemClassification
|
||||
from worlds.Files import APTokenTypes
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from .Rom import RomData
|
||||
from .Rom import KDL3ProcedurePatch
|
||||
|
||||
animal_map = {
|
||||
"Rick Spawn": 0,
|
||||
@@ -42,12 +43,13 @@ class KDL3Room(Region):
|
||||
self.consumables = consumables
|
||||
self.consumable_pointer = consumable_pointer
|
||||
|
||||
def patch(self, rom: "RomData"):
|
||||
rom.write_byte(self.pointer + 2, self.music)
|
||||
def patch(self, patch: "KDL3ProcedurePatch"):
|
||||
patch.write_token(APTokenTypes.WRITE, self.pointer + 2, self.music.to_bytes(1, "little"))
|
||||
animals = [x.item.name for x in self.locations if "Animal" in x.name]
|
||||
if len(animals) > 0:
|
||||
for current_animal, address in zip(animals, self.animal_pointers):
|
||||
rom.write_byte(self.pointer + address + 7, animal_map[current_animal])
|
||||
patch.write_token(APTokenTypes.WRITE, self.pointer + address + 7,
|
||||
animal_map[current_animal].to_bytes(1, "little"))
|
||||
if self.multiworld.worlds[self.player].options.consumables:
|
||||
load_len = len(self.entity_load)
|
||||
for consumable in self.consumables:
|
||||
@@ -64,7 +66,8 @@ class KDL3Room(Region):
|
||||
vtype = 0
|
||||
else:
|
||||
vtype = 2
|
||||
rom.write_byte(self.pointer + 88 + (replacement_target * 2), vtype)
|
||||
patch.write_token(APTokenTypes.WRITE, self.pointer + 88 + (replacement_target * 2),
|
||||
vtype.to_bytes(1, "little"))
|
||||
self.entity_load[replacement_target] = [vtype, 22]
|
||||
else:
|
||||
if is_progression:
|
||||
@@ -79,9 +82,10 @@ class KDL3Room(Region):
|
||||
else:
|
||||
self.entity_load.append([2, 22])
|
||||
if load_len < len(self.entity_load):
|
||||
rom.write_bytes(self.pointer + 88 + (load_len * 2), bytes(self.entity_load[load_len]))
|
||||
rom.write_bytes(self.pointer + 104 + (load_len * 2),
|
||||
bytes(struct.pack("H", self.consumable_pointer)))
|
||||
patch.write_token(APTokenTypes.WRITE, self.pointer + 88 + (load_len * 2),
|
||||
bytes(self.entity_load[load_len]))
|
||||
patch.write_token(APTokenTypes.WRITE, self.pointer + 104 + (load_len * 2),
|
||||
bytes(struct.pack("H", self.consumable_pointer)))
|
||||
if is_progression:
|
||||
if [1, 22] in self.entity_load:
|
||||
vtype = 1
|
||||
@@ -92,4 +96,5 @@ class KDL3Room(Region):
|
||||
vtype = 3
|
||||
else:
|
||||
vtype = 2
|
||||
rom.write_byte(self.pointer + consumable["pointer"] + 7, vtype)
|
||||
patch.write_token(APTokenTypes.WRITE, self.pointer + consumable["pointer"] + 7,
|
||||
vtype.to_bytes(1, "little"))
|
||||
|
||||
@@ -16,7 +16,7 @@ from .Presets import kdl3_options_presets
|
||||
from .Names import LocationName
|
||||
from .Room import KDL3Room
|
||||
from .Rules import set_rules
|
||||
from .Rom import KDL3DeltaPatch, get_base_rom_path, RomData, patch_rom, KDL3JHASH, KDL3UHASH
|
||||
from .Rom import KDL3ProcedurePatch, get_base_rom_path, patch_rom, KDL3JHASH, KDL3UHASH
|
||||
from .Client import KDL3SNIClient
|
||||
|
||||
from typing import Dict, TextIO, Optional, List
|
||||
@@ -81,12 +81,6 @@ class KDL3World(World):
|
||||
self.boss_butch_bosses: List[Optional[bool]] = list()
|
||||
self.rooms: Optional[List[KDL3Room]] = None
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
|
||||
rom_file: str = get_base_rom_path()
|
||||
if not os.path.exists(rom_file):
|
||||
raise FileNotFoundError(f"Could not find base ROM for {cls.game}: {rom_file}")
|
||||
|
||||
create_regions = create_levels
|
||||
|
||||
def create_item(self, name: str, force_non_progression=False) -> KDL3Item:
|
||||
@@ -297,24 +291,18 @@ class KDL3World(World):
|
||||
self.boss_butch_bosses = [False for _ in range(6)]
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
rom_path = ""
|
||||
try:
|
||||
rom = RomData(get_base_rom_path())
|
||||
patch_rom(self, rom)
|
||||
patch = KDL3ProcedurePatch()
|
||||
patch_rom(self, patch)
|
||||
|
||||
rom_path = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.sfc")
|
||||
rom.write_to_file(rom_path)
|
||||
self.rom_name = rom.name
|
||||
self.rom_name = patch.name
|
||||
|
||||
patch = KDL3DeltaPatch(os.path.splitext(rom_path)[0] + KDL3DeltaPatch.patch_file_ending, player=self.player,
|
||||
player_name=self.multiworld.player_name[self.player], patched_path=rom_path)
|
||||
patch.write()
|
||||
patch.write(os.path.join(output_directory,
|
||||
f"{self.multiworld.get_out_file_name_base(self.player)}{patch.patch_file_ending}"))
|
||||
except Exception:
|
||||
raise
|
||||
finally:
|
||||
self.rom_name_available_event.set() # make sure threading continues and errors are collected
|
||||
if os.path.exists(rom_path):
|
||||
os.unlink(rom_path)
|
||||
|
||||
def modify_multidata(self, multidata: dict):
|
||||
# wait for self.rom_name to be available.
|
||||
|
||||
Reference in New Issue
Block a user