25 Commits

Author SHA1 Message Date
2055ed6d96 Minor gfx/patch updates 2026-05-29 19:21:17 -05:00
1a9cea452d Zora/Bottle Vendor ask about item before taking money when hints are on 2026-05-28 21:03:10 -05:00
8451a64984 Update baserom 2026-05-28 18:20:57 -05:00
f5dde931dc cave pots show on HUD 2026-05-28 17:20:17 -05:00
b82ec31f98 Update baserom 2026-05-28 06:30:13 -05:00
2ff19b3781 Update baserom 2026-05-27 23:16:45 -05:00
5278f9f2c6 Change more Dark Palace to Palace of Darkness to Appease Fouton 2026-05-25 10:51:55 -05:00
5f1c08920f Update baserom 2026-05-25 00:17:48 -05:00
57db996f27 Merge branch 'fouton_vanilla' into beta 2026-05-24 17:20:30 -05:00
5af7073ed1 Add bumper to enemy_deny 2026-05-24 17:16:51 -05:00
33cd2d076c Change intro text for mystery 2026-05-24 08:56:21 -05:00
78819bbd9d Update standard escape ammo refills to be more consistent 2026-05-19 22:45:15 -05:00
b2d2aae61a Add RainDeathRefill spots to no_escape_assist 2026-05-19 20:33:23 -05:00
320e518519 Add no_escape_assist patch 2026-05-19 20:30:30 -05:00
c39a31b299 Add all_starting patch 2026-05-17 15:08:46 -05:00
0ef55c5f6a Merge branch 'beta' into fouton_vanilla 2026-05-16 22:21:40 -05:00
151765d8d2 Fix broken patches arg 2026-05-15 21:53:28 -05:00
1ae9da59bb Fix Sword and Shield item 2026-05-14 23:52:59 -05:00
84270544bf vanilla with universal keys 2026-05-14 22:28:41 -05:00
8ae1aefcf3 Add prize pack info to vanilla_placements 2026-05-14 22:09:49 -05:00
821c0ee46e Remove accidentally .bak file 2026-05-14 21:36:27 -05:00
a946c2b3b0 Allow applying patches to rom from CLI 2026-05-14 21:23:10 -05:00
ec89afaa5e Fix up junk items in vanilla_placements 2026-05-14 20:05:39 -05:00
d530b68e86 Fix crystal numbers 2026-05-14 10:31:03 -05:00
924bd7a616 Start of attempt for vanilla item placements 2026-05-14 00:14:54 -05:00
19 changed files with 151 additions and 44 deletions

View File

@@ -1519,7 +1519,10 @@ class CollectionState(object):
if not item:
return
changed = False
if item.name.startswith('Progressive '):
if item.name == "Sword and Shield":
self.prog_items["Fighter Sword", item.player] += 1
self.prog_items["Blue Shield", item.player] += 1
elif item.name.startswith('Progressive '):
if 'Sword' in item.name:
if self.has('Golden Sword', item.player):
pass
@@ -2895,6 +2898,10 @@ class Item(object):
def compass(self):
return self.type == 'Compass'
@property
def event(self):
return self.type == 'Event'
@property
def dungeon(self):
if not self.smallkey and not self.bigkey and not self.map and not self.compass:

12
CLI.py
View File

@@ -89,6 +89,7 @@ def parse_cli(argv, no_defaults=False):
parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else 1), help="\n".join(fish.translate("cli", "help", "count")), type=int)
parser.add_argument('--tries', default=defval(int(settings["tries"]) if settings["tries"] != "" and settings["tries"] is not None else 1), help="\n".join(fish.translate("cli", "help", "tries")), type=int)
parser.add_argument('--customitemarray', default={}, help=argparse.SUPPRESS)
parser.add_argument('--skip_money_balance', action="store_true", help=argparse.SUPPRESS)
# included for backwards compatibility
parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255))
@@ -106,7 +107,14 @@ def parse_cli(argv, no_defaults=False):
ret = parser.parse_args(argv)
if ret.keysanity:
ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = ['wild'] * 4
if ret.mapshuffle == "none":
ret.mapshuffle = "wild"
if ret.compassshuffle == "none":
ret.compassshuffle = "wild"
if ret.keyshuffle == "none":
ret.keyshuffle = "wild"
if ret.bigkeyshuffle == "none":
ret.bigkeyshuffle = "wild"
if ret.keydropshuffle:
ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle
@@ -178,7 +186,7 @@ def parse_cli(argv, no_defaults=False):
'decoupledoors', 'door_type_mode', 'bonk_drops',
'trap_door_mode', 'key_logic_algorithm',
'door_self_loops', 'any_enemy_logic', 'aga_randomness',
'money_balance']:
'money_balance', 'patches']:
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
if player == 1:
setattr(ret, name, {1: value})

View File

@@ -252,6 +252,7 @@ def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_pl
or (world.algorithm == 'vanilla_fill' and item_to_place.is_near_dungeon_item(world)))) \
or valid_dungeon_placement(item_to_place, location, world):
return location
if item_to_place.smallkey or item_to_place.bigkey or item_to_place.prize:
location.item = None
location.event = False
@@ -311,7 +312,9 @@ def valid_dungeon_placement(item, location, world):
dungeon = check_dungeon
if dungeon:
layout = world.dungeon_layouts[location.player][dungeon.name]
if not is_dungeon_item(item, world) or item.player != location.player:
if item.event:
return True
elif not is_dungeon_item(item, world) or item.player != location.player:
if item.prize and item.is_near_dungeon_item(world):
return item.dungeon_object == dungeon and layout.free_items > 0
return layout.free_items > 0

View File

@@ -372,6 +372,13 @@ def generate_itempool(world, player):
world.push_precollected(ItemFactory(item, player))
if world.mode[player] == 'standard' and not world.state.has_blunt_weapon(player):
if world.customizer:
placements = world.customizer.get_placements()
if placements:
custom_uncle = placements.get(player, {}).get("Link's Uncle")
if custom_uncle:
placed_items["Link's Uncle"] = custom_uncle
if "Link's Uncle" not in placed_items:
found_sword = False
found_bow = False
@@ -1669,12 +1676,15 @@ def fill_specific_items(world):
item_to_place, event_flag = get_item_and_event_flag(item, world, player,
dungeon_pool, prize_set, prize_pool)
if item_to_place:
world.push_item(loc, item_to_place, False)
loc.locked = True
track_outside_keys(item_to_place, loc, world)
track_dungeon_items(item_to_place, loc, world)
loc.event = (event_flag or item_to_place.advancement
or item_to_place.bigkey or item_to_place.smallkey)
if not loc.item:
world.push_item(loc, item_to_place, False)
loc.locked = True
track_outside_keys(item_to_place, loc, world)
track_dungeon_items(item_to_place, loc, world)
loc.event = (event_flag or item_to_place.advancement
or item_to_place.bigkey or item_to_place.smallkey)
elif loc.item != item_to_place:
logging.getLogger('').warning("Failed to place item %s at location %s because it already contained %s", item_to_place, loc.name, loc.item)
else:
raise Exception(f'Did not find "{item}" in item pool to place at "{location}"')
advanced_placements = world.customizer.get_advanced_placements()
@@ -1802,7 +1812,7 @@ def shuffle_event_items(world, player):
continue
break
else:
raise FillError(f'Unable to place followers: {", ".join(list(map(lambda d: d.hint_text, follower_locations)))}')
raise FillError(f'Unable to place followers: {", ".join(list(map(lambda f: f.name, pickup_items)))}')
def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_pool):

View File

@@ -55,7 +55,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'Bow!\nJoin the archer class
'Master Sword': (True, False, 'Sword', 0x50, 100, 'Master Sword!\nEvil\'s bane!', '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, 'Tempered Sword!\nMore slashy!', '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, 'Fighter Sword!\nStarter level slashy!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the Fighter Sword'),
'Sword and Shield': (True, False, 'Sword', 0x00, 'Sword and Shield!\nUncle sword ahoy!', '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'),
'Sword and Shield': (True, False, 'Sword', 0x00, 50, 'Sword and Shield!\nUncle sword ahoy!', '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, 'Golden Sword!\nBest slashy!', '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, 'Sword!\nA better sword for 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, 'Glove!\nLift more than you can now!', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'),
@@ -134,9 +134,9 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'Bow!\nJoin the archer class
'Big Key (Agahnims Tower)': (False, False, 'BigKey', 0x9B, 60, 'A big key for\nAgahnim\'s Tower', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Castle Tower'),
'Compass (Agahnims Tower)': (False, True, 'Compass', 0x8B, 10, 'A compass for\nAgahnim\'s Tower', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds null again', 'a compass to Castle Tower'),
'Map (Agahnims Tower)': (False, True, 'Map', 0x7B, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Castle Tower'),
'Small Key (Palace of Darkness)': (False, False, 'SmallKey', 0xA6, 40, 'A small key for\nDark Palace', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Palace of Darkness'),
'Big Key (Palace of Darkness)': (False, False, 'BigKey', 0x99, 60, 'A big key for\nDark Palace', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Palace of Darkness'),
'Compass (Palace of Darkness)': (False, True, 'Compass', 0x89, 10, 'A compass for\nDark Palace', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Palace of Darkness'),
'Small Key (Palace of Darkness)': (False, False, 'SmallKey', 0xA6, 40, 'A small key for\nPalace of Darkness', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Palace of Darkness'),
'Big Key (Palace of Darkness)': (False, False, 'BigKey', 0x99, 60, 'A big key for\nPalace of Darkness', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Palace of Darkness'),
'Compass (Palace of Darkness)': (False, True, 'Compass', 0x89, 10, 'A compass for\nPalace of Darkness', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Palace of Darkness'),
'Map (Palace of Darkness)': (False, True, 'Map', 0x79, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Palace of Darkness'),
'Small Key (Thieves Town)': (False, False, 'SmallKey', 0xAB, 40, 'A small key for\nThieves Town', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Thieves\' Town'),
'Big Key (Thieves Town)': (False, False, 'BigKey', 0x94, 60, 'A big key for\nThieves Town', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Thieves\' Town'),

View File

@@ -67,6 +67,7 @@ from Rom import (
JsonRom,
LocalRom,
apply_rom_settings,
apply_rom_patches,
get_hash_string,
patch_race_rom,
patch_rom,
@@ -335,7 +336,7 @@ def main(args, seed=None, fish=None):
for player in range(1, world.players+1):
if world.shopsanity[player]:
customize_shops(world, player)
if args.algorithm in ['balanced', 'equitable']:
if not args.skip_money_balance and args.algorithm in ['balanced', 'equitable']:
balance_money_progression(world)
ensure_good_items(world, True)
@@ -364,6 +365,8 @@ def main(args, seed=None, fish=None):
rom_names.append((player, team, list(rom.name)))
world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash)
if args.patches[player]:
apply_rom_patches(rom, map(lambda arg: arg.strip(), args.patches[player].split(",")))
apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player],
args.fastmenu[player], args.disablemusic[player], args.sprite[player], args.triforce_gfx[player],
args.ow_palettes[player], args.uw_palettes[player], args.reduce_flashing[player],
@@ -572,7 +575,7 @@ def init_world(args, fish):
world.collection_rate = args.collection_rate.copy()
world.colorizepots = args.colorizepots.copy()
world.aga_randomness = args.aga_randomness.copy()
world.money_balance = args.money_balance.copy()
world.money_balance = {player: int(args.money_balance[player]) for player in range(1, world.players + 1)}
# custom settings - these haven't been promoted to full settings yet
in_progress_settings = ['force_enemy']

View File

@@ -509,7 +509,7 @@ boots_clips = [
mirror_clips_local = [
('Desert East Mirror Clip', 'Mire Area', 'Desert Mouth'),
('EDDM Mirror Clip', 'East Dark Death Mountain (Bottom Left)', 'East Dark Death Mountain (Bottom)'),
('EDDM Bridge Mirror Clip', 'East Dark Death Mountain (Bottom Left)', 'East Dark Death Mountain (Bottom)'),
('EDDM Mirror Clip', 'East Dark Death Mountain (Top)', 'Dark Death Mountain Ledge')
]
@@ -520,4 +520,4 @@ mirror_clips = [
mirror_offsets = [
(['DM Offset Mirror', 'DDM Offset Mirror'], ['West Death Mountain (Bottom)', 'West Dark Death Mountain (Bottom)'], ['Hyrule Castle Courtyard Northeast', 'Pyramid Crack'], ['Pyramid Area', 'Hyrule Castle Courtyard']),
(['DM To HC Ledge Offset Mirror', 'DDM To Pyramid Offset Mirror'], ['West Death Mountain (Bottom)', 'West Dark Death Mountain (Bottom)'], ['Hyrule Castle Ledge', 'Pyramid Area'], ['Pyramid Area', 'Hyrule Castle Area'])
]
]

113
Rom.py
View File

@@ -85,7 +85,7 @@ from Utils import int16_as_bytes, int32_as_bytes, local_path, snes_to_pc
from Versions import DRVersion, GKVersion, ORVersion
JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'd5b351a79bab079408bdf19b0deaa655'
RANDOMIZERBASEHASH = '5c4c3cbe6d3fee849e66d4ac4a059792'
class JsonRom(object):
@@ -994,7 +994,7 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
set_inverted_mode(world, player, rom, inverted_buffer)
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', 'Sword and Shield']:
# disable sword sprite from uncle
rom.write_bytes(0x6D263, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
rom.write_bytes(0x6D26B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
@@ -1007,6 +1007,10 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
rom.write_bytes(0x6D31B, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
rom.write_bytes(0x6D323, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
bridge_item = world.get_location("Hobo", player).item
if bridge_item is None or not bridge_item.name.startswith("Bottle"):
rom.write_bytes(0x1E9C0, [0xFB, 0xFF, 0x03, 0x00, 0xAB, 0x00, 0x00, 0x00])
# set light cones
if world.dark_rooms[player] == 'no_dark_rooms':
light_cone = 0x20
@@ -1497,28 +1501,28 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
loot_source = 0x09
if world.prizeshuffle[player] != 'none':
loot_source |= 0x10
if world.pottery[player] not in ['none', 'cave']:
if world.pottery[player] != 'none':
loot_source |= 0x02
if world.dropshuffle[player] != 'none':
loot_source |= 0x04
rom.write_byte(0x1CFF10, loot_source)
if world.loothud[player] == 'never':
rom.write_byte(0x1CFF12, 0x00)
rom.write_byte(0x1CFF13, 0x00)
elif world.loothud[player] == 'presence':
rom.write_byte(0x1CFF12, 0x01)
rom.write_byte(0x1CFF13, 0x01)
rom.write_bytes(0x1CFF0E, [0x01, 0x01])
elif world.loothud[player] == 'value':
rom.write_byte(0x1CFF12, 0x01)
rom.write_byte(0x1CFF13, 0x01)
rom.write_bytes(0x1CFF0E, [0xFF, 0xFF])
elif world.loothud[player] == 'dungeon_value':
rom.write_byte(0x1CFF12, 0x01)
rom.write_byte(0x1CFF13, 0x01)
rom.write_bytes(0x1CFF0E, [0xFF, 0x01])
if world.showloot[player] == 'never':
rom.write_bytes(0x1CFF08, [0x00, 0x00, 0x00, 0x00])
rom.write_bytes(0x1CFF08, [0x02, 0x00, 0x00, 0x00])
rom.write_byte(0x1CFF11, 0x00)
rom.write_byte(0x1CFF12, 0x00) # turn off hud icon too just to be safe
rom.write_byte(0x1CFF12, 0x00)
elif world.showloot[player] == 'presence':
rom.write_bytes(0x1CFF08, [0x01, 0x00, 0x00, 0x00])
rom.write_byte(0x1CFF11, 0x00)
@@ -1779,14 +1783,15 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
rom.write_byte(0x180358, 0x01 if glitches_enabled else 0x00)
rom.write_byte(0x18008B, 0x01 if glitches_enabled else 0x00)
# remove shield from uncle
rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
rom.write_bytes(0x6D25B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
rom.write_bytes(0x6D283, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
rom.write_bytes(0x6D28B, [0x00, 0x00, 0xf7, 0xff, 0x00, 0x0E])
rom.write_bytes(0x6D2CB, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
if uncle_location.item is None or uncle_location.item.name not in ['Blue Shield', 'Red Shield', 'Mirror Shield', 'Progressive Shield', 'Sword and Shield']:
# remove shield from uncle
rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
rom.write_bytes(0x6D25B, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
rom.write_bytes(0x6D283, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E])
rom.write_bytes(0x6D28B, [0x00, 0x00, 0xf7, 0xff, 0x00, 0x0E])
rom.write_bytes(0x6D2CB, [0x00, 0x00, 0xf6, 0xff, 0x02, 0x0E])
rom.write_bytes(0x6D2FB, [0x00, 0x00, 0xf7, 0xff, 0x02, 0x0E])
rom.write_bytes(0x6D313, [0x00, 0x00, 0xe4, 0xff, 0x08, 0x0E])
write_int16(rom, 0x180183, 0) # Escape fill rupee bow
# Uncle / Zelda / Mantle respawn refills (magic, bombs, arrows)
@@ -1931,7 +1936,7 @@ def patch_rom(world, rom, player, team, is_mystery=False, rom_header=None):
write_enemizer_tweaks(rom, world, player)
randomize_damage_table(rom, world, player)
write_strings(rom, world, player, team)
write_strings(rom, world, player, team, is_mystery)
# write initial sram
rom.write_initial_sram()
@@ -2074,6 +2079,49 @@ def hud_format_text(text):
output += b'\x7f\x00'
return output[:32]
def read_bytes(f, count):
values = f.read(count)
if len(values) < count:
raise EOFError
return values
def apply_rom_patches(rom, patches):
for patch in patches:
if not os.path.exists(f"patches/{patch}.ips"):
logging.getLogger('').warning("Patch %s not found -- skipping", patch)
continue
with open(f"patches/{patch}.ips", "rb") as f:
byte_changes = []
try:
header = read_bytes(f, 5)
if header != b"PATCH":
logging.getLogger('').warning("Patch %s invalid -- skipping", patch)
continue
while True:
address = read_bytes(f, 3)
if address == b"EOF":
break
address = int.from_bytes(address, byteorder="big")
length = int.from_bytes(read_bytes(f, 2), byteorder="big")
if length > 0:
byte_changes.append((address, list(f.read(length))))
else:
length = int.from_bytes(read_bytes(f, 2), byteorder="big")
value = read_bytes(f, 1)
byte_changes.append((address, length * list(value)))
for address, values in byte_changes:
rom.write_bytes(address, values)
logging.getLogger("").info("Patch %s applied successfully", patch)
except EOFError:
logging.getLogger('').warning("Patch %s invalid -- skipping", patch)
continue
def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, sprite, triforce_gfx,
ow_palettes, uw_palettes, reduce_flashing, shuffle_sfx,
@@ -2390,7 +2438,7 @@ def write_string_to_rom(rom, target, string):
rom.write_bytes(address, MultiByteTextMapper.convert(string, maxbytes))
def write_strings(rom, world, player, team):
def write_strings(rom, world, player, team, is_mystery=False):
tt = TextTable()
tt.removeUnwantedText()
if world.shuffle[player] != 'vanilla':
@@ -2441,6 +2489,15 @@ def write_strings(rom, world, player, team):
# For hints, first we write hints about entrances, some from the inconvenient list others from all reasonable entrances.
if world.hints[player]:
zoraitem = world.get_location('King Zora', player).item.hint_text
if len(zoraitem) <= 15:
tt['zora_meeting'] = f"Whaddaya want?\n{zoraitem.title()}\n Nothin'{{CHOICE}}"
else:
tt['zora_meeting'] = f"Do you want {zoraitem}?\n ≥ I'll pay\n No thanks{{CHOICE}}"
bottleitem = world.get_location('Bottle Merchant', player).item.hint_text
tt['bottle_vendor_choice'] = f"Do you want {bottleitem}?\n ≥ I'll take it\n No thanks!\n{{CHOICE}}"
tt['sign_north_of_links_house'] = '> Randomizer The telepathic tiles can have hints!'
hint_locations = HintLocations.copy()
random.shuffle(hint_locations)
@@ -2905,7 +2962,22 @@ def write_strings(rom, world, player, team):
sanc_text = "Dark Chapel"
tt['menu_start_2'] = "{MENU}\n{SPEED0}\n≥@'s " + lh_text + "\n " + sanc_text + "\n{CHOICE3}"
tt['menu_start_3'] = "{MENU}\n{SPEED0}\n≥@'s " + lh_text + "\n " + sanc_text + "\n Mountain Cave\n{CHOICE2}"
if world.mode[player] == 'inverted':
if is_mystery:
tt['intro_main'] = CompressedTextMapper.convert(
"{INTRO}\n Episode III"
+ "{PAUSE3}\n A Link to the Past"
+ "{PAUSE3}\n Mystery Randomizer"
+ "{PAUSE3}\n\n\n"
+ "{PAUSE3}\nAfter mostly disregarding what happened in the first two games,"
+ "{PAUSE3}\nLink awakens to a massive mystery."
+ "{PAUSE3}\nHe doesn't know why he's here, or what he must do."
+ "{PAUSE3} {CHANGEPIC}\nGanon has moved around all the items in Hyrule."
+ "{PAUSE7}\nYou will have to find all the items necessary to achieve your goal."
+ "{PAUSE7}\nThis is your chance to be a hero."
+ "{PAUSE3} {CHANGEPIC}\nGood luck out there, and try not to die."
+ "{PAUSE9} {CHANGEPIC}", False)
elif world.mode[player] == 'inverted':
tt['intro_main'] = CompressedTextMapper.convert(
"{INTRO}\n Episode III"
+ "{PAUSE3}\n A Link to the Past"
@@ -2919,6 +2991,7 @@ def write_strings(rom, world, player, team):
+ "{PAUSE7}\nThis is your chance to be a hero."
+ "{PAUSE3} {CHANGEPIC}\nYou must get the 7 crystals to beat Ganon."
+ "{PAUSE9} {CHANGEPIC}", False)
rom.write_bytes(0xE0000, tt.getBytes())
credits = Credits()

View File

@@ -505,7 +505,7 @@ def global_rules(world, player):
set_rule(world.get_entrance('Skull Woods Rock (West)', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Skull Woods Rock (East)', player), lambda state: state.can_lift_rocks(player))
# this more like an ohko rule - dependent on bird being present too - so enemizer could turn this off?
set_rule(world.get_entrance('Bumper Cave Ledge Drop', player), lambda state: world.can_take_damage or state.has('Cape', player) or state.has('Cane of Byrna', player) or state.has_sword(player))
set_rule(world.get_entrance('Bumper Cave Ledge Drop', player), lambda state: state.has('Cape', player) or state.has('Cane of Byrna', player) or state.has_sword(player))
set_rule(world.get_entrance('Bumper Cave Rock (Outer)', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Bumper Cave Rock (Inner)', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Skull Woods Pass Rock (North)', player), lambda state: state.can_lift_heavy_rocks(player))
@@ -1404,8 +1404,7 @@ def ow_bunny_rules(world, player):
add_bunny_rule(world.get_entrance('Skull Woods Forgotten Bush (East)', player), player)
add_bunny_rule(world.get_entrance('Skull Woods Second Section Hole', player), player)
add_bunny_rule(world.get_entrance('East Dark Death Mountain Bushes', player), player)
if not world.can_take_damage:
add_bunny_rule(world.get_entrance('Bumper Cave Ledge Drop', player), player)
add_bunny_rule(world.get_entrance('Bumper Cave Ledge Drop', player), player)
add_bunny_rule(world.get_entrance('Bumper Cave Rock (Outer)', player), player)
add_bunny_rule(world.get_entrance('Bumper Cave Rock (Inner)', player), player)
add_bunny_rule(world.get_entrance('Skull Woods Pass Bush Row (West)', player), player)

View File

@@ -1766,7 +1766,7 @@ class TextTable(object):
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_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→ Palace of Darkness")
text['sign_east_of_bomb_shop'] = CompressedTextMapper.convert("\n← Bomb Shoppe")
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!")

Binary file not shown.

BIN
patches/2way_mirror.ips Normal file

Binary file not shown.

BIN
patches/all_starting.ips Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
patches/pod_key_swap.ips Normal file

Binary file not shown.

BIN
patches/quiet_zora.ips Normal file

Binary file not shown.

View File

@@ -754,6 +754,10 @@
"type": "bool"
},
"money_balance": {},
"patches": {
"type": "str",
"help": "suppress"
},
"settingsonload": {
"choices": [
"default",

View File

@@ -309,7 +309,7 @@ UwGeneralDeny:
- [0x00b0, 7, [ "StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay
- [0x00b0, 8, [ "StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay
- [ 0x00b1, 2, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Misery Mire - Hourglass - Spike Trap 1"
- [ 0x00b1, 3, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Misery Mire - Hourglass - Spike Trap 2"
- [ 0x00b1, 3, [ "RollerVerticalUp", "RollerVerticalDown", "Bumper" ] ] #"Misery Mire - Hourglass - Spike Trap 2"
- [0x00b1, 4, ["Bumper", "BigSpike", "AntiFairyCircle", "Statue"]] # Wizzrobe near door
- [ 0x00b2, 1, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots
- [ 0x00b2, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots