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
2017-12-08 00:43:22 +00:00
import struct
2017-12-17 05:25:46 +00:00
import random
2018-02-17 23:38:54 +00:00
from BaseClasses import ShopType
2017-12-17 05:25:46 +00:00
from Dungeons import dungeon_music_addresses
2018-03-10 23:45:14 +00:00
from Text import MultiByteTextMapper , text_addresses , Credits
2017-12-17 05:25:46 +00:00
from Text import Uncle_texts , Ganon1_texts , PyramidFairy_texts , TavernMan_texts , Sahasrahla2_texts , Triforce_texts , Blind_texts , BombShop2_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
2018-02-17 23:38:54 +00:00
from Utils import local_path , int16_as_bytes , int32_as_bytes
2018-01-04 06:06:22 +00:00
from Items import ItemFactory
2017-12-17 05:25:46 +00:00
2017-06-03 12:20:39 +00:00
JAP10HASH = ' 03a63945398191337e896e5771f77173 '
2018-02-17 23:38:54 +00:00
RANDOMIZERBASEHASH = ' 354bad0d01b6284c89f05c9b414d48c1 '
2017-05-25 10:09:50 +00:00
2017-07-14 12:37:34 +00:00
class JsonRom ( object ) :
def __init__ ( self ) :
self . patches = { }
def write_byte ( self , address , value ) :
self . patches [ str ( address ) ] = [ value ]
def write_bytes ( self , startaddress , values ) :
self . patches [ str ( startaddress ) ] = list ( values )
2017-11-19 01:43:37 +00:00
2017-11-11 21:25:56 +00:00
def write_int16_to_rom ( self , address , value ) :
self . write_bytes ( address , int16_as_bytes ( value ) )
2017-11-19 01:43:37 +00:00
2017-11-11 21:25:56 +00:00
def write_int32_to_rom ( self , address , value ) :
self . write_bytes ( address , int32_as_bytes ( value ) )
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
class LocalRom ( object ) :
2017-12-14 04:21:43 +00:00
def __init__ ( self , file , patch = True ) :
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 )
2017-11-11 21:25:56 +00:00
def write_int16_to_rom ( self , address , value ) :
self . write_bytes ( address , int16_as_bytes ( value ) )
2017-11-19 01:43:37 +00:00
2017-11-11 21:25:56 +00:00
def write_int32_to_rom ( self , address , value ) :
self . write_bytes ( address , int32_as_bytes ( value ) )
2017-07-14 12:37:34 +00:00
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
self . buffer . extend ( bytearray ( [ 0x00 ] * ( 2097152 - len ( self . buffer ) ) ) )
# 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
patchedmd5 = hashlib . md5 ( )
patchedmd5 . update ( self . buffer )
2017-12-17 05:25:46 +00:00
if RANDOMIZERBASEHASH != patchedmd5 . hexdigest ( ) :
2017-11-04 18:23:57 +00:00
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
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-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
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 )
2018-02-28 02:26:33 +00:00
def patch_rom ( world , rom , hashtable , beep = ' normal ' , color = ' red ' , sprite = None ) :
2017-05-25 10:09:50 +00:00
# patch items
for location in world . get_locations ( ) :
itemid = location . item . code if location . item is not None else 0x5A
2017-06-17 12:40:37 +00:00
if itemid is None or location . address is None :
continue
2017-05-25 15:47:15 +00:00
locationaddress = location . address
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
2017-11-10 10:08:59 +00:00
if location . item is not None and location . item . key and dungeon . is_dungeon_item ( location . item ) :
2017-11-04 18:23:57 +00:00
if location . item . type == " BigKey " :
itemid = 0x32
if location . item . type == " SmallKey " :
itemid = 0x24
2017-07-14 12:37:34 +00:00
rom . write_byte ( locationaddress , itemid )
2017-05-25 15:47:15 +00:00
else :
2017-05-25 10:09:50 +00:00
# crystals
for address , value in zip ( locationaddress , 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 ]
2017-10-28 22:34:37 +00:00
if world . keysanity :
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
2017-10-28 22:34:37 +00:00
if world . keysanity :
2017-11-04 18:23:57 +00:00
rom . write_byte ( 0x155C9 , random . choice ( [ 0x11 , 0x16 ] ) ) # Randomize GT music too in keysanity mode
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 :
if exit . target is not None :
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 )
rom . write_int16_to_rom ( 0x15BDB + 2 * offset , vram_loc )
rom . write_int16_to_rom ( 0x15C79 + 2 * offset , scroll_y )
rom . write_int16_to_rom ( 0x15D17 + 2 * offset , scroll_x )
# 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.
rom . write_int16_to_rom ( 0x15DB5 + 2 * offset , link_y ) # same as final else
elif room_id == 0x0059 and world . fix_skullwoods_exit :
2018-01-17 01:56:35 +00:00
rom . write_int16_to_rom ( 0x15DB5 + 2 * offset , 0x00F8 )
elif room_id == 0x004a and world . fix_palaceofdarkness_exit :
rom . write_int16_to_rom ( 0x15DB5 + 2 * offset , 0x0640 )
elif room_id == 0x00d6 and world . fix_trock_exit :
rom . write_int16_to_rom ( 0x15DB5 + 2 * offset , 0x0134 )
elif room_id == 0x000c and world . fix_gtower_exit : # fix ganons tower exit point
2018-02-17 23:15:51 +00:00
rom . write_int16_to_rom ( 0x15DB5 + 2 * offset , 0x00A4 )
2018-01-17 01:56:35 +00:00
else :
rom . write_int16_to_rom ( 0x15DB5 + 2 * offset , link_y )
2018-01-19 02:36:06 +00:00
2018-01-17 01:56:35 +00:00
rom . write_int16_to_rom ( 0x15E53 + 2 * offset , link_x )
rom . write_int16_to_rom ( 0x15EF1 + 2 * offset , camera_y )
rom . write_int16_to_rom ( 0x15F8F + 2 * offset , camera_x )
rom . write_byte ( 0x1602D + offset , unknown_1 )
rom . write_byte ( 0x1607C + offset , unknown_2 )
rom . write_int16_to_rom ( 0x160CB + 2 * offset , door_1 )
rom . write_int16_to_rom ( 0x16169 + 2 * offset , door_2 )
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 )
2018-02-17 23:38:54 +00:00
write_custom_shops ( rom , world )
2017-06-17 11:16:13 +00:00
2017-05-25 10:09:50 +00:00
# patch medallion requirements
if world . required_medallions [ 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 )
2017-05-25 10:09:50 +00:00
elif world . required_medallions [ 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 )
2017-05-25 10:09:50 +00:00
if world . required_medallions [ 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 )
2017-05-25 10:09:50 +00:00
elif world . required_medallions [ 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:
2017-06-24 16:48:03 +00:00
if world . mode in [ ' open ' , ' swordless ' ] :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180032 , 0x01 ) # open mode
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-05-25 10:09:50 +00:00
else :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180032 , 0x00 ) # standard mode
2017-06-03 13:46:05 +00:00
# set light cones
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180038 , 0x01 if world . sewer_light_cone else 0x00 )
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
TRIFORCE_PIECE = ItemFactory ( ' Triforce Piece ' ) . code
GREEN_CLOCK = ItemFactory ( ' Green Clock ' ) . code
2018-03-02 01:29:18 +00:00
rom . write_byte ( 0x18004F , 0x01 ) # Byrna Invulnerability: on
2017-05-25 10:09:50 +00:00
# handle difficulty
2017-06-04 12:44:23 +00:00
if world . difficulty == ' 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
rom . write_bytes ( 0x3ADA7 , [ 0x02 , 0x02 , 0x02 ] )
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
2018-01-22 02:43:44 +00:00
rom . write_int16_to_rom ( 0x180036 , world . rupoor_cost )
2018-03-14 18:30:11 +00:00
# Set stun items
rom . write_byte ( 0x180180 , 0x01 ) # Hookshot only
# Make silver arrows only usable against Ganon
rom . write_byte ( 0x180181 , 0x01 )
2017-11-11 21:25:56 +00:00
#Make Blue Shield more expensive
rom . write_bytes ( 0xF73D2 , [ 0xFC , 0xFF ] )
rom . write_bytes ( 0xF73DA , [ 0x04 , 0x00 ] )
rom . write_bytes ( 0xF73E2 , [ 0x0C , 0x00 ] )
rom . write_byte ( 0xF73D6 , 0x31 )
rom . write_byte ( 0xF73DE , 0x30 )
rom . write_byte ( 0xF73E6 , 0x30 )
rom . write_byte ( 0xF7201 , 0x00 )
rom . write_byte ( 0xF71FF , 0x64 )
#Make Red Shield more expensive
rom . write_bytes ( 0xF73FA , [ 0xFC , 0xFF ] )
rom . write_bytes ( 0xF7402 , [ 0x04 , 0x00 ] )
rom . write_bytes ( 0xF740A , [ 0x0C , 0x00 ] )
rom . write_byte ( 0xF73FE , 0x33 )
rom . write_byte ( 0xF7406 , 0x33 )
rom . write_byte ( 0xF740E , 0x33 )
rom . write_byte ( 0xF7241 , 0x03 )
rom . write_byte ( 0xF723F , 0xE7 )
2017-11-10 10:08:59 +00:00
elif world . difficulty == ' expert ' :
# 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
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180084 , 0x08 ) # One Heart
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
2018-03-14 18:30:11 +00:00
rom . write_bytes ( 0x3ADA7 , [ 0x01 , 0x01 , 0x01 ] )
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
2018-03-14 18:30:11 +00:00
rom . write_int16_to_rom ( 0x180036 , world . rupoor_cost )
# Set stun items
rom . write_byte ( 0x180180 , 0x00 ) # Nothing
# Make silver arrows only usable against Ganon
rom . write_byte ( 0x180181 , 0x01 )
2017-11-11 21:25:56 +00:00
#Make Blue Shield more expensive
rom . write_bytes ( 0xF73D2 , [ 0xFC , 0xFF ] )
rom . write_bytes ( 0xF73DA , [ 0x04 , 0x00 ] )
rom . write_bytes ( 0xF73E2 , [ 0x0C , 0x00 ] )
rom . write_byte ( 0xF73D6 , 0x3C )
rom . write_byte ( 0xF73DE , 0x3C )
rom . write_byte ( 0xF73E6 , 0x3C )
rom . write_byte ( 0xF7201 , 0x27 )
rom . write_byte ( 0xF71FF , 0x06 )
#Make Red Shield more expensive
rom . write_bytes ( 0xF73FA , [ 0xFC , 0xFF ] )
rom . write_bytes ( 0xF7402 , [ 0x04 , 0x00 ] )
rom . write_bytes ( 0xF740A , [ 0x0C , 0x00 ] )
rom . write_byte ( 0xF73FE , 0x3C )
rom . write_byte ( 0xF7406 , 0x3C )
rom . write_byte ( 0xF740E , 0x3C )
rom . write_byte ( 0xF7241 , 0x27 )
rom . write_byte ( 0xF723F , 0x06 )
2017-11-10 10:08:59 +00:00
elif world . difficulty == ' insane ' :
# Powdered Fairies Prize
rom . write_byte ( 0x36DD0 , 0x79 ) # Bees
# potion heal amount
rom . write_byte ( 0x180084 , 0x00 ) # No healing
# potion magic restore amount
rom . write_byte ( 0x180085 , 0x00 ) # No healing
#Cape magic cost
2018-03-14 18:30:11 +00:00
rom . write_bytes ( 0x3ADA7 , [ 0x01 , 0x01 , 0x01 ] )
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
2018-03-14 18:30:11 +00:00
rom . write_int16_to_rom ( 0x180036 , world . rupoor_cost )
# Set stun items
rom . write_byte ( 0x180180 , 0x00 ) # Nothing
# Make silver arrows only usable against Ganon
rom . write_byte ( 0x180181 , 0x01 )
2017-11-11 21:25:56 +00:00
#Make Blue Shield more expensive
rom . write_bytes ( 0xF73D2 , [ 0xFC , 0xFF ] )
rom . write_bytes ( 0xF73DA , [ 0x04 , 0x00 ] )
rom . write_bytes ( 0xF73E2 , [ 0x0C , 0x00 ] )
rom . write_byte ( 0xF73D6 , 0x3C )
rom . write_byte ( 0xF73DE , 0x3C )
rom . write_byte ( 0xF73E6 , 0x3C )
rom . write_byte ( 0xF7201 , 0x27 )
rom . write_byte ( 0xF71FF , 0x10 )
#Make Red Shield more expensive
rom . write_bytes ( 0xF73FA , [ 0xFC , 0xFF ] )
rom . write_bytes ( 0xF7402 , [ 0x04 , 0x00 ] )
rom . write_bytes ( 0xF740A , [ 0x0C , 0x00 ] )
rom . write_byte ( 0xF73FE , 0x3C )
rom . write_byte ( 0xF7406 , 0x3C )
rom . write_byte ( 0xF740E , 0x3C )
rom . write_byte ( 0xF7241 , 0x27 )
rom . write_byte ( 0xF723F , 0x10 )
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
rom . write_int16_to_rom ( 0x180036 , world . rupoor_cost )
# Set stun items
rom . write_byte ( 0x180180 , 0x03 ) # All standard items
# Make silver arrows freely usable
rom . write_byte ( 0x180181 , 0x00 )
2017-11-11 21:25:56 +00:00
#Set overflow items for progressive equipment
if world . goal == ' triforcehunt ' :
2018-01-04 06:06:22 +00:00
overflow_replacement = TRIFORCE_PIECE
2017-11-11 21:25:56 +00:00
elif 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
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
#Set overflow items for progressive equipment
rom . write_bytes ( 0x180090 ,
[ difficulty . progressive_sword_limit , overflow_replacement ,
difficulty . progressive_shield_limit , overflow_replacement ,
difficulty . progressive_armor_limit , overflow_replacement ,
difficulty . progressive_bottle_limit , overflow_replacement ] )
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 ]
if world . retro :
prize_replacements = { 0xE1 : 0xDA , #5 Arrows -> Blue Rupee
0xE2 : 0xDB } #10 Arrows -> Red Rupee
prizes = [ prize_replacements . get ( prize , prize ) for prize in prizes ]
dig_prizes = [ prize_replacements . get ( prize , prize ) for prize in dig_prizes ]
rom . write_bytes ( 0x180100 , dig_prizes )
2017-05-25 10:09:50 +00:00
random . shuffle ( prizes )
# 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
# prize pack drop chances
2017-11-10 10:08:59 +00:00
if world . difficulty in [ ' hard ' , ' expert ' , ' insane ' ] :
2017-05-25 10:09:50 +00:00
droprates = [ 0x01 , 0x02 , 0x03 , 0x03 , 0x03 , 0x04 , 0x04 ] # 50%, 25%, 3* 12.5%, 2* 6.25%
2017-06-04 12:44:23 +00:00
else :
droprates = [ 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ] # 50%
2017-05-25 10:09:50 +00:00
random . shuffle ( droprates )
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x37A62 , droprates )
vanilla_prize_pack_assignment = [ 131 , 150 , 132 , 128 , 128 , 128 , 128 , 128 , 2 , 0 , 2 , 128 , 160 , 131 , 151 , 128 , 128 , 148 , 145 , 7 , 0 , 128 , 0 , 128 , 146 , 150 , 128 , 160 , 0 , 0 , 0 , 128 , 4 , 128 ,
130 , 6 , 6 , 0 , 0 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 0 , 0 , 128 , 128 , 144 , 128 , 145 , 145 ,
145 , 151 , 145 , 149 , 149 , 147 , 151 , 20 , 145 , 146 , 129 , 130 , 130 , 128 , 133 , 128 , 128 , 128 , 4 , 4 , 128 , 145 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 0 , 128 ,
128 , 130 , 138 , 128 , 128 , 128 , 128 , 146 , 145 , 128 , 130 , 129 , 129 , 128 , 129 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 128 , 151 , 128 , 128 , 128 , 128 , 194 ,
128 , 21 , 21 , 23 , 6 , 0 , 128 , 0 , 192 , 19 , 64 , 0 , 2 , 6 , 16 , 20 , 0 , 0 , 64 , 0 , 0 , 0 , 0 , 19 , 70 , 17 , 128 , 128 , 0 , 0 , 0 , 16 , 0 , 0 , 0 , 22 , 22 , 22 , 129 , 135 , 130 ,
0 , 128 , 128 , 0 , 0 , 0 , 0 , 128 , 128 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 128 , 0 , 0 , 0 , 23 , 0 , 18 , 0 , 0 , 0 , 0 , 0 , 16 , 23 , 0 , 64 , 1 , 0 , 0 , 0 , 0 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 64 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 128 , 0 , 0 , 0 , 0 , 0 , 0 ]
2017-05-25 10:09:50 +00:00
2017-05-25 13:58:35 +00:00
# shuffle enemies to prize packs
2017-05-25 10:09:50 +00:00
for i in range ( 243 ) :
2017-07-14 12:37:34 +00:00
if vanilla_prize_pack_assignment [ i ] & 0x0F != 0x00 :
rom . write_byte ( 0x6B632 + i , ( vanilla_prize_pack_assignment [ i ] & 0xF0 ) | random . randint ( 1 , 7 ) )
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
if world . difficulty in [ ' easy ' ] :
rom . write_bytes ( 0x184000 , [
# original_item, limit, replacement_item, filler
0x12 , 0x01 , 0x35 , 0xFF , # lamp -> 5 rupees
0x58 , 0x01 , 0x43 , 0xFF , # silver arrows -> 1 arrow
0xFF , 0xFF , 0xFF , 0xFF , # end of table sentinel
] )
else :
rom . write_bytes ( 0x184000 , [
# original_item, limit, replacement_item, filler
0x12 , 0x01 , 0x35 , 0xFF , # lamp -> 5 rupees
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
2017-11-11 21:25:56 +00:00
if world . difficulty in [ ' hard ' , ' expert ' , ' insane ' ] :
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
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
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x18003F , 0x01 if world . mode == ' swordless ' else 0x00 ) # hammer can harm ganon
rom . write_byte ( 0x180040 , 0x01 if world . mode == ' swordless ' else 0x00 ) # open curtains
rom . write_byte ( 0x180041 , 0x01 if world . mode == ' swordless ' else 0x00 ) # swordless medallions
2017-07-17 01:06:21 +00:00
rom . write_byte ( 0x180043 , 0xFF if world . mode == ' swordless ' else 0x00 ) # starting sword for link
2017-10-14 18:45:59 +00:00
rom . write_byte ( 0x180044 , 0x01 if world . mode == ' 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
2017-12-14 21:04:28 +00:00
if world . keysanity :
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
2017-11-19 01:36:42 +00:00
rom . write_int32_to_rom ( 0x180200 , 0 ) # red clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180204 , 0 ) # blue clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180208 , 0 ) # green clock adjustment time (in frames, sint32)
rom . write_int32_to_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
2017-11-19 01:36:42 +00:00
rom . write_int32_to_rom ( 0x180200 , 0 ) # red clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180204 , 0 ) # blue clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180208 , 0 ) # green clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x18020C , 0 ) # starting time (in frames, sint32)
elif world . clock_mode == ' countdown-ohko ' :
rom . write_bytes ( 0x180190 , [ 0x01 , 0x02 , 0x01 ] ) # ohko timer with resetable timer functionality
rom . write_int32_to_rom ( 0x180200 , - 100 * 60 * 60 * 60 ) # red clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180204 , 2 * 60 * 60 ) # blue clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180208 , 4 * 60 * 60 ) # green clock adjustment time (in frames, sint32)
2017-11-12 02:19:29 +00:00
if world . difficulty == ' easy ' :
2017-12-14 21:04:28 +00:00
rom . write_int32_to_rom ( 0x18020C , ( 20 + ERtimeincrease ) * 60 * 60 ) # starting time (in frames, sint32)
2017-11-12 02:19:29 +00:00
elif world . difficulty == ' normal ' :
2017-12-14 21:04:28 +00:00
rom . write_int32_to_rom ( 0x18020C , ( 10 + ERtimeincrease ) * 60 * 60 ) # starting time (in frames, sint32)
2017-11-12 02:19:29 +00:00
else :
2017-12-14 21:04:28 +00:00
rom . write_int32_to_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
2017-11-19 01:36:42 +00:00
rom . write_int32_to_rom ( 0x180200 , - 2 * 60 * 60 ) # red clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180204 , 2 * 60 * 60 ) # blue clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180208 , 4 * 60 * 60 ) # green clock adjustment time (in frames, sint32)
rom . write_int32_to_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
2017-11-19 01:36:42 +00:00
rom . write_int32_to_rom ( 0x180200 , - 2 * 60 * 60 ) # red clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180204 , 2 * 60 * 60 ) # blue clock adjustment time (in frames, sint32)
rom . write_int32_to_rom ( 0x180208 , 4 * 60 * 60 ) # green clock adjustment time (in frames, sint32)
2017-12-14 21:04:28 +00:00
rom . write_int32_to_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 )
2017-06-04 11:10:22 +00:00
2017-12-08 21:53:53 +00:00
# TODO: a proper race rom mode should be implemented, that changes the following flag, and rummages the table (or uses the future encryption feature, etc)
rom . write_bytes ( 0x180213 , [ 0x00 , 0x01 ] ) # Not a Tournament Seed
2018-03-02 01:31:35 +00:00
rom . write_byte ( 0x180211 , 0x06 ) #Game type, we set the Entrance and item randomization flags
2017-06-03 12:20:39 +00:00
# assorted fixes
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180030 , 0x00 ) # Disable SRAM trace
2017-12-08 21:53:53 +00:00
rom . write_byte ( 0x1800A2 , 0x01 ) # remain in real dark world when dying in dark word dungion 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.
2017-11-13 22:12:28 +00:00
rom . write_byte ( 0x180171 , 0x01 if world . ganon_at_pyramid 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
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
2018-03-02 01:31:35 +00:00
rom . write_byte ( 0x18004A , 0x00 ) # Inverted mode (off)
rom . write_byte ( 0x2AF79 , 0xD0 ) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both)
rom . write_byte ( 0x3A943 , 0xD0 ) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both)
rom . write_byte ( 0x3A96D , 0xF0 ) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader))
rom . write_byte ( 0x3A9A7 , 0xD0 ) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader))
rom . write_byte ( 0x18004D , 0x00 ) # Escape assist (off)
rom . write_byte ( 0x18004E , 0x00 ) # uncle Refill (off)
2018-01-30 01:46:16 +00:00
2017-12-08 21:53:53 +00:00
if world . goal in [ ' pedestal ' , ' triforcehunt ' ] :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x18003E , 0x01 ) # make ganon invincible
2017-06-23 19:33:04 +00:00
elif world . goal in [ ' dungeons ' ] :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x18003E , 0x02 ) # make ganon invincible until all dungeons are beat
2017-08-01 17:07:44 +00:00
elif world . goal in [ ' crystals ' ] :
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
2017-10-28 22:34:37 +00:00
rom . write_byte ( 0x18016A , 0x01 if world . keysanity else 0x00 ) # free roaming item text boxes
rom . write_byte ( 0x18003B , 0x01 if world . keysanity 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
2017-11-10 10:50:50 +00:00
elif world . difficulty == ' easy ' :
rom . write_byte ( 0x18003C , 0x02 ) # always on
2017-10-28 22:34:37 +00:00
elif world . keysanity :
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
2017-11-12 10:38:41 +00:00
rom . write_byte ( 0x180045 , 0xFF if world . keysanity else 0x00 ) # free roaming items in menu
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
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
2017-12-08 21:53:53 +00:00
# patch swamp: Need to enable permanent drain of water as dam or swamp were moved
rom . write_byte ( 0x18003D , 0x01 if world . swamp_patch_required else 0x00 )
2017-05-25 10:09:50 +00:00
2018-02-12 04:25:19 +00:00
# powder patch: remove the need to leave the scrren after powder, since it causes problems for potion shop at race game
# temporarally we are just nopping out this check we will conver this to a rom fix soon.
2018-02-17 23:38:54 +00:00
rom . write_bytes ( 0x02F539 , [ 0xEA , 0xEA , 0xEA , 0xEA , 0xEA ] if world . powder_patch_required 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
2017-10-29 03:42:35 +00:00
if world . get_location ( ' Tower of Hera - Basement Cage ' ) . item is not None and world . get_location ( ' Tower of Hera - Basement Cage ' ) . item . name == ' Small Key (Tower of Hera) ' :
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
2017-06-17 11:16:13 +00:00
if world . fix_trock_doors :
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
2017-12-14 04:21:43 +00:00
write_strings ( rom , world )
# set rom name
# 21 bytes
2018-03-03 18:29:52 +00:00
rom . write_bytes ( 0x7FC0 , bytearray ( ' ER_060_ %09d \0 ' % world . seed , ' utf8 ' ) + world . option_identifier . to_bytes ( 4 , ' big ' ) )
2017-12-14 04:21:43 +00:00
# store hash table for main menu hash
rom . write_bytes ( 0x187F00 , hashtable )
2018-02-28 02:26:33 +00:00
apply_rom_settings ( rom , beep , color , world . quickswap , world . fastmenu , world . disable_music , sprite )
2017-12-14 04:21:43 +00:00
return rom
2018-02-17 23:38:54 +00:00
def write_custom_shops ( rom , world ) :
shops = [ shop for shop in world . shops if shop . replaceable and shop . active ]
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
item_data = [ shop_id , ItemFactory ( item [ ' item ' ] ) . code ] + int16_as_bytes ( item [ ' price ' ] ) + [ item [ ' max ' ] , ItemFactory ( item [ ' replacement ' ] ) . code if item [ ' replacement ' ] else 0xFF ] + int16_as_bytes ( item [ ' replacement_price ' ] )
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 )
2018-02-28 02:26:33 +00:00
def apply_rom_settings ( rom , beep , color , quickswap , fastmenu , disable_music , sprite ) :
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
music_volumes = [
2018-01-27 20:01:16 +00:00
( 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 ] )
2017-12-14 04:21:43 +00:00
]
for volume , addresses in music_volumes :
for address in addresses :
rom . write_byte ( address , volume if not 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
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x180033 , { ' off ' : 0x00 , ' half ' : 0x40 , ' quarter ' : 0x80 , ' normal ' : 0x20 } [ beep ] )
2017-05-25 15:47:15 +00:00
2018-02-28 02:26:33 +00:00
# set heart color
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
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
def write_strings ( rom , world ) :
2017-05-25 15:47:15 +00:00
silverarrows = world . find_items ( ' Silver Arrows ' )
2017-06-03 12:20:39 +00:00
silverarrow_hint = ( ' %s ? ' % silverarrows [ 0 ] . hint_text ) if silverarrows else ' ? \n I think not! '
write_string_to_rom ( rom , ' Ganon2 ' , ' Did you find the silver arrows %s ' % silverarrow_hint )
2017-05-25 15:47:15 +00:00
crystal5 = world . find_items ( ' Crystal 5 ' ) [ 0 ]
crystal6 = world . find_items ( ' Crystal 6 ' ) [ 0 ]
write_string_to_rom ( rom , ' BombShop1 ' , ' Big Bomb? \n My supply is blocked until you clear %s and %s . ' % ( crystal5 . hint_text , crystal6 . hint_text ) )
greenpendant = world . find_items ( ' Green Pendant ' ) [ 0 ]
write_string_to_rom ( rom , ' Sahasrahla1 ' , ' I lost my family heirloom in %s ' % greenpendant . hint_text )
2017-05-25 13:58:35 +00:00
write_string_to_rom ( rom , ' Uncle ' , Uncle_texts [ random . randint ( 0 , len ( Uncle_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' Triforce ' , Triforce_texts [ random . randint ( 0 , len ( Triforce_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' BombShop2 ' , BombShop2_texts [ random . randint ( 0 , len ( BombShop2_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' PyramidFairy ' , PyramidFairy_texts [ random . randint ( 0 , len ( PyramidFairy_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' Sahasrahla2 ' , Sahasrahla2_texts [ random . randint ( 0 , len ( Sahasrahla2_texts ) - 1 ) ] )
write_string_to_rom ( rom , ' Blind ' , Blind_texts [ random . randint ( 0 , len ( Blind_texts ) - 1 ) ] )
2017-08-01 17:07:44 +00:00
if world . goal in [ ' pedestal ' , ' triforcehunt ' ] :
2017-07-17 16:04:24 +00:00
write_string_to_rom ( rom , ' Ganon1Invincible ' , ' Why are you even here? \n You can \' t even hurt me! ' )
write_string_to_rom ( rom , ' Ganon2Invincible ' , ' Seriously? Go Away, I will not Die. ' )
2017-06-03 19:27:34 +00:00
else :
write_string_to_rom ( rom , ' Ganon1 ' , Ganon1_texts [ random . randint ( 0 , len ( Ganon1_texts ) - 1 ) ] )
2017-07-17 16:04:24 +00:00
write_string_to_rom ( rom , ' Ganon1Invincible ' , ' You cannot defeat me until you finish your goal! ' )
write_string_to_rom ( rom , ' Ganon2Invincible ' , ' Got wax in \n your ears? \n I can not die! ' )
2017-05-25 13:58:35 +00:00
write_string_to_rom ( rom , ' TavernMan ' , TavernMan_texts [ random . randint ( 0 , len ( TavernMan_texts ) - 1 ) ] )
2017-10-29 03:42:35 +00:00
pedestalitem = world . get_location ( ' Master Sword Pedestal ' ) . item
pedestal_text = ' Some Hot Air ' if pedestalitem is None else pedestalitem . pedestal_hint_text if pedestalitem . pedestal_hint_text is not None else ' Unknown Item '
write_string_to_rom ( rom , ' Pedestal ' , pedestal_text )
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
2017-07-14 21:32:40 +00:00
etheritem = world . get_location ( ' Ether Tablet ' ) . item
2017-10-29 03:42:35 +00:00
ether_text = ' Some Hot Air ' if etheritem is None else etheritem . pedestal_hint_text if etheritem . pedestal_hint_text is not None else ' Unknown Item '
2017-07-14 21:32:40 +00:00
write_string_to_rom ( rom , ' EtherTablet ' , ether_text )
bombositem = world . get_location ( ' Bombos Tablet ' ) . item
2017-10-29 03:42:35 +00:00
bombos_text = ' Some Hot Air ' if bombositem is None else bombositem . pedestal_hint_text if bombositem . pedestal_hint_text is not None else ' Unknown Item '
2017-07-14 21:32:40 +00:00
write_string_to_rom ( rom , ' BombosTablet ' , bombos_text )
2017-11-05 04:06:00 +00:00
credits = Credits ( )
2017-05-25 13:58:35 +00:00
sickkiditem = world . get_location ( ' Sick Kid ' ) . 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
2017-05-25 13:58:35 +00:00
zoraitem = world . get_location ( ' King Zora ' ) . 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
2017-10-29 03:42:35 +00:00
magicshopitem = world . get_location ( ' Potion Shop ' ) . 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
2017-11-14 10:25:12 +00:00
fluteboyitem = world . get_location ( ' Flute Spot ' ) . 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 ) )
credits . update_credits_line ( ' sancturary ' , 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 ] ] )