2019-12-15 15:16:49 +00:00
import bisect
2017-12-08 22:33:59 +00:00
import io
2017-06-03 12:20:39 +00:00
import json
import hashlib
import logging
2017-12-08 22:33:59 +00:00
import os
2019-07-27 13:13:13 +00:00
import random
2017-12-08 00:43:22 +00:00
import struct
2019-12-15 15:16:39 +00:00
import sys
2019-05-29 23:10:16 +00:00
import subprocess
2017-12-17 05:25:46 +00:00
2019-04-18 09:23:24 +00:00
from BaseClasses import ShopType , Region , Location , Item
2017-12-17 05:25:46 +00:00
from Dungeons import dungeon_music_addresses
2019-07-27 13:13:13 +00:00
from Text import MultiByteTextMapper , CompressedTextMapper , text_addresses , Credits , TextTable
2019-01-20 07:01:02 +00:00
from Text import Uncle_texts , Ganon1_texts , TavernMan_texts , Sahasrahla2_texts , Triforce_texts , Blind_texts , BombShop2_texts , junk_texts
2018-01-02 02:17:17 +00:00
from Text import KingsReturn_texts , Sanctuary_texts , Kakariko_texts , Blacksmiths_texts , DeathMountain_texts , LostWoods_texts , WishingWell_texts , DesertPalace_texts , MountainTower_texts , LinksHouse_texts , Lumberjacks_texts , SickKid_texts , FluteBoy_texts , Zora_texts , MagicShop_texts , Sahasrahla_names
2019-07-27 13:13:13 +00:00
from Utils import output_path , local_path , int16_as_bytes , int32_as_bytes , snes_to_pc
2019-04-18 09:23:24 +00:00
from Items import ItemFactory , item_table
2019-07-27 13:13:13 +00:00
from EntranceShuffle import door_addresses
2017-12-17 05:25:46 +00:00
2017-06-03 12:20:39 +00:00
JAP10HASH = ' 03a63945398191337e896e5771f77173 '
2019-12-09 18:27:56 +00:00
# RANDOMIZERBASEHASH = '1907d4caccffe60fc69940cfa11b2dab'
2017-05-25 10:09:50 +00:00
2017-07-14 12:37:34 +00:00
class JsonRom ( object ) :
def __init__ ( self ) :
2019-04-18 09:23:24 +00:00
self . name = None
2017-07-14 12:37:34 +00:00
self . patches = { }
2019-12-15 15:16:49 +00:00
self . addresses = [ ]
2017-07-14 12:37:34 +00:00
def write_byte ( self , address , value ) :
2019-12-15 15:16:49 +00:00
self . write_bytes ( address , [ value ] )
2017-07-14 12:37:34 +00:00
def write_bytes ( self , startaddress , values ) :
2018-08-04 20:27:41 +00:00
if not values :
return
2019-12-15 15:16:49 +00:00
if type ( values ) is not list :
values = list ( values )
pos = bisect . bisect_right ( self . addresses , startaddress )
intervalstart = self . addresses [ pos - 1 ] if pos else None
intervalpatch = self . patches [ str ( intervalstart ) ] if pos else None
if pos and startaddress < = intervalstart + len ( intervalpatch ) : # merge with previous segment
offset = startaddress - intervalstart
intervalpatch [ offset : offset + len ( values ) ] = values
startaddress = intervalstart
values = intervalpatch
else : # new segment
self . addresses . insert ( pos , startaddress )
self . patches [ str ( startaddress ) ] = values
pos = pos + 1
while pos < len ( self . addresses ) and self . addresses [ pos ] < = startaddress + len ( values ) : # merge the next segment into this one
intervalstart = self . addresses [ pos ]
values . extend ( self . patches [ str ( intervalstart ) ] [ startaddress + len ( values ) - intervalstart : ] )
del self . patches [ str ( intervalstart ) ]
del self . addresses [ pos ]
2017-11-19 01:43:37 +00:00
2017-07-14 12:37:34 +00:00
def write_to_file ( self , file ) :
2017-12-17 05:25:46 +00:00
with open ( file , ' w ' ) as stream :
json . dump ( [ self . patches ] , stream )
2017-07-14 12:37:34 +00:00
2018-09-23 02:51:54 +00:00
def get_hash ( self ) :
h = hashlib . md5 ( )
h . update ( json . dumps ( [ self . patches ] ) . encode ( ' utf-8 ' ) )
return h . hexdigest ( )
2017-07-14 12:37:34 +00:00
class LocalRom ( object ) :
2017-12-14 04:21:43 +00:00
def __init__ ( self , file , patch = True ) :
2019-04-18 09:23:24 +00:00
self . name = None
2017-12-17 05:25:46 +00:00
with open ( file , ' rb ' ) as stream :
2018-03-01 23:59:37 +00:00
self . buffer = read_rom ( stream )
2017-12-14 04:21:43 +00:00
if patch :
self . patch_base_rom ( )
2017-07-14 12:37:34 +00:00
def write_byte ( self , address , value ) :
self . buffer [ address ] = value
def write_bytes ( self , startaddress , values ) :
for i , value in enumerate ( values ) :
self . write_byte ( startaddress + i , value )
def write_to_file ( self , file ) :
with open ( file , ' wb ' ) as outfile :
outfile . write ( self . buffer )
def patch_base_rom ( self ) :
# verify correct checksum of baserom
basemd5 = hashlib . md5 ( )
basemd5 . update ( self . buffer )
2017-12-17 05:25:46 +00:00
if JAP10HASH != basemd5 . hexdigest ( ) :
2017-07-14 12:37:34 +00:00
logging . getLogger ( ' ' ) . warning ( ' Supplied Base Rom does not match known MD5 for JAP(1.0) release. Will try to patch anyway. ' )
# extend to 2MB
2019-05-29 23:10:16 +00:00
self . buffer . extend ( bytearray ( [ 0x00 ] * ( 0x200000 - len ( self . buffer ) ) ) )
2017-07-14 12:37:34 +00:00
# load randomizer patches
2017-12-17 05:25:46 +00:00
with open ( local_path ( ' data/base2current.json ' ) , ' r ' ) as stream :
patches = json . load ( stream )
2017-07-14 12:37:34 +00:00
for patch in patches :
if isinstance ( patch , dict ) :
for baseaddress , values in patch . items ( ) :
self . write_bytes ( int ( baseaddress ) , values )
# verify md5
2019-12-09 18:27:56 +00:00
# patchedmd5 = hashlib.md5()
# patchedmd5.update(self.buffer)
# if RANDOMIZERBASEHASH != patchedmd5.hexdigest():
# raise RuntimeError('Provided Base Rom unsuitable for patching. Please provide a JAP(1.0) "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc" rom to use as a base.')
2017-07-14 12:37:34 +00:00
2019-05-29 23:10:16 +00:00
def patch_enemizer ( self , rando_patch , base_enemizer_patch_path , enemizer_patch ) :
# extend to 4MB
self . buffer . extend ( bytearray ( [ 0x00 ] * ( 0x400000 - len ( self . buffer ) ) ) )
# apply randomizer patches
for address , values in rando_patch . items ( ) :
self . write_bytes ( int ( address ) , values )
# load base enemizer patches
with open ( base_enemizer_patch_path , ' r ' ) as f :
base_enemizer_patch = json . load ( f )
for patch in base_enemizer_patch :
self . write_bytes ( patch [ " address " ] , patch [ " patchData " ] )
# apply enemizer patches
for patch in enemizer_patch :
self . write_bytes ( patch [ " address " ] , patch [ " patchData " ] )
2017-07-14 12:37:34 +00:00
def write_crc ( self ) :
2017-11-10 10:08:59 +00:00
crc = ( sum ( self . buffer [ : 0x7FDC ] + self . buffer [ 0x7FE0 : ] ) + 0x01FE ) & 0xFFFF
2017-07-14 12:37:34 +00:00
inv = crc ^ 0xFFFF
self . write_bytes ( 0x7FDC , [ inv & 0xFF , ( inv >> 8 ) & 0xFF , crc & 0xFF , ( crc >> 8 ) & 0xFF ] )
2018-09-23 02:51:54 +00:00
def get_hash ( self ) :
h = hashlib . md5 ( )
h . update ( self . buffer )
return h . hexdigest ( )
2019-12-09 18:27:56 +00:00
def write_int16 ( rom , address , value ) :
rom . write_bytes ( address , int16_as_bytes ( value ) )
def write_int32 ( rom , address , value ) :
rom . write_bytes ( address , int32_as_bytes ( value ) )
def write_int16s ( rom , startaddress , values ) :
for i , value in enumerate ( values ) :
write_int16 ( rom , startaddress + ( i * 2 ) , value )
def write_int32s ( rom , startaddress , values ) :
for i , value in enumerate ( values ) :
write_int32 ( rom , startaddress + ( i * 4 ) , value )
2018-03-01 23:59:37 +00:00
def read_rom ( stream ) :
" Reads rom into bytearray and strips off any smc header "
buffer = bytearray ( stream . read ( ) )
if len ( buffer ) % 0x400 == 0x200 :
buffer = buffer [ 0x200 : ]
return buffer
2019-05-29 23:10:16 +00:00
def get_enemizer_patch ( world , player , rom , baserom_path , enemizercli , shuffleenemies , enemy_health , enemy_damage , shufflepalette , shufflepots ) :
baserom_path = os . path . abspath ( baserom_path )
basepatch_path = os . path . abspath ( local_path ( ' data/base2current.json ' ) )
randopatch_path = os . path . abspath ( output_path ( ' enemizer_randopatch.json ' ) )
options_path = os . path . abspath ( output_path ( ' enemizer_options.json ' ) )
enemizer_output_path = os . path . abspath ( output_path ( ' enemizer_output.json ' ) )
# write options file for enemizer
options = {
2019-12-16 00:35:10 +00:00
' RandomizeEnemies ' : shuffleenemies != ' none ' ,
2019-05-29 23:10:16 +00:00
' RandomizeEnemiesType ' : 3 ,
2019-12-16 00:35:10 +00:00
' RandomizeBushEnemyChance ' : shuffleenemies == ' chaos ' ,
2019-05-29 23:10:16 +00:00
' RandomizeEnemyHealthRange ' : enemy_health != ' default ' ,
' RandomizeEnemyHealthType ' : { ' default ' : 0 , ' easy ' : 0 , ' normal ' : 1 , ' hard ' : 2 , ' expert ' : 3 } [ enemy_health ] ,
' OHKO ' : False ,
' RandomizeEnemyDamage ' : enemy_damage != ' default ' ,
' AllowEnemyZeroDamage ' : True ,
' ShuffleEnemyDamageGroups ' : enemy_damage != ' default ' ,
' EnemyDamageChaosMode ' : enemy_damage == ' chaos ' ,
' EasyModeEscape ' : False ,
' EnemiesAbsorbable ' : False ,
' AbsorbableSpawnRate ' : 10 ,
' AbsorbableTypes ' : {
' FullMagic ' : True , ' SmallMagic ' : True , ' Bomb_1 ' : True , ' BlueRupee ' : True , ' Heart ' : True , ' BigKey ' : True , ' Key ' : True ,
' Fairy ' : True , ' Arrow_10 ' : True , ' Arrow_5 ' : True , ' Bomb_8 ' : True , ' Bomb_4 ' : True , ' GreenRupee ' : True , ' RedRupee ' : True
} ,
' BossMadness ' : False ,
' RandomizeBosses ' : True ,
' RandomizeBossesType ' : 0 ,
' RandomizeBossHealth ' : False ,
' RandomizeBossHealthMinAmount ' : 0 ,
' RandomizeBossHealthMaxAmount ' : 300 ,
' RandomizeBossDamage ' : False ,
' RandomizeBossDamageMinAmount ' : 0 ,
' RandomizeBossDamageMaxAmount ' : 200 ,
' RandomizeBossBehavior ' : False ,
' RandomizeDungeonPalettes ' : shufflepalette ,
' SetBlackoutMode ' : False ,
' RandomizeOverworldPalettes ' : shufflepalette ,
' RandomizeSpritePalettes ' : shufflepalette ,
' SetAdvancedSpritePalettes ' : False ,
' PukeMode ' : False ,
' NegativeMode ' : False ,
' GrayscaleMode ' : False ,
' GenerateSpoilers ' : False ,
' RandomizeLinkSpritePalette ' : False ,
' RandomizePots ' : shufflepots ,
' ShuffleMusic ' : False ,
' BootlegMagic ' : True ,
' CustomBosses ' : False ,
' AndyMode ' : False ,
' HeartBeepSpeed ' : 0 ,
' AlternateGfx ' : False ,
' ShieldGraphics ' : " shield_gfx/normal.gfx " ,
' SwordGraphics ' : " sword_gfx/normal.gfx " ,
' BeeMizer ' : False ,
' BeesLevel ' : 0 ,
2019-12-16 00:35:10 +00:00
' RandomizeTileTrapPattern ' : shuffleenemies == ' chaos ' ,
2019-05-29 23:10:16 +00:00
' RandomizeTileTrapFloorTile ' : False ,
2019-12-16 00:35:10 +00:00
' AllowKillableThief ' : bool ( random . randint ( 0 , 1 ) ) if shuffleenemies == ' chaos ' else shuffleenemies != ' none ' ,
2019-05-29 23:10:16 +00:00
' RandomizeSpriteOnHit ' : False ,
' DebugMode ' : False ,
' DebugForceEnemy ' : False ,
' DebugForceEnemyId ' : 0 ,
' DebugForceBoss ' : False ,
' DebugForceBossId ' : 0 ,
' DebugOpenShutterDoors ' : False ,
' DebugForceEnemyDamageZero ' : False ,
' DebugShowRoomIdInRupeeCounter ' : False ,
' UseManualBosses ' : True ,
' ManualBosses ' : {
' EasternPalace ' : world . get_dungeon ( " Eastern Palace " , player ) . boss . enemizer_name ,
' DesertPalace ' : world . get_dungeon ( " Desert Palace " , player ) . boss . enemizer_name ,
' TowerOfHera ' : world . get_dungeon ( " Tower of Hera " , player ) . boss . enemizer_name ,
' AgahnimsTower ' : ' Agahnim ' ,
' PalaceOfDarkness ' : world . get_dungeon ( " Palace of Darkness " , player ) . boss . enemizer_name ,
' SwampPalace ' : world . get_dungeon ( " Swamp Palace " , player ) . boss . enemizer_name ,
' SkullWoods ' : world . get_dungeon ( " Skull Woods " , player ) . boss . enemizer_name ,
' ThievesTown ' : world . get_dungeon ( " Thieves Town " , player ) . boss . enemizer_name ,
' IcePalace ' : world . get_dungeon ( " Ice Palace " , player ) . boss . enemizer_name ,
' MiseryMire ' : world . get_dungeon ( " Misery Mire " , player ) . boss . enemizer_name ,
' TurtleRock ' : world . get_dungeon ( " Turtle Rock " , player ) . boss . enemizer_name ,
' GanonsTower4 ' : ' Agahnim2 ' ,
' Ganon ' : ' Ganon ' ,
}
}
2019-12-16 15:54:46 +00:00
if world . mode [ player ] != ' inverted ' :
2019-07-27 13:13:13 +00:00
options [ ' ManualBosses ' ] [ ' GanonsTower1 ' ] = world . get_dungeon ( ' Ganons Tower ' , player ) . bosses [ ' bottom ' ] . enemizer_name
options [ ' ManualBosses ' ] [ ' GanonsTower2 ' ] = world . get_dungeon ( ' Ganons Tower ' , player ) . bosses [ ' middle ' ] . enemizer_name
options [ ' ManualBosses ' ] [ ' GanonsTower3 ' ] = world . get_dungeon ( ' Ganons Tower ' , player ) . bosses [ ' top ' ] . enemizer_name
else :
options [ ' ManualBosses ' ] [ ' GanonsTower1 ' ] = world . get_dungeon ( ' Inverted Ganons Tower ' , player ) . bosses [ ' bottom ' ] . enemizer_name
options [ ' ManualBosses ' ] [ ' GanonsTower2 ' ] = world . get_dungeon ( ' Inverted Ganons Tower ' , player ) . bosses [ ' middle ' ] . enemizer_name
options [ ' ManualBosses ' ] [ ' GanonsTower3 ' ] = world . get_dungeon ( ' Inverted Ganons Tower ' , player ) . bosses [ ' top ' ] . enemizer_name
2019-05-29 23:10:16 +00:00
rom . write_to_file ( randopatch_path )
with open ( options_path , ' w ' ) as f :
json . dump ( options , f )
subprocess . check_call ( [ os . path . abspath ( enemizercli ) ,
' --rom ' , baserom_path ,
' --seed ' , str ( world . rom_seeds [ player ] ) ,
' --base ' , basepatch_path ,
' --randomizer ' , randopatch_path ,
' --enemizer ' , options_path ,
' --output ' , enemizer_output_path ] ,
cwd = os . path . dirname ( enemizercli ) , stdout = subprocess . DEVNULL )
with open ( enemizer_output_path , ' r ' ) as f :
ret = json . load ( f )
if os . path . exists ( randopatch_path ) :
os . remove ( randopatch_path )
if os . path . exists ( options_path ) :
os . remove ( options_path )
if os . path . exists ( enemizer_output_path ) :
os . remove ( enemizer_output_path )
return ret
2017-11-13 05:29:42 +00:00
class Sprite ( object ) :
default_palette = [ 255 , 127 , 126 , 35 , 183 , 17 , 158 , 54 , 165 , 20 , 255 , 1 , 120 , 16 , 157 ,
89 , 71 , 54 , 104 , 59 , 74 , 10 , 239 , 18 , 92 , 42 , 113 , 21 , 24 , 122 ,
255 , 127 , 126 , 35 , 183 , 17 , 158 , 54 , 165 , 20 , 255 , 1 , 120 , 16 , 157 ,
89 , 128 , 105 , 145 , 118 , 184 , 38 , 127 , 67 , 92 , 42 , 153 , 17 , 24 , 122 ,
255 , 127 , 126 , 35 , 183 , 17 , 158 , 54 , 165 , 20 , 255 , 1 , 120 , 16 , 157 ,
89 , 87 , 16 , 126 , 69 , 243 , 109 , 185 , 126 , 92 , 42 , 39 , 34 , 24 , 122 ,
255 , 127 , 126 , 35 , 218 , 17 , 158 , 54 , 165 , 20 , 255 , 1 , 120 , 16 , 151 ,
61 , 71 , 54 , 104 , 59 , 74 , 10 , 239 , 18 , 126 , 86 , 114 , 24 , 24 , 122 ]
default_glove_palette = [ 246 , 82 , 118 , 3 ]
def __init__ ( self , filename ) :
2017-12-08 00:43:22 +00:00
with open ( filename , ' rb ' ) as file :
filedata = bytearray ( file . read ( ) )
2017-12-08 22:33:59 +00:00
self . name = os . path . basename ( filename )
self . author_name = None
2017-12-08 00:43:22 +00:00
self . valid = True
2017-11-13 05:29:42 +00:00
if len ( filedata ) == 0x7000 :
# sprite file with graphics and without palette data
self . sprite = filedata [ : 0x7000 ]
2017-12-17 22:30:50 +00:00
self . palette = list ( self . default_palette )
self . glove_palette = list ( self . default_glove_palette )
2017-11-13 05:29:42 +00:00
elif len ( filedata ) == 0x7078 :
# sprite file with graphics and palette data
self . sprite = filedata [ : 0x7000 ]
self . palette = filedata [ 0x7000 : ]
self . glove_palette = filedata [ 0x7036 : 0x7038 ] + filedata [ 0x7054 : 0x7056 ]
2017-12-08 00:43:22 +00:00
elif len ( filedata ) == 0x707C :
# sprite file with graphics and palette data including gloves
self . sprite = filedata [ : 0x7000 ]
self . palette = filedata [ 0x7000 : 0x7078 ]
self . glove_palette = filedata [ 0x7078 : ]
2017-11-13 05:29:42 +00:00
elif len ( filedata ) in [ 0x100000 , 0x200000 ] :
# full rom with patched sprite, extract it
self . sprite = filedata [ 0x80000 : 0x87000 ]
self . palette = filedata [ 0xDD308 : 0xDD380 ]
self . glove_palette = filedata [ 0xDEDF5 : 0xDEDF9 ]
2017-12-08 00:43:22 +00:00
elif filedata . startswith ( b ' ZSPR ' ) :
2017-12-17 05:25:46 +00:00
result = self . parse_zspr ( filedata , 1 )
2017-12-08 00:43:22 +00:00
if result is None :
self . valid = False
return
2017-12-08 22:33:59 +00:00
( sprite , palette , self . name , self . author_name ) = result
2017-12-08 00:43:22 +00:00
if len ( sprite ) != 0x7000 :
self . valid = False
return
self . sprite = sprite
if len ( palette ) == 0 :
2017-12-17 22:30:50 +00:00
self . palette = list ( self . default_palette )
self . glove_palette = list ( self . default_glove_palette )
2017-12-08 00:43:22 +00:00
elif len ( palette ) == 0x78 :
self . palette = palette
2017-12-17 22:30:50 +00:00
self . glove_palette = list ( self . default_glove_palette )
2017-12-08 00:43:22 +00:00
elif len ( palette ) == 0x7C :
self . palette = palette [ : 0x78 ]
self . glove_palette = palette [ 0x78 : ]
else :
self . valid = False
2017-11-13 05:29:42 +00:00
else :
2017-12-17 05:25:46 +00:00
self . valid = False
2017-11-13 05:29:42 +00:00
2018-01-05 05:42:35 +00:00
@staticmethod
def default_link_sprite ( ) :
return Sprite ( local_path ( ' data/default.zspr ' ) )
2017-11-13 05:29:42 +00:00
def decode8 ( self , pos ) :
2017-12-17 05:25:46 +00:00
arr = [ [ 0 for _ in range ( 8 ) ] for _ in range ( 8 ) ]
2017-11-13 05:29:42 +00:00
for y in range ( 8 ) :
for x in range ( 8 ) :
position = 1 << ( 7 - x )
2017-12-17 05:25:46 +00:00
val = 0
if self . sprite [ pos + 2 * y ] & position :
val + = 1
if self . sprite [ pos + 2 * y + 1 ] & position :
val + = 2
if self . sprite [ pos + 2 * y + 16 ] & position :
val + = 4
if self . sprite [ pos + 2 * y + 17 ] & position :
val + = 8
arr [ y ] [ x ] = val
2017-11-13 05:29:42 +00:00
return arr
def decode16 ( self , pos ) :
2017-12-17 05:25:46 +00:00
arr = [ [ 0 for _ in range ( 16 ) ] for _ in range ( 16 ) ]
2017-11-13 05:29:42 +00:00
top_left = self . decode8 ( pos )
top_right = self . decode8 ( pos + 0x20 )
bottom_left = self . decode8 ( pos + 0x200 )
bottom_right = self . decode8 ( pos + 0x220 )
for x in range ( 8 ) :
for y in range ( 8 ) :
arr [ y ] [ x ] = top_left [ y ] [ x ]
arr [ y ] [ x + 8 ] = top_right [ y ] [ x ]
arr [ y + 8 ] [ x ] = bottom_left [ y ] [ x ]
arr [ y + 8 ] [ x + 8 ] = bottom_right [ y ] [ x ]
return arr
2017-12-08 00:43:22 +00:00
def parse_zspr ( self , filedata , expected_kind ) :
2017-12-08 22:33:59 +00:00
logger = logging . getLogger ( ' ' )
2017-12-08 00:43:22 +00:00
headerstr = " <4xBHHIHIHH6x "
headersize = struct . calcsize ( headerstr )
if len ( filedata ) < headersize :
return None
2017-12-17 05:25:46 +00:00
( version , csum , icsum , sprite_offset , sprite_size , palette_offset , palette_size , kind ) = struct . unpack_from ( headerstr , filedata )
2017-12-08 00:43:22 +00:00
if version not in [ 1 ] :
2017-12-08 22:33:59 +00:00
logger . error ( ' Error parsing ZSPR file: Version %g not supported ' , version )
2017-12-08 00:43:22 +00:00
return None
if kind != expected_kind :
return None
2017-12-08 22:33:59 +00:00
stream = io . BytesIO ( filedata )
stream . seek ( headersize )
def read_utf16le ( stream ) :
" Decodes a null-terminated UTF-16_LE string of unknown size from a stream "
raw = bytearray ( )
while True :
char = stream . read ( 2 )
if char in [ b ' ' , b ' \x00 \x00 ' ] :
break
raw + = char
return raw . decode ( ' utf-16_le ' )
sprite_name = read_utf16le ( stream )
author_name = read_utf16le ( stream )
# Ignoring the Author Rom name for the time being.
2017-12-08 00:43:22 +00:00
real_csum = sum ( filedata ) % 0x10000
if real_csum != csum or real_csum ^ 0xFFFF != icsum :
2017-12-08 22:33:59 +00:00
logger . warning ( ' ZSPR file has incorrect checksum. It may be corrupted. ' )
2017-12-17 05:25:46 +00:00
2017-12-08 00:43:22 +00:00
sprite = filedata [ sprite_offset : sprite_offset + sprite_size ]
palette = filedata [ palette_offset : palette_offset + palette_size ]
2017-12-08 22:33:59 +00:00
if len ( sprite ) != sprite_size or len ( palette ) != palette_size :
logger . error ( ' Error parsing ZSPR file: Unexpected end of file ' )
return None
return ( sprite , palette , sprite_name , author_name )
2017-12-08 00:43:22 +00:00
2017-11-13 05:29:42 +00:00
def decode_palette ( self ) :
" Returns the palettes as an array of arrays of 15 colors "
2017-12-17 05:25:46 +00:00
def array_chunk ( arr , size ) :
2017-11-13 05:29:42 +00:00
return list ( zip ( * [ iter ( arr ) ] * size ) )
def make_int16 ( pair ) :
return pair [ 1 ] << 8 | pair [ 0 ]
def expand_color ( i ) :
2017-12-17 05:25:46 +00:00
return ( ( i & 0x1F ) * 8 , ( i >> 5 & 0x1F ) * 8 , ( i >> 10 & 0x1F ) * 8 )
2017-11-13 05:29:42 +00:00
raw_palette = self . palette
2017-12-17 05:25:46 +00:00
if raw_palette is None :
raw_palette = Sprite . default_palette
2017-11-13 05:29:42 +00:00
# turn palette data into a list of RGB tuples with 8 bit values
palette_as_colors = [ expand_color ( make_int16 ( chnk ) ) for chnk in array_chunk ( raw_palette , 2 ) ]
# split into palettes of 15 colors
return array_chunk ( palette_as_colors , 15 )
2019-12-15 09:54:49 +00:00
def patch_rom ( world , player , rom , enemized ) :
2019-04-18 09:23:24 +00:00
random . seed ( world . rom_seeds [ player ] )
2019-08-22 02:40:19 +00:00
# progressive bow silver arrow hint hack
prog_bow_locs = world . find_items ( ' Progressive Bow ' , player )
if len ( prog_bow_locs ) > 1 :
# only pick a distingushed bow if we have at least two
distinguished_prog_bow_loc = random . choice ( prog_bow_locs )
distinguished_prog_bow_loc . item . code = 0x65
2017-05-25 10:09:50 +00:00
# patch items
for location in world . get_locations ( ) :
2019-04-18 09:23:24 +00:00
if location . player != player :
continue
2017-05-25 10:09:50 +00:00
itemid = location . item . code if location . item is not None else 0x5A
2019-04-18 09:23:24 +00:00
if location . address is None :
2017-06-17 12:40:37 +00:00
continue
2017-05-25 15:47:15 +00:00
if not location . crystal :
2017-11-04 18:23:57 +00:00
# Keys in their native dungeon should use the orignal item code for keys
2017-10-15 20:04:58 +00:00
if location . parent_region . dungeon :
2017-11-04 18:23:57 +00:00
dungeon = location . parent_region . dungeon
2019-12-13 21:37:52 +00:00
if location . item is not None and dungeon . is_dungeon_item ( location . item ) :
if location . item . bigkey :
2017-11-04 18:23:57 +00:00
itemid = 0x32
2019-12-13 21:37:52 +00:00
if location . item . smallkey :
2017-11-04 18:23:57 +00:00
itemid = 0x24
2019-12-16 09:58:25 +00:00
if location . item . map :
itemid = 0x33
if location . item . compass :
itemid = 0x25
2019-12-09 18:27:56 +00:00
if location . item and location . item . player != player :
if location . player_address is not None :
rom . write_byte ( location . player_address , location . item . player )
else :
itemid = 0x5A
2019-04-18 09:23:24 +00:00
rom . write_byte ( location . address , itemid )
2017-05-25 15:47:15 +00:00
else :
2017-05-25 10:09:50 +00:00
# crystals
2019-04-18 09:23:24 +00:00
for address , value in zip ( location . address , itemid ) :
2017-07-14 12:37:34 +00:00
rom . write_byte ( address , value )
2017-05-25 10:09:50 +00:00
# patch music
music_addresses = dungeon_music_addresses [ location . name ]
2019-12-13 21:37:52 +00:00
if world . mapshuffle :
2017-10-28 22:34:37 +00:00
music = random . choice ( [ 0x11 , 0x16 ] )
else :
music = 0x11 if ' Pendant ' in location . item . name else 0x16
2017-05-25 10:09:50 +00:00
for music_address in music_addresses :
2017-07-14 12:37:34 +00:00
rom . write_byte ( music_address , music )
2017-11-04 18:23:57 +00:00
2019-12-13 21:37:52 +00:00
if world . mapshuffle :
rom . write_byte ( 0x155C9 , random . choice ( [ 0x11 , 0x16 ] ) ) # Randomize GT music too with map shuffle
2017-06-17 11:16:13 +00:00
2018-02-25 06:04:41 +00:00
# patch entrance/exits/holes
2017-05-25 10:09:50 +00:00
for region in world . regions :
for exit in region . exits :
2019-04-18 09:23:24 +00:00
if exit . target is not None and exit . player == player :
2018-01-17 01:56:35 +00:00
if isinstance ( exit . addresses , tuple ) :
offset = exit . target
room_id , ow_area , vram_loc , scroll_y , scroll_x , link_y , link_x , camera_y , camera_x , unknown_1 , unknown_2 , door_1 , door_2 = exit . addresses
#room id is deliberately not written
rom . write_byte ( 0x15B8C + offset , ow_area )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15BDB + 2 * offset , vram_loc )
write_int16 ( rom , 0x15C79 + 2 * offset , scroll_y )
write_int16 ( rom , 0x15D17 + 2 * offset , scroll_x )
2018-01-17 01:56:35 +00:00
# for positioning fixups we abuse the roomid as a way of identifying which exit data we are appling
# Thanks to Zarby89 for originally finding these values
# todo fix screen scrolling
2018-02-20 23:28:41 +00:00
if world . shuffle not in [ ' insanity ' , ' insanity_legacy ' , ' madness_legacy ' ] and \
2018-02-20 04:20:01 +00:00
exit . name in [ ' Eastern Palace Exit ' , ' Tower of Hera Exit ' , ' Thieves Town Exit ' , ' Skull Woods Final Section Exit ' , ' Ice Palace Exit ' , ' Misery Mire Exit ' ,
' Palace of Darkness Exit ' , ' Swamp Palace Exit ' , ' Ganons Tower Exit ' , ' Desert Palace Exit (North) ' , ' Agahnims Tower Exit ' , ' Spiral Cave Exit (Top) ' ,
' Superbunny Cave Exit (Bottom) ' , ' Turtle Rock Ledge Exit (East) ' ] :
2018-02-10 01:27:34 +00:00
# For exits that connot be reached from another, no need to apply offset fixes.
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15DB5 + 2 * offset , link_y ) # same as final else
2018-02-10 01:27:34 +00:00
elif room_id == 0x0059 and world . fix_skullwoods_exit :
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15DB5 + 2 * offset , 0x00F8 )
2018-01-17 01:56:35 +00:00
elif room_id == 0x004a and world . fix_palaceofdarkness_exit :
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15DB5 + 2 * offset , 0x0640 )
2018-01-17 01:56:35 +00:00
elif room_id == 0x00d6 and world . fix_trock_exit :
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15DB5 + 2 * offset , 0x0134 )
2018-01-17 01:56:35 +00:00
elif room_id == 0x000c and world . fix_gtower_exit : # fix ganons tower exit point
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15DB5 + 2 * offset , 0x00A4 )
2018-01-17 01:56:35 +00:00
else :
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15DB5 + 2 * offset , link_y )
2018-01-19 02:36:06 +00:00
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15E53 + 2 * offset , link_x )
write_int16 ( rom , 0x15EF1 + 2 * offset , camera_y )
write_int16 ( rom , 0x15F8F + 2 * offset , camera_x )
2018-01-17 01:56:35 +00:00
rom . write_byte ( 0x1602D + offset , unknown_1 )
rom . write_byte ( 0x1607C + offset , unknown_2 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x160CB + 2 * offset , door_1 )
write_int16 ( rom , 0x16169 + 2 * offset , door_2 )
2018-01-17 01:56:35 +00:00
elif isinstance ( exit . addresses , list ) :
# is hole
for address in exit . addresses :
rom . write_byte ( address , exit . target )
else :
# patch door table
rom . write_byte ( 0xDBB73 + exit . addresses , exit . target )
2019-12-16 15:54:46 +00:00
if world . mode [ player ] == ' inverted ' :
2019-07-27 13:13:13 +00:00
patch_shuffled_dark_sanc ( world , rom , player )
2019-04-18 09:23:24 +00:00
write_custom_shops ( rom , world , player )
2017-06-17 11:16:13 +00:00
2017-05-25 10:09:50 +00:00
# patch medallion requirements
2019-04-18 09:23:24 +00:00
if world . required_medallions [ player ] [ 0 ] == ' Bombos ' :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180022 , 0x00 ) # requirement
rom . write_byte ( 0x4FF2 , 0x31 ) # sprite
rom . write_byte ( 0x50D1 , 0x80 )
rom . write_byte ( 0x51B0 , 0x00 )
2019-04-18 09:23:24 +00:00
elif world . required_medallions [ player ] [ 0 ] == ' Quake ' :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180022 , 0x02 ) # requirement
rom . write_byte ( 0x4FF2 , 0x31 ) # sprite
rom . write_byte ( 0x50D1 , 0x88 )
rom . write_byte ( 0x51B0 , 0x00 )
2019-04-18 09:23:24 +00:00
if world . required_medallions [ player ] [ 1 ] == ' Bombos ' :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180023 , 0x00 ) # requirement
rom . write_byte ( 0x5020 , 0x31 ) # sprite
rom . write_byte ( 0x50FF , 0x90 )
rom . write_byte ( 0x51DE , 0x00 )
2019-04-18 09:23:24 +00:00
elif world . required_medallions [ player ] [ 1 ] == ' Ether ' :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180023 , 0x01 ) # requirement
rom . write_byte ( 0x5020 , 0x31 ) # sprite
rom . write_byte ( 0x50FF , 0x98 )
rom . write_byte ( 0x51DE , 0x00 )
2017-05-25 10:09:50 +00:00
# set open mode:
2019-12-16 15:54:46 +00:00
if world . mode [ player ] in [ ' open ' , ' inverted ' ] :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180032 , 0x01 ) # open mode
2019-12-16 15:54:46 +00:00
if world . mode [ player ] == ' inverted ' :
2019-08-07 01:36:45 +00:00
set_inverted_mode ( world , rom )
2019-12-16 15:54:46 +00:00
elif world . mode [ player ] == ' standard ' :
2019-08-07 01:36:45 +00:00
rom . write_byte ( 0x180032 , 0x00 ) # standard mode
2017-05-25 10:09:50 +00:00
2019-08-07 01:36:45 +00:00
uncle_location = world . get_location ( ' Link \' s Uncle ' , player )
if uncle_location . item is None or uncle_location . item . name not in [ ' Master Sword ' , ' Tempered Sword ' , ' Fighter Sword ' , ' Golden Sword ' , ' Progressive Sword ' ] :
2017-05-25 10:09:50 +00:00
# disable sword sprite from uncle
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x6D263 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
rom . write_bytes ( 0x6D26B , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
rom . write_bytes ( 0x6D293 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
rom . write_bytes ( 0x6D29B , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x00 , 0x0E ] )
rom . write_bytes ( 0x6D2B3 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x02 , 0x0E ] )
rom . write_bytes ( 0x6D2BB , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x02 , 0x0E ] )
rom . write_bytes ( 0x6D2E3 , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x02 , 0x0E ] )
rom . write_bytes ( 0x6D2EB , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x02 , 0x0E ] )
rom . write_bytes ( 0x6D31B , [ 0x00 , 0x00 , 0xe4 , 0xff , 0x08 , 0x0E ] )
rom . write_bytes ( 0x6D323 , [ 0x00 , 0x00 , 0xe4 , 0xff , 0x08 , 0x0E ] )
2017-06-03 13:46:05 +00:00
# set light cones
2019-12-16 15:54:46 +00:00
rom . write_byte ( 0x180038 , 0x01 if world . sewer_light_cone [ player ] else 0x00 )
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180039 , 0x01 if world . light_world_light_cone else 0x00 )
rom . write_byte ( 0x18003A , 0x01 if world . dark_world_light_cone else 0x00 )
2017-05-25 10:09:50 +00:00
2018-01-04 06:06:22 +00:00
GREEN_TWENTY_RUPEES = 0x47
2019-04-18 09:23:24 +00:00
TRIFORCE_PIECE = ItemFactory ( ' Triforce Piece ' , player ) . code
GREEN_CLOCK = ItemFactory ( ' Green Clock ' , player ) . code
2018-01-04 06:06:22 +00:00
2018-03-02 01:29:18 +00:00
rom . write_byte ( 0x18004F , 0x01 ) # Byrna Invulnerability: on
2019-08-10 23:37:26 +00:00
# handle difficulty_adjustments
if world . difficulty_adjustments == ' hard ' :
2017-05-25 10:09:50 +00:00
# Powdered Fairies Prize
2017-11-10 10:08:59 +00:00
rom . write_byte ( 0x36DD0 , 0xD8 ) # One Heart
# potion heal amount
2018-03-14 18:30:11 +00:00
rom . write_byte ( 0x180084 , 0x38 ) # Seven Hearts
2017-11-10 10:08:59 +00:00
# potion magic restore amount
rom . write_byte ( 0x180085 , 0x40 ) # Half Magic
#Cape magic cost
2019-08-04 16:32:35 +00:00
rom . write_bytes ( 0x3ADA7 , [ 0x02 , 0x04 , 0x08 ] )
2018-03-02 01:29:18 +00:00
# Byrna Invulnerability: off
rom . write_byte ( 0x18004F , 0x00 )
2017-11-10 10:08:59 +00:00
#Disable catching fairies
rom . write_byte ( 0x34FD6 , 0x80 )
2018-01-04 06:06:22 +00:00
overflow_replacement = GREEN_TWENTY_RUPEES
2017-12-08 21:53:53 +00:00
# Rupoor negative value
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x180036 , world . rupoor_cost )
2018-03-14 18:30:11 +00:00
# Set stun items
2018-03-30 02:07:05 +00:00
rom . write_byte ( 0x180180 , 0x02 ) # Hookshot only
2019-08-10 23:37:26 +00:00
elif world . difficulty_adjustments == ' expert ' :
2017-11-10 10:08:59 +00:00
# Powdered Fairies Prize
2018-03-14 18:30:11 +00:00
rom . write_byte ( 0x36DD0 , 0xD8 ) # One Heart
2017-05-25 10:09:50 +00:00
# potion heal amount
2019-08-04 16:32:35 +00:00
rom . write_byte ( 0x180084 , 0x20 ) # 4 Hearts
2017-05-25 10:09:50 +00:00
# potion magic restore amount
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180085 , 0x20 ) # Quarter Magic
2017-11-10 10:08:59 +00:00
#Cape magic cost
2019-08-04 16:32:35 +00:00
rom . write_bytes ( 0x3ADA7 , [ 0x02 , 0x04 , 0x08 ] )
2018-03-02 01:29:18 +00:00
# Byrna Invulnerability: off
rom . write_byte ( 0x18004F , 0x00 )
2017-11-10 10:08:59 +00:00
#Disable catching fairies
rom . write_byte ( 0x34FD6 , 0x80 )
2018-01-04 06:06:22 +00:00
overflow_replacement = GREEN_TWENTY_RUPEES
2017-12-08 21:53:53 +00:00
# Rupoor negative value
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x180036 , world . rupoor_cost )
2018-03-14 18:30:11 +00:00
# Set stun items
rom . write_byte ( 0x180180 , 0x00 ) # Nothing
2017-06-04 12:44:23 +00:00
else :
# Powdered Fairies Prize
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x36DD0 , 0xE3 ) # fairy
2017-06-04 12:44:23 +00:00
# potion heal amount
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180084 , 0xA0 ) # full
2017-06-04 12:44:23 +00:00
# potion magic restore amount
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180085 , 0x80 ) # full
2017-12-08 21:53:53 +00:00
#Cape magic cost
rom . write_bytes ( 0x3ADA7 , [ 0x04 , 0x08 , 0x10 ] )
2018-03-02 01:29:18 +00:00
# Byrna Invulnerability: on
rom . write_byte ( 0x18004F , 0x01 )
2017-12-08 21:53:53 +00:00
#Enable catching fairies
rom . write_byte ( 0x34FD6 , 0xF0 )
2018-03-14 18:30:11 +00:00
# Rupoor negative value
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x180036 , world . rupoor_cost )
2018-03-14 18:30:11 +00:00
# Set stun items
rom . write_byte ( 0x180180 , 0x03 ) # All standard items
2017-11-11 21:25:56 +00:00
#Set overflow items for progressive equipment
2018-03-30 02:01:43 +00:00
if world . timer in [ ' timed ' , ' timed-countdown ' , ' timed-ohko ' ] :
2018-01-04 06:06:22 +00:00
overflow_replacement = GREEN_CLOCK
2017-11-11 21:25:56 +00:00
else :
2018-01-04 06:06:22 +00:00
overflow_replacement = GREEN_TWENTY_RUPEES
2019-08-10 23:37:26 +00:00
rom . write_byte ( 0x180181 , 0x00 ) # Make silver arrows freely usable
2019-08-04 16:32:35 +00:00
rom . write_byte ( 0x180182 , 0x01 ) # auto equip silvers on pickup
2018-03-24 17:18:14 +00:00
2018-03-02 01:29:18 +00:00
#Byrna residual magic cost
rom . write_bytes ( 0x45C42 , [ 0x04 , 0x02 , 0x01 ] )
2018-01-04 06:06:22 +00:00
difficulty = world . difficulty_requirements
2019-08-10 23:37:26 +00:00
2018-01-04 06:06:22 +00:00
#Set overflow items for progressive equipment
2019-12-09 18:27:56 +00:00
mw_sword_replacements = { 0 : overflow_replacement ,
1 : item_table [ ' Fighter Sword ' ] [ 3 ] ,
2 : item_table [ ' Master Sword ' ] [ 3 ] ,
3 : item_table [ ' Tempered Sword ' ] [ 3 ] ,
4 : item_table [ ' Golden Sword ' ] [ 3 ] }
mw_shield_replacements = { 0 : overflow_replacement ,
1 : item_table [ ' Blue Shield ' ] [ 3 ] ,
2 : item_table [ ' Red Shield ' ] [ 3 ] ,
3 : item_table [ ' Mirror Shield ' ] [ 3 ] }
mw_armor_replacements = { 0 : overflow_replacement ,
1 : item_table [ ' Blue Mail ' ] [ 3 ] ,
2 : item_table [ ' Red Mail ' ] [ 3 ] }
mw_bottle_replacements = { 0 : overflow_replacement ,
1 : item_table [ ' Blue Potion ' ] [ 3 ] ,
2 : item_table [ ' Blue Potion ' ] [ 3 ] ,
3 : item_table [ ' Blue Potion ' ] [ 3 ] ,
4 : item_table [ ' Blue Potion ' ] [ 3 ] }
mw_bow_replacements = { 0 : overflow_replacement ,
1 : item_table [ ' Bow ' ] [ 3 ] ,
2 : item_table [ ' Bow ' ] [ 3 ] }
2018-01-04 06:06:22 +00:00
rom . write_bytes ( 0x180090 ,
2019-12-09 18:27:56 +00:00
[ difficulty . progressive_sword_limit , mw_sword_replacements [ difficulty . progressive_sword_limit ] if world . players > 1 else overflow_replacement ,
difficulty . progressive_shield_limit , mw_shield_replacements [ difficulty . progressive_shield_limit ] if world . players > 1 else overflow_replacement ,
difficulty . progressive_armor_limit , mw_armor_replacements [ difficulty . progressive_armor_limit ] if world . players > 1 else overflow_replacement ,
difficulty . progressive_bottle_limit , mw_bottle_replacements [ difficulty . progressive_bottle_limit ] if world . players > 1 else overflow_replacement ,
difficulty . progressive_bow_limit , mw_bow_replacements [ difficulty . progressive_bow_limit ] if world . players > 1 else overflow_replacement ] )
2019-12-16 13:31:47 +00:00
if difficulty . progressive_bow_limit < 2 and world . swords [ player ] == ' swordless ' :
2019-12-09 18:27:56 +00:00
rom . write_bytes ( 0x180098 , [ 2 , mw_bow_replacements [ difficulty . progressive_bow_limit ] if world . players > 1 else overflow_replacement ] )
2019-08-26 02:28:12 +00:00
rom . write_byte ( 0x180181 , 0x01 ) # Make silver arrows work only on ganon
2017-05-25 10:09:50 +00:00
# set up game internal RNG seed
for i in range ( 1024 ) :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x178000 + i , random . randint ( 0 , 255 ) )
2017-05-25 10:09:50 +00:00
# shuffle prize packs
prizes = [ 0xD8 , 0xD8 , 0xD8 , 0xD8 , 0xD9 , 0xD8 , 0xD8 , 0xD9 , 0xDA , 0xD9 , 0xDA , 0xDB , 0xDA , 0xD9 , 0xDA , 0xDA , 0xE0 , 0xDF , 0xDF , 0xDA , 0xE0 , 0xDF , 0xD8 , 0xDF ,
0xDC , 0xDC , 0xDC , 0xDD , 0xDC , 0xDC , 0xDE , 0xDC , 0xE1 , 0xD8 , 0xE1 , 0xE2 , 0xE1 , 0xD8 , 0xE1 , 0xE2 , 0xDF , 0xD9 , 0xD8 , 0xE1 , 0xDF , 0xDC , 0xD9 , 0xD8 ,
0xD8 , 0xE3 , 0xE0 , 0xDB , 0xDE , 0xD8 , 0xDB , 0xE2 , 0xD9 , 0xDA , 0xDB , 0xD9 , 0xDB , 0xD9 , 0xDB ]
2018-02-17 23:38:54 +00:00
dig_prizes = [ 0xB2 , 0xD8 , 0xD8 , 0xD8 , 0xD8 , 0xD8 , 0xD8 , 0xD8 , 0xD8 ,
0xD9 , 0xD9 , 0xD9 , 0xD9 , 0xD9 , 0xDA , 0xDA , 0xDA , 0xDA , 0xDA ,
0xDB , 0xDB , 0xDB , 0xDB , 0xDB , 0xDC , 0xDC , 0xDC , 0xDC , 0xDC ,
0xDD , 0xDD , 0xDD , 0xDD , 0xDD , 0xDE , 0xDE , 0xDE , 0xDE , 0xDE ,
0xDF , 0xDF , 0xDF , 0xDF , 0xDF , 0xE0 , 0xE0 , 0xE0 , 0xE0 , 0xE0 ,
0xE1 , 0xE1 , 0xE1 , 0xE1 , 0xE1 , 0xE2 , 0xE2 , 0xE2 , 0xE2 , 0xE2 ,
0xE3 , 0xE3 , 0xE3 , 0xE3 , 0xE3 ]
2019-08-04 15:37:44 +00:00
def chunk ( l , n ) :
return [ l [ i : i + n ] for i in range ( 0 , len ( l ) , n ) ]
# randomize last 7 slots
prizes [ - 7 : ] = random . sample ( prizes , 7 )
#shuffle order of 7 main packs
packs = chunk ( prizes [ : 56 ] , 8 )
random . shuffle ( packs )
prizes [ : 56 ] = [ drop for pack in packs for drop in pack ]
2019-08-10 23:37:26 +00:00
if world . difficulty_adjustments in [ ' hard ' , ' expert ' ] :
2018-09-23 02:51:54 +00:00
prize_replacements = { 0xE0 : 0xDF , # Fairy -> heart
0xE3 : 0xD8 } # Big magic -> small magic
prizes = [ prize_replacements . get ( prize , prize ) for prize in prizes ]
dig_prizes = [ prize_replacements . get ( prize , prize ) for prize in dig_prizes ]
2018-02-17 23:38:54 +00:00
if world . retro :
prize_replacements = { 0xE1 : 0xDA , #5 Arrows -> Blue Rupee
0xE2 : 0xDB } #10 Arrows -> Red Rupee
2018-09-23 02:51:54 +00:00
prizes = [ prize_replacements . get ( prize , prize ) for prize in prizes ]
dig_prizes = [ prize_replacements . get ( prize , prize ) for prize in dig_prizes ]
2018-02-17 23:38:54 +00:00
rom . write_bytes ( 0x180100 , dig_prizes )
2017-05-25 10:09:50 +00:00
# write tree pull prizes
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0xEFBD4 , prizes . pop ( ) )
rom . write_byte ( 0xEFBD5 , prizes . pop ( ) )
rom . write_byte ( 0xEFBD6 , prizes . pop ( ) )
2017-05-25 10:09:50 +00:00
# rupee crab prizes
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x329C8 , prizes . pop ( ) ) # first prize
rom . write_byte ( 0x329C4 , prizes . pop ( ) ) # final prize
2017-05-25 10:09:50 +00:00
# stunned enemy prize
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x37993 , prizes . pop ( ) )
2017-05-25 10:09:50 +00:00
# saved fish prize
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0xE82CC , prizes . pop ( ) )
2017-05-25 10:09:50 +00:00
# fill enemy prize packs
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x37A78 , prizes )
2017-05-25 10:09:50 +00:00
# set bonk prizes
2017-12-08 21:53:53 +00:00
bonk_prizes = [ 0x79 , 0xE3 , 0x79 , 0xAC , 0xAC , 0xE0 , 0xDC , 0xAC , 0xE3 , 0xE3 , 0xDA , 0xE3 , 0xDA , 0xD8 , 0xAC , 0xAC , 0xE3 , 0xD8 , 0xE3 , 0xE3 , 0xE3 , 0xE3 , 0xE3 , 0xE3 , 0xDC , 0xDB , 0xE3 , 0xDA , 0x79 , 0x79 , 0xE3 , 0xE3 ,
0xDA , 0x79 , 0xAC , 0xAC , 0x79 , 0xE3 , 0x79 , 0xAC , 0xAC , 0xE0 , 0xDC , 0xE3 , 0x79 , 0xDE , 0xE3 , 0xAC , 0xDB , 0x79 , 0xE3 , 0xD8 , 0xAC , 0x79 , 0xE3 , 0xDB , 0xDB , 0xE3 , 0xE3 , 0x79 , 0xD8 , 0xDD ]
bonk_addresses = [ 0x4CF6C , 0x4CFBA , 0x4CFE0 , 0x4CFFB , 0x4D018 , 0x4D01B , 0x4D028 , 0x4D03C , 0x4D059 , 0x4D07A , 0x4D09E , 0x4D0A8 , 0x4D0AB , 0x4D0AE , 0x4D0BE , 0x4D0DD ,
0x4D16A , 0x4D1E5 , 0x4D1EE , 0x4D20B , 0x4CBBF , 0x4CBBF , 0x4CC17 , 0x4CC1A , 0x4CC4A , 0x4CC4D , 0x4CC53 , 0x4CC69 , 0x4CC6F , 0x4CC7C , 0x4CCEF , 0x4CD51 ,
0x4CDC0 , 0x4CDC3 , 0x4CDC6 , 0x4CE37 , 0x4D2DE , 0x4D32F , 0x4D355 , 0x4D367 , 0x4D384 , 0x4D387 , 0x4D397 , 0x4D39E , 0x4D3AB , 0x4D3AE , 0x4D3D1 , 0x4D3D7 ,
0x4D3F8 , 0x4D416 , 0x4D420 , 0x4D423 , 0x4D42D , 0x4D449 , 0x4D48C , 0x4D4D9 , 0x4D4DC , 0x4D4E3 , 0x4D504 , 0x4D507 , 0x4D55E , 0x4D56A ]
2017-07-14 12:37:34 +00:00
if world . shuffle_bonk_prizes :
random . shuffle ( bonk_prizes )
2017-12-08 21:53:53 +00:00
for prize , address in zip ( bonk_prizes , bonk_addresses ) :
rom . write_byte ( address , prize )
2018-03-02 01:31:35 +00:00
# Fill in item substitutions table
2019-08-04 16:32:35 +00:00
rom . write_bytes ( 0x184000 , [
# original_item, limit, replacement_item, filler
0x12 , 0x01 , 0x35 , 0xFF , # lamp -> 5 rupees
0x51 , 0x06 , 0x52 , 0xFF , # 6 +5 bomb upgrades -> +10 bomb upgrade
0x53 , 0x06 , 0x54 , 0xFF , # 6 +5 arrow upgrades -> +10 arrow upgrade
2019-08-10 23:37:26 +00:00
0x58 , 0x01 , 0x36 if world . retro else 0x43 , 0xFF , # silver arrows -> single arrow (red 20 in retro mode)
0x3E , difficulty . boss_heart_container_limit , 0x47 , 0xff , # boss heart -> green 20
0x17 , difficulty . heart_piece_limit , 0x47 , 0xff , # piece of heart -> green 20
2019-08-04 16:32:35 +00:00
0xFF , 0xFF , 0xFF , 0xFF , # end of table sentinel
] )
2017-05-25 10:09:50 +00:00
2017-05-25 15:52:31 +00:00
# set Fountain bottle exchange items
2019-08-04 16:32:35 +00:00
if world . difficulty in [ ' hard ' , ' expert ' ] :
2017-11-11 21:25:56 +00:00
rom . write_byte ( 0x348FF , [ 0x16 , 0x2B , 0x2C , 0x2D , 0x3C , 0x48 ] [ random . randint ( 0 , 5 ) ] )
rom . write_byte ( 0x3493B , [ 0x16 , 0x2B , 0x2C , 0x2D , 0x3C , 0x48 ] [ random . randint ( 0 , 5 ) ] )
else :
rom . write_byte ( 0x348FF , [ 0x16 , 0x2B , 0x2C , 0x2D , 0x3C , 0x3D , 0x48 ] [ random . randint ( 0 , 6 ) ] )
rom . write_byte ( 0x3493B , [ 0x16 , 0x2B , 0x2C , 0x2D , 0x3C , 0x3D , 0x48 ] [ random . randint ( 0 , 6 ) ] )
2017-12-08 21:53:53 +00:00
#enable Fat Fairy Chests
rom . write_bytes ( 0x1FC16 , [ 0xB1 , 0xC6 , 0xF9 , 0xC9 , 0xC6 , 0xF9 ] )
2017-06-03 12:20:39 +00:00
# set Fat Fairy Bow/Sword prizes to be disappointing
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x34914 , 0x3A ) # Bow and Arrow
rom . write_byte ( 0x180028 , 0x49 ) # Fighter Sword
2017-08-01 15:25:08 +00:00
# enable Waterfall fairy chests
rom . write_bytes ( 0xE9AE , [ 0x14 , 0x01 ] )
rom . write_bytes ( 0xE9CF , [ 0x14 , 0x01 ] )
2017-08-03 21:45:14 +00:00
rom . write_bytes ( 0x1F714 , [ 225 , 0 , 16 , 172 , 13 , 41 , 154 , 1 , 88 , 152 , 15 , 17 , 177 , 97 , 252 , 77 , 129 , 32 , 218 , 2 , 44 , 225 , 97 , 252 , 190 , 129 , 97 , 177 , 98 , 84 , 218 , 2 ,
253 , 141 , 131 , 68 , 225 , 98 , 253 , 30 , 131 , 49 , 165 , 201 , 49 , 164 , 105 , 49 , 192 , 34 , 77 , 164 , 105 , 49 , 198 , 249 , 73 , 198 , 249 , 16 , 153 , 160 , 92 , 153 ,
162 , 11 , 152 , 96 , 13 , 232 , 192 , 85 , 232 , 192 , 11 , 146 , 0 , 115 , 152 , 96 , 254 , 105 , 0 , 152 , 163 , 97 , 254 , 107 , 129 , 254 , 171 , 133 , 169 , 200 , 97 , 254 ,
174 , 129 , 255 , 105 , 2 , 216 , 163 , 98 , 255 , 107 , 131 , 255 , 43 , 135 , 201 , 200 , 98 , 255 , 46 , 131 , 254 , 161 , 0 , 170 , 33 , 97 , 254 , 166 , 129 , 255 , 33 , 2 ,
202 , 33 , 98 , 255 , 38 , 131 , 187 , 35 , 250 , 195 , 35 , 250 , 187 , 43 , 250 , 195 , 43 , 250 , 187 , 83 , 250 , 195 , 83 , 250 , 176 , 160 , 61 , 152 , 19 , 192 , 152 , 82 ,
192 , 136 , 0 , 96 , 144 , 0 , 96 , 232 , 0 , 96 , 240 , 0 , 96 , 152 , 202 , 192 , 216 , 202 , 192 , 216 , 19 , 192 , 216 , 82 , 192 , 252 , 189 , 133 , 253 , 29 , 135 , 255 ,
255 , 255 , 255 , 240 , 255 , 128 , 46 , 97 , 14 , 129 , 14 , 255 , 255 ] )
2017-10-14 18:45:59 +00:00
# set Waterfall fairy prizes to be disappointing
2018-03-03 18:24:32 +00:00
rom . write_byte ( 0x348DB , 0x3A ) # Red Boomerang becomes Red Boomerang
2017-10-14 18:45:59 +00:00
rom . write_byte ( 0x348EB , 0x05 ) # Blue Shield becomes Blue Shield
2017-06-03 12:20:39 +00:00
2018-09-23 02:51:54 +00:00
# Remove Statues for upgrade fairy
rom . write_bytes ( 0x01F810 , [ 0x1A , 0x1E , 0x01 , 0x1A , 0x1E , 0x01 ] )
2017-12-08 21:53:53 +00:00
rom . write_byte ( 0x180029 , 0x01 ) # Smithy quick item give
2017-06-23 19:33:04 +00:00
# set swordless mode settings
2019-12-16 13:31:47 +00:00
rom . write_byte ( 0x18003F , 0x01 if world . swords [ player ] == ' swordless ' else 0x00 ) # hammer can harm ganon
rom . write_byte ( 0x180040 , 0x01 if world . swords [ player ] == ' swordless ' else 0x00 ) # open curtains
rom . write_byte ( 0x180041 , 0x01 if world . swords [ player ] == ' swordless ' else 0x00 ) # swordless medallions
rom . write_byte ( 0x180043 , 0xFF if world . swords [ player ] == ' swordless ' else 0x00 ) # starting sword for link
rom . write_byte ( 0x180044 , 0x01 if world . swords [ player ] == ' swordless ' else 0x00 ) # hammer activates tablets
2017-06-23 19:33:04 +00:00
2017-06-04 11:10:22 +00:00
# set up clocks for timed modes
2017-12-14 21:04:28 +00:00
if world . shuffle == ' vanilla ' :
ERtimeincrease = 0
elif world . shuffle in [ ' dungeonssimple ' , ' dungeonsfull ' ] :
2017-12-17 05:25:46 +00:00
ERtimeincrease = 10
2017-12-14 21:04:28 +00:00
else :
2017-12-17 05:25:46 +00:00
ERtimeincrease = 20
2019-12-13 21:37:52 +00:00
if world . keyshuffle or world . bigkeyshuffle or world . mapshuffle :
2017-12-14 21:04:28 +00:00
ERtimeincrease = ERtimeincrease + 15
2017-06-04 11:10:22 +00:00
if world . clock_mode == ' off ' :
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x180190 , [ 0x00 , 0x00 , 0x00 ] ) # turn off clock mode
2019-12-09 18:27:56 +00:00
write_int32 ( rom , 0x180200 , 0 ) # red clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180204 , 0 ) # blue clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180208 , 0 ) # green clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x18020C , 0 ) # starting time (in frames, sint32)
2017-06-04 11:10:22 +00:00
elif world . clock_mode == ' ohko ' :
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x180190 , [ 0x01 , 0x02 , 0x01 ] ) # ohko timer with resetable timer functionality
2019-12-09 18:27:56 +00:00
write_int32 ( rom , 0x180200 , 0 ) # red clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180204 , 0 ) # blue clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180208 , 0 ) # green clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x18020C , 0 ) # starting time (in frames, sint32)
2017-11-19 01:36:42 +00:00
elif world . clock_mode == ' countdown-ohko ' :
rom . write_bytes ( 0x180190 , [ 0x01 , 0x02 , 0x01 ] ) # ohko timer with resetable timer functionality
2019-12-09 18:27:56 +00:00
write_int32 ( rom , 0x180200 , - 100 * 60 * 60 * 60 ) # red clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180204 , 2 * 60 * 60 ) # blue clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180208 , 4 * 60 * 60 ) # green clock adjustment time (in frames, sint32)
2019-08-10 23:37:26 +00:00
if world . difficulty_adjustments == ' normal ' :
2019-12-09 18:27:56 +00:00
write_int32 ( rom , 0x18020C , ( 10 + ERtimeincrease ) * 60 * 60 ) # starting time (in frames, sint32)
2017-11-12 02:19:29 +00:00
else :
2019-12-09 18:27:56 +00:00
write_int32 ( rom , 0x18020C , int ( ( 5 + ERtimeincrease / 2 ) * 60 * 60 ) ) # starting time (in frames, sint32)
2017-06-04 11:10:22 +00:00
if world . clock_mode == ' stopwatch ' :
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x180190 , [ 0x02 , 0x01 , 0x00 ] ) # set stopwatch mode
2019-12-09 18:27:56 +00:00
write_int32 ( rom , 0x180200 , - 2 * 60 * 60 ) # red clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180204 , 2 * 60 * 60 ) # blue clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180208 , 4 * 60 * 60 ) # green clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x18020C , 0 ) # starting time (in frames, sint32)
2017-06-04 11:10:22 +00:00
if world . clock_mode == ' countdown ' :
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x180190 , [ 0x01 , 0x01 , 0x00 ] ) # set countdown, with no reset available
2019-12-09 18:27:56 +00:00
write_int32 ( rom , 0x180200 , - 2 * 60 * 60 ) # red clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180204 , 2 * 60 * 60 ) # blue clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x180208 , 4 * 60 * 60 ) # green clock adjustment time (in frames, sint32)
write_int32 ( rom , 0x18020C , ( 40 + ERtimeincrease ) * 60 * 60 ) # starting time (in frames, sint32)
2017-06-04 11:10:22 +00:00
# set up goals for treasure hunt
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x180165 , [ 0x0E , 0x28 ] if world . treasure_hunt_icon == ' Triforce Piece ' else [ 0x0D , 0x28 ] )
rom . write_byte ( 0x180167 , world . treasure_hunt_count % 256 )
2019-08-29 01:12:44 +00:00
rom . write_byte ( 0x180194 , 1 ) # Must turn in triforced pieces (instant win not enabled)
2017-06-04 11:10:22 +00:00
2017-12-08 21:53:53 +00:00
rom . write_bytes ( 0x180213 , [ 0x00 , 0x01 ] ) # Not a Tournament Seed
2019-12-15 09:54:49 +00:00
gametype = 0x04 # item
if world . shuffle != ' vanilla ' :
gametype | = 0x02 # entrance
if enemized :
gametype | = 0x01 # enemizer
rom . write_byte ( 0x180211 , gametype ) # Game type
2018-03-02 01:31:35 +00:00
2017-06-03 12:20:39 +00:00
# assorted fixes
2019-08-04 16:32:35 +00:00
rom . write_byte ( 0x1800A2 , 0x01 ) # remain in real dark world when dying in dark world dungeon before killing aga1
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180169 , 0x01 if world . lock_aga_door_in_escape else 0x00 ) # Lock or unlock aga tower door during escape sequence.
2019-12-16 15:54:46 +00:00
if world . mode [ player ] == ' inverted ' :
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x180169 , 0x02 ) # lock aga/ganon tower door with crystals in inverted
2019-04-18 09:23:24 +00:00
rom . write_byte ( 0x180171 , 0x01 if world . ganon_at_pyramid [ player ] else 0x00 ) # Enable respawning on pyramid after ganon death
2018-03-02 01:31:35 +00:00
rom . write_byte ( 0x180173 , 0x01 ) # Bob is enabled
2017-11-10 10:08:59 +00:00
rom . write_byte ( 0x180168 , 0x08 ) # Spike Cave Damage
2017-12-08 21:53:53 +00:00
rom . write_bytes ( 0x18016B , [ 0x04 , 0x02 , 0x01 ] ) #Set spike cave and MM spike room Cape usage
rom . write_bytes ( 0x18016E , [ 0x04 , 0x08 , 0x10 ] ) #Set spike cave and MM spike room Cape usage
rom . write_bytes ( 0x50563 , [ 0x3F , 0x14 ] ) # disable below ganon chest
rom . write_byte ( 0x50599 , 0x00 ) # disable below ganon chest
rom . write_bytes ( 0xE9A5 , [ 0x7E , 0x00 , 0x24 ] ) # disable below ganon chest
2019-12-12 08:20:32 +00:00
rom . write_byte ( 0x18008B , 0x01 if world . open_pyramid else 0x00 ) # pre-open Pyramid Hole
rom . write_byte ( 0x18008C , 0x01 if world . crystals_needed_for_gt == 0 else 0x00 ) # GT pre-opened if crystal requirement is 0
2017-12-16 14:36:29 +00:00
rom . write_byte ( 0xF5D73 , 0xF0 ) # bees are catchable
rom . write_byte ( 0xF5F10 , 0xF0 ) # bees are catchable
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180086 , 0x00 if world . aga_randomness else 0x01 ) # set blue ball and ganon warp randomness
2017-12-08 21:53:53 +00:00
rom . write_byte ( 0x1800A0 , 0x01 ) # return to light world on s+q without mirror
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x1800A1 , 0x01 ) # enable overworld screen transition draining for water level inside swamp
2018-01-25 01:07:33 +00:00
rom . write_byte ( 0x180174 , 0x01 if world . fix_fake_world else 0x00 )
2018-03-15 21:23:02 +00:00
rom . write_byte ( 0x18017E , 0x01 ) # Fairy fountains only trade in bottles
2018-01-30 01:46:16 +00:00
rom . write_byte ( 0x180034 , 0x0A ) # starting max bombs
rom . write_byte ( 0x180035 , 30 ) # starting max arrows
for x in range ( 0x183000 , 0x18304F ) :
rom . write_byte ( x , 0 ) # Zero the initial equipment array
rom . write_byte ( 0x18302C , 0x18 ) # starting max health
rom . write_byte ( 0x18302D , 0x18 ) # starting current health
rom . write_byte ( 0x183039 , 0x68 ) # starting abilities, bit array
2019-08-10 19:30:14 +00:00
for item in world . precollected_items :
if item . player != player :
continue
if item . name == ' Fighter Sword ' :
rom . write_byte ( 0x183000 + 0x19 , 0x01 )
rom . write_byte ( 0x0271A6 + 0x19 , 0x01 )
rom . write_byte ( 0x180043 , 0x01 ) # special starting sword byte
else :
raise RuntimeError ( " Unsupported pre-collected item: {} " . format ( item ) )
2019-12-16 15:54:46 +00:00
rom . write_byte ( 0x18004A , 0x00 if world . mode [ player ] != ' inverted ' else 0x01 ) # Inverted mode
2018-09-23 02:51:54 +00:00
rom . write_byte ( 0x18005D , 0x00 ) # Hammer always breaks barrier
2019-12-16 15:54:46 +00:00
rom . write_byte ( 0x2AF79 , 0xD0 if world . mode [ player ] != ' inverted ' else 0xF0 ) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both)
rom . write_byte ( 0x3A943 , 0xD0 if world . mode [ player ] != ' inverted ' else 0xF0 ) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both)
rom . write_byte ( 0x3A96D , 0xF0 if world . mode [ player ] != ' inverted ' else 0xD0 ) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader))
2019-08-24 01:46:49 +00:00
rom . write_byte ( 0x3A9A7 , 0xD0 ) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader))
2018-03-02 01:31:35 +00:00
2018-09-23 02:51:54 +00:00
rom . write_bytes ( 0x180080 , [ 50 , 50 , 70 , 70 ] ) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10)
2019-12-16 15:54:46 +00:00
rom . write_byte ( 0x18004D , ( ( 0x01 if ' arrows ' in world . escape_assist [ player ] else 0x00 ) |
( 0x02 if ' bombs ' in world . escape_assist [ player ] else 0x00 ) |
( 0x04 if ' magic ' in world . escape_assist [ player ] else 0x00 ) ) ) # Escape assist
2017-12-08 21:53:53 +00:00
2019-12-16 14:27:20 +00:00
if world . goal [ player ] in [ ' pedestal ' , ' triforcehunt ' ] :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x18003E , 0x01 ) # make ganon invincible
2019-12-16 14:27:20 +00:00
elif world . goal [ player ] in [ ' dungeons ' ] :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x18003E , 0x02 ) # make ganon invincible until all dungeons are beat
2019-12-16 14:27:20 +00:00
elif world . goal [ player ] in [ ' crystals ' ] :
2017-08-01 17:07:44 +00:00
rom . write_byte ( 0x18003E , 0x04 ) # make ganon invincible until all crystals
2017-12-08 21:53:53 +00:00
else :
rom . write_byte ( 0x18003E , 0x03 ) # make ganon invincible until all crystals and aga 2 are collected
2019-07-25 22:25:14 +00:00
rom . write_byte ( 0x18005E , world . crystals_needed_for_gt )
rom . write_byte ( 0x18005F , world . crystals_needed_for_ganon )
2019-12-16 15:54:46 +00:00
rom . write_byte ( 0x18008A , 0x01 if world . mode [ player ] == " standard " else 0x00 ) # block HC upstairs doors in rain state in standard mode
2019-07-25 22:25:14 +00:00
2019-12-13 21:37:52 +00:00
rom . write_byte ( 0x18016A , 0x10 | ( ( 0x01 if world . keyshuffle else 0x00 )
| ( 0x02 if world . compassshuffle else 0x00 )
| ( 0x04 if world . mapshuffle else 0x00 )
| ( 0x08 if world . bigkeyshuffle else 0x00 ) ) ) # free roaming item text boxes
rom . write_byte ( 0x18003B , 0x01 if world . mapshuffle else 0x00 ) # maps showing crystals on overworld
2017-11-04 18:23:57 +00:00
2017-10-28 22:34:37 +00:00
# compasses showing dungeon count
if world . clock_mode != ' off ' :
2017-11-04 18:23:57 +00:00
rom . write_byte ( 0x18003C , 0x00 ) # Currently must be off if timer is on, because they use same HUD location
2019-12-13 21:37:52 +00:00
elif world . compassshuffle :
2017-11-04 18:23:57 +00:00
rom . write_byte ( 0x18003C , 0x01 ) # show on pickup
2017-10-28 22:34:37 +00:00
else :
rom . write_byte ( 0x18003C , 0x00 )
2017-11-04 18:23:57 +00:00
2019-12-13 21:37:52 +00:00
rom . write_byte ( 0x180045 , ( ( 0x01 if world . keyshuffle else 0x00 )
| ( 0x02 if world . bigkeyshuffle else 0x00 )
| ( 0x04 if world . compassshuffle else 0x00 )
| ( 0x08 if world . mapshuffle else 0x00 ) ) ) # free roaming items in menu
2019-08-04 17:22:37 +00:00
# Map reveals
reveal_bytes = {
" Eastern Palace " : 0x2000 ,
" Desert Palace " : 0x1000 ,
" Tower of Hera " : 0x0020 ,
" Palace of Darkness " : 0x0200 ,
" Thieves Town " : 0x0010 ,
" Skull Woods " : 0x0080 ,
" Swamp Palace " : 0x0400 ,
" Ice Palace " : 0x0040 ,
" Misery Mire ' " : 0x0100 ,
" Turtle Rock " : 0x0008 ,
}
def get_reveal_bytes ( itemName ) :
locations = world . find_items ( itemName , player )
if len ( locations ) < 1 :
return 0x0000
location = locations [ 0 ]
if location . parent_region and location . parent_region . dungeon :
return reveal_bytes . get ( location . parent_region . dungeon . name , 0x0000 )
return 0x0000
2019-12-13 21:37:52 +00:00
write_int16 ( rom , 0x18017A , get_reveal_bytes ( ' Green Pendant ' ) if world . mapshuffle else 0x0000 ) # Sahasrahla reveal
write_int16 ( rom , 0x18017C , get_reveal_bytes ( ' Crystal 5 ' ) | get_reveal_bytes ( ' Crystal 6 ' ) if world . mapshuffle else 0x0000 ) # Bomb Shop Reveal
2019-08-04 17:22:37 +00:00
2018-03-15 21:23:02 +00:00
rom . write_byte ( 0x180172 , 0x01 if world . retro else 0x00 ) # universal keys
rom . write_byte ( 0x180175 , 0x01 if world . retro else 0x00 ) # rupee bow
rom . write_byte ( 0x180176 , 0x0A if world . retro else 0x00 ) # wood arrow cost
rom . write_byte ( 0x180178 , 0x32 if world . retro else 0x00 ) # silver arrow cost
rom . write_byte ( 0x301FC , 0xDA if world . retro else 0xE1 ) # rupees replace arrows under pots
2018-03-20 15:20:24 +00:00
rom . write_byte ( 0x30052 , 0xDB if world . retro else 0xE2 ) # replace arrows in fish prize from bottle merchant
2018-03-15 21:23:02 +00:00
rom . write_bytes ( 0xECB4E , [ 0xA9 , 0x00 , 0xEA , 0xEA ] if world . retro else [ 0xAF , 0x77 , 0xF3 , 0x7E ] ) # Thief steals rupees instead of arrows
rom . write_bytes ( 0xF0D96 , [ 0xA9 , 0x00 , 0xEA , 0xEA ] if world . retro else [ 0xAF , 0x77 , 0xF3 , 0x7E ] ) # Pikit steals rupees instead of arrows
rom . write_bytes ( 0xEDA5 , [ 0x35 , 0x41 ] if world . retro else [ 0x43 , 0x44 ] ) # Chest game gives rupees instead of arrows
2017-06-23 19:33:04 +00:00
digging_game_rng = random . randint ( 1 , 30 ) # set rng for digging game
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180020 , digging_game_rng )
rom . write_byte ( 0xEFD95 , digging_game_rng )
rom . write_byte ( 0x1800A3 , 0x01 ) # enable correct world setting behaviour after agahnim kills
2019-12-16 12:26:07 +00:00
rom . write_byte ( 0x1800A4 , 0x01 if world . logic [ player ] != ' nologic ' else 0x00 ) # enable POD EG fix
2018-03-15 21:23:02 +00:00
rom . write_byte ( 0x180042 , 0x01 if world . save_and_quit_from_boss else 0x00 ) # Allow Save and Quit after boss kill
2017-06-03 12:20:39 +00:00
# remove shield from uncle
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x6D253 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
rom . write_bytes ( 0x6D25B , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
rom . write_bytes ( 0x6D283 , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x00 , 0x0E ] )
rom . write_bytes ( 0x6D28B , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x00 , 0x0E ] )
rom . write_bytes ( 0x6D2CB , [ 0x00 , 0x00 , 0xf6 , 0xff , 0x02 , 0x0E ] )
rom . write_bytes ( 0x6D2FB , [ 0x00 , 0x00 , 0xf7 , 0xff , 0x02 , 0x0E ] )
rom . write_bytes ( 0x6D313 , [ 0x00 , 0x00 , 0xe4 , 0xff , 0x08 , 0x0E ] )
2017-05-25 15:52:31 +00:00
2019-08-07 01:36:45 +00:00
rom . write_byte ( 0x18004E , 0 ) # Escape Fill (nothing)
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x180183 , 300 ) # Escape fill rupee bow
2019-08-07 01:36:45 +00:00
rom . write_bytes ( 0x180185 , [ 0 , 0 , 0 ] ) # Uncle respawn refills (magic, bombs, arrows)
rom . write_bytes ( 0x180188 , [ 0 , 0 , 0 ] ) # Zelda respawn refills (magic, bombs, arrows)
rom . write_bytes ( 0x18018B , [ 0 , 0 , 0 ] ) # Mantle respawn refills (magic, bombs, arrows)
2019-12-16 15:54:46 +00:00
if world . mode [ player ] == ' standard ' :
2019-08-07 01:36:45 +00:00
if uncle_location . item is not None and uncle_location . item . name in [ ' Bow ' , ' Progressive Bow ' ] :
rom . write_byte ( 0x18004E , 1 ) # Escape Fill (arrows)
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x180183 , 300 ) # Escape fill rupee bow
2019-08-07 01:36:45 +00:00
rom . write_bytes ( 0x180185 , [ 0 , 0 , 70 ] ) # Uncle respawn refills (magic, bombs, arrows)
rom . write_bytes ( 0x180188 , [ 0 , 0 , 10 ] ) # Zelda respawn refills (magic, bombs, arrows)
rom . write_bytes ( 0x18018B , [ 0 , 0 , 10 ] ) # Mantle respawn refills (magic, bombs, arrows)
2019-12-11 10:41:05 +00:00
elif uncle_location . item is not None and uncle_location . item . name in [ ' Bombs (10) ' ] :
rom . write_byte ( 0x18004E , 2 ) # Escape Fill (bombs)
rom . write_bytes ( 0x180185 , [ 0 , 50 , 0 ] ) # Uncle respawn refills (magic, bombs, arrows)
rom . write_bytes ( 0x180188 , [ 0 , 3 , 0 ] ) # Zelda respawn refills (magic, bombs, arrows)
rom . write_bytes ( 0x18018B , [ 0 , 3 , 0 ] ) # Mantle respawn refills (magic, bombs, arrows)
2019-08-07 01:36:45 +00:00
elif uncle_location . item is not None and uncle_location . item . name in [ ' Cane of Somaria ' , ' Cane of Byrna ' , ' Fire Rod ' ] :
rom . write_byte ( 0x18004E , 4 ) # Escape Fill (magic)
rom . write_bytes ( 0x180185 , [ 0x80 , 0 , 0 ] ) # Uncle respawn refills (magic, bombs, arrows)
rom . write_bytes ( 0x180188 , [ 0x20 , 0 , 0 ] ) # Zelda respawn refills (magic, bombs, arrows)
rom . write_bytes ( 0x18018B , [ 0x20 , 0 , 0 ] ) # Mantle respawn refills (magic, bombs, arrows)
2017-12-08 21:53:53 +00:00
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved
2019-04-18 09:23:24 +00:00
rom . write_byte ( 0x18003D , 0x01 if world . swamp_patch_required [ player ] else 0x00 )
2017-05-25 10:09:50 +00:00
2019-04-18 09:23:24 +00:00
# powder patch: remove the need to leave the screen after powder, since it causes problems for potion shop at race game
2018-02-12 04:25:19 +00:00
# temporarally we are just nopping out this check we will conver this to a rom fix soon.
2019-04-18 09:23:24 +00:00
rom . write_bytes ( 0x02F539 , [ 0xEA , 0xEA , 0xEA , 0xEA , 0xEA ] if world . powder_patch_required [ player ] else [ 0xAD , 0xBF , 0x0A , 0xF0 , 0x4F ] )
2018-02-12 04:25:19 +00:00
2018-02-25 06:04:41 +00:00
# allow smith into multi-entrance caves in appropriate shuffles
if world . shuffle in [ ' restricted ' , ' full ' , ' crossed ' , ' insanity ' ] :
rom . write_byte ( 0x18004C , 0x01 )
2017-05-25 10:09:50 +00:00
# set correct flag for hera basement item
2019-04-18 09:23:24 +00:00
hera_basement = world . get_location ( ' Tower of Hera - Basement Cage ' , player )
if hera_basement . item is not None and hera_basement . item . name == ' Small Key (Tower of Hera) ' and hera_basement . item . player == player :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x4E3BB , 0xE4 )
2017-05-25 10:09:50 +00:00
else :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x4E3BB , 0xEB )
2017-05-25 10:50:42 +00:00
2017-05-28 13:40:59 +00:00
# fix trock doors for reverse entrances
2019-12-16 15:54:46 +00:00
if world . fix_trock_doors [ player ] :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0xFED31 , 0x0E ) # preopen bombable exit
rom . write_byte ( 0xFEE41 , 0x0E ) # preopen bombable exit
2017-12-08 21:53:53 +00:00
# included unconditionally in base2current
#rom.write_byte(0xFE465, 0x1E) # remove small key door on backside of big key door
else :
rom . write_byte ( 0xFED31 , 0x2A ) # preopen bombable exit
rom . write_byte ( 0xFEE41 , 0x2A ) # preopen bombable exit
2017-05-28 13:40:59 +00:00
2019-04-18 09:23:24 +00:00
write_strings ( rom , world , player )
2017-12-14 04:21:43 +00:00
# set rom name
# 21 bytes
2019-07-11 22:48:21 +00:00
from Main import __version__
2019-12-09 18:27:56 +00:00
rom . name = bytearray ( ' ER {0} _ {1} _ {2:09} \0 ' . format ( __version__ . split ( ' - ' ) [ 0 ] . replace ( ' . ' , ' ' ) [ 0 : 3 ] , player , world . seed ) , ' utf8 ' )
rom . write_bytes ( 0x7FC0 , rom . name [ 0 : 21 ] )
2017-12-14 04:21:43 +00:00
2018-09-23 02:51:54 +00:00
# Write title screen Code
hashint = int ( rom . get_hash ( ) , 16 )
code = [
( hashint >> 20 ) & 0x1F ,
( hashint >> 15 ) & 0x1F ,
( hashint >> 10 ) & 0x1F ,
( hashint >> 5 ) & 0x1F ,
hashint & 0x1F ,
]
rom . write_bytes ( 0x180215 , code )
2017-12-14 04:21:43 +00:00
return rom
2019-12-15 15:16:39 +00:00
try :
import RaceRom
except ImportError :
RaceRom = None
def get_race_rom_patches ( rom ) :
patches = { str ( 0x180213 ) : [ 0x01 , 0x00 ] } # Tournament Seed
if ' RaceRom ' in sys . modules :
RaceRom . encrypt ( rom , patches )
return patches
2019-04-18 09:23:24 +00:00
def write_custom_shops ( rom , world , player ) :
shops = [ shop for shop in world . shops if shop . replaceable and shop . active and shop . region . player == player ]
2018-02-17 23:38:54 +00:00
shop_data = bytearray ( )
items_data = bytearray ( )
sram_offset = 0
for shop_id , shop in enumerate ( shops ) :
if shop_id == len ( shops ) - 1 :
shop_id = 0xFF
bytes = shop . get_bytes ( )
bytes [ 0 ] = shop_id
bytes [ - 1 ] = sram_offset
if shop . type == ShopType . TakeAny :
sram_offset + = 1
else :
sram_offset + = shop . item_count
shop_data . extend ( bytes )
# [id][item][price-low][price-high][max][repl_id][repl_price-low][repl_price-high]
for item in shop . inventory :
if item is None :
break
2019-04-18 09:23:24 +00:00
item_data = [ shop_id , ItemFactory ( item [ ' item ' ] , player ) . code ] + int16_as_bytes ( item [ ' price ' ] ) + [ item [ ' max ' ] , ItemFactory ( item [ ' replacement ' ] , player ) . code if item [ ' replacement ' ] else 0xFF ] + int16_as_bytes ( item [ ' replacement_price ' ] )
2018-02-17 23:38:54 +00:00
items_data . extend ( item_data )
rom . write_bytes ( 0x184800 , shop_data )
items_data . extend ( [ 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ] )
rom . write_bytes ( 0x184900 , items_data )
2019-12-09 18:27:56 +00:00
def hud_format_text ( text ) :
output = bytes ( )
for char in text . lower ( ) :
if ' a ' < = char < = ' z ' :
output + = bytes ( [ 0x5d + ord ( char ) - ord ( ' a ' ) , 0x29 ] )
elif ' 0 ' < = char < = ' 8 ' :
output + = bytes ( [ 0x77 + ord ( char ) - ord ( ' 0 ' ) , 0x29 ] )
elif char == ' 9 ' :
output + = b ' \x4b \x29 '
elif char == ' ' :
output + = b ' \x7f \x00 '
else :
output + = b ' \x2a \x29 '
while len ( output ) < 32 :
output + = b ' \x7f \x00 '
return output [ : 32 ]
2018-02-17 23:38:54 +00:00
2019-12-09 18:27:56 +00:00
def apply_rom_settings ( rom , beep , color , quickswap , fastmenu , disable_music , sprite , names = None ) :
2017-07-14 14:11:07 +00:00
2017-11-04 18:23:57 +00:00
# enable instant item menu
2018-01-05 22:53:29 +00:00
if fastmenu == ' instant ' :
2017-10-14 16:00:41 +00:00
rom . write_byte ( 0x6DD9A , 0x20 )
rom . write_byte ( 0x6DF2A , 0x20 )
rom . write_byte ( 0x6E0E9 , 0x20 )
2017-12-14 04:21:43 +00:00
else :
2017-12-08 21:53:53 +00:00
rom . write_byte ( 0x6DD9A , 0x11 )
rom . write_byte ( 0x6DF2A , 0x12 )
rom . write_byte ( 0x6E0E9 , 0x12 )
2018-01-05 22:53:29 +00:00
if fastmenu == ' instant ' :
rom . write_byte ( 0x180048 , 0xE8 )
elif fastmenu == ' double ' :
rom . write_byte ( 0x180048 , 0x10 )
elif fastmenu == ' triple ' :
rom . write_byte ( 0x180048 , 0x18 )
elif fastmenu == ' quadruple ' :
rom . write_byte ( 0x180048 , 0x20 )
elif fastmenu == ' half ' :
rom . write_byte ( 0x180048 , 0x04 )
else :
rom . write_byte ( 0x180048 , 0x08 )
2017-11-04 18:23:57 +00:00
2018-03-02 02:46:23 +00:00
rom . write_byte ( 0x18004B , 0x01 if quickswap else 0x00 )
2017-12-14 04:21:43 +00:00
2019-12-10 02:21:49 +00:00
music_volumes = [
( 0x00 , [ 0xD373B , 0xD375B , 0xD90F8 ] ) ,
( 0x14 , [ 0xDA710 , 0xDA7A4 , 0xDA7BB , 0xDA7D2 ] ) ,
( 0x3C , [ 0xD5954 , 0xD653B , 0xDA736 , 0xDA752 , 0xDA772 , 0xDA792 ] ) ,
( 0x50 , [ 0xD5B47 , 0xD5B5E ] ) ,
( 0x54 , [ 0xD4306 ] ) ,
( 0x64 ,
[ 0xD6878 , 0xD6883 , 0xD6E48 , 0xD6E76 , 0xD6EFB , 0xD6F2D , 0xDA211 , 0xDA35B , 0xDA37B , 0xDA38E , 0xDA39F , 0xDA5C3 ,
0xDA691 , 0xDA6A8 , 0xDA6DF ] ) ,
( 0x78 ,
[ 0xD2349 , 0xD3F45 , 0xD42EB , 0xD48B9 , 0xD48FF , 0xD543F , 0xD5817 , 0xD5957 , 0xD5ACB , 0xD5AE8 , 0xD5B4A , 0xDA5DE ,
0xDA608 , 0xDA635 ,
0xDA662 , 0xDA71F , 0xDA7AF , 0xDA7C6 , 0xDA7DD ] ) ,
( 0x82 , [ 0xD2F00 , 0xDA3D5 ] ) ,
( 0xA0 ,
[ 0xD249C , 0xD24CD , 0xD2C09 , 0xD2C53 , 0xD2CAF , 0xD2CEB , 0xD2D91 , 0xD2EE6 , 0xD38ED , 0xD3C91 , 0xD3CD3 , 0xD3CE8 ,
0xD3F0C ,
0xD3F82 , 0xD405F , 0xD4139 , 0xD4198 , 0xD41D5 , 0xD41F6 , 0xD422B , 0xD4270 , 0xD42B1 , 0xD4334 , 0xD4371 , 0xD43A6 ,
0xD43DB ,
0xD441E , 0xD4597 , 0xD4B3C , 0xD4BAB , 0xD4C03 , 0xD4C53 , 0xD4C7F , 0xD4D9C , 0xD5424 , 0xD65D2 , 0xD664F , 0xD6698 ,
0xD66FF ,
0xD6985 , 0xD6C5C , 0xD6C6F , 0xD6C8E , 0xD6CB4 , 0xD6D7D , 0xD827D , 0xD960C , 0xD9828 , 0xDA233 , 0xDA3A2 , 0xDA49E ,
0xDA72B ,
0xDA745 , 0xDA765 , 0xDA785 , 0xDABF6 , 0xDAC0D , 0xDAEBE , 0xDAFAC ] ) ,
( 0xAA , [ 0xD9A02 , 0xD9BD6 ] ) ,
( 0xB4 ,
[ 0xD21CD , 0xD2279 , 0xD2E66 , 0xD2E70 , 0xD2EAB , 0xD3B97 , 0xD3BAC , 0xD3BE8 , 0xD3C0D , 0xD3C39 , 0xD3C68 , 0xD3C9F ,
0xD3CBC ,
0xD401E , 0xD4290 , 0xD443E , 0xD456F , 0xD47D3 , 0xD4D43 , 0xD4DCC , 0xD4EBA , 0xD4F0B , 0xD4FE5 , 0xD5012 , 0xD54BC ,
0xD54D5 ,
0xD54F0 , 0xD5509 , 0xD57D8 , 0xD59B9 , 0xD5A2F , 0xD5AEB , 0xD5E5E , 0xD5FE9 , 0xD658F , 0xD674A , 0xD6827 , 0xD69D6 ,
0xD69F5 ,
0xD6A05 , 0xD6AE9 , 0xD6DCF , 0xD6E20 , 0xD6ECB , 0xD71D4 , 0xD71E6 , 0xD7203 , 0xD721E , 0xD8724 , 0xD8732 , 0xD9652 ,
0xD9698 ,
0xD9CBC , 0xD9DC0 , 0xD9E49 , 0xDAA68 , 0xDAA77 , 0xDAA88 , 0xDAA99 , 0xDAF04 ] ) ,
( 0x8c ,
[ 0xD1D28 , 0xD1D41 , 0xD1D5C , 0xD1D77 , 0xD1EEE , 0xD311D , 0xD31D1 , 0xD4148 , 0xD5543 , 0xD5B6F , 0xD65B3 , 0xD6760 ,
0xD6B6B ,
0xD6DF6 , 0xD6E0D , 0xD73A1 , 0xD814C , 0xD825D , 0xD82BE , 0xD8340 , 0xD8394 , 0xD842C , 0xD8796 , 0xD8903 , 0xD892A ,
0xD91E8 ,
0xD922B , 0xD92E0 , 0xD937E , 0xD93C1 , 0xDA958 , 0xDA971 , 0xDA98C , 0xDA9A7 ] ) ,
( 0xC8 ,
[ 0xD1D92 , 0xD1DBD , 0xD1DEB , 0xD1F5D , 0xD1F9F , 0xD1FBD , 0xD1FDC , 0xD1FEA , 0xD20CA , 0xD21BB , 0xD22C9 , 0xD2754 ,
0xD284C ,
0xD2866 , 0xD2887 , 0xD28A0 , 0xD28BA , 0xD28DB , 0xD28F4 , 0xD293E , 0xD2BF3 , 0xD2C1F , 0xD2C69 , 0xD2CA1 , 0xD2CC5 ,
0xD2D05 ,
0xD2D73 , 0xD2DAF , 0xD2E3D , 0xD2F36 , 0xD2F46 , 0xD2F6F , 0xD2FCF , 0xD2FDF , 0xD302B , 0xD3086 , 0xD3099 , 0xD30A5 ,
0xD30CD ,
0xD30F6 , 0xD3154 , 0xD3184 , 0xD333A , 0xD33D9 , 0xD349F , 0xD354A , 0xD35E5 , 0xD3624 , 0xD363C , 0xD3672 , 0xD3691 ,
0xD36B4 ,
0xD36C6 , 0xD3724 , 0xD3767 , 0xD38CB , 0xD3B1D , 0xD3B2F , 0xD3B55 , 0xD3B70 , 0xD3B81 , 0xD3BBF , 0xD3F65 , 0xD3FA6 ,
0xD404F ,
0xD4087 , 0xD417A , 0xD41A0 , 0xD425C , 0xD4319 , 0xD433C , 0xD43EF , 0xD440C , 0xD4452 , 0xD4494 , 0xD44B5 , 0xD4512 ,
0xD45D1 ,
0xD45EF , 0xD4682 , 0xD46C3 , 0xD483C , 0xD4848 , 0xD4855 , 0xD4862 , 0xD486F , 0xD487C , 0xD4A1C , 0xD4A3B , 0xD4A60 ,
0xD4B27 ,
0xD4C7A , 0xD4D12 , 0xD4D81 , 0xD4E90 , 0xD4ED6 , 0xD4EE2 , 0xD5005 , 0xD502E , 0xD503C , 0xD5081 , 0xD51B1 , 0xD51C7 ,
0xD51CF ,
0xD51EF , 0xD520C , 0xD5214 , 0xD5231 , 0xD5257 , 0xD526D , 0xD5275 , 0xD52AF , 0xD52BD , 0xD52CD , 0xD52DB , 0xD549C ,
0xD5801 ,
0xD58A4 , 0xD5A68 , 0xD5A7F , 0xD5C12 , 0xD5D71 , 0xD5E10 , 0xD5E9A , 0xD5F8B , 0xD5FA4 , 0xD651A , 0xD6542 , 0xD65ED ,
0xD661D ,
0xD66D7 , 0xD6776 , 0xD68BD , 0xD68E5 , 0xD6956 , 0xD6973 , 0xD69A8 , 0xD6A51 , 0xD6A86 , 0xD6B96 , 0xD6C3E , 0xD6D4A ,
0xD6E9C ,
0xD6F80 , 0xD717E , 0xD7190 , 0xD71B9 , 0xD811D , 0xD8139 , 0xD816B , 0xD818A , 0xD819E , 0xD81BE , 0xD829C , 0xD82E1 ,
0xD8306 ,
0xD830E , 0xD835E , 0xD83AB , 0xD83CA , 0xD83F0 , 0xD83F8 , 0xD844B , 0xD8479 , 0xD849E , 0xD84CB , 0xD84EB , 0xD84F3 ,
0xD854A ,
0xD8573 , 0xD859D , 0xD85B4 , 0xD85CE , 0xD862A , 0xD8681 , 0xD87E3 , 0xD87FF , 0xD887B , 0xD88C6 , 0xD88E3 , 0xD8944 ,
0xD897B ,
0xD8C97 , 0xD8CA4 , 0xD8CB3 , 0xD8CC2 , 0xD8CD1 , 0xD8D01 , 0xD917B , 0xD918C , 0xD919A , 0xD91B5 , 0xD91D0 , 0xD91DD ,
0xD9220 ,
0xD9273 , 0xD9284 , 0xD9292 , 0xD92AD , 0xD92C8 , 0xD92D5 , 0xD9311 , 0xD9322 , 0xD9330 , 0xD934B , 0xD9366 , 0xD9373 ,
0xD93B6 ,
0xD97A6 , 0xD97C2 , 0xD97DC , 0xD97FB , 0xD9811 , 0xD98FF , 0xD996F , 0xD99A8 , 0xD99D5 , 0xD9A30 , 0xD9A4E , 0xD9A6B ,
0xD9A88 ,
0xD9AF7 , 0xD9B1D , 0xD9B43 , 0xD9B7C , 0xD9BA9 , 0xD9C84 , 0xD9C8D , 0xD9CAC , 0xD9CE8 , 0xD9CF3 , 0xD9CFD , 0xD9D46 ,
0xDA35E ,
0xDA37E , 0xDA391 , 0xDA478 , 0xDA4C3 , 0xDA4D7 , 0xDA4F6 , 0xDA515 , 0xDA6E2 , 0xDA9C2 , 0xDA9ED , 0xDAA1B , 0xDAA57 ,
0xDABAF ,
0xDABC9 , 0xDABE2 , 0xDAC28 , 0xDAC46 , 0xDAC63 , 0xDACB8 , 0xDACEC , 0xDAD08 , 0xDAD25 , 0xDAD42 , 0xDAD5F , 0xDAE17 ,
0xDAE34 ,
0xDAE51 , 0xDAF2E , 0xDAF55 , 0xDAF6B , 0xDAF81 , 0xDB14F , 0xDB16B , 0xDB180 , 0xDB195 , 0xDB1AA ] ) ,
( 0xD2 , [ 0xD2B88 , 0xD364A , 0xD369F , 0xD3747 ] ) ,
( 0xDC ,
[ 0xD213F , 0xD2174 , 0xD229E , 0xD2426 , 0xD4731 , 0xD4753 , 0xD4774 , 0xD4795 , 0xD47B6 , 0xD4AA5 , 0xD4AE4 , 0xD4B96 ,
0xD4CA5 ,
0xD5477 , 0xD5A3D , 0xD6566 , 0xD672C , 0xD67C0 , 0xD69B8 , 0xD6AB1 , 0xD6C05 , 0xD6DB3 , 0xD71AB , 0xD8E2D , 0xD8F0D ,
0xD94E0 ,
0xD9544 , 0xD95A8 , 0xD9982 , 0xD9B56 , 0xDA694 , 0xDA6AB , 0xDAE88 , 0xDAEC8 , 0xDAEE6 , 0xDB1BF ] ) ,
( 0xE6 , [ 0xD210A , 0xD22DC , 0xD2447 , 0xD5A4D , 0xD5DDC , 0xDA251 , 0xDA26C ] ) ,
( 0xF0 , [ 0xD945E , 0xD967D , 0xD96C2 , 0xD9C95 , 0xD9EE6 , 0xDA5C6 ] ) ,
( 0xFA ,
[ 0xD2047 , 0xD24C2 , 0xD24EC , 0xD25A4 , 0xD51A8 , 0xD51E6 , 0xD524E , 0xD529E , 0xD6045 , 0xD81DE , 0xD821E , 0xD94AA ,
0xD9A9E ,
0xD9AE4 , 0xDA289 ] ) ,
( 0xFF , [ 0xD2085 , 0xD21C5 , 0xD5F28 ] )
]
for volume , addresses in music_volumes :
for address in addresses :
rom . write_byte ( address , volume if not disable_music else 0x00 )
2019-08-26 02:36:19 +00:00
rom . write_byte ( 0x18021A , 1 if disable_music else 0x00 )
2018-01-27 20:01:16 +00:00
2018-01-27 19:58:19 +00:00
# restore Mirror sound effect volumes (for existing seeds that lack it)
rom . write_byte ( 0xD3E04 , 0xC8 )
rom . write_byte ( 0xD3DC6 , 0xC8 )
rom . write_byte ( 0xD3D6E , 0xC8 )
rom . write_byte ( 0xD3D34 , 0xC8 )
rom . write_byte ( 0xD3D55 , 0xC8 )
rom . write_byte ( 0xD3E38 , 0xC8 )
rom . write_byte ( 0xD3DAA , 0xFA )
2017-05-25 15:47:15 +00:00
# set heart beep rate
2018-09-26 21:34:15 +00:00
rom . write_byte ( 0x180033 , { ' off ' : 0x00 , ' half ' : 0x40 , ' quarter ' : 0x80 , ' normal ' : 0x20 , ' double ' : 0x10 } [ beep ] )
2017-05-25 15:47:15 +00:00
2018-02-28 02:26:33 +00:00
# set heart color
2019-04-15 19:28:14 +00:00
if color == ' random ' :
color = random . choice ( [ ' red ' , ' blue ' , ' green ' , ' yellow ' ] )
2018-02-28 02:26:33 +00:00
rom . write_byte ( 0x6FA1E , { ' red ' : 0x24 , ' blue ' : 0x2C , ' green ' : 0x3C , ' yellow ' : 0x28 } [ color ] )
rom . write_byte ( 0x6FA20 , { ' red ' : 0x24 , ' blue ' : 0x2C , ' green ' : 0x3C , ' yellow ' : 0x28 } [ color ] )
rom . write_byte ( 0x6FA22 , { ' red ' : 0x24 , ' blue ' : 0x2C , ' green ' : 0x3C , ' yellow ' : 0x28 } [ color ] )
rom . write_byte ( 0x6FA24 , { ' red ' : 0x24 , ' blue ' : 0x2C , ' green ' : 0x3C , ' yellow ' : 0x28 } [ color ] )
rom . write_byte ( 0x6FA26 , { ' red ' : 0x24 , ' blue ' : 0x2C , ' green ' : 0x3C , ' yellow ' : 0x28 } [ color ] )
rom . write_byte ( 0x6FA28 , { ' red ' : 0x24 , ' blue ' : 0x2C , ' green ' : 0x3C , ' yellow ' : 0x28 } [ color ] )
rom . write_byte ( 0x6FA2A , { ' red ' : 0x24 , ' blue ' : 0x2C , ' green ' : 0x3C , ' yellow ' : 0x28 } [ color ] )
rom . write_byte ( 0x6FA2C , { ' red ' : 0x24 , ' blue ' : 0x2C , ' green ' : 0x3C , ' yellow ' : 0x28 } [ color ] )
rom . write_byte ( 0x6FA2E , { ' 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 ] )
2017-05-26 16:39:32 +00:00
# write link sprite if required
if sprite is not None :
2017-07-14 12:37:34 +00:00
write_sprite ( rom , sprite )
2017-05-25 10:09:50 +00:00
2019-12-09 18:27:56 +00:00
# set player names
for player , name in names . items ( ) :
if 0 < player < = 64 :
rom . write_bytes ( 0x185380 + ( ( player - 1 ) * 32 ) , hud_format_text ( name ) )
2017-07-14 12:37:34 +00:00
if isinstance ( rom , LocalRom ) :
rom . write_crc ( )
2017-05-25 10:09:50 +00:00
2017-07-14 12:37:34 +00:00
def write_sprite ( rom , sprite ) :
2017-12-17 05:25:46 +00:00
if not sprite . valid :
return
2017-11-13 05:29:42 +00:00
rom . write_bytes ( 0x80000 , sprite . sprite )
2017-12-17 22:30:50 +00:00
rom . write_bytes ( 0xDD308 , sprite . palette )
rom . write_bytes ( 0xDEDF5 , sprite . glove_palette )
2017-05-25 10:09:50 +00:00
def write_string_to_rom ( rom , target , string ) :
address , maxbytes = text_addresses [ target ]
2018-03-10 23:45:14 +00:00
rom . write_bytes ( address , MultiByteTextMapper . convert ( string , maxbytes ) )
2017-05-25 13:58:35 +00:00
2019-04-18 09:23:24 +00:00
def write_strings ( rom , world , player ) :
2018-04-19 01:17:40 +00:00
tt = TextTable ( )
tt . removeUnwantedText ( )
2019-04-29 21:11:23 +00:00
# Let's keep this guy's text accurate to the shuffle setting.
if world . shuffle in [ ' vanilla ' , ' dungeonsfull ' , ' dungeonssimple ' ] :
2019-04-30 07:50:05 +00:00
tt [ ' kakariko_flophouse_man_no_flippers ' ] = ' I really hate mowing my yard. \n {PAGEBREAK} \n I should move. '
tt [ ' kakariko_flophouse_man ' ] = ' I really hate mowing my yard. \n {PAGEBREAK} \n I should move. '
2019-04-29 21:11:23 +00:00
2019-04-18 09:23:24 +00:00
def hint_text ( dest , ped_hint = False ) :
hint = dest . hint_text if not ped_hint else dest . pedestal_hint_text
if dest . player != player :
if ped_hint :
hint + = " for p %d ! " % dest . player
elif type ( dest ) in [ Region , Location ] :
hint + = " in p %d ' s world " % dest . player
elif type ( dest ) is Item :
hint + = " for p %d " % dest . player
return hint
2019-01-20 07:01:02 +00:00
# For hints, first we write hints about entrances, some from the inconvenient list others from all reasonable entrances.
2019-01-23 09:04:42 +00:00
if world . hints :
2019-04-30 15:14:55 +00:00
tt [ ' sign_north_of_links_house ' ] = ' > Randomizer The telepathic tiles can have hints! '
2019-01-23 09:04:42 +00:00
hint_locations = HintLocations . copy ( )
random . shuffle ( hint_locations )
2019-04-18 09:23:24 +00:00
all_entrances = [ entrance for entrance in world . get_entrances ( ) if entrance . player == player ]
2019-01-23 09:04:42 +00:00
random . shuffle ( all_entrances )
2019-09-16 05:51:24 +00:00
#First we take care of the one inconvenient dungeon in the appropriately simple shuffles.
entrances_to_hint = { }
entrances_to_hint . update ( InconvenientDungeonEntrances )
if world . shuffle_ganon :
2019-12-16 15:54:46 +00:00
if world . mode [ player ] == ' inverted ' :
2019-10-24 01:45:02 +00:00
entrances_to_hint . update ( { ' Inverted Ganons Tower ' : ' The sealed castle door ' } )
else :
entrances_to_hint . update ( { ' Ganons Tower ' : ' Ganon \' s Tower ' } )
2019-09-16 05:51:24 +00:00
if world . shuffle in [ ' simple ' , ' restricted ' , ' restricted_legacy ' ] :
for entrance in all_entrances :
if entrance . name in entrances_to_hint :
this_hint = entrances_to_hint [ entrance . name ] + ' leads to ' + hint_text ( entrance . connected_region ) + ' . '
tt [ hint_locations . pop ( 0 ) ] = this_hint
entrances_to_hint = { }
break
#Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones.
entrances_to_hint . update ( InconvenientOtherEntrances )
if world . shuffle in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
hint_count = 0
elif world . shuffle in [ ' simple ' , ' restricted ' , ' restricted_legacy ' ] :
hint_count = 2
else :
hint_count = 4
2019-01-23 09:04:42 +00:00
for entrance in all_entrances :
if entrance . name in entrances_to_hint :
2019-04-18 09:23:24 +00:00
if hint_count > 0 :
this_hint = entrances_to_hint [ entrance . name ] + ' leads to ' + hint_text ( entrance . connected_region ) + ' . '
tt [ hint_locations . pop ( 0 ) ] = this_hint
entrances_to_hint . pop ( entrance . name )
hint_count - = 1
else :
2019-01-23 09:04:42 +00:00
break
2019-01-20 07:01:02 +00:00
2019-09-16 05:51:24 +00:00
#Next we handle hints for randomly selected other entrances, curating the selection intelligently based on shuffle.
if world . shuffle not in [ ' simple ' , ' restricted ' , ' restricted_legacy ' ] :
entrances_to_hint . update ( ConnectorEntrances )
entrances_to_hint . update ( DungeonEntrances )
2019-12-16 15:54:46 +00:00
if world . mode [ player ] == ' inverted ' :
2019-10-24 01:45:02 +00:00
entrances_to_hint . update ( { ' Inverted Agahnims Tower ' : ' The dark mountain tower ' } )
else :
entrances_to_hint . update ( { ' Agahnims Tower ' : ' The sealed castle door ' } )
2019-09-16 05:51:24 +00:00
elif world . shuffle == ' restricted ' :
entrances_to_hint . update ( ConnectorEntrances )
2019-01-23 09:04:42 +00:00
entrances_to_hint . update ( OtherEntrances )
2019-12-16 15:54:46 +00:00
if world . mode [ player ] == ' inverted ' :
2019-10-24 01:45:02 +00:00
entrances_to_hint . update ( { ' Inverted Dark Sanctuary ' : ' The dark sanctuary cave ' } )
entrances_to_hint . update ( { ' Inverted Big Bomb Shop ' : ' The old hero \' s dark home ' } )
entrances_to_hint . update ( { ' Inverted Links House ' : ' The old hero \' s light home ' } )
else :
entrances_to_hint . update ( { ' Dark Sanctuary Hint ' : ' The dark sanctuary cave ' } )
entrances_to_hint . update ( { ' Big Bomb Shop ' : ' The old bomb shop ' } )
2019-01-23 09:04:42 +00:00
if world . shuffle in [ ' insanity ' , ' madness_legacy ' , ' insanity_legacy ' ] :
entrances_to_hint . update ( InsanityEntrances )
if world . shuffle_ganon :
2019-12-16 15:54:46 +00:00
if world . mode [ player ] == ' inverted ' :
2019-10-24 01:45:02 +00:00
entrances_to_hint . update ( { ' Inverted Pyramid Entrance ' : ' The extra castle passage ' } )
else :
entrances_to_hint . update ( { ' Pyramid Ledge ' : ' The pyramid ledge ' } )
2019-09-16 05:51:24 +00:00
hint_count = 4 if world . shuffle not in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] else 0
2019-01-23 09:04:42 +00:00
for entrance in all_entrances :
if entrance . name in entrances_to_hint :
2019-04-18 09:23:24 +00:00
if hint_count > 0 :
this_hint = entrances_to_hint [ entrance . name ] + ' leads to ' + hint_text ( entrance . connected_region ) + ' . '
tt [ hint_locations . pop ( 0 ) ] = this_hint
entrances_to_hint . pop ( entrance . name )
hint_count - = 1
else :
2019-01-23 09:04:42 +00:00
break
2019-01-20 07:01:02 +00:00
2019-01-23 09:04:42 +00:00
# Next we write a few hints for specific inconvenient locations. We don't make many because in entrance this is highly unpredictable.
locations_to_hint = InconvenientLocations . copy ( )
2019-09-16 05:51:24 +00:00
if world . shuffle in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
locations_to_hint . extend ( InconvenientVanillaLocations )
2019-01-23 09:04:42 +00:00
random . shuffle ( locations_to_hint )
2019-09-16 05:51:24 +00:00
hint_count = 3 if world . shuffle not in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] else 5
2019-04-18 09:23:24 +00:00
del locations_to_hint [ hint_count : ]
2019-01-23 09:04:42 +00:00
for location in locations_to_hint :
if location == ' Swamp Left ' :
2019-04-09 01:11:33 +00:00
if random . randint ( 0 , 1 ) == 0 :
2019-04-18 09:23:24 +00:00
first_item = hint_text ( world . get_location ( ' Swamp Palace - West Chest ' , player ) . item )
second_item = hint_text ( world . get_location ( ' Swamp Palace - Big Key Chest ' , player ) . item )
2019-04-09 01:11:33 +00:00
else :
2019-04-18 09:23:24 +00:00
second_item = hint_text ( world . get_location ( ' Swamp Palace - West Chest ' , player ) . item )
first_item = hint_text ( world . get_location ( ' Swamp Palace - Big Key Chest ' , player ) . item )
2019-04-09 01:11:33 +00:00
this_hint = ( ' The westmost chests in Swamp Palace contain ' + first_item + ' and ' + second_item + ' . ' )
2019-01-23 09:04:42 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
elif location == ' Mire Left ' :
2019-04-09 01:11:33 +00:00
if random . randint ( 0 , 1 ) == 0 :
2019-04-18 09:23:24 +00:00
first_item = hint_text ( world . get_location ( ' Misery Mire - Compass Chest ' , player ) . item )
second_item = hint_text ( world . get_location ( ' Misery Mire - Big Key Chest ' , player ) . item )
2019-04-09 01:11:33 +00:00
else :
2019-04-18 09:23:24 +00:00
second_item = hint_text ( world . get_location ( ' Misery Mire - Compass Chest ' , player ) . item )
first_item = hint_text ( world . get_location ( ' Misery Mire - Big Key Chest ' , player ) . item )
2019-04-09 01:11:33 +00:00
this_hint = ( ' The westmost chests in Misery Mire contain ' + first_item + ' and ' + second_item + ' . ' )
2019-01-23 09:04:42 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
elif location == ' Tower of Hera - Big Key Chest ' :
2019-04-18 09:23:24 +00:00
this_hint = ' Waiting in the Tower of Hera basement leads to ' + hint_text ( world . get_location ( location , player ) . item ) + ' . '
2019-01-23 09:04:42 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
2019-04-10 22:03:01 +00:00
elif location == ' Ganons Tower - Big Chest ' :
2019-04-18 09:23:24 +00:00
this_hint = ' The big chest in Ganon \' s Tower contains ' + hint_text ( world . get_location ( location , player ) . item ) + ' . '
2019-04-10 22:03:01 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
elif location == ' Thieves \' Town - Big Chest ' :
2019-04-18 09:23:24 +00:00
this_hint = ' The big chest in Thieves \' Town contains ' + hint_text ( world . get_location ( location , player ) . item ) + ' . '
2019-04-10 22:03:01 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
elif location == ' Ice Palace - Big Chest ' :
2019-04-18 09:23:24 +00:00
this_hint = ' The big chest in Ice Palace contains ' + hint_text ( world . get_location ( location , player ) . item ) + ' . '
2019-04-10 22:03:01 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
elif location == ' Eastern Palace - Big Key Chest ' :
2019-04-18 09:23:24 +00:00
this_hint = ' The antifairy guarded chest in Eastern Palace contains ' + hint_text ( world . get_location ( location , player ) . item ) + ' . '
2019-04-10 22:03:01 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
2019-09-16 05:51:24 +00:00
elif location == ' Sahasrahla ' :
this_hint = ' Sahasrahla seeks a green pendant for ' + hint_text ( world . get_location ( location , player ) . item ) + ' . '
tt [ hint_locations . pop ( 0 ) ] = this_hint
elif location == ' Graveyard Cave ' :
this_hint = ' The cave north of the graveyard contains ' + hint_text ( world . get_location ( location , player ) . item ) + ' . '
tt [ hint_locations . pop ( 0 ) ] = this_hint
2019-01-20 07:01:02 +00:00
else :
2019-09-16 05:51:24 +00:00
this_hint = location + ' contains ' + hint_text ( world . get_location ( location , player ) . item ) + ' . '
2019-01-23 09:04:42 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
# Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well.
items_to_hint = RelevantItems . copy ( )
2019-12-13 21:37:52 +00:00
if world . keyshuffle :
items_to_hint . extend ( SmallKeys )
if world . bigkeyshuffle :
items_to_hint . extend ( BigKeys )
2019-01-23 09:04:42 +00:00
random . shuffle ( items_to_hint )
2019-09-16 05:51:24 +00:00
hint_count = 5 if world . shuffle not in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] else 8
2019-04-18 09:23:24 +00:00
while hint_count > 0 :
2019-01-23 09:04:42 +00:00
this_item = items_to_hint . pop ( 0 )
2019-04-18 09:23:24 +00:00
this_location = world . find_items ( this_item , player )
2019-01-23 09:04:42 +00:00
random . shuffle ( this_location )
2019-09-16 05:51:24 +00:00
#This looks dumb but prevents hints for Skull Woods Pinball Room's key safely with any item pool.
if this_location :
if this_location [ 0 ] . name == ' Skull Woods - Pinball Room ' :
this_location . pop ( 0 )
2019-01-23 09:04:42 +00:00
if this_location :
2019-04-18 09:23:24 +00:00
this_hint = this_location [ 0 ] . item . hint_text + ' can be found ' + hint_text ( this_location [ 0 ] ) + ' . '
2019-01-23 09:04:42 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
hint_count - = 1
2019-01-20 07:01:02 +00:00
2019-01-23 09:04:42 +00:00
# All remaining hint slots are filled with junk hints. It is done this way to ensure the same junk hint isn't selected twice.
junk_hints = junk_texts . copy ( )
random . shuffle ( junk_hints )
for location in hint_locations :
2019-04-29 21:11:23 +00:00
tt [ location ] = junk_hints . pop ( 0 )
2019-01-20 07:01:02 +00:00
2019-08-22 02:40:19 +00:00
# We still need the older hints of course. Those are done here.
2019-04-18 09:23:24 +00:00
silverarrows = world . find_items ( ' Silver Arrows ' , player )
2019-01-23 09:04:42 +00:00
random . shuffle ( silverarrows )
2019-04-18 09:23:24 +00:00
silverarrow_hint = ( ' %s ? ' % hint_text ( silverarrows [ 0 ] ) . replace ( ' Ganon \' s ' , ' my ' ) ) if silverarrows else ' ? \n I think not! '
2019-08-22 02:40:19 +00:00
tt [ ' ganon_phase_3_no_silvers ' ] = ' Did you find the silver arrows %s ' % silverarrow_hint
2019-09-02 19:33:34 +00:00
tt [ ' ganon_phase_3_no_silvers_alt ' ] = ' Did you find the silver arrows %s ' % silverarrow_hint
2019-08-22 02:40:19 +00:00
prog_bow_locs = world . find_items ( ' Progressive Bow ' , player )
distinguished_prog_bow_loc = next ( ( location for location in prog_bow_locs if location . item . code == 0x65 ) , None )
if distinguished_prog_bow_loc :
prog_bow_locs . remove ( distinguished_prog_bow_loc )
silverarrow_hint = ( ' %s ? ' % hint_text ( distinguished_prog_bow_loc ) . replace ( ' Ganon \' s ' , ' my ' ) )
2019-09-02 19:33:34 +00:00
tt [ ' ganon_phase_3_no_silvers_alt ' ] = ' Did you find the silver arrows %s ' % silverarrow_hint
2019-08-22 02:40:19 +00:00
if any ( prog_bow_locs ) :
silverarrow_hint = ( ' %s ? ' % hint_text ( random . choice ( prog_bow_locs ) ) . replace ( ' Ganon \' s ' , ' my ' ) )
2019-09-02 19:33:34 +00:00
tt [ ' ganon_phase_3_no_silvers ' ] = ' Did you find the silver arrows %s ' % silverarrow_hint
2019-08-22 02:40:19 +00:00
silverarrow_hint = ( ' %s ? ' % hint_text ( silverarrows [ 0 ] ) . replace ( ' Ganon \' s ' , ' my ' ) ) if silverarrows else ' ? \n I think not! '
2017-05-25 15:47:15 +00:00
2019-04-18 09:23:24 +00:00
crystal5 = world . find_items ( ' Crystal 5 ' , player ) [ 0 ]
crystal6 = world . find_items ( ' Crystal 6 ' , player ) [ 0 ]
2018-04-19 01:17:40 +00:00
tt [ ' bomb_shop ' ] = ' Big Bomb? \n My supply is blocked until you clear %s and %s . ' % ( crystal5 . hint_text , crystal6 . hint_text )
2017-05-25 15:47:15 +00:00
2019-04-18 09:23:24 +00:00
greenpendant = world . find_items ( ' Green Pendant ' , player ) [ 0 ]
2018-04-19 01:17:40 +00:00
tt [ ' sahasrahla_bring_courage ' ] = ' I lost my family heirloom in %s ' % greenpendant . hint_text
2019-08-04 16:32:35 +00:00
tt [ ' sign_ganons_tower ' ] = ( ' You need %d crystal to enter. ' if world . crystals_needed_for_gt == 1 else ' You need %d crystals to enter. ' ) % world . crystals_needed_for_gt
tt [ ' sign_ganon ' ] = ( ' You need %d crystal to beat Ganon. ' if world . crystals_needed_for_ganon == 1 else ' You need %d crystals to beat Ganon. ' ) % world . crystals_needed_for_ganon
2019-12-16 14:27:20 +00:00
if world . goal [ player ] in [ ' dungeons ' ] :
2019-08-24 19:36:54 +00:00
tt [ ' sign_ganon ' ] = ' You need to complete all the dungeons. '
2018-04-19 01:17:40 +00:00
tt [ ' uncle_leaving_text ' ] = Uncle_texts [ random . randint ( 0 , len ( Uncle_texts ) - 1 ) ]
2018-08-04 20:29:07 +00:00
tt [ ' end_triforce ' ] = " {NOBORDER} \n " + Triforce_texts [ random . randint ( 0 , len ( Triforce_texts ) - 1 ) ]
2018-04-19 01:17:40 +00:00
tt [ ' bomb_shop_big_bomb ' ] = BombShop2_texts [ random . randint ( 0 , len ( BombShop2_texts ) - 1 ) ]
2018-07-24 23:22:47 +00:00
# this is what shows after getting the green pendant item in rando
tt [ ' sahasrahla_quest_have_master_sword ' ] = Sahasrahla2_texts [ random . randint ( 0 , len ( Sahasrahla2_texts ) - 1 ) ]
2018-04-19 01:17:40 +00:00
tt [ ' blind_by_the_light ' ] = Blind_texts [ random . randint ( 0 , len ( Blind_texts ) - 1 ) ]
2019-12-16 14:27:20 +00:00
if world . goal [ player ] in [ ' triforcehunt ' ] :
2019-08-24 19:36:54 +00:00
tt [ ' ganon_fall_in_alt ' ] = ' Why are you even here? \n You can \' t even hurt me! Get the Triforce Pieces. '
2018-04-19 01:17:40 +00:00
tt [ ' ganon_phase_3_alt ' ] = ' Seriously? Go Away, I will not Die. '
2019-08-26 02:22:31 +00:00
tt [ ' sign_ganon ' ] = ' Go find the Triforce pieces... Ganon is invincible! '
2019-09-02 19:34:52 +00:00
tt [ ' murahdahla ' ] = " Hello @. I \n am Murahdahla, brother of \n Sahasrahla and Aginah. Behold the power of \n invisibility. \n \n \n \n … … … \n \n Wait! you can see me? I knew I should have \n hidden in a hollow tree. If you bring \n %d triforce pieces, I can reassemble it. " % world . treasure_hunt_count
2019-12-16 14:27:20 +00:00
elif world . goal [ player ] in [ ' pedestal ' ] :
2019-08-24 19:36:54 +00:00
tt [ ' ganon_fall_in_alt ' ] = ' Why are you even here? \n You can \' t even hurt me! Your goal is at the pedestal. '
tt [ ' ganon_phase_3_alt ' ] = ' Seriously? Go Away, I will not Die. '
tt [ ' sign_ganon ' ] = ' You need to get to the pedestal... Ganon is invincible! '
2017-06-03 19:27:34 +00:00
else :
2018-04-19 01:17:40 +00:00
tt [ ' ganon_fall_in ' ] = Ganon1_texts [ random . randint ( 0 , len ( Ganon1_texts ) - 1 ) ]
tt [ ' ganon_fall_in_alt ' ] = ' You cannot defeat me until you finish your goal! '
tt [ ' ganon_phase_3_alt ' ] = ' Got wax in \n your ears? \n I can not die! '
2019-08-24 19:36:54 +00:00
2018-04-19 01:17:40 +00:00
tt [ ' kakariko_tavern_fisherman ' ] = TavernMan_texts [ random . randint ( 0 , len ( TavernMan_texts ) - 1 ) ]
2017-05-25 13:58:35 +00:00
2019-04-18 09:23:24 +00:00
pedestalitem = world . get_location ( ' Master Sword Pedestal ' , player ) . item
pedestal_text = ' Some Hot Air ' if pedestalitem is None else hint_text ( pedestalitem , True ) if pedestalitem . pedestal_hint_text is not None else ' Unknown Item '
2018-04-19 01:17:40 +00:00
tt [ ' mastersword_pedestal_translated ' ] = pedestal_text
2017-10-29 03:42:35 +00:00
pedestal_credit_text = ' and the Hot Air ' if pedestalitem is None else pedestalitem . pedestal_credit_text if pedestalitem . pedestal_credit_text is not None else ' and the Unknown Item '
2017-05-25 13:58:35 +00:00
2019-04-18 09:23:24 +00:00
etheritem = world . get_location ( ' Ether Tablet ' , player ) . item
ether_text = ' Some Hot Air ' if etheritem is None else hint_text ( etheritem , True ) if etheritem . pedestal_hint_text is not None else ' Unknown Item '
2018-04-19 01:17:40 +00:00
tt [ ' tablet_ether_book ' ] = ether_text
2019-04-18 09:23:24 +00:00
bombositem = world . get_location ( ' Bombos Tablet ' , player ) . item
bombos_text = ' Some Hot Air ' if bombositem is None else hint_text ( bombositem , True ) if bombositem . pedestal_hint_text is not None else ' Unknown Item '
2018-04-19 01:17:40 +00:00
tt [ ' tablet_bombos_book ' ] = bombos_text
2019-07-27 13:13:13 +00:00
# inverted spawn menu changes
2019-12-16 15:54:46 +00:00
if world . mode [ player ] == ' inverted ' :
2019-07-27 13:13:13 +00:00
tt [ ' menu_start_2 ' ] = " {MENU} \n {SPEED0} \n ≥@ ' s house \n Dark Chapel \n {CHOICE3} "
tt [ ' menu_start_3 ' ] = " {MENU} \n {SPEED0} \n ≥@ ' s house \n Dark Chapel \n Mountain Cave \n {CHOICE2} "
tt [ ' intro_main ' ] = CompressedTextMapper . convert (
" {INTRO} \n Episode III \n {PAUSE3} \n A Link to \n the Past \n "
+ " {PAUSE3} \n Inverted \n Randomizer \n {PAUSE3} \n After mostly disregarding what happened in the first two games. \n "
+ " {PAUSE3} \n Link has been transported to the Dark World \n {PAUSE3} \n While he was slumbering \n "
+ " {PAUSE3} \n Whatever will happen? \n {PAUSE3} \n {CHANGEPIC} \n Ganon has moved around all the items in Hyrule. \n "
+ " {PAUSE7} \n You will have to find all the items necessary to beat Ganon. \n "
+ " {PAUSE7} \n This is your chance to be a hero. \n {PAUSE3} \n {CHANGEPIC} \n "
+ " You must get the 7 crystals to beat Ganon. \n {PAUSE9} \n {CHANGEPIC} " , False )
2018-04-19 01:17:40 +00:00
rom . write_bytes ( 0xE0000 , tt . getBytes ( ) )
2017-07-14 21:32:40 +00:00
2017-11-05 04:06:00 +00:00
credits = Credits ( )
2017-05-25 13:58:35 +00:00
2019-04-18 09:23:24 +00:00
sickkiditem = world . get_location ( ' Sick Kid ' , player ) . item
2017-11-05 04:06:00 +00:00
sickkiditem_text = random . choice ( SickKid_texts ) if sickkiditem is None or sickkiditem . sickkid_credit_text is None else sickkiditem . sickkid_credit_text
2017-11-04 18:23:57 +00:00
2019-04-18 09:23:24 +00:00
zoraitem = world . get_location ( ' King Zora ' , player ) . item
2017-11-05 04:06:00 +00:00
zoraitem_text = random . choice ( Zora_texts ) if zoraitem is None or zoraitem . zora_credit_text is None else zoraitem . zora_credit_text
2017-11-04 18:23:57 +00:00
2019-04-18 09:23:24 +00:00
magicshopitem = world . get_location ( ' Potion Shop ' , player ) . item
2017-11-05 04:06:00 +00:00
magicshopitem_text = random . choice ( MagicShop_texts ) if magicshopitem is None or magicshopitem . magicshop_credit_text is None else magicshopitem . magicshop_credit_text
2017-11-04 18:23:57 +00:00
2019-04-18 09:23:24 +00:00
fluteboyitem = world . get_location ( ' Flute Spot ' , player ) . item
2017-11-05 04:06:00 +00:00
fluteboyitem_text = random . choice ( FluteBoy_texts ) if fluteboyitem is None or fluteboyitem . fluteboy_credit_text is None else fluteboyitem . fluteboy_credit_text
credits . update_credits_line ( ' castle ' , 0 , random . choice ( KingsReturn_texts ) )
2018-08-04 20:29:07 +00:00
credits . update_credits_line ( ' sanctuary ' , 0 , random . choice ( Sanctuary_texts ) )
2018-01-02 02:17:17 +00:00
credits . update_credits_line ( ' kakariko ' , 0 , random . choice ( Kakariko_texts ) . format ( random . choice ( Sahasrahla_names ) ) )
2017-11-05 04:06:00 +00:00
credits . update_credits_line ( ' desert ' , 0 , random . choice ( DesertPalace_texts ) )
credits . update_credits_line ( ' hera ' , 0 , random . choice ( MountainTower_texts ) )
credits . update_credits_line ( ' house ' , 0 , random . choice ( LinksHouse_texts ) )
credits . update_credits_line ( ' zora ' , 0 , zoraitem_text )
credits . update_credits_line ( ' witch ' , 0 , magicshopitem_text )
credits . update_credits_line ( ' lumberjacks ' , 0 , random . choice ( Lumberjacks_texts ) )
credits . update_credits_line ( ' grove ' , 0 , fluteboyitem_text )
credits . update_credits_line ( ' well ' , 0 , random . choice ( WishingWell_texts ) )
credits . update_credits_line ( ' smithy ' , 0 , random . choice ( Blacksmiths_texts ) )
credits . update_credits_line ( ' kakariko2 ' , 0 , sickkiditem_text )
credits . update_credits_line ( ' bridge ' , 0 , random . choice ( DeathMountain_texts ) )
credits . update_credits_line ( ' woods ' , 0 , random . choice ( LostWoods_texts ) )
credits . update_credits_line ( ' pedestal ' , 0 , pedestal_credit_text )
( pointers , data ) = credits . get_bytes ( )
rom . write_bytes ( 0x181500 , data )
rom . write_bytes ( 0x76CC0 , [ byte for p in pointers for byte in [ p & 0xFF , p >> 8 & 0xFF ] ] )
2019-01-20 07:01:02 +00:00
2019-07-27 13:13:13 +00:00
def set_inverted_mode ( world , rom ) :
rom . write_byte ( snes_to_pc ( 0x0283E0 ) , 0xF0 ) # residual portals
rom . write_byte ( snes_to_pc ( 0x02B34D ) , 0xF0 )
rom . write_byte ( snes_to_pc ( 0x06DB78 ) , 0x8B )
2019-08-24 01:46:49 +00:00
rom . write_byte ( snes_to_pc ( 0x05AF79 ) , 0xF0 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x0DB3C5 ) , 0xC6 )
rom . write_byte ( snes_to_pc ( 0x07A3F4 ) , 0xF0 ) # duck
2019-12-09 18:27:56 +00:00
write_int16s ( rom , snes_to_pc ( 0x02E849 ) , [ 0x0043 , 0x0056 , 0x0058 , 0x006C , 0x006F , 0x0070 , 0x007B , 0x007F , 0x001B ] ) # dw flute
write_int16 ( rom , snes_to_pc ( 0x02E8D5 ) , 0x07C8 )
write_int16 ( rom , snes_to_pc ( 0x02E8F7 ) , 0x01F8 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x08D40C ) , 0xD0 ) # morph proof
# the following bytes should only be written in vanilla
# or they'll overwrite the randomizer's shuffles
if world . shuffle == ' vanilla ' :
rom . write_byte ( 0xDBB73 + 0x23 , 0x37 ) # switch AT and GT
rom . write_byte ( 0xDBB73 + 0x36 , 0x24 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15AEE + 2 * 0x38 , 0x00E0 )
write_int16 ( rom , 0x15AEE + 2 * 0x25 , 0x000C )
2019-09-22 02:10:19 +00:00
if world . shuffle in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
rom . write_byte ( 0x15B8C , 0x6C )
rom . write_byte ( 0xDBB73 + 0x00 , 0x53 ) # switch bomb shop and links house
rom . write_byte ( 0xDBB73 + 0x52 , 0x01 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x15 , 0x06 ) # bumper and old man cave
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15AEE + 2 * 0x17 , 0x00F0 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x05 , 0x16 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15AEE + 2 * 0x07 , 0x00FB )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x2D , 0x17 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15AEE + 2 * 0x2F , 0x00EB )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x06 , 0x2E )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15AEE + 2 * 0x08 , 0x00E6 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x16 , 0x5E )
rom . write_byte ( 0xDBB73 + 0x6F , 0x07 ) # DDM fairy to old man cave
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15AEE + 2 * 0x18 , 0x00F1 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x15B8C + 0x18 , 0x43 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15BDB + 2 * 0x18 , 0x1400 )
write_int16 ( rom , 0x15C79 + 2 * 0x18 , 0x0294 )
write_int16 ( rom , 0x15D17 + 2 * 0x18 , 0x0600 )
write_int16 ( rom , 0x15DB5 + 2 * 0x18 , 0x02E8 )
write_int16 ( rom , 0x15E53 + 2 * 0x18 , 0x0678 )
write_int16 ( rom , 0x15EF1 + 2 * 0x18 , 0x0303 )
write_int16 ( rom , 0x15F8F + 2 * 0x18 , 0x0685 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x1602D + 0x18 , 0x0A )
rom . write_byte ( 0x1607C + 0x18 , 0xF6 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x160CB + 2 * 0x18 , 0x0000 )
write_int16 ( rom , 0x16169 + 2 * 0x18 , 0x0000 )
write_int16 ( rom , 0x15AEE + 2 * 0x3D , 0x0003 ) # pyramid exit and houlihan
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x15B8C + 0x3D , 0x5B )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15BDB + 2 * 0x3D , 0x0B0E )
write_int16 ( rom , 0x15C79 + 2 * 0x3D , 0x075A )
write_int16 ( rom , 0x15D17 + 2 * 0x3D , 0x0674 )
write_int16 ( rom , 0x15DB5 + 2 * 0x3D , 0x07A8 )
write_int16 ( rom , 0x15E53 + 2 * 0x3D , 0x06E8 )
write_int16 ( rom , 0x15EF1 + 2 * 0x3D , 0x07C7 )
write_int16 ( rom , 0x15F8F + 2 * 0x3D , 0x06F3 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x1602D + 0x3D , 0x06 )
rom . write_byte ( 0x1607C + 0x3D , 0xFA )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x160CB + 2 * 0x3D , 0x0000 )
write_int16 ( rom , 0x16169 + 2 * 0x3D , 0x0000 )
write_int16 ( rom , snes_to_pc ( 0x02D8D4 ) , 0x112 ) # change sactuary spawn point to dark sanc
2019-07-27 13:13:13 +00:00
rom . write_bytes ( snes_to_pc ( 0x02D8E8 ) , [ 0x22 , 0x22 , 0x22 , 0x23 , 0x04 , 0x04 , 0x04 , 0x05 ] )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , snes_to_pc ( 0x02D91A ) , 0x0400 )
write_int16 ( rom , snes_to_pc ( 0x02D928 ) , 0x222E )
write_int16 ( rom , snes_to_pc ( 0x02D936 ) , 0x229A )
write_int16 ( rom , snes_to_pc ( 0x02D944 ) , 0x0480 )
write_int16 ( rom , snes_to_pc ( 0x02D952 ) , 0x00A5 )
write_int16 ( rom , snes_to_pc ( 0x02D960 ) , 0x007F )
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x02D96D ) , 0x14 )
rom . write_byte ( snes_to_pc ( 0x02D974 ) , 0x00 )
rom . write_byte ( snes_to_pc ( 0x02D97B ) , 0xFF )
rom . write_byte ( snes_to_pc ( 0x02D982 ) , 0x00 )
rom . write_byte ( snes_to_pc ( 0x02D989 ) , 0x02 )
rom . write_byte ( snes_to_pc ( 0x02D990 ) , 0x00 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , snes_to_pc ( 0x02D998 ) , 0x0000 )
write_int16 ( rom , snes_to_pc ( 0x02D9A6 ) , 0x005A )
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x02D9B3 ) , 0x12 )
# keep the old man spawn point at old man house unless shuffle is vanilla
2019-09-22 02:10:19 +00:00
if world . shuffle in [ ' vanilla ' , ' dungeonsfull ' , ' dungeonssimple ' ] :
2019-07-27 13:13:13 +00:00
rom . write_bytes ( snes_to_pc ( 0x308350 ) , [ 0x00 , 0x00 , 0x01 ] )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , snes_to_pc ( 0x02D8DE ) , 0x00F1 )
2019-07-27 13:13:13 +00:00
rom . write_bytes ( snes_to_pc ( 0x02D910 ) , [ 0x1F , 0x1E , 0x1F , 0x1F , 0x03 , 0x02 , 0x03 , 0x03 ] )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , snes_to_pc ( 0x02D924 ) , 0x0300 )
write_int16 ( rom , snes_to_pc ( 0x02D932 ) , 0x1F10 )
write_int16 ( rom , snes_to_pc ( 0x02D940 ) , 0x1FC0 )
write_int16 ( rom , snes_to_pc ( 0x02D94E ) , 0x0378 )
write_int16 ( rom , snes_to_pc ( 0x02D95C ) , 0x0187 )
write_int16 ( rom , snes_to_pc ( 0x02D96A ) , 0x017F )
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x02D972 ) , 0x06 )
rom . write_byte ( snes_to_pc ( 0x02D979 ) , 0x00 )
rom . write_byte ( snes_to_pc ( 0x02D980 ) , 0xFF )
rom . write_byte ( snes_to_pc ( 0x02D987 ) , 0x00 )
rom . write_byte ( snes_to_pc ( 0x02D98E ) , 0x22 )
rom . write_byte ( snes_to_pc ( 0x02D995 ) , 0x12 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , snes_to_pc ( 0x02D9A2 ) , 0x0000 )
write_int16 ( rom , snes_to_pc ( 0x02D9B0 ) , 0x0007 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x02D9B8 ) , 0x12 )
rom . write_bytes ( 0x180247 , [ 0x00 , 0x5A , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ] )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15AEE + 2 * 0x06 , 0x0020 ) # post aga hyrule castle spawn
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x15B8C + 0x06 , 0x1B )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15BDB + 2 * 0x06 , 0x00AE )
write_int16 ( rom , 0x15C79 + 2 * 0x06 , 0x0610 )
write_int16 ( rom , 0x15D17 + 2 * 0x06 , 0x077E )
write_int16 ( rom , 0x15DB5 + 2 * 0x06 , 0x0672 )
write_int16 ( rom , 0x15E53 + 2 * 0x06 , 0x07F8 )
write_int16 ( rom , 0x15EF1 + 2 * 0x06 , 0x067D )
write_int16 ( rom , 0x15F8F + 2 * 0x06 , 0x0803 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x1602D + 0x06 , 0x00 )
rom . write_byte ( 0x1607C + 0x06 , 0xF2 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x160CB + 2 * 0x06 , 0x0000 )
write_int16 ( rom , 0x16169 + 2 * 0x06 , 0x0000 )
write_int16 ( rom , snes_to_pc ( 0x02E87B ) , 0x00AE ) # move flute splot 9
write_int16 ( rom , snes_to_pc ( 0x02E89D ) , 0x0610 )
write_int16 ( rom , snes_to_pc ( 0x02E8BF ) , 0x077E )
write_int16 ( rom , snes_to_pc ( 0x02E8E1 ) , 0x0672 )
write_int16 ( rom , snes_to_pc ( 0x02E903 ) , 0x07F8 )
write_int16 ( rom , snes_to_pc ( 0x02E925 ) , 0x067D )
write_int16 ( rom , snes_to_pc ( 0x02E947 ) , 0x0803 )
write_int16 ( rom , snes_to_pc ( 0x02E969 ) , 0x0000 )
write_int16 ( rom , snes_to_pc ( 0x02E98B ) , 0xFFF2 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x1AF696 ) , 0xF0 ) # bat sprite retreat
rom . write_byte ( snes_to_pc ( 0x1AF6B2 ) , 0x33 )
rom . write_bytes ( snes_to_pc ( 0x1AF730 ) , [ 0x6A , 0x9E , 0x0C , 0x00 , 0x7A , 0x9E , 0x0C ,
0x00 , 0x8A , 0x9E , 0x0C , 0x00 , 0x6A , 0xAE ,
0x0C , 0x00 , 0x7A , 0xAE , 0x0C , 0x00 , 0x8A ,
0xAE , 0x0C , 0x00 , 0x67 , 0x97 , 0x0C , 0x00 ,
0x8D , 0x97 , 0x0C , 0x00 ] )
2019-12-09 18:27:56 +00:00
write_int16s ( rom , snes_to_pc ( 0x0FF1C8 ) , [ 0x190F , 0x190F , 0x190F , 0x194C , 0x190F ,
2019-07-27 13:13:13 +00:00
0x194B , 0x190F , 0x195C , 0x594B , 0x194C ,
0x19EE , 0x19EE , 0x194B , 0x19EE , 0x19EE ,
0x19EE , 0x594B , 0x190F , 0x595C , 0x190F ,
0x190F , 0x195B , 0x190F , 0x190F , 0x19EE ,
0x19EE , 0x195C , 0x19EE , 0x19EE , 0x19EE ,
0x19EE , 0x595C , 0x595B , 0x190F , 0x190F ,
0x190F ] )
2019-12-09 18:27:56 +00:00
write_int16s ( rom , snes_to_pc ( 0x0FA480 ) , [ 0x190F , 0x196B , 0x9D04 , 0x9D04 , 0x196B ,
2019-07-27 13:13:13 +00:00
0x190F , 0x9D04 , 0x9D04 ] )
2019-12-09 18:27:56 +00:00
write_int16s ( rom , snes_to_pc ( 0x1bb810 ) , [ 0x00BE , 0x00C0 , 0x013E ] )
write_int16s ( rom , snes_to_pc ( 0x1bb836 ) , [ 0x001B , 0x001B , 0x001B ] )
write_int16 ( rom , snes_to_pc ( 0x308300 ) , 0x0140 ) # new pyramid hole entrance
write_int16 ( rom , snes_to_pc ( 0x308320 ) , 0x001B )
2019-09-22 01:59:16 +00:00
if world . shuffle in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x308340 ) , 0x7B )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , snes_to_pc ( 0x1af504 ) , 0x148B )
write_int16 ( rom , snes_to_pc ( 0x1af50c ) , 0x149B )
write_int16 ( rom , snes_to_pc ( 0x1af514 ) , 0x14A4 )
write_int16 ( rom , snes_to_pc ( 0x1af51c ) , 0x1489 )
write_int16 ( rom , snes_to_pc ( 0x1af524 ) , 0x14AC )
write_int16 ( rom , snes_to_pc ( 0x1af52c ) , 0x54AC )
write_int16 ( rom , snes_to_pc ( 0x1af534 ) , 0x148C )
write_int16 ( rom , snes_to_pc ( 0x1af53c ) , 0x548C )
write_int16 ( rom , snes_to_pc ( 0x1af544 ) , 0x1484 )
write_int16 ( rom , snes_to_pc ( 0x1af54c ) , 0x5484 )
write_int16 ( rom , snes_to_pc ( 0x1af554 ) , 0x14A2 )
write_int16 ( rom , snes_to_pc ( 0x1af55c ) , 0x54A2 )
write_int16 ( rom , snes_to_pc ( 0x1af564 ) , 0x14A0 )
write_int16 ( rom , snes_to_pc ( 0x1af56c ) , 0x54A0 )
write_int16 ( rom , snes_to_pc ( 0x1af574 ) , 0x148E )
write_int16 ( rom , snes_to_pc ( 0x1af57c ) , 0x548E )
write_int16 ( rom , snes_to_pc ( 0x1af584 ) , 0x14AE )
write_int16 ( rom , snes_to_pc ( 0x1af58c ) , 0x54AE )
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x00DB9D ) , 0x1A ) # castle hole graphics
rom . write_byte ( snes_to_pc ( 0x00DC09 ) , 0x1A )
rom . write_byte ( snes_to_pc ( 0x00D009 ) , 0x31 )
rom . write_byte ( snes_to_pc ( 0x00D0e8 ) , 0xE0 )
rom . write_byte ( snes_to_pc ( 0x00D1c7 ) , 0x00 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , snes_to_pc ( 0x1BE8DA ) , 0x39AD )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xF6E58 , 0x80 ) # no whirlpool under castle gate
rom . write_bytes ( 0x0086E , [ 0x5C , 0x00 , 0xA0 , 0xA1 ] ) # TR tail
rom . write_bytes ( snes_to_pc ( 0x1BC67A ) , [ 0x2E , 0x0B , 0x82 ] ) # add warps under rocks
rom . write_bytes ( snes_to_pc ( 0x1BC81E ) , [ 0x94 , 0x1D , 0x82 ] )
rom . write_bytes ( snes_to_pc ( 0x1BC655 ) , [ 0x4A , 0x1D , 0x82 ] )
rom . write_bytes ( snes_to_pc ( 0x1BC80D ) , [ 0xB2 , 0x0B , 0x82 ] )
rom . write_bytes ( snes_to_pc ( 0x1BC3DF ) , [ 0xD8 , 0xD1 ] )
rom . write_bytes ( snes_to_pc ( 0x1BD1D8 ) , [ 0xA8 , 0x02 , 0x82 , 0xFF , 0xFF ] )
rom . write_bytes ( snes_to_pc ( 0x1BC85A ) , [ 0x50 , 0x0F , 0x82 ] )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0xDB96F + 2 * 0x35 , 0x001B ) # move pyramid exit door
write_int16 ( rom , 0xDBA71 + 2 * 0x35 , 0x06A4 )
2019-09-22 01:59:16 +00:00
if world . shuffle in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x35 , 0x36 )
rom . write_byte ( snes_to_pc ( 0x09D436 ) , 0xF3 ) # remove castle gate warp
2019-09-22 01:59:16 +00:00
if world . shuffle in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15AEE + 2 * 0x37 , 0x0010 ) # pyramid exit to new hc area
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x15B8C + 0x37 , 0x1B )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x15BDB + 2 * 0x37 , 0x0418 )
write_int16 ( rom , 0x15C79 + 2 * 0x37 , 0x0679 )
write_int16 ( rom , 0x15D17 + 2 * 0x37 , 0x06B4 )
write_int16 ( rom , 0x15DB5 + 2 * 0x37 , 0x06C6 )
write_int16 ( rom , 0x15E53 + 2 * 0x37 , 0x0738 )
write_int16 ( rom , 0x15EF1 + 2 * 0x37 , 0x06E6 )
write_int16 ( rom , 0x15F8F + 2 * 0x37 , 0x0733 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x1602D + 0x37 , 0x07 )
rom . write_byte ( 0x1607C + 0x37 , 0xF9 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x160CB + 2 * 0x37 , 0x0000 )
write_int16 ( rom , 0x16169 + 2 * 0x37 , 0x0000 )
2019-07-27 13:13:13 +00:00
rom . write_bytes ( snes_to_pc ( 0x1BC387 ) , [ 0xDD , 0xD1 ] )
rom . write_bytes ( snes_to_pc ( 0x1BD1DD ) , [ 0xA4 , 0x06 , 0x82 , 0x9E , 0x06 , 0x82 , 0xFF , 0xFF ] )
rom . write_byte ( 0x180089 , 0x01 ) # open TR after exit
rom . write_byte ( snes_to_pc ( 0x0ABFBB ) , 0x90 )
rom . write_byte ( snes_to_pc ( 0x0280A6 ) , 0xD0 )
rom . write_bytes ( snes_to_pc ( 0x06B2AB ) , [ 0xF0 , 0xE1 , 0x05 ] )
def patch_shuffled_dark_sanc ( world , rom , player ) :
dark_sanc_entrance = str ( world . get_region ( ' Inverted Dark Sanctuary ' , player ) . entrances [ 0 ] . name )
room_id , ow_area , vram_loc , scroll_y , scroll_x , link_y , link_x , camera_y , camera_x , unknown_1 , unknown_2 , door_1 , door_2 = door_addresses [ dark_sanc_entrance ] [ 1 ]
door_index = door_addresses [ str ( dark_sanc_entrance ) ] [ 0 ]
rom . write_byte ( 0x180241 , 0x01 )
rom . write_byte ( 0x180248 , door_index + 1 )
2019-12-09 18:27:56 +00:00
write_int16 ( rom , 0x180250 , room_id )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x180252 , ow_area )
2019-12-09 18:27:56 +00:00
write_int16s ( rom , 0x180253 , [ vram_loc , scroll_y , scroll_x , link_y , link_x , camera_y , camera_x ] )
2019-07-27 13:13:13 +00:00
rom . write_bytes ( 0x180262 , [ unknown_1 , unknown_2 , 0x00 ] )
2019-09-16 05:51:24 +00:00
InconvenientDungeonEntrances = { ' Turtle Rock ' : ' Turtle Rock Main ' ,
' Misery Mire ' : ' Misery Mire ' ,
' Ice Palace ' : ' Ice Palace ' ,
' Skull Woods Final Section ' : ' The back of Skull Woods ' ,
}
InconvenientOtherEntrances = { ' Death Mountain Return Cave (West) ' : ' The SW DM foothills cave ' ,
' Mimic Cave ' : ' Mimic Ledge ' ,
' Dark World Hammer Peg Cave ' : ' The rows of pegs ' ,
' Pyramid Fairy ' : ' The crack on the pyramid '
}
ConnectorEntrances = { ' Elder House (East) ' : ' Elder House ' ,
' Elder House (West) ' : ' Elder House ' ,
' Two Brothers House (East) ' : ' Eastern Quarreling Brothers \' house ' ,
' Old Man Cave (West) ' : ' The lower DM entrance ' ,
' Bumper Cave (Bottom) ' : ' The lower Bumper Cave ' ,
' Superbunny Cave (Top) ' : ' The summit of dark DM cave ' ,
' Superbunny Cave (Bottom) ' : ' The base of east dark DM ' ,
' Hookshot Cave ' : ' The rock on dark DM ' ,
' Two Brothers House (West) ' : ' The door near the race game ' ,
' Old Man Cave (East) ' : ' The SW-most cave on west DM ' ,
' Old Man House (Bottom) ' : ' A cave with a door on west DM ' ,
' Old Man House (Top) ' : ' The eastmost cave on west DM ' ,
' Death Mountain Return Cave (East) ' : ' The westmost cave on west DM ' ,
' Spectacle Rock Cave Peak ' : ' The highest cave on west DM ' ,
' Spectacle Rock Cave ' : ' The right ledge on west DM ' ,
' Spectacle Rock Cave (Bottom) ' : ' The left ledge on west DM ' ,
' Paradox Cave (Bottom) ' : ' The right paired cave on east DM ' ,
' Paradox Cave (Middle) ' : ' The southmost cave on east DM ' ,
' Paradox Cave (Top) ' : ' The east DM summit cave ' ,
' Fairy Ascension Cave (Bottom) ' : ' The east DM cave behind rocks ' ,
' Fairy Ascension Cave (Top) ' : ' The central ledge on east DM ' ,
' Spiral Cave ' : ' The left ledge on east DM ' ,
' Spiral Cave (Bottom) ' : ' The SWmost cave on east DM '
}
DungeonEntrances = { ' Eastern Palace ' : ' Eastern Palace ' ,
' Hyrule Castle Entrance (South) ' : ' The ground level castle door ' ,
' Thieves Town ' : ' Thieves \' Town ' ,
' Swamp Palace ' : ' Swamp Palace ' ,
' Dark Death Mountain Ledge (West) ' : ' The East dark DM connector ledge ' ,
' Dark Death Mountain Ledge (East) ' : ' The East dark DM connector ledge ' ,
' Desert Palace Entrance (South) ' : ' The book sealed passage ' ,
' Tower of Hera ' : ' The Tower of Hera ' ,
' Palace of Darkness ' : ' Palace of Darkness ' ,
' Hyrule Castle Entrance (West) ' : ' The left castle door ' ,
' Hyrule Castle Entrance (East) ' : ' The right castle door ' ,
' Desert Palace Entrance (West) ' : ' The westmost building in the desert ' ,
' Desert Palace Entrance (North) ' : ' The northmost cave in the desert '
}
OtherEntrances = { ' Blinds Hideout ' : ' Blind \' s old house ' ,
2019-01-20 07:01:02 +00:00
' Lake Hylia Fairy ' : ' A cave NE of Lake Hylia ' ,
' Light Hype Fairy ' : ' The cave south of your house ' ,
' Desert Fairy ' : ' The cave near the desert ' ,
' Chicken House ' : ' The chicken lady \' s house ' ,
2019-04-10 22:03:01 +00:00
' Aginahs Cave ' : ' The open desert cave ' ,
' Sahasrahlas Hut ' : ' The house near armos ' ,
2019-01-20 07:01:02 +00:00
' Cave Shop (Lake Hylia) ' : ' The cave NW Lake Hylia ' ,
' Blacksmiths Hut ' : ' The old smithery ' ,
' Sick Kids House ' : ' The central house in Kakariko ' ,
' Lost Woods Gamble ' : ' A tree trunk door ' ,
' Fortune Teller (Light) ' : ' A building NE of Kakariko ' ,
' Snitch Lady (East) ' : ' A house guarded by a snitch ' ,
' Snitch Lady (West) ' : ' A house guarded by a snitch ' ,
' Bush Covered House ' : ' A house with an uncut lawn ' ,
' Tavern (Front) ' : ' A building with a backdoor ' ,
' Light World Bomb Hut ' : ' A Kakariko building with no door ' ,
' Kakariko Shop ' : ' The old Kakariko shop ' ,
' Mini Moldorm Cave ' : ' The cave south of Lake Hylia ' ,
' Long Fairy Cave ' : ' The eastmost portal cave ' ,
' Good Bee Cave ' : ' The open cave SE Lake Hylia ' ,
' 20 Rupee Cave ' : ' The rock SE Lake Hylia ' ,
' 50 Rupee Cave ' : ' The rock near the desert ' ,
' Ice Rod Cave ' : ' The sealed cave SE Lake Hylia ' ,
' Library ' : ' The old library ' ,
' Potion Shop ' : ' The witch \' s building ' ,
' Dam ' : ' The old dam ' ,
' Lumberjack House ' : ' The lumberjack house ' ,
' Lake Hylia Fortune Teller ' : ' The building NW Lake Hylia ' ,
' Kakariko Gamble Game ' : ' The old Kakariko gambling den ' ,
' Waterfall of Wishing ' : ' Going behind the waterfall ' ,
' Capacity Upgrade ' : ' The cave on the island ' ,
' Bonk Rock Cave ' : ' The rock pile near Sanctuary ' ,
' Graveyard Cave ' : ' The graveyard ledge ' ,
' Checkerboard Cave ' : ' The NE desert ledge ' ,
' Cave 45 ' : ' The ledge south of haunted grove ' ,
' Kings Grave ' : ' The northeastmost grave ' ,
' Bonk Fairy (Light) ' : ' The rock pile near your home ' ,
2019-04-10 22:03:01 +00:00
' Hookshot Fairy ' : ' The left paired cave on east DM ' ,
2019-01-20 07:01:02 +00:00
' Bonk Fairy (Dark) ' : ' The rock pile near the old bomb shop ' ,
' Dark Lake Hylia Fairy ' : ' The cave NE dark Lake Hylia ' ,
' C-Shaped House ' : ' The NE house in Village of Outcasts ' ,
' Dark Death Mountain Fairy ' : ' The SW cave on dark DM ' ,
' Dark Lake Hylia Shop ' : ' The building NW dark Lake Hylia ' ,
' Dark World Shop ' : ' The hammer sealed building ' ,
' Red Shield Shop ' : ' The fenced in building ' ,
' Mire Shed ' : ' The western hut in the mire ' ,
' East Dark World Hint ' : ' The dark cave near the eastmost portal ' ,
' Dark Desert Hint ' : ' The cave east of the mire ' ,
' Spike Cave ' : ' The ledge cave on west dark DM ' ,
' Palace of Darkness Hint ' : ' The building south of Kiki ' ,
' Dark Lake Hylia Ledge Spike Cave ' : ' The rock SE dark Lake Hylia ' ,
' Cave Shop (Dark Death Mountain) ' : ' The base of east dark DM ' ,
' Dark World Potion Shop ' : ' The building near the catfish ' ,
' Archery Game ' : ' The old archery game ' ,
' Dark World Lumberjack Shop ' : ' The northmost Dark World building ' ,
' Hype Cave ' : ' The cave south of the old bomb shop ' ,
' Brewery ' : ' The Village of Outcasts building with no door ' ,
' Dark Lake Hylia Ledge Hint ' : ' The open cave SE dark Lake Hylia ' ,
' Chest Game ' : ' The westmost building in the Village of Outcasts ' ,
' Dark Desert Fairy ' : ' The eastern hut in the mire ' ,
' Dark Lake Hylia Ledge Fairy ' : ' The sealed cave SE dark Lake Hylia ' ,
' Fortune Teller (Dark) ' : ' The building NE the Village of Outcasts '
}
InsanityEntrances = { ' Sanctuary ' : ' Sanctuary ' ,
' Lumberjack Tree Cave ' : ' The cave Behind Lumberjacks ' ,
' Lost Woods Hideout Stump ' : ' The stump in Lost Woods ' ,
' North Fairy Cave ' : ' The cave East of Graveyard ' ,
' Bat Cave Cave ' : ' The cave in eastern Kakariko ' ,
' Kakariko Well Cave ' : ' The cave in northern Kakariko ' ,
' Hyrule Castle Secret Entrance Stairs ' : ' The tunnel near the castle ' ,
' Skull Woods First Section Door ' : ' The southeastmost skull ' ,
' Skull Woods Second Section Door (East) ' : ' The central open skull ' ,
' Skull Woods Second Section Door (West) ' : ' The westmost open skull ' ,
' Desert Palace Entrance (East) ' : ' The eastern building in the desert ' ,
' Turtle Rock Isolated Ledge Entrance ' : ' The isolated ledge on east dark DM ' ,
' Bumper Cave (Top) ' : ' The upper Bumper Cave ' ,
' Hookshot Cave Back Entrance ' : ' The stairs on the floating island '
}
2019-07-25 22:25:14 +00:00
HintLocations = [ ' telepathic_tile_eastern_palace ' ,
' telepathic_tile_tower_of_hera_floor_4 ' ,
' telepathic_tile_spectacle_rock ' ,
' telepathic_tile_swamp_entrance ' ,
' telepathic_tile_thieves_town_upstairs ' ,
' telepathic_tile_misery_mire ' ,
' telepathic_tile_palace_of_darkness ' ,
' telepathic_tile_desert_bonk_torch_room ' ,
' telepathic_tile_castle_tower ' ,
' telepathic_tile_ice_large_room ' ,
' telepathic_tile_turtle_rock ' ,
' telepathic_tile_ice_entrace ' ,
' telepathic_tile_ice_stalfos_knights_room ' ,
' telepathic_tile_tower_of_hera_entrance ' ,
' telepathic_tile_south_east_darkworld_cave ' ,
' dark_palace_tree_dude ' ,
' dark_sanctuary_hint_0 ' ,
' dark_sanctuary_hint_1 ' ,
' dark_sanctuary_yes ' ,
' dark_sanctuary_hint_2 ' ]
InconvenientLocations = [ ' Spike Cave ' ,
' Sahasrahla ' ,
' Purple Chest ' ,
' Swamp Left ' ,
' Mire Left ' ,
' Tower of Hera - Big Key Chest ' ,
' Eastern Palace - Big Key Chest ' ,
' Thieves \' Town - Big Chest ' ,
' Ice Palace - Big Chest ' ,
' Ganons Tower - Big Chest ' ,
' Magic Bat ' ]
2019-09-16 05:51:24 +00:00
InconvenientVanillaLocations = [ ' Graveyard Cave ' ,
' Mimic Cave ' ]
2019-07-25 22:25:14 +00:00
RelevantItems = [ ' Bow ' ,
2019-09-16 05:51:24 +00:00
' Progressive Bow ' ,
2019-07-25 22:25:14 +00:00
' Book of Mudora ' ,
' Hammer ' ,
' Hookshot ' ,
' Magic Mirror ' ,
' Ocarina ' ,
' Pegasus Boots ' ,
' Power Glove ' ,
' Cape ' ,
' Mushroom ' ,
' Shovel ' ,
' Lamp ' ,
' Magic Powder ' ,
' Moon Pearl ' ,
' Cane of Somaria ' ,
' Fire Rod ' ,
' Flippers ' ,
' Ice Rod ' ,
' Titans Mitts ' ,
' Ether ' ,
' Bombos ' ,
' Quake ' ,
' Bottle ' ,
' Bottle (Red Potion) ' ,
' Bottle (Green Potion) ' ,
' Bottle (Blue Potion) ' ,
' Bottle (Fairy) ' ,
' Bottle (Bee) ' ,
' Bottle (Good Bee) ' ,
' Master Sword ' ,
' Tempered Sword ' ,
' Fighter Sword ' ,
' Golden Sword ' ,
' Progressive Sword ' ,
' Progressive Glove ' ,
' Master Sword ' ,
' Power Star ' ,
' Triforce Piece ' ,
' Single Arrow ' ,
' Blue Mail ' ,
' Red Mail ' ,
' Progressive Armor ' ,
' Blue Boomerang ' ,
' Red Boomerang ' ,
' Blue Shield ' ,
' Red Shield ' ,
' Mirror Shield ' ,
' Progressive Shield ' ,
' Bug Catching Net ' ,
' Cane of Byrna ' ,
' Magic Upgrade (1/2) ' ,
' Magic Upgrade (1/4) '
]
2019-12-13 21:37:52 +00:00
SmallKeys = [ ' Small Key (Eastern Palace) ' ,
' Small Key (Escape) ' ,
' Small Key (Desert Palace) ' ,
' Small Key (Tower of Hera) ' ,
' Small Key (Agahnims Tower) ' ,
' Small Key (Palace of Darkness) ' ,
' Small Key (Thieves Town) ' ,
' Small Key (Swamp Palace) ' ,
' Small Key (Skull Woods) ' ,
' Small Key (Ice Palace) ' ,
' Small Key (Misery Mire) ' ,
' Small Key (Turtle Rock) ' ,
' Small Key (Ganons Tower) ' ,
]
BigKeys = [ ' Big Key (Eastern Palace) ' ,
' Big Key (Desert Palace) ' ,
' Big Key (Tower of Hera) ' ,
' Big Key (Palace of Darkness) ' ,
' Big Key (Thieves Town) ' ,
' Big Key (Swamp Palace) ' ,
' Big Key (Skull Woods) ' ,
' Big Key (Ice Palace) ' ,
' Big Key (Misery Mire) ' ,
' Big Key (Turtle Rock) ' ,
' Big Key (Ganons Tower) '
]