From c4e6fbb8cd15539f89361d0e9d903fb38b123574 Mon Sep 17 00:00:00 2001 From: cassidoxa Date: Mon, 12 Jun 2023 13:52:17 -0400 Subject: [PATCH] Ganonhunt --- EntranceRandomizer.py | 4 +++- ItemList.py | 21 ++++++++++++++++----- Rom.py | 18 +++++++++++++----- Rules.py | 12 +++++++++--- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index efd02861..b97ce123 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -55,7 +55,7 @@ def start(): Palace, to allow for an alternative to firerod. Vanilla: Swords are in vanilla locations. ''') - parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals', 'all_items', 'completionist'], + parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'ganonhunt', 'crystals', 'all_items', 'completionist'], help='''\ Select completion goal. (default: %(default)s) Ganon: Collect all crystals, beat Agahnim 2 then @@ -66,6 +66,8 @@ def start(): Agahnim fights and then defeat Ganon. Triforce Hunt: Places 30 Triforce Pieces in the world, collect 20 of them to beat the game. + Ganonhunt: Places 30 Triforce Pieces in the world, collect + 20 of them to beat Ganon. All Items: Requires collecting 216 items to defeat Ganon. Completionist: Same as above, plus All Dungeons ''') diff --git a/ItemList.py b/ItemList.py index 0ced4a65..3a1b6041 100644 --- a/ItemList.py +++ b/ItemList.py @@ -35,8 +35,8 @@ Difficulty = namedtuple('Difficulty', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', 'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivesword', 'basicsword', 'basicbow', 'timedohko', 'timedother', - 'triforcehunt', 'triforce_pieces_required', 'retro', - 'extras', 'progressive_sword_limit', 'progressive_shield_limit', + 'triforcehunt', 'ganonhunt', 'triforce_pieces_required', 'ganonhunt_pieces_required', + 'retro', 'extras', 'progressive_sword_limit', 'progressive_shield_limit', 'progressive_armor_limit', 'progressive_bottle_limit', 'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit']) @@ -59,7 +59,9 @@ difficulties = { timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, + ganonhunt = ['Triforce Piece'] * 50, triforce_pieces_required = 20, + ganonhunt_pieces_required = 40, retro = ['Small Key (Universal)'] * 17 + ['Rupees (20)'] * 10, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 4, @@ -86,7 +88,9 @@ difficulties = { timedohko = ['Green Clock'] * 25, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, + ganonhunt = ['Triforce Piece'] * 50, triforce_pieces_required = 20, + ganonhunt_pieces_required = 40, retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 3, @@ -113,7 +117,9 @@ difficulties = { timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, + ganonhunt = ['Triforce Piece'] * 50, triforce_pieces_required = 20, + ganonhunt_pieces_required = 40, retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 2, @@ -127,7 +133,7 @@ difficulties = { } def generate_itempool(world, player): - if (world.difficulty not in ['normal', 'hard', 'expert'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals', 'all_items', 'completionist'] + if (world.difficulty not in ['normal', 'hard', 'expert'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'ganonhunt', 'crystals', 'all_items', 'completionist'] or world.mode not in ['open', 'standard', 'inverted'] or world.timer not in ['none', 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'] or world.progressive not in ['on', 'off', 'random']): raise NotImplementedError('Not supported yet') @@ -457,11 +463,16 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r pool.extend(diff.timedohko) extraitems -= len(diff.timedohko) clock_mode = 'countdown-ohko' - if goal == 'triforcehunt': + if goal in ['triforcehunt']: pool.extend(diff.triforcehunt) extraitems -= len(diff.triforcehunt) treasure_hunt_count = diff.triforce_pieces_required treasure_hunt_icon = 'Triforce Piece' + if goal in ['ganonhunt']: + pool.extend(diff.ganonhunt) + extraitems -= len(diff.ganonhunt) + treasure_hunt_count = diff.ganonhunt_pieces_required + treasure_hunt_icon = 'Triforce Piece' for extra in diff.extras: if extraitems > 0: @@ -582,7 +593,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s treasure_hunt_count = max(min(customitemarray[65], 99), 1) #To display, count must be between 1 and 99. treasure_hunt_icon = 'Triforce Piece' # Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling. - if (customitemarray[64] < treasure_hunt_count) and (goal == 'triforcehunt') and (customitemarray[66] == 0): + if (customitemarray[64] < treasure_hunt_count) and (goal in ['triforcehunt', 'ganonhunt']) and (customitemarray[66] == 0): extrapieces = treasure_hunt_count - customitemarray[64] pool.extend(['Triforce Piece'] * extrapieces) itemtotal = itemtotal + extrapieces diff --git a/Rom.py b/Rom.py index d2dd9f48..f383ddee 100644 --- a/Rom.py +++ b/Rom.py @@ -905,6 +905,8 @@ def patch_rom(world, player, rom): rom.write_byte(0x18003E, 0x0A) # make ganon invincible until all items elif world.goal in ['completionist']: rom.write_byte(0x18003E, 0x0B) # make ganon invincible until all items and dungeons + elif world.goal in ['ganonhunt']: + rom.write_byte(0x18003E, 0x05) # make ganon invincible until goal triforce pieces else: rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected @@ -1040,7 +1042,7 @@ def patch_rom(world, player, rom): # write total item count and item counter hud mode item_total = len(world.get_filled_locations()) - 18 # minus non-item locations rom.write_int16(0x180196, item_total+1) - if world.item_counter_hud[player] and world.goal != 'triforcehunt': + if world.item_counter_hud[player] and world.goal not in ['triforcehunt', 'ganonhunt']: rom.write_byte(0x180039, 0x01) else: rom.write_byte(0x180039, 0x00) @@ -1337,10 +1339,11 @@ def write_strings(rom, world, player): if world.goal == 'ganon': ganon_crystals_singular = 'To beat Ganon you must collect %d crystal and defeat his minion at the top of his tower.' ganon_crystals_plural = 'To beat Ganon you must collect %d crystals and defeat his minion at the top of his tower.' - - tt['sign_ganon'] = (ganon_crystals_singular if world.crystals_needed_for_ganon == 1 else ganon_crystals_plural) % world.crystals_needed_for_ganon - - + tt['sign_ganon'] = (ganon_crystals_singular if world.crystals_needed_for_ganon == 1 else ganon_crystals_plural) % world.crystals_needed_for_ganon + if world.goal == 'ganonhunt': + ganon_triforce_singular = 'To beat Ganon you must collect %d triforce pieces and defeat his minion at the top of his tower.' + ganon_triforce_plural = 'To beat Ganon you must collect %d triforce pieces and defeat his minion at the top of his tower.' + tt['sign_ganon'] = (ganon_triforce_singular if world.treasure_hunt_count == 1 else ganon_triforce_plural) % world.treasure_hunt_count if world.goal in ['dungeons']: tt['sign_ganon'] = 'You need to complete all the dungeons.' @@ -1358,6 +1361,11 @@ def write_strings(rom, world, player): 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 ['ganonhunt']: + tt['ganon_fall_in'] = Ganon1_texts[random.randint(0, len(Ganon1_texts) - 1)] + 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'] = 'You need %d triforce pieces to beat Ganon.' 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.' diff --git a/Rules.py b/Rules.py index 708c20c0..926a0648 100644 --- a/Rules.py +++ b/Rules.py @@ -466,7 +466,10 @@ def global_rules(world, player): forbid_item(world.get_location(location, player), 'Big Key (Ganons Tower)', player) set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon, player) - and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times + and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12)) + and (False if world.goal == 'completionist' and (world.accessibility != 'locations') else True) + and (False if (world.goal == 'ganonhunt' and not state.has('Triforce Piece', player, world.treasure_hunt_count)) else True) + ) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop set_rule(world.get_entrance('Ganons Tower', player), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic. @@ -876,7 +879,10 @@ def inverted_rules(world, player): forbid_item(world.get_location(location, player), 'Big Key (Ganons Tower)', player) set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon, player) - and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times + and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12)) + and (False if world.goal == 'completionist' and (world.accessibility != 'locations') else True) + and (False if (world.goal == 'ganonhunt' and not state.has('Triforce Piece', player, world.treasure_hunt_count)) else True) + ) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop set_rule(world.get_entrance('Inverted Ganons Tower', player), lambda state: False) # This is a safety for the TR function below to not require GT entrance in its key logic. @@ -1055,7 +1061,7 @@ def set_trock_key_rules(world, player): # No matter what, the Big Key cannot be in the Big Chest or held by Trinexx. non_big_key_locations = ['Turtle Rock - Big Chest', 'Turtle Rock - Boss'] - def tr_big_key_chest_keys_needed(world, state): + def tr_big_key_chest_keys_needed(state): # This function handles the key requirements for the TR Big Chest in the situations it having the Big Key should logically require 2 keys, small key # should logically require no keys, and anything else should logically require 4 keys. item = item_name(state, 'Turtle Rock - Big Key Chest', player)