Doing GitHub's work for them and ACTUALLY merging in DR v0.4.0.7 changes
This commit is contained in:
@@ -35,6 +35,7 @@ def main():
|
||||
help='Select the color of Link\'s heart meter. (default: %(default)s)')
|
||||
parser.add_argument('--ow_palettes', default='default', choices=['default', 'random', 'blackout'])
|
||||
parser.add_argument('--uw_palettes', default='default', choices=['default', 'random', 'blackout'])
|
||||
parser.add_argument('--reduce_flashing', help='Reduce some in-game flashing.', action='store_true')
|
||||
parser.add_argument('--sprite', help='''\
|
||||
Path to a sprite sheet to use for Link. Needs to be in
|
||||
binary format and have a length of 0x7000 (28672) bytes,
|
||||
|
||||
@@ -24,8 +24,10 @@ def adjust(args):
|
||||
if not hasattr(args,"sprite"):
|
||||
args.sprite = None
|
||||
|
||||
apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, args.sprite, args.ow_palettes, args.uw_palettes)
|
||||
apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic,
|
||||
args.sprite, args.ow_palettes, args.uw_palettes, args.reduce_flashing)
|
||||
|
||||
output_path.cached_path = args.outputpath
|
||||
rom.write_to_file(output_path('%s.sfc' % outfilebase))
|
||||
|
||||
logger.info('Done. Enjoy.')
|
||||
|
||||
4
CLI.py
4
CLI.py
@@ -102,7 +102,8 @@ def parse_cli(argv, no_defaults=False):
|
||||
'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters',
|
||||
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
|
||||
'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep',
|
||||
'remote_items', 'shopsanity', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code']:
|
||||
'remote_items', 'shopsanity', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code',
|
||||
'reduce_flashing']:
|
||||
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
||||
if player == 1:
|
||||
setattr(ret, name, {1: value})
|
||||
@@ -192,6 +193,7 @@ def parse_settings():
|
||||
"fastmenu": "normal",
|
||||
"ow_palettes": "default",
|
||||
"uw_palettes": "default",
|
||||
"reduce_flashing": False,
|
||||
|
||||
# Spoiler defaults to TRUE
|
||||
# Playthrough defaults to TRUE
|
||||
|
||||
@@ -99,6 +99,7 @@ def link_doors_main(world, player):
|
||||
for portal in world.dungeon_portals[player]:
|
||||
connect_portal(portal, world, player)
|
||||
|
||||
fix_big_key_doors_with_ugly_smalls(world, player)
|
||||
if world.doorShuffle[player] == 'vanilla':
|
||||
for entrance, ext in open_edges:
|
||||
connect_two_way(world, entrance, ext, player)
|
||||
@@ -674,7 +675,6 @@ def find_entrance_region(portal):
|
||||
# paired_door.pair = False
|
||||
|
||||
def within_dungeon(world, player):
|
||||
fix_big_key_doors_with_ugly_smalls(world, player)
|
||||
add_inaccessible_doors(world, player)
|
||||
entrances_map, potentials, connections = determine_entrance_list(world, player)
|
||||
connections_tuple = (entrances_map, potentials, connections)
|
||||
@@ -930,7 +930,6 @@ def treat_split_as_whole_dungeon(split_dungeon, name, origin_list, world, player
|
||||
|
||||
|
||||
def cross_dungeon(world, player):
|
||||
fix_big_key_doors_with_ugly_smalls(world, player)
|
||||
add_inaccessible_doors(world, player)
|
||||
entrances_map, potentials, connections = determine_entrance_list(world, player)
|
||||
connections_tuple = (entrances_map, potentials, connections)
|
||||
@@ -1617,13 +1616,15 @@ def reassign_key_doors(builder, world, player):
|
||||
else:
|
||||
room.delete(d.doorListPos)
|
||||
d.smallKey = False
|
||||
elif d.type is DoorType.Interior and d not in flat_proposal and d.dest not in flat_proposal and not d.entranceFlag:
|
||||
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
|
||||
elif d.type is DoorType.Interior and d not in flat_proposal and d.dest not in flat_proposal:
|
||||
if not d.entranceFlag:
|
||||
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
|
||||
d.smallKey = False
|
||||
d.dest.smallKey = False
|
||||
queue.remove(d.dest)
|
||||
elif d.type is DoorType.Normal and d not in flat_proposal and not d.entranceFlag:
|
||||
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
|
||||
elif d.type is DoorType.Normal and d not in flat_proposal:
|
||||
if not d.entranceFlag:
|
||||
world.get_room(d.roomIndex, player).change(d.doorListPos, DoorKind.Normal)
|
||||
d.smallKey = False
|
||||
for dp in world.paired_doors[player]:
|
||||
if dp.door_a == d.name or dp.door_b == d.name:
|
||||
@@ -2299,6 +2300,8 @@ logical_connections = [
|
||||
('GT Double Switch Entry to Left Barrier - Orange', 'GT Double Switch Left'),
|
||||
('GT Double Switch Entry to Ranged Switches', 'GT Double Switch Entry - Ranged Switches'),
|
||||
('GT Double Switch Entry Ranged Switches Exit', 'GT Double Switch Entry'),
|
||||
('GT Double Switch Left to Crystal', 'GT Double Switch Left - Crystal'),
|
||||
('GT Double Switch Left Crystal Exit', 'GT Double Switch Left'),
|
||||
('GT Double Switch Left to Entry Barrier - Orange', 'GT Double Switch Entry'),
|
||||
('GT Double Switch Left to Entry Bypass', 'GT Double Switch Entry'),
|
||||
('GT Double Switch Left to Pot Corners Bypass', 'GT Double Switch Pot Corners'),
|
||||
|
||||
23
Doors.py
23
Doors.py
@@ -397,10 +397,10 @@ def create_doors(world, player):
|
||||
create_door(player, 'PoD Arena Landing to Right Barrier - Blue', Lgcl),
|
||||
create_door(player, 'PoD Arena Landing to North Barrier - Orange', Lgcl),
|
||||
create_door(player, 'PoD Arena Right to Landing Barrier - Blue', Lgcl),
|
||||
create_door(player, 'PoD Arena Right to Ranged Crystal', Lgcl),
|
||||
create_door(player, 'PoD Arena Right Ranged Crystal Exit', Lgcl),
|
||||
create_door(player, 'PoD Arena Ledge to Ranged Crystal', Lgcl),
|
||||
create_door(player, 'PoD Arena Ledge Ranged Crystal Exit', Lgcl),
|
||||
create_door(player, 'PoD Arena Right to Ranged Crystal', Lgcl), # considered out of logic
|
||||
create_door(player, 'PoD Arena Right Ranged Crystal Exit', Lgcl).no_exit(), # blocked here for pre-validate
|
||||
create_door(player, 'PoD Arena Ledge to Ranged Crystal', Lgcl), # considered out of logic
|
||||
create_door(player, 'PoD Arena Ledge Ranged Crystal Exit', Lgcl).no_exit(), # blocked here for pre-validate
|
||||
create_door(player, 'PoD Arena Ledge ES', Nrml).dir(Ea, 0x2a, Bot, High).pos(2),
|
||||
create_door(player, 'PoD Sexy Statue W', Nrml).dir(We, 0x2b, Mid, High).pos(3),
|
||||
create_door(player, 'PoD Sexy Statue NW', Nrml).dir(No, 0x2b, Left, High).trap(0x1).pos(2),
|
||||
@@ -875,7 +875,7 @@ def create_doors(world, player):
|
||||
create_door(player, 'Mire Left Bridge Down Stairs', Sprl).dir(Dn, 0xa2, 0, HTL).ss(A, 0x12, 0x00),
|
||||
create_door(player, 'Mire Fishbone E', Nrml).dir(Ea, 0xa1, Mid, High).pos(1),
|
||||
create_door(player, 'Mire Fishbone Blue Barrier', Lgcl),
|
||||
create_door(player, 'Mire Fishbone Blue Barrier Bypass', Lgcl),
|
||||
create_door(player, 'Mire Fishbone Blue Barrier Bypass', Lgcl).no_exit(), # considered out of logic
|
||||
create_door(player, 'Mire South Fish Blue Barrier', Lgcl),
|
||||
create_door(player, 'Mire Fishbone SE', Nrml).dir(So, 0xa1, Right, High).small_key().pos(0),
|
||||
create_door(player, 'Mire Spike Barrier NE', Nrml).dir(No, 0xb1, Right, High).small_key().pos(1),
|
||||
@@ -1116,16 +1116,18 @@ def create_doors(world, player):
|
||||
create_door(player, 'GT Double Switch NW', Nrml).dir(No, 0x9b, Left, High).pos(1).kill(),
|
||||
create_door(player, 'GT Double Switch Entry to Pot Corners Barrier - Orange', Lgcl),
|
||||
create_door(player, 'GT Double Switch Entry to Left Barrier - Orange', Lgcl),
|
||||
create_door(player, 'GT Double Switch Entry to Ranged Switches', Lgcl),
|
||||
create_door(player, 'GT Double Switch Entry Ranged Switches Exit', Lgcl),
|
||||
create_door(player, 'GT Double Switch Entry to Ranged Switches', Lgcl), # considered out of logic
|
||||
create_door(player, 'GT Double Switch Entry Ranged Switches Exit', Lgcl).no_exit(), # blocked here for reasons
|
||||
create_door(player, 'GT Double Switch Left to Crystal', Lgcl),
|
||||
create_door(player, 'GT Double Switch Left Crystal Exit', Lgcl),
|
||||
create_door(player, 'GT Double Switch Left to Entry Barrier - Orange', Lgcl),
|
||||
create_door(player, 'GT Double Switch Left to Pot Corners Bypass', Lgcl),
|
||||
create_door(player, 'GT Double Switch Left to Entry Bypass', Lgcl),
|
||||
create_door(player, 'GT Double Switch Left to Exit Bypass', Lgcl),
|
||||
create_door(player, 'GT Double Switch Left to Exit Bypass', Lgcl).no_exit(), # considered out of logic
|
||||
create_door(player, 'GT Double Switch Pot Corners to Entry Barrier - Orange', Lgcl),
|
||||
create_door(player, 'GT Double Switch Pot Corners to Exit Barrier - Blue', Lgcl),
|
||||
create_door(player, 'GT Double Switch Pot Corners to Ranged Switches', Lgcl),
|
||||
create_door(player, 'GT Double Switch Pot Corners Ranged Switches Exit', Lgcl),
|
||||
create_door(player, 'GT Double Switch Pot Corners to Ranged Switches', Lgcl), # considered out of logic
|
||||
create_door(player, 'GT Double Switch Pot Corners Ranged Switches Exit', Lgcl).no_exit(), # blocked here
|
||||
create_door(player, 'GT Double Switch Exit to Blue Barrier', Lgcl),
|
||||
create_door(player, 'GT Double Switch EN', Intr).dir(Ea, 0x9b, Top, High).small_key().pos(0),
|
||||
create_door(player, 'GT Spike Crystals WN', Intr).dir(We, 0x9b, Top, High).small_key().pos(0),
|
||||
@@ -1438,6 +1440,7 @@ def create_doors(world, player):
|
||||
world.get_door('GT Double Switch Entry to Pot Corners Barrier - Orange', player).barrier(CrystalBarrier.Orange)
|
||||
world.get_door('GT Double Switch Entry to Left Barrier - Orange', player).barrier(CrystalBarrier.Orange)
|
||||
world.get_door('GT Double Switch Entry Ranged Switches Exit', player).c_switch()
|
||||
world.get_door('GT Double Switch Left Crystal Exit', player).c_switch()
|
||||
world.get_door('GT Double Switch Left to Entry Barrier - Orange', player).barrier(CrystalBarrier.Orange)
|
||||
world.get_door('GT Double Switch Left to Entry Bypass', player).barrier(CrystalBarrier.Blue)
|
||||
world.get_door('GT Double Switch Left to Pot Corners Bypass', player).barrier(CrystalBarrier.Blue)
|
||||
|
||||
@@ -304,7 +304,8 @@ gt_regions = [
|
||||
'GT Crystal Conveyor Corner - Ranged Crystal',
|
||||
'GT Compass Room', 'GT Invisible Bridges', 'GT Invisible Catwalk', 'GT Conveyor Cross', 'GT Hookshot East Platform',
|
||||
'GT Hookshot North Platform', 'GT Hookshot South Platform', 'GT Hookshot South Entry', 'GT Hookshot South Entry - Ranged Crystal', 'GT Map Room',
|
||||
'GT Double Switch Entry', 'GT Double Switch Pot Corners - Ranged Switches', 'GT Double Switch Pot Corners', 'GT Double Switch Left',
|
||||
'GT Double Switch Entry', 'GT Double Switch Pot Corners - Ranged Switches', 'GT Double Switch Pot Corners',
|
||||
'GT Double Switch Left', 'GT Double Switch Left - Crystal',
|
||||
'GT Double Switch Entry - Ranged Switches', 'GT Double Switch Exit', 'GT Spike Crystal Left',
|
||||
'GT Spike Crystal Right', 'GT Warp Maze - Left Section', 'GT Warp Maze - Mid Section',
|
||||
'GT Warp Maze - Right Section', 'GT Warp Maze - Pit Section', 'GT Warp Maze - Pit Exit Warp Spot',
|
||||
|
||||
4
Main.py
4
Main.py
@@ -290,7 +290,9 @@ def main(args, seed=None, fish=None):
|
||||
rom_names.append((player, team, list(rom.name)))
|
||||
world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash)
|
||||
|
||||
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player], args.ow_palettes[player], args.uw_palettes[player])
|
||||
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player],
|
||||
args.fastmenu[player], args.disablemusic[player], args.sprite[player],
|
||||
args.ow_palettes[player], args.uw_palettes[player], args.reduce_flashing[player])
|
||||
|
||||
if args.jsonout:
|
||||
jsonout[f'patch_t{team}_p{player}'] = rom.patches
|
||||
|
||||
@@ -876,7 +876,8 @@ def create_dungeon_regions(world, player):
|
||||
|
||||
create_dungeon_region(player, 'GT Double Switch Entry', 'Ganon\'s Tower', None, ['GT Double Switch NW', 'GT Double Switch Entry to Left Barrier - Orange', 'GT Double Switch Entry to Pot Corners Barrier - Orange', 'GT Double Switch Entry to Ranged Switches']),
|
||||
create_dungeon_region(player, 'GT Double Switch Entry - Ranged Switches', 'Ganon\'s Tower', None, ['GT Double Switch Entry Ranged Switches Exit']),
|
||||
create_dungeon_region(player, 'GT Double Switch Left', 'Ganon\'s Tower', None, ['GT Double Switch Left to Entry Barrier - Orange', 'GT Double Switch Left to Entry Bypass', 'GT Double Switch Left to Pot Corners Bypass', 'GT Double Switch Left to Exit Bypass']),
|
||||
create_dungeon_region(player, 'GT Double Switch Left', 'Ganon\'s Tower', None, ['GT Double Switch Left to Crystal', 'GT Double Switch Left to Entry Barrier - Orange', 'GT Double Switch Left to Entry Bypass', 'GT Double Switch Left to Pot Corners Bypass', 'GT Double Switch Left to Exit Bypass']),
|
||||
create_dungeon_region(player, 'GT Double Switch Left - Crystal', 'Ganon\'s Tower', None, ['GT Double Switch Left Crystal Exit']),
|
||||
create_dungeon_region(player, 'GT Double Switch Pot Corners', 'Ganon\'s Tower', ['Ganons Tower - Double Switch Pot Key'], ['GT Double Switch Pot Corners to Entry Barrier - Orange', 'GT Double Switch Pot Corners to Exit Barrier - Blue', 'GT Double Switch Pot Corners to Ranged Switches']),
|
||||
create_dungeon_region(player, 'GT Double Switch Pot Corners - Ranged Switches', 'Ganon\'s Tower', None, ['GT Double Switch Pot Corners Ranged Switches Exit']),
|
||||
create_dungeon_region(player, 'GT Double Switch Exit', 'Ganon\'s Tower', None, ['GT Double Switch EN', 'GT Double Switch Exit to Blue Barrier']),
|
||||
@@ -997,7 +998,7 @@ def create_dungeon_regions(world, player):
|
||||
world.get_region('GT Crystal Conveyor Corner - Ranged Crystal', player).crystal_switch = True
|
||||
world.get_region('GT Hookshot South Platform', player).crystal_switch = True
|
||||
world.get_region('GT Hookshot South Entry - Ranged Crystal', player).crystal_switch = True
|
||||
world.get_region('GT Double Switch Left', player).crystal_switch = True
|
||||
world.get_region('GT Double Switch Left - Crystal', player).crystal_switch = True
|
||||
world.get_region('GT Double Switch Entry - Ranged Switches', player).crystal_switch = True
|
||||
world.get_region('GT Double Switch Pot Corners - Ranged Switches', player).crystal_switch = True
|
||||
world.get_region('GT Spike Crystal Left', player).crystal_switch = True
|
||||
|
||||
92
Rom.py
92
Rom.py
@@ -496,8 +496,6 @@ class Sprite(object):
|
||||
sprite_name = read_utf16le(stream)
|
||||
author_name = read_utf16le(stream)
|
||||
|
||||
# Ignoring the Author Rom name for the time being.
|
||||
|
||||
real_csum = sum(filedata) % 0x10000
|
||||
if real_csum != csum or real_csum ^ 0xFFFF != icsum:
|
||||
logger.warning('ZSPR file has incorrect checksum. It may be corrupted.')
|
||||
@@ -1723,7 +1721,15 @@ def hud_format_text(text):
|
||||
return output[:32]
|
||||
|
||||
|
||||
def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, ow_palettes, uw_palettes):
|
||||
def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite,
|
||||
ow_palettes, uw_palettes, reduce_flashing):
|
||||
if not os.path.exists("data/sprites/official/001.link.1.zspr"):
|
||||
dump_zspr(rom.orig_buffer[0x80000:0x87000], rom.orig_buffer[0xdd308:0xdd380],
|
||||
rom.orig_buffer[0xdedf5:0xdedf9], "data/sprites/official/001.link.1.zspr", "Nintendo", "Link")
|
||||
|
||||
# todo: implement a flag for msu resume delay
|
||||
rom.write_bytes(0x18021D, [0, 0]) # default to off for now
|
||||
|
||||
if sprite and not isinstance(sprite, Sprite):
|
||||
sprite = Sprite(sprite) if os.path.isfile(sprite) else get_sprite_from_name(sprite)
|
||||
|
||||
@@ -1780,6 +1786,32 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr
|
||||
if sprite is not None:
|
||||
write_sprite(rom, sprite)
|
||||
|
||||
# sprite author credits
|
||||
padded_author = sprite.author_name if sprite is not None else "Nintendo"
|
||||
padded_author = padded_author[:28] if len(padded_author) > 28 else padded_author
|
||||
padded_author = padded_author.center(28).upper()
|
||||
|
||||
def convert_char_to_credits(char):
|
||||
char_map = {
|
||||
" ": (0x9F, 0x9F), "0": (0x53, 0x79), "1": (0x54, 0x7A), "2": (0x55, 0x7B), "3": (0x56, 0x7C),
|
||||
"4": (0x57, 0x7D), "5": (0x58, 0x7E), "6": (0x59, 0x7F), "7": (0x5A, 0x80), "8": (0x5B, 0x81),
|
||||
"9": (0x5C, 0x82), "A": (0x5D, 0x83), "B": (0x5E, 0x84), "C": (0x5F, 0x85), "D": (0x60, 0x86),
|
||||
"E": (0x61, 0x87), "F": (0x62, 0x88), "G": (0x63, 0x89), "H": (0x64, 0x8A), "I": (0x65, 0x8B),
|
||||
"J": (0x66, 0x8C), "K": (0x67, 0x8D), "L": (0x68, 0x8E), "M": (0x69, 0x8F), "N": (0x6A, 0x90),
|
||||
"O": (0x6B, 0x91), "P": (0x6C, 0x92), "Q": (0x6D, 0x93), "R": (0x6E, 0x94), "S": (0x6F, 0x95),
|
||||
"T": (0x70, 0x96), "U": (0x71, 0x97), "V": (0x72, 0x98), "W": (0x73, 0x99), "X": (0x74, 0x9A),
|
||||
"Y": (0x75, 0x9B), "Z": (0x76, 0x9C), "'": (0x77, 0x9d), ".": (0xA0, 0xC0), "/": (0xA2, 0xC2),
|
||||
":": (0xA3, 0xC3), "_": (0xA6, 0xC6)}
|
||||
return char_map[char] if char in char_map else (0x9F, 0x9F)
|
||||
|
||||
character_bytes = map(convert_char_to_credits, padded_author)
|
||||
for i, pair in enumerate(character_bytes):
|
||||
rom.write_byte(0x118002 + i, pair[0])
|
||||
rom.write_byte(0x118020 + i, pair[1])
|
||||
|
||||
if reduce_flashing:
|
||||
rom.write_byte(0x18017f, 1)
|
||||
|
||||
default_ow_palettes(rom)
|
||||
if ow_palettes == 'random':
|
||||
randomize_ow_palettes(rom)
|
||||
@@ -1795,6 +1827,60 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr
|
||||
if isinstance(rom, LocalRom):
|
||||
rom.write_crc()
|
||||
|
||||
# .zspr file dumping logic copied with permission from SpriteSomething:
|
||||
# https://github.com/Artheau/SpriteSomething/blob/master/source/meta/classes/spritelib.py#L443 (thanks miketrethewey!)
|
||||
def dump_zspr(basesprite, basepalette, baseglove, outfilename, author_name, sprite_name):
|
||||
palettes = basepalette
|
||||
# Add glove data
|
||||
palettes.extend(baseglove)
|
||||
HEADER_STRING = b"ZSPR"
|
||||
VERSION = 0x01
|
||||
SPRITE_TYPE = 0x01 # this format has "1" for the player sprite
|
||||
RESERVED_BYTES = b'\x00\x00\x00\x00\x00\x00'
|
||||
QUAD_BYTE_NULL_CHAR = b'\x00\x00\x00\x00'
|
||||
DOUBLE_BYTE_NULL_CHAR = b'\x00\x00'
|
||||
SINGLE_BYTE_NULL_CHAR = b'\x00'
|
||||
|
||||
write_buffer = bytearray()
|
||||
|
||||
write_buffer.extend(HEADER_STRING)
|
||||
write_buffer.extend(struct.pack('B', VERSION)) # as_u8
|
||||
checksum_start = len(write_buffer)
|
||||
write_buffer.extend(QUAD_BYTE_NULL_CHAR) # checksum
|
||||
sprite_sheet_pointer = len(write_buffer)
|
||||
write_buffer.extend(QUAD_BYTE_NULL_CHAR)
|
||||
write_buffer.extend(struct.pack('<H', len(basesprite))) # as_u16
|
||||
palettes_pointer = len(write_buffer)
|
||||
write_buffer.extend(QUAD_BYTE_NULL_CHAR)
|
||||
write_buffer.extend(struct.pack('<H', len(palettes))) # as_u16
|
||||
write_buffer.extend(struct.pack('<H', SPRITE_TYPE)) # as_u16
|
||||
write_buffer.extend(RESERVED_BYTES)
|
||||
# sprite.name
|
||||
write_buffer.extend(sprite_name.encode('utf-16-le'))
|
||||
write_buffer.extend(DOUBLE_BYTE_NULL_CHAR)
|
||||
# author.name
|
||||
write_buffer.extend(author_name.encode('utf-16-le'))
|
||||
write_buffer.extend(DOUBLE_BYTE_NULL_CHAR)
|
||||
# author.name-short
|
||||
write_buffer.extend(author_name.encode('ascii'))
|
||||
write_buffer.extend(SINGLE_BYTE_NULL_CHAR)
|
||||
write_buffer[sprite_sheet_pointer:sprite_sheet_pointer +
|
||||
4] = struct.pack('<L', len(write_buffer)) # as_u32
|
||||
write_buffer.extend(basesprite)
|
||||
write_buffer[palettes_pointer:palettes_pointer +
|
||||
4] = struct.pack('<L', len(write_buffer)) # as_u32
|
||||
write_buffer.extend(palettes)
|
||||
|
||||
checksum = (sum(write_buffer) + 0xFF + 0xFF) % 0x10000
|
||||
checksum_complement = 0xFFFF - checksum
|
||||
|
||||
write_buffer[checksum_start:checksum_start +
|
||||
2] = struct.pack('<H', checksum) # as_u16
|
||||
write_buffer[checksum_start + 2:checksum_start +
|
||||
4] = struct.pack('<H', checksum_complement) # as_u16
|
||||
|
||||
with open('%s' % outfilename, "wb") as zspr_file:
|
||||
zspr_file.write(write_buffer)
|
||||
|
||||
def write_sprite(rom, sprite):
|
||||
if not sprite.valid:
|
||||
|
||||
13
Rules.py
13
Rules.py
@@ -445,7 +445,7 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('Hera Front to Crystal', player), lambda state: state.can_hit_crystal(player))
|
||||
set_rule(world.get_entrance('Hera Down Stairs Landing to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_blue(world.get_region('Hera Down Stairs Landing', player), player))) # or state.has_beam_sword(player)
|
||||
set_rule(world.get_entrance('Hera Up Stairs Landing to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('Hera Up Stairs Landing', player), player))) # or state.has_beam_sword(player)
|
||||
set_rule(world.get_entrance('Hera Back to Ranged Crystal', player), lambda state: False) # state.can_shoot_arrows(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player) or state.has_beam_sword(player) or (state.has('Hookshot', player) and state.has('Red Boomerang', player))
|
||||
set_rule(world.get_entrance('Hera Back to Ranged Crystal', player), lambda state: state.can_shoot_arrows(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player)) # or state.has_beam_sword(player) or (state.has('Hookshot', player) and state.has('Red Boomerang', player))
|
||||
set_rule(world.get_entrance('Hera Front to Back Bypass', player), lambda state: state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player) or state.has('Cane of Somaria', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player)) # or state.has_beam_sword(player)
|
||||
set_rule(world.get_entrance('Hera Basement Cage to Crystal', player), lambda state: state.can_hit_crystal(player))
|
||||
set_rule(world.get_entrance('Hera Tridorm to Crystal', player), lambda state: state.can_hit_crystal(player))
|
||||
@@ -473,7 +473,7 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('PoD Map Balcony to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player)) # or state.has('Red Boomerang', player)
|
||||
set_rule(world.get_entrance('PoD Bow Statue Left to Crystal', player), lambda state: state.can_hit_crystal(player))
|
||||
set_rule(world.get_entrance('PoD Bow Statue Right to Ranged Crystal', player), lambda state: state.has('Cane of Somaria', player))
|
||||
set_rule(world.get_entrance('PoD Bow Statue Left to Right Bypass', player), lambda state: state.has('Cane of Somaria', player)) # or state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has_beam_sword(player) or state.has('Red Boomrang', player) or state.has('Ice Rod', player) or state.has('Fire Rod', player)
|
||||
set_rule(world.get_entrance('PoD Bow Statue Left to Right Bypass', player), lambda state: state.has('Cane of Somaria', player) or state.can_use_bombs(player) or state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Ice Rod', player) or state.has('Fire Rod', player)) # or state.has_beam_sword(player)
|
||||
set_rule(world.get_entrance('PoD Dark Pegs Landing to Ranged Crystal', player), lambda state: state.has('Cane of Somaria', player)) # or state.can_use_bombs(player) or state.has('Blue boomerang', player) or state.has('Red boomerang', player)
|
||||
set_rule(world.get_entrance('PoD Dark Pegs Middle to Ranged Crystal', player), lambda state: state.can_shoot_arrows(player) or state.can_use_bombs(player) or state.has('Red Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player) or (state.has('Hookshot', player) and state.can_reach_orange(world.get_region('PoD Dark Pegs Middle', player), player))) # or state.has_beam_sword(player)
|
||||
set_rule(world.get_entrance('PoD Dark Pegs Left to Ranged Crystal', player), lambda state: state.can_shoot_arrows(player) or state.has('Red Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player)) # or state.has_beam_sword(player)
|
||||
@@ -579,14 +579,15 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('GT Crystal Conveyor Left to Corner Barrier - Orange', player), lambda state: state.can_reach_orange(world.get_region('GT Crystal Conveyor Left', player), player))
|
||||
set_rule(world.get_entrance('GT Crystal Circles Barrier - Orange', player), lambda state: state.can_reach_orange(world.get_region('GT Crystal Circles', player), player))
|
||||
|
||||
set_rule(world.get_entrance('GT Hookshot Platform Barrier Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or state.has('Cane of Somaria', player)) # or state.has_Boots(player) /// There is a super precise trick where you can throw a pot and climp into the blue barrier, then sprint out of them.
|
||||
set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or state.has('Cane of Somaria', player))
|
||||
set_rule(world.get_entrance('GT Hookshot Platform Barrier Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player) or state.has('Cane of Somaria', player)) # or state.has_Boots(player) /// There is a super precise trick where you can throw a pot and climp into the blue barrier, then sprint out of them.
|
||||
set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) # or state.has('Cane of Somaria', player))
|
||||
set_rule(world.get_entrance('GT Double Switch Left to Crystal', player), lambda state: state.can_hit_crystal(player))
|
||||
set_rule(world.get_entrance('GT Double Switch Entry to Ranged Switches', player), lambda state: False) # state.has('Cane of Somaria', player)
|
||||
set_rule(world.get_entrance('GT Double Switch Left to Entry Bypass', player), lambda state: True) # Can always use pots
|
||||
set_rule(world.get_entrance('GT Double Switch Left to Pot Corners Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player) or state.has('Red Boomerang', player)) # or (state.has('Blue Boomerang', player) and state.has('Hookshot', player)) or (state.has('Ice Rod', player) and state.has('Hookshot', player)) or state.has('Hookshot', player) /// You can do this with just a pot and a hookshot
|
||||
set_rule(world.get_entrance('GT Double Switch Left to Exit Bypass', player), lambda state: False) # state.can_use_bombs(player) or (state.has('Cane of Somaria', player) and (state.has('Red Boomerang', player) or (state.has('Hookshot', player) and state.has('Blue Boomerang', player)) or (state.has('Hookshot', player) and state.has('Ice Rod', player))))
|
||||
set_rule(world.get_entrance('GT Double Switch Pot Corners to Ranged Switches', player), lambda state: False) # state.can_use_bombs(player) or state.has('Cane of Somaria', player) or (state.has('Cane of Somaria', player) and state.has_Boots(player)) /// There's two ways to interact with the switch. Somaria bounce at the top corner, or timed throws at the bottom corner.
|
||||
set_rule(world.get_entrance('GT Spike Crystal Left to Right Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player)) # or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.can_use_beam_sword(player)
|
||||
set_rule(world.get_entrance('GT Spike Crystal Left to Right Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player) or state.has('Red Boomerang', player) or state.has('Blue Boomerang', player) or state.has('Fire Rod', player) or state.has('Ice Rod', player)) # or state.can_use_beam_sword(player)
|
||||
set_rule(world.get_entrance('GT Crystal Conveyor to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player))
|
||||
set_rule(world.get_entrance('GT Crystal Conveyor Corner to Ranged Crystal', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player))
|
||||
set_rule(world.get_entrance('GT Crystal Conveyor Corner to Left Bypass', player), lambda state: state.can_use_bombs(player) or state.has('Cane of Somaria', player))
|
||||
@@ -1596,6 +1597,8 @@ def standard_rules(world, player):
|
||||
add_rule(world.get_entrance('Hyrule Castle Main Gate (North)', player), lambda state: state.has('Zelda Delivered', player))
|
||||
add_rule(world.get_entrance('Hyrule Castle Ledge Drop', player), lambda state: state.has('Zelda Delivered', player))
|
||||
|
||||
# don't allow bombs to get past here before zelda is rescued
|
||||
set_rule(world.get_entrance('GT Hookshot South Entry to Ranged Crystal', player), lambda state: (state.can_use_bombs(player) and state.has('Zelda Delivered', player)) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) # or state.has('Cane of Somaria', player))
|
||||
|
||||
def find_rules_for_zelda_delivery(world, player):
|
||||
# path rules for backtracking
|
||||
|
||||
@@ -239,6 +239,10 @@
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
},
|
||||
"reduce_flashing": {
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
},
|
||||
"mapshuffle": {
|
||||
"action": "store_true",
|
||||
"type": "bool"
|
||||
|
||||
@@ -306,7 +306,7 @@
|
||||
"Include the Ganon's Tower and Pyramid Hole in the",
|
||||
"entrance shuffle pool. (default: %(default)s)"
|
||||
],
|
||||
"shufflelink": [
|
||||
"shufflelinks": [
|
||||
"Include Link's House in the entrance shuffle pool. (default: %(default)s)"
|
||||
],
|
||||
"heartbeep": [
|
||||
@@ -321,6 +321,7 @@
|
||||
"Alternatively, can be a ALttP Rom patched with a Link",
|
||||
"sprite that will be extracted."
|
||||
],
|
||||
"reduce_flashing": [ "Reduce some in-game flashing (default: %(default)s)" ],
|
||||
"create_rom": [ "Create an output rom file. (default: %(default)s)" ],
|
||||
"gui": [ "Launch the GUI. (default: %(default)s)" ],
|
||||
"jsonout": [
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"checkboxes": {
|
||||
"nobgm": { "type": "checkbox" },
|
||||
"quickswap": { "type": "checkbox" }
|
||||
"quickswap": { "type": "checkbox" },
|
||||
"reduce_flashing": {"type": "checkbox"}
|
||||
},
|
||||
"leftAdjustFrame": {
|
||||
"heartcolor": {
|
||||
|
||||
@@ -148,6 +148,7 @@
|
||||
|
||||
"randomizer.gameoptions.nobgm": "Disable Music & MSU-1",
|
||||
"randomizer.gameoptions.quickswap": "L/R Quickswapping",
|
||||
"randomizer.gameoptions.reduce_flashing": "Reduce Flashing",
|
||||
|
||||
"randomizer.gameoptions.heartcolor": "Heart Color",
|
||||
"randomizer.gameoptions.heartcolor.red": "Red",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"checkboxes": {
|
||||
"nobgm": { "type": "checkbox" },
|
||||
"quickswap": { "type": "checkbox" }
|
||||
"quickswap": { "type": "checkbox" },
|
||||
"reduce_flashing": { "type": "checkbox" }
|
||||
},
|
||||
"leftRomOptionsFrame": {
|
||||
"heartcolor": {
|
||||
|
||||
@@ -110,7 +110,7 @@ class SpriteSelector(object):
|
||||
|
||||
sprites = []
|
||||
|
||||
for file in glob(output_path(path)):
|
||||
for file in glob(path):
|
||||
sprites.append(Sprite(file))
|
||||
|
||||
sprites.sort(key=lambda s: str.lower(s.name or "").strip())
|
||||
@@ -161,7 +161,8 @@ class SpriteSelector(object):
|
||||
task.update_status("Determining needed sprites")
|
||||
current_sprites = [os.path.basename(file) for file in glob(os.path.join(self.official_sprite_dir,"*"))]
|
||||
official_sprites = [(sprite['file'], os.path.basename(urlparse(sprite['file']).path)) for sprite in sprites_arr]
|
||||
needed_sprites = [(sprite_url, filename) for (sprite_url, filename) in official_sprites if filename not in current_sprites]
|
||||
needed_sprites = [(sprite_url, filename) for (sprite_url, filename) in official_sprites
|
||||
if filename not in current_sprites and filename != "001.link.1.zspr"]
|
||||
bundled_sprites = [os.path.basename(file) for file in glob(os.path.join(self.unofficial_sprite_dir,"*"))]
|
||||
# todo: eventually use the above list to avoid downloading any sprites that we already have cached in the bundle.
|
||||
|
||||
|
||||
@@ -111,7 +111,8 @@ SETTINGSTOPROCESS = {
|
||||
"heartbeep": "heartbeep",
|
||||
"menuspeed": "fastmenu",
|
||||
"owpalettes": "ow_palettes",
|
||||
"uwpalettes": "uw_palettes"
|
||||
"uwpalettes": "uw_palettes",
|
||||
"reduce_flashing": "reduce_flashing"
|
||||
},
|
||||
"generation": {
|
||||
"createspoiler": "create_spoiler",
|
||||
|
||||
@@ -101,7 +101,8 @@ def adjust_page(top, parent, settings):
|
||||
"owpalettes": "ow_palettes",
|
||||
"uwpalettes": "uw_palettes",
|
||||
"quickswap": "quickswap",
|
||||
"nobgm": "disablemusic"
|
||||
"nobgm": "disablemusic",
|
||||
"reduce_flashing": "reduce_flashing"
|
||||
}
|
||||
guiargs = Namespace()
|
||||
for option in options:
|
||||
@@ -110,6 +111,7 @@ def adjust_page(top, parent, settings):
|
||||
guiargs.rom = self.romVar2.get()
|
||||
guiargs.baserom = top.pages["randomizer"].pages["generation"].widgets["rom"].storageVar.get()
|
||||
guiargs.sprite = self.sprite
|
||||
guiargs.outputpath = os.path.dirname(guiargs.rom)
|
||||
try:
|
||||
adjust(args=guiargs)
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user