Merge branch 'DoorDevVolatile' into Customizer

# Conflicts:
#	DoorShuffle.py
#	ItemList.py
#	Main.py
This commit is contained in:
aerinon
2022-05-24 10:09:36 -06:00
17 changed files with 525 additions and 382 deletions

View File

@@ -1967,8 +1967,10 @@ ohko_forbidden = {
def filter_dashable_candidates(candidates, world): def filter_dashable_candidates(candidates, world):
forbidden_set = dashable_forbidden if world.timer in ['ohko', 'timed-ohko'] else ohko_forbidden forbidden_set = dashable_forbidden
return [x for x in candidates if x not in forbidden_set and x.dest not in forbidden_set] if world.timer in ['ohko', 'timed-ohko']:
forbidden_set = ohko_forbidden.union(dashable_forbidden)
return [x for x in candidates if x.name not in forbidden_set and x.dest.name not in forbidden_set]
def shuffle_bombable_dashable(bd_candidates, world, player): def shuffle_bombable_dashable(bd_candidates, world, player):

253
InitialSram.py Normal file
View File

@@ -0,0 +1,253 @@
from dataclasses import dataclass, field
from typing import List
from BaseClasses import CollectionState
from Utils import count_set_bits
SRAM_SIZE = 0x500
ROOM_DATA = 0x000
OVERWORLD_DATA = 0x280
def _new_default_sram():
sram_buf = [0x00] * 0x500
sram_buf[ROOM_DATA+0x20D] = 0xF0
sram_buf[ROOM_DATA+0x20F] = 0xF0
sram_buf[0x379] = 0x68
sram_buf[0x401] = 0xFF
sram_buf[0x402] = 0xFF
return sram_buf
@dataclass
class InitialSram:
_initial_sram_bytes: List[int] = field(default_factory=_new_default_sram)
def _set_value(self, idx: int, val:int):
if idx > SRAM_SIZE:
raise IndexError('SRAM index out of bounds: {idx}')
if not (-1 < val < 256):
raise ValueError('SRAM value must be between 0 and 255: {val}')
self._initial_sram_bytes[idx] = val
def _or_value(self, idx: int, val:int):
if idx > SRAM_SIZE:
raise IndexError('SRAM index out of bounds: {idx}')
if not (-1 < val < 256):
raise ValueError('SRAM value must be between 0 and 255: {val}')
self._initial_sram_bytes[idx] |= val
def pre_open_aga_curtains(self):
self._or_value(ROOM_DATA+0x61, 0x80)
def pre_open_skullwoods_curtains(self):
self._or_value(ROOM_DATA+0x93, 0x80)
def pre_open_lumberjack(self):
self._or_value(OVERWORLD_DATA+0x02, 0x20)
def pre_open_castle_gate(self):
self._or_value(OVERWORLD_DATA+0x1B, 0x20)
def pre_open_ganons_tower(self):
self._or_value(OVERWORLD_DATA+0x43, 0x20)
def pre_open_pyramid_hole(self):
self._or_value(OVERWORLD_DATA+0x5B, 0x20)
def set_starting_equipment(self, world: object, player: int):
equip = [0] * (0x340 + 0x4F)
equip[0x36C] = 0x18
equip[0x36D] = 0x18
equip[0x379] = 0x68
if world.bombbag[player]:
starting_max_bombs = 0
else:
starting_max_bombs = 10
starting_max_arrows = 30
starting_bomb_cap_upgrades = 0
starting_arrow_cap_upgrades = 0
starting_bombs = 0
starting_arrows = 0
startingstate = CollectionState(world)
if startingstate.has('Bow', player):
equip[0x340] = 3 if startingstate.has('Silver Arrows', player) else 1
equip[0x38E] |= 0x20 # progressive flag to get the correct hint in all cases
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
for item in world.precollected_items:
if item.player != player:
continue
if item.name in ['Bow', 'Silver Arrows', 'Progressive Bow', 'Progressive Bow (Alt)',
'Titans Mitts', 'Power Glove', 'Progressive Glove',
'Golden Sword', 'Tempered Sword', 'Master Sword', 'Fighter Sword', 'Progressive Sword',
'Mirror Shield', 'Red Shield', 'Blue Shield', 'Progressive Shield',
'Red Mail', 'Blue Mail', 'Progressive Armor',
'Magic Upgrade (1/4)', 'Magic Upgrade (1/2)']:
continue
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)}
or_table = {'Green Pendant': (0x374, 0x04), 'Red Pendant': (0x374, 0x01), 'Blue Pendant': (0x374, 0x02),
'Crystal 1': (0x37A, 0x02), 'Crystal 2': (0x37A, 0x10), 'Crystal 3': (0x37A, 0x40), 'Crystal 4': (0x37A, 0x20),
'Crystal 5': (0x37A, 0x04), 'Crystal 6': (0x37A, 0x01), 'Crystal 7': (0x37A, 0x08),
'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 (Escape)': (0x367, 0xC0), 'Compass (Escape)': (0x365, 0xC0), 'Map (Escape)': (0x369, 0xC0),
'Big Key (Agahnims Tower)': (0x367, 0x08), 'Compass (Agahnims Tower)': (0x365, 0x08), 'Map (Agahnims Tower)': (0x369, 0x08),
'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),
'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),
'Shovel': (0x34C, 1, 0x38C, 0x04), 'Ocarina': (0x34C, 3, 0x38C, 0x01),
'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],
'Small Key (Universal)': [0x38B], 'Small Key (Escape)': [0x37C, 0x37D]}
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}
rupees = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)': 50, 'Rupees (100)': 100, 'Rupees (300)': 300}
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:
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))
elif item.name in bomb_caps:
starting_bomb_cap_upgrades += bomb_caps[item.name]
elif item.name in arrow_caps:
starting_arrow_cap_upgrades += arrow_caps[item.name]
elif item.name in bombs:
starting_bombs += bombs[item.name]
elif item.name in arrows:
if world.retro[player]:
equip[0x38E] |= 0x80
starting_arrows = 1
else:
starting_arrows += 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)
else:
raise RuntimeError(f'Unsupported item in starting equipment: {item.name}')
equip[0x370] = min(starting_bomb_cap_upgrades, 50)
equip[0x371] = min(starting_arrow_cap_upgrades, 70)
equip[0x343] = min(starting_bombs, (equip[0x370] + starting_max_bombs))
equip[0x377] = min(starting_arrows, (equip[0x371] + starting_max_arrows))
# Assertion and copy equip to initial_sram_bytes
assert equip[:0x340] == [0] * 0x340
self._initial_sram_bytes[0x340:0x38F] = equip[0x340:0x38F]
# Set counters and highest equipment values
self._initial_sram_bytes[0x471] = count_set_bits(self._initial_sram_bytes[0x37A])
self._initial_sram_bytes[0x429] = count_set_bits(self._initial_sram_bytes[0x374])
self._initial_sram_bytes[0x417] = self._initial_sram_bytes[0x359]
self._initial_sram_bytes[0x422] = self._initial_sram_bytes[0x35A]
self._initial_sram_bytes[0x46E] = self._initial_sram_bytes[0x35B]
if world.swords[player] == "swordless":
self._initial_sram_bytes[0x359] = 0xFF
self._initial_sram_bytes[0x417] = 0x00
def set_starting_rupees(self, rupees: int):
if not (-1 < rupees < 10000):
raise ValueError("Starting rupees must be between 0 and 9999")
self._initial_sram_bytes[0x362] = self._initial_sram_bytes[0x360] = rupees & 0xFF
self._initial_sram_bytes[0x363] = self._initial_sram_bytes[0x361] = rupees >> 8
def set_progress_indicator(self, indicator: int):
self._set_value(0x3C5, indicator)
def set_progress_flags(self, flags: int):
self._set_value(0x3C6, flags)
def set_starting_entrance(self, entrance: int):
self._set_value(0x3C8, entrance)
def set_starting_timer(self, seconds: int):
timer = (seconds * 60).to_bytes(4, "little")
self._initial_sram_bytes[0x454] = timer[0]
self._initial_sram_bytes[0x455] = timer[1]
self._initial_sram_bytes[0x456] = timer[2]
self._initial_sram_bytes[0x457] = timer[3]
def set_swordless_curtains(self):
self._or_value(ROOM_DATA+0x61, 0x80)
self._or_value(ROOM_DATA+0x93, 0x80)
def get_initial_sram(self):
assert len(self._initial_sram_bytes) == SRAM_SIZE
return self._initial_sram_bytes[:]

View File

@@ -44,6 +44,7 @@ Difficulty = namedtuple('Difficulty',
'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit']) 'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit'])
total_items_to_place = 153 total_items_to_place = 153
max_goal = 850
difficulties = { difficulties = {
'normal': Difficulty( 'normal': Difficulty(
@@ -203,6 +204,7 @@ def generate_itempool(world, player):
world.push_item(loc, ItemFactory('Triforce', player), False) world.push_item(loc, ItemFactory('Triforce', player), False)
loc.event = True loc.event = True
loc.locked = True loc.locked = True
loc.forced_item = loc.item
world.get_location('Ganon', player).event = True world.get_location('Ganon', player).event = True
world.get_location('Ganon', player).locked = True world.get_location('Ganon', player).locked = True
@@ -795,7 +797,8 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
if goal in ['triforcehunt', 'trinity']: if goal in ['triforcehunt', 'trinity']:
if treasure_hunt_total == 0: if treasure_hunt_total == 0:
treasure_hunt_total = 30 if goal == 'triforcehunt' else 10 treasure_hunt_total = 30 if goal == 'triforcehunt' else 10
triforcepool = ['Triforce Piece'] * int(treasure_hunt_total) # triforce pieces max out
triforcepool = ['Triforce Piece'] * min(treasure_hunt_total, max_goal)
pool.extend(alwaysitems) pool.extend(alwaysitems)
@@ -984,8 +987,8 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s
pool.append(thisbottle) pool.append(thisbottle)
if customitemarray["triforcepieces"] > 0 or customitemarray["triforcepiecesgoal"] > 0: if customitemarray["triforcepieces"] > 0 or customitemarray["triforcepiecesgoal"] > 0:
# To display, count must be between 1 and 254 - larger values are not yet supported # Location pool doesn't support larger values
treasure_hunt_count = max(min(customitemarray["triforcepiecesgoal"], 254), 1) treasure_hunt_count = max(min(customitemarray["triforcepiecesgoal"], max_goal), 1)
treasure_hunt_icon = 'Triforce Piece' treasure_hunt_icon = 'Triforce Piece'
# Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling. # Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling.
if ((customitemarray["triforcepieces"] < treasure_hunt_count) and (goal in ['triforcehunt', 'trinity']) if ((customitemarray["triforcepieces"] < treasure_hunt_count) and (goal in ['triforcehunt', 'trinity'])
@@ -1176,7 +1179,7 @@ def get_player_dungeon_item_pool(world, player):
if dungeon.player == player and item.location is None] if dungeon.player == player and item.location is None]
# To display, count must be between 1 and 254 - larger values are not yet supported # location pool doesn't support larger values at this time
def set_default_triforce(goal, custom_goal, custom_total): def set_default_triforce(goal, custom_goal, custom_total):
triforce_goal, triforce_total = 0, 0 triforce_goal, triforce_total = 0, 0
if goal == 'triforcehunt': if goal == 'triforcehunt':
@@ -1184,9 +1187,9 @@ def set_default_triforce(goal, custom_goal, custom_total):
elif goal == 'trinity': elif goal == 'trinity':
triforce_goal, triforce_total = 8, 10 triforce_goal, triforce_total = 8, 10
if custom_goal > 0: if custom_goal > 0:
triforce_goal = max(min(custom_goal, 254), 1) triforce_goal = max(min(custom_goal, max_goal), 1)
if custom_total > 0: if custom_total > 0:
triforce_total = max(min(custom_total, 254), triforce_goal) triforce_total = max(min(custom_total, max_goal), triforce_goal)
return triforce_goal, triforce_total return triforce_goal, triforce_total

View File

@@ -26,23 +26,23 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Progressive Bow': (True, False, None, 0x64, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), 'Progressive Bow': (True, False, None, 0x64, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'),
'Progressive Bow (Alt)': (True, False, None, 0x65, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), 'Progressive Bow (Alt)': (True, False, None, 0x65, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'),
'Book of Mudora': (True, False, None, 0x1D, 150, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'), 'Book of Mudora': (True, False, None, 0x1D, 150, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'),
'Hammer': (True, False, None, 0x09, 250, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the hammer'), 'Hammer': (True, False, None, 0x09, 250, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the Hammer'),
'Hookshot': (True, False, None, 0x0A, 250, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'), 'Hookshot': (True, False, None, 0x0A, 250, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'),
'Magic Mirror': (True, False, None, 0x1A, 250, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the Mirror'), 'Magic Mirror': (True, False, None, 0x1A, 250, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the Mirror'),
'Ocarina': (True, False, None, 0x14, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'), 'Ocarina': (True, False, None, 0x14, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'),
'Pegasus Boots': (True, False, None, 0x4B, 250, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the Boots'), 'Pegasus Boots': (True, False, None, 0x4B, 250, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the Boots'),
'Power Glove': (True, False, None, 0x1B, 100, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the glove'), 'Power Glove': (True, False, None, 0x1B, 100, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the Glove'),
'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the cape'), 'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the Cape'),
'Mushroom': (True, False, None, 0x29, 50, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the mushroom'), 'Mushroom': (True, False, None, 0x29, 50, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the Mushroom'),
'Shovel': (True, False, None, 0x13, 50, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the shovel'), 'Shovel': (True, False, None, 0x13, 50, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the Shovel'),
'Lamp': (True, False, None, 0x12, 150, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the lamp'), 'Lamp': (True, False, None, 0x12, 150, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the Lamp'),
'Magic Powder': (True, False, None, 0x0D, 50, 'you can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the powder'), 'Magic Powder': (True, False, None, 0x0D, 50, 'you can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the Powder'),
'Moon Pearl': (True, False, None, 0x1F, 200, ' Bunny Link\n be\n gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the moon pearl'), 'Moon Pearl': (True, False, None, 0x1F, 200, ' Bunny Link\n be\n gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the Moon Pearl'),
'Cane of Somaria': (True, False, None, 0x15, 250, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the red cane'), 'Cane of Somaria': (True, False, None, 0x15, 250, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the red Cane'),
'Fire Rod': (True, False, None, 0x07, 250, 'I\'m the hot\nrod. I make\nthings burn!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the fire rod'), 'Fire Rod': (True, False, None, 0x07, 250, 'I\'m the hot\nrod. I make\nthings burn!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the Fire Rod'),
'Flippers': (True, False, None, 0x1E, 250, 'fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the flippers'), 'Flippers': (True, False, None, 0x1E, 250, 'fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the Flippers'),
'Ice Rod': (True, False, None, 0x08, 250, 'I\'m the cold\nrod. I make\nthings freeze!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the ice rod'), 'Ice Rod': (True, False, None, 0x08, 250, 'I\'m the cold\nrod. I make\nthings freeze!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the Ice Rod'),
'Titans Mitts': (True, False, None, 0x1C, 200, 'Now you can\nlift heavy\nstuff!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the mitts'), 'Titans Mitts': (True, False, None, 0x1C, 200, 'Now you can\nlift heavy\nstuff!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the Mitts'),
'Bombos': (True, False, None, 0x0F, 100, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'medallion boy melts room again', 'Bombos'), 'Bombos': (True, False, None, 0x0F, 100, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'medallion boy melts room again', 'Bombos'),
'Ether': (True, False, None, 0x10, 100, 'This magic\ncoin freezes\neverything!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-coin', 'medallion boy sees floor again', 'Ether'), 'Ether': (True, False, None, 0x10, 100, 'This magic\ncoin freezes\neverything!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-coin', 'medallion boy sees floor again', 'Ether'),
'Quake': (True, False, None, 0x11, 100, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'medallion boy shakes dirt again', 'Quake'), 'Quake': (True, False, None, 0x11, 100, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'medallion boy shakes dirt again', 'Quake'),
@@ -56,23 +56,24 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Master Sword': (True, False, 'Sword', 0x50, 100, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'), 'Master Sword': (True, False, 'Sword', 0x50, 100, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'),
'Tempered Sword': (True, False, 'Sword', 0x02, 150, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'), 'Tempered Sword': (True, False, 'Sword', 0x02, 150, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'),
'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the small sword'), 'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the small sword'),
'Sword and Shield': (True, False, 'Sword', 0x00, 'An uncle\nsword rests\nhere!', 'the sword and shield', 'sword and shield-wielding kid', 'training set for sale', 'fungus for training set', 'sword and shield boy fights again', 'the small sword and shield'),
'Golden Sword': (True, False, 'Sword', 0x03, 200, 'The butter\nsword rests\nhere!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'), 'Golden Sword': (True, False, 'Sword', 0x03, 200, 'The butter\nsword rests\nhere!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'),
'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'a better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a sword'), 'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'a better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a sword'),
'Progressive Glove': (True, False, None, 0x61, 150, 'a way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'), 'Progressive Glove': (True, False, None, 0x61, 150, 'a way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'),
'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the silver arrows'), 'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the silver arrows'),
'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x01, 0x01], 999, None, None, None, None, None, None, None), 'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x01, 0x08], 999, None, None, None, None, None, None, None),
'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02, 0x02], 999, None, None, None, None, None, None, None), 'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02, 0x09], 999, None, None, None, None, None, None, None),
'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03, 0x02], 999, None, None, None, None, None, None, None), 'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03, 0x0a], 999, None, None, None, None, None, None, None),
'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'), 'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'),
'Power Star': (True, False, None, 0x6B, 100, 'a small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), 'Power Star': (True, False, None, 0x6B, 100, 'a small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'),
'Triforce Piece': (True, False, None, 0x6C, 100, 'a small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce Piece'), 'Triforce Piece': (True, False, None, 0x6C, 100, 'a small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce Piece'),
'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x06, 0x04], 999, None, None, None, None, None, None, None), 'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x06, 0x01], 999, None, None, None, None, None, None, None),
'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x06, 0x04], 999, None, None, None, None, None, None, None), 'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x06, 0x02], 999, None, None, None, None, None, None, None),
'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x06, 0x04], 999, None, None, None, None, None, None, None), 'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x06, 0x03], 999, None, None, None, None, None, None, None),
'Crystal 4': (True, False, 'Crystal', [0x20, 0x34, 0x64, 0x40, 0x6D, 0x06, 0x04], 999, None, None, None, None, None, None, None), 'Crystal 4': (True, False, 'Crystal', [0x20, 0x34, 0x64, 0x40, 0x6D, 0x06, 0x04], 999, None, None, None, None, None, None, None),
'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x06, 0x03], 999, None, None, None, None, None, None, None), 'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x06, 0x05], 999, None, None, None, None, None, None, None),
'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06, 0x03], 999, None, None, None, None, None, None, None), 'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06, 0x06], 999, None, None, None, None, None, None, None),
'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x06, 0x04], 999, None, None, None, None, None, None, None), 'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x06, 0x07], 999, None, None, None, None, None, None, None),
'Single Arrow': (False, False, None, 0x43, 3, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), 'Single Arrow': (False, False, None, 0x43, 3, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'),
'Arrows (10)': (False, False, None, 0x44, 30, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'), 'Arrows (10)': (False, False, None, 0x44, 30, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'),
'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
@@ -91,12 +92,12 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Progressive Armor': (False, True, None, 0x60, 50, 'time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), 'Progressive Armor': (False, True, None, 0x60, 50, 'time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'),
'Blue Boomerang': (True, False, None, 0x0C, 50, 'No matter what\nyou do, blue\nreturns to you', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the blue boomerang'), 'Blue Boomerang': (True, False, None, 0x0C, 50, 'No matter what\nyou do, blue\nreturns to you', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the blue boomerang'),
'Red Boomerang': (True, False, None, 0x2A, 50, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the red boomerang'), 'Red Boomerang': (True, False, None, 0x2A, 50, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the red boomerang'),
'Blue Shield': (False, True, None, 0x04, 50, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'the blue shield'), 'Blue Shield': (False, True, None, 0x04, 50, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a blue shield'),
'Red Shield': (False, True, None, 0x05, 500, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'the red shield'), 'Red Shield': (False, True, None, 0x05, 500, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'a red shield'),
'Mirror Shield': (True, False, None, 0x06, 200, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the mirror shield'), 'Mirror Shield': (True, False, None, 0x06, 200, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the Mirror Shield'),
'Progressive Shield': (True, False, None, 0x5F, 50, 'have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'), 'Progressive Shield': (True, False, None, 0x5F, 50, 'have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'),
'Bug Catching Net': (True, False, None, 0x21, 50, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the bug net'), 'Bug Catching Net': (True, False, None, 0x21, 50, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the bug net'),
'Cane of Byrna': (True, False, None, 0x18, 50, 'Use this to\nbecome\ninvincible!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the blue cane'), 'Cane of Byrna': (True, False, None, 0x18, 50, 'Use this to\nbecome\ninvincible!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the blue Cane'),
'Boss Heart Container': (False, True, None, 0x3E, 40, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), 'Boss Heart Container': (False, True, None, 0x3E, 40, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'),
'Sanctuary Heart Container': (False, True, None, 0x3F, 50, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), 'Sanctuary Heart Container': (False, True, None, 0x3F, 50, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'),
'Piece of Heart': (False, False, None, 0x17, 10, 'Just a little\npiece of love!', 'and the broken heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart piece'), 'Piece of Heart': (False, False, None, 0x17, 10, 'Just a little\npiece of love!', 'and the broken heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart piece'),
@@ -112,8 +113,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Green Clock': (False, True, None, 0x5D, 200, 'a lot of time', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'), 'Green Clock': (False, True, None, 0x5D, 200, 'a lot of time', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'),
'Single RNG': (False, True, None, 0x62, 300, 'something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'), 'Single RNG': (False, True, None, 0x62, 300, 'something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'),
'Multi RNG': (False, True, None, 0x63, 100, 'something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'), 'Multi RNG': (False, True, None, 0x63, 100, 'something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'),
'Magic Upgrade (1/2)': (True, False, None, 0x4E, 50, 'Your magic\npower has been\ndoubled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'half magic'), # can be required to beat mothula in an open seed in very very rare circumstance 'Magic Upgrade (1/2)': (True, False, None, 0x4E, 50, 'Your magic\npower has been\ndoubled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Half Magic'), # can be required to beat mothula in an open seed in very very rare circumstance
'Magic Upgrade (1/4)': (True, False, None, 0x4F, 100, 'Your magic\npower has been\nquadrupled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'quarter magic'), # can be required to beat mothula in an open seed in very very rare circumstance 'Magic Upgrade (1/4)': (True, False, None, 0x4F, 100, 'Your magic\npower has been\nquadrupled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Quarter Magic'), # can be required to beat mothula in an open seed in very very rare circumstance
'Small Key (Eastern Palace)': (False, False, 'SmallKey', 0xA2, 40, 'A small key to Armos Knights', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Eastern Palace'), 'Small Key (Eastern Palace)': (False, False, 'SmallKey', 0xA2, 40, 'A small key to Armos Knights', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Eastern Palace'),
'Big Key (Eastern Palace)': (False, False, 'BigKey', 0x9D, 60, 'A big key to Armos Knights', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Eastern Palace'), 'Big Key (Eastern Palace)': (False, False, 'BigKey', 0x9D, 60, 'A big key to Armos Knights', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Eastern Palace'),
'Compass (Eastern Palace)': (False, True, 'Compass', 0x8D, 10, 'Now you can find the Armos Knights!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Eastern Palace'), 'Compass (Eastern Palace)': (False, True, 'Compass', 0x8D, 10, 'Now you can find the Armos Knights!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Eastern Palace'),

View File

@@ -33,7 +33,7 @@ from source.overworld.EntranceShuffle2 import link_entrances_new
from source.tools.BPS import create_bps_from_data from source.tools.BPS import create_bps_from_data
from source.classes.CustomSettings import CustomSettings from source.classes.CustomSettings import CustomSettings
__version__ = '1.0.1.13w' __version__ = '1.0.2.4-w'
from source.classes.BabelFish import BabelFish from source.classes.BabelFish import BabelFish
@@ -115,8 +115,8 @@ def main(args, seed=None, fish=None):
world.potshuffle = args.shufflepots.copy() world.potshuffle = args.shufflepots.copy()
world.mixed_travel = args.mixed_travel.copy() world.mixed_travel = args.mixed_travel.copy()
world.standardize_palettes = args.standardize_palettes.copy() world.standardize_palettes = args.standardize_palettes.copy()
world.treasure_hunt_count = args.triforce_goal.copy() world.treasure_hunt_count = {k: int(v) for k, v in args.triforce_goal.items()}
world.treasure_hunt_total = args.triforce_pool.copy() world.treasure_hunt_total = {k: int(v) for k, v in args.triforce_pool.items()}
world.shufflelinks = args.shufflelinks.copy() world.shufflelinks = args.shufflelinks.copy()
world.pseudoboots = args.pseudoboots.copy() world.pseudoboots = args.pseudoboots.copy()
world.overworld_map = args.overworld_map.copy() world.overworld_map = args.overworld_map.copy()

View File

@@ -90,9 +90,11 @@ INGAME_MODES = {0x07, 0x09, 0x0b}
SAVEDATA_START = WRAM_START + 0xF000 SAVEDATA_START = WRAM_START + 0xF000
SAVEDATA_SIZE = 0x500 SAVEDATA_SIZE = 0x500
POT_ITEMS_SRAM_START = WRAM_START + 0x016600 POT_ITEMS_SRAM_START = WRAM_START + 0x016018 # 2 bytes per room
SPRITE_ITEMS_SRAM_START = WRAM_START + 0x016850 SPRITE_ITEMS_SRAM_START = WRAM_START + 0x016268 # 2 bytes per room
SHOP_SRAM_START = WRAM_START + 0x0164B8 # 2 bytes?
ITEM_SRAM_SIZE = 0x250 ITEM_SRAM_SIZE = 0x250
SHOP_SRAM_LEN = 0x29 # 41 tracked items
RECV_PROGRESS_ADDR = SAVEDATA_START + 0x4D0 # 2 bytes RECV_PROGRESS_ADDR = SAVEDATA_START + 0x4D0 # 2 bytes
RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2 # 1 byte RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2 # 1 byte
@@ -103,11 +105,9 @@ SCOUT_LOCATION_ADDR = SAVEDATA_START + 0x4D7 # 1 byte
SCOUTREPLY_LOCATION_ADDR = SAVEDATA_START + 0x4D8 # 1 byte SCOUTREPLY_LOCATION_ADDR = SAVEDATA_START + 0x4D8 # 1 byte
SCOUTREPLY_ITEM_ADDR = SAVEDATA_START + 0x4D9 # 1 byte SCOUTREPLY_ITEM_ADDR = SAVEDATA_START + 0x4D9 # 1 byte
SCOUTREPLY_PLAYER_ADDR = SAVEDATA_START + 0x4DA # 1 byte SCOUTREPLY_PLAYER_ADDR = SAVEDATA_START + 0x4DA # 1 byte
SHOP_ADDR = SAVEDATA_START + 0x302 # 2 bytes?
DYNAMIC_TOTAL_ADDR = SAVEDATA_START + 0x33E # 2 bytes DYNAMIC_TOTAL_ADDR = SAVEDATA_START + 0x33E # 2 bytes
MODE_FLAGS = SAVEDATA_START + 0x33D # 1 byte MODE_FLAGS = SAVEDATA_START + 0x33D # 1 byte
SHOP_SRAM_LEN = 0x29 # 41 tracked items
location_shop_order = [Regions.shop_to_location_table.keys()] + [Regions.retro_shops.keys()] location_shop_order = [Regions.shop_to_location_table.keys()] + [Regions.retro_shops.keys()]
location_shop_ids = {0x0112, 0x0110, 0x010F, 0x00FF, 0x011F, 0x0109, 0x0115} location_shop_ids = {0x0112, 0x0110, 0x010F, 0x00FF, 0x011F, 0x0109, 0x0115}
@@ -894,7 +894,7 @@ async def track_locations(ctx : Context, roomid, roomdata):
try: try:
if ctx.shop_mode or ctx.retro_mode: if ctx.shop_mode or ctx.retro_mode:
misc_data = await snes_read(ctx, SHOP_ADDR, SHOP_SRAM_LEN) misc_data = await snes_read(ctx, SHOP_SRAM_START, SHOP_SRAM_LEN)
for cnt, b in enumerate(misc_data): for cnt, b in enumerate(misc_data):
my_check = Regions.shop_table_by_location_id[0x400000 + cnt] my_check = Regions.shop_table_by_location_id[0x400000 + cnt]
if int(b) > 0 and my_check not in ctx.locations_checked: if int(b) > 0 and my_check not in ctx.locations_checked:

View File

@@ -1015,8 +1015,8 @@ key_drop_data = {
'Swamp Palace - Waterway Pot Key': ['Pot', 0x16, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], 'Swamp Palace - Waterway Pot Key': ['Pot', 0x16, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'],
'Skull Woods - West Lobby Pot Key': ['Pot', 0x56, 'in a pot in Skull Woods', 'Small Key (Skull Woods)'], 'Skull Woods - West Lobby Pot Key': ['Pot', 0x56, 'in a pot in Skull Woods', 'Small Key (Skull Woods)'],
'Skull Woods - Spike Corner Key Drop': ['Drop', (0x09DD74, 0x39, 1), 'dropped near Mothula', 'Small Key (Skull Woods)'], 'Skull Woods - Spike Corner Key Drop': ['Drop', (0x09DD74, 0x39, 1), 'dropped near Mothula', 'Small Key (Skull Woods)'],
"Thieves' Town - Hallway Pot Key": ['Pot', 0xBC, "in a pot in Thieves' Town", 'Small Key (Thieves Town)'], "Thieves' Town - Hallway Pot Key": ['Pot', 0xBC, "in a pot in Thieves Town", 'Small Key (Thieves Town)'],
"Thieves' Town - Spike Switch Pot Key": ['Pot', 0xAB, "in a pot in Thieves' Town", 'Small Key (Thieves Town)'], "Thieves' Town - Spike Switch Pot Key": ['Pot', 0xAB, "in a pot in Thieves Town", 'Small Key (Thieves Town)'],
'Ice Palace - Jelly Key Drop': ['Drop', (0x09DA21, 0xE, 3), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], 'Ice Palace - Jelly Key Drop': ['Drop', (0x09DA21, 0xE, 3), 'dropped in Ice Palace', 'Small Key (Ice Palace)'],
'Ice Palace - Conveyor Key Drop': ['Drop', (0x09DE08, 0x3E, 8), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], 'Ice Palace - Conveyor Key Drop': ['Drop', (0x09DE08, 0x3E, 8), 'dropped in Ice Palace', 'Small Key (Ice Palace)'],
'Ice Palace - Hammer Block Key Drop': ['Pot', 0x3F, 'under a block in Ice Palace', 'Small Key (Ice Palace)'], 'Ice Palace - Hammer Block Key Drop': ['Pot', 0x3F, 'under a block in Ice Palace', 'Small Key (Ice Palace)'],
@@ -1052,7 +1052,7 @@ class PotSecretTable(object):
rom.write_bytes(pointer_address + room * 2, int16_as_bytes(data_address)) rom.write_bytes(pointer_address + room * 2, int16_as_bytes(data_address))
for pot in self.room_map[room]: for pot in self.room_map[room]:
rom.write_bytes(data_pointer + list_idx * 3, pot.pot_data()) rom.write_bytes(data_pointer + list_idx * 3, pot.pot_data())
if pot.location is not None: if pot.location is not None and not pot.location.forced_item:
collection_rate_mask |= 1 << (15 - list_idx) collection_rate_mask |= 1 << (15 - list_idx)
if colorize and pot.obj_ref: if colorize and pot.obj_ref:
pot.obj_ref.change_type(Shuffled_Pot) pot.obj_ref.change_type(Shuffled_Pot)

View File

@@ -107,7 +107,7 @@ These districts are chosen at random and then filled with major items. If a loca
In entrance shuffle, what is shuffled to the entrances is considered instead of where the interior was originally. For example, if Blind's Hut is shuffled to the Dam, then the 5 chests in Blind's Hut are part of Central Hyrule instead of Kakariko. In entrance shuffle, what is shuffled to the entrances is considered instead of where the interior was originally. For example, if Blind's Hut is shuffled to the Dam, then the 5 chests in Blind's Hut are part of Central Hyrule instead of Kakariko.
Bombos Table, Lake Hylia Island, Bumper Cave Ledge, the Floating Island, Cave 45, the Graveyard Cave, Checkerboard Cave and Mimic Cave are considered part of the dark world region that you mirror from to get there (except in inverted where these are only accessible in the Light World). Note that Spectacle Rock is always part of light Death Mountain. Note: Bombos Tablet, Lake Hylia Island, Bumper Cave Ledge, the Floating Island, Cave 45, the Graveyard Cave, Checkerboard Cave and Mimic Cave are considered part of the light world region rather than the dark world region you mirror from.
In multiworld, the districts chosen apply to all players. In multiworld, the districts chosen apply to all players.
@@ -170,6 +170,27 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
#### Volatile #### Volatile
* 1.0.2.4
* Updated tourney winners (included Doors Async League winners)
* Fixed a couple issues with dungeon counters and the DungeonCompletion field for autotracking
* 1.0.2.3
* Fix MultiClient for new shop data location in SRAM
* Some minor text updates
* 1.0.2.2
* Change to all key pots and enemy key drops: always use the same address
* Don't colorize key pots in mystery if the item is "forced"
* 1.0.2.1
* Fix for paired doors
* Fix for forbidding certain dashable doors (it actually does something this time)
* 1.0.2.0
* Updated baserom to bleeding edge
* Pottery and enemy SRAM re-located to final destination
* Bulk of work on new font
* Updated TFH to support up to 850 pieces
* Fix for major item algorithm and pottery
* Updated map display on keysanity menu to work better with overworld_amp option
* Minor bug in crossed doors
* Minor bug in MultiClient which would count switches
* 1.0.1.13 * 1.0.1.13
* New pottery modes * New pottery modes
* Trinity goal added * Trinity goal added
@@ -260,6 +281,10 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
#### Unstable #### Unstable
* 1.0.0.3
* overworld_map=map mode fixed. Location of dungeons with maps are not shown until map is retrieved. (Dungeon that do not have map like Castle Tower are simply never shown)
* Aga2 completion on overworld_map now tied to boss defeat flag instead of pyramid hole being opened (fast ganon fix)
* Minor issue in dungeon_only algorithm fixed (minorly affected major_only keyshuffle and vanilla fallbacks)
* 1.0.0.2 * 1.0.0.2
* Include 1.0.1 fixes * Include 1.0.1 fixes
* District hint rework * District hint rework

View File

@@ -1097,7 +1097,7 @@ def create_pot_location(pot, pot_index, super_tile, world, player):
def pot_address(pot_index, super_tile): def pot_address(pot_index, super_tile):
return 0x7f6600 + super_tile * 2 + (pot_index << 24) return 0x7f6018 + super_tile * 2 + (pot_index << 24)
# (type, room_id, shopkeeper, custom, locked, [items]) # (type, room_id, shopkeeper, custom, locked, [items])
@@ -1251,7 +1251,7 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
'Paradox Cave Lower - Middle': (0xeb36, 0x18630a, False, 'in a cave with seven chests'), 'Paradox Cave Lower - Middle': (0xeb36, 0x18630a, False, 'in a cave with seven chests'),
'Paradox Cave Upper - Left': (0xeb39, 0x18630d, False, 'in a cave with seven chests'), 'Paradox Cave Upper - Left': (0xeb39, 0x18630d, False, 'in a cave with seven chests'),
'Paradox Cave Upper - Right': (0xeb3c, 0x186310, False, 'in a cave with seven chests'), 'Paradox Cave Upper - Right': (0xeb3c, 0x186310, False, 'in a cave with seven chests'),
'Spiral Cave': (0xe9bf, 0x186193, False, 'in spiral cave'), 'Spiral Cave': (0xe9bf, 0x186193, False, 'in Spiral Cave'),
'Ether Tablet': (0x180016, 0x18633b, False, 'at a monolith'), 'Ether Tablet': (0x180016, 0x18633b, False, 'at a monolith'),
'Spectacle Rock': (0x180140, 0x18634f, False, 'atop a rock'), 'Spectacle Rock': (0x180140, 0x18634f, False, 'atop a rock'),
'Tower of Hera - Basement Cage': (0x180162, 0x18633a, False, 'in Tower of Hera'), 'Tower of Hera - Basement Cage': (0x180162, 0x18633a, False, 'in Tower of Hera'),
@@ -1260,7 +1260,7 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
'Tower of Hera - Compass Chest': (0xe9fb, 0x1861cf, False, 'in Tower of Hera'), 'Tower of Hera - Compass Chest': (0xe9fb, 0x1861cf, False, 'in Tower of Hera'),
'Tower of Hera - Big Chest': (0xe9f8, 0x1861cc, False, 'in Tower of Hera'), 'Tower of Hera - Big Chest': (0xe9f8, 0x1861cc, False, 'in Tower of Hera'),
'Tower of Hera - Boss': (0x180152, 0x186340, False, 'with Moldorm'), 'Tower of Hera - Boss': (0x180152, 0x186340, False, 'with Moldorm'),
'Pyramid': (0x180147, 0x186356, False, 'on the pyramid'), 'Pyramid': (0x180147, 0x186356, False, 'on the Pyramid'),
'Catfish': (0xee185, 0x186361, False, 'with a catfish'), 'Catfish': (0xee185, 0x186361, False, 'with a catfish'),
'Stumpy': (0x330c7, 0x18636a, False, 'with tree boy'), 'Stumpy': (0x330c7, 0x18636a, False, 'with tree boy'),
'Digging Game': (0x180148, 0x186357, False, 'underground'), 'Digging Game': (0x180148, 0x186357, False, 'underground'),
@@ -1298,13 +1298,13 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
'Swamp Palace - Flooded Room - Right': (0xeaac, 0x186280, False, 'in Swamp Palace'), 'Swamp Palace - Flooded Room - Right': (0xeaac, 0x186280, False, 'in Swamp Palace'),
'Swamp Palace - Waterfall Room': (0xeaaf, 0x186283, False, 'in Swamp Palace'), 'Swamp Palace - Waterfall Room': (0xeaaf, 0x186283, False, 'in Swamp Palace'),
'Swamp Palace - Boss': (0x180154, 0x186342, False, 'with Arrghus'), 'Swamp Palace - Boss': (0x180154, 0x186342, False, 'with Arrghus'),
"Thieves' Town - Big Key Chest": (0xea04, 0x1861d8, False, "in Thieves' Town"), "Thieves' Town - Big Key Chest": (0xea04, 0x1861d8, False, "in Thieves Town"),
"Thieves' Town - Map Chest": (0xea01, 0x1861d5, False, "in Thieves' Town"), "Thieves' Town - Map Chest": (0xea01, 0x1861d5, False, "in Thieves Town"),
"Thieves' Town - Compass Chest": (0xea07, 0x1861db, False, "in Thieves' Town"), "Thieves' Town - Compass Chest": (0xea07, 0x1861db, False, "in Thieves Town"),
"Thieves' Town - Ambush Chest": (0xea0a, 0x1861de, False, "in Thieves' Town"), "Thieves' Town - Ambush Chest": (0xea0a, 0x1861de, False, "in Thieves Town"),
"Thieves' Town - Attic": (0xea0d, 0x1861e1, False, "in Thieves' Town"), "Thieves' Town - Attic": (0xea0d, 0x1861e1, False, "in Thieves Town"),
"Thieves' Town - Big Chest": (0xea10, 0x1861e4, False, "in Thieves' Town"), "Thieves' Town - Big Chest": (0xea10, 0x1861e4, False, "in Thieves Town"),
"Thieves' Town - Blind's Cell": (0xea13, 0x1861e7, False, "in Thieves' Town"), "Thieves' Town - Blind's Cell": (0xea13, 0x1861e7, False, "in Thieves Town"),
"Thieves' Town - Boss": (0x180156, 0x186344, False, 'with Blind'), "Thieves' Town - Boss": (0x180156, 0x186344, False, 'with Blind'),
'Skull Woods - Compass Chest': (0xe992, 0x186166, False, 'in Skull Woods'), 'Skull Woods - Compass Chest': (0xe992, 0x186166, False, 'in Skull Woods'),
'Skull Woods - Map Chest': (0xe99b, 0x18616f, False, 'in Skull Woods'), 'Skull Woods - Map Chest': (0xe99b, 0x18616f, False, 'in Skull Woods'),
@@ -1405,7 +1405,7 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
'Tower of Hera - Prize': ([0x120A5, 0x53E78, 0x53E79, 0x18005A, 0x180071, 0xC706, 0x186FEA], None, True, 'Tower of Hera'), 'Tower of Hera - Prize': ([0x120A5, 0x53E78, 0x53E79, 0x18005A, 0x180071, 0xC706, 0x186FEA], None, True, 'Tower of Hera'),
'Palace of Darkness - Prize': ([0x120A1, 0x53E7C, 0x53E7D, 0x180056, 0x180073, 0xC702, 0x186FE6], None, True, 'Palace of Darkness'), 'Palace of Darkness - Prize': ([0x120A1, 0x53E7C, 0x53E7D, 0x180056, 0x180073, 0xC702, 0x186FE6], None, True, 'Palace of Darkness'),
'Swamp Palace - Prize': ([0x120A0, 0x53E88, 0x53E89, 0x180055, 0x180079, 0xC701, 0x186FE5], None, True, 'Swamp Palace'), 'Swamp Palace - Prize': ([0x120A0, 0x53E88, 0x53E89, 0x180055, 0x180079, 0xC701, 0x186FE5], None, True, 'Swamp Palace'),
'Thieves\' Town - Prize': ([0x120A6, 0x53E82, 0x53E83, 0x18005B, 0x180076, 0xC707, 0x186FEB], None, True, 'Thieves\' Town'), 'Thieves\' Town - Prize': ([0x120A6, 0x53E82, 0x53E83, 0x18005B, 0x180076, 0xC707, 0x186FEB], None, True, 'Thieves Town'),
'Skull Woods - Prize': ([0x120A3, 0x53E7E, 0x53E7F, 0x180058, 0x180074, 0xC704, 0x186FE8], None, True, 'Skull Woods'), 'Skull Woods - Prize': ([0x120A3, 0x53E7E, 0x53E7F, 0x180058, 0x180074, 0xC704, 0x186FE8], None, True, 'Skull Woods'),
'Ice Palace - Prize': ([0x120A4, 0x53E86, 0x53E87, 0x180059, 0x180078, 0xC705, 0x186FE9], None, True, 'Ice Palace'), 'Ice Palace - Prize': ([0x120A4, 0x53E86, 0x53E87, 0x180059, 0x180078, 0xC705, 0x186FE9], None, True, 'Ice Palace'),
'Misery Mire - Prize': ([0x120A2, 0x53E84, 0x53E85, 0x180057, 0x180077, 0xC703, 0x186FE7], None, True, 'Misery Mire'), 'Misery Mire - Prize': ([0x120A2, 0x53E84, 0x53E85, 0x180057, 0x180077, 0xC703, 0x186FE7], None, True, 'Misery Mire'),

227
Rom.py
View File

@@ -15,7 +15,7 @@ try:
except ImportError: except ImportError:
raise Exception('Could not load BPS module') raise Exception('Could not load BPS module')
from BaseClasses import CollectionState, ShopType, Region, Location, Door, DoorType, RegionType, PotItem, LocationType from BaseClasses import ShopType, Region, Location, Door, DoorType, RegionType, LocationType
from DoorShuffle import compass_data, DROptions, boss_indicator, dungeon_portals from DoorShuffle import compass_data, DROptions, boss_indicator, dungeon_portals
from Dungeons import dungeon_music_addresses, dungeon_table from Dungeons import dungeon_music_addresses, dungeon_table
from Regions import location_table, shop_to_location_table, retro_shops from Regions import location_table, shop_to_location_table, retro_shops
@@ -29,6 +29,7 @@ from Text import Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, M
from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc
from Items import ItemFactory from Items import ItemFactory
from EntranceShuffle import door_addresses, exit_ids, ow_prize_table from EntranceShuffle import door_addresses, exit_ids, ow_prize_table
from InitialSram import InitialSram
from source.classes.SFX import randomize_sfx from source.classes.SFX import randomize_sfx
from source.item.FillUtil import valid_pot_items from source.item.FillUtil import valid_pot_items
@@ -36,7 +37,7 @@ from source.dungeon.RoomList import Room0127
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'd143684aa6a8e4560eb3ac912d600525' RANDOMIZERBASEHASH = '01166fb16b38b49ef79acc9993dc4f02'
class JsonRom(object): class JsonRom(object):
@@ -47,6 +48,7 @@ class JsonRom(object):
self.orig_buffer = None self.orig_buffer = None
self.patches = {} self.patches = {}
self.addresses = [] self.addresses = []
self.initial_sram = InitialSram()
def write_byte(self, address, value): def write_byte(self, address, value):
self.write_bytes(address, [value]) self.write_bytes(address, [value])
@@ -76,6 +78,9 @@ class JsonRom(object):
del self.patches[str(intervalstart)] del self.patches[str(intervalstart)]
del self.addresses[pos] del self.addresses[pos]
def write_initial_sram(self):
self.write_bytes(0x183000, self.initial_sram.get_initial_sram())
def write_to_file(self, file): def write_to_file(self, file):
with open(file, 'w') as stream: with open(file, 'w') as stream:
json.dump([self.patches], stream) json.dump([self.patches], stream)
@@ -93,6 +98,7 @@ class LocalRom(object):
self.hash = hash self.hash = hash
self.orig_buffer = None self.orig_buffer = None
self.file = file self.file = file
self.initial_sram = InitialSram()
self.has_smc_header = False self.has_smc_header = False
if not os.path.isfile(file): if not os.path.isfile(file):
raise RuntimeError("Could not find valid local base rom for patching at expected path %s." % file) raise RuntimeError("Could not find valid local base rom for patching at expected path %s." % file)
@@ -108,6 +114,9 @@ class LocalRom(object):
def write_bytes(self, startaddress, values): def write_bytes(self, startaddress, values):
self.buffer[startaddress:startaddress + len(values)] = values self.buffer[startaddress:startaddress + len(values)] = values
def write_initial_sram(self):
self.write_bytes(0x183000, self.initial_sram.get_initial_sram())
def write_to_file(self, file): def write_to_file(self, file):
with open(file, 'wb') as outfile: with open(file, 'wb') as outfile:
outfile.write(self.buffer) outfile.write(self.buffer)
@@ -958,11 +967,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# set open mode: # set open mode:
if world.mode[player] in ['open', 'inverted']: if world.mode[player] in ['open', 'inverted']:
rom.write_byte(0x180032, 0x01) # open mode init_open_mode_sram(rom)
if world.mode[player] == 'inverted': if world.mode[player] == 'inverted':
set_inverted_mode(world, player, rom) set_inverted_mode(world, player, rom)
elif world.mode[player] == 'standard': elif world.mode[player] == 'standard':
rom.write_byte(0x180032, 0x00) # standard mode init_standard_mode_sram(rom)
uncle_location = world.get_location('Link\'s Uncle', player) uncle_location = world.get_location('Link\'s Uncle', player)
if uncle_location.item is None or uncle_location.item.name not in ['Master Sword', 'Tempered Sword', 'Fighter Sword', 'Golden Sword', 'Progressive Sword']: if uncle_location.item is None or uncle_location.item.name not in ['Master Sword', 'Tempered Sword', 'Fighter Sword', 'Golden Sword', 'Progressive Sword']:
@@ -1186,10 +1195,10 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# set swordless mode settings # set swordless mode settings
rom.write_byte(0x18003F, 0x01 if world.swords[player] == 'swordless' else 0x00) # hammer can harm ganon 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(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 rom.write_byte(0x180044, 0x01 if world.swords[player] == 'swordless' else 0x00) # hammer activates tablets
if world.swords[player] == 'swordless':
rom.initial_sram.set_swordless_curtains() # open curtains
# set up clocks for timed modes # set up clocks for timed modes
if world.shuffle[player] == 'vanilla': if world.shuffle[player] == 'vanilla':
@@ -1205,39 +1214,39 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
write_int32(rom, 0x180200, 0) # red clock adjustment time (in frames, sint32) write_int32(rom, 0x180200, 0) # red clock adjustment time (in frames, sint32)
write_int32(rom, 0x180204, 0) # blue clock adjustment time (in frames, sint32) write_int32(rom, 0x180204, 0) # blue clock adjustment time (in frames, sint32)
write_int32(rom, 0x180208, 0) # green clock adjustment time (in frames, sint32) write_int32(rom, 0x180208, 0) # green clock adjustment time (in frames, sint32)
write_int32(rom, 0x18020C, 0) # starting time (in frames, sint32) rom.initial_sram.set_starting_timer(0) # starting time (in frames, sint32)
elif world.clock_mode == 'ohko': elif world.clock_mode == 'ohko':
rom.write_bytes(0x180190, [0x01, 0x02, 0x01]) # ohko timer with resetable timer functionality rom.write_bytes(0x180190, [0x01, 0x02, 0x01]) # ohko timer with resetable timer functionality
write_int32(rom, 0x180200, 0) # red clock adjustment time (in frames, sint32) write_int32(rom, 0x180200, 0) # red clock adjustment time (in frames, sint32)
write_int32(rom, 0x180204, 0) # blue clock adjustment time (in frames, sint32) write_int32(rom, 0x180204, 0) # blue clock adjustment time (in frames, sint32)
write_int32(rom, 0x180208, 0) # green clock adjustment time (in frames, sint32) write_int32(rom, 0x180208, 0) # green clock adjustment time (in frames, sint32)
write_int32(rom, 0x18020C, 0) # starting time (in frames, sint32) rom.initial_sram.set_starting_timer(0) # starting time (in frames, sint32)
elif world.clock_mode == 'countdown-ohko': elif world.clock_mode == 'countdown-ohko':
rom.write_bytes(0x180190, [0x01, 0x02, 0x01]) # ohko timer with resetable timer functionality rom.write_bytes(0x180190, [0x01, 0x02, 0x01]) # ohko timer with resetable timer functionality
write_int32(rom, 0x180200, -100 * 60 * 60 * 60) # red clock adjustment time (in frames, sint32) write_int32(rom, 0x180200, -100 * 60 * 60 * 60) # red clock adjustment time (in frames, sint32)
write_int32(rom, 0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32) write_int32(rom, 0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32)
write_int32(rom, 0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32) write_int32(rom, 0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32)
if world.difficulty_adjustments[player] == 'normal': if world.difficulty_adjustments == 'normal':
write_int32(rom, 0x18020C, (10 + ERtimeincrease) * 60 * 60) # starting time (in frames, sint32) rom.initial_sram.set_starting_timer((10 + ERtimeincrease) * 60) # starting time (in seconds)
else: else:
write_int32(rom, 0x18020C, int((5 + ERtimeincrease / 2) * 60 * 60)) # starting time (in frames, sint32) rom.initial_sram.set_starting_timer(int((5 + ERtimeincrease / 2) * 60)) # starting time (in seconds)
if world.clock_mode == 'stopwatch': if world.clock_mode == 'stopwatch':
rom.write_bytes(0x180190, [0x02, 0x01, 0x00]) # set stopwatch mode rom.write_bytes(0x180190, [0x02, 0x01, 0x00]) # set stopwatch mode
write_int32(rom, 0x180200, -2 * 60 * 60) # red clock adjustment time (in frames, sint32) write_int32(rom, 0x180200, -2 * 60 * 60) # red clock adjustment time (in frames, sint32)
write_int32(rom, 0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32) write_int32(rom, 0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32)
write_int32(rom, 0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32) write_int32(rom, 0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32)
write_int32(rom, 0x18020C, 0) # starting time (in frames, sint32) rom.initial_sram.set_starting_timer(0) # starting time (in frames, sint32)
if world.clock_mode == 'countdown': if world.clock_mode == 'countdown':
rom.write_bytes(0x180190, [0x01, 0x01, 0x00]) # set countdown, with no reset available rom.write_bytes(0x180190, [0x01, 0x01, 0x00]) # set countdown, with no reset available
write_int32(rom, 0x180200, -2 * 60 * 60) # red clock adjustment time (in frames, sint32) write_int32(rom, 0x180200, -2 * 60 * 60) # red clock adjustment time (in frames, sint32)
write_int32(rom, 0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32) write_int32(rom, 0x180204, 2 * 60 * 60) # blue clock adjustment time (in frames, sint32)
write_int32(rom, 0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32) write_int32(rom, 0x180208, 4 * 60 * 60) # green clock adjustment time (in frames, sint32)
write_int32(rom, 0x18020C, (40 + ERtimeincrease) * 60 * 60) # starting time (in frames, sint32) rom.initial_sram.set_starting_timer((40 + ERtimeincrease) * 60) # starting time (in seconds)
# set up goals for treasure hunt # set up goals for treasure hunt
rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon[player] == 'Triforce Piece' else [0x0D, 0x28]) rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon[player] == 'Triforce Piece' else [0x0D, 0x28])
if world.goal[player] in ['triforcehunt', 'trinity']: if world.goal[player] in ['triforcehunt', 'trinity']:
rom.write_byte(0x180167, int(world.treasure_hunt_count[player]) % 256) rom.write_bytes(0x180167, int16_as_bytes(world.treasure_hunt_count[player]))
rom.write_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled) rom.write_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled)
rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed
@@ -1257,14 +1266,16 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted
rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid[player] else 0x00) # Enable respawning on pyramid after ganon death 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 rom.write_byte(0x180173, 0x01) # Bob is enabled
rom.write_byte(0x180168, 0x08) # Spike Cave Damage rom.write_byte(0x180195, 0x08) # Spike Cave Damage
rom.write_bytes(0x18016B, [0x04, 0x02, 0x01]) # Set spike cave and MM spike room Byrna usage rom.write_bytes(0x18016B, [0x04, 0x02, 0x01]) # Set spike cave and MM spike room Byrna usage
rom.write_bytes(0x18016E, [0x04, 0x08, 0x10]) # 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_bytes(0x50563, [0x3F, 0x14]) # disable below ganon chest
rom.write_byte(0x50599, 0x00) # 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_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest
rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player] or world.goal[player] == 'trinity' else 0x00) # pre-open Pyramid Hole if world.open_pyramid[player] or world.goal[player] == 'trinity':
rom.write_byte(0x18008C, 0x01 if world.crystals_needed_for_gt[player] == 0 else 0x00) # GT pre-opened if crystal requirement is 0 rom.initial_sram.pre_open_pyramid_hole()
if world.crystals_needed_for_gt == 0:
rom.initial_sram.pre_open_ganons_tower()
rom.write_byte(0xF5D73, 0xF0) # bees are catchable rom.write_byte(0xF5D73, 0xF0) # bees are catchable
rom.write_byte(0xF5F10, 0xF0) # bees are catchable rom.write_byte(0xF5F10, 0xF0) # bees are catchable
rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness
@@ -1276,161 +1287,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
# Starting equipment # Starting equipment
if world.pseudoboots[player]: if world.pseudoboots[player]:
rom.write_byte(0x18008E, 0x01) rom.write_byte(0x18008E, 0x01)
rom.initial_sram.set_starting_equipment(world, player)
equip = [0] * (0x340 + 0x4F) rom.write_byte(0x180034, 10 if not world.bombbag[player] else 0) # starting max bombs
equip[0x36C] = 0x18 rom.write_byte(0x180035, 30) # starting max arrows
equip[0x36D] = 0x18
equip[0x379] = 0x68
if world.bombbag[player]:
starting_max_bombs = 0
else:
starting_max_bombs = 10
starting_max_arrows = 30
startingstate = CollectionState(world)
if startingstate.has('Bow', player):
equip[0x340] = 3 if startingstate.has('Silver Arrows', player) else 1
equip[0x38E] |= 0x20 # progressive flag to get the correct hint in all cases
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
for item in world.precollected_items:
if item.player != player:
continue
if item.name in ['Bow', 'Silver Arrows', 'Progressive Bow', 'Progressive Bow (Alt)',
'Titans Mitts', 'Power Glove', 'Progressive Glove',
'Golden Sword', 'Tempered Sword', 'Master Sword', 'Fighter Sword', 'Progressive Sword',
'Mirror Shield', 'Red Shield', 'Blue Shield', 'Progressive Shield',
'Red Mail', 'Blue Mail', 'Progressive Armor',
'Magic Upgrade (1/4)', 'Magic Upgrade (1/2)']:
continue
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)}
or_table = {'Green Pendant': (0x374, 0x04), 'Red Pendant': (0x374, 0x01), 'Blue Pendant': (0x374, 0x02),
'Crystal 1': (0x37A, 0x02), 'Crystal 2': (0x37A, 0x10), 'Crystal 3': (0x37A, 0x40), 'Crystal 4': (0x37A, 0x20),
'Crystal 5': (0x37A, 0x04), 'Crystal 6': (0x37A, 0x01), 'Crystal 7': (0x37A, 0x08),
'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 (Escape)': (0x367, 0xC0), 'Compass (Escape)': (0x365, 0xC0), 'Map (Escape)': (0x369, 0xC0),
'Big Key (Agahnims Tower)': (0x367, 0x08), 'Compass (Agahnims Tower)': (0x365, 0x08), 'Map (Agahnims Tower)': (0x369, 0x08),
'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),
'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),
'Shovel': (0x34C, 1, 0x38C, 0x04), 'Ocarina': (0x34C, 3, 0x38C, 0x01),
'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],
'Small Key (Universal)': [0x38B], 'Small Key (Escape)': [0x37C, 0x37D]}
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}
rupees = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)': 50, 'Rupees (100)': 100, 'Rupees (300)': 300}
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:
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))
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)
else:
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:])
rom.write_bytes(0x271A6, equip[0x340:0x340+60])
rom.write_byte(0x18004A, 0x00 if world.mode[player] != 'inverted' else 0x01) # Inverted mode rom.write_byte(0x18004A, 0x00 if world.mode[player] != 'inverted' else 0x01) # Inverted mode
rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier
@@ -1503,8 +1362,13 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
for idx, x_map in enumerate(x_map_position_generic): for idx, x_map in enumerate(x_map_position_generic):
rom.write_bytes(0x53df6+idx*2, int16_as_bytes(x_map)) rom.write_bytes(0x53df6+idx*2, int16_as_bytes(x_map))
rom.write_bytes(0x53e16+idx*2, int16_as_bytes(0xFC0)) rom.write_bytes(0x53e16+idx*2, int16_as_bytes(0xFC0))
if world.compassshuffle[player] and world.overworld_map[player] == 'compass': if world.overworld_map[player] == 'compass':
compass_mode |= 0x20 # check for compass
if world.compassshuffle[player]:
compass_mode |= 0x40 # compasses are wild compass_mode |= 0x40 # compasses are wild
elif world.overworld_map[player] == 'map':
if world.mapshuffle[player]:
compass_mode |= 0x40 # maps are wild
for dungeon, portal_list in dungeon_portals.items(): for dungeon, portal_list in dungeon_portals.items():
ow_map_index = dungeon_table[dungeon].map_index ow_map_index = dungeon_table[dungeon].map_index
if len(portal_list) == 1: if len(portal_list) == 1:
@@ -1680,6 +1544,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
write_strings(rom, world, player, team) write_strings(rom, world, player, team)
# write initial sram
rom.write_initial_sram()
rom.write_byte(0x18636C, 1 if world.remote_items[player] else 0) rom.write_byte(0x18636C, 1 if world.remote_items[player] else 0)
# set rom name # set rom name
@@ -2451,8 +2318,8 @@ def write_strings(rom, world, player, team):
# inverted spawn menu changes # inverted spawn menu changes
if world.mode[player] == 'inverted': if world.mode[player] == 'inverted':
tt['menu_start_2'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n{CHOICE3}" tt['menu_start_2'] = "{MENU}\n{SPEED0}\n≥@'s House\n Dark Chapel\n{CHOICE3}"
tt['menu_start_3'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n Mountain Cave\n{CHOICE2}" tt['menu_start_3'] = "{MENU}\n{SPEED0}\n≥@'s House\n Dark Chapel\n Mountain Cave\n{CHOICE2}"
tt['intro_main'] = CompressedTextMapper.convert( tt['intro_main'] = CompressedTextMapper.convert(
"{INTRO}\n Episode III\n{PAUSE3}\n A Link to\n the Past\n" "{INTRO}\n Episode III\n{PAUSE3}\n A Link to\n the Past\n"
+ "{PAUSE3}\nInverted\n Randomizer\n{PAUSE3}\nAfter mostly disregarding what happened in the first two games.\n" + "{PAUSE3}\nInverted\n Randomizer\n{PAUSE3}\nAfter mostly disregarding what happened in the first two games.\n"
@@ -2517,6 +2384,16 @@ def text_for_item(item, world, player, team):
else: else:
return f'{item.hint_text} for {world.player_names[item.player][team]}' return f'{item.hint_text} for {world.player_names[item.player][team]}'
def init_open_mode_sram(rom):
rom.initial_sram.pre_open_castle_gate()
rom.initial_sram.set_progress_indicator(0x02)
rom.initial_sram.set_progress_flags(0x14)
rom.initial_sram.set_starting_entrance(0x01)
def init_standard_mode_sram(rom):
rom.initial_sram.set_progress_indicator(0x00)
rom.initial_sram.set_progress_flags(0x0)
rom.initial_sram.set_starting_entrance(0x00)
def set_inverted_mode(world, player, rom): def set_inverted_mode(world, player, rom):
rom.write_byte(snes_to_pc(0x0283E0), 0xF0) # residual portals rom.write_byte(snes_to_pc(0x0283E0), 0xF0) # residual portals

139
Text.py
View File

@@ -45,7 +45,7 @@ Uncle_texts = [
"I am leaving\nforever.\nGoodbye.", "I am leaving\nforever.\nGoodbye.",
"Don't worry.\nI got this\ncovered.", "Don't worry.\nI got this\ncovered.",
"Race you to\nthe castle!", "Race you to\nthe castle!",
"\n hi", "\n Hi",
"I'M JUST GOING\nOUT FOR A\nPACK OF SMOKES", "I'M JUST GOING\nOUT FOR A\nPACK OF SMOKES",
"It's dangerous\nto go alone.\nSee ya!", "It's dangerous\nto go alone.\nSee ya!",
"ARE YOU A BAD\nENOUGH DUDE TO\nRESCUE ZELDA?", "ARE YOU A BAD\nENOUGH DUDE TO\nRESCUE ZELDA?",
@@ -94,44 +94,44 @@ Triforce_texts = [
'This was meant to be a trapezoid', 'This was meant to be a trapezoid',
# these ones are from web randomizer # these ones are from web randomizer
"\n G G", "\n G G",
"All your base\nare belong\nto us.", " All your base\n are belong\n to us.",
"You have ended\nthe domination\nof Dr. Wily", " You have ended\n the domination\n of Dr. Wily",
" thanks for\n playing!!!", " Thanks for\n playing!!!",
"\n You Win!", "\n You Win!",
" Thank you!\n your quest\n is over.", " Thank you!\n Your quest\n is over.",
" A winner\n is\n you!", " A winner\n is you!",
"\n WINNER!!", "\n WINNER!!",
"\n I'm sorry\n\n but your\nprincess is in\nanother castle", "\n I'm sorry\n\nbut our princess is\n in another castle",
"\n success!", "\n Success!",
" Whelp…\n that just\n happened", " Whelp…\n that just\n happened",
" Oh hey…\n it's you", " Oh hey…\n it's you",
"\n Wheeeeee!!", "\n Wheeeeee!!",
" Time for\n another one?", " Time for\n another one?",
"and\n\n scene", " And\n\n scene",
"\n GOT EM!!", "\n GOT EM!!",
"\nTHE VALUUUE!!!", "\n THE VALUUUE!!!",
"Cool seed,\n\nright?", " Cool seed,\n\n right?",
"\n We did it!", "\n We did it!",
" Spam those\n emotes in\n wilds chat", " Spam those\n emotes in\n wilds chat",
"\n O M G", "\n O M G",
" Hello. Will\n you be my\n friend?", " Hello. Will you\n you be my friend?",
" Beetorp\n was\n here!", " Beetorp\n was\n here!",
"The Wind Fish\nwill wake\nsoon. Hoot!", " The Wind Fish\n will wake soon.\n Hoot!",
"meow meow meow\nmeow meow meow\n oh my god!", " Meow Meow Meow\n Meow Meow Meow\n Oh my god!",
"Ahhhhhhhhh\nYa ya yaaaah\nYa ya yaaah", " Ahhhhhhhhh\n Ya ya yaaaah\n Ya ya yaaah",
".done\n\n.comment lol", " .done\n\n .comment lol",
"You get to\ndrink from\nthe firehose", " You get to\n drink from\n the firehose",
"Do you prefer\n bacon, pork,\n or ham?", " Do you prefer\n bacon, pork,\n or ham?",
"You get one\nwish. Choose\nwisely, hero!", " You get one\n wish. Choose\n wisely, hero!",
"Can you please\nbreak us three\nup? Thanks.", " Can you please\n break us three\n up? Thanks.",
" Pick us up\n before we\n get dizzy!", " Pick us up\n before we\n get dizzy!",
"Thank you,\nMikey. Youre\n2 minutes late", " Thank you,\n Mikey. Youre\n 2 minutes late",
"This was a\n7000 series\ntrain.", " This was a\n 7000 series\n train.",
" I'd buy\n that for\n a rupee!", " I'd buy\n that for\n a rupee!",
" Did you like\n that bow\n placement?", " Did you like\n that bow\n placement?",
"I promise the\nnext seed will\nbe better.", " I promise the\n next seed will\n be better.",
"\n Honk.", "\n Honk.",
"Breakfast\nis served!", " Breakfast\n is served!",
] ]
BombShop2_texts = ['Bombs!\nBombs!\nBiggest!\nBestest!\nGreatest!\nBoomest!'] BombShop2_texts = ['Bombs!\nBombs!\nBiggest!\nBestest!\nGreatest!\nBoomest!']
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?']
@@ -217,7 +217,7 @@ Ganon1_texts = [
"The Hemiptera\nor true bugs\nare an order\nof insects\ncovering 50k\nto 80k species\nlike aphids,\ncicadas, and\nshield bugs.", "The Hemiptera\nor true bugs\nare an order\nof insects\ncovering 50k\nto 80k species\nlike aphids,\ncicadas, and\nshield bugs.",
"Thanks for\ndropping in.\nThe first\npassengers\nin a hot\nair balloon\nwere a duck,\na sheep,\nand a rooster.", "Thanks for\ndropping in.\nThe first\npassengers\nin a hot\nair balloon\nwere a duck,\na sheep,\nand a rooster.",
"You think you\nare so smart?\n\nI bet you\ndidn't know\nyou can't hum\nwhile holding\nyour nose\nclosed.", "You think you\nare so smart?\n\nI bet you\ndidn't know\nyou can't hum\nwhile holding\nyour nose\nclosed.",
"grumble,\n\ngrumble…\ngrumble,\n\ngrumble…\nSeriously, you\nwere supposed\nto bring food.", "Grumble,\n\ngrumble…\nGrumble,\n\ngrumble…\nSeriously, you\nwere supposed\nto bring food.",
"Join me hero,\nand I shall\nmake your face\nthe greatest\nin the Dark\nWorld!\n\nOr else you\nwill die!", "Join me hero,\nand I shall\nmake your face\nthe greatest\nin the Dark\nWorld!\n\nOr else you\nwill die!",
"Why rule over\na desert full\nof stereotypes\nwhen I can\ncorrupt a\nworld into\npure evil and\nrule over\nthat instead?", "Why rule over\na desert full\nof stereotypes\nwhen I can\ncorrupt a\nworld into\npure evil and\nrule over\nthat instead?",
"When I conquer\nthe Light\nWorld, I'll\nhold a parade\nof all my\nmonsters to\ndemonstrate my\nmight to the\npeople!", "When I conquer\nthe Light\nWorld, I'll\nhold a parade\nof all my\nmonsters to\ndemonstrate my\nmight to the\npeople!",
@@ -627,8 +627,7 @@ class MultiByteCoreTextMapper(object):
} }
@classmethod @classmethod
def convert(cls, text, pause=True, wrap=14): def convert(cls, text, pause=True, wrap=19):
text = text.upper()
lines = text.split('\n') lines = text.split('\n')
outbuf = bytearray() outbuf = bytearray()
lineindex = 0 lineindex = 0
@@ -655,7 +654,8 @@ class MultiByteCoreTextMapper(object):
pending_space = False pending_space = False
while words: while words:
word = words.pop(0) word = words.pop(0)
# sanity check: if the word we have is more than 14 characters, we take as much as we can still fit and push the rest back for later # sanity check: if the word we have is more than 19 characters,
# we take as much as we can still fit and push the rest back for later
if cls.wordlen(word) > wrap: if cls.wordlen(word) > wrap:
(word_first, word_rest) = cls.splitword(word, linespace) (word_first, word_rest) = cls.splitword(word, linespace)
words.insert(0, word_rest) words.insert(0, word_rest)
@@ -736,7 +736,7 @@ class CompressedTextMapper(object):
} }
@classmethod @classmethod
def convert(cls, text, pause=True, max_bytes_expanded=0x800, wrap=14): def convert(cls, text, pause=True, max_bytes_expanded=0x800, wrap=19):
inbuf = MultiByteCoreTextMapper.convert(text, pause, wrap) inbuf = MultiByteCoreTextMapper.convert(text, pause, wrap)
# Links name will need 8 bytes in the target buffer # Links name will need 8 bytes in the target buffer
@@ -772,20 +772,23 @@ class CompressedTextMapper(object):
class CharTextMapper(object): class CharTextMapper(object):
number_offset = None number_offset = None
alpha_offset = 0 alpha_offset = 0
alpha_lower_offset = 0
char_map = {} char_map = {}
@classmethod @classmethod
def map_char(cls, char): def map_char(cls, char):
if cls.number_offset is not None: if cls.number_offset is not None:
if 0x30 <= ord(char) <= 0x39: if 0x30 <= ord(char) <= 0x39:
return ord(char) + cls.number_offset return ord(char) + cls.number_offset
if 0x41 <= ord(char) <= 0x5A:
return ord(char) + 0x20 + cls.alpha_offset
if 0x61 <= ord(char) <= 0x7A: if 0x61 <= ord(char) <= 0x7A:
return ord(char) + cls.alpha_offset return ord(char) + cls.alpha_lower_offset
return cls.char_map.get(char, cls.char_map[' ']) return cls.char_map.get(char, cls.char_map[' '])
@classmethod @classmethod
def convert(cls, text): def convert(cls, text):
buf = bytearray() buf = bytearray()
for char in text.lower(): for char in text:
buf.append(cls.map_char(char)) buf.append(cls.map_char(char))
return buf return buf
@@ -1240,6 +1243,7 @@ class RawMBTextMapper(CharTextMapper):
"": 0xFE, "": 0xFE,
"": 0xFF} "": 0xFF}
alpha_offset = 0x49 alpha_offset = 0x49
alpha_lower_offset = -0x31
number_offset = 0x70 number_offset = 0x70
@classmethod @classmethod
@@ -1251,7 +1255,7 @@ class RawMBTextMapper(CharTextMapper):
@classmethod @classmethod
def convert(cls, text): def convert(cls, text):
buf = bytearray() buf = bytearray()
for char in text.lower(): for char in text:
res = cls.map_char(char) res = cls.map_char(char)
if isinstance(res, int): if isinstance(res, int):
buf.extend([0x00, res]) buf.extend([0x00, res])
@@ -1267,16 +1271,19 @@ class GoldCreditMapper(CharTextMapper):
'-': 0x36, '-': 0x36,
'.': 0x37,} '.': 0x37,}
alpha_offset = -0x47 alpha_offset = -0x47
alpha_lower_offset = -0x47
class GreenCreditMapper(CharTextMapper): class GreenCreditMapper(CharTextMapper):
char_map = {' ': 0x9F, char_map = {' ': 0x9F,
'·': 0x52} '·': 0x52}
alpha_offset = -0x29 alpha_offset = -0x29
alpha_lower_offset = -0x29
class RedCreditMapper(CharTextMapper): class RedCreditMapper(CharTextMapper):
char_map = {' ': 0x9F} char_map = {' ': 0x9F}
alpha_offset = -0x61 alpha_offset = -0x61
alpha_lower_offset = -0x61
class LargeCreditTopMapper(CharTextMapper): class LargeCreditTopMapper(CharTextMapper):
char_map = {' ': 0x9F, char_map = {' ': 0x9F,
@@ -1296,6 +1303,7 @@ class LargeCreditTopMapper(CharTextMapper):
'': 0xAA, '': 0xAA,
'': 0xAB,} '': 0xAB,}
alpha_offset = -0x04 alpha_offset = -0x04
alpha_lower_offset = -0x04
number_offset = 0x23 number_offset = 0x23
@@ -1317,6 +1325,7 @@ class LargeCreditBottomMapper(CharTextMapper):
'': 0xCA, '': 0xCA,
'': 0xCB,} '': 0xCB,}
alpha_offset = 0x22 alpha_offset = 0x22
alpha_lower_offset = 0x22
number_offset = 0x49 number_offset = 0x49
class TextTable(object): class TextTable(object):
@@ -1549,7 +1558,7 @@ class TextTable(object):
text['tutorial_guard_7'] = CompressedTextMapper.convert("Jeeze! There really are a lot of things.") text['tutorial_guard_7'] = CompressedTextMapper.convert("Jeeze! There really are a lot of things.")
text['priest_sanctuary_before_leave'] = CompressedTextMapper.convert("Go be a hero!") text['priest_sanctuary_before_leave'] = CompressedTextMapper.convert("Go be a hero!")
text['sanctuary_enter'] = CompressedTextMapper.convert("YAY!\nYou saved Zelda!") text['sanctuary_enter'] = CompressedTextMapper.convert("YAY!\nYou saved Zelda!")
text['zelda_sanctuary_story'] = CompressedTextMapper.convert("Do you want to hear me say this again?\n{HARP}\nno\n yes\n{CHOICE}") text['zelda_sanctuary_story'] = CompressedTextMapper.convert("Do you want to hear me say this again?\n{HARP}\nNo\n Yes\n{CHOICE}")
text['priest_sanctuary_before_pendants'] = CompressedTextMapper.convert("Go'on and get them pendants so you can beat up Agahnim.") text['priest_sanctuary_before_pendants'] = CompressedTextMapper.convert("Go'on and get them pendants so you can beat up Agahnim.")
text['priest_sanctuary_after_pendants_before_master_sword'] = CompressedTextMapper.convert("Kudos! But seriously, you should be getting the master sword, not having a kegger in here.") text['priest_sanctuary_after_pendants_before_master_sword'] = CompressedTextMapper.convert("Kudos! But seriously, you should be getting the master sword, not having a kegger in here.")
text['priest_sanctuary_dying'] = CompressedTextMapper.convert("They took her to the castle! Take your sword and save her!") text['priest_sanctuary_dying'] = CompressedTextMapper.convert("They took her to the castle! Take your sword and save her!")
@@ -1563,14 +1572,14 @@ class TextTable(object):
text['zelda_push_throne'] = CompressedTextMapper.convert("Let's push it from the left!") text['zelda_push_throne'] = CompressedTextMapper.convert("Let's push it from the left!")
text['zelda_switch_room_pull'] = CompressedTextMapper.convert("Pull this lever using A.") text['zelda_switch_room_pull'] = CompressedTextMapper.convert("Pull this lever using A.")
text['zelda_save_lets_go'] = CompressedTextMapper.convert("Let's get out of here!") text['zelda_save_lets_go'] = CompressedTextMapper.convert("Let's get out of here!")
text['zelda_save_repeat'] = CompressedTextMapper.convert("I like talking, do you?\nno\n yes\n{CHOICE}") text['zelda_save_repeat'] = CompressedTextMapper.convert("I like talking, do you?\nNo\n Yes\n{CHOICE}")
text['zelda_before_pendants'] = CompressedTextMapper.convert("You need to find all the pendants…\n\n\nNumpty.") text['zelda_before_pendants'] = CompressedTextMapper.convert("You need to find all the pendants…\n\n\nNumpty.")
text['zelda_after_pendants_before_master_sword'] = CompressedTextMapper.convert("Very pretty pendants, but really you should be getting that sword in the forest!") text['zelda_after_pendants_before_master_sword'] = CompressedTextMapper.convert("Very pretty pendants, but really you should be getting that sword in the forest!")
text['telepathic_zelda_right_after_master_sword'] = CompressedTextMapper.convert("{NOBORDER}\n{SPEED6}\nHi @,\nHave you been thinking about me?\narrrrrgghh…\n… … …") text['telepathic_zelda_right_after_master_sword'] = CompressedTextMapper.convert("{NOBORDER}\n{SPEED6}\nHi @,\nHave you been thinking about me?\narrrrrgghh…\n… … …")
text['zelda_sewers'] = CompressedTextMapper.convert("Just a little further to the Sanctuary.") text['zelda_sewers'] = CompressedTextMapper.convert("Just a little further to the Sanctuary.")
text['zelda_switch_room'] = CompressedTextMapper.convert("The Sanctuary!\n\nPull my finger") text['zelda_switch_room'] = CompressedTextMapper.convert("The Sanctuary!\n\nPull my finger")
text['kakariko_saharalasa_wife'] = CompressedTextMapper.convert("Heya, @!\nLong time no see.\nYou want a master sword?\n\nWell good luck with that.") text['kakariko_saharalasa_wife'] = CompressedTextMapper.convert("Heya, @!\nLong time no see.\nYou want a master sword?\n\nWell good luck with that.")
text['kakariko_saharalasa_wife_sword_story'] = CompressedTextMapper.convert("It occurs to me that I like toast and jam, but cheese and crackers is better.\nYou like?\ncheese\n jam\n{CHOICE}") text['kakariko_saharalasa_wife_sword_story'] = CompressedTextMapper.convert("It occurs to me that I like toast and jam, but cheese and crackers is better.\nYou like?\nCheese\n Jam\n{CHOICE}")
text['kakariko_saharalasa_wife_closing'] = CompressedTextMapper.convert("Anywho, I have things to do. You see those 2 ovens?\n\nYeah 2!\nWho has 2 ovens nowadays?") text['kakariko_saharalasa_wife_closing'] = CompressedTextMapper.convert("Anywho, I have things to do. You see those 2 ovens?\n\nYeah 2!\nWho has 2 ovens nowadays?")
text['kakariko_saharalasa_after_master_sword'] = CompressedTextMapper.convert("Cool sword!\n\n\n\n\n\n\n\n\nPlease save us") text['kakariko_saharalasa_after_master_sword'] = CompressedTextMapper.convert("Cool sword!\n\n\n\n\n\n\n\n\nPlease save us")
text['kakariko_alert_guards'] = CompressedTextMapper.convert("GUARDS! HELP!\nThe creeper\n@ is here!") text['kakariko_alert_guards'] = CompressedTextMapper.convert("GUARDS! HELP!\nThe creeper\n@ is here!")
@@ -1580,7 +1589,7 @@ class TextTable(object):
text['sahasrahla_quest_information'] = CompressedTextMapper.convert( text['sahasrahla_quest_information'] = CompressedTextMapper.convert(
"{BOTTOM}\n" "{BOTTOM}\n"
+ "Sahasrahla, I am. You would do well to find the 3 pendants from the 3 dungeons in the Light World.\n" + "Sahasrahla, I am. You would do well to find the 3 pendants from the 3 dungeons in the Light World.\n"
+ "Understand?\nyes\n no\n{CHOICE}") + "Understand?\nYes\n No\n{CHOICE}")
text['sahasrahla_bring_courage'] = CompressedTextMapper.convert( text['sahasrahla_bring_courage'] = CompressedTextMapper.convert(
"{BOTTOM}\n" "{BOTTOM}\n"
+ "While you're here, could you do me a solid and get the green pendant from that dungeon?\n" + "While you're here, could you do me a solid and get the green pendant from that dungeon?\n"
@@ -1671,15 +1680,15 @@ class TextTable(object):
text['talking_tree_other'] = CompressedTextMapper.convert("I can breathe!") text['talking_tree_other'] = CompressedTextMapper.convert("I can breathe!")
text['item_get_pendant_power_alt'] = CompressedTextMapper.convert("We have the Pendant of Power! How robust!") text['item_get_pendant_power_alt'] = CompressedTextMapper.convert("We have the Pendant of Power! How robust!")
text['item_get_pendant_wisdom_alt'] = CompressedTextMapper.convert("We have the Pendant of Wisdom! How astute!") text['item_get_pendant_wisdom_alt'] = CompressedTextMapper.convert("We have the Pendant of Wisdom! How astute!")
text['game_shooting_choice'] = CompressedTextMapper.convert("20 rupees.\n5 arrows.\nWin rupees!\nWant to play?\nyes\n no\n{CHOICE}") text['game_shooting_choice'] = CompressedTextMapper.convert("20 rupees.\n5 arrows.\nWin rupees!\nWant to play?\nYes\n No\n{CHOICE}")
text['game_shooting_yes'] = CompressedTextMapper.convert("Let's do this!") text['game_shooting_yes'] = CompressedTextMapper.convert("Let's do this!")
text['game_shooting_no'] = CompressedTextMapper.convert("Where are you going? Straight up!") text['game_shooting_no'] = CompressedTextMapper.convert("Where are you going? Straight up!")
text['game_shooting_continue'] = CompressedTextMapper.convert("Keep playing?\n ≥ yes\n no\n{CHOICE}") text['game_shooting_continue'] = CompressedTextMapper.convert("Keep playing?\n ≥ yes\n no\n{CHOICE}")
text['pond_of_wishing'] = CompressedTextMapper.convert("-Wishing Pond-\n\n On Vacation") text['pond_of_wishing'] = CompressedTextMapper.convert("-Wishing Pond-\n\n On Vacation")
text['pond_item_select'] = CompressedTextMapper.convert("Pick something\nto throw in.\n{ITEMSELECT}") text['pond_item_select'] = CompressedTextMapper.convert("Pick something\nto throw in.\n{ITEMSELECT}")
text['pond_item_test'] = CompressedTextMapper.convert("You toss this?\nyup\n wrong\n{CHOICE}") text['pond_item_test'] = CompressedTextMapper.convert("You toss this?\nYup\n Wrong\n{CHOICE}")
text['pond_will_upgrade'] = CompressedTextMapper.convert("You're honest, so I'll give you a present.") text['pond_will_upgrade'] = CompressedTextMapper.convert("You're honest, so I'll give you a present.")
text['pond_item_test_no'] = CompressedTextMapper.convert("You sure?\noh yeah\n um\n{CHOICE}") text['pond_item_test_no'] = CompressedTextMapper.convert("You sure?\nOh yeah\n Um\n{CHOICE}")
text['pond_item_test_no_no'] = CompressedTextMapper.convert("Well, I don't want it, so take it back.") text['pond_item_test_no_no'] = CompressedTextMapper.convert("Well, I don't want it, so take it back.")
text['pond_item_boomerang'] = CompressedTextMapper.convert("I don't much like you, so have this worse Boomerang.") text['pond_item_boomerang'] = CompressedTextMapper.convert("I don't much like you, so have this worse Boomerang.")
# 90 # 90
@@ -1688,7 +1697,7 @@ class TextTable(object):
text['pond_item_bottle_filled'] = CompressedTextMapper.convert("Bottle Filled!\nMoney Saved!") text['pond_item_bottle_filled'] = CompressedTextMapper.convert("Bottle Filled!\nMoney Saved!")
text['pond_item_sword'] = CompressedTextMapper.convert("Thank you for the sword, here is a stick of butter.") text['pond_item_sword'] = CompressedTextMapper.convert("Thank you for the sword, here is a stick of butter.")
text['pond_of_wishing_happiness'] = CompressedTextMapper.convert("Happiness up!\nYou are now\nᚌᚋ happy!") text['pond_of_wishing_happiness'] = CompressedTextMapper.convert("Happiness up!\nYou are now\nᚌᚋ happy!")
text['pond_of_wishing_choice'] = CompressedTextMapper.convert("Your wish?\nmore bombs\n more arrows\n{CHOICE}") text['pond_of_wishing_choice'] = CompressedTextMapper.convert("Your wish?\nMore bombs\n More arrows\n{CHOICE}")
text['pond_of_wishing_bombs'] = CompressedTextMapper.convert("Woo-hoo!\nYou can now\ncarry ᚌᚋ bombs") text['pond_of_wishing_bombs'] = CompressedTextMapper.convert("Woo-hoo!\nYou can now\ncarry ᚌᚋ bombs")
text['pond_of_wishing_arrows'] = CompressedTextMapper.convert("Woo-hoo!\nYou can now\nhold ᚌᚋ arrows") text['pond_of_wishing_arrows'] = CompressedTextMapper.convert("Woo-hoo!\nYou can now\nhold ᚌᚋ arrows")
text['pond_of_wishing_full_upgrades'] = CompressedTextMapper.convert("Youhave all I can give you, here are your rupees back.") text['pond_of_wishing_full_upgrades'] = CompressedTextMapper.convert("Youhave all I can give you, here are your rupees back.")
@@ -1709,7 +1718,7 @@ class TextTable(object):
text['running_man'] = CompressedTextMapper.convert("Hi, Do you\nknow Veetorp?\n\nYou really\nshould. And\nall the other great guys who made this possible.\nGo thank them.\n\n\nIf you can catch them…") text['running_man'] = CompressedTextMapper.convert("Hi, Do you\nknow Veetorp?\n\nYou really\nshould. And\nall the other great guys who made this possible.\nGo thank them.\n\n\nIf you can catch them…")
text['game_race_sign'] = CompressedTextMapper.convert("Why are you reading this sign? Run!!!") text['game_race_sign'] = CompressedTextMapper.convert("Why are you reading this sign? Run!!!")
text['sign_bumper_cave'] = CompressedTextMapper.convert("You need Cape, but not Hookshot") text['sign_bumper_cave'] = CompressedTextMapper.convert("You need Cape, but not Hookshot")
text['sign_catfish'] = CompressedTextMapper.convert("toss rocks\ntoss items\ntoss cookies") text['sign_catfish'] = CompressedTextMapper.convert("Toss rocks\nToss items\nToss cookies")
text['sign_north_village_of_outcasts'] = CompressedTextMapper.convert("↑ Skull Woods\n\n↓ Steve's Town") text['sign_north_village_of_outcasts'] = CompressedTextMapper.convert("↑ Skull Woods\n\n↓ Steve's Town")
text['sign_south_of_bumper_cave'] = CompressedTextMapper.convert("\n→ Karkats cave") text['sign_south_of_bumper_cave'] = CompressedTextMapper.convert("\n→ Karkats cave")
text['sign_east_of_pyramid'] = CompressedTextMapper.convert("\n→ Dark Palace") text['sign_east_of_pyramid'] = CompressedTextMapper.convert("\n→ Dark Palace")
@@ -1717,7 +1726,7 @@ class TextTable(object):
text['sign_east_of_mire'] = CompressedTextMapper.convert("\n← Misery Mire\n no way in.\n no way out.") text['sign_east_of_mire'] = CompressedTextMapper.convert("\n← Misery Mire\n no way in.\n no way out.")
text['sign_village_of_outcasts'] = CompressedTextMapper.convert("Have a Trulie Awesome Day!") text['sign_village_of_outcasts'] = CompressedTextMapper.convert("Have a Trulie Awesome Day!")
# B0 # B0
text['sign_before_wishing_pond'] = CompressedTextMapper.convert("waterfall\nup ahead\nmake wishes") text['sign_before_wishing_pond'] = CompressedTextMapper.convert("Waterfall\nup ahead\nMake wishes")
text['sign_before_catfish_area'] = CompressedTextMapper.convert("→↑ Have you met Woeful Ike?") text['sign_before_catfish_area'] = CompressedTextMapper.convert("→↑ Have you met Woeful Ike?")
text['castle_wall_guard'] = CompressedTextMapper.convert("Looking for a Princess? Look downstairs.") text['castle_wall_guard'] = CompressedTextMapper.convert("Looking for a Princess? Look downstairs.")
text['gate_guard'] = CompressedTextMapper.convert("No Lonks Allowed!") text['gate_guard'] = CompressedTextMapper.convert("No Lonks Allowed!")
@@ -1731,7 +1740,7 @@ class TextTable(object):
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("Secondary tournament winners\n{HARP}\n ~~~2017~~~\nA: Zaen") text['telepathic_tile_under_ganon'] = CompressedTextMapper.convert("Doors Async League winners\n{HARP}\n ~~~2022~~~\nAndy\n\n ~~~2021~~~\nprdwong")
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.")
@@ -1741,9 +1750,9 @@ class TextTable(object):
text['telepathic_tile_ice_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nYou can use Fire Rod or Bombos to pass.") text['telepathic_tile_ice_entrance'] = 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['houlihan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n ~~~2018~~~\nS: Andy\n\n ~~~2017~~~\nA: ajneb174\nS: ajneb174") text['houlihan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n ~~~2021~~~\nDaaanty\n\n ~~~2019~~~\nJet082\n\n ~~~2018~~~\nAndy\n\n ~~~2017~~~\nA: ajneb174\nS: ajneb174")
text['caught_a_bee'] = CompressedTextMapper.convert("Caught a Bee\nkeep\n release\n{CHOICE}") text['caught_a_bee'] = CompressedTextMapper.convert("Caught a Bee\nKeep\n Release\n{CHOICE}")
text['caught_a_fairy'] = CompressedTextMapper.convert("Caught Fairy!\nkeep\n release\n{CHOICE}") text['caught_a_fairy'] = CompressedTextMapper.convert("Caught Fairy!\nKeep\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.")
text['game_race_boy_time'] = CompressedTextMapper.convert("Your time was\nᚎᚍ min ᚌᚋ sec.") text['game_race_boy_time'] = CompressedTextMapper.convert("Your time was\nᚎᚍ min ᚌᚋ sec.")
text['game_race_girl'] = CompressedTextMapper.convert("You have 15 seconds,\nGo… Go… Go…") text['game_race_girl'] = CompressedTextMapper.convert("You have 15 seconds,\nGo… Go… Go…")
@@ -1773,7 +1782,7 @@ class TextTable(object):
text['blacksmiths_still_working'] = CompressedTextMapper.convert("Something this precious takes time… Come back later.") text['blacksmiths_still_working'] = CompressedTextMapper.convert("Something this precious takes time… Come back later.")
text['blacksmiths_saving_bows'] = CompressedTextMapper.convert("Thanks!\n\nThanks!") text['blacksmiths_saving_bows'] = CompressedTextMapper.convert("Thanks!\n\nThanks!")
text['blacksmiths_hammer_anvil'] = CompressedTextMapper.convert("Dernt Take Er Jerbs!") text['blacksmiths_hammer_anvil'] = CompressedTextMapper.convert("Dernt Take Er Jerbs!")
text['dark_flute_boy_storytime'] = CompressedTextMapper.convert("Hi!\nI'm Stumpy\nI've been chillin' in this world for a while now, but I miss my flute. If I gave you a shovel, would you go digging for it?\nsure\n nahh\n{CHOICE}") text['dark_flute_boy_storytime'] = CompressedTextMapper.convert("Hi!\nI'm Stumpy\nI've been chillin' in this world for a while now, but I miss my flute. If I gave you a shovel, would you go digging for it?\nSure\n Nahh\n{CHOICE}")
text['dark_flute_boy_get_shovel'] = CompressedTextMapper.convert("Schaweet! Here you go. Happy digging!") text['dark_flute_boy_get_shovel'] = CompressedTextMapper.convert("Schaweet! Here you go. Happy digging!")
text['dark_flute_boy_no_get_shovel'] = CompressedTextMapper.convert("Oh I see, not good enough for you… FINE!") text['dark_flute_boy_no_get_shovel'] = CompressedTextMapper.convert("Oh I see, not good enough for you… FINE!")
text['dark_flute_boy_flute_not_found'] = CompressedTextMapper.convert("Still haven't found the item? Dig in the Light World around here, dingus!") text['dark_flute_boy_flute_not_found'] = CompressedTextMapper.convert("Still haven't found the item? Dig in the Light World around here, dingus!")
@@ -1787,7 +1796,7 @@ class TextTable(object):
text['shop_fortune_teller_lw_hint_6'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, Spin, Hammer, or Net to hurt Agahnim") text['shop_fortune_teller_lw_hint_6'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, Spin, Hammer, or Net to hurt Agahnim")
text['shop_fortune_teller_lw_hint_7'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, You can jump in the well by the blacksmiths") text['shop_fortune_teller_lw_hint_7'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, You can jump in the well by the blacksmiths")
text['shop_fortune_teller_lw_no_rupees'] = CompressedTextMapper.convert("{BOTTOM}\nThe black cats are hungry, come back with rupees") text['shop_fortune_teller_lw_no_rupees'] = CompressedTextMapper.convert("{BOTTOM}\nThe black cats are hungry, come back with rupees")
text['shop_fortune_teller_lw'] = CompressedTextMapper.convert("{BOTTOM}\nWelcome to the Fortune Shoppe!\nFancy a read?\n ≥I must know\n negative\n{CHOICE}") text['shop_fortune_teller_lw'] = CompressedTextMapper.convert("{BOTTOM}\nWelcome to the Fortune Shoppe!\nFancy a read?\n ≥I must know\n Negative\n{CHOICE}")
text['shop_fortune_teller_lw_post_hint'] = CompressedTextMapper.convert("{BOTTOM}\nFor ᚋᚌ rupees\nIt is done.\nBe gone!") text['shop_fortune_teller_lw_post_hint'] = CompressedTextMapper.convert("{BOTTOM}\nFor ᚋᚌ rupees\nIt is done.\nBe gone!")
text['shop_fortune_teller_lw_no'] = CompressedTextMapper.convert("{BOTTOM}\nWell then, why did you even come in here?") text['shop_fortune_teller_lw_no'] = CompressedTextMapper.convert("{BOTTOM}\nWell then, why did you even come in here?")
text['shop_fortune_teller_lw_hint_8'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, why you do?") text['shop_fortune_teller_lw_hint_8'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, why you do?")
@@ -1798,7 +1807,7 @@ class TextTable(object):
text['shop_fortune_teller_lw_hint_13'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, big bombs blow up cracked walls in pyramids") text['shop_fortune_teller_lw_hint_13'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, big bombs blow up cracked walls in pyramids")
text['shop_fortune_teller_lw_hint_14'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, you need all the crystals to open Ganon's Tower") text['shop_fortune_teller_lw_hint_14'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, you need all the crystals to open Ganon's Tower")
text['shop_fortune_teller_lw_hint_15'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, Silver Arrows will defeat Ganon in his final phase") text['shop_fortune_teller_lw_hint_15'] = CompressedTextMapper.convert("{BOTTOM}\nBy the black cats, Silver Arrows will defeat Ganon in his final phase")
text['dark_sanctuary'] = CompressedTextMapper.convert("For 20 rupees I'll tell you something?\nHow about it?\nyes\n no\n{CHOICE}") text['dark_sanctuary'] = CompressedTextMapper.convert("For 20 rupees I'll tell you something?\nHow about it?\nYes\n No\n{CHOICE}")
text['dark_sanctuary_hint_0'] = CompressedTextMapper.convert("I once was a tea kettle, but then I moved up in the world, and now you can see me as this. Makes you wonder. What I could be next time.") text['dark_sanctuary_hint_0'] = CompressedTextMapper.convert("I once was a tea kettle, but then I moved up in the world, and now you can see me as this. Makes you wonder. What I could be next time.")
# 100 # 100
text['dark_sanctuary_no'] = CompressedTextMapper.convert("Then go away!") text['dark_sanctuary_no'] = CompressedTextMapper.convert("Then go away!")
@@ -1814,8 +1823,8 @@ class TextTable(object):
text['sick_kid_trade'] = CompressedTextMapper.convert("{BOTTOM}\nCool Bottle! Here's something for you.") text['sick_kid_trade'] = CompressedTextMapper.convert("{BOTTOM}\nCool Bottle! Here's something for you.")
text['sick_kid_post_trade'] = CompressedTextMapper.convert("{BOTTOM}\nLeave me alone\nI'm sick. You have my item.") text['sick_kid_post_trade'] = CompressedTextMapper.convert("{BOTTOM}\nLeave me alone\nI'm sick. You have my item.")
text['desert_thief_sitting'] = CompressedTextMapper.convert("………………………") text['desert_thief_sitting'] = CompressedTextMapper.convert("………………………")
text['desert_thief_following'] = CompressedTextMapper.convert("why……………") text['desert_thief_following'] = CompressedTextMapper.convert("Why……………")
text['desert_thief_question'] = CompressedTextMapper.convert("I was a thief, I open purple chests!\nKeep secret?\nsure thing\n never!\n{CHOICE}") text['desert_thief_question'] = CompressedTextMapper.convert("I was a thief, I open purple chests!\nKeep secret?\nSure thing\n Never!\n{CHOICE}")
text['desert_thief_question_yes'] = CompressedTextMapper.convert("Cool, bring me any purple chests you find.") text['desert_thief_question_yes'] = CompressedTextMapper.convert("Cool, bring me any purple chests you find.")
text['desert_thief_after_item_get'] = CompressedTextMapper.convert("You tell anyone and I will give you such a pinch!") text['desert_thief_after_item_get'] = CompressedTextMapper.convert("You tell anyone and I will give you such a pinch!")
text['desert_thief_reassure'] = CompressedTextMapper.convert("Bring chests. It's a secret to everyone.") text['desert_thief_reassure'] = CompressedTextMapper.convert("Bring chests. It's a secret to everyone.")
@@ -1841,10 +1850,10 @@ class TextTable(object):
text['bomb_shop_big_bomb'] = CompressedTextMapper.convert("30 bombs for 100 rupees, 100 rupees 1 BIG bomb. Good deals all day!") text['bomb_shop_big_bomb'] = CompressedTextMapper.convert("30 bombs for 100 rupees, 100 rupees 1 BIG bomb. Good deals all day!")
text['bomb_shop_big_bomb_buy'] = CompressedTextMapper.convert("Thanks!\nBoom goes the dynamite!") text['bomb_shop_big_bomb_buy'] = CompressedTextMapper.convert("Thanks!\nBoom goes the dynamite!")
text['item_get_big_bomb'] = CompressedTextMapper.convert("YAY! press A to splode it!") text['item_get_big_bomb'] = CompressedTextMapper.convert("YAY! press A to splode it!")
text['kiki_second_extortion'] = CompressedTextMapper.convert("For 100 more, I'll open this place.\nHow about it?\nopen\n nah\n{CHOICE}") text['kiki_second_extortion'] = CompressedTextMapper.convert("For 100 more, I'll open this place.\nHow about it?\nOpen\n Nah\n{CHOICE}")
text['kiki_second_extortion_no'] = CompressedTextMapper.convert("Heh, good luck getting in.") text['kiki_second_extortion_no'] = CompressedTextMapper.convert("Heh, good luck getting in.")
text['kiki_second_extortion_yes'] = CompressedTextMapper.convert("Yay! Rupees!\nOkay, let's do this!") text['kiki_second_extortion_yes'] = CompressedTextMapper.convert("Yay! Rupees!\nOkay, let's do this!")
text['kiki_first_extortion'] = CompressedTextMapper.convert("I'm Kiki, I like rupees, may I have 10?\nHow about it?\nyes\n no\n{CHOICE}") text['kiki_first_extortion'] = CompressedTextMapper.convert("I'm Kiki, I like rupees, may I have 10?\nHow about it?\nYes\n No\n{CHOICE}")
text['kiki_first_extortion_yes'] = CompressedTextMapper.convert("Nice. I'll tag along with you for a bit.") text['kiki_first_extortion_yes'] = CompressedTextMapper.convert("Nice. I'll tag along with you for a bit.")
# 120 # 120
text['kiki_first_extortion_no'] = CompressedTextMapper.convert("Pfft. I have no reason to hang. See ya!") text['kiki_first_extortion_no'] = CompressedTextMapper.convert("Pfft. I have no reason to hang. See ya!")
@@ -1903,7 +1912,7 @@ class TextTable(object):
text['pond_of_wishing_good_luck'] = CompressedTextMapper.convert("\n is good luck") text['pond_of_wishing_good_luck'] = CompressedTextMapper.convert("\n is good luck")
text['pond_of_wishing_meh_luck'] = CompressedTextMapper.convert("\n is meh luck") text['pond_of_wishing_meh_luck'] = CompressedTextMapper.convert("\n is meh luck")
# Repurposed to no items in Randomizer # Repurposed to no items in Randomizer
text['pond_of_wishing_bad_luck'] = CompressedTextMapper.convert("Why you come in here and pretend like you have something this fountain wants? Come back with bottles!") text['pond_of_wishing_bad_luck'] = CompressedTextMapper.convert("Why come in here and pretend like you have something this fountain wants? Come back with bottles!")
text['pond_of_wishing_fortune'] = CompressedTextMapper.convert("by the way, your fortune,") text['pond_of_wishing_fortune'] = CompressedTextMapper.convert("by the way, your fortune,")
text['item_get_14_heart'] = CompressedTextMapper.convert("3 more to go\n ¼\nYay!") text['item_get_14_heart'] = CompressedTextMapper.convert("3 more to go\n ¼\nYay!")
text['item_get_24_heart'] = CompressedTextMapper.convert("2 more to go\n ½\nWhee!") text['item_get_24_heart'] = CompressedTextMapper.convert("2 more to go\n ½\nWhee!")
@@ -1917,7 +1926,7 @@ class TextTable(object):
text['death_mountain_bully_with_pearl'] = CompressedTextMapper.convert("I think I forgot how to smile…") text['death_mountain_bully_with_pearl'] = CompressedTextMapper.convert("I think I forgot how to smile…")
text['shop_darkworld_enter'] = CompressedTextMapper.convert("It's dangerous outside, buy my crap for safety.") text['shop_darkworld_enter'] = CompressedTextMapper.convert("It's dangerous outside, buy my crap for safety.")
# 160 # 160
text['game_chest_village_of_outcasts'] = CompressedTextMapper.convert("Pay 30 rupees, open 2 chests. Are you lucky?\nSo, Play game?\nplay\n never!\n{CHOICE}") text['game_chest_village_of_outcasts'] = CompressedTextMapper.convert("Pay 30 rupees, open 2 chests. Are you lucky?\nSo, Play game?\nPlay\n Never!\n{CHOICE}")
text['game_chest_no_cash'] = CompressedTextMapper.convert("So, like, you need 30 rupees.\nSilly!") text['game_chest_no_cash'] = CompressedTextMapper.convert("So, like, you need 30 rupees.\nSilly!")
text['game_chest_not_played'] = CompressedTextMapper.convert("You want to play a game?\nTalk to me.") text['game_chest_not_played'] = CompressedTextMapper.convert("You want to play a game?\nTalk to me.")
text['game_chest_played'] = CompressedTextMapper.convert("You've opened the chests!\nTime to go.") text['game_chest_played'] = CompressedTextMapper.convert("You've opened the chests!\nTime to go.")
@@ -1948,28 +1957,28 @@ class TextTable(object):
text['cukeman_2'] = CompressedTextMapper.convert("You found Shabadoo, huh?\nNiiiiice.") text['cukeman_2'] = CompressedTextMapper.convert("You found Shabadoo, huh?\nNiiiiice.")
text['potion_shop_no_cash'] = CompressedTextMapper.convert("Yo! I'm not running a charity here.") text['potion_shop_no_cash'] = CompressedTextMapper.convert("Yo! I'm not running a charity here.")
text['kakariko_powdered_chicken'] = CompressedTextMapper.convert("Smallhacker…\n\n\nWas hiding, you found me!\n\n\nOkay, you can leave now.") text['kakariko_powdered_chicken'] = CompressedTextMapper.convert("Smallhacker…\n\n\nWas hiding, you found me!\n\n\nOkay, you can leave now.")
text['game_chest_south_of_kakariko'] = CompressedTextMapper.convert("Pay 20 rupees, open 1 chest. Are you lucky?\nSo, Play game?\nplay\n never!\n{CHOICE}") text['game_chest_south_of_kakariko'] = CompressedTextMapper.convert("Pay 20 rupees, open 1 chest. Are you lucky?\nSo, Play game?\nPlay\n Never!\n{CHOICE}")
text['game_chest_play_yes'] = CompressedTextMapper.convert("Good luck then") text['game_chest_play_yes'] = CompressedTextMapper.convert("Good luck then")
# 180 # 180
text['game_chest_play_no'] = CompressedTextMapper.convert("Well fine, I didn't want your rupees.") text['game_chest_play_no'] = CompressedTextMapper.convert("Well fine, I didn't want your rupees.")
text['game_chest_lost_woods'] = CompressedTextMapper.convert("Pay 100 rupees open 1 chest. Are you lucky?\nSo, Play game?\nplay\n never!\n{CHOICE}") text['game_chest_lost_woods'] = CompressedTextMapper.convert("Pay 100 rupees open 1 chest. Are you lucky?\nSo, Play game?\nPlay\n Never!\n{CHOICE}")
text['kakariko_flophouse_man_no_flippers'] = CompressedTextMapper.convert("I really hate mowing my yard.\nI moved my house and everyone else's to avoid it.\n{PAGEBREAK}\nI hope you don't mind.") text['kakariko_flophouse_man_no_flippers'] = CompressedTextMapper.convert("I really hate mowing my yard.\nI moved my house and everyone else's to avoid it.\n{PAGEBREAK}\nI hope you don't mind.")
text['kakariko_flophouse_man'] = CompressedTextMapper.convert("I really hate mowing my yard.\nI moved my house and everyone else's to avoid it.\n{PAGEBREAK}\nI hope you don't mind.") text['kakariko_flophouse_man'] = CompressedTextMapper.convert("I really hate mowing my yard.\nI moved my house and everyone else's to avoid it.\n{PAGEBREAK}\nI hope you don't mind.")
text['menu_start_2'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s house\n Sanctuary\n{CHOICE3}", False) text['menu_start_2'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s House\n Sanctuary\n{CHOICE3}", False)
text['menu_start_3'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s house\n Sanctuary\n Mountain Cave\n{CHOICE2}", False) text['menu_start_3'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s House\n Sanctuary\n Mountain Cave\n{CHOICE2}", False)
text['menu_pause'] = CompressedTextMapper.convert("{SPEED0}\ncontinue\n save and quit\n{CHOICE3}", False) text['menu_pause'] = CompressedTextMapper.convert("{SPEED0}\nContinue\n Save and Quit\n{CHOICE3}", False)
text['game_digging_choice'] = CompressedTextMapper.convert("Have 80 Rupees? Want to play digging game?\nyes\n no\n{CHOICE}") text['game_digging_choice'] = CompressedTextMapper.convert("Have 80 Rupees? Want to play digging game?\nYes\n No\n{CHOICE}")
text['game_digging_start'] = CompressedTextMapper.convert("Okay, use the shovel with Y!") text['game_digging_start'] = CompressedTextMapper.convert("Okay, use the shovel with Y!")
text['game_digging_no_cash'] = CompressedTextMapper.convert("Shovel rental is 80 rupees.\nI have all day") text['game_digging_no_cash'] = CompressedTextMapper.convert("Shovel rental is 80 rupees.\nI have all day")
text['game_digging_end_time'] = CompressedTextMapper.convert("Time's up!\nTime for you to go.") text['game_digging_end_time'] = CompressedTextMapper.convert("Time's up!\nTime for you to go.")
text['game_digging_come_back_later'] = CompressedTextMapper.convert("Come back later, I have to bury things.") text['game_digging_come_back_later'] = CompressedTextMapper.convert("Come back later, I have to bury things.")
text['game_digging_no_follower'] = CompressedTextMapper.convert("Something is following you. I don't like.") text['game_digging_no_follower'] = CompressedTextMapper.convert("Something is following you. I don't like.")
text['menu_start_4'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s house\n Mountain Cave\n{CHOICE3}", False) text['menu_start_4'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s House\n Mountain Cave\n{CHOICE3}", False)
# Start of new text data # Start of new text data
text['ganon_fall_in_alt'] = CompressedTextMapper.convert("You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!") text['ganon_fall_in_alt'] = CompressedTextMapper.convert("You think you are ready to face me?\n\nI will not die unless you complete your goals. Dingus!")
text['ganon_phase_3_alt'] = CompressedTextMapper.convert("Got wax in\nyour ears?\nI cannot die!") text['ganon_phase_3_alt'] = CompressedTextMapper.convert("Got wax in your ears? I cannot die!")
# 190 # 190
text['sign_east_death_mountain_bridge'] = CompressedTextMapper.convert("How did you get up here?") text['sign_east_death_mountain_bridge'] = CompressedTextMapper.convert("Glitched\ntournament\nwinners\n{HARP}\n~~~HMG 2021~~~\nKrithel\n\n~~~OWG 2019~~~\nGlan\n\n~~~OWG 2018~~~\nChristosOwen\nthe numpty")
text['fish_money'] = CompressedTextMapper.convert("It's a secret to everyone.") text['fish_money'] = CompressedTextMapper.convert("It's a secret to everyone.")
text['sign_ganons_tower'] = CompressedTextMapper.convert("You need all 7 crystals to enter.") text['sign_ganons_tower'] = CompressedTextMapper.convert("You need all 7 crystals to enter.")
text['sign_ganon'] = CompressedTextMapper.convert("You need all 7 crystals to beat Ganon.") text['sign_ganon'] = CompressedTextMapper.convert("You need all 7 crystals to beat Ganon.")

View File

@@ -678,6 +678,11 @@ def extract_data_from_jp_rom(rom):
# print_data_block(secretdata) # print_data_block(secretdata)
# print() # print()
def count_set_bits(val):
if val == 0:
return 0
else:
return (val & 1) + count_set_bits(val >> 1)
def check_pots(): def check_pots():
from PotShuffle import vanilla_pots from PotShuffle import vanilla_pots

View File

@@ -196,8 +196,8 @@ OldHudToNewHudTable:
dw 1, 2, 3, 10, 4, 6, 5, 8, 11, 9, 7, 12, 13 dw 1, 2, 3, 10, 4, 6, 5, 8, 11, 9, 7, 12, 13
IndicatorCharacters: IndicatorCharacters:
; check G P R C ; check 1 2 3 4 5 6 7 G B R
dw $2426, $2590, $2599, $259B, $258C dw $2426, $2817, $2818, $2819, $281A, $281B, $281C, $281D, $2590, $258B, $259B
MapIndicator: MapIndicator:
LDA.l CrystalPendantFlags_3, X : AND #$00FF LDA.l CrystalPendantFlags_3, X : AND #$00FF
@@ -223,7 +223,7 @@ ConvertToDisplay:
ConvertToDisplay2: ConvertToDisplay2:
and.w #$00ff : beq ++ and.w #$00ff : beq ++
cmp #$000a : !blt + cmp #$000a : !blt +
!add #$2553 : rts !add #$2553 : rts ; todo: use 2580 with 258A as "A" for non transparent digits
+ !add #$2816 : rts + !add #$2816 : rts
++ lda #$2827 : rts ; 0/O for 0 or placeholder digit ;2483 ++ lda #$2827 : rts ; 0/O for 0 or placeholder digit ;2483

Binary file not shown.

View File

@@ -5,6 +5,10 @@
major_only: 1 major_only: 1
dungeon_only: 1 dungeon_only: 1
district: 1 district: 1
restrict_boss_items:
none: 1
# mapcompass: 1 Bug exists, not recommended
dungeon: 1
door_shuffle: door_shuffle:
vanilla: 0 vanilla: 0
basic: 2 basic: 2
@@ -17,11 +21,15 @@
on: 1 on: 1
off: 1 off: 1
pottery: pottery:
none: 4 none: 8
keys: 1 keys: 1
cave: 1 cave: 1
dungeon: 1 dungeon: 1
lottery: 1 lottery: 1
cavekeys: 1
reduced: 1
clustered: 1
nonempty: 1
shopsanity: shopsanity:
on: 1 on: 1
off: 1 off: 1

View File

@@ -261,8 +261,8 @@
"Keys: Key pots are included in the location pool and other items can take their place", "Keys: Key pots are included in the location pool and other items can take their place",
"Cave: Only pots in houses and caves are included in the location pool", "Cave: Only pots in houses and caves are included in the location pool",
"CaveKeys: Both pots in houses and caves and keys pots are included in the location pool", "CaveKeys: Both pots in houses and caves and keys pots are included in the location pool",
"Reduced: Same as KeyCaves + 25% of Pots in dungeons (dynamic mode)", "Reduced: Same as KeyCaves + 25%% of Pots in dungeons (dynamic mode)",
"Clustered: Same as KeyCaves + 50% of Pots in dungeons, chosen by logical group (dynamic mode)", "Clustered: Same as KeyCaves + 50%% of Pots in dungeons, chosen by logical group (dynamic mode)",
"NonEmpty: All pots that are not originally empty are included in the location pool", "NonEmpty: All pots that are not originally empty are included in the location pool",
"Dungeon: Only pots in dungeons are included in the location pool", "Dungeon: Only pots in dungeons are included in the location pool",
"Lottery: All pots are part of the location pool" "Lottery: All pots are part of the location pool"

View File

@@ -29,7 +29,6 @@ class LocationGroup(object):
# flags # flags
self.keyshuffle = False self.keyshuffle = False
self.keydropshuffle = False
self.shopsanity = False self.shopsanity = False
self.retro = False self.retro = False
@@ -37,9 +36,8 @@ class LocationGroup(object):
self.locations = list(locs) self.locations = list(locs)
return self return self
def flags(self, k, d=False, s=False, r=False): def flags(self, k, s=False, r=False):
self.keyshuffle = k self.keyshuffle = k
self.keydropshuffle = d
self.shopsanity = s self.shopsanity = s
self.retro = r self.retro = r
return self return self
@@ -124,12 +122,14 @@ def create_item_pool_config(world):
groups = LocationGroup('Major').locs(init_set) groups = LocationGroup('Major').locs(init_set)
if world.bigkeyshuffle[player]: if world.bigkeyshuffle[player]:
groups.locations.extend(mode_grouping['Big Keys']) groups.locations.extend(mode_grouping['Big Keys'])
if world.keydropshuffle[player] != 'none': if world.dropshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Big Key Drops']) groups.locations.extend(mode_grouping['Big Key Drops'])
if world.keyshuffle[player]: if world.keyshuffle[player]:
groups.locations.extend(mode_grouping['Small Keys']) groups.locations.extend(mode_grouping['Small Keys'])
if world.keydropshuffle[player] != 'none': if world.dropshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Key Drops']) groups.locations.extend(mode_grouping['Key Drops'])
if world.pottery[player] not in ['none', 'cave']:
groups.locations.extend(mode_grouping['Pot Keys'])
if world.compassshuffle[player]: if world.compassshuffle[player]:
groups.locations.extend(mode_grouping['Compasses']) groups.locations.extend(mode_grouping['Compasses'])
if world.mapshuffle[player]: if world.mapshuffle[player]:
@@ -275,7 +275,7 @@ def massage_item_pool(world):
world.itempool.remove(deleted) world.itempool.remove(deleted)
discrepancy -= 1 discrepancy -= 1
if discrepancy > 0: if discrepancy > 0:
logging.getLogger('').warning(f'Too many good items in pool, something will be removed at random') raise Exception(f'Too many required items in pool, {discrepancy} items cannot be placed')
if world.item_pool_config.placeholders is not None: if world.item_pool_config.placeholders is not None:
removed = 0 removed = 0
single_rupees = [item for item in world.itempool if item.name == 'Rupee (1)'] single_rupees = [item for item in world.itempool if item.name == 'Rupee (1)']
@@ -329,49 +329,6 @@ def count_major_items(config, world, player):
return sum(1 for x in world.itempool if x.name in config.item_pool[player] and x.player == player) return sum(1 for x in world.itempool if x.name in config.item_pool[player] and x.player == player)
def calc_dungeon_limits(world, player):
b, s, c, m, k, r, bi = (world.bigkeyshuffle[player], world.keyshuffle[player], world.compassshuffle[player],
world.mapshuffle[player], world.keydropshuffle[player], world.retro[player],
world.restrict_boss_items[player])
if world.doorShuffle[player] in ['vanilla', 'basic']:
limits = {}
for dungeon, info in dungeon_table.items():
val = info.free_items
if bi != 'none' and info.prize:
if bi == 'mapcompass' and (not c or not m):
val -= 1
if bi == 'dungeon' and (not c or not m or not (s or r) or not b):
val -= 1
if b:
val += 1 if info.bk_present else 0
if k != 'none':
val += 1 if info.bk_drops else 0
if s or r:
val += info.key_num
if k != 'none':
val += info.key_drops
if c:
val += 1 if info.compass_present else 0
if m:
val += 1 if info.map_present else 0
limits[dungeon] = val
else:
limits = 60
if world.bigkeyshuffle[player]:
limits += 11
if world.keydropshuffle[player] != 'none':
limits += 1
if world.keyshuffle[player] or world.retro[player]:
limits += 29
if world.keydropshuffle[player] != 'none':
limits += 32
if world.compassshuffle[player]:
limits += 11
if world.mapshuffle[player]:
limits += 12
return limits
def determine_major_items(world, player): def determine_major_items(world, player):
major_item_set = set(major_items) major_item_set = set(major_items)
if world.progressive == 'off': if world.progressive == 'off':
@@ -731,7 +688,7 @@ mode_grouping = {
'Sewers - Dark Cross', 'Desert Palace - Torch', 'Tower of Hera - Basement Cage', 'Sewers - Dark Cross', 'Desert Palace - Torch', 'Tower of Hera - Basement Cage',
'Castle Tower - Room 03', 'Castle Tower - Dark Maze', 'Castle Tower - Room 03', 'Castle Tower - Dark Maze',
'Palace of Darkness - Stalfos Basement', 'Palace of Darkness - Dark Basement - Right', 'Palace of Darkness - Stalfos Basement', 'Palace of Darkness - Dark Basement - Right',
'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Shooter Room', 'Palace of Darkness - Harmless Hellway', 'Palace of Darkness - Shooter Room',
'Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - The Arena - Ledge',
"Thieves' Town - Blind's Cell", 'Skull Woods - Bridge Room', 'Ice Palace - Spike Room', "Thieves' Town - Blind's Cell", 'Skull Woods - Bridge Room', 'Ice Palace - Spike Room',
'Skull Woods - Pot Prison', 'Skull Woods - Pinball Room', 'Misery Mire - Spike Chest', 'Skull Woods - Pot Prison', 'Skull Woods - Pinball Room', 'Misery Mire - Spike Chest',
@@ -776,19 +733,22 @@ mode_grouping = {
], ],
'Key Drops': [ 'Key Drops': [
'Hyrule Castle - Map Guard Key Drop', 'Hyrule Castle - Boomerang Guard Key Drop', 'Hyrule Castle - Map Guard Key Drop', 'Hyrule Castle - Boomerang Guard Key Drop',
'Hyrule Castle - Key Rat Key Drop', 'Eastern Palace - Dark Square Pot Key', 'Hyrule Castle - Key Rat Key Drop', 'Eastern Palace - Dark Eyegore Key Drop',
'Eastern Palace - Dark Eyegore Key Drop', 'Desert Palace - Desert Tiles 1 Pot Key',
'Desert Palace - Beamos Hall Pot Key', 'Desert Palace - Desert Tiles 2 Pot Key',
'Castle Tower - Dark Archer Key Drop', 'Castle Tower - Circle of Pots Key Drop', 'Castle Tower - Dark Archer Key Drop', 'Castle Tower - Circle of Pots Key Drop',
'Skull Woods - Spike Corner Key Drop', 'Ice Palace - Jelly Key Drop', 'Ice Palace - Conveyor Key Drop',
'Misery Mire - Conveyor Crystal Key Drop', 'Turtle Rock - Pokey 1 Key Drop',
'Turtle Rock - Pokey 2 Key Drop', 'Ganons Tower - Mini Helmasuar Key Drop',
],
'Pot Keys': [
'Eastern Palace - Dark Square Pot Key', 'Desert Palace - Desert Tiles 1 Pot Key',
'Desert Palace - Beamos Hall Pot Key', 'Desert Palace - Desert Tiles 2 Pot Key',
'Swamp Palace - Pot Row Pot Key', 'Swamp Palace - Trench 1 Pot Key', 'Swamp Palace - Hookshot Pot Key', 'Swamp Palace - Pot Row Pot Key', 'Swamp Palace - Trench 1 Pot Key', 'Swamp Palace - Hookshot Pot Key',
'Swamp Palace - Trench 2 Pot Key', 'Swamp Palace - Waterway Pot Key', 'Skull Woods - West Lobby Pot Key', 'Swamp Palace - Trench 2 Pot Key', 'Swamp Palace - Waterway Pot Key', 'Skull Woods - West Lobby Pot Key',
'Skull Woods - Spike Corner Key Drop', "Thieves' Town - Hallway Pot Key", "Thieves' Town - Hallway Pot Key", "Thieves' Town - Spike Switch Pot Key",
"Thieves' Town - Spike Switch Pot Key", 'Ice Palace - Jelly Key Drop', 'Ice Palace - Conveyor Key Drop',
'Ice Palace - Hammer Block Key Drop', 'Ice Palace - Many Pots Pot Key', 'Misery Mire - Spikes Pot Key', 'Ice Palace - Hammer Block Key Drop', 'Ice Palace - Many Pots Pot Key', 'Misery Mire - Spikes Pot Key',
'Misery Mire - Fishbone Pot Key', 'Misery Mire - Conveyor Crystal Key Drop', 'Turtle Rock - Pokey 1 Key Drop', 'Misery Mire - Fishbone Pot Key', 'Ganons Tower - Conveyor Cross Pot Key',
'Turtle Rock - Pokey 2 Key Drop', 'Ganons Tower - Conveyor Cross Pot Key',
'Ganons Tower - Double Switch Pot Key', 'Ganons Tower - Conveyor Star Pits Pot Key', 'Ganons Tower - Double Switch Pot Key', 'Ganons Tower - Conveyor Star Pits Pot Key',
'Ganons Tower - Mini Helmasuar Key Drop',
], ],
'Big Key Drops': ['Hyrule Castle - Big Key Drop'], 'Big Key Drops': ['Hyrule Castle - Big Key Drop'],
'Shops': [ 'Shops': [