Random sprite on hit now part of basepatch. Other events possible too.

This commit is contained in:
CaitSith2 2020-10-04 10:57:30 -07:00
parent 104f780873
commit 38ac943a9c
5 changed files with 103 additions and 44 deletions

43
Gui.py
View File

@ -138,7 +138,10 @@ def guiMain(args=None):
sprite = None sprite = None
def set_sprite(sprite_param): def set_sprite(sprite_param):
nonlocal sprite nonlocal sprite
if sprite_param is None or not sprite_param.valid: if isinstance(sprite_param, str):
sprite = sprite_param
spriteNameVar.set(sprite_param)
elif sprite_param is None or not sprite_param.valid:
sprite = None sprite = None
spriteNameVar.set('(unchanged)') spriteNameVar.set('(unchanged)')
else: else:
@ -1409,7 +1412,31 @@ class SpriteSelector(object):
button = Button(frame, text="Default Link sprite", command=self.use_default_link_sprite) button = Button(frame, text="Default Link sprite", command=self.use_default_link_sprite)
button.pack(side=LEFT, padx=(0, 5)) button.pack(side=LEFT, padx=(0, 5))
button = Button(frame, text="Random sprite", command=self.use_random_sprite) self.randomButtonText = StringVar()
button = Button(frame, textvariable=self.randomButtonText, command=self.use_random_sprite)
button.pack(side=LEFT, padx=(0, 5))
self.randomButtonText.set("Random")
self.randomOnEventText = StringVar()
self.randomOnHitVar = IntVar()
self.randomOnEnterVar = IntVar()
self.randomOnExitVar = IntVar()
self.randomOnSlashVar = IntVar()
self.randomOnItemVar = IntVar()
button = Checkbutton(frame, text="Hit", command=self.update_random_button, variable=self.randomOnHitVar)
button.pack(side=LEFT, padx=(0, 5))
button = Checkbutton(frame, text="Enter", command=self.update_random_button, variable=self.randomOnEnterVar)
button.pack(side=LEFT, padx=(0, 5))
button = Checkbutton(frame, text="Exit", command=self.update_random_button, variable=self.randomOnExitVar)
button.pack(side=LEFT, padx=(0, 5))
button = Checkbutton(frame, text="Slash", command=self.update_random_button, variable=self.randomOnSlashVar)
button.pack(side=LEFT, padx=(0, 5))
button = Checkbutton(frame, text="Item", command=self.update_random_button, variable=self.randomOnItemVar)
button.pack(side=LEFT, padx=(0, 5)) button.pack(side=LEFT, padx=(0, 5))
if adjuster: if adjuster:
@ -1574,8 +1601,18 @@ class SpriteSelector(object):
self.callback(Sprite.default_link_sprite()) self.callback(Sprite.default_link_sprite())
self.window.destroy() self.window.destroy()
def update_random_button(self):
randomon = "-hit" if self.randomOnHitVar.get() else ""
randomon += "-enter" if self.randomOnEnterVar.get() else ""
randomon += "-exit" if self.randomOnExitVar.get() else ""
randomon += "-slash" if self.randomOnSlashVar.get() else ""
randomon += "-item" if self.randomOnItemVar.get() else ""
self.randomOnEventText.set(f"randomon{randomon}" if randomon else None)
self.randomButtonText.set("Random On Event" if randomon else "Random")
def use_random_sprite(self): def use_random_sprite(self):
self.callback(random.choice(self.all_sprites) if self.all_sprites else None) randomon = self.randomOnEventText.get()
self.callback(randomon if randomon else (random.choice(self.all_sprites) if self.all_sprites else None))
self.window.destroy() self.window.destroy()
def select_sprite(self, spritename): def select_sprite(self, spritename):

View File

@ -177,10 +177,9 @@ def main(args, seed=None):
rom_names = [] rom_names = []
def _gen_rom(team: int, player: int): def _gen_rom(team: int, player: int):
sprite_random_on_hit = type(args.sprite[player]) is str and args.sprite[player].lower() == 'randomonhit'
use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player]
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
or world.shufflepots[player] or sprite_random_on_hit or world.bush_shuffle[player] or world.shufflepots[player] or world.bush_shuffle[player]
or world.killable_thieves[player] or world.tile_shuffle[player]) or world.killable_thieves[player] or world.tile_shuffle[player])
rom = LocalRom(args.rom) rom = LocalRom(args.rom)
@ -188,8 +187,7 @@ def main(args, seed=None):
patch_rom(world, rom, player, team, use_enemizer) patch_rom(world, rom, player, team, use_enemizer)
if use_enemizer: if use_enemizer:
patch_enemizer(world, player, rom, args.enemizercli, patch_enemizer(world, player, rom, args.enemizercli)
sprite_random_on_hit)
if args.race: if args.race:
patch_race_rom(rom) patch_race_rom(rom)
@ -198,7 +196,7 @@ def main(args, seed=None):
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player],
args.fastmenu[player], args.disablemusic[player], args.sprite[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player],
args.ow_palettes[player], args.uw_palettes[player], world, player) args.ow_palettes[player], args.uw_palettes[player], world, player, True)
mcsb_name = '' mcsb_name = ''
if all([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], if all([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player],

89
Rom.py
View File

@ -1,5 +1,5 @@
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '45a7732cfb056a251285fcb14e9bb8a7' RANDOMIZERBASEHASH = 'f71376f57dfd3d69eaac96f2f391ff64'
import io import io
import json import json
@ -59,15 +59,6 @@ class LocalRom(object):
self.buffer[startaddress:startaddress + len(values)] = values self.buffer[startaddress:startaddress + len(values)] = values
def write_to_file(self, file, hide_enemizer=False): def write_to_file(self, file, hide_enemizer=False):
if hide_enemizer:
extra_zeroes = 0x400000 - len(self.buffer)
if extra_zeroes > 0:
buffer = self.buffer + bytes([0x00] * extra_zeroes)
with open(file, 'wb') as outfile:
outfile.write(buffer)
return
with open(file, 'wb') as outfile: with open(file, 'wb') as outfile:
outfile.write(self.buffer) outfile.write(self.buffer)
@ -106,7 +97,7 @@ class LocalRom(object):
'Supplied Base Rom does not match known MD5 for JAP(1.0) release. Will try to patch anyway.') 'Supplied Base Rom does not match known MD5 for JAP(1.0) release. Will try to patch anyway.')
# extend to 2MB # extend to 2MB
self.buffer.extend(bytearray([0x00]) * (0x200000 - len(self.buffer))) self.buffer.extend(bytearray([0x00]) * (0x400000 - len(self.buffer)))
# load randomizer patches # load randomizer patches
with open(local_path('data', 'base2current.json')) as stream: with open(local_path('data', 'base2current.json')) as stream:
@ -196,7 +187,50 @@ def check_enemizer(enemizercli):
check_enemizer.done = True check_enemizer.done = True
def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, random_sprite_on_hit: bool): def apply_random_sprite_on_event(rom: LocalRom, sprite, local_random, allow_random_on_event):
onevent = onhit = 0
sprites = list()
if not allow_random_on_event:
allow_random_on_event = not rom.read_byte(0x186381) # Check if explicitly disabled in rom. If so, it stays that way.
if sprite and not isinstance(sprite, Sprite):
sprite = sprite.lower()
if sprite.startswith('randomon'):
onevent = onhit = 0x01 if 'hit' in sprite else 0x00
onevent += 0x02 if 'enter' in sprite else 0x00
onevent += 0x04 if 'exit' in sprite else 0x00
onevent += 0x08 if 'slash' in sprite else 0x00
onevent += 0x10 if 'item' in sprite else 0x00
sprite = Sprite(sprite) if os.path.isfile(sprite) else get_sprite_from_name(sprite, local_random)
# write link sprite if required
if sprite:
sprite.write_to_rom(rom)
if allow_random_on_event:
rom.write_int16(0x18637F, onevent)
rom.write_byte(0x186381, 0x00) # Enable usage of Random On Event.
if rom.read_byte(0x200000):
rom.write_byte(0x200103, onhit)
_populate_sprite_table()
if onevent:
sprites = list(set(_sprite_table.values())) # convert to list and remove dupes
else:
sprites.append(sprite)
if sprites:
while len(sprites) < 32:
sprites.extend(sprites)
local_random.shuffle(sprites)
for i, sprite in enumerate(sprites[:32]):
if not i and not onevent:
continue
rom.write_bytes(0x300000 + (i * 0x8000), sprite.sprite)
rom.write_bytes(0x307000 + (i * 0x8000), sprite.palette)
rom.write_bytes(0x307078 + (i * 0x8000), sprite.glove_palette)
def patch_enemizer(world, player: int, rom: LocalRom, enemizercli):
check_enemizer(enemizercli) check_enemizer(enemizercli)
randopatch_path = os.path.abspath(output_path(f'enemizer_randopatch_{player}.sfc')) randopatch_path = os.path.abspath(output_path(f'enemizer_randopatch_{player}.sfc'))
options_path = os.path.abspath(output_path(f'enemizer_options_{player}.json')) options_path = os.path.abspath(output_path(f'enemizer_options_{player}.json'))
@ -258,7 +292,7 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, random_sprite
'RandomizeTileTrapPattern': world.tile_shuffle[player], 'RandomizeTileTrapPattern': world.tile_shuffle[player],
'RandomizeTileTrapFloorTile': False, 'RandomizeTileTrapFloorTile': False,
'AllowKillableThief': world.killable_thieves[player], 'AllowKillableThief': world.killable_thieves[player],
'RandomizeSpriteOnHit': random_sprite_on_hit, 'RandomizeSpriteOnHit': False,
'DebugMode': False, 'DebugMode': False,
'DebugForceEnemy': False, 'DebugForceEnemy': False,
'DebugForceEnemyId': 0, 'DebugForceEnemyId': 0,
@ -334,19 +368,6 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, random_sprite
rom.write_byte(0x04DE81, 6) rom.write_byte(0x04DE81, 6)
rom.write_byte(0x200101, 0) # Do not close boss room door on entry. rom.write_byte(0x200101, 0) # Do not close boss room door on entry.
if random_sprite_on_hit:
_populate_sprite_table()
sprites = list(set(_sprite_table.values())) # convert to list and remove dupes
if sprites:
while len(sprites) < 32:
sprites.extend(sprites)
world.rom_seeds[player].shuffle(sprites)
for i, sprite in enumerate(sprites[:32]):
rom.write_bytes(0x300000 + (i * 0x8000), sprite.sprite)
rom.write_bytes(0x307000 + (i * 0x8000), sprite.palette)
rom.write_bytes(0x307078 + (i * 0x8000), sprite.glove_palette)
for used in (randopatch_path, options_path): for used in (randopatch_path, options_path):
try: try:
os.remove(used) os.remove(used)
@ -376,7 +397,7 @@ def _populate_sprite_table():
def get_sprite_from_name(name, local_random=random): def get_sprite_from_name(name, local_random=random):
_populate_sprite_table() _populate_sprite_table()
name = name.lower() name = name.lower()
if name in ['random', 'randomonhit']: if name.startswith('random'):
return local_random.choice(list(_sprite_table.values())) return local_random.choice(list(_sprite_table.values()))
return _sprite_table.get(name, None) return _sprite_table.get(name, None)
@ -411,7 +432,7 @@ class Sprite(object):
self.sprite = filedata[:0x7000] self.sprite = filedata[:0x7000]
self.palette = filedata[0x7000:0x7078] self.palette = filedata[0x7000:0x7078]
self.glove_palette = filedata[0x7078:] self.glove_palette = filedata[0x7078:]
elif len(filedata) in [0x100000, 0x200000]: elif len(filedata) in [0x100000, 0x200000, 0x400000]:
# full rom with patched sprite, extract it # full rom with patched sprite, extract it
self.sprite = filedata[0x80000:0x87000] self.sprite = filedata[0x80000:0x87000]
self.palette = filedata[0xDD308:0xDD380] self.palette = filedata[0xDD308:0xDD380]
@ -546,6 +567,9 @@ class Sprite(object):
rom.write_bytes(0x80000, self.sprite) rom.write_bytes(0x80000, self.sprite)
rom.write_bytes(0xDD308, self.palette) rom.write_bytes(0xDD308, self.palette)
rom.write_bytes(0xDEDF5, self.glove_palette) rom.write_bytes(0xDEDF5, self.glove_palette)
rom.write_bytes(0x300000, self.sprite)
rom.write_bytes(0x307000, self.palette)
rom.write_bytes(0x307078, self.glove_palette)
def patch_rom(world, rom, player, team, enemized): def patch_rom(world, rom, player, team, enemized):
local_random = world.rom_seeds[player] local_random = world.rom_seeds[player]
@ -1460,10 +1484,9 @@ def hud_format_text(text):
return output[:32] return output[:32]
def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, ow_palettes, uw_palettes, world=None, player=1): def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, ow_palettes, uw_palettes, world=None, player=1, allow_random_on_event=False):
local_random = random if not world else world.rom_seeds[player] local_random = random if not world else world.rom_seeds[player]
if sprite and not isinstance(sprite, Sprite): apply_random_sprite_on_event(rom, sprite, local_random, allow_random_on_event)
sprite = Sprite(sprite) if os.path.isfile(sprite) else get_sprite_from_name(sprite, local_random)
# enable instant item menu # enable instant item menu
if fastmenu == 'instant': if fastmenu == 'instant':
@ -1514,10 +1537,6 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr
rom.write_byte(0x6FA30, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) rom.write_byte(0x6FA30, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color])
rom.write_byte(0x65561, {'red': 0x05, 'blue': 0x0D, 'green': 0x19, 'yellow': 0x09}[color]) rom.write_byte(0x65561, {'red': 0x05, 'blue': 0x0D, 'green': 0x19, 'yellow': 0x09}[color])
# write link sprite if required
if sprite:
sprite.write_to_rom(rom)
# reset palette if it was adjusted already # reset palette if it was adjusted already
default_ow_palettes(rom) default_ow_palettes(rom)
default_uw_palettes(rom) default_uw_palettes(rom)

Binary file not shown.

View File

@ -288,7 +288,12 @@ debug: # Only available if the host uses the doors branch, it is ignored otherwi
rom: rom:
sprite: # Enter the name of your preferred sprite and weight it appropriately sprite: # Enter the name of your preferred sprite and weight it appropriately
random: 0 random: 0
randomonhit: 0 randomonhit: 0 # Random sprite on hit
randomonenter: 0 # Random sprite on entering the underworld.
randomonexit: 0 # Random sprite on exiting the underworld.
randomonslash: 0 # Random sprite on sword slashes
randomonitem: 0 # Random sprite on getting items.
# You can combine these events like this. randomonhit-enter-exit if you want it on hit, enter, exit.
Link: 50 # To add other sprites: open the gui/Creator, go to adjust, select a sprite and write down the name the gui calls it Link: 50 # To add other sprites: open the gui/Creator, go to adjust, select a sprite and write down the name the gui calls it
disablemusic: # If "on", all in-game music will be disabled disablemusic: # If "on", all in-game music will be disabled
on: 0 on: 0