diff --git a/BaseClasses.py b/BaseClasses.py index 3593c103..b459eee6 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -284,7 +284,7 @@ class World(object): def has_beaten_game(self, state, player=None): if player: - return state.has('Triforce', player) or (self.goal in ['triforcehunt'] and (state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) > self.treasure_hunt_count)) + return state.has('Triforce', player) else: return all((self.has_beaten_game(state, p) for p in range(1, self.players + 1))) diff --git a/Bosses.py b/Bosses.py index 30cd56da..2713c92c 100644 --- a/Bosses.py +++ b/Bosses.py @@ -119,11 +119,11 @@ def can_place_boss(world, boss, dungeon_name, level=None): if world.swords in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace': return False - if dungeon_name == 'Ganons Tower' and level == 'top': + if dungeon_name in ['Ganons Tower', 'Inverted Ganons Tower'] and level == 'top': if boss in ["Armos Knights", "Arrghus", "Blind", "Trinexx", "Lanmolas"]: return False - if dungeon_name == 'Ganons Tower' and level == 'middle': + if dungeon_name in ['Ganons Tower', 'Inverted Ganons Tower'] and level == 'middle': if boss in ["Blind"]: return False diff --git a/EntranceShuffle.py b/EntranceShuffle.py index ce490608..46ad1c30 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -3507,8 +3507,8 @@ inverted_default_connections = [('Waterfall of Wishing', 'Waterfall of Wishing' ('Bumper Cave (Top)', 'Dark Death Mountain Healer Fairy'), ('Bumper Cave Exit (Top)', 'Death Mountain Return Ledge'), ('Bumper Cave Exit (Bottom)', 'Light World'), - ('Death Mountain Return Cave (East)', 'Bumper Cave'), - ('Death Mountain Return Cave (West)', 'Death Mountain Return Cave'), + ('Death Mountain Return Cave (West)', 'Bumper Cave'), + ('Death Mountain Return Cave (East)', 'Death Mountain Return Cave'), ('Death Mountain Return Cave Exit (West)', 'Death Mountain'), ('Death Mountain Return Cave Exit (East)', 'Death Mountain'), ('Hookshot Cave Exit (South)', 'Dark Death Mountain'), diff --git a/ItemList.py b/ItemList.py index d8df5fbd..6dac7a01 100644 --- a/ItemList.py +++ b/ItemList.py @@ -137,6 +137,23 @@ def generate_itempool(world, player): else: world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False) + if world.goal in ['triforcehunt']: + if world.mode == 'inverted': + region = world.get_region('Light World',player) + else: + region = world.get_region('Hyrule Castle Courtyard', player) + + loc = Location(player, "Murahdahla", parent=region) + loc.access_rule = lambda state: state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) > state.world.treasure_hunt_count + region.locations.append(loc) + world.dynamic_locations.append(loc) + + world.clear_location_cache() + + world.push_item(loc, ItemFactory('Triforce', player), False) + loc.event = True + loc.locked = True + world.get_location('Ganon', player).event = True world.get_location('Ganon', player).locked = True world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False) diff --git a/Main.py b/Main.py index ff9ef1ec..a5695b57 100644 --- a/Main.py +++ b/Main.py @@ -285,6 +285,11 @@ def copy_dynamic_regions_and_locations(world, ret): for location in world.dynamic_locations: new_reg = ret.get_region(location.parent_region.name, location.parent_region.player) new_loc = Location(location.player, location.name, location.address, location.crystal, location.hint_text, new_reg) + # todo: this is potentially dangerous. later refactor so we + # can apply dynamic region rules on top of copied world like other rules + new_loc.access_rule = location.access_rule + new_loc.always_allow = location.always_allow + new_loc.item_rule = location.item_rule new_reg.locations.append(new_loc) ret.clear_location_cache() diff --git a/Rom.py b/Rom.py index 782aeb00..a94b9c87 100644 --- a/Rom.py +++ b/Rom.py @@ -842,6 +842,7 @@ def patch_rom(world, player, rom): # set up goals for treasure hunt rom.write_bytes(0x180165, [0x0E, 0x28] if world.treasure_hunt_icon == 'Triforce Piece' else [0x0D, 0x28]) rom.write_byte(0x180167, world.treasure_hunt_count % 256) + rom.write_byte(0x180194, 1) # Must turn in triforced pieces (instant win not enabled) # TODO: a proper race rom mode should be implemented, that changes the following flag, and rummages the table (or uses the future encryption feature, etc) rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed @@ -1282,17 +1283,18 @@ def write_strings(rom, world, player): random.shuffle(silverarrows) silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!' tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint + tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint prog_bow_locs = world.find_items('Progressive Bow', player) distinguished_prog_bow_loc = next((location for location in prog_bow_locs if location.item.code == 0x65), None) if distinguished_prog_bow_loc: prog_bow_locs.remove(distinguished_prog_bow_loc) silverarrow_hint = (' %s?' % hint_text(distinguished_prog_bow_loc).replace('Ganon\'s', 'my')) - tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint + tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint if any(prog_bow_locs): silverarrow_hint = (' %s?' % hint_text(random.choice(prog_bow_locs)).replace('Ganon\'s', 'my')) - tt['ganon_phase_3_no_silvers_alt'] = 'Did you find the silver arrows%s' % silverarrow_hint + tt['ganon_phase_3_no_silvers'] = 'Did you find the silver arrows%s' % silverarrow_hint silverarrow_hint = (' %s?' % hint_text(silverarrows[0]).replace('Ganon\'s', 'my')) if silverarrows else '?\nI think not!' @@ -1323,6 +1325,7 @@ def write_strings(rom, world, player): tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Get the Triforce Pieces.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' tt['sign_ganon'] = 'Go find the Triforce pieces... Ganon is invincible!' + tt['murahdahla'] = "Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n\n\n\n… … …\n\nWait! you can see me? I knew I should have\nhidden in a hollow tree. If you bring\n%d triforce pieces, I can reassemble it." % world.treasure_hunt_count elif world.goal in ['pedestal']: tt['ganon_fall_in_alt'] = 'Why are you even here?\n You can\'t even hurt me! Your goal is at the pedestal.' tt['ganon_phase_3_alt'] = 'Seriously? Go Away, I will not Die.' @@ -1406,8 +1409,6 @@ def set_inverted_mode(world, rom): rom.write_int16s(snes_to_pc(0x02E849), [0x0043, 0x0056, 0x0058, 0x006C, 0x006F, 0x0070, 0x007B, 0x007F, 0x001B]) # dw flute rom.write_int16(snes_to_pc(0x02E8D5), 0x07C8) rom.write_int16(snes_to_pc(0x02E8F7), 0x01F8) - rom.write_byte(0x7A943, 0xF0) - rom.write_byte(0x7A96D, 0xD0) rom.write_byte(snes_to_pc(0x08D40C), 0xD0) # morph proof # the following bytes should only be written in vanilla # or they'll overwrite the randomizer's shuffles diff --git a/Text.py b/Text.py index 517ccf18..ac254287 100644 --- a/Text.py +++ b/Text.py @@ -1890,5 +1890,6 @@ class TextTable(object): text['ganon_phase_3_no_silvers_alt'] = CompressedTextMapper.convert("You can't best me without silver arrows!") text['ganon_phase_3_no_silvers'] = CompressedTextMapper.convert("You can't best me without silver arrows!") text['ganon_phase_3_silvers'] = CompressedTextMapper.convert("Oh no! Silver! My one true weakness!") + text['murahdahla'] = CompressedTextMapper.convert("Hello @. I\nam Murahdahla, brother of\nSahasrahla and Aginah. Behold the power of\ninvisibility.\n{PAUSE3}\n… … …\nWait! you can see me? I knew I should have\nhidden in a hollow tree.") text['end_pad_data'] = bytearray([0xfb]) text['terminator'] = bytearray([0xFF, 0xFF])