v30 updates

This commit is contained in:
Kevin Cathcart 2018-09-22 22:51:54 -04:00
parent b259c10d27
commit 5539143f00
10 changed files with 93 additions and 85 deletions

View File

@ -45,7 +45,7 @@ class World(object):
self.aga_randomness = True self.aga_randomness = True
self.lock_aga_door_in_escape = False self.lock_aga_door_in_escape = False
self.fix_trock_doors = self.shuffle != 'vanilla' self.fix_trock_doors = self.shuffle != 'vanilla'
self.save_and_quit_from_boss = False self.save_and_quit_from_boss = True
self.check_beatable_only = check_beatable_only self.check_beatable_only = check_beatable_only
self.fix_skullwoods_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] self.fix_skullwoods_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']
self.fix_palaceofdarkness_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] self.fix_palaceofdarkness_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']
@ -456,6 +456,16 @@ class CollectionState(object):
return self.has('Bow') and (self.has('Silver Arrows') or self.can_buy_unlimited('Single Arrow')) return self.has('Bow') and (self.has('Silver Arrows') or self.can_buy_unlimited('Single Arrow'))
return self.has('Bow') return self.has('Bow')
def can_get_good_bee(self):
cave = self.world.get_region('Good Bee Cave')
return (
self.has_bottle() and
self.has('Bug Catching Net') and
(self.has_Boots() or (self.has_sword() and self.has('Quake'))) and
cave.can_reach(self) and
(cave.is_light_world or self.has_Pearl())
)
def has_sword(self): def has_sword(self):
return self.has('Fighter Sword') or self.has('Master Sword') or self.has('Tempered Sword') or self.has('Golden Sword') return self.has('Fighter Sword') or self.has('Master Sword') or self.has('Tempered Sword') or self.has('Golden Sword')
@ -775,6 +785,7 @@ class Crystal(Item):
class ShopType(Enum): class ShopType(Enum):
Shop = 0 Shop = 0
TakeAny = 1 TakeAny = 1
UpgradeShop = 2
class Shop(object): class Shop(object):
def __init__(self, region, room_id, type, shopkeeper_config, replaceable): def __init__(self, region, room_id, type, shopkeeper_config, replaceable):
@ -804,6 +815,8 @@ class Shop(object):
config |= 0x40 # ignore door id config |= 0x40 # ignore door id
if self.type == ShopType.TakeAny: if self.type == ShopType.TakeAny:
config |= 0x80 config |= 0x80
if self.type == ShopType.UpgradeShop:
config |= 0x10 # Alt. VRAM
return [0x00]+int16_as_bytes(self.room_id)+[door_id, 0x00, config, self.shopkeeper_config, 0x00] return [0x00]+int16_as_bytes(self.room_id)+[door_id, 0x00, config, self.shopkeeper_config, 0x00]
def has_unlimited(self, item): def has_unlimited(self, item):

2
Gui.py
View File

@ -1114,7 +1114,7 @@ class SpriteSelector(object):
try: try:
task.update_status("Downloading official sprites list") task.update_status("Downloading official sprites list")
with urlopen('http://vt.alttp.run/sprites') as response: with urlopen('https://alttpr.com/sprites') as response:
sprites_arr = json.loads(response.read().decode("utf-8")) sprites_arr = json.loads(response.read().decode("utf-8"))
except Exception as e: except Exception as e:
resultmessage = "Error getting list of official sprites. Sprites not updated.\n\n%s: %s" % (type(e).__name__, e) resultmessage = "Error getting list of official sprites. Sprites not updated.\n\n%s: %s" % (type(e).__name__, e)

View File

@ -20,9 +20,9 @@ basicgloves = ['Power Glove', 'Titans Mitts']
normalbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Fairy)', 'Bottle (Bee)', 'Bottle (Good Bee)'] normalbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Fairy)', 'Bottle (Bee)', 'Bottle (Good Bee)']
hardbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Bee)', 'Bottle (Good Bee)'] hardbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Bee)', 'Bottle (Good Bee)']
normalbaseitems = (['Silver Arrows', 'Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + normalbaseitems = (['Silver Arrows', 'Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrows (10)', 'Bombs (3)'] +
['Rupees (300)'] * 4 + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24) ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24)
normalfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6 normalfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Arrows (10)'] * 6 + ['Bombs (3)'] * 6
normalsecond15extra = ['Bombs (3)'] * 9 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] + ['Bombs (10)'] normalsecond15extra = ['Bombs (3)'] * 9 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] + ['Bombs (10)']
normalthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Arrows (10)', 'Rupee (1)', 'Rupees (5)'] normalthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Arrows (10)', 'Rupee (1)', 'Rupees (5)']
normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)'] normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)']
@ -33,7 +33,7 @@ easybaseitems = (['Sanctuary Heart Container'] + ['Rupees (300)'] * 4 + ['Magic
['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12) ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12)
easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)'] easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)']
easylimitedextra = ['Boss Heart Container'] * 3 # collapsing down the 12 pieces of heart easylimitedextra = ['Boss Heart Container'] * 3 # collapsing down the 12 pieces of heart
easyfirst15extra = ['Rupees (100)', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6 easyfirst15extra = ['Rupees (100)'] + ['Arrows (10)'] * 7 + ['Bombs (3)'] * 7
easysecond10extra = ['Bombs (3)'] * 7 + ['Rupee (1)', 'Rupees (50)', 'Bombs (10)'] easysecond10extra = ['Bombs (3)'] * 7 + ['Rupee (1)', 'Rupees (50)', 'Bombs (10)']
easythird5extra = ['Rupees (50)'] * 2 + ['Bombs (3)'] * 2 + ['Arrows (10)'] easythird5extra = ['Rupees (50)'] * 2 + ['Bombs (3)'] * 2 + ['Arrows (10)']
easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2 easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2

View File

@ -6,7 +6,7 @@ import logging
import random import random
import time import time
from BaseClasses import World, CollectionState, Item, Region, Location, Entrance, Shop from BaseClasses import World, CollectionState, Item, Region, Location, Shop
from Regions import create_regions, mark_light_world_regions from Regions import create_regions, mark_light_world_regions
from EntranceShuffle import link_entrances from EntranceShuffle import link_entrances
from Rom import patch_rom, Sprite, LocalRom, JsonRom from Rom import patch_rom, Sprite, LocalRom, JsonRom

View File

@ -1,7 +1,7 @@
# ALttPEntranceRandomizer # ALttPEntranceRandomizer
This is a entrance randomizer for _The Legend of Zelda: A Link to the Past_ for the SNES. This is a entrance randomizer for _The Legend of Zelda: A Link to the Past_ for the SNES.
See http://vt.alttp.run for more details on the normal randomizer. See https://alttpr.com/ for more details on the normal randomizer.
# Installation # Installation

View File

@ -299,6 +299,10 @@ def create_regions(world):
for index, (item, price) in enumerate(default_shop_contents[region_name]): for index, (item, price) in enumerate(default_shop_contents[region_name]):
shop.add_inventory(index, item, price) shop.add_inventory(index, item, price)
region = world.get_region('Capacity Upgrade')
shop = Shop(region, 0x0115, ShopType.UpgradeShop, 0x04, True)
shop.add_inventory(0, 'Bomb Upgrade (+5)', 100, 7)
shop.add_inventory(1, 'Arrow Upgrade (+5)', 100, 7)
world.intialize_regions() world.intialize_regions()
def create_lw_region(name, locations=None, exits=None): def create_lw_region(name, locations=None, exits=None):
@ -358,15 +362,15 @@ def mark_light_world_regions(world):
# (room_id, shopkeeper, replaceable) # (room_id, shopkeeper, replaceable)
shop_table = { shop_table = {
'Cave Shop (Dark Death Mountain)': (0x0112, 0x51, True), 'Cave Shop (Dark Death Mountain)': (0x0112, 0xC1, True),
'Red Shield Shop': (0x0110, 0x51, True), 'Red Shield Shop': (0x0110, 0xC1, True),
'Dark Lake Hylia Shop': (0x010F, 0x51, True), 'Dark Lake Hylia Shop': (0x010F, 0xC1, True),
'Dark World Lumberjack Shop': (0x010F, 0x51, True), 'Dark World Lumberjack Shop': (0x010F, 0xC1, True),
'Village of Outcasts Shop': (0x010F, 0x51, True), 'Village of Outcasts Shop': (0x010F, 0xC1, True),
'Dark World Potion Shop': (0x010F, 0x51, True), 'Dark World Potion Shop': (0x010F, 0xC1, True),
'Light World Death Mountain Shop': (0x00FF, 0x51, True), 'Light World Death Mountain Shop': (0x00FF, 0xA0, True),
'Kakariko Shop': (0x011F, 0x51, True), 'Kakariko Shop': (0x011F, 0xA0, True),
'Cave Shop (Lake Hylia)': (0x0112, 0x51, True), 'Cave Shop (Lake Hylia)': (0x0112, 0xA0, True),
'Potion Shop': (0x0109, 0xFF, False), 'Potion Shop': (0x0109, 0xFF, False),
# Bomb Shop not currently modeled as a shop, due to special nature of items # Bomb Shop not currently modeled as a shop, due to special nature of items
} }

101
Rom.py
View File

@ -9,7 +9,7 @@ import random
from BaseClasses import ShopType from BaseClasses import ShopType
from Dungeons import dungeon_music_addresses from Dungeons import dungeon_music_addresses
from Text import MultiByteTextMapper, text_addresses, Credits, TextTable from Text import MultiByteTextMapper, text_addresses, Credits, TextTable
from Text import Uncle_texts, Ganon1_texts, PyramidFairy_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts from Text import Uncle_texts, Ganon1_texts, TavernMan_texts, Sahasrahla2_texts, Triforce_texts, Blind_texts, BombShop2_texts
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 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 local_path, int16_as_bytes, int32_as_bytes from Utils import local_path, int16_as_bytes, int32_as_bytes
from Items import ItemFactory from Items import ItemFactory
@ -42,6 +42,12 @@ class JsonRom(object):
with open(file, 'w') as stream: with open(file, 'w') as stream:
json.dump([self.patches], stream) json.dump([self.patches], stream)
def get_hash(self):
h = hashlib.md5()
h.update(json.dumps([self.patches]).encode('utf-8'))
return h.hexdigest()
class LocalRom(object): class LocalRom(object):
@ -97,6 +103,11 @@ class LocalRom(object):
inv = crc ^ 0xFFFF inv = crc ^ 0xFFFF
self.write_bytes(0x7FDC, [inv & 0xFF, (inv >> 8) & 0xFF, crc & 0xFF, (crc >> 8) & 0xFF]) self.write_bytes(0x7FDC, [inv & 0xFF, (inv >> 8) & 0xFF, crc & 0xFF, (crc >> 8) & 0xFF])
def get_hash(self):
h = hashlib.md5()
h.update(self.buffer)
return h.hexdigest()
def read_rom(stream): def read_rom(stream):
"Reads rom into bytearray and strips off any smc header" "Reads rom into bytearray and strips off any smc header"
buffer = bytearray(stream.read()) buffer = bytearray(stream.read())
@ -422,24 +433,6 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_byte(0x180180, 0x02) # Hookshot only rom.write_byte(0x180180, 0x02) # Hookshot only
# Make silver arrows only usable against Ganon # Make silver arrows only usable against Ganon
rom.write_byte(0x180181, 0x01) rom.write_byte(0x180181, 0x01)
#Make Blue Shield more expensive
rom.write_bytes(0xF73D2, [0xFC, 0xFF])
rom.write_bytes(0xF73DA, [0x04, 0x00])
rom.write_bytes(0xF73E2, [0x0C, 0x00])
rom.write_byte(0xF73D6, 0x31)
rom.write_byte(0xF73DE, 0x30)
rom.write_byte(0xF73E6, 0x30)
rom.write_byte(0xF7201, 0x00)
rom.write_byte(0xF71FF, 0x64)
#Make Red Shield more expensive
rom.write_bytes(0xF73FA, [0xFC, 0xFF])
rom.write_bytes(0xF7402, [0x04, 0x00])
rom.write_bytes(0xF740A, [0x0C, 0x00])
rom.write_byte(0xF73FE, 0x33)
rom.write_byte(0xF7406, 0x33)
rom.write_byte(0xF740E, 0x33)
rom.write_byte(0xF7241, 0x03)
rom.write_byte(0xF723F, 0xE7)
elif world.difficulty == 'expert': elif world.difficulty == 'expert':
# Powdered Fairies Prize # Powdered Fairies Prize
rom.write_byte(0x36DD0, 0xD8) # One Heart rom.write_byte(0x36DD0, 0xD8) # One Heart
@ -460,24 +453,6 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_byte(0x180180, 0x00) # Nothing rom.write_byte(0x180180, 0x00) # Nothing
# Make silver arrows only usable against Ganon # Make silver arrows only usable against Ganon
rom.write_byte(0x180181, 0x01) rom.write_byte(0x180181, 0x01)
#Make Blue Shield more expensive
rom.write_bytes(0xF73D2, [0xFC, 0xFF])
rom.write_bytes(0xF73DA, [0x04, 0x00])
rom.write_bytes(0xF73E2, [0x0C, 0x00])
rom.write_byte(0xF73D6, 0x3C)
rom.write_byte(0xF73DE, 0x3C)
rom.write_byte(0xF73E6, 0x3C)
rom.write_byte(0xF7201, 0x27)
rom.write_byte(0xF71FF, 0x06)
#Make Red Shield more expensive
rom.write_bytes(0xF73FA, [0xFC, 0xFF])
rom.write_bytes(0xF7402, [0x04, 0x00])
rom.write_bytes(0xF740A, [0x0C, 0x00])
rom.write_byte(0xF73FE, 0x3C)
rom.write_byte(0xF7406, 0x3C)
rom.write_byte(0xF740E, 0x3C)
rom.write_byte(0xF7241, 0x27)
rom.write_byte(0xF723F, 0x06)
elif world.difficulty == 'insane': elif world.difficulty == 'insane':
# Powdered Fairies Prize # Powdered Fairies Prize
rom.write_byte(0x36DD0, 0x79) # Bees rom.write_byte(0x36DD0, 0x79) # Bees
@ -498,24 +473,6 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_byte(0x180180, 0x00) # Nothing rom.write_byte(0x180180, 0x00) # Nothing
# Make silver arrows only usable against Ganon # Make silver arrows only usable against Ganon
rom.write_byte(0x180181, 0x01) rom.write_byte(0x180181, 0x01)
#Make Blue Shield more expensive
rom.write_bytes(0xF73D2, [0xFC, 0xFF])
rom.write_bytes(0xF73DA, [0x04, 0x00])
rom.write_bytes(0xF73E2, [0x0C, 0x00])
rom.write_byte(0xF73D6, 0x3C)
rom.write_byte(0xF73DE, 0x3C)
rom.write_byte(0xF73E6, 0x3C)
rom.write_byte(0xF7201, 0x27)
rom.write_byte(0xF71FF, 0x10)
#Make Red Shield more expensive
rom.write_bytes(0xF73FA, [0xFC, 0xFF])
rom.write_bytes(0xF7402, [0x04, 0x00])
rom.write_bytes(0xF740A, [0x0C, 0x00])
rom.write_byte(0xF73FE, 0x3C)
rom.write_byte(0xF7406, 0x3C)
rom.write_byte(0xF740E, 0x3C)
rom.write_byte(0xF7241, 0x27)
rom.write_byte(0xF723F, 0x10)
else: else:
# Powdered Fairies Prize # Powdered Fairies Prize
rom.write_byte(0x36DD0, 0xE3) # fairy rom.write_byte(0x36DD0, 0xE3) # fairy
@ -543,7 +500,7 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
if world.difficulty in ['easy']: if world.difficulty in ['easy']:
rom.write_byte(0x180182, 0x03) # auto equip silvers on pickup and at ganon rom.write_byte(0x180182, 0x03) # auto equip silvers on pickup and at ganon
elif world.retro and world.difficulty in ['hard','expert', 'insane']: #FIXME: this is temporary for v29 baserom elif world.retro and world.difficulty in ['hard', 'expert', 'insane']: #FIXME: this is temporary for v29 baserom (perhaps no so temporary?)
rom.write_byte(0x180182, 0x03) # auto equip silvers on pickup and at ganon rom.write_byte(0x180182, 0x03) # auto equip silvers on pickup and at ganon
else: else:
rom.write_byte(0x180182, 0x01) # auto equip silvers on pickup rom.write_byte(0x180182, 0x01) # auto equip silvers on pickup
@ -574,6 +531,12 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0,
0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2,
0xE3, 0xE3, 0xE3, 0xE3, 0xE3] 0xE3, 0xE3, 0xE3, 0xE3, 0xE3]
if world.difficulty in ['hard', 'expert', 'insane']:
prize_replacements = {0xE0: 0xDF, # Fairy -> heart
0xE3: 0xD8} # Big magic -> small magic
prizes = [prize_replacements.get(prize, prize) for prize in prizes]
dig_prizes = [prize_replacements.get(prize, prize) for prize in dig_prizes]
if world.retro: if world.retro:
prize_replacements = {0xE1: 0xDA, #5 Arrows -> Blue Rupee prize_replacements = {0xE1: 0xDA, #5 Arrows -> Blue Rupee
0xE2: 0xDB} #10 Arrows -> Red Rupee 0xE2: 0xDB} #10 Arrows -> Red Rupee
@ -640,12 +603,16 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
# original_item, limit, replacement_item, filler # original_item, limit, replacement_item, filler
0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees 0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees
0x58, 0x01, 0x43, 0xFF, # silver arrows -> 1 arrow 0x58, 0x01, 0x43, 0xFF, # silver arrows -> 1 arrow
0x51, 0x06, 0x52, 0xFF, # 6 +5 bomb upgrades -> +10 bomb upgrade
0x53, 0x06, 0x54, 0xFF, # 6 +5 arrow upgrades -> +10 arrow upgrade
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel 0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
]) ])
else: else:
rom.write_bytes(0x184000, [ rom.write_bytes(0x184000, [
# original_item, limit, replacement_item, filler # original_item, limit, replacement_item, filler
0x12, 0x01, 0x35, 0xFF, # lamp -> 5 rupees 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
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel 0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
]) ])
@ -676,6 +643,10 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_byte(0x348DB, 0x3A) # Red Boomerang becomes Red Boomerang rom.write_byte(0x348DB, 0x3A) # Red Boomerang becomes Red Boomerang
rom.write_byte(0x348EB, 0x05) # Blue Shield becomes Blue Shield rom.write_byte(0x348EB, 0x05) # Blue Shield becomes Blue Shield
# Remove Statues for upgrade fairy
rom.write_bytes(0x01F810, [0x1A, 0x1E, 0x01, 0x1A, 0x1E, 0x01])
rom.write_byte(0x180029, 0x01) # Smithy quick item give rom.write_byte(0x180029, 0x01) # Smithy quick item give
# set swordless mode settings # set swordless mode settings
@ -740,7 +711,6 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_byte(0x180211, 0x06) #Game type, we set the Entrance and item randomization flags rom.write_byte(0x180211, 0x06) #Game type, we set the Entrance and item randomization flags
# assorted fixes # assorted fixes
rom.write_byte(0x180030, 0x00) # Disable SRAM trace
rom.write_byte(0x1800A2, 0x01) # remain in real dark world when dying in dark word dungion before killing aga1 rom.write_byte(0x1800A2, 0x01) # remain in real dark world when dying in dark word dungion 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. rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence.
rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid else 0x00) # Enable respawning on pyramid after ganon death rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid else 0x00) # Enable respawning on pyramid after ganon death
@ -766,11 +736,14 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_byte(0x18302D, 0x18) # starting current health rom.write_byte(0x18302D, 0x18) # starting current health
rom.write_byte(0x183039, 0x68) # starting abilities, bit array rom.write_byte(0x183039, 0x68) # starting abilities, bit array
rom.write_byte(0x18004A, 0x00) # Inverted mode (off) rom.write_byte(0x18004A, 0x00) # Inverted mode (off)
rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier
rom.write_byte(0x2AF79, 0xD0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both) rom.write_byte(0x2AF79, 0xD0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both)
rom.write_byte(0x3A943, 0xD0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both) rom.write_byte(0x3A943, 0xD0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both)
rom.write_byte(0x3A96D, 0xF0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader)) rom.write_byte(0x3A96D, 0xF0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader))
rom.write_byte(0x3A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader)) rom.write_byte(0x3A9A7, 0xD0) # Residual Portal: Normal (D0= Light Side, F0=Dark Side, 42 = both (Darth Vader))
rom.write_bytes(0x180080, [50, 50, 70, 70]) # values to fill for Capacity Upgrades (Bomb5, Bomb10, Arrow5, Arrow10)
rom.write_byte(0x18004D, 0x00) # Escape assist (off) rom.write_byte(0x18004D, 0x00) # Escape assist (off)
rom.write_byte(0x18004E, 0x00) # escape fills rom.write_byte(0x18004E, 0x00) # escape fills
rom.write_int16_to_rom(0x180183, 0) # rupee fill (for bow if rupee arrows enabled) rom.write_int16_to_rom(0x180183, 0) # rupee fill (for bow if rupee arrows enabled)
@ -815,6 +788,7 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
rom.write_byte(0x180020, digging_game_rng) rom.write_byte(0x180020, digging_game_rng)
rom.write_byte(0xEFD95, digging_game_rng) rom.write_byte(0xEFD95, digging_game_rng)
rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills
rom.write_byte(0x1800A4, 0x01 if world.logic != 'nologic' else 0x00) # enable POD EG fix
rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill
# remove shield from uncle # remove shield from uncle
@ -859,8 +833,16 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None):
# 21 bytes # 21 bytes
rom.write_bytes(0x7FC0, bytearray('ER_060_%09d\0' % world.seed, 'utf8') + world.option_identifier.to_bytes(4, 'big')) rom.write_bytes(0x7FC0, bytearray('ER_060_%09d\0' % world.seed, 'utf8') + world.option_identifier.to_bytes(4, 'big'))
# store hash table for main menu hash # Write title screen Code
rom.write_bytes(0x187F00, hashtable) 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)
apply_rom_settings(rom, beep, color, world.quickswap, world.fastmenu, world.disable_music, sprite) apply_rom_settings(rom, beep, color, world.quickswap, world.fastmenu, world.disable_music, sprite)
@ -1047,7 +1029,6 @@ def write_strings(rom, world):
tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)] tt['uncle_leaving_text'] = Uncle_texts[random.randint(0, len(Uncle_texts) - 1)]
tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[random.randint(0, len(Triforce_texts) - 1)] tt['end_triforce'] = "{NOBORDER}\n" + Triforce_texts[random.randint(0, len(Triforce_texts) - 1)]
tt['bomb_shop_big_bomb'] = BombShop2_texts[random.randint(0, len(BombShop2_texts) - 1)] tt['bomb_shop_big_bomb'] = BombShop2_texts[random.randint(0, len(BombShop2_texts) - 1)]
tt['pond_will_upgrade'] = PyramidFairy_texts[random.randint(0, len(PyramidFairy_texts) - 1)]
# this is what shows after getting the green pendant item in rando # this is what shows after getting the green pendant item in rando
tt['sahasrahla_quest_have_master_sword'] = Sahasrahla2_texts[random.randint(0, len(Sahasrahla2_texts) - 1)] tt['sahasrahla_quest_have_master_sword'] = Sahasrahla2_texts[random.randint(0, len(Sahasrahla2_texts) - 1)]

View File

@ -294,6 +294,7 @@ def global_rules(world):
set_rule(world.get_entrance('Ice Palace Entrance Room'), lambda state: state.has('Fire Rod') or (state.has('Bombos') and state.has_sword())) set_rule(world.get_entrance('Ice Palace Entrance Room'), lambda state: state.has('Fire Rod') or (state.has('Bombos') and state.has_sword()))
set_rule(world.get_location('Ice Palace - Big Chest'), lambda state: state.has('Big Key (Ice Palace)')) set_rule(world.get_location('Ice Palace - Big Chest'), lambda state: state.has('Big Key (Ice Palace)'))
set_rule(world.get_entrance('Ice Palace (Kholdstare)'), lambda state: state.can_lift_rocks() and state.has('Hammer') and state.has('Big Key (Ice Palace)') and (state.has_key('Small Key (Ice Palace)', 2) or (state.has('Cane of Somaria') and state.has_key('Small Key (Ice Palace)', 1)))) set_rule(world.get_entrance('Ice Palace (Kholdstare)'), lambda state: state.can_lift_rocks() and state.has('Hammer') and state.has('Big Key (Ice Palace)') and (state.has_key('Small Key (Ice Palace)', 2) or (state.has('Cane of Somaria') and state.has_key('Small Key (Ice Palace)', 1))))
# TODO: investigate change from VT. Changed to hookshot or 2 keys (no checking for big key in specific chests)
set_rule(world.get_entrance('Ice Palace (East)'), lambda state: (state.has('Hookshot') or (item_in_locations(state, 'Big Key (Ice Palace)', ['Ice Palace - Spike Room', 'Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']) and state.has_key('Small Key (Ice Palace)'))) and (state.world.can_take_damage or state.has('Hookshot') or state.has('Cape') or state.has('Cane of Byrna'))) set_rule(world.get_entrance('Ice Palace (East)'), lambda state: (state.has('Hookshot') or (item_in_locations(state, 'Big Key (Ice Palace)', ['Ice Palace - Spike Room', 'Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']) and state.has_key('Small Key (Ice Palace)'))) and (state.world.can_take_damage or state.has('Hookshot') or state.has('Cape') or state.has('Cane of Byrna')))
set_rule(world.get_entrance('Ice Palace (East Top)'), lambda state: state.can_lift_rocks() and state.has('Hammer')) set_rule(world.get_entrance('Ice Palace (East Top)'), lambda state: state.can_lift_rocks() and state.has('Hammer'))
for location in ['Ice Palace - Big Chest', 'Ice Palace - Kholdstare']: for location in ['Ice Palace - Big Chest', 'Ice Palace - Kholdstare']:

17
Text.py
View File

@ -108,7 +108,6 @@ Triforce_texts = [
" Pick us up\n before we\n get dizzy!", " Pick us up\n before we\n get dizzy!",
] ]
BombShop2_texts = ['Bombs!\nBombs!\nBiggest!\nBestest!\nGreatest!\nBoomest!'] BombShop2_texts = ['Bombs!\nBombs!\nBiggest!\nBestest!\nGreatest!\nBoomest!']
PyramidFairy_texts = ['May I talk to you about our lord and savior, Ganon?']
Sahasrahla2_texts = ['You already got my item, idiot.', 'Why are you still talking to me?', 'This text won\'t change.', 'Have you met my brother, Hasarahshla?'] Sahasrahla2_texts = ['You already got my item, idiot.', 'Why are you still talking to me?', 'This text won\'t change.', 'Have you met my brother, Hasarahshla?']
Blind_texts = [ Blind_texts = [
"I hate insect\npuns, they\nreally bug me.", "I hate insect\npuns, they\nreally bug me.",
@ -126,7 +125,7 @@ Blind_texts = [
"When you're a\nbaker, don't\nloaf around.", "When you're a\nbaker, don't\nloaf around.",
"Mire requires\nEther Quake,\nor Bombos.", "Mire requires\nEther Quake,\nor Bombos.",
"Broken pencils\nare pointless.", "Broken pencils\nare pointless.",
"The food they\nserve guards\nlasts sentries.", "The food they\nserve guards\nlasts sentries",
"Being crushed\nby big objects\nis depressing.", "Being crushed\nby big objects\nis depressing.",
"A tap dancer's\nroutine runs\nhot and cold.", "A tap dancer's\nroutine runs\nhot and cold.",
"A weeknight is\na tiny\nnobleman.", "A weeknight is\na tiny\nnobleman.",
@ -144,6 +143,7 @@ Blind_texts = [
"Sausage is\nthe wurst.", "Sausage is\nthe wurst.",
"I tried to\ncatch fog,\nbut I mist.", "I tried to\ncatch fog,\nbut I mist.",
"Winter is a\ngreat time\nto chill.", "Winter is a\ngreat time\nto chill.",
"Do you think\nthe Ice Rod\nis cool?",
] ]
Ganon1_texts = [ Ganon1_texts = [
"Start your day\nsmiling with a\ndelicious\nwhole grain\nbreakfast\ncreated for\nyour\nincredible\ninsides.", "Start your day\nsmiling with a\ndelicious\nwhole grain\nbreakfast\ncreated for\nyour\nincredible\ninsides.",
@ -246,7 +246,7 @@ Sahasrahla_names = [
"sandstorms", "sandwiched", "sauerkraut", "schipperke", "schismatic", "schizocarp", "schmalzier", "sandstorms", "sandwiched", "sauerkraut", "schipperke", "schismatic", "schizocarp", "schmalzier",
"schmeering", "schmoosing", "shibboleth", "shovelnose", "sahananana", "sarararara", "salamander", "schmeering", "schmoosing", "shibboleth", "shovelnose", "sahananana", "sarararara", "salamander",
"sharshalah", "shahabadoo", "sassafrass", "saddlebags", "sandalwood", "shagadelic", "sandcastle", "sharshalah", "shahabadoo", "sassafrass", "saddlebags", "sandalwood", "shagadelic", "sandcastle",
"saltpeters", "shabbiness", "shlrshlrsh", "saltpeters", "shabbiness", "shlrshlrsh", "sassyralph", "sallyacorn",
] ]
Kakariko_texts = ["{}'s homecoming"] Kakariko_texts = ["{}'s homecoming"]
@ -1300,6 +1300,9 @@ class TextTable(object):
'item_get_14_heart', 'item_get_14_heart',
'item_get_24_heart', 'item_get_24_heart',
'item_get_34_heart', 'item_get_34_heart',
'pond_item_test',
'pond_will_upgrade',
# misc # misc
'agahnim_final_meeting', 'agahnim_final_meeting',
'agahnim_hide_and_seek_found', 'agahnim_hide_and_seek_found',
@ -1461,7 +1464,7 @@ class TextTable(object):
text['sahasrahla_have_courage'] = CompressedTextMapper.convert("{BOTTOM}\nLook, you have the green pendant! I'll give you something. Go kill the other two bosses for more pendant fun!") text['sahasrahla_have_courage'] = CompressedTextMapper.convert("{BOTTOM}\nLook, you have the green pendant! I'll give you something. Go kill the other two bosses for more pendant fun!")
text['sahasrahla_found'] = CompressedTextMapper.convert("{BOTTOM}\nYup!\n\nI'm the old man you are looking for. I'll keep it short and sweet: Go into that dungeon, then bring me the green pendant and talk to me again.") text['sahasrahla_found'] = CompressedTextMapper.convert("{BOTTOM}\nYup!\n\nI'm the old man you are looking for. I'll keep it short and sweet: Go into that dungeon, then bring me the green pendant and talk to me again.")
text['sign_rain_north_of_links_house'] = CompressedTextMapper.convert("↑ Dying Uncle\n This way…") text['sign_rain_north_of_links_house'] = CompressedTextMapper.convert("↑ Dying Uncle\n This way…")
text['sign_north_of_links_house'] = CompressedTextMapper.convert("> Randomizer Don't read me, go beat Ganon!") text['sign_north_of_links_house'] = CompressedTextMapper.convert("> Randomizer") #"> Randomizer The telepathic tiles can have hints!"
text['sign_path_to_death_mountain'] = CompressedTextMapper.convert("Cave to lost, old man.\nGood luck.") text['sign_path_to_death_mountain'] = CompressedTextMapper.convert("Cave to lost, old man.\nGood luck.")
text['sign_lost_woods'] = CompressedTextMapper.convert("\n↑ Lost Woods") text['sign_lost_woods'] = CompressedTextMapper.convert("\n↑ Lost Woods")
text['sign_zoras'] = CompressedTextMapper.convert("Danger!\nDeep water!\nZoras!") text['sign_zoras'] = CompressedTextMapper.convert("Danger!\nDeep water!\nZoras!")
@ -1596,11 +1599,11 @@ class TextTable(object):
text['mastersword_pedestal_translated'] = CompressedTextMapper.convert("A test of strength: If you have 3 pendants, I'm yours.") text['mastersword_pedestal_translated'] = CompressedTextMapper.convert("A test of strength: If you have 3 pendants, I'm yours.")
text['telepathic_tile_spectacle_rock'] = CompressedTextMapper.convert("{NOBORDER}\n{NOBORDER}\nUse the Mirror, or the Hookshot and Hammer, to get to Tower of Hera!") text['telepathic_tile_spectacle_rock'] = CompressedTextMapper.convert("{NOBORDER}\n{NOBORDER}\nUse the Mirror, or the Hookshot and Hammer, to get to Tower of Hera!")
text['telepathic_tile_swamp_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nDrain the floodgate to raise the water here!") text['telepathic_tile_swamp_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nDrain the floodgate to raise the water here!")
text['telepathic_tile_thieves_town_upstairs'] = CompressedTextMapper.convert("Secondary tournament winners\n{HARP}\n ~~~2017~~~\nA: Zaen") text['telepathic_tile_thieves_town_upstairs'] = CompressedTextMapper.convert("{NOBORDER}\nBlind hate's bright light.")
text['telepathic_tile_misery_mire'] = CompressedTextMapper.convert("{NOBORDER}\nLighting 4 torches will open your way forward!") text['telepathic_tile_misery_mire'] = CompressedTextMapper.convert("{NOBORDER}\nLighting 4 torches will open your way forward!")
text['hylian_text_2'] = CompressedTextMapper.convert("%%^= %==%\n ^ =%^=\n==%= ^^%^") text['hylian_text_2'] = CompressedTextMapper.convert("%%^= %==%\n ^ =%^=\n==%= ^^%^")
text['desert_entry_translated'] = CompressedTextMapper.convert("Kneel before this stone, and magic will move around you.") text['desert_entry_translated'] = CompressedTextMapper.convert("Kneel before this stone, and magic will move around you.")
text['telepathic_tile_under_ganon'] = CompressedTextMapper.convert("{NOBORDER}\nOnly arrows will finish off a blue Ganon, or really well-timed spins in phase 4.") text['telepathic_tile_under_ganon'] = CompressedTextMapper.convert("Secondary tournament winners\n{HARP}\n ~~~2017~~~\nA: Zaen")
text['telepathic_tile_palace_of_darkness'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a funny looking Enemizer") text['telepathic_tile_palace_of_darkness'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a funny looking Enemizer")
# C0 # C0
text['telepathic_tile_desert_bonk_torch_room'] = CompressedTextMapper.convert("{NOBORDER}\nThings can be knocked down, if you fancy yourself a dashing dude.") text['telepathic_tile_desert_bonk_torch_room'] = CompressedTextMapper.convert("{NOBORDER}\nThings can be knocked down, if you fancy yourself a dashing dude.")
@ -1610,7 +1613,7 @@ class TextTable(object):
text['telepathic_tile_ice_entrace'] = CompressedTextMapper.convert("{NOBORDER}\nYou can use Fire Rod or Bombos to pass.") text['telepathic_tile_ice_entrace'] = CompressedTextMapper.convert("{NOBORDER}\nYou can use Fire Rod or Bombos to pass.")
text['telepathic_tile_ice_stalfos_knights_room'] = CompressedTextMapper.convert("{NOBORDER}\nKnock 'em down and then bomb them dead.") text['telepathic_tile_ice_stalfos_knights_room'] = CompressedTextMapper.convert("{NOBORDER}\nKnock 'em down and then bomb them dead.")
text['telepathic_tile_tower_of_hera_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a bad place, with a guy who will make you fall…\n\n\na lot.") text['telepathic_tile_tower_of_hera_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a bad place, with a guy who will make you fall…\n\n\na lot.")
text['houlahan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n ~~~2017~~~\nA: ajneb174\nS: ajneb174") text['houlihan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n ~~~2018~~~\nS: Andy\n\n ~~~2017~~~\nA: ajneb174\nS: ajneb174")
text['caught_a_bee'] = CompressedTextMapper.convert("Caught a Bee\n ≥ keep\n release\n{CHOICE}") text['caught_a_bee'] = CompressedTextMapper.convert("Caught a Bee\n ≥ keep\n release\n{CHOICE}")
text['caught_a_fairy'] = CompressedTextMapper.convert("Caught Fairy!\n ≥ keep\n release\n{CHOICE}") text['caught_a_fairy'] = CompressedTextMapper.convert("Caught Fairy!\n ≥ keep\n release\n{CHOICE}")
text['no_empty_bottles'] = CompressedTextMapper.convert("Whoa, bucko!\nNo empty bottles.") text['no_empty_bottles'] = CompressedTextMapper.convert("Whoa, bucko!\nNo empty bottles.")

View File

@ -10,6 +10,12 @@ def int32_as_bytes(value):
value = value & 0xFFFFFFFF value = value & 0xFFFFFFFF
return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF] return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]
def pc_to_snes(value):
return ((value<<1) & 0x7F0000)|(value & 0x7FFF)|0x8000
def snes_to_pc(value):
return ((value & 0x7F0000)>>1)|(value & 0x7FFF)
def is_bundled(): def is_bundled():
return getattr(sys, 'frozen', False) return getattr(sys, 'frozen', False)