From c97260a337e1ff6791cdcdac36d53440c8f09f18 Mon Sep 17 00:00:00 2001 From: Kara Alexandra Date: Wed, 3 Sep 2025 19:58:38 -0500 Subject: [PATCH] Add random Ganon vulnerability item. --- BaseClasses.py | 44 ++++++++++++++- CLI.py | 3 +- Main.py | 27 +++++++++ Rom.py | 98 +++++++++++++++++++++++++-------- Rules.py | 11 +++- Text.py | 25 ++++++--- data/base2current.bps | Bin 137013 -> 137700 bytes resources/app/cli/args.json | 19 +++++++ resources/app/cli/lang/en.json | 7 +++ 9 files changed, 199 insertions(+), 35 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 4e4b8827..3a0a1204 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -74,6 +74,8 @@ class World(object): self.dark_rooms = {} self.damage_challenge = {} self.shuffle_damage_table = {} + self.ganon_item = {} + self.ganon_item_orig = {} self.custom = custom self.customitemarray = customitemarray self.can_take_damage = {} @@ -161,8 +163,10 @@ class World(object): set_player_attr('escape_assist', []) set_player_attr('crystals_needed_for_ganon', 7) set_player_attr('crystals_needed_for_gt', 7) + set_player_attr('ganon_item', 'silver') set_player_attr('crystals_ganon_orig', {}) set_player_attr('crystals_gt_orig', {}) + set_player_attr('ganon_item_orig', 'silver') set_player_attr('open_pyramid', 'auto') set_player_attr('take_any', 'none') set_player_attr('treasure_hunt_icon', 'Triforce Piece') @@ -1346,6 +1350,42 @@ class CollectionState(object): def has_blunt_weapon(self, player): return self.has_sword(player) or self.has('Hammer', player) + def can_hit_stunned_ganon(self, player): + ganon_item = self.world.ganon_item[player] + if ganon_item == "silver": + return self.has("Silver Arroys", player) and self.can_shoot_arrows(player) + elif ganon_item == "boomerang": + return self.has("Blue Boomerang", player) or self.has("Red Boomerang", player) + elif ganon_item == "hookshot": + return self.has("Hookshot", player) + elif ganon_item == "bomb": + return self.can_use_bombs(player) + elif ganon_item == "powder": + return self.has("Magic Powder", player) + elif ganon_item == "fire_rod": + return self.has("Fire Rod", player) + elif ganon_item == "ice_rod": + return self.has("Ice Rod", player) + elif ganon_item == "bombos": + return self.has("Bombos", player) and self.can_use_medallions(player) + elif ganon_item == "ether": + return self.has("Ether", player) and self.can_use_medallions(player) + elif ganon_item == "quake": + return self.has("Quake", player) and self.can_use_medallions(player) + elif ganon_item == "hammer": + return self.has("Hammer", player) + elif ganon_item == "bee": + return (self.has_bottle(player) and (self.has("Bug Catching Net", player) or self.can_buy_unlimited("Bee", player))) + elif ganon_item == "somaria": + return self.has("Cane of Somaria", player) + elif ganon_item == "byrna": + return self.has("Cane of Byrna", player) + else: + return False + + def can_use_medallions(self, player): + return self.has_sword(player) + def has_Mirror(self, player): return self.has('Magic Mirror', player) @@ -1364,7 +1404,7 @@ class CollectionState(object): return self.has('Ocarina (Activated)', player) def can_melt_things(self, player): - return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player)) + return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.can_use_medallions(player)) def can_avoid_lasers(self, player): return (self.has('Mirror Shield', player) or self.has('Cape', player) @@ -3051,6 +3091,7 @@ class Spoiler(object): 'beemizer': self.world.beemizer, 'gt_crystals': self.world.crystals_needed_for_gt, 'ganon_crystals': self.world.crystals_needed_for_ganon, + 'ganon_item': self.world.ganon_item, 'open_pyramid': self.world.open_pyramid, 'accessibility': self.world.accessibility, 'restricted_boss_items': self.world.restrict_boss_items, @@ -3263,6 +3304,7 @@ class Spoiler(object): outfile.write('Triforce Pieces Total:'.ljust(line_width) + '%s\n' % self.metadata['triforcepool'][player]) outfile.write('Crystals Required for GT:'.ljust(line_width) + '%s\n' % str(self.world.crystals_gt_orig[player])) outfile.write('Crystals Required for Ganon:'.ljust(line_width) + '%s\n' % str(self.world.crystals_ganon_orig[player])) + outfile.write('Item Required for Ganon:'.ljust(line_width) + '%s\n' % str(self.world.ganon_item_orig[player])) outfile.write('Swords:'.ljust(line_width) + '%s\n' % self.metadata['weapons'][player]) outfile.write('\n') outfile.write('Accessibility:'.ljust(line_width) + '%s\n' % self.metadata['accessibility'][player]) diff --git a/CLI.py b/CLI.py index 8f51760c..4bedd4fd 100644 --- a/CLI.py +++ b/CLI.py @@ -133,7 +133,7 @@ def parse_cli(argv, no_defaults=False): for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'ow_shuffle', 'ow_terrain', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle', 'flute_mode', 'bow_mode', 'take_any', 'boots_hint', 'shuffle_followers', - 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', + 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'ganon_item', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'prizeshuffle', 'startinventory', 'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items', 'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', @@ -177,6 +177,7 @@ def parse_settings(): "goal": "ganon", "crystals_gt": "7", "crystals_ganon": "7", + "ganon_item": "silver", "swords": "random", "flute_mode": "normal", "bow_mode": "progressive", diff --git a/Main.py b/Main.py index 67c4c01e..4f476a6a 100644 --- a/Main.py +++ b/Main.py @@ -58,6 +58,26 @@ def check_python_version(): logging.warning(BabelFish().translate("cli","cli","old.python.version"), sys.version) +def random_ganon_item(sword_mode): + options = [ + "silver", + "boomerang", + "hookshot", + "powder", + "fire_rod", + "ice_rod", + "hammer", + "bee", + "somaria", + "byrna", + "bombos", + "ether", + "quake", + ] + max_choice = len(options) - 3 if sword_mode == "swordless" else len(options) + return options[random.randint(0, max_choice - 1)] + + def main(args, seed=None, fish=None): check_python_version() @@ -99,6 +119,8 @@ def main(args, seed=None, fish=None): world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)} world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)} + world.ganon_item = {player: random_ganon_item(args.swords[player]) if args.ganon_item[player] == 'random' else args.ganon_item[player] for player in range(1, world.players + 1)} + world.intensity = {player: random.randint(1, 3) if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)} world.treasure_hunt_count = {} @@ -479,6 +501,7 @@ def init_world(args, fish): world.bow_mode = args.bow_mode.copy() world.crystals_ganon_orig = args.crystals_ganon.copy() world.crystals_gt_orig = args.crystals_gt.copy() + world.ganon_item_orig = args.ganon_item.copy() world.owTerrain = args.ow_terrain.copy() world.owKeepSimilar = args.ow_keepsimilar.copy() world.owWhirlpoolShuffle = args.ow_whirlpool.copy() @@ -604,8 +627,10 @@ def copy_world(world): ret.bow_mode = world.bow_mode.copy() ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy() ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy() + ret.ganon_item = world.ganon_item.copy() ret.crystals_ganon_orig = world.crystals_ganon_orig.copy() ret.crystals_gt_orig = world.crystals_gt_orig.copy() + ret.ganon_item_orig = world.ganon_item_orig.copy() ret.owTerrain = world.owTerrain.copy() ret.owKeepSimilar = world.owKeepSimilar.copy() ret.owWhirlpoolShuffle = world.owWhirlpoolShuffle.copy() @@ -829,8 +854,10 @@ def copy_world_premature(world, player): ret.bow_mode = world.bow_mode.copy() ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy() ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy() + ret.ganon_item = world.ganon_item.copy() ret.crystals_ganon_orig = world.crystals_ganon_orig.copy() ret.crystals_gt_orig = world.crystals_gt_orig.copy() + ret.ganon_item_orig = world.ganon_item_orig.copy() ret.owTerrain = world.owTerrain.copy() ret.owKeepSimilar = world.owKeepSimilar.copy() ret.owWhirlpoolShuffle = world.owWhirlpoolShuffle.copy() diff --git a/Rom.py b/Rom.py index b77523c8..f553529d 100644 --- a/Rom.py +++ b/Rom.py @@ -22,7 +22,7 @@ from Dungeons import dungeon_music_addresses, dungeon_table from Regions import location_table, shop_to_location_table, retro_shops from RoomData import DoorKind from Text import MultiByteTextMapper, CompressedTextMapper, text_addresses, Credits, TextTable -from Text import Uncle_texts, Ganon1_texts, Ganon_Phase_3_No_Silvers_texts, TavernMan_texts, Sahasrahla2_texts +from Text import Uncle_texts, Ganon1_texts, Ganon_Phase_3_No_Silvers_texts, Ganon_Phase_3_No_Weakness_texts, TavernMan_texts, Sahasrahla2_texts from Text import Triforce_texts, Blind_texts, BombShop2_texts, junk_texts from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths_texts, DeathMountain_texts from Text import LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts @@ -44,7 +44,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'b2566e746c7694b21cb19566763c42e3' +RANDOMIZERBASEHASH = 'f0d684ceb9639cb9bb3b13b52a7cea66' class JsonRom(object): @@ -1292,6 +1292,25 @@ def patch_rom(world, rom, player, team, is_mystery=False): rom.write_byte(0x1801A6, world.crystals_needed_for_ganon[player]) rom.write_byte(0x1801A2, 0x00) # ped requirement is vanilla, set to 0x1 for special requirements + ganon_item_byte = { + "silver": 0x00, + "boomerang": 0x02, + "hookshot": 0x03, + "bomb": 0x04, + "powder": 0x05, + "fire_rod": 0x06, + "ice_rod": 0x07, + "bombos": 0x08, + "ether": 0x09, + "quake": 0x0A, + "hammer": 0x0C, + "bee": 0x10, + "somaria": 0x11, + "byrna": 0x12, + "none": 0xFF, + } + rom.write_byte(0x18002E, ganon_item_byte[world.ganon_item[player]]) + # block HC upstairs doors in rain state in standard mode prevent_rain = world.mode[player] == 'standard' and world.shuffle[player] != 'vanilla' and world.logic[player] != 'nologic' rom.write_byte(0x18008A, 0x01 if prevent_rain else 0x00) @@ -2420,29 +2439,64 @@ def write_strings(rom, world, player, team): no_silver_text = Ganon_Phase_3_No_Silvers_texts[random.randint(0, len(Ganon_Phase_3_No_Silvers_texts) - 1)] - silverarrows = world.find_items('Silver Arrows', player) - random.shuffle(silverarrows) - if silverarrows: - hint_phrase = hint_text(silverarrows[0]).replace("Ganon's", "my") - silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' - else: - silverarrow_hint = no_silver_text - tt['ganon_phase_3_no_silvers'] = silverarrow_hint - tt['ganon_phase_3_no_silvers_alt'] = 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) - progressive_silvers = world.difficulty_requirements[player].progressive_bow_limit >= 2 or world.swords[player] == 'swordless' - if distinguished_prog_bow_loc: - prog_bow_locs.remove(distinguished_prog_bow_loc) - hint_phrase = hint_text(distinguished_prog_bow_loc).replace("Ganon's", "my") - silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' if progressive_silvers else no_silver_text + ganon_item = world.ganon_item[player] + if ganon_item == "silver": + silverarrows = world.find_items('Silver Arrows', player) + random.shuffle(silverarrows) + if silverarrows: + hint_phrase = hint_text(silverarrows[0]).replace("Ganon's", "my") + silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' + else: + silverarrow_hint = no_silver_text tt['ganon_phase_3_no_silvers'] = silverarrow_hint - if any(prog_bow_locs): - hint_phrase = hint_text(random.choice(prog_bow_locs)).replace("Ganon's", "my") - silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' if progressive_silvers else no_silver_text tt['ganon_phase_3_no_silvers_alt'] = 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) + progressive_silvers = world.difficulty_requirements[player].progressive_bow_limit >= 2 or world.swords[player] == 'swordless' + if distinguished_prog_bow_loc: + prog_bow_locs.remove(distinguished_prog_bow_loc) + hint_phrase = hint_text(distinguished_prog_bow_loc).replace("Ganon's", "my") + silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' if progressive_silvers else no_silver_text + tt['ganon_phase_3_no_silvers'] = silverarrow_hint + if any(prog_bow_locs): + hint_phrase = hint_text(random.choice(prog_bow_locs)).replace("Ganon's", "my") + silverarrow_hint = f'Did you find the silver arrows {hint_phrase}?' if progressive_silvers else no_silver_text + tt['ganon_phase_3_no_silvers_alt'] = silverarrow_hint + elif ganon_item == "bomb": + tt['ganon_phase_3_no_bow'] = "You can't best\nme without\nexplosives!" + tt['ganon_phase_3_silvers'] = "Explosives!\nMy one true\nweakness!" + elif ganon_item == "bee": + tt['ganon_phase_3_no_bow'] = "You can't best\nme without\na bee!" + tt['ganon_phase_3_silvers'] = "Oh no! A bee!\nMy one true\nweakness!" + elif ganon_item == "none": + no_weakness_text = Ganon_Phase_3_No_Weakness_texts[random.randint(0, len(Ganon_Phase_3_No_Weakness_texts) - 1)] + tt['ganon_phase_3_no_bow'] = no_weakness_text + tt['ganon_phase_3_silvers'] = no_weakness_text + else: + name_table = { + 'boomerang': ['a boomerang', 'A boomerang', 'Red Boomerang'], + 'hookshot': ['a hookshot', 'A hookshot', 'Hookshot'], + 'powder': ['the powder', 'Powder', 'Magic Powder'], + 'fire_rod': ['the fire rod', 'The fire rod', 'Fire Rod'], + 'ice_rod': ['the ice rod', 'The ice rod', 'Ice Rod'], + 'bombos': ['bombos', 'Bombos', 'Bombos'], + 'ether': ['ether', 'Ether', 'Ether'], + 'quake': ['quake', 'Quake', 'Quake'], + 'hammer': ['a hammer', 'A hammer', 'Hammer'], + 'somaria': ['somaria', 'Somaria', 'Cane of Somaria'], + 'byrna': ['byrna', 'Byrna', 'Cane of Byrna'], + } + locations = world.find_items(name_table[ganon_item][2], player) + random.shuffle(locations) + location_hint = (' %s?' % hint_text(locations[0]).replace("Ganon's", "my")) if locations else "?\nI think not!" + have_text = name_table[ganon_item][1] + none_text = name_table[ganon_item][0] + if len(have_text) <= 6: + have_text = "Oh no! %s" % have_text + tt['ganon_phase_3_no_bow'] = "Did you find %s%s" % (none_text, location_hint) + tt['ganon_phase_3_silvers'] = "%s!\nMy one true\nweakness!" % have_text + crystal5 = world.find_items('Crystal 5', player) crystal6 = world.find_items('Crystal 6', player) greenpendant = world.find_items('Green Pendant', player) diff --git a/Rules.py b/Rules.py index a79aec03..c548646f 100644 --- a/Rules.py +++ b/Rules.py @@ -862,8 +862,15 @@ def global_rules(world, player): add_mc_rule('Agahnim 1') add_mc_rule('Agahnim 2') - set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(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 + set_rule( + world.get_location('Ganon', player), + lambda state: state.has_beam_sword(player) + and state.has_fire_source(player) + and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) + or state.can_hit_stunned_ganon(player) + or state.has('Lamp', player) + or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times + set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop diff --git a/Text.py b/Text.py index 1a66b22f..eb7af9b6 100644 --- a/Text.py +++ b/Text.py @@ -234,30 +234,37 @@ Ganon_Phase_3_No_Silvers_texts = [ "Did you find\nthe arrows on\nThe Moon?", "Did you find\nthe arrows\nIn dev null?", "I have sold\nthe arrows for\na green big 20", - "Did you find\nThe arrows in\nCount Dracula?", + "Did you find\nthe arrows in\nCount Dracula?", "Error 404\nSilver arrows\nnot found.", - "No arrows for\nYou today,\nSorry", + "No arrows for\nyou today.\nSorry.", "No arrows?\nCheck your\njunk mail." - "Careful, all\nthat spinning\nmakes me dizzy", "Did you find\nthe arrows in\nJabu's belly?", "Silver is not\nan appropriate\narrow material", "Did you find\nthe arrows in\nNarnia?", - "Are you ready\nTo spin\nTo win?", "DID YOU FIND\nTHE ARROWS IN\nKEFKA'S TOWER", - "Did you find\nthe arrows in\nRecycle Bin?", + "Did you find\nthe arrows in\nyour Recycle Bin?", "Silver Arrows?\n\nLUL", "Imagine\nfinding the\narrows", "Did you find\nsilvers in\nscenic Ohio?", - "Did you find\nThe arrows in\n*mumblemumble*", - "\nSpin To Win!\n", - "did you find\nthe arrows in\nthe hourglass?", + "Did you find\nthe arrows in\n*mumblemumble*", + "Did you find\nthe arrows in\nthe hourglass?", "Silver Arrows\nare so v30", "OH, NO, THEY\nACTUALLY SAID\nSILVER MARROW", "SURELY THE\nLEFTMOST TILES\nWILL STAY UP", "Did you find\nthe arrows in\nWorld 4-2?", + "SILLY HERO,\nSILVER IS FOR\nWEREWOLVES!", + "Did you find\nthe silvers in\nGanti's ears", +] + +Ganon_Phase_3_No_Weakness_texts = [ + "Error 404\nWeakness\nnot found.", + "No weakness\nfor you today.\nSorry", + "Careful, all\nthat spinning\nmakes me dizzy", + "Are you ready\nTo spin\nTo win?", + "\nSpin To Win!\n", + "SURELY THE\nLEFTMOST TILES\nWILL STAY UP", "You Spin Me\nRight Round\nLike A Record", "SILLY HERO,\nSILVER IS FOR\nWEREWOLVES!", - "Did you find\nthe silvers in\nganti's ears", ] TavernMan_texts = [ diff --git a/data/base2current.bps b/data/base2current.bps index 2099709d178f6079b78e01b53518b989b7e830dc..5b012671547e2042e3a834a0ee35943306c2c113 100644 GIT binary patch delta 6609 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 A9RL6T delta 5788 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 OJHhHHaIy#7(f)s%Z74VZ diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 94308c69..26a2c49d 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -311,6 +311,25 @@ "random" ] }, + "ganon_item": { + "choices": [ + "silver", + "boomerang", + "hookshot", + "powder", + "fire_rod", + "ice_rod", + "bombos", + "ether", + "quake", + "hammer", + "bee", + "somaria", + "byrna", + "random", + "none" + ] + }, "beemizer": { "choices": [ "4", "3", "2", "1", "0" diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index f554ff39..e5093801 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -331,6 +331,13 @@ "Random: Picks a random value between 0 and 7 (inclusive).", "0-7: Number of crystals needed" ], + "ganon_item": [ + "What item Ganon is vulnerable to while stunned in his final phase.", + "Silver: Silver arrows (as in the vanilla game).", + "Random: Picks a random damaging item (but not a medallion if swordless).", + ": The specified item will damage stunned ganon.", + "None: Stunned Ganon cannot be damaged. You must use the normal silverless methods." + ], "openpyramid": [ "Pre-opens the pyramid hole, this removes the Agahnim 2 requirement for it. (default: %(default)s)" ], "rom": [ "Path to an ALttP JP (1.0) rom to use as a base." ,