diff --git a/BaseClasses.py b/BaseClasses.py index 7076eb24..d587d2ab 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -32,7 +32,7 @@ class World(object): self.light_world_light_cone = False self.dark_world_light_cone = False self.treasure_hunt_count = 0 - self.treasure_hunt_icon = 'Power Star' + self.treasure_hunt_icon = 'Triforce Piece' self.clock_mode = 'off' self.aga_randomness = True self.lock_aga_door_in_escape = False @@ -186,7 +186,7 @@ class World(object): return True elif location.item.name in ['Triforce Piece', 'Power Star']: treasure_pieces_collected += 1 - if self.goal in ['starhunt', 'triforcehunt'] and treasure_pieces_collected >= self.treasure_hunt_count: + if self.goal in ['triforcehunt'] and treasure_pieces_collected >= self.treasure_hunt_count: return True sphere.append(location) @@ -205,10 +205,10 @@ class World(object): logic = 0 if self.logic == 'noglitches' else 1 mode = ['standard', 'open', 'swordless'].index(self.mode) dungeonitems = 0 if self.place_dungeon_items else 1 - goal = ['ganon', 'pedestal', 'dungeons', 'starhunt', 'triforcehunt'].index(self.goal) + goal = ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'].index(self.goal) shuffle = ['vanilla', 'simple', 'restricted', 'full', 'madness', 'insanity', 'dungeonsfull', 'dungeonssimple'].index(self.shuffle) difficulty = ['normal', 'timed', 'timed-ohko', 'timed-countdown'].index(self.difficulty) - algorithm = ['freshness', 'flood', 'vt21', 'vt22', 'restrictive'].index(self.algorithm) + algorithm = ['freshness', 'flood', 'vt21', 'vt22', 'vt25'].index(self.algorithm) beatableonly = 1 if self.check_beatable_only else 0 shuffleganon = 1 if self.shuffle_ganon else 0 return logic | (beatableonly << 1) | (dungeonitems << 2) | (shuffleganon << 3) | (goal << 4) | (shuffle << 7) | (difficulty << 11) | (algorithm << 13) | (mode << 16) diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 8199a7bf..e42f7aab 100644 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -39,18 +39,17 @@ if __name__ == '__main__': Bombos Tablet are unreachable but contain trash items always. ''') - parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'starhunt', 'triforcehunt'], + parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'], help='''\ Select completion goal. (default: %(default)s) Ganon: Collect all crystals, beat Agahnim 2 then defeat Ganon. + Crystals: Collect all crystals then defeat Ganon. Pedestal: Places the Triforce at the Master Sword Pedestal. All Dungeons: Collect all crystals, pendants, beat both Agahnim fights and then defeat Ganon. - Star Hunt: Places 15 Power Stars in the world, collect 10 of - them to beat the game. - Triforce Hunt: Places 3 Triforce Pieces in the world, collect - them all to beat the game. + Triforce Hunt: Places 30 Triforce Pieces in the world, collect + 20 of them to beat the game. ''') parser.add_argument('--difficulty', default='normal', const='normal', nargs='?', choices=['normal', 'timed', 'timed-ohko', 'timed-countdown'], help='''\ @@ -72,10 +71,10 @@ if __name__ == '__main__': Timed mode. If time runs out, you lose (but can still keep playing). ''') - parser.add_argument('--algorithm', default='restrictive', const='restrictive', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'restrictive'], + parser.add_argument('--algorithm', default='vt25', const='vt25', nargs='?', choices=['freshness', 'flood', 'vt21', 'vt22', 'vt25'], help='''\ Select item filling algorithm. (default: %(default)s) - Restrictive: Shuffle items and place them in a random location + vt25: Shuffle items and place them in a random location that it is not impossible to be in. vt21: Unbiased in its selection, but has tendency to put Ice Rod in Turtle Rock. diff --git a/Gui.py b/Gui.py index 60a0668b..7a694219 100644 --- a/Gui.py +++ b/Gui.py @@ -90,7 +90,7 @@ def guiMain(args=None): goalFrame = Frame(drowDownFrame) goalVar = StringVar() goalVar.set('ganon') - goalOptionMenu = OptionMenu(goalFrame, goalVar, 'ganon', 'pedestal', 'dungeons', 'starhunt', 'triforcehunt') + goalOptionMenu = OptionMenu(goalFrame, goalVar, 'ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals') goalOptionMenu.pack(side=RIGHT) goalLabel = Label(goalFrame, text='Game goal') goalLabel.pack(side=LEFT) @@ -105,8 +105,8 @@ def guiMain(args=None): algorithmFrame = Frame(drowDownFrame) algorithmVar = StringVar() - algorithmVar.set('restrictive') - algorithmOptionMenu = OptionMenu(algorithmFrame, algorithmVar, 'freshness', 'flood', 'vt21', 'vt22', 'restrictive') + algorithmVar.set('vt25') + algorithmOptionMenu = OptionMenu(algorithmFrame, algorithmVar, 'freshness', 'flood', 'vt21', 'vt22', 'vt25') algorithmOptionMenu.pack(side=RIGHT) algorithmLabel = Label(algorithmFrame, text='Item distribution algorithm') algorithmLabel.pack(side=LEFT) diff --git a/Main.py b/Main.py index d141c95f..977ebe46 100644 --- a/Main.py +++ b/Main.py @@ -67,7 +67,7 @@ def main(args, seed=None): distribute_items_cutoff(world, 0.66) elif args.algorithm == 'freshness': distribute_items_staleness(world) - elif args.algorithm == 'restrictive': + elif args.algorithm == 'vt25': distribute_items_restrictive(world, 0) logger.info('Calculating playthrough.') @@ -398,7 +398,7 @@ def flood_items(world): def generate_itempool(world): - if world.difficulty not in ['normal', 'timed', 'timed-ohko', 'timed-countdown'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'starhunt', 'triforcehunt'] or world.mode not in ['open', 'standard', 'swordless']: + if world.difficulty not in ['normal', 'timed', 'timed-ohko', 'timed-countdown'] or world.goal not in ['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'] or world.mode not in ['open', 'standard', 'swordless']: raise NotImplementedError('Not supported yet') world.push_item('Ganon', ItemFactory('Triforce'), False) @@ -463,14 +463,10 @@ def generate_itempool(world): if world.goal == 'pedestal': world.push_item('Altar', ItemFactory('Triforce'), False) - elif world.goal == 'starhunt': - world.treasure_hunt_count = 10 - world.treasure_hunt_icon = 'Power Star' - world.itempool.extend(ItemFactory(['Power Star'] * 15)) elif world.goal == 'triforcehunt': - world.treasure_hunt_count = 3 + world.treasure_hunt_count = 20 world.treasure_hunt_icon = 'Triforce Piece' - world.itempool.extend(ItemFactory(['Triforce Piece'] * 3)) + world.itempool.extend(ItemFactory(['Triforce Piece'] * 30)) if random.randint(0, 3) == 0: world.itempool.append(ItemFactory('Magic Upgrade (1/4)')) @@ -568,7 +564,7 @@ def create_playthrough(world): world = copy_world(world) # in treasure hunt and pedestal goals, ganon is invincible - if world.goal in ['pedestal', 'starhunt', 'triforcehunt']: + if world.goal in ['pedestal', 'triforcehunt']: world.get_location('Ganon').item = None # if we only check for beatable, we can do this sanity check first before writing down spheres diff --git a/Rom.py b/Rom.py index 06fd8211..12df938e 100644 --- a/Rom.py +++ b/Rom.py @@ -285,11 +285,13 @@ def patch_rom(world, rom, hashtable, quickswap=False, beep='normal', sprite=None rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp if world.goal in ['ganon']: - rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals are collected - elif world.goal in ['pedestal', 'starhunt', 'triforcehunt']: + rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected + elif world.goal in ['pedestal', 'triforcehunt']: rom.write_byte(0x18003E, 0x01) # make ganon invincible elif world.goal in ['dungeons']: rom.write_byte(0x18003E, 0x02) # make ganon invincible until all dungeons are beat + elif world.goal in ['crystals']: + rom.write_byte(0x18003E, 0x04) # make ganon invincible until all crystals rom.write_byte(0x18016A, 0x00) # disable free roaming item text boxes rom.write_byte(0x18003B, 0x00) # disable maps showing crystals on overworld rom.write_byte(0x18003C, 0x00) # disable compasses showing dungeon count @@ -425,7 +427,7 @@ def write_strings(rom, world): write_string_to_rom(rom, 'PyramidFairy', PyramidFairy_texts[random.randint(0, len(PyramidFairy_texts) - 1)]) write_string_to_rom(rom, 'Sahasrahla2', Sahasrahla2_texts[random.randint(0, len(Sahasrahla2_texts) - 1)]) write_string_to_rom(rom, 'Blind', Blind_texts[random.randint(0, len(Blind_texts) - 1)]) - if world.goal in ['pedestal', 'starhunt', 'triforcehunt']: + if world.goal in ['pedestal', 'triforcehunt']: write_string_to_rom(rom, 'Ganon1Invincible', 'Why are you even here?\n You can\'t even hurt me!') write_string_to_rom(rom, 'Ganon2Invincible', 'Seriously? Go Away, I will not Die.') else: diff --git a/Rules.py b/Rules.py index b3a6d89a..7ec0bec3 100644 --- a/Rules.py +++ b/Rules.py @@ -22,7 +22,10 @@ def set_rules(world): if world.goal == 'dungeons': # require all dungeons to beat ganon - add_rule(world.get_location('Ganon'), lambda state: state.can_reach('Altar', 'Location') and state.has('Beat Agahnim 1')) + add_rule(world.get_location('Ganon'), lambda state: state.can_reach('Altar', 'Location') and state.has('Beat Agahnim 1') and state.has('Beat Agahnim 2')) + elif world.goal == 'ganon': + # require aga2 to beat ganon + add_rule(world.get_location('Ganon'), lambda state: state.has('Beat Agahnim 2')) set_big_bomb_rules(world) @@ -306,7 +309,7 @@ def global_rules(world): '[dungeon-A2-6F] Ganons Tower - Room before Moldorm', '[dungeon-A2-6F] Ganons Tower - Moldorm Room']: forbid_item(world.get_location(location), 'Big Key (Ganons Tower)') - set_rule(world.get_location('Ganon'), lambda state: state.has_beam_sword() and state.has_fire_source() and state.has('Beat Agahnim 2') and state.has('Crystal 1') and state.has('Crystal 2') + set_rule(world.get_location('Ganon'), lambda state: state.has_beam_sword() and state.has_fire_source() and state.has('Crystal 1') and state.has('Crystal 2') and state.has('Crystal 3') and state.has('Crystal 4') and state.has('Crystal 5') and state.has('Crystal 6') and state.has('Crystal 7') and (state.has('Tempered Sword') or state.has('Golden Sword') or (state.has('Silver Arrows') and state.has('Bow')) or state.has('Lamp') or state.has('Bottle') or state.has('Half Magic') or state.has('Quarter Magic'))) # need to light torch a sufficient amount of times set_rule(world.get_entrance('Ganon Drop'), lambda state: state.has_beam_sword()) # need to damage ganon to get tiles to drop @@ -383,7 +386,7 @@ def swordless_rules(world): set_rule(world.get_entrance('Turtle Rock'), lambda state: state.has_Pearl() and state.has_turtle_rock_medallion() and state.can_reach('Turtle Rock (Top)', 'Region')) # sword not required to use medallion for opening in swordless (!) set_rule(world.get_entrance('Skull Woods Torch Room'), lambda state: state.has('Small Key (Skull Woods)', 3) and state.has('Fire Rod')) # no curtain set_rule(world.get_location('Agahnim 2'), lambda state: state.has('Hammer') or state.has('Bug Catching Net')) - set_rule(world.get_location('Ganon'), lambda state: state.has('Hammer') and state.has_fire_source() and state.has('Silver Arrows') and state.has('Bow') and state.has('Beat Agahnim 2') and state.has('Crystal 1') and state.has('Crystal 2') + set_rule(world.get_location('Ganon'), lambda state: state.has('Hammer') and state.has_fire_source() and state.has('Silver Arrows') and state.has('Bow') and state.has('Crystal 1') and state.has('Crystal 2') and state.has('Crystal 3') and state.has('Crystal 4') and state.has('Crystal 5') and state.has('Crystal 6') and state.has('Crystal 7')) set_rule(world.get_entrance('Ganon Drop'), lambda state: state.has('Hammer')) # need to damage ganon to get tiles to drop