Merge branch 'main' of https://github.com/espeon65536/Archipelago into main
This commit is contained in:
		
						commit
						5d6592f296
					
				| 
						 | 
				
			
			@ -813,6 +813,9 @@ class CollectionState(object):
 | 
			
		|||
            rules.append(self.has('Moon Pearl', player))
 | 
			
		||||
        return all(rules)
 | 
			
		||||
 | 
			
		||||
    def can_bomb_clip(self, region: Region, player: int) -> bool: 
 | 
			
		||||
        return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player)
 | 
			
		||||
 | 
			
		||||
    # Minecraft logic functions
 | 
			
		||||
    def has_iron_ingots(self, player: int):
 | 
			
		||||
        return self.has('Progressive Tools', player) and self.has('Ingot Crafting', player)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -582,11 +582,11 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
 | 
			
		|||
 | 
			
		||||
def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
 | 
			
		||||
    glitches_required = get_choice('glitches_required', weights)
 | 
			
		||||
    if glitches_required not in [None, 'none', 'no_logic', 'overworld_glitches', 'minor_glitches']:
 | 
			
		||||
        logging.warning("Only NMG, OWG and No Logic supported")
 | 
			
		||||
    if glitches_required not in [None, 'none', 'no_logic', 'overworld_glitches', 'hybrid_major_glitches', 'minor_glitches']:
 | 
			
		||||
        logging.warning("Only NMG, OWG, HMG and No Logic supported")
 | 
			
		||||
        glitches_required = 'none'
 | 
			
		||||
    ret.logic = {None: 'noglitches', 'none': 'noglitches', 'no_logic': 'nologic', 'overworld_glitches': 'owglitches',
 | 
			
		||||
                 'minor_glitches': 'minorglitches'}[
 | 
			
		||||
                 'minor_glitches': 'minorglitches', 'hybrid_major_glitches': 'hybridglitches'}[
 | 
			
		||||
        glitches_required]
 | 
			
		||||
 | 
			
		||||
    ret.dark_room_logic = get_choice("dark_room_logic", weights, "lamp")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,8 +146,10 @@ class Logic(Choice):
 | 
			
		|||
    option_no_glitches = 0
 | 
			
		||||
    option_minor_glitches = 1
 | 
			
		||||
    option_overworld_glitches = 2
 | 
			
		||||
    option_hybrid_major_glitches = 3
 | 
			
		||||
    option_no_logic = 4
 | 
			
		||||
    alias_owg = 2
 | 
			
		||||
    alias_hmg = 3
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Objective(Choice):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
# ToDo: With shuffle_ganon option, prevent gtower from linking to an exit only location through a 2 entrance cave.
 | 
			
		||||
from collections import defaultdict
 | 
			
		||||
 | 
			
		||||
from worlds.alttp.UnderworldGlitchRules import underworld_glitch_connections
 | 
			
		||||
 | 
			
		||||
def link_entrances(world, player):
 | 
			
		||||
    connect_two_way(world, 'Links House', 'Links House Exit', player) # unshuffled. For now
 | 
			
		||||
| 
						 | 
				
			
			@ -1066,6 +1066,10 @@ def link_entrances(world, player):
 | 
			
		|||
        raise NotImplementedError(
 | 
			
		||||
            f'{world.shuffle[player]} Shuffling not supported yet. Player {world.get_player_names(player)}')
 | 
			
		||||
 | 
			
		||||
    # mandatory hybrid major glitches connections
 | 
			
		||||
    if world.logic[player] in ['hybridglitches', 'nologic']:
 | 
			
		||||
        underworld_glitch_connections(world, player)
 | 
			
		||||
 | 
			
		||||
    # check for swamp palace fix
 | 
			
		||||
    if world.get_entrance('Dam', player).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', player).connected_region.name != 'Swamp Palace (Entrance)':
 | 
			
		||||
        world.swamp_patch_required[player] = True
 | 
			
		||||
| 
						 | 
				
			
			@ -1767,6 +1771,10 @@ def link_inverted_entrances(world, player):
 | 
			
		|||
    else:
 | 
			
		||||
        raise NotImplementedError('Shuffling not supported yet')
 | 
			
		||||
 | 
			
		||||
    # mandatory hybrid major glitches connections
 | 
			
		||||
    if world.logic[player] in ['hybridglitches', 'nologic']:
 | 
			
		||||
        underworld_glitch_connections(world, player)
 | 
			
		||||
 | 
			
		||||
    # patch swamp drain
 | 
			
		||||
    if world.get_entrance('Dam', player).connected_region.name != 'Dam' or world.get_entrance('Swamp Palace', player).connected_region.name != 'Swamp Palace (Entrance)':
 | 
			
		||||
        world.swamp_patch_required[player] = True
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -571,7 +571,7 @@ def get_pool_core(world, player: int):
 | 
			
		|||
        return world.random.choice([True, False]) if progressive == 'random' else progressive == 'on'
 | 
			
		||||
 | 
			
		||||
    # provide boots to major glitch dependent seeds
 | 
			
		||||
    if logic in {'owglitches', 'nologic'} and world.glitch_boots[player] and goal != 'icerodhunt':
 | 
			
		||||
    if logic in {'owglitches', 'hybridglitches', 'nologic'} and world.glitch_boots[player] and goal != 'icerodhunt':
 | 
			
		||||
        precollected_items.append('Pegasus Boots')
 | 
			
		||||
        pool.remove('Pegasus Boots')
 | 
			
		||||
        pool.append('Rupees (20)')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ from worlds.alttp import OverworldGlitchRules
 | 
			
		|||
from BaseClasses import RegionType, MultiWorld, Entrance
 | 
			
		||||
from worlds.alttp.Items import ItemFactory, progression_items, item_name_groups
 | 
			
		||||
from worlds.alttp.OverworldGlitchRules import overworld_glitches_rules, no_logic_rules
 | 
			
		||||
from worlds.alttp.UnderworldGlitchRules import underworld_glitches_rules
 | 
			
		||||
from worlds.alttp.Bosses import GanonDefeatRule
 | 
			
		||||
from worlds.generic.Rules import set_rule, add_rule, forbid_item, add_item_rule, item_in_locations, \
 | 
			
		||||
    item_name
 | 
			
		||||
| 
						 | 
				
			
			@ -47,12 +48,17 @@ def set_rules(world, player):
 | 
			
		|||
 | 
			
		||||
    if world.logic[player] == 'noglitches':
 | 
			
		||||
        no_glitches_rules(world, player)
 | 
			
		||||
    elif world.logic[player] in ['owglitches', 'nologic']:
 | 
			
		||||
    elif world.logic[player] == 'owglitches':
 | 
			
		||||
        # Initially setting no_glitches_rules to set the baseline rules for some
 | 
			
		||||
        # entrances. The overworld_glitches_rules set is primarily additive.
 | 
			
		||||
        no_glitches_rules(world, player)
 | 
			
		||||
        fake_flipper_rules(world, player)
 | 
			
		||||
        overworld_glitches_rules(world, player)
 | 
			
		||||
    elif world.logic[player] in ['hybridglitches', 'nologic']: 
 | 
			
		||||
        no_glitches_rules(world, player)
 | 
			
		||||
        fake_flipper_rules(world, player)
 | 
			
		||||
        overworld_glitches_rules(world, player)
 | 
			
		||||
        underworld_glitches_rules(world, player)
 | 
			
		||||
    elif world.logic[player] == 'minorglitches':
 | 
			
		||||
        no_glitches_rules(world, player)
 | 
			
		||||
        fake_flipper_rules(world, player)
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +81,8 @@ def set_rules(world, player):
 | 
			
		|||
        set_inverted_big_bomb_rules(world, player)
 | 
			
		||||
 | 
			
		||||
    # if swamp and dam have not been moved we require mirror for swamp palace
 | 
			
		||||
    if not world.swamp_patch_required[player]:
 | 
			
		||||
    # however there is mirrorless swamp in hybrid MG, so we don't necessarily want this. HMG handles this requirement itself. 
 | 
			
		||||
    if not world.swamp_patch_required[player] and world.logic[player] not in ['hybridglitches', 'nologic']:
 | 
			
		||||
        add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player))
 | 
			
		||||
 | 
			
		||||
    # GT Entrance may be required for Turtle Rock for OWG and < 7 required
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,114 @@
 | 
			
		|||
 | 
			
		||||
from BaseClasses import Entrance
 | 
			
		||||
from worlds.generic.Rules import set_rule, add_rule
 | 
			
		||||
 | 
			
		||||
# We actually need the logic to properly "mark" these regions as Light or Dark world. 
 | 
			
		||||
# Therefore we need to make these connections during the normal link_entrances stage, rather than during set_rules. 
 | 
			
		||||
def underworld_glitch_connections(world, player): 
 | 
			
		||||
    specrock = world.get_region('Spectacle Rock Cave (Bottom)', player)
 | 
			
		||||
    mire = world.get_region('Misery Mire (West)', player)
 | 
			
		||||
 | 
			
		||||
    kikiskip = Entrance(player, 'Kiki Skip', specrock)
 | 
			
		||||
    mire_to_hera = Entrance(player, 'Mire to Hera Clip', mire)
 | 
			
		||||
    mire_to_swamp = Entrance(player, 'Hera to Swamp Clip', mire)
 | 
			
		||||
    specrock.exits.append(kikiskip)
 | 
			
		||||
    mire.exits.extend([mire_to_hera, mire_to_swamp])
 | 
			
		||||
 | 
			
		||||
    if world.fix_fake_world[player]: 
 | 
			
		||||
        kikiskip.connect(world.get_entrance('Palace of Darkness Exit', player).connected_region)
 | 
			
		||||
        mire_to_hera.connect(world.get_entrance('Tower of Hera Exit', player).connected_region)
 | 
			
		||||
        mire_to_swamp.connect(world.get_entrance('Swamp Palace Exit', player).connected_region)
 | 
			
		||||
    else: 
 | 
			
		||||
        kikiskip.connect(world.get_region('Palace of Darkness (Entrance)', player))
 | 
			
		||||
        mire_to_hera.connect(world.get_region('Tower of Hera (Bottom)', player))
 | 
			
		||||
        mire_to_swamp.connect(world.get_region('Swamp Palace (Entrance)', player))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# For some entrances, we need to fake having pearl, because we're in fake DW/LW. 
 | 
			
		||||
# This creates a copy of the input state that has Moon Pearl. 
 | 
			
		||||
def fake_pearl_state(state, player): 
 | 
			
		||||
    if state.has('Moon Pearl', player):
 | 
			
		||||
        return state
 | 
			
		||||
    fake_state = state.copy()
 | 
			
		||||
    fake_state.prog_items['Moon Pearl', player] += 1
 | 
			
		||||
    return fake_state
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Sets the rules on where we can actually go using this clip.
 | 
			
		||||
# Behavior differs based on what type of ER shuffle we're playing. 
 | 
			
		||||
def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, dungeon_exit: str): 
 | 
			
		||||
    fix_dungeon_exits = world.fix_palaceofdarkness_exit[player]
 | 
			
		||||
    fix_fake_worlds = world.fix_fake_world[player]
 | 
			
		||||
 | 
			
		||||
    dungeon_entrance = [r for r in world.get_region(dungeon_region, player).entrances if r.name != clip.name][0]
 | 
			
		||||
    if not fix_dungeon_exits: # vanilla, simple, restricted, dungeonssimple; should never have fake worlds fix
 | 
			
		||||
        # Dungeons are only shuffled among themselves. We need to check SW, MM, and AT because they can't be reentered trivially. 
 | 
			
		||||
        if dungeon_entrance.name == 'Skull Woods Final Section': 
 | 
			
		||||
            set_rule(clip, lambda state: False) # entrance doesn't exist until you fire rod it from the other side
 | 
			
		||||
        elif dungeon_entrance.name == 'Misery Mire': 
 | 
			
		||||
            add_rule(clip, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # open the dungeon
 | 
			
		||||
        elif dungeon_entrance.name == 'Agahnims Tower': 
 | 
			
		||||
            add_rule(clip, lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # kill/bypass barrier
 | 
			
		||||
        # Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally. 
 | 
			
		||||
        add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
 | 
			
		||||
    elif not fix_fake_worlds: # full, dungeonsfull; fixed dungeon exits, but no fake worlds fix
 | 
			
		||||
        # Entry requires the entrance's requirements plus a fake pearl, but you don't gain logical access to the surrounding region. 
 | 
			
		||||
        add_rule(clip, lambda state: dungeon_entrance.access_rule(fake_pearl_state(state, player)))
 | 
			
		||||
        # exiting restriction
 | 
			
		||||
        add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state))
 | 
			
		||||
    # Otherwise, the shuffle type is crossed, dungeonscrossed, or insanity; all of these do not need additional rules on where we can go, 
 | 
			
		||||
    # since the clip links directly to the exterior region. 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def underworld_glitches_rules(world, player): 
 | 
			
		||||
    fix_dungeon_exits = world.fix_palaceofdarkness_exit[player]
 | 
			
		||||
    fix_fake_worlds = world.fix_fake_world[player]
 | 
			
		||||
 | 
			
		||||
    # Ice Palace Entrance Clip
 | 
			
		||||
    # This is the easiest one since it's a simple internal clip. Just need to also add melting to freezor chest since it's otherwise assumed. 
 | 
			
		||||
    add_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.can_bomb_clip(world.get_region('Ice Palace (Entrance)', player), player), combine='or')
 | 
			
		||||
    add_rule(world.get_location('Ice Palace - Freezor Chest', player), lambda state: state.can_melt_things(player))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Kiki Skip
 | 
			
		||||
    kikiskip = world.get_entrance('Kiki Skip', player)
 | 
			
		||||
    set_rule(kikiskip, lambda state: state.can_bomb_clip(kikiskip.parent_region, player))
 | 
			
		||||
    dungeon_reentry_rules(world, player, kikiskip, 'Palace of Darkness (Entrance)', 'Palace of Darkness Exit')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Mire -> Hera -> Swamp
 | 
			
		||||
    # Using mire keys on other dungeon doors
 | 
			
		||||
    mire = world.get_region('Misery Mire (West)', player)
 | 
			
		||||
    mire_clip = lambda state: state.can_reach('Misery Mire (West)', 'Region', player) and state.can_bomb_clip(mire, player) and state.has_fire_source(player)
 | 
			
		||||
    hera_clip = lambda state: state.can_reach('Tower of Hera (Top)', 'Region', player) and state.can_bomb_clip(world.get_region('Tower of Hera (Top)', player), player)
 | 
			
		||||
    add_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: mire_clip(state) and state.has('Big Key (Misery Mire)', player), combine='or')
 | 
			
		||||
    add_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: mire_clip(state), combine='or')
 | 
			
		||||
    add_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: mire_clip(state) or hera_clip(state), combine='or')
 | 
			
		||||
 | 
			
		||||
    # Build the rule for SP moat. 
 | 
			
		||||
    # We need to be able to s+q to old man, then go to either Mire or Hera at either Hera or GT. 
 | 
			
		||||
    # First we require a certain type of entrance shuffle, then build the rule from its pieces. 
 | 
			
		||||
    if not world.swamp_patch_required[player]:
 | 
			
		||||
        mirrorless_moat_rules = []
 | 
			
		||||
        if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'dungeonscrossed']: 
 | 
			
		||||
            mirrorless_moat_rules.append(lambda state: state.can_reach('Old Man S&Q', 'Entrance', player) and mire_clip(state))
 | 
			
		||||
            rule_map = {
 | 
			
		||||
                'Misery Mire (Entrance)': (lambda state: True),
 | 
			
		||||
                'Tower of Hera (Bottom)': (lambda state: state.can_reach('Tower of Hera Big Key Door', 'Entrance', player))
 | 
			
		||||
            }
 | 
			
		||||
            inverted = world.mode[player] == 'inverted'
 | 
			
		||||
            hera_rule = lambda state: (state.has('Moon Pearl', player) or not inverted) and \
 | 
			
		||||
                                      rule_map.get(world.get_entrance('Tower of Hera', player).connected_region.name, lambda state: False)(state)
 | 
			
		||||
            gt_rule = lambda state: (state.has('Moon Pearl', player) or inverted) and \
 | 
			
		||||
                                    rule_map.get(world.get_entrance(('Ganons Tower' if not inverted else 'Inverted Ganons Tower'), player).connected_region.name, lambda state: False)(state)
 | 
			
		||||
            add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player) or all([rule(state) for rule in mirrorless_moat_rules]))
 | 
			
		||||
        else: 
 | 
			
		||||
            add_rule(world.get_entrance('Swamp Palace Moat', player), lambda state: state.has('Magic Mirror', player))
 | 
			
		||||
 | 
			
		||||
    # Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys
 | 
			
		||||
    mire_to_hera = world.get_entrance('Mire to Hera Clip', player)
 | 
			
		||||
    mire_to_swamp = world.get_entrance('Hera to Swamp Clip', player)
 | 
			
		||||
    set_rule(mire_to_hera, mire_clip)
 | 
			
		||||
    set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has('Flippers', player))
 | 
			
		||||
    dungeon_reentry_rules(world, player, mire_to_hera, 'Tower of Hera (Bottom)', 'Tower of Hera Exit')
 | 
			
		||||
    dungeon_reentry_rules(world, player, mire_to_swamp, 'Swamp Palace (Entrance)', 'Swamp Palace Exit')
 | 
			
		||||
		Loading…
	
		Reference in New Issue