2020-10-24 03:33:52 +00:00
from __future__ import annotations
2020-07-10 20:43:54 +00:00
JAP10HASH = ' 03a63945398191337e896e5771f77173 '
2020-11-24 15:54:08 +00:00
RANDOMIZERBASEHASH = ' 5a607e36a82bbd14180536c8ec3ae49b '
2020-07-10 20:43:54 +00:00
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
2020-08-12 06:48:29 +00:00
import threading
2020-10-20 08:16:20 +00:00
import xxtea
2020-08-21 16:35:48 +00:00
import concurrent . futures
2020-09-15 03:31:17 +00:00
from typing import Optional
2017-12-17 05:25:46 +00:00
2020-01-06 18:13:42 +00:00
from BaseClasses import CollectionState , ShopType , Region , Location
2017-12-17 05:25:46 +00:00
from Dungeons import dungeon_music_addresses
2020-10-27 08:36:55 +00:00
from Regions import location_table , old_location_address_to_new_location_address
2019-07-27 13:13:13 +00:00
from Text import MultiByteTextMapper , CompressedTextMapper , text_addresses , Credits , TextTable
2020-10-24 03:33:52 +00:00
from Text import Uncle_texts , Ganon1_texts , TavernMan_texts , Sahasrahla2_texts , Triforce_texts , Blind_texts , \
BombShop2_texts , junk_texts
2020-02-08 23:17:34 +00:00
2020-08-25 11:22:47 +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
from Utils import output_path , local_path , int16_as_bytes , int32_as_bytes , snes_to_pc , is_bundled
2020-01-06 18:13:42 +00:00
from Items import ItemFactory
2019-07-27 13:13:13 +00:00
from EntranceShuffle import door_addresses
2020-07-10 20:43:54 +00:00
import Patch
2017-12-17 05:25:46 +00:00
2020-08-23 10:06:00 +00:00
try :
from maseya import z3pr
2020-09-13 15:09:28 +00:00
from maseya . z3pr . palette_randomizer import build_offset_collections
2020-08-23 10:06:00 +00:00
except :
z3pr = None
2017-05-25 10:09:50 +00:00
2020-10-24 03:33:52 +00:00
2017-07-14 12:37:34 +00:00
class LocalRom ( object ) :
2020-10-24 00:44:27 +00:00
def __init__ ( self , file , patch = True , vanillaRom = None , name = None , hash = None ) :
2020-01-14 21:13:37 +00:00
self . name = name
self . hash = hash
2020-01-08 02:43:48 +00:00
self . orig_buffer = None
2020-10-24 03:33:52 +00:00
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 :
2020-06-07 03:49:53 +00:00
self . patch_base_rom ( )
2020-01-08 02:43:48 +00:00
self . orig_buffer = self . buffer . copy ( )
2020-10-24 00:44:27 +00:00
if vanillaRom :
with open ( vanillaRom , ' rb ' ) as vanillaStream :
self . orig_buffer = read_rom ( vanillaStream )
2017-07-14 12:37:34 +00:00
2020-08-27 01:16:38 +00:00
def read_byte ( self , address : int ) - > int :
return self . buffer [ address ]
def read_bytes ( self , startaddress : int , length : int ) - > bytes :
return self . buffer [ startaddress : startaddress + length ]
2020-08-21 16:35:48 +00:00
def write_byte ( self , address : int , value : int ) :
2017-07-14 12:37:34 +00:00
self . buffer [ address ] = value
2020-04-16 08:38:18 +00:00
def write_bytes ( self , startaddress : int , values ) :
self . buffer [ startaddress : startaddress + len ( values ) ] = values
2017-07-14 12:37:34 +00:00
2020-10-20 08:16:20 +00:00
def encrypt_range ( self , startaddress : int , length : int , key : bytes ) :
for i in range ( 0 , length , 8 ) :
data = bytes ( self . read_bytes ( startaddress + i , 8 ) )
data = xxtea . encrypt ( data , key , padding = False )
self . write_bytes ( startaddress + i , bytearray ( data ) )
def encrypt ( self , world , player ) :
local_random = world . rom_seeds [ player ]
key = bytes ( local_random . getrandbits ( 8 * 16 ) . to_bytes ( 16 , ' big ' ) )
self . write_bytes ( 0x1800B0 , bytearray ( key ) )
self . write_int16 ( 0x180087 , 1 )
itemtable = [ ]
locationtable = [ ]
2020-10-21 09:02:13 +00:00
itemplayertable = [ ]
2020-10-20 08:16:20 +00:00
for i in range ( 168 ) :
itemtable . append ( self . read_byte ( 0xE96E + ( i * 3 ) ) )
2020-10-21 09:02:13 +00:00
itemplayertable . append ( self . read_byte ( 0x186142 + ( i * 3 ) ) )
2020-10-20 08:16:20 +00:00
locationtable . append ( self . read_byte ( 0xe96C + ( i * 3 ) ) )
locationtable . append ( self . read_byte ( 0xe96D + ( i * 3 ) ) )
self . write_bytes ( 0xE96C , locationtable )
self . write_bytes ( 0xE96C + 0x150 , itemtable )
self . encrypt_range ( 0xE96C + 0x150 , 168 , key )
2020-10-21 09:02:13 +00:00
self . write_bytes ( 0x186140 , [ 0 ] * 0x150 )
self . write_bytes ( 0x186140 + 0x150 , itemplayertable )
self . encrypt_range ( 0x186140 + 0x150 , 168 , key )
2020-10-27 08:36:55 +00:00
self . encrypt_range ( 0x186338 , 56 , key )
2020-10-20 08:16:20 +00:00
self . encrypt_range ( 0x180000 , 32 , key )
self . encrypt_range ( 0x180140 , 32 , key )
2020-10-27 08:36:55 +00:00
self . encrypt_range ( 0xEDA1 , 8 , key )
2020-10-20 08:16:20 +00:00
2020-08-16 09:13:50 +00:00
def write_to_file ( self , file , hide_enemizer = False ) :
2017-07-14 12:37:34 +00:00
with open ( file , ' wb ' ) as outfile :
outfile . write ( self . buffer )
2020-06-11 02:56:47 +00:00
def read_from_file ( self , file ) :
with open ( file , ' rb ' ) as stream :
self . buffer = bytearray ( stream . read ( ) )
2020-01-14 21:13:37 +00:00
@staticmethod
2020-08-21 16:35:48 +00:00
def verify ( buffer , expected : str = RANDOMIZERBASEHASH ) - > bool :
2020-06-09 20:11:14 +00:00
buffermd5 = hashlib . md5 ( )
buffermd5 . update ( buffer )
2020-06-09 20:12:46 +00:00
return expected == buffermd5 . hexdigest ( )
2020-06-09 20:11:14 +00:00
2020-06-07 03:49:53 +00:00
def patch_base_rom ( self ) :
2020-06-17 06:59:50 +00:00
if os . path . isfile ( local_path ( ' basepatch.sfc ' ) ) :
with open ( local_path ( ' basepatch.sfc ' ) , ' rb ' ) as stream :
2020-06-09 19:18:48 +00:00
buffer = bytearray ( stream . read ( ) )
2020-06-09 20:11:14 +00:00
if self . verify ( buffer ) :
2020-06-09 19:18:48 +00:00
self . buffer = buffer
2020-08-25 11:22:47 +00:00
if not os . path . exists ( local_path ( ' data ' , ' basepatch.bmbp ' ) ) :
2020-07-10 20:43:54 +00:00
Patch . create_patch_file ( local_path ( ' basepatch.sfc ' ) )
2020-06-09 19:18:48 +00:00
return
2020-06-18 23:31:32 +00:00
2020-08-25 11:22:47 +00:00
if os . path . isfile ( local_path ( ' data ' , ' basepatch.bmbp ' ) ) :
_ , target , buffer = Patch . create_rom_bytes ( local_path ( ' data ' , ' basepatch.bmbp ' ) )
2020-06-09 20:11:14 +00:00
if self . verify ( buffer ) :
2020-06-09 19:18:48 +00:00
self . buffer = bytearray ( buffer )
2020-06-17 06:59:50 +00:00
with open ( local_path ( ' basepatch.sfc ' ) , ' wb ' ) as stream :
2020-06-09 19:18:48 +00:00
stream . write ( buffer )
return
2020-10-05 17:49:32 +00:00
raise RuntimeError ( ' Could not find Base Patch. Unable to continue. ' )
2019-05-29 23:10:16 +00:00
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 ] )
2020-08-21 16:35:48 +00:00
def get_hash ( self ) - > str :
2018-09-23 02:51:54 +00:00
h = hashlib . md5 ( )
h . update ( self . buffer )
return h . hexdigest ( )
2020-08-21 16:35:48 +00:00
def write_int16 ( self , address : int , value : int ) :
self . write_bytes ( address , int16_as_bytes ( value ) )
2019-12-09 18:27:56 +00:00
2020-08-21 16:35:48 +00:00
def write_int32 ( self , address : int , value : int ) :
self . write_bytes ( address , int32_as_bytes ( value ) )
2019-12-09 18:27:56 +00:00
2020-08-21 16:35:48 +00:00
def write_int16s ( self , startaddress : int , values ) :
for i , value in enumerate ( values ) :
self . write_int16 ( startaddress + ( i * 2 ) , value )
2019-12-09 18:27:56 +00:00
2020-08-21 16:35:48 +00:00
def write_int32s ( self , startaddress : int , values ) :
for i , value in enumerate ( values ) :
self . write_int32 ( startaddress + ( i * 4 ) , value )
2019-12-09 18:27:56 +00:00
2020-04-16 08:38:18 +00:00
def read_rom ( stream ) - > bytearray :
2018-03-01 23:59:37 +00:00
" Reads rom into bytearray and strips off any smc header "
buffer = bytearray ( stream . read ( ) )
2020-04-16 08:38:18 +00:00
if len ( buffer ) % 0x400 == 0x200 :
2018-03-01 23:59:37 +00:00
buffer = buffer [ 0x200 : ]
return buffer
2020-07-10 22:52:49 +00:00
2020-08-12 06:48:29 +00:00
check_lock = threading . Lock ( )
2020-07-10 22:52:49 +00:00
def check_enemizer ( enemizercli ) :
2020-08-21 16:35:48 +00:00
if getattr ( check_enemizer , " done " , None ) :
return
if not os . path . exists ( enemizercli ) and not os . path . exists ( enemizercli + " .exe " ) :
raise Exception ( f " Enemizer not found at { enemizercli } , please install it. "
f " Such as https://github.com/Ijwu/Enemizer/releases " )
2020-09-03 01:54:12 +00:00
with check_lock :
# some time may have passed since the lock was acquired, as such a quick re-check doesn't hurt
if getattr ( check_enemizer , " done " , None ) :
return
# version info is saved on the lib, for some reason
library_info = os . path . join ( os . path . dirname ( enemizercli ) , " EnemizerCLI.Core.deps.json " )
with open ( library_info ) as f :
info = json . load ( f )
for lib in info [ " libraries " ] :
if lib . startswith ( " EnemizerLibrary/ " ) :
version = lib . split ( " / " ) [ - 1 ]
version = tuple ( int ( element ) for element in version . split ( " . " ) )
2020-09-04 16:54:56 +00:00
logging . debug ( f " Found Enemizer version { version } " )
2020-12-29 19:01:27 +00:00
if version < ( 6 , 4 , 0 ) :
2020-09-03 01:54:12 +00:00
raise Exception (
f " Enemizer found at { enemizercli } is outdated ( { info } ), please update your Enemizer. "
f " Such as https://github.com/Ijwu/Enemizer/releases " )
break
else :
raise Exception ( f " Could not find Enemizer library version information in { library_info } " )
2020-08-21 16:35:48 +00:00
2020-07-10 22:52:49 +00:00
check_enemizer . done = True
2020-10-06 20:22:03 +00:00
def apply_random_sprite_on_event ( rom : LocalRom , sprite , local_random , allow_random_on_event , sprite_pool ) :
2020-10-13 08:52:37 +00:00
userandomsprites = False
2020-10-04 17:57:30 +00:00
if sprite and not isinstance ( sprite , Sprite ) :
sprite = sprite . lower ( )
2020-10-13 08:52:37 +00:00
userandomsprites = sprite . startswith ( ' randomon ' )
racerom = rom . read_byte ( 0x180213 )
if allow_random_on_event or not racerom :
# Changes to this byte for race rom seeds are only permitted on initial rolling of the seed.
# However, if the seed is not a racerom seed, then it is always allowed.
rom . write_byte ( 0x186381 , 0x00 if userandomsprites else 0x01 )
onevent = 0
2020-10-06 02:57:36 +00:00
if sprite == ' randomonall ' :
onevent = 0xFFFF # Support all current and future events that can cause random sprite changes.
2020-10-13 08:52:37 +00:00
elif sprite == ' randomonnone ' :
# Allows for opting into random on events on race rom seeds, without actually enabling any of the events initially.
onevent = 0x0000
elif userandomsprites :
2020-10-07 17:34:19 +00:00
onevent = 0x01 if ' hit ' in sprite else 0x00
2020-10-04 17:57:30 +00:00
onevent + = 0x02 if ' enter ' in sprite else 0x00
onevent + = 0x04 if ' exit ' in sprite else 0x00
onevent + = 0x08 if ' slash ' in sprite else 0x00
onevent + = 0x10 if ' item ' in sprite else 0x00
2020-10-07 23:13:42 +00:00
onevent + = 0x20 if ' bonk ' in sprite else 0x00
2020-10-13 08:52:37 +00:00
rom . write_int16 ( 0x18637F , onevent )
2020-10-24 03:33:52 +00:00
sprite = Sprite ( sprite ) if os . path . isfile ( sprite ) else Sprite . get_sprite_from_name ( sprite , local_random )
2020-10-04 17:57:30 +00:00
# write link sprite if required
if sprite :
2020-10-13 08:52:37 +00:00
sprites = list ( )
2020-10-04 17:57:30 +00:00
sprite . write_to_rom ( rom )
_populate_sprite_table ( )
2020-10-13 08:52:37 +00:00
if userandomsprites :
2020-10-06 20:22:03 +00:00
if sprite_pool :
if isinstance ( sprite_pool , str ) :
sprite_pool = sprite_pool . split ( ' : ' )
2020-10-16 21:20:45 +00:00
for spritename in sprite_pool :
2020-10-24 03:33:52 +00:00
sprite = Sprite ( spritename ) if os . path . isfile ( spritename ) else Sprite . get_sprite_from_name (
spritename , local_random )
2020-10-16 21:20:45 +00:00
if sprite :
sprites . append ( sprite )
else :
logging . info ( f " Sprite { spritename } was not found. " )
2020-10-06 20:22:03 +00:00
else :
sprites = list ( set ( _sprite_table . values ( ) ) ) # convert to list and remove dupes
2020-10-04 17:57:30 +00:00
else :
sprites . append ( sprite )
if sprites :
while len ( sprites ) < 32 :
sprites . extend ( sprites )
local_random . shuffle ( sprites )
for i , sprite in enumerate ( sprites [ : 32 ] ) :
2020-10-13 08:52:37 +00:00
if not i and not userandomsprites :
2020-10-04 17:57:30 +00:00
continue
rom . write_bytes ( 0x300000 + ( i * 0x8000 ) , sprite . sprite )
rom . write_bytes ( 0x307000 + ( i * 0x8000 ) , sprite . palette )
rom . write_bytes ( 0x307078 + ( i * 0x8000 ) , sprite . glove_palette )
def patch_enemizer ( world , player : int , rom : LocalRom , enemizercli ) :
2020-07-10 22:52:49 +00:00
check_enemizer ( enemizercli )
2020-06-11 02:56:47 +00:00
randopatch_path = os . path . abspath ( output_path ( f ' enemizer_randopatch_ { player } .sfc ' ) )
2020-03-06 22:08:46 +00:00
options_path = os . path . abspath ( output_path ( f ' enemizer_options_ { player } .json ' ) )
2020-06-11 02:56:47 +00:00
enemizer_output_path = os . path . abspath ( output_path ( f ' enemizer_output_ { player } .sfc ' ) )
2019-05-29 23:10:16 +00:00
# write options file for enemizer
options = {
2020-08-19 21:24:17 +00:00
' RandomizeEnemies ' : world . enemy_shuffle [ player ] ,
2019-05-29 23:10:16 +00:00
' RandomizeEnemiesType ' : 3 ,
2020-08-19 21:24:17 +00:00
' RandomizeBushEnemyChance ' : world . bush_shuffle [ player ] ,
2019-12-17 14:55:53 +00:00
' RandomizeEnemyHealthRange ' : world . enemy_health [ player ] != ' default ' ,
2020-03-06 22:08:46 +00:00
' RandomizeEnemyHealthType ' : { ' default ' : 0 , ' easy ' : 0 , ' normal ' : 1 , ' hard ' : 2 , ' expert ' : 3 } [
world . enemy_health [ player ] ] ,
2019-05-29 23:10:16 +00:00
' OHKO ' : False ,
2019-12-17 14:55:53 +00:00
' RandomizeEnemyDamage ' : world . enemy_damage [ player ] != ' default ' ,
2019-05-29 23:10:16 +00:00
' AllowEnemyZeroDamage ' : True ,
2019-12-17 14:55:53 +00:00
' ShuffleEnemyDamageGroups ' : world . enemy_damage [ player ] != ' default ' ,
' EnemyDamageChaosMode ' : world . enemy_damage [ player ] == ' chaos ' ,
2020-07-05 00:06:00 +00:00
' EasyModeEscape ' : world . mode [ player ] == " standard " ,
2019-05-29 23:10:16 +00:00
' EnemiesAbsorbable ' : False ,
' AbsorbableSpawnRate ' : 10 ,
' AbsorbableTypes ' : {
2020-07-05 00:06:00 +00:00
' 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
2019-05-29 23:10:16 +00:00
} ,
' BossMadness ' : False ,
' RandomizeBosses ' : True ,
' RandomizeBossesType ' : 0 ,
' RandomizeBossHealth ' : False ,
' RandomizeBossHealthMinAmount ' : 0 ,
' RandomizeBossHealthMaxAmount ' : 300 ,
' RandomizeBossDamage ' : False ,
' RandomizeBossDamageMinAmount ' : 0 ,
' RandomizeBossDamageMaxAmount ' : 200 ,
' RandomizeBossBehavior ' : False ,
2020-01-08 02:43:48 +00:00
' RandomizeDungeonPalettes ' : False ,
2019-05-29 23:10:16 +00:00
' SetBlackoutMode ' : False ,
2020-01-08 02:43:48 +00:00
' RandomizeOverworldPalettes ' : False ,
' RandomizeSpritePalettes ' : False ,
2019-05-29 23:10:16 +00:00
' SetAdvancedSpritePalettes ' : False ,
' PukeMode ' : False ,
' NegativeMode ' : False ,
' GrayscaleMode ' : False ,
' GenerateSpoilers ' : False ,
' RandomizeLinkSpritePalette ' : False ,
2020-07-05 00:06:00 +00:00
' RandomizePots ' : world . shufflepots [ player ] ,
2019-05-29 23:10:16 +00:00
' 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 ,
2020-12-22 09:05:48 +00:00
' RandomizeTileTrapPattern ' : False ,
2019-05-29 23:10:16 +00:00
' RandomizeTileTrapFloorTile ' : False ,
2020-08-19 21:24:17 +00:00
' AllowKillableThief ' : world . killable_thieves [ player ] ,
2020-10-04 17:57:30 +00:00
' RandomizeSpriteOnHit ' : False ,
2019-05-29 23:10:16 +00:00
' 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 ,
2020-10-24 03:33:52 +00:00
' GanonsTower1 ' :
world . get_dungeon ( ' Ganons Tower ' if world . mode [ player ] != ' inverted ' else ' Inverted Ganons Tower ' ,
player ) . bosses [ ' bottom ' ] . enemizer_name ,
' GanonsTower2 ' :
world . get_dungeon ( ' Ganons Tower ' if world . mode [ player ] != ' inverted ' else ' Inverted Ganons Tower ' ,
player ) . bosses [ ' middle ' ] . enemizer_name ,
' GanonsTower3 ' :
world . get_dungeon ( ' Ganons Tower ' if world . mode [ player ] != ' inverted ' else ' Inverted Ganons Tower ' ,
player ) . bosses [ ' top ' ] . enemizer_name ,
2019-05-29 23:10:16 +00:00
' GanonsTower4 ' : ' Agahnim2 ' ,
' Ganon ' : ' Ganon ' ,
}
}
rom . write_to_file ( randopatch_path )
with open ( options_path , ' w ' ) as f :
json . dump ( options , f )
2020-07-29 19:56:43 +00:00
max_enemizer_tries = 5
for i in range ( max_enemizer_tries ) :
enemizer_seed = str ( world . rom_seeds [ player ] . randint ( 0 , 999999999 ) )
enemizer_command = [ os . path . abspath ( enemizercli ) ,
' --rom ' , randopatch_path ,
' --seed ' , enemizer_seed ,
' --binary ' ,
' --enemizer ' , options_path ,
' --output ' , enemizer_output_path ]
p_open = subprocess . Popen ( enemizer_command ,
cwd = os . path . dirname ( enemizercli ) ,
stdout = subprocess . PIPE ,
stderr = subprocess . STDOUT ,
universal_newlines = True )
2020-10-24 03:33:52 +00:00
logging . debug (
f " Enemizer attempt { i + 1 } of { max_enemizer_tries } for player { player } using enemizer seed { enemizer_seed } " )
2020-07-29 19:56:43 +00:00
for stdout_line in iter ( p_open . stdout . readline , " " ) :
2020-07-30 18:17:52 +00:00
logging . debug ( stdout_line . rstrip ( ) )
2020-07-29 19:56:43 +00:00
p_open . stdout . close ( )
return_code = p_open . wait ( )
if return_code :
2020-10-24 03:33:52 +00:00
if i == max_enemizer_tries - 1 :
2020-07-29 19:56:43 +00:00
raise subprocess . CalledProcessError ( return_code , enemizer_command )
continue
2020-10-24 03:33:52 +00:00
for j in range ( i + 1 , max_enemizer_tries ) :
2020-07-29 19:56:43 +00:00
world . rom_seeds [ player ] . randint ( 0 , 999999999 )
# Sacrifice all remaining random numbers that would have been used for unused enemizer tries.
# This allows for future enemizer bug fixes to NOT affect the rest of the seed's randomness
break
2020-06-11 02:56:47 +00:00
rom . read_from_file ( enemizer_output_path )
os . remove ( enemizer_output_path )
2019-05-29 23:10:16 +00:00
2020-10-07 17:34:19 +00:00
if world . get_dungeon ( " Thieves Town " , player ) . boss . enemizer_name == " Blind " :
2020-08-27 09:06:26 +00:00
rom . write_byte ( 0x04DE81 , 6 )
2020-10-07 17:34:19 +00:00
rom . write_byte ( 0x1B0101 , 0 ) # Do not close boss room door on entry.
2020-08-27 01:16:38 +00:00
2020-06-11 02:56:47 +00:00
for used in ( randopatch_path , options_path ) :
2020-02-25 13:47:52 +00:00
try :
os . remove ( used )
except OSError :
pass
2020-12-22 09:05:48 +00:00
tile_list_lock = threading . Lock ( )
_tile_collection_table = [ ]
def _populate_tile_sets ( ) :
with tile_list_lock :
if not _tile_collection_table :
def load_tileset_from_file ( file ) :
tileset = TileSet ( file )
_tile_collection_table . append ( tileset )
with concurrent . futures . ThreadPoolExecutor ( ) as pool :
for dir in [ local_path ( ' data ' , ' tiles ' ) ] :
for file in os . listdir ( dir ) :
pool . submit ( load_tileset_from_file , os . path . join ( dir , file ) )
class TileSet :
def __init__ ( self , filename ) :
with open ( filename , ' rt ' , encoding = ' utf-8-sig ' ) as file :
jsondata = json . load ( file )
self . speed = jsondata [ ' Speed ' ]
self . tiles = jsondata [ ' Items ' ]
self . name = os . path . basename ( os . path . splitext ( filename ) [ 0 ] )
def __hash__ ( self ) :
return hash ( self . name )
def get_bytes ( self ) :
data = [ ]
for tile in self . tiles :
data . append ( ( tile [ ' x ' ] + 3 ) * 16 )
while len ( data ) < 22 :
data . append ( 0 )
for tile in self . tiles :
data . append ( ( tile [ ' y ' ] + 4 ) * 16 )
return data
def get_speed ( self ) :
return self . speed
def get_len ( self ) :
return len ( self . tiles )
@staticmethod
def get_random_tile_set ( localrandom = random ) :
_populate_tile_sets ( )
tile_sets = list ( set ( _tile_collection_table ) )
tile_sets . sort ( key = lambda x : x . name )
return localrandom . choice ( tile_sets )
2020-10-24 03:33:52 +00:00
2020-08-22 00:56:33 +00:00
sprite_list_lock = threading . Lock ( )
2020-01-09 01:30:00 +00:00
_sprite_table = { }
2020-08-22 00:56:33 +00:00
2020-01-09 01:30:00 +00:00
def _populate_sprite_table ( ) :
2020-08-22 00:56:33 +00:00
with sprite_list_lock :
if not _sprite_table :
def load_sprite_from_file ( file ) :
sprite = Sprite ( file )
if sprite . valid :
_sprite_table [ sprite . name . lower ( ) ] = sprite
_sprite_table [ os . path . basename ( file ) . split ( " . " ) [ 0 ] . lower ( ) ] = sprite # alias for filename base
2020-08-22 17:19:29 +00:00
else :
logging . debug ( f " Spritefile { file } could not be loaded as a valid sprite. " )
2020-08-22 00:56:33 +00:00
with concurrent . futures . ThreadPoolExecutor ( ) as pool :
2020-08-25 11:22:47 +00:00
for dir in [ local_path ( ' data ' , ' sprites ' , ' alttpr ' ) , local_path ( ' data ' , ' sprites ' , ' custom ' ) ] :
2020-08-22 00:56:33 +00:00
for file in os . listdir ( dir ) :
pool . submit ( load_sprite_from_file , os . path . join ( dir , file ) )
2020-01-09 01:30:00 +00:00
2017-11-13 05:29:42 +00:00
class Sprite ( object ) :
2020-09-15 03:31:17 +00:00
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 )
glove_palette = ( 246 , 82 , 118 , 3 )
2020-10-24 03:33:52 +00:00
author_name : Optional [ str ] = None
2017-11-13 05:29:42 +00:00
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 )
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 ]
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 : ]
2020-10-04 17:57:30 +00:00
elif len ( filedata ) in [ 0x100000 , 0x200000 , 0x400000 ] :
2017-11-13 05:29:42 +00:00
# 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 :
2020-09-15 03:31:17 +00:00
pass
2017-12-08 00:43:22 +00:00
elif len ( palette ) == 0x78 :
self . palette = palette
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
2020-10-24 03:33:52 +00:00
@staticmethod
def get_sprite_from_name ( name : str , local_random = random ) - > Optional [ Sprite ] :
_populate_sprite_table ( )
name = name . lower ( )
if name . startswith ( ' random ' ) :
sprites = list ( set ( _sprite_table . values ( ) ) )
sprites . sort ( key = lambda x : x . name )
return local_random . choice ( sprites )
return _sprite_table . get ( name , None )
2018-01-05 05:42:35 +00:00
@staticmethod
def default_link_sprite ( ) :
2020-08-25 11:22:47 +00:00
return Sprite ( local_path ( ' data ' , ' default.zspr ' ) )
2018-01-05 05:42:35 +00:00
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 ) :
2020-10-24 03:33:52 +00:00
position = 1 << ( 7 - x )
2017-12-17 05:25:46 +00:00
val = 0
2020-10-24 03:33:52 +00:00
if self . sprite [ pos + 2 * y ] & position :
2017-12-17 05:25:46 +00:00
val + = 1
2020-10-24 03:33:52 +00:00
if self . sprite [ pos + 2 * y + 1 ] & position :
2017-12-17 05:25:46 +00:00
val + = 2
2020-10-24 03:33:52 +00:00
if self . sprite [ pos + 2 * y + 16 ] & position :
2017-12-17 05:25:46 +00:00
val + = 4
2020-10-24 03:33:52 +00:00
if self . sprite [ pos + 2 * y + 17 ] & position :
2017-12-17 05:25:46 +00:00
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 )
2020-10-24 03:33:52 +00:00
top_right = self . decode8 ( pos + 0x20 )
bottom_left = self . decode8 ( pos + 0x200 )
bottom_right = self . decode8 ( pos + 0x220 )
2017-11-13 05:29:42 +00:00
for x in range ( 8 ) :
for y in range ( 8 ) :
arr [ y ] [ x ] = top_left [ y ] [ x ]
2020-10-24 03:33:52 +00:00
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 ]
2017-11-13 05:29:42 +00:00
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
2020-10-24 03:33:52 +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 "
2020-10-24 03:33:52 +00:00
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 ) )
2020-10-24 03:33:52 +00:00
2017-11-13 05:29:42 +00:00
def make_int16 ( pair ) :
2020-10-24 03:33:52 +00:00
return pair [ 1 ] << 8 | pair [ 0 ]
2020-08-21 16:35:48 +00:00
2017-11-13 05:29:42 +00:00
def expand_color ( i ) :
2020-08-21 16:35:48 +00:00
return ( ( i & 0x1F ) * 8 , ( i >> 5 & 0x1F ) * 8 , ( i >> 10 & 0x1F ) * 8 )
2017-11-13 05:29:42 +00:00
# turn palette data into a list of RGB tuples with 8 bit values
2020-10-28 06:55:29 +00:00
palette_as_colors = [ expand_color ( make_int16 ( chnk ) ) for chnk in array_chunk ( self . palette , 2 ) ]
2017-11-13 05:29:42 +00:00
# split into palettes of 15 colors
return array_chunk ( palette_as_colors , 15 )
2020-08-21 16:35:48 +00:00
def __hash__ ( self ) :
return hash ( self . name )
2020-10-24 03:33:52 +00:00
def write_to_rom ( self , rom : LocalRom ) :
2020-09-14 17:24:44 +00:00
if not self . valid :
logging . warning ( " Tried writing invalid sprite to rom, skipping. " )
return
rom . write_bytes ( 0x80000 , self . sprite )
rom . write_bytes ( 0xDD308 , self . palette )
rom . write_bytes ( 0xDEDF5 , self . glove_palette )
2020-10-04 17:57:30 +00:00
rom . write_bytes ( 0x300000 , self . sprite )
rom . write_bytes ( 0x307000 , self . palette )
rom . write_bytes ( 0x307078 , self . glove_palette )
2020-09-14 17:24:44 +00:00
2020-10-24 03:33:52 +00:00
2020-01-14 09:42:27 +00:00
def patch_rom ( world , rom , player , team , enemized ) :
2020-07-16 06:01:29 +00:00
local_random = 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
2020-05-24 10:43:03 +00:00
distinguished_prog_bow_loc = local_random . choice ( prog_bow_locs )
2019-08-22 02:40:19 +00:00
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 :
2020-01-18 08:50:12 +00:00
if location . item is not None :
# Keys in their native dungeon should use the orignal item code for keys
if location . parent_region . dungeon :
if location . parent_region . dungeon . is_dungeon_item ( location . item ) :
if location . item . bigkey :
itemid = 0x32
if location . item . smallkey :
itemid = 0x24
if location . item . map :
itemid = 0x33
if location . item . compass :
itemid = 0x25
if world . remote_items [ player ] :
itemid = list ( location_table . keys ( ) ) . index ( location . name ) + 1
assert itemid < 0x100
rom . write_byte ( location . player_address , 0xFF )
elif location . item . player != player :
if location . player_address is not None :
rom . write_byte ( location . player_address , location . item . player )
else :
itemid = 0x5A
2020-10-27 08:36:55 +00:00
location_address = old_location_address_to_new_location_address . get ( location . address , location . address )
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-16 20:46:47 +00:00
if world . mapshuffle [ player ] :
2020-05-24 10:43:03 +00:00
music = local_random . choice ( [ 0x11 , 0x16 ] )
2017-10-28 22:34:37 +00:00
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-16 20:46:47 +00:00
if world . mapshuffle [ player ] :
2020-05-24 10:43:03 +00:00
rom . write_byte ( 0x155C9 , local_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
2020-08-21 16:35:48 +00:00
# room id is deliberately not written
2018-01-17 01:56:35 +00:00
rom . write_byte ( 0x15B8C + offset , ow_area )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15BDB + 2 * offset , vram_loc )
rom . write_int16 ( 0x15C79 + 2 * offset , scroll_y )
rom . write_int16 ( 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
2020-11-06 02:25:26 +00:00
if world . shuffle [ player ] not in { ' insanity ' , ' insanity_legacy ' , ' madness_legacy ' } and \
exit . name in { ' Eastern Palace Exit ' , ' Tower of Hera Exit ' , ' Thieves Town Exit ' ,
2020-08-21 16:35:48 +00:00
' 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) ' ,
2020-11-06 02:25:26 +00:00
' 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.
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15DB5 + 2 * offset , link_y ) # same as final else
2019-12-16 17:24:34 +00:00
elif room_id == 0x0059 and world . fix_skullwoods_exit [ player ] :
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15DB5 + 2 * offset , 0x00F8 )
2019-12-16 17:24:34 +00:00
elif room_id == 0x004a and world . fix_palaceofdarkness_exit [ player ] :
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15DB5 + 2 * offset , 0x0640 )
2019-12-16 17:24:34 +00:00
elif room_id == 0x00d6 and world . fix_trock_exit [ player ] :
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15DB5 + 2 * offset , 0x0134 )
elif room_id == 0x000c and world . fix_gtower_exit : # fix ganons tower exit point
rom . write_int16 ( 0x15DB5 + 2 * offset , 0x00A4 )
2018-01-17 01:56:35 +00:00
else :
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15DB5 + 2 * offset , link_y )
2018-01-19 02:36:06 +00:00
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15E53 + 2 * offset , link_x )
rom . write_int16 ( 0x15EF1 + 2 * offset , camera_y )
rom . write_int16 ( 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 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x160CB + 2 * offset , door_1 )
rom . write_int16 ( 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 )
2020-07-30 18:14:05 +00:00
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-12-16 17:24:34 +00:00
set_inverted_mode ( world , player , 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 )
2020-10-24 03:33:52 +00:00
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
GREEN_CLOCK = ItemFactory ( ' Green Clock ' , player ) . code
2018-01-04 06:06:22 +00:00
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x18004F , 0x01 ) # Byrna Invulnerability: on
2019-08-10 23:37:26 +00:00
# handle difficulty_adjustments
2019-12-16 16:46:21 +00:00
if world . difficulty_adjustments [ player ] == ' hard ' :
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180181 , 0x01 ) # Make silver arrows work only on ganon
rom . write_byte ( 0x180182 , 0x00 ) # Don't auto equip silvers on pickup
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
2020-10-24 03:33:52 +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 )
2020-10-24 03:33:52 +00:00
# Disable catching fairies
2017-11-10 10:08:59 +00:00
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
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x180036 , world . rupoor_cost )
2018-03-14 18:30:11 +00:00
# Set stun items
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180180 , 0x02 ) # Hookshot only
2019-12-16 16:46:21 +00:00
elif world . difficulty_adjustments [ player ] == ' expert ' :
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180181 , 0x01 ) # Make silver arrows work only on ganon
rom . write_byte ( 0x180182 , 0x00 ) # Don't auto equip silvers on pickup
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
2020-10-24 03:33:52 +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 )
2020-10-24 03:33:52 +00:00
# Disable catching fairies
2017-11-10 10:08:59 +00:00
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
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x180036 , world . rupoor_cost )
2018-03-14 18:30:11 +00:00
# Set stun items
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180180 , 0x00 ) # Nothing
2017-06-04 12:44:23 +00:00
else :
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180181 , 0x00 ) # Make silver arrows freely usable
rom . write_byte ( 0x180182 , 0x01 ) # auto equip silvers on pickup
2017-06-04 12:44:23 +00:00
# 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
2020-10-24 03:33:52 +00:00
# Cape magic cost
2017-12-08 21:53:53 +00:00
rom . write_bytes ( 0x3ADA7 , [ 0x04 , 0x08 , 0x10 ] )
2018-03-02 01:29:18 +00:00
# Byrna Invulnerability: on
rom . write_byte ( 0x18004F , 0x01 )
2020-10-24 03:33:52 +00:00
# Enable catching fairies
2017-12-08 21:53:53 +00:00
rom . write_byte ( 0x34FD6 , 0xF0 )
2018-03-14 18:30:11 +00:00
# Rupoor negative value
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x180036 , world . rupoor_cost )
2018-03-14 18:30:11 +00:00
# Set stun items
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180180 , 0x03 ) # All standard items
# Set overflow items for progressive equipment
2020-02-03 01:10:56 +00:00
if world . timer [ player ] 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
2020-10-24 03:33:52 +00:00
# Byrna residual magic cost
2018-03-02 01:29:18 +00:00
rom . write_bytes ( 0x45C42 , [ 0x04 , 0x02 , 0x01 ] )
2019-12-16 16:46:21 +00:00
difficulty = world . difficulty_requirements [ player ]
2019-08-10 23:37:26 +00:00
2020-10-24 03:33:52 +00:00
# Set overflow items for progressive equipment
2018-01-04 06:06:22 +00:00
rom . write_bytes ( 0x180090 ,
2020-10-24 03:33:52 +00:00
[ difficulty . progressive_sword_limit if world . swords [ player ] != ' swordless ' else 0 ,
overflow_replacement ,
2018-01-04 06:06:22 +00:00
difficulty . progressive_shield_limit , overflow_replacement ,
difficulty . progressive_armor_limit , overflow_replacement ,
2019-12-08 01:41:28 +00:00
difficulty . progressive_bottle_limit , overflow_replacement ] )
2020-04-04 07:16:39 +00:00
2020-10-24 03:33:52 +00:00
# Work around for json patch ordering issues - write bow limit separately so that it is replaced in the patch
2019-12-08 01:41:28 +00:00
rom . write_bytes ( 0x180098 , [ difficulty . progressive_bow_limit , overflow_replacement ] )
2019-12-09 18:27:56 +00:00
2020-10-24 03:33:52 +00:00
if difficulty . progressive_bow_limit < 2 and (
world . swords [ player ] == ' swordless ' or world . logic [ player ] == ' noglitches ' ) :
2019-08-26 02:28:12 +00:00
rom . write_bytes ( 0x180098 , [ 2 , overflow_replacement ] )
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180181 , 0x01 ) # Make silver arrows work only on ganon
rom . write_byte ( 0x180182 , 0x00 ) # Don't auto equip silvers on pickup
2017-05-25 10:09:50 +00:00
# set up game internal RNG seed
2020-05-24 10:43:03 +00:00
rom . write_bytes ( 0x178000 , local_random . getrandbits ( 8 * 1024 ) . to_bytes ( 1024 , ' big ' ) )
2020-09-20 02:35:45 +00:00
if " g " in world . shuffle_prizes [ player ] :
# shuffle prize packs
2020-10-24 03:33:52 +00:00
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 ,
2020-09-20 02:35:45 +00:00
0xD8 , 0xE3 , 0xE0 , 0xDB , 0xDE , 0xD8 , 0xDB , 0xE2 , 0xD9 , 0xDA , 0xDB , 0xD9 , 0xDB , 0xD9 , 0xDB ]
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 ]
2020-10-24 03:33:52 +00:00
def chunk ( l , n ) :
return [ l [ i : i + n ] for i in range ( 0 , len ( l ) , n ) ]
2020-09-20 02:35:45 +00:00
# randomize last 7 slots
2020-10-24 03:33:52 +00:00
prizes [ - 7 : ] = local_random . sample ( prizes , 7 )
2020-09-20 02:35:45 +00:00
2020-10-24 03:33:52 +00:00
# shuffle order of 7 main packs
2020-09-20 02:35:45 +00:00
packs = chunk ( prizes [ : 56 ] , 8 )
local_random . shuffle ( packs )
prizes [ : 56 ] = [ drop for pack in packs for drop in pack ]
if world . difficulty_adjustments [ player ] in [ ' hard ' , ' expert ' ] :
2020-10-24 03:33:52 +00:00
prize_replacements = { 0xE0 : 0xDF , # Fairy -> heart
0xE3 : 0xD8 } # Big magic -> small magic
2020-09-20 02:35:45 +00:00
prizes = [ prize_replacements . get ( prize , prize ) for prize in prizes ]
dig_prizes = [ prize_replacements . get ( prize , prize ) for prize in dig_prizes ]
if world . retro [ player ] :
2020-10-24 03:33:52 +00:00
prize_replacements = { 0xE1 : 0xDA , # 5 Arrows -> Blue Rupee
0xE2 : 0xDB } # 10 Arrows -> Red Rupee
2020-09-20 02:35:45 +00:00
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 )
# write tree pull prizes
rom . write_byte ( 0xEFBD4 , prizes . pop ( ) )
rom . write_byte ( 0xEFBD5 , prizes . pop ( ) )
rom . write_byte ( 0xEFBD6 , prizes . pop ( ) )
# rupee crab prizes
rom . write_byte ( 0x329C8 , prizes . pop ( ) ) # first prize
rom . write_byte ( 0x329C4 , prizes . pop ( ) ) # final prize
# stunned enemy prize
rom . write_byte ( 0x37993 , prizes . pop ( ) )
# saved fish prize
rom . write_byte ( 0xE82CC , prizes . pop ( ) )
# fill enemy prize packs
rom . write_bytes ( 0x37A78 , prizes )
if " b " in world . shuffle_prizes [ player ] :
# set bonk prizes
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 ]
2020-05-24 10:43:03 +00:00
local_random . shuffle ( bonk_prizes )
2020-09-20 02:35:45 +00:00
for prize , address in zip ( bonk_prizes , bonk_addresses ) :
rom . write_byte ( address , prize )
2017-12-08 21:53:53 +00:00
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
2020-10-24 03:33:52 +00:00
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
0x58 , 0x01 , 0x36 if world . retro [ player ] 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
0xFF , 0xFF , 0xFF , 0xFF , # end of table sentinel
2019-08-04 16:32:35 +00:00
] )
2017-05-25 10:09:50 +00:00
2017-05-25 15:52:31 +00:00
# set Fountain bottle exchange items
2019-12-16 16:46:21 +00:00
if world . difficulty [ player ] in [ ' hard ' , ' expert ' ] :
2020-05-24 10:43:03 +00:00
rom . write_byte ( 0x348FF , [ 0x16 , 0x2B , 0x2C , 0x2D , 0x3C , 0x48 ] [ local_random . randint ( 0 , 5 ) ] )
rom . write_byte ( 0x3493B , [ 0x16 , 0x2B , 0x2C , 0x2D , 0x3C , 0x48 ] [ local_random . randint ( 0 , 5 ) ] )
2017-11-11 21:25:56 +00:00
else :
2020-05-24 10:43:03 +00:00
rom . write_byte ( 0x348FF , [ 0x16 , 0x2B , 0x2C , 0x2D , 0x3C , 0x3D , 0x48 ] [ local_random . randint ( 0 , 6 ) ] )
rom . write_byte ( 0x3493B , [ 0x16 , 0x2B , 0x2C , 0x2D , 0x3C , 0x3D , 0x48 ] [ local_random . randint ( 0 , 6 ) ] )
2017-12-08 21:53:53 +00:00
2020-10-24 03:33:52 +00:00
# enable Fat Fairy Chests
2017-12-08 21:53:53 +00:00
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 ] )
2020-10-24 03:33:52 +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 ] )
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180029 , 0x01 ) # Smithy quick item give
2017-12-08 21:53:53 +00:00
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
2020-09-17 05:00:27 +00:00
if world . difficulty_adjustments [ player ] == ' easy ' :
rom . write_byte ( 0x18003F , 0x01 ) # hammer can harm ganon
rom . write_byte ( 0x180041 , 0x02 ) # Allow swordless medallion use EVERYWHERE.
rom . write_byte ( 0x180044 , 0x01 ) # hammer activates tablets
2017-06-04 11:10:22 +00:00
# set up clocks for timed modes
2020-10-28 23:20:59 +00:00
if world . clock_mode [ player ] in [ ' ohko ' , ' countdown-ohko ' ] :
rom . write_bytes ( 0x180190 , [ 0x01 , 0x02 , 0x01 ] ) # ohko timer with resetable timer functionality
elif world . clock_mode [ player ] == ' stopwatch ' :
rom . write_bytes ( 0x180190 , [ 0x02 , 0x01 , 0x00 ] ) # set stopwatch mode
elif world . clock_mode [ player ] == ' countdown ' :
rom . write_bytes ( 0x180190 , [ 0x01 , 0x01 , 0x00 ] ) # set countdown, with no reset available
2017-12-14 21:04:28 +00:00
else :
2017-07-14 12:37:34 +00:00
rom . write_bytes ( 0x180190 , [ 0x00 , 0x00 , 0x00 ] ) # turn off clock mode
2020-10-28 23:20:59 +00:00
# Set up requested clock settings
if world . clock_mode [ player ] in [ ' countdown-ohko ' , ' stopwatch ' , ' countdown ' ] :
rom . write_int32 ( 0x180200 , world . red_clock_time [ player ] * 60 * 60 ) # red clock adjustment time (in frames, sint32)
rom . write_int32 ( 0x180204 , world . blue_clock_time [ player ] * 60 * 60 ) # blue clock adjustment time (in frames, sint32)
rom . write_int32 ( 0x180208 , world . green_clock_time [ player ] * 60 * 60 ) # green clock adjustment time (in frames, sint32)
else :
2020-08-21 16:35:48 +00:00
rom . write_int32 ( 0x180200 , 0 ) # red clock adjustment time (in frames, sint32)
rom . write_int32 ( 0x180204 , 0 ) # blue clock adjustment time (in frames, sint32)
rom . write_int32 ( 0x180208 , 0 ) # green clock adjustment time (in frames, sint32)
2020-10-28 23:20:59 +00:00
# Set up requested start time for countdown modes
if world . clock_mode [ player ] in [ ' countdown-ohko ' , ' countdown ' ] :
rom . write_int32 ( 0x18020C , world . countdown_start_time [ player ] * 60 * 60 ) # starting time (in frames, sint32)
else :
2020-08-21 16:35:48 +00:00
rom . write_int32 ( 0x18020C , 0 ) # starting time (in frames, sint32)
2017-06-04 11:10:22 +00:00
# set up goals for treasure hunt
2019-12-21 09:42:59 +00:00
rom . write_bytes ( 0x180165 , [ 0x0E , 0x28 ] if world . treasure_hunt_icon [ player ] == ' Triforce Piece ' else [ 0x0D , 0x28 ] )
rom . write_byte ( 0x180167 , world . treasure_hunt_count [ player ] % 256 )
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180194 , 1 ) # Must turn in triforced pieces (instant win not enabled)
2017-06-04 11:10:22 +00:00
2020-10-24 03:33:52 +00:00
rom . write_bytes ( 0x180213 , [ 0x00 , 0x01 ] ) # Not a Tournament Seed
2017-12-08 21:53:53 +00:00
2020-10-24 03:33:52 +00:00
gametype = 0x04 # item
2019-12-16 17:24:34 +00:00
if world . shuffle [ player ] != ' vanilla ' :
2020-10-24 03:33:52 +00:00
gametype | = 0x02 # entrance
2019-12-15 09:54:49 +00:00
if enemized :
2020-10-24 03:33:52 +00:00
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
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x1800A2 , 0x01 if world . fix_fake_world [
player ] else 0x00 ) # Toggle whether to be in real/fake dark world when dying in a DW dungeon before killing aga1
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
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x180171 ,
0x01 if world . ganon_at_pyramid [ player ] else 0x00 ) # Enable respawning on pyramid after ganon death
rom . write_byte ( 0x180173 , 0x01 ) # Bob is enabled
2017-11-10 10:08:59 +00:00
rom . write_byte ( 0x180168 , 0x08 ) # Spike Cave Damage
2020-10-24 03:33:52 +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
rom . write_byte ( 0x18008B , 0x01 if world . open_pyramid [ player ] else 0x00 ) # pre-open Pyramid Hole
rom . write_byte ( 0x18008C , 0x01 if world . crystals_needed_for_gt [
player ] == 0 else 0x00 ) # GT pre-opened if crystal requirement is 0
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
2019-12-18 19:45:51 +00:00
rom . write_byte ( 0x180174 , 0x01 if world . fix_fake_world [ player ] else 0x00 )
2020-10-24 03:33:52 +00:00
rom . write_byte ( 0x18017E , 0x01 ) # Fairy fountains only trade in bottles
2020-01-06 18:13:42 +00:00
# Starting equipment
equip = [ 0 ] * ( 0x340 + 0x4F )
equip [ 0x36C ] = 0x18
equip [ 0x36D ] = 0x18
equip [ 0x379 ] = 0x68
starting_max_bombs = 10
starting_max_arrows = 30
startingstate = CollectionState ( world )
2020-08-23 01:03:21 +00:00
if startingstate . has ( ' Silver Bow ' , player ) :
2020-01-06 18:13:42 +00:00
equip [ 0x340 ] = 1
2020-08-23 01:03:21 +00:00
equip [ 0x38E ] | = 0x60
if not world . retro [ player ] :
equip [ 0x38E ] | = 0x80
elif startingstate . has ( ' Bow ' , player ) :
equip [ 0x340 ] = 1
equip [ 0x38E ] | = 0x20 # progressive flag to get the correct hint in all cases
2020-01-06 18:13:42 +00:00
if not world . retro [ player ] :
equip [ 0x38E ] | = 0x80
if startingstate . has ( ' Silver Arrows ' , player ) :
equip [ 0x38E ] | = 0x40
if startingstate . has ( ' Titans Mitts ' , player ) :
equip [ 0x354 ] = 2
elif startingstate . has ( ' Power Glove ' , player ) :
equip [ 0x354 ] = 1
if startingstate . has ( ' Golden Sword ' , player ) :
equip [ 0x359 ] = 4
elif startingstate . has ( ' Tempered Sword ' , player ) :
equip [ 0x359 ] = 3
elif startingstate . has ( ' Master Sword ' , player ) :
equip [ 0x359 ] = 2
elif startingstate . has ( ' Fighter Sword ' , player ) :
equip [ 0x359 ] = 1
if startingstate . has ( ' Mirror Shield ' , player ) :
equip [ 0x35A ] = 3
elif startingstate . has ( ' Red Shield ' , player ) :
equip [ 0x35A ] = 2
elif startingstate . has ( ' Blue Shield ' , player ) :
equip [ 0x35A ] = 1
if startingstate . has ( ' Red Mail ' , player ) :
equip [ 0x35B ] = 2
elif startingstate . has ( ' Blue Mail ' , player ) :
equip [ 0x35B ] = 1
if startingstate . has ( ' Magic Upgrade (1/4) ' , player ) :
equip [ 0x37B ] = 2
equip [ 0x36E ] = 0x80
elif startingstate . has ( ' Magic Upgrade (1/2) ' , player ) :
equip [ 0x37B ] = 1
equip [ 0x36E ] = 0x80
2019-08-10 19:30:14 +00:00
for item in world . precollected_items :
if item . player != player :
continue
2020-08-23 01:03:21 +00:00
if item . name in { ' Bow ' , ' Silver Bow ' , ' Silver Arrows ' , ' Progressive Bow ' , ' Progressive Bow (Alt) ' ,
2020-01-06 18:13:42 +00:00
' Titans Mitts ' , ' Power Glove ' , ' Progressive Glove ' ,
' Golden Sword ' , ' Tempered Sword ' , ' Master Sword ' , ' Fighter Sword ' , ' Progressive Sword ' ,
' Mirror Shield ' , ' Red Shield ' , ' Blue Shield ' , ' Progressive Shield ' ,
2020-09-06 15:19:34 +00:00
' Red Mail ' , ' Blue Mail ' , ' Progressive Mail ' ,
2020-08-23 01:03:21 +00:00
' Magic Upgrade (1/4) ' , ' Magic Upgrade (1/2) ' } :
2020-01-06 18:13:42 +00:00
continue
2020-10-24 03:33:52 +00:00
set_table = { ' Book of Mudora ' : ( 0x34E , 1 ) , ' Hammer ' : ( 0x34B , 1 ) , ' Bug Catching Net ' : ( 0x34D , 1 ) ,
' Hookshot ' : ( 0x342 , 1 ) , ' Magic Mirror ' : ( 0x353 , 2 ) ,
' Cape ' : ( 0x352 , 1 ) , ' Lamp ' : ( 0x34A , 1 ) , ' Moon Pearl ' : ( 0x357 , 1 ) , ' Cane of Somaria ' : ( 0x350 , 1 ) ,
' Cane of Byrna ' : ( 0x351 , 1 ) ,
' Fire Rod ' : ( 0x345 , 1 ) , ' Ice Rod ' : ( 0x346 , 1 ) , ' Bombos ' : ( 0x347 , 1 ) , ' Ether ' : ( 0x348 , 1 ) ,
' Quake ' : ( 0x349 , 1 ) }
2020-01-06 18:13:42 +00:00
or_table = { ' Green Pendant ' : ( 0x374 , 0x04 ) , ' Red Pendant ' : ( 0x374 , 0x01 ) , ' Blue Pendant ' : ( 0x374 , 0x02 ) ,
2020-06-24 14:22:49 +00:00
' Crystal 1 ' : ( 0x37A , 0x02 ) , ' Crystal 2 ' : ( 0x37A , 0x10 ) , ' Crystal 3 ' : ( 0x37A , 0x40 ) ,
' Crystal 4 ' : ( 0x37A , 0x20 ) ,
2020-01-06 18:13:42 +00:00
' Crystal 5 ' : ( 0x37A , 0x04 ) , ' Crystal 6 ' : ( 0x37A , 0x01 ) , ' Crystal 7 ' : ( 0x37A , 0x08 ) ,
2020-06-24 14:22:49 +00:00
' Big Key (Eastern Palace) ' : ( 0x367 , 0x20 ) , ' Compass (Eastern Palace) ' : ( 0x365 , 0x20 ) ,
' Map (Eastern Palace) ' : ( 0x369 , 0x20 ) ,
' Big Key (Desert Palace) ' : ( 0x367 , 0x10 ) , ' Compass (Desert Palace) ' : ( 0x365 , 0x10 ) ,
' Map (Desert Palace) ' : ( 0x369 , 0x10 ) ,
' Big Key (Tower of Hera) ' : ( 0x366 , 0x20 ) , ' Compass (Tower of Hera) ' : ( 0x364 , 0x20 ) ,
' Map (Tower of Hera) ' : ( 0x368 , 0x20 ) ,
' Big Key (Hyrule Castle) ' : ( 0x367 , 0xC0 ) , ' Compass (Hyrule Castle) ' : ( 0x365 , 0xC0 ) ,
' Map (Hyrule Castle) ' : ( 0x369 , 0xC0 ) ,
2020-04-11 04:35:07 +00:00
# doors-specific items
2020-06-24 14:22:49 +00:00
' Big Key (Agahnims Tower) ' : ( 0x367 , 0x08 ) , ' Compass (Agahnims Tower) ' : ( 0x365 , 0x08 ) ,
' Map (Agahnims Tower) ' : ( 0x369 , 0x08 ) ,
2020-04-11 04:35:07 +00:00
# end of doors-specific items
2020-06-24 14:22:49 +00:00
' Big Key (Palace of Darkness) ' : ( 0x367 , 0x02 ) , ' Compass (Palace of Darkness) ' : ( 0x365 , 0x02 ) ,
' Map (Palace of Darkness) ' : ( 0x369 , 0x02 ) ,
' Big Key (Thieves Town) ' : ( 0x366 , 0x10 ) , ' Compass (Thieves Town) ' : ( 0x364 , 0x10 ) ,
' Map (Thieves Town) ' : ( 0x368 , 0x10 ) ,
' Big Key (Skull Woods) ' : ( 0x366 , 0x80 ) , ' Compass (Skull Woods) ' : ( 0x364 , 0x80 ) ,
' Map (Skull Woods) ' : ( 0x368 , 0x80 ) ,
' Big Key (Swamp Palace) ' : ( 0x367 , 0x04 ) , ' Compass (Swamp Palace) ' : ( 0x365 , 0x04 ) ,
' Map (Swamp Palace) ' : ( 0x369 , 0x04 ) ,
' Big Key (Ice Palace) ' : ( 0x366 , 0x40 ) , ' Compass (Ice Palace) ' : ( 0x364 , 0x40 ) ,
' Map (Ice Palace) ' : ( 0x368 , 0x40 ) ,
' Big Key (Misery Mire) ' : ( 0x367 , 0x01 ) , ' Compass (Misery Mire) ' : ( 0x365 , 0x01 ) ,
' Map (Misery Mire) ' : ( 0x369 , 0x01 ) ,
' Big Key (Turtle Rock) ' : ( 0x366 , 0x08 ) , ' Compass (Turtle Rock) ' : ( 0x364 , 0x08 ) ,
' Map (Turtle Rock) ' : ( 0x368 , 0x08 ) ,
2020-10-24 03:33:52 +00:00
' Big Key (Ganons Tower) ' : ( 0x366 , 0x04 ) , ' Compass (Ganons Tower) ' : ( 0x364 , 0x04 ) ,
' Map (Ganons Tower) ' : ( 0x368 , 0x04 ) }
set_or_table = { ' Flippers ' : ( 0x356 , 1 , 0x379 , 0x02 ) , ' Pegasus Boots ' : ( 0x355 , 1 , 0x379 , 0x04 ) ,
2020-02-17 09:08:03 +00:00
' Shovel ' : ( 0x34C , 1 , 0x38C , 0x04 ) , ' Flute ' : ( 0x34C , 3 , 0x38C , 0x01 ) ,
2020-01-06 18:13:42 +00:00
' Mushroom ' : ( 0x344 , 1 , 0x38C , 0x20 | 0x08 ) , ' Magic Powder ' : ( 0x344 , 2 , 0x38C , 0x10 ) ,
' Blue Boomerang ' : ( 0x341 , 1 , 0x38C , 0x80 ) , ' Red Boomerang ' : ( 0x341 , 2 , 0x38C , 0x40 ) }
keys = { ' Small Key (Eastern Palace) ' : [ 0x37E ] , ' Small Key (Desert Palace) ' : [ 0x37F ] ,
' Small Key (Tower of Hera) ' : [ 0x386 ] ,
' Small Key (Agahnims Tower) ' : [ 0x380 ] , ' Small Key (Palace of Darkness) ' : [ 0x382 ] ,
' Small Key (Thieves Town) ' : [ 0x387 ] ,
' Small Key (Skull Woods) ' : [ 0x384 ] , ' Small Key (Swamp Palace) ' : [ 0x381 ] ,
' Small Key (Ice Palace) ' : [ 0x385 ] ,
' Small Key (Misery Mire) ' : [ 0x383 ] , ' Small Key (Turtle Rock) ' : [ 0x388 ] ,
' Small Key (Ganons Tower) ' : [ 0x389 ] ,
2020-06-24 14:22:49 +00:00
' Small Key (Universal) ' : [ 0x38B ] , ' Small Key (Hyrule Castle) ' : [ 0x37C , 0x37D ] }
2020-01-06 18:13:42 +00:00
bottles = { ' Bottle ' : 2 , ' Bottle (Red Potion) ' : 3 , ' Bottle (Green Potion) ' : 4 , ' Bottle (Blue Potion) ' : 5 ,
' Bottle (Fairy) ' : 6 , ' Bottle (Bee) ' : 7 , ' Bottle (Good Bee) ' : 8 }
2020-10-24 03:33:52 +00:00
rupees = { ' Rupee (1) ' : 1 , ' Rupees (5) ' : 5 , ' Rupees (20) ' : 20 , ' Rupees (50) ' : 50 , ' Rupees (100) ' : 100 ,
' Rupees (300) ' : 300 }
2020-01-06 18:13:42 +00:00
bomb_caps = { ' Bomb Upgrade (+5) ' : 5 , ' Bomb Upgrade (+10) ' : 10 }
arrow_caps = { ' Arrow Upgrade (+5) ' : 5 , ' Arrow Upgrade (+10) ' : 10 }
bombs = { ' Single Bomb ' : 1 , ' Bombs (3) ' : 3 , ' Bombs (10) ' : 10 }
arrows = { ' Single Arrow ' : 1 , ' Arrows (10) ' : 10 }
if item . name in set_table :
equip [ set_table [ item . name ] [ 0 ] ] = set_table [ item . name ] [ 1 ]
elif item . name in or_table :
equip [ or_table [ item . name ] [ 0 ] ] | = or_table [ item . name ] [ 1 ]
elif item . name in set_or_table :
equip [ set_or_table [ item . name ] [ 0 ] ] = set_or_table [ item . name ] [ 1 ]
equip [ set_or_table [ item . name ] [ 2 ] ] | = set_or_table [ item . name ] [ 3 ]
elif item . name in keys :
for address in keys [ item . name ] :
equip [ address ] = min ( equip [ address ] + 1 , 99 )
elif item . name in bottles :
if equip [ 0x34F ] < world . difficulty_requirements [ player ] . progressive_bottle_limit :
equip [ 0x35C + equip [ 0x34F ] ] = bottles [ item . name ]
equip [ 0x34F ] + = 1
elif item . name in rupees :
2020-10-24 03:33:52 +00:00
equip [ 0x360 : 0x362 ] = list (
min ( equip [ 0x360 ] + ( equip [ 0x361 ] << 8 ) + rupees [ item . name ] , 9999 ) . to_bytes ( 2 , byteorder = ' little ' ,
signed = False ) )
equip [ 0x362 : 0x364 ] = list (
min ( equip [ 0x362 ] + ( equip [ 0x363 ] << 8 ) + rupees [ item . name ] , 9999 ) . to_bytes ( 2 , byteorder = ' little ' ,
signed = False ) )
2020-01-06 18:13:42 +00:00
elif item . name in bomb_caps :
starting_max_bombs = min ( starting_max_bombs + bomb_caps [ item . name ] , 50 )
elif item . name in arrow_caps :
starting_max_arrows = min ( starting_max_arrows + arrow_caps [ item . name ] , 70 )
elif item . name in bombs :
equip [ 0x343 ] + = bombs [ item . name ]
elif item . name in arrows :
if world . retro [ player ] :
equip [ 0x38E ] | = 0x80
equip [ 0x377 ] = 1
else :
equip [ 0x377 ] + = arrows [ item . name ]
elif item . name in [ ' Piece of Heart ' , ' Boss Heart Container ' , ' Sanctuary Heart Container ' ] :
if item . name == ' Piece of Heart ' :
equip [ 0x36B ] = ( equip [ 0x36B ] + 1 ) % 4
if item . name != ' Piece of Heart ' or equip [ 0x36B ] == 0 :
equip [ 0x36C ] = min ( equip [ 0x36C ] + 0x08 , 0xA0 )
equip [ 0x36D ] = min ( equip [ 0x36D ] + 0x08 , 0xA0 )
2019-08-10 19:30:14 +00:00
else :
2020-01-06 18:13:42 +00:00
raise RuntimeError ( f ' Unsupported item in starting equipment: { item . name } ' )
equip [ 0x343 ] = min ( equip [ 0x343 ] , starting_max_bombs )
rom . write_byte ( 0x180034 , starting_max_bombs )
equip [ 0x377 ] = min ( equip [ 0x377 ] , starting_max_arrows )
rom . write_byte ( 0x180035 , starting_max_arrows )
rom . write_bytes ( 0x180046 , equip [ 0x360 : 0x362 ] )
if equip [ 0x359 ] :
rom . write_byte ( 0x180043 , equip [ 0x359 ] )
assert equip [ : 0x340 ] == [ 0 ] * 0x340
rom . write_bytes ( 0x183000 , equip [ 0x340 : ] )
2020-08-23 19:38:21 +00:00
rom . write_bytes ( 0x271A6 , equip [ 0x340 : 0x340 + 60 ] )
2019-08-10 19:30:14 +00:00
2019-12-16 15:54:46 +00:00
rom . write_byte ( 0x18004A , 0x00 if world . mode [ player ] != ' inverted ' else 0x01 ) # Inverted mode
2020-08-23 19:38:21 +00:00
rom . write_byte ( 0x18005D , 0x00 ) # Hammer always breaks barrier
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))
rom . write_byte ( 0x3A9A7 , 0xD0 ) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader))
if ' u ' in world . shop_shuffle [ player ] :
rom . write_bytes ( 0x180080 ,
[ 5 , 10 , 5 , 10 ] ) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10)
else :
rom . write_bytes ( 0x180080 ,
[ 50 , 50 , 70 , 70 ] ) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10)
2018-09-23 02:51:54 +00:00
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 ) |
2020-08-23 19:38:21 +00:00
( 0x04 if ' magic ' in world . escape_assist [ player ] else 0x00 ) ) ) # Escape assist
2017-12-08 21:53:53 +00:00
2020-06-03 00:19:16 +00:00
if world . goal [ player ] in [ ' pedestal ' , ' triforcehunt ' , ' localtriforcehunt ' ] :
2017-07-14 12:37:34 +00:00
rom . write_byte ( 0x18003E , 0x01 ) # make ganon invincible
2020-06-26 14:18:53 +00:00
elif world . goal [ player ] in [ ' ganontriforcehunt ' , ' localganontriforcehunt ' ] :
2020-08-23 01:03:21 +00:00
rom . write_byte ( 0x18003E , 0x05 ) # make ganon invincible until enough triforce pieces are collected
2020-10-15 22:24:52 +00:00
elif world . goal [ player ] in [ ' ganonpedestal ' ] :
rom . write_byte ( 0x18003E , 0x06 )
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-12-16 18:09:15 +00:00
rom . write_byte ( 0x18005E , world . crystals_needed_for_gt [ player ] )
rom . write_byte ( 0x18005F , world . crystals_needed_for_ganon [ player ] )
2019-07-25 22:25:14 +00:00
2020-10-24 03:33:52 +00:00
# Bitfield - enable text box to show with free roaming items
#
# ---o bmcs
# o - enabled for outside dungeon items
# b - enabled for inside big keys
# m - enabled for inside maps
# c - enabled for inside compasses
# s - enabled for inside small keys
2020-01-10 06:15:11 +00:00
# block HC upstairs doors in rain state in standard mode
rom . write_byte ( 0x18008A , 0x01 if world . mode [ player ] == " standard " and world . shuffle [ player ] != ' vanilla ' else 0x00 )
2019-07-25 22:25:14 +00:00
2020-08-21 00:39:18 +00:00
rom . write_byte ( 0x18016A , 0x10 | ( ( 0x01 if world . keyshuffle [ player ] is True else 0x00 )
2019-12-16 20:46:47 +00:00
| ( 0x02 if world . compassshuffle [ player ] else 0x00 )
| ( 0x04 if world . mapshuffle [ player ] else 0x00 )
| ( 0x08 if world . bigkeyshuffle [ player ] else 0x00 ) ) ) # free roaming item text boxes
rom . write_byte ( 0x18003B , 0x01 if world . mapshuffle [ player ] 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
2020-04-12 22:46:32 +00:00
if world . clock_mode [ player ] or not world . dungeon_counters [ player ] :
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
2020-08-23 01:03:21 +00:00
elif world . dungeon_counters [ player ] is True :
2020-04-12 22:46:32 +00:00
rom . write_byte ( 0x18003C , 0x02 ) # always on
elif world . compassshuffle [ player ] or world . dungeon_counters [ player ] == ' pickup ' :
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
2020-10-24 03:33:52 +00:00
# Bitfield - enable free items to show up in menu
#
# ----dcba
# d - Compass
# c - Map
# b - Big Key
# a - Small Key
#
2020-08-21 00:39:18 +00:00
rom . write_byte ( 0x180045 , ( ( 0x01 if world . keyshuffle [ player ] is True else 0x00 )
2019-12-16 20:46:47 +00:00
| ( 0x02 if world . bigkeyshuffle [ player ] else 0x00 )
2020-04-12 22:46:32 +00:00
| ( 0x04 if world . mapshuffle [ player ] else 0x00 )
| ( 0x08 if world . compassshuffle [ player ] 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
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x18017A ,
get_reveal_bytes ( ' Green Pendant ' ) if world . mapshuffle [ player ] else 0x0000 ) # Sahasrahla reveal
rom . write_int16 ( 0x18017C , get_reveal_bytes ( ' Crystal 5 ' ) | get_reveal_bytes ( ' Crystal 6 ' ) if world . mapshuffle [
player ] else 0x0000 ) # Bomb Shop Reveal
2019-08-04 17:22:37 +00:00
2020-08-21 00:59:46 +00:00
rom . write_byte ( 0x180172 , 0x01 if world . keyshuffle [ player ] == " universal " else 0x00 ) # universal keys
2020-08-31 23:43:15 +00:00
rom . write_byte ( 0x18637E , 0x01 if world . retro [ player ] else 0x00 ) # Skip quiver in item shops once bought
2019-12-16 23:16:02 +00:00
rom . write_byte ( 0x180175 , 0x01 if world . retro [ player ] else 0x00 ) # rupee bow
rom . write_byte ( 0x180176 , 0x0A if world . retro [ player ] else 0x00 ) # wood arrow cost
rom . write_byte ( 0x180178 , 0x32 if world . retro [ player ] else 0x00 ) # silver arrow cost
rom . write_byte ( 0x301FC , 0xDA if world . retro [ player ] else 0xE1 ) # rupees replace arrows under pots
2020-08-21 16:35:48 +00:00
rom . write_byte ( 0x30052 , 0xDB if world . retro [ player ] else 0xE2 ) # replace arrows in fish prize from bottle merchant
rom . write_bytes ( 0xECB4E , [ 0xA9 , 0x00 , 0xEA , 0xEA ] if world . retro [ player ] else [ 0xAF , 0x77 , 0xF3 ,
0x7E ] ) # Thief steals rupees instead of arrows
rom . write_bytes ( 0xF0D96 , [ 0xA9 , 0x00 , 0xEA , 0xEA ] if world . retro [ player ] else [ 0xAF , 0x77 , 0xF3 ,
0x7E ] ) # Pikit steals rupees instead of arrows
rom . write_bytes ( 0xEDA5 ,
[ 0x35 , 0x41 ] if world . retro [ player ] else [ 0x43 , 0x44 ] ) # Chest game gives rupees instead of arrows
2020-05-24 10:43:03 +00:00
digging_game_rng = local_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
2020-08-21 16:35:48 +00:00
rom . write_byte ( 0x18004E , 0 ) # Escape Fill (nothing)
rom . write_int16 ( 0x180183 , 300 ) # Escape fill rupee bow
rom . write_bytes ( 0x180185 , [ 0 , 0 , 0 ] ) # Uncle respawn refills (magic, bombs, arrows)
2020-10-24 03:33:52 +00:00
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)
2020-11-07 04:03:14 +00:00
if world . mode [ player ] == ' standard ' and uncle_location . item and uncle_location . item . player == player :
if uncle_location . item . name in { ' Bow ' , ' Progressive Bow ' } :
2020-08-21 16:35:48 +00:00
rom . write_byte ( 0x18004E , 1 ) # Escape Fill (arrows)
rom . write_int16 ( 0x180183 , 300 ) # Escape fill rupee bow
rom . write_bytes ( 0x180185 , [ 0 , 0 , 70 ] ) # Uncle respawn refills (magic, bombs, arrows)
2020-10-24 03:33:52 +00:00
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)
2020-11-07 04:03:14 +00:00
elif uncle_location . item . name in { ' Bombs (10) ' } :
2020-10-24 03:33:52 +00:00
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)
2020-11-07 04:03:14 +00:00
elif uncle_location . item . name in { ' Cane of Somaria ' , ' Cane of Byrna ' , ' Fire Rod ' } :
2020-10-24 03:33:52 +00:00
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)
2019-08-07 01:36:45 +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
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.
2020-10-24 03:33:52 +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
2020-10-24 03:33:52 +00:00
if world . shuffle [ player ] in [ ' restricted ' , ' full ' , ' crossed ' , ' insanity ' ] or (
world . shuffle [ player ] == ' simple ' and world . mode [ player ] == ' inverted ' ) :
2018-02-25 06:04:41 +00:00
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
2020-10-24 03:33:52 +00:00
# rom.write_byte(0xFE465, 0x1E) # remove small key door on backside of big key door
2017-12-08 21:53:53 +00:00
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
2020-12-22 09:05:48 +00:00
if world . tile_shuffle [ player ] :
tile_set = TileSet . get_random_tile_set ( world . rom_seeds [ player ] )
rom . write_byte ( 0x4BA21 , tile_set . get_speed ( ) )
rom . write_byte ( 0x4BA1D , tile_set . get_len ( ) )
rom . write_bytes ( 0x4BA2A , tile_set . get_bytes ( ) )
2020-01-14 09:42:27 +00:00
write_strings ( rom , world , player , team )
2017-12-14 04:21:43 +00:00
2020-10-27 08:36:55 +00:00
rom . write_byte ( 0x18637C , 1 if world . remote_items [ player ] else 0 )
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__
2020-07-14 02:48:56 +00:00
rom . name = bytearray ( f ' BM { __version__ . replace ( " . " , " " ) [ 0 : 3 ] } _ { team + 1 } _ { player } _ { world . seed : 09 } \0 ' , ' utf8 ' ) [ : 21 ]
2020-01-14 09:42:27 +00:00
rom . name . extend ( [ 0 ] * ( 21 - len ( rom . name ) ) )
2019-04-18 09:23:24 +00:00
rom . write_bytes ( 0x7FC0 , rom . name )
2017-12-14 04:21:43 +00:00
2020-01-14 09:42:27 +00:00
# set player names
2020-06-07 11:26:20 +00:00
for p in range ( 1 , min ( world . players , 255 ) + 1 ) :
rom . write_bytes ( 0x195FFC + ( ( p - 1 ) * 32 ) , hud_format_text ( world . player_names [ p ] [ team ] ) )
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 )
2020-01-14 09:42:27 +00:00
rom . hash = code
2017-12-14 04:21:43 +00:00
return rom
2019-12-15 15:16:39 +00:00
2020-10-20 08:16:20 +00:00
def patch_race_rom ( rom , world , player ) :
2020-10-24 03:33:52 +00:00
rom . write_bytes ( 0x180213 , [ 0x01 , 0x00 ] ) # Tournament Seed
2020-10-20 08:16:20 +00:00
rom . encrypt ( world , player )
2019-12-15 15:16:39 +00:00
2020-10-24 03:33:52 +00:00
2019-04-18 09:23:24 +00:00
def write_custom_shops ( rom , world , player ) :
2020-01-10 10:41:22 +00:00
shops = [ shop for shop in world . shops if shop . custom 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
2020-10-24 03:33:52 +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 '
2020-06-07 18:52:03 +00:00
elif char == ' ' or char == ' _ ' :
2019-12-09 18:27:56 +00:00
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
2020-10-24 03:33:52 +00:00
def apply_rom_settings ( rom , beep , color , quickswap , fastmenu , disable_music , sprite : str , palettes_options ,
world = None , player = 1 , allow_random_on_event = False ) :
2020-07-16 10:40:47 +00:00
local_random = random if not world else world . rom_seeds [ player ]
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
2020-01-08 07:17:25 +00:00
rom . write_byte ( 0x0CFE18 , 0x00 if disable_music else rom . orig_buffer [ 0x0CFE18 ] if rom . orig_buffer else 0x70 )
rom . write_byte ( 0x0CFEC1 , 0x00 if disable_music else rom . orig_buffer [ 0x0CFEC1 ] if rom . orig_buffer else 0xC0 )
2020-10-24 03:33:52 +00:00
rom . write_bytes ( 0x0D0000 ,
[ 0x00 , 0x00 ] if disable_music else rom . orig_buffer [ 0x0D0000 : 0x0D0002 ] if rom . orig_buffer else [ 0xDA ,
0x58 ] )
rom . write_bytes ( 0x0D00E7 ,
[ 0xC4 , 0x58 ] if disable_music else rom . orig_buffer [ 0x0D00E7 : 0x0D00E9 ] if rom . orig_buffer else [ 0xDA ,
0x58 ] )
2018-01-27 20:01:16 +00:00
2019-08-26 02:36:19 +00:00
rom . write_byte ( 0x18021A , 1 if disable_music else 0x00 )
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 ' :
2020-07-16 10:40:47 +00:00
color = local_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 ] )
2020-08-23 10:06:00 +00:00
if z3pr :
2020-10-24 03:33:52 +00:00
def buildAndRandomize ( option_name , mode ) :
2020-10-24 00:44:27 +00:00
options = {
2020-10-24 03:33:52 +00:00
option_name : True
2020-10-24 00:44:27 +00:00
}
2020-08-25 11:27:34 +00:00
data_dir = local_path ( " data " ) if is_bundled ( ) else None
2020-09-13 15:09:28 +00:00
offsets_array = build_offset_collections ( options , data_dir )
2020-10-24 03:33:52 +00:00
restore_maseya_colors ( rom , offsets_array )
2020-10-24 00:44:27 +00:00
if mode == ' default ' :
return
2020-08-30 13:57:26 +00:00
ColorF = z3pr . ColorF
2020-10-24 03:33:52 +00:00
2020-08-30 13:57:26 +00:00
def next_color_generator ( ) :
while True :
yield ColorF ( local_random . random ( ) , local_random . random ( ) , local_random . random ( ) )
2020-10-24 03:33:52 +00:00
2020-10-24 00:44:27 +00:00
if mode == ' random ' :
mode = ' maseya '
z3pr . randomize ( rom . buffer , mode , offset_collections = offsets_array , random_colors = next_color_generator ( ) )
2020-10-24 03:33:52 +00:00
uw_palettes = palettes_options [ ' dungeon ' ]
ow_palettes = palettes_options [ ' overworld ' ]
hud_palettes = palettes_options [ ' hud ' ]
sword_palettes = palettes_options [ ' sword ' ]
shield_palettes = palettes_options [ ' shield ' ]
2020-11-06 07:31:10 +00:00
# link_palettes = palettes_options['link']
2020-10-24 00:44:27 +00:00
buildAndRandomize ( " randomize_dungeon " , uw_palettes )
buildAndRandomize ( " randomize_overworld " , ow_palettes )
buildAndRandomize ( " randomize_hud " , hud_palettes )
buildAndRandomize ( " randomize_sword " , sword_palettes )
buildAndRandomize ( " randomize_shield " , shield_palettes )
2020-10-24 03:33:52 +00:00
# link palette shuffle does not work very well and it's incompatible with random sprite on event
# buildAndRandomize("randomize_link_sprite", link_palettes)
2020-08-23 10:06:00 +00:00
else :
2020-10-24 03:33:52 +00:00
# reset palette if it was adjusted already
2020-10-24 00:44:27 +00:00
default_ow_palettes ( rom )
default_uw_palettes ( rom )
2020-08-23 10:06:00 +00:00
logging . warning ( " Could not find z3pr palette shuffle. "
" If you want improved palette shuffling please install the maseya-z3pr package. " )
2020-10-24 03:33:52 +00:00
if palettes_options [ ' overworld ' ] == ' random ' :
2020-08-23 10:06:00 +00:00
randomize_ow_palettes ( rom , local_random )
2020-10-24 03:33:52 +00:00
elif palettes_options [ ' overworld ' ] == ' blackout ' :
2020-10-24 00:44:27 +00:00
blackout_ow_palettes ( rom )
2020-01-08 02:43:48 +00:00
2020-10-24 03:33:52 +00:00
if palettes_options [ ' dungeon ' ] == ' blackout ' :
blackout_uw_palettes ( rom )
elif palettes_options [ ' dungeon ' ] == ' random ' :
randomize_uw_palettes ( rom , local_random )
2020-12-29 07:03:56 +00:00
apply_random_sprite_on_event ( rom , sprite , local_random , allow_random_on_event ,
world . sprite_pool [ player ] if world else [ ] )
2017-07-14 12:37:34 +00:00
if isinstance ( rom , LocalRom ) :
rom . write_crc ( )
2017-05-25 10:09:50 +00:00
2020-10-24 03:33:52 +00:00
def restore_maseya_colors ( rom , offsets_array ) :
2020-10-24 00:44:27 +00:00
if not rom . orig_buffer :
return
for offsetC in offsets_array :
for address in offsetC :
2020-10-24 03:33:52 +00:00
rom . write_bytes ( address , rom . orig_buffer [ address : address + 2 ] )
2017-05-25 10:09:50 +00:00
2020-01-08 02:43:48 +00:00
def set_color ( rom , address , color , shade ) :
r = round ( min ( color [ 0 ] , 0xFF ) * pow ( 0.8 , shade ) * 0x1F / 0xFF )
g = round ( min ( color [ 1 ] , 0xFF ) * pow ( 0.8 , shade ) * 0x1F / 0xFF )
b = round ( min ( color [ 2 ] , 0xFF ) * pow ( 0.8 , shade ) * 0x1F / 0xFF )
rom . write_bytes ( address , ( ( b << 10 ) | ( g << 5 ) | ( r << 0 ) ) . to_bytes ( 2 , byteorder = ' little ' , signed = False ) )
2020-10-24 03:33:52 +00:00
2020-01-08 02:43:48 +00:00
def default_ow_palettes ( rom ) :
if not rom . orig_buffer :
return
rom . write_bytes ( 0xDE604 , rom . orig_buffer [ 0xDE604 : 0xDEBB4 ] )
for address in [ 0x067FB4 , 0x067F94 , 0x067FC6 , 0x067FE6 , 0x067FE1 , 0x05FEA9 , 0x05FEB3 ] :
2020-10-24 03:33:52 +00:00
rom . write_bytes ( address , rom . orig_buffer [ address : address + 2 ] )
2020-01-08 02:43:48 +00:00
2020-07-16 10:40:47 +00:00
def randomize_ow_palettes ( rom , local_random ) :
2020-10-24 03:33:52 +00:00
grass , grass2 , grass3 , dirt , dirt2 , water , clouds , dwdirt , \
dwgrass , dwwater , dwdmdirt , dwdmgrass , dwdmclouds1 , dwdmclouds2 = [ [ local_random . randint ( 60 , 215 ) for _ in range ( 3 ) ]
for _ in range ( 14 ) ]
2020-07-16 10:40:47 +00:00
dwtree = [ c + local_random . randint ( - 20 , 10 ) for c in dwgrass ]
treeleaf = [ c + local_random . randint ( - 20 , 10 ) for c in grass ]
2020-01-08 02:43:48 +00:00
2020-10-24 03:33:52 +00:00
patches = { 0x067FB4 : ( grass , 0 ) , 0x067F94 : ( grass , 0 ) , 0x067FC6 : ( grass , 0 ) , 0x067FE6 : ( grass , 0 ) ,
0x067FE1 : ( grass , 3 ) , 0x05FEA9 : ( grass , 0 ) , 0x05FEB3 : ( dwgrass , 1 ) ,
0x0DD4AC : ( grass , 2 ) , 0x0DE6DE : ( grass2 , 2 ) , 0x0DE6E0 : ( grass2 , 1 ) , 0x0DD4AE : ( grass2 , 1 ) ,
0x0DE9FA : ( grass2 , 1 ) , 0x0DEA0E : ( grass2 , 1 ) , 0x0DE9FE : ( grass2 , 0 ) ,
0x0DD3D2 : ( grass2 , 2 ) , 0x0DE88C : ( grass2 , 2 ) , 0x0DE8A8 : ( grass2 , 2 ) , 0x0DE9F8 : ( grass2 , 2 ) ,
0x0DEA4E : ( grass2 , 2 ) , 0x0DEAF6 : ( grass2 , 2 ) , 0x0DEB2E : ( grass2 , 2 ) , 0x0DEB4A : ( grass2 , 2 ) ,
0x0DE892 : ( grass , 1 ) , 0x0DE886 : ( grass , 0 ) , 0x0DE6D2 : ( grass , 0 ) , 0x0DE6FA : ( grass , 3 ) ,
0x0DE6FC : ( grass , 0 ) , 0x0DE6FE : ( grass , 0 ) , 0x0DE70A : ( grass , 0 ) , 0x0DE708 : ( grass , 2 ) ,
0x0DE70C : ( grass , 1 ) ,
0x0DE6D4 : ( dirt , 2 ) , 0x0DE6CA : ( dirt , 5 ) , 0x0DE6CC : ( dirt , 4 ) , 0x0DE6CE : ( dirt , 3 ) , 0x0DE6E2 : ( dirt , 2 ) ,
0x0DE6D8 : ( dirt , 5 ) , 0x0DE6DA : ( dirt , 4 ) , 0x0DE6DC : ( dirt , 2 ) ,
0x0DE6F0 : ( dirt , 2 ) , 0x0DE6E6 : ( dirt , 5 ) , 0x0DE6E8 : ( dirt , 4 ) , 0x0DE6EA : ( dirt , 2 ) , 0x0DE6EC : ( dirt , 4 ) ,
0x0DE6EE : ( dirt , 2 ) ,
2020-01-08 02:43:48 +00:00
0x0DE91E : ( grass , 0 ) ,
0x0DE920 : ( dirt , 2 ) , 0x0DE916 : ( dirt , 3 ) , 0x0DE934 : ( dirt , 3 ) ,
2020-10-24 03:33:52 +00:00
0x0DE92C : ( grass , 0 ) , 0x0DE93A : ( grass , 0 ) , 0x0DE91C : ( grass , 1 ) , 0x0DE92A : ( grass , 1 ) ,
0x0DEA1C : ( grass , 0 ) , 0x0DEA2A : ( grass , 0 ) , 0x0DEA30 : ( grass , 0 ) ,
2020-01-08 02:43:48 +00:00
0x0DEA2E : ( dirt , 5 ) ,
2020-10-24 03:33:52 +00:00
0x0DE884 : ( grass , 3 ) , 0x0DE8AE : ( grass , 3 ) , 0x0DE8BE : ( grass , 3 ) , 0x0DE8E4 : ( grass , 3 ) ,
0x0DE938 : ( grass , 3 ) , 0x0DE9C4 : ( grass , 3 ) , 0x0DE6D0 : ( grass , 4 ) ,
2020-01-08 02:43:48 +00:00
0x0DE890 : ( treeleaf , 1 ) , 0x0DE894 : ( treeleaf , 0 ) ,
2020-10-24 03:33:52 +00:00
0x0DE924 : ( water , 3 ) , 0x0DE668 : ( water , 3 ) , 0x0DE66A : ( water , 2 ) , 0x0DE670 : ( water , 1 ) ,
0x0DE918 : ( water , 1 ) , 0x0DE66C : ( water , 0 ) , 0x0DE91A : ( water , 0 ) , 0x0DE92E : ( water , 1 ) ,
0x0DEA1A : ( water , 1 ) , 0x0DEA16 : ( water , 3 ) , 0x0DEA10 : ( water , 4 ) ,
2020-01-08 02:43:48 +00:00
0x0DE66E : ( dirt , 3 ) , 0x0DE672 : ( dirt , 2 ) , 0x0DE932 : ( dirt , 4 ) , 0x0DE936 : ( dirt , 2 ) , 0x0DE93C : ( dirt , 1 ) ,
2020-10-24 03:33:52 +00:00
0x0DE756 : ( dirt2 , 4 ) , 0x0DE764 : ( dirt2 , 4 ) , 0x0DE772 : ( dirt2 , 4 ) , 0x0DE994 : ( dirt2 , 4 ) ,
0x0DE9A2 : ( dirt2 , 4 ) , 0x0DE758 : ( dirt2 , 3 ) , 0x0DE766 : ( dirt2 , 3 ) , 0x0DE774 : ( dirt2 , 3 ) ,
0x0DE996 : ( dirt2 , 3 ) , 0x0DE9A4 : ( dirt2 , 3 ) , 0x0DE75A : ( dirt2 , 2 ) , 0x0DE768 : ( dirt2 , 2 ) ,
0x0DE776 : ( dirt2 , 2 ) , 0x0DE778 : ( dirt2 , 2 ) , 0x0DE998 : ( dirt2 , 2 ) , 0x0DE9A6 : ( dirt2 , 2 ) ,
0x0DE9AC : ( dirt2 , 1 ) , 0x0DE99E : ( dirt2 , 1 ) , 0x0DE760 : ( dirt2 , 1 ) , 0x0DE77A : ( dirt2 , 1 ) ,
0x0DE77C : ( dirt2 , 1 ) , 0x0DE798 : ( dirt2 , 1 ) , 0x0DE980 : ( dirt2 , 1 ) ,
0x0DE75C : ( grass3 , 2 ) , 0x0DE786 : ( grass3 , 2 ) , 0x0DE794 : ( grass3 , 2 ) , 0x0DE99A : ( grass3 , 2 ) ,
0x0DE75E : ( grass3 , 1 ) , 0x0DE788 : ( grass3 , 1 ) , 0x0DE796 : ( grass3 , 1 ) , 0x0DE99C : ( grass3 , 1 ) ,
0x0DE76A : ( clouds , 2 ) , 0x0DE9A8 : ( clouds , 2 ) , 0x0DE76E : ( clouds , 0 ) , 0x0DE9AA : ( clouds , 0 ) ,
0x0DE8DA : ( clouds , 0 ) , 0x0DE8D8 : ( clouds , 0 ) , 0x0DE8D0 : ( clouds , 0 ) , 0x0DE98C : ( clouds , 2 ) ,
0x0DE990 : ( clouds , 0 ) ,
2020-01-08 02:43:48 +00:00
0x0DEB34 : ( dwtree , 4 ) , 0x0DEB30 : ( dwtree , 3 ) , 0x0DEB32 : ( dwtree , 1 ) ,
2020-10-24 03:33:52 +00:00
0x0DE710 : ( dwdirt , 5 ) , 0x0DE71E : ( dwdirt , 5 ) , 0x0DE72C : ( dwdirt , 5 ) , 0x0DEAD6 : ( dwdirt , 5 ) ,
0x0DE712 : ( dwdirt , 4 ) , 0x0DE720 : ( dwdirt , 4 ) , 0x0DE72E : ( dwdirt , 4 ) , 0x0DE660 : ( dwdirt , 4 ) ,
0x0DEAD8 : ( dwdirt , 4 ) , 0x0DEADA : ( dwdirt , 3 ) , 0x0DE714 : ( dwdirt , 3 ) , 0x0DE722 : ( dwdirt , 3 ) ,
0x0DE730 : ( dwdirt , 3 ) , 0x0DE732 : ( dwdirt , 3 ) , 0x0DE734 : ( dwdirt , 2 ) , 0x0DE736 : ( dwdirt , 2 ) ,
2020-01-08 02:43:48 +00:00
0x0DE728 : ( dwdirt , 2 ) , 0x0DE71A : ( dwdirt , 2 ) , 0x0DE664 : ( dwdirt , 2 ) , 0x0DEAE0 : ( dwdirt , 2 ) ,
2020-10-24 03:33:52 +00:00
0x0DE716 : ( dwgrass , 3 ) , 0x0DE740 : ( dwgrass , 3 ) , 0x0DE74E : ( dwgrass , 3 ) , 0x0DEAC0 : ( dwgrass , 3 ) ,
0x0DEACE : ( dwgrass , 3 ) , 0x0DEADC : ( dwgrass , 3 ) , 0x0DEB24 : ( dwgrass , 3 ) , 0x0DE752 : ( dwgrass , 2 ) ,
0x0DE718 : ( dwgrass , 1 ) , 0x0DE742 : ( dwgrass , 1 ) , 0x0DE750 : ( dwgrass , 1 ) , 0x0DEB26 : ( dwgrass , 1 ) ,
0x0DEAC2 : ( dwgrass , 1 ) , 0x0DEAD0 : ( dwgrass , 1 ) , 0x0DEADE : ( dwgrass , 1 ) ,
0x0DE65A : ( dwwater , 5 ) , 0x0DE65C : ( dwwater , 3 ) , 0x0DEAC8 : ( dwwater , 3 ) , 0x0DEAD2 : ( dwwater , 2 ) ,
0x0DEABC : ( dwwater , 2 ) , 0x0DE662 : ( dwwater , 2 ) , 0x0DE65E : ( dwwater , 1 ) , 0x0DEABE : ( dwwater , 1 ) ,
0x0DEA98 : ( dwwater , 2 ) ,
0x0DE79A : ( dwdmdirt , 6 ) , 0x0DE7A8 : ( dwdmdirt , 6 ) , 0x0DE7B6 : ( dwdmdirt , 6 ) , 0x0DEB60 : ( dwdmdirt , 6 ) ,
0x0DEB6E : ( dwdmdirt , 6 ) , 0x0DE93E : ( dwdmdirt , 6 ) , 0x0DE94C : ( dwdmdirt , 6 ) , 0x0DEBA6 : ( dwdmdirt , 6 ) ,
0x0DE79C : ( dwdmdirt , 4 ) , 0x0DE7AA : ( dwdmdirt , 4 ) , 0x0DE7B8 : ( dwdmdirt , 4 ) , 0x0DEB70 : ( dwdmdirt , 4 ) ,
0x0DEBA8 : ( dwdmdirt , 4 ) , 0x0DEB72 : ( dwdmdirt , 3 ) , 0x0DEB74 : ( dwdmdirt , 3 ) , 0x0DE79E : ( dwdmdirt , 3 ) ,
0x0DE7AC : ( dwdmdirt , 3 ) , 0x0DEBAA : ( dwdmdirt , 3 ) , 0x0DE7A0 : ( dwdmdirt , 3 ) ,
2020-01-08 02:43:48 +00:00
0x0DE7BC : ( dwdmgrass , 3 ) ,
2020-10-24 03:33:52 +00:00
0x0DEBAC : ( dwdmdirt , 2 ) , 0x0DE7AE : ( dwdmdirt , 2 ) , 0x0DE7C2 : ( dwdmdirt , 2 ) , 0x0DE7A6 : ( dwdmdirt , 2 ) ,
0x0DEB7A : ( dwdmdirt , 2 ) , 0x0DEB6C : ( dwdmdirt , 2 ) , 0x0DE7C0 : ( dwdmdirt , 2 ) ,
0x0DE7A2 : ( dwdmgrass , 3 ) , 0x0DE7BE : ( dwdmgrass , 3 ) , 0x0DE7CC : ( dwdmgrass , 3 ) , 0x0DE7DA : ( dwdmgrass , 3 ) ,
0x0DEB6A : ( dwdmgrass , 3 ) , 0x0DE948 : ( dwdmgrass , 3 ) , 0x0DE956 : ( dwdmgrass , 3 ) , 0x0DE964 : ( dwdmgrass , 3 ) ,
0x0DE7CE : ( dwdmgrass , 1 ) , 0x0DE7A4 : ( dwdmgrass , 1 ) , 0x0DEBA2 : ( dwdmgrass , 1 ) , 0x0DEBB0 : ( dwdmgrass , 1 ) ,
0x0DE644 : ( dwdmclouds1 , 2 ) , 0x0DEB84 : ( dwdmclouds1 , 2 ) , 0x0DE648 : ( dwdmclouds1 , 1 ) ,
0x0DEB88 : ( dwdmclouds1 , 1 ) ,
0x0DEBAE : ( dwdmclouds2 , 2 ) , 0x0DE7B0 : ( dwdmclouds2 , 2 ) , 0x0DE7B4 : ( dwdmclouds2 , 0 ) ,
0x0DEB78 : ( dwdmclouds2 , 0 ) , 0x0DEBB2 : ( dwdmclouds2 , 0 )
2020-01-08 02:43:48 +00:00
}
for address , ( color , shade ) in patches . items ( ) :
set_color ( rom , address , color , shade )
2020-10-24 03:33:52 +00:00
2020-01-08 02:43:48 +00:00
def blackout_ow_palettes ( rom ) :
rom . write_bytes ( 0xDE604 , [ 0 ] * 0xC4 )
for i in range ( 0xDE6C8 , 0xDE86C , 70 ) :
rom . write_bytes ( i , [ 0 ] * 64 )
2020-10-24 03:33:52 +00:00
rom . write_bytes ( i + 66 , [ 0 ] * 4 )
2020-01-08 02:43:48 +00:00
rom . write_bytes ( 0xDE86C , [ 0 ] * 0x348 )
for address in [ 0x067FB4 , 0x067F94 , 0x067FC6 , 0x067FE6 , 0x067FE1 , 0x05FEA9 , 0x05FEB3 ] :
2020-10-24 03:33:52 +00:00
rom . write_bytes ( address , [ 0 , 0 ] )
2020-01-08 02:43:48 +00:00
def default_uw_palettes ( rom ) :
if not rom . orig_buffer :
return
rom . write_bytes ( 0xDD734 , rom . orig_buffer [ 0xDD734 : 0xDE544 ] )
2020-10-24 03:33:52 +00:00
2020-07-16 10:40:47 +00:00
def randomize_uw_palettes ( rom , local_random ) :
2020-01-08 02:43:48 +00:00
for dungeon in range ( 20 ) :
2020-07-16 10:40:47 +00:00
wall , pot , chest , floor1 , floor2 , floor3 = [ [ local_random . randint ( 60 , 240 ) for _ in range ( 3 ) ] for _ in range ( 6 ) ]
2020-01-08 02:43:48 +00:00
for i in range ( 5 ) :
shade = 10 - ( i * 2 )
set_color ( rom , 0x0DD734 + ( 0xB4 * dungeon ) + ( i * 2 ) , wall , shade )
set_color ( rom , 0x0DD770 + ( 0xB4 * dungeon ) + ( i * 2 ) , wall , shade )
set_color ( rom , 0x0DD744 + ( 0xB4 * dungeon ) + ( i * 2 ) , wall , shade )
if dungeon == 0 :
set_color ( rom , 0x0DD7CA + ( 0xB4 * dungeon ) + ( i * 2 ) , wall , shade )
if dungeon == 2 :
set_color ( rom , 0x0DD74E + ( 0xB4 * dungeon ) , wall , 3 )
set_color ( rom , 0x0DD750 + ( 0xB4 * dungeon ) , wall , 5 )
set_color ( rom , 0x0DD73E + ( 0xB4 * dungeon ) , wall , 3 )
set_color ( rom , 0x0DD740 + ( 0xB4 * dungeon ) , wall , 5 )
set_color ( rom , 0x0DD7E4 + ( 0xB4 * dungeon ) , wall , 4 )
set_color ( rom , 0x0DD7E6 + ( 0xB4 * dungeon ) , wall , 2 )
set_color ( rom , 0xDD7DA + ( 0xB4 * dungeon ) , wall , 10 )
set_color ( rom , 0xDD7DC + ( 0xB4 * dungeon ) , wall , 8 )
set_color ( rom , 0x0DD75A + ( 0xB4 * dungeon ) , pot , 7 )
set_color ( rom , 0x0DD75C + ( 0xB4 * dungeon ) , pot , 1 )
set_color ( rom , 0x0DD75E + ( 0xB4 * dungeon ) , pot , 3 )
set_color ( rom , 0x0DD76A + ( 0xB4 * dungeon ) , wall , 7 )
set_color ( rom , 0x0DD76C + ( 0xB4 * dungeon ) , wall , 2 )
set_color ( rom , 0x0DD76E + ( 0xB4 * dungeon ) , wall , 4 )
set_color ( rom , 0x0DD7AE + ( 0xB4 * dungeon ) , chest , 2 )
set_color ( rom , 0x0DD7B0 + ( 0xB4 * dungeon ) , chest , 0 )
for i in range ( 3 ) :
shade = 6 - ( i * 2 )
set_color ( rom , 0x0DD764 + ( 0xB4 * dungeon ) + ( i * 2 ) , floor1 , shade )
set_color ( rom , 0x0DD782 + ( 0xB4 * dungeon ) + ( i * 2 ) , floor1 , shade + 3 )
set_color ( rom , 0x0DD7A0 + ( 0xB4 * dungeon ) + ( i * 2 ) , floor2 , shade )
set_color ( rom , 0x0DD7BE + ( 0xB4 * dungeon ) + ( i * 2 ) , floor2 , shade + 3 )
set_color ( rom , 0x0DD7E2 + ( 0xB4 * dungeon ) , floor3 , 3 )
set_color ( rom , 0x0DD796 + ( 0xB4 * dungeon ) , floor3 , 4 )
2020-10-24 03:33:52 +00:00
2020-01-08 02:43:48 +00:00
def blackout_uw_palettes ( rom ) :
for i in range ( 0xDD734 , 0xDE544 , 180 ) :
rom . write_bytes ( i , [ 0 ] * 38 )
2020-10-24 03:33:52 +00:00
rom . write_bytes ( i + 44 , [ 0 ] * 76 )
rom . write_bytes ( i + 136 , [ 0 ] * 44 )
2017-05-25 10:09:50 +00:00
2020-01-14 09:42:27 +00:00
def get_hash_string ( hash ) :
return " , " . join ( [ hash_alphabet [ code & 0x1F ] for code in hash ] )
2017-05-25 10:09:50 +00:00
2020-10-24 03:33:52 +00:00
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
2020-01-14 09:42:27 +00:00
def write_strings ( rom , world , player , team ) :
2020-07-16 06:01:29 +00:00
local_random = world . rom_seeds [ 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.
2019-12-16 17:24:34 +00:00
if world . shuffle [ player ] 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 ) :
2019-12-31 14:47:49 +00:00
if not dest :
return " nothing "
if ped_hint :
hint = dest . pedestal_hint_text if dest . pedestal_hint_text else " unknown item "
else :
hint = dest . hint_text if dest . hint_text else " something "
2019-04-18 09:23:24 +00:00
if dest . player != player :
if ped_hint :
2020-01-14 09:42:27 +00:00
hint + = f " for { world . player_names [ dest . player ] [ team ] } ! "
2019-04-18 09:23:24 +00:00
elif type ( dest ) in [ Region , Location ] :
2020-01-14 09:42:27 +00:00
hint + = f " in { world . player_names [ dest . player ] [ team ] } ' s world "
2019-12-31 14:47:49 +00:00
else :
2020-01-14 09:42:27 +00:00
hint + = f " for { world . player_names [ dest . player ] [ team ] } "
2019-04-18 09:23:24 +00:00
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-12-17 11:22:55 +00:00
if world . hints [ player ] :
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 ( )
2020-07-16 06:01:29 +00:00
local_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 ]
2020-07-16 06:01:29 +00:00
local_random . shuffle ( all_entrances )
2019-09-16 05:51:24 +00:00
2020-10-24 03:33:52 +00:00
# First we take care of the one inconvenient dungeon in the appropriately simple shuffles.
2019-09-16 05:51:24 +00:00
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-12-16 17:24:34 +00:00
if world . shuffle [ player ] in [ ' simple ' , ' restricted ' , ' restricted_legacy ' ] :
2019-09-16 05:51:24 +00:00
for entrance in all_entrances :
if entrance . name in entrances_to_hint :
2020-10-24 03:33:52 +00:00
this_hint = entrances_to_hint [ entrance . name ] + ' leads to ' + hint_text (
entrance . connected_region ) + ' . '
2019-09-16 05:51:24 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
entrances_to_hint = { }
break
2020-10-24 03:33:52 +00:00
# Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones.
2019-09-16 05:51:24 +00:00
entrances_to_hint . update ( InconvenientOtherEntrances )
2019-12-16 17:24:34 +00:00
if world . shuffle [ player ] in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
2019-09-16 05:51:24 +00:00
hint_count = 0
2019-12-16 17:24:34 +00:00
elif world . shuffle [ player ] in [ ' simple ' , ' restricted ' , ' restricted_legacy ' ] :
2019-09-16 05:51:24 +00:00
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 :
2020-10-24 03:33:52 +00:00
this_hint = entrances_to_hint [ entrance . name ] + ' leads to ' + hint_text (
entrance . connected_region ) + ' . '
2019-04-18 09:23:24 +00:00
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
2020-10-24 03:33:52 +00:00
# Next we handle hints for randomly selected other entrances, curating the selection intelligently based on shuffle.
2019-12-16 17:24:34 +00:00
if world . shuffle [ player ] not in [ ' simple ' , ' restricted ' , ' restricted_legacy ' ] :
2019-09-16 05:51:24 +00:00
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-12-16 17:24:34 +00:00
elif world . shuffle [ player ] == ' restricted ' :
2019-09-16 05:51:24 +00:00
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-12-16 17:24:34 +00:00
if world . shuffle [ player ] in [ ' insanity ' , ' madness_legacy ' , ' insanity_legacy ' ] :
2019-01-23 09:04:42 +00:00
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-12-16 17:24:34 +00:00
hint_count = 4 if world . shuffle [ player ] 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 :
2020-10-24 03:33:52 +00:00
this_hint = entrances_to_hint [ entrance . name ] + ' leads to ' + hint_text (
entrance . connected_region ) + ' . '
2019-04-18 09:23:24 +00:00
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-12-16 17:24:34 +00:00
if world . shuffle [ player ] in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
2019-09-16 05:51:24 +00:00
locations_to_hint . extend ( InconvenientVanillaLocations )
2020-07-16 06:01:29 +00:00
local_random . shuffle ( locations_to_hint )
2019-12-16 17:24:34 +00:00
hint_count = 3 if world . shuffle [ player ] 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 ' :
2020-07-16 06:01:29 +00:00
if local_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 ' :
2020-07-16 06:01:29 +00:00
if local_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 ' :
2020-10-24 03:33:52 +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 ' :
2020-10-24 03:33:52 +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 ' :
2020-10-24 03:33:52 +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 ' :
2020-10-24 03:33:52 +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 ' :
2020-10-24 03:33:52 +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 ' :
2020-10-24 03:33:52 +00:00
this_hint = ' Sahasrahla seeks a green pendant for ' + hint_text (
world . get_location ( location , player ) . item ) + ' . '
2019-09-16 05:51:24 +00:00
tt [ hint_locations . pop ( 0 ) ] = this_hint
elif location == ' Graveyard Cave ' :
2020-10-24 03:33:52 +00:00
this_hint = ' The cave north of the graveyard contains ' + hint_text (
world . get_location ( location , player ) . item ) + ' . '
2019-09-16 05:51:24 +00:00
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-16 20:46:47 +00:00
if world . keyshuffle [ player ] :
2019-12-13 21:37:52 +00:00
items_to_hint . extend ( SmallKeys )
2019-12-16 20:46:47 +00:00
if world . bigkeyshuffle [ player ] :
2019-12-13 21:37:52 +00:00
items_to_hint . extend ( BigKeys )
2020-07-16 06:01:29 +00:00
local_random . shuffle ( items_to_hint )
2019-12-16 17:24:34 +00:00
hint_count = 5 if world . shuffle [ player ] 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 )
2020-07-16 06:01:29 +00:00
local_random . shuffle ( this_location )
2020-10-24 03:33:52 +00:00
# This looks dumb but prevents hints for Skull Woods Pinball Room's key safely with any item pool.
2019-09-16 05:51:24 +00:00
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 ( )
2020-07-16 06:01:29 +00:00
local_random . shuffle ( junk_hints )
2019-01-23 09:04:42 +00:00
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.
2020-06-30 07:51:11 +00:00
silverarrows = world . find_items ( ' Silver Bow ' , player )
2020-07-16 06:01:29 +00:00
local_random . shuffle ( silverarrows )
2020-10-24 03:33:52 +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 )
2020-10-24 03:33:52 +00:00
progressive_silvers = world . difficulty_requirements [ player ] . progressive_bow_limit > = 2 or (
world . swords [ player ] == ' swordless ' or world . logic [ player ] == ' noglitches ' )
2019-08-22 02:40:19 +00:00
if distinguished_prog_bow_loc :
prog_bow_locs . remove ( distinguished_prog_bow_loc )
2020-10-24 03:33:52 +00:00
silverarrow_hint = ( ' %s ? ' % hint_text ( distinguished_prog_bow_loc ) . replace ( ' Ganon \' s ' ,
' my ' ) ) if progressive_silvers else ' ? \n I think not! '
2020-05-05 08:56:41 +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
if any ( prog_bow_locs ) :
2020-07-16 06:01:29 +00:00
silverarrow_hint = ( ' %s ? ' % hint_text ( local_random . choice ( prog_bow_locs ) ) . replace ( ' Ganon \' s ' ,
2020-07-14 05:01:51 +00:00
' my ' ) ) if progressive_silvers else ' ? \n I think not! '
2020-05-05 08:56:41 +00:00
tt [ ' ganon_phase_3_no_silvers_alt ' ] = ' Did you find the silver arrows %s ' % silverarrow_hint
2019-04-18 09:23:24 +00:00
crystal5 = world . find_items ( ' Crystal 5 ' , player ) [ 0 ]
crystal6 = world . find_items ( ' Crystal 6 ' , player ) [ 0 ]
2020-10-24 03:33:52 +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
2020-11-24 18:40:05 +00:00
if world . crystals_needed_for_gt [ player ] == 1 :
tt [ ' sign_ganons_tower ' ] = ' You need a crystal to enter. '
else :
tt [ ' sign_ganons_tower ' ] = f ' You need { world . crystals_needed_for_gt [ player ] } crystals to enter. '
2019-08-04 16:32:35 +00:00
2020-09-11 01:37:28 +00:00
if world . goal [ player ] == ' dungeons ' :
2019-08-24 19:36:54 +00:00
tt [ ' sign_ganon ' ] = ' You need to complete all the dungeons. '
2020-10-17 03:25:51 +00:00
elif world . goal [ player ] == ' ganonpedestal ' :
2020-10-15 22:24:52 +00:00
tt [ ' sign_ganon ' ] = ' You need to pull the pedestal to defeat Ganon. '
2020-10-24 03:33:52 +00:00
elif world . goal [ player ] == " ganon " :
2020-09-11 01:37:28 +00:00
if world . crystals_needed_for_ganon [ player ] == 1 :
2020-10-18 21:33:17 +00:00
tt [ ' sign_ganon ' ] = ' You need a crystal to beat Ganon and have beaten Agahnim atop Ganons Tower. '
2020-09-11 01:37:28 +00:00
else :
tt [ ' sign_ganon ' ] = f ' You need { world . crystals_needed_for_ganon [ player ] } crystals to beat Ganon and ' \
f ' have beaten Agahnim atop Ganons Tower '
else :
if world . crystals_needed_for_ganon [ player ] == 1 :
2020-10-18 21:33:17 +00:00
tt [ ' sign_ganon ' ] = ' You need a crystal to beat Ganon. '
2020-09-11 01:37:28 +00:00
else :
tt [ ' sign_ganon ' ] = f ' You need { world . crystals_needed_for_ganon [ player ] } crystals to beat Ganon. '
2019-08-24 19:36:54 +00:00
2020-07-16 06:01:29 +00:00
tt [ ' uncle_leaving_text ' ] = Uncle_texts [ local_random . randint ( 0 , len ( Uncle_texts ) - 1 ) ]
tt [ ' end_triforce ' ] = " {NOBORDER} \n " + Triforce_texts [ local_random . randint ( 0 , len ( Triforce_texts ) - 1 ) ]
tt [ ' bomb_shop_big_bomb ' ] = BombShop2_texts [ local_random . randint ( 0 , len ( BombShop2_texts ) - 1 ) ]
2018-04-19 01:17:40 +00:00
2018-07-24 23:22:47 +00:00
# this is what shows after getting the green pendant item in rando
2020-07-16 06:01:29 +00:00
tt [ ' sahasrahla_quest_have_master_sword ' ] = Sahasrahla2_texts [ local_random . randint ( 0 , len ( Sahasrahla2_texts ) - 1 ) ]
tt [ ' blind_by_the_light ' ] = Blind_texts [ local_random . randint ( 0 , len ( Blind_texts ) - 1 ) ]
2018-04-19 01:17:40 +00:00
2020-06-03 00:19:16 +00:00
if world . goal [ player ] in [ ' triforcehunt ' , ' localtriforcehunt ' ] :
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. '
2020-06-10 01:34:07 +00:00
if world . goal [ player ] == ' triforcehunt ' and world . players > 1 :
tt [ ' sign_ganon ' ] = ' Go find the Triforce pieces with your friends... Ganon is invincible! '
else :
tt [ ' sign_ganon ' ] = ' Go find the Triforce pieces... Ganon is invincible! '
2020-09-12 21:22:07 +00:00
if world . treasure_hunt_count [ player ] > 1 :
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 out of %d , I can reassemble it. " % \
( world . treasure_hunt_count [ player ] , world . triforce_pieces_available [ player ] )
else :
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 piece out of %d , I can reassemble it. " % \
( world . treasure_hunt_count [ player ] , world . triforce_pieces_available [ player ] )
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 :
2020-07-16 06:01:29 +00:00
tt [ ' ganon_fall_in ' ] = Ganon1_texts [ local_random . randint ( 0 , len ( Ganon1_texts ) - 1 ) ]
2018-04-19 01:17:40 +00:00
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! '
2020-09-12 21:22:07 +00:00
if world . treasure_hunt_count [ player ] > 1 :
if world . goal [ player ] == ' ganontriforcehunt ' and world . players > 1 :
tt [ ' sign_ganon ' ] = ' You need to find %d Triforce pieces out of %d with your friends to defeat Ganon. ' % \
( world . treasure_hunt_count [ player ] , world . triforce_pieces_available [ player ] )
elif world . goal [ player ] in [ ' ganontriforcehunt ' , ' localganontriforcehunt ' ] :
tt [ ' sign_ganon ' ] = ' You need to find %d Triforce pieces out of %d to defeat Ganon. ' % \
( world . treasure_hunt_count [ player ] , world . triforce_pieces_available [ player ] )
else :
if world . goal [ player ] == ' ganontriforcehunt ' and world . players > 1 :
tt [ ' sign_ganon ' ] = ' You need to find %d Triforce piece out of %d with your friends to defeat Ganon. ' % \
( world . treasure_hunt_count [ player ] , world . triforce_pieces_available [ player ] )
elif world . goal [ player ] in [ ' ganontriforcehunt ' , ' localganontriforcehunt ' ] :
tt [ ' sign_ganon ' ] = ' You need to find %d Triforce piece out of %d to defeat Ganon. ' % \
( world . treasure_hunt_count [ player ] , world . triforce_pieces_available [ player ] )
2019-08-24 19:36:54 +00:00
2020-07-16 06:01:29 +00:00
tt [ ' kakariko_tavern_fisherman ' ] = TavernMan_texts [ local_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
2020-10-24 03:33:52 +00:00
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
2020-10-24 03:33:52 +00:00
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
2020-10-24 03:33:52 +00:00
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} "
2020-09-01 19:53:06 +00:00
2021-01-02 15:44:58 +00:00
for at , text in world . plando_texts [ player ] . items ( ) :
if at not in tt :
raise Exception ( f " No text target \" { at } \" found. " )
else :
tt [ at ] = text
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
2020-07-16 06:01:29 +00:00
sickkiditem_text = local_random . choice (
2020-07-14 05:01:51 +00:00
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
2020-07-16 06:01:29 +00:00
zoraitem_text = local_random . choice (
2020-07-14 05:01:51 +00:00
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
2020-07-16 06:01:29 +00:00
magicshopitem_text = local_random . choice (
2020-07-14 05:01:51 +00:00
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
2020-07-16 06:01:29 +00:00
fluteboyitem_text = local_random . choice (
2020-07-14 05:01:51 +00:00
FluteBoy_texts ) if fluteboyitem is None or fluteboyitem . fluteboy_credit_text is None else fluteboyitem . fluteboy_credit_text
2017-11-05 04:06:00 +00:00
2020-07-16 06:01:29 +00:00
credits . update_credits_line ( ' castle ' , 0 , local_random . choice ( KingsReturn_texts ) )
credits . update_credits_line ( ' sanctuary ' , 0 , local_random . choice ( Sanctuary_texts ) )
2018-01-02 02:17:17 +00:00
2020-07-14 05:01:51 +00:00
credits . update_credits_line ( ' kakariko ' , 0 ,
2020-07-16 06:01:29 +00:00
local_random . choice ( Kakariko_texts ) . format ( local_random . choice ( Sahasrahla_names ) ) )
credits . update_credits_line ( ' desert ' , 0 , local_random . choice ( DesertPalace_texts ) )
credits . update_credits_line ( ' hera ' , 0 , local_random . choice ( MountainTower_texts ) )
credits . update_credits_line ( ' house ' , 0 , local_random . choice ( LinksHouse_texts ) )
2017-11-05 04:06:00 +00:00
credits . update_credits_line ( ' zora ' , 0 , zoraitem_text )
credits . update_credits_line ( ' witch ' , 0 , magicshopitem_text )
2020-07-16 06:01:29 +00:00
credits . update_credits_line ( ' lumberjacks ' , 0 , local_random . choice ( Lumberjacks_texts ) )
2017-11-05 04:06:00 +00:00
credits . update_credits_line ( ' grove ' , 0 , fluteboyitem_text )
2020-07-16 06:01:29 +00:00
credits . update_credits_line ( ' well ' , 0 , local_random . choice ( WishingWell_texts ) )
credits . update_credits_line ( ' smithy ' , 0 , local_random . choice ( Blacksmiths_texts ) )
2017-11-05 04:06:00 +00:00
credits . update_credits_line ( ' kakariko2 ' , 0 , sickkiditem_text )
2020-07-16 06:01:29 +00:00
credits . update_credits_line ( ' bridge ' , 0 , local_random . choice ( DeathMountain_texts ) )
credits . update_credits_line ( ' woods ' , 0 , local_random . choice ( LostWoods_texts ) )
2017-11-05 04:06:00 +00:00
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
2020-10-24 03:33:52 +00:00
2019-12-16 17:24:34 +00:00
def set_inverted_mode ( world , player , rom ) :
2019-07-27 13:13:13 +00:00
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
2020-08-21 16:35:48 +00:00
rom . write_int16s ( snes_to_pc ( 0x02E849 ) ,
[ 0x0043 , 0x0056 , 0x0058 , 0x006C , 0x006F , 0x0070 , 0x007B , 0x007F , 0x001B ] ) # dw flute
rom . write_int16 ( snes_to_pc ( 0x02E8D5 ) , 0x07C8 )
rom . write_int16 ( 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
2019-12-16 17:24:34 +00:00
if world . shuffle [ player ] == ' vanilla ' :
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x23 , 0x37 ) # switch AT and GT
rom . write_byte ( 0xDBB73 + 0x36 , 0x24 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15AEE + 2 * 0x38 , 0x00E0 )
rom . write_int16 ( 0x15AEE + 2 * 0x25 , 0x000C )
2019-12-16 17:24:34 +00:00
if world . shuffle [ player ] in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
2019-09-22 02:10:19 +00:00
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
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15AEE + 2 * 0x17 , 0x00F0 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x05 , 0x16 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15AEE + 2 * 0x07 , 0x00FB )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x2D , 0x17 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15AEE + 2 * 0x2F , 0x00EB )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0xDBB73 + 0x06 , 0x2E )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 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
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15AEE + 2 * 0x18 , 0x00F1 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x15B8C + 0x18 , 0x43 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15BDB + 2 * 0x18 , 0x1400 )
rom . write_int16 ( 0x15C79 + 2 * 0x18 , 0x0294 )
rom . write_int16 ( 0x15D17 + 2 * 0x18 , 0x0600 )
rom . write_int16 ( 0x15DB5 + 2 * 0x18 , 0x02E8 )
rom . write_int16 ( 0x15E53 + 2 * 0x18 , 0x0678 )
rom . write_int16 ( 0x15EF1 + 2 * 0x18 , 0x0303 )
rom . write_int16 ( 0x15F8F + 2 * 0x18 , 0x0685 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x1602D + 0x18 , 0x0A )
rom . write_byte ( 0x1607C + 0x18 , 0xF6 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x160CB + 2 * 0x18 , 0x0000 )
rom . write_int16 ( 0x16169 + 2 * 0x18 , 0x0000 )
rom . write_int16 ( 0x15AEE + 2 * 0x3D , 0x0003 ) # pyramid exit and houlihan
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x15B8C + 0x3D , 0x5B )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15BDB + 2 * 0x3D , 0x0B0E )
rom . write_int16 ( 0x15C79 + 2 * 0x3D , 0x075A )
rom . write_int16 ( 0x15D17 + 2 * 0x3D , 0x0674 )
rom . write_int16 ( 0x15DB5 + 2 * 0x3D , 0x07A8 )
rom . write_int16 ( 0x15E53 + 2 * 0x3D , 0x06E8 )
rom . write_int16 ( 0x15EF1 + 2 * 0x3D , 0x07C7 )
rom . write_int16 ( 0x15F8F + 2 * 0x3D , 0x06F3 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x1602D + 0x3D , 0x06 )
rom . write_byte ( 0x1607C + 0x3D , 0xFA )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x160CB + 2 * 0x3D , 0x0000 )
rom . write_int16 ( 0x16169 + 2 * 0x3D , 0x0000 )
rom . write_int16 ( 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 ] )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( snes_to_pc ( 0x02D91A ) , 0x0400 )
rom . write_int16 ( snes_to_pc ( 0x02D928 ) , 0x222E )
rom . write_int16 ( snes_to_pc ( 0x02D936 ) , 0x229A )
rom . write_int16 ( snes_to_pc ( 0x02D944 ) , 0x0480 )
rom . write_int16 ( snes_to_pc ( 0x02D952 ) , 0x00A5 )
rom . write_int16 ( 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 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( snes_to_pc ( 0x02D998 ) , 0x0000 )
rom . write_int16 ( 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-12-16 17:24:34 +00:00
if world . shuffle [ player ] in [ ' vanilla ' , ' dungeonsfull ' , ' dungeonssimple ' ] :
2019-07-27 13:13:13 +00:00
rom . write_bytes ( snes_to_pc ( 0x308350 ) , [ 0x00 , 0x00 , 0x01 ] )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 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 ] )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( snes_to_pc ( 0x02D924 ) , 0x0300 )
rom . write_int16 ( snes_to_pc ( 0x02D932 ) , 0x1F10 )
rom . write_int16 ( snes_to_pc ( 0x02D940 ) , 0x1FC0 )
rom . write_int16 ( snes_to_pc ( 0x02D94E ) , 0x0378 )
rom . write_int16 ( snes_to_pc ( 0x02D95C ) , 0x0187 )
rom . write_int16 ( 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 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( snes_to_pc ( 0x02D9A2 ) , 0x0000 )
rom . write_int16 ( 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 ] )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15AEE + 2 * 0x06 , 0x0020 ) # post aga hyrule castle spawn
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x15B8C + 0x06 , 0x1B )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15BDB + 2 * 0x06 , 0x00AE )
rom . write_int16 ( 0x15C79 + 2 * 0x06 , 0x0610 )
rom . write_int16 ( 0x15D17 + 2 * 0x06 , 0x077E )
rom . write_int16 ( 0x15DB5 + 2 * 0x06 , 0x0672 )
rom . write_int16 ( 0x15E53 + 2 * 0x06 , 0x07F8 )
rom . write_int16 ( 0x15EF1 + 2 * 0x06 , 0x067D )
rom . write_int16 ( 0x15F8F + 2 * 0x06 , 0x0803 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x1602D + 0x06 , 0x00 )
rom . write_byte ( 0x1607C + 0x06 , 0xF2 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x160CB + 2 * 0x06 , 0x0000 )
rom . write_int16 ( 0x16169 + 2 * 0x06 , 0x0000 )
2020-12-04 22:56:21 +00:00
rom . write_int16 ( snes_to_pc ( 0x02E87B ) , 0x00AE ) # move flute spot 9
2020-08-21 16:35:48 +00:00
rom . write_int16 ( snes_to_pc ( 0x02E89D ) , 0x0610 )
rom . write_int16 ( snes_to_pc ( 0x02E8BF ) , 0x077E )
rom . write_int16 ( snes_to_pc ( 0x02E8E1 ) , 0x0672 )
rom . write_int16 ( snes_to_pc ( 0x02E903 ) , 0x07F8 )
rom . write_int16 ( snes_to_pc ( 0x02E925 ) , 0x067D )
rom . write_int16 ( snes_to_pc ( 0x02E947 ) , 0x0803 )
rom . write_int16 ( snes_to_pc ( 0x02E969 ) , 0x0000 )
rom . write_int16 ( 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 ] )
2020-08-21 16:35:48 +00:00
rom . write_int16s ( snes_to_pc ( 0x0FF1C8 ) , [ 0x190F , 0x190F , 0x190F , 0x194C , 0x190F ,
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 ] )
rom . write_int16s ( snes_to_pc ( 0x0FA480 ) , [ 0x190F , 0x196B , 0x9D04 , 0x9D04 , 0x196B ,
0x190F , 0x9D04 , 0x9D04 ] )
rom . write_int16s ( snes_to_pc ( 0x1bb810 ) , [ 0x00BE , 0x00C0 , 0x013E ] )
rom . write_int16s ( snes_to_pc ( 0x1bb836 ) , [ 0x001B , 0x001B , 0x001B ] )
rom . write_int16 ( snes_to_pc ( 0x308300 ) , 0x0140 ) # new pyramid hole entrance
rom . write_int16 ( snes_to_pc ( 0x308320 ) , 0x001B )
2019-12-16 17:24:34 +00:00
if world . shuffle [ player ] in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
2019-07-27 13:13:13 +00:00
rom . write_byte ( snes_to_pc ( 0x308340 ) , 0x7B )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( snes_to_pc ( 0x1af504 ) , 0x148B )
rom . write_int16 ( snes_to_pc ( 0x1af50c ) , 0x149B )
rom . write_int16 ( snes_to_pc ( 0x1af514 ) , 0x14A4 )
rom . write_int16 ( snes_to_pc ( 0x1af51c ) , 0x1489 )
rom . write_int16 ( snes_to_pc ( 0x1af524 ) , 0x14AC )
rom . write_int16 ( snes_to_pc ( 0x1af52c ) , 0x54AC )
rom . write_int16 ( snes_to_pc ( 0x1af534 ) , 0x148C )
rom . write_int16 ( snes_to_pc ( 0x1af53c ) , 0x548C )
rom . write_int16 ( snes_to_pc ( 0x1af544 ) , 0x1484 )
rom . write_int16 ( snes_to_pc ( 0x1af54c ) , 0x5484 )
rom . write_int16 ( snes_to_pc ( 0x1af554 ) , 0x14A2 )
rom . write_int16 ( snes_to_pc ( 0x1af55c ) , 0x54A2 )
rom . write_int16 ( snes_to_pc ( 0x1af564 ) , 0x14A0 )
rom . write_int16 ( snes_to_pc ( 0x1af56c ) , 0x54A0 )
rom . write_int16 ( snes_to_pc ( 0x1af574 ) , 0x148E )
rom . write_int16 ( snes_to_pc ( 0x1af57c ) , 0x548E )
rom . write_int16 ( snes_to_pc ( 0x1af584 ) , 0x14AE )
rom . write_int16 ( 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 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 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 ] )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0xDB96F + 2 * 0x35 , 0x001B ) # move pyramid exit door
rom . write_int16 ( 0xDBA71 + 2 * 0x35 , 0x06A4 )
2019-12-16 17:24:34 +00:00
if world . shuffle [ player ] 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-12-16 17:24:34 +00:00
if world . shuffle [ player ] in [ ' vanilla ' , ' dungeonssimple ' , ' dungeonsfull ' ] :
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15AEE + 2 * 0x37 , 0x0010 ) # pyramid exit to new hc area
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x15B8C + 0x37 , 0x1B )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x15BDB + 2 * 0x37 , 0x0418 )
rom . write_int16 ( 0x15C79 + 2 * 0x37 , 0x0679 )
rom . write_int16 ( 0x15D17 + 2 * 0x37 , 0x06B4 )
rom . write_int16 ( 0x15DB5 + 2 * 0x37 , 0x06C6 )
rom . write_int16 ( 0x15E53 + 2 * 0x37 , 0x0738 )
rom . write_int16 ( 0x15EF1 + 2 * 0x37 , 0x06E6 )
rom . write_int16 ( 0x15F8F + 2 * 0x37 , 0x0733 )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x1602D + 0x37 , 0x07 )
rom . write_byte ( 0x1607C + 0x37 , 0xF9 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x160CB + 2 * 0x37 , 0x0000 )
rom . write_int16 ( 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 ] )
2020-10-24 03:33:52 +00:00
2019-07-27 13:13:13 +00:00
def patch_shuffled_dark_sanc ( world , rom , player ) :
2020-05-10 09:27:13 +00:00
dark_sanc = world . get_region ( ' Inverted Dark Sanctuary ' , player )
dark_sanc_entrance = str ( [ i for i in dark_sanc . entrances if i . parent_region . name != ' Menu ' ] [ 0 ] . name )
2020-10-24 03:33:52 +00:00
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 ]
2019-07-27 13:13:13 +00:00
door_index = door_addresses [ str ( dark_sanc_entrance ) ] [ 0 ]
2020-07-30 18:14:05 +00:00
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x180241 , 0x01 )
2020-07-30 18:14:05 +00:00
rom . write_byte ( 0x180248 , door_index + 1 )
2020-08-21 16:35:48 +00:00
rom . write_int16 ( 0x180250 , room_id )
2019-07-27 13:13:13 +00:00
rom . write_byte ( 0x180252 , ow_area )
2020-08-21 16:35:48 +00:00
rom . write_int16s ( 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 ] )
2020-10-24 03:33:52 +00:00
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 ' ,
2020-10-24 03:33:52 +00:00
' Bonk Fairy (Dark) ' : ' The rock pile near the old bomb shop ' ,
2019-01-20 07:01:02 +00:00
' 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 ' ,
2020-02-17 09:08:03 +00:00
' Flute ' ,
2019-07-25 22:25:14 +00:00
' 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 ' ,
2020-09-06 15:19:34 +00:00
' Progressive Mail ' ,
2019-07-25 22:25:14 +00:00
' 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) ' ,
2020-06-24 14:22:49 +00:00
' Small Key (Hyrule Castle) ' ,
2019-12-13 21:37:52 +00:00
' 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) '
]
2020-01-14 09:42:27 +00:00
hash_alphabet = [
" Bow " , " Boomerang " , " Hookshot " , " Bomb " , " Mushroom " , " Powder " , " Rod " , " Pendant " , " Bombos " , " Ether " , " Quake " ,
2020-02-17 09:08:03 +00:00
" Lamp " , " Hammer " , " Shovel " , " Flute " , " Bug Net " , " Book " , " Bottle " , " Potion " , " Cane " , " Cape " , " Mirror " , " Boots " ,
2020-01-14 09:42:27 +00:00
" Gloves " , " Flippers " , " Pearl " , " Shield " , " Tunic " , " Heart " , " Map " , " Compass " , " Key "
]