diff --git a/BaseClasses.py b/BaseClasses.py index 8ea0acb4..a5c52678 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -372,7 +372,7 @@ class World(object): def has_beaten_game(self, state, player: Union[None, int] = None): if player: - return state.has('Triforce', player) + return state.has('Triforce', player) or state.world.logic[player] == 'nologic' else: return all((self.has_beaten_game(state, p) for p in range(1, self.players + 1))) @@ -953,8 +953,7 @@ class Location(object): self.player = player def can_fill(self, state: CollectionState, item: Item, check_access=True) -> bool: - return self.always_allow(state, item) or (self.parent_region.can_fill(item) and self.item_rule(item) and ( - not check_access or self.can_reach(state))) + return self.parent_region.can_fill(item) and (not check_access or self.always_allow(state, item) or (self.item_rule(item) and self.can_reach(state))) def can_reach(self, state: CollectionState) -> bool: if self.parent_region.can_reach(state) and self.access_rule(state): diff --git a/Rules.py b/Rules.py index ab9ae102..1676fcf9 100644 --- a/Rules.py +++ b/Rules.py @@ -11,13 +11,16 @@ def set_rules(world, player): if world.logic[player] == 'nologic': logging.getLogger('').info( 'WARNING! Seeds generated under this logic often require major glitches and may be impossible!') - world.get_region('Menu', player).can_reach_private = lambda state: True - no_logic_rules(world, player) - for exit in world.get_region('Menu', player).exits: - exit.hide_path = True - return - - crossover_logic(world, player) + if world.players == 1: + world.get_region('Menu', player).can_reach_private = lambda state: True + no_logic_rules(world, player) + for exit in world.get_region('Menu', player).exits: + exit.hide_path = True + return + else: + # Set access rules according to max glitches for multiworld progression. Set accessibility to none, and shuffle assuming the no logic players can always win + world.accessibility[player] = 'none' + world.progression_balancing[player] = False global_rules(world, player) @@ -36,7 +39,7 @@ def set_rules(world, player): if world.logic[player] == 'noglitches': no_glitches_rules(world, player) - elif world.logic[player] == 'owglitches': + elif world.logic[player] in ['owglitches', 'nologic']: # 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) @@ -57,7 +60,7 @@ def set_rules(world, player): if world.mode[player] != 'inverted': set_big_bomb_rules(world, player) - if world.logic[player] == 'owglitches' and world.shuffle not in ('insanity', 'insanity_legacy'): + if world.logic[player] in ['owglitches', 'nologic'] and world.shuffle not in ('insanity', 'insanity_legacy'): path_to_courtyard = mirrorless_path_to_castle_courtyard(world, player) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.world.get_entrance('Dark Death Mountain Offset Mirror', player).can_reach(state) and all(rule(state) for rule in path_to_courtyard), 'or') else: @@ -69,13 +72,13 @@ def set_rules(world, player): # GT Entrance may be required for Turtle Rock for OWG and < 7 required ganons_tower = world.get_entrance('Inverted Ganons Tower' if world.mode[player] == 'inverted' else 'Ganons Tower', player) - if world.crystals_needed_for_gt[player] == 7 and not (world.logic[player] == 'owglitches' and world.mode[player] != 'inverted'): + if world.crystals_needed_for_gt[player] == 7 and not (world.logic[player] in ['owglitches', 'nologic'] and world.mode[player] != 'inverted'): set_rule(ganons_tower, lambda state: False) set_trock_key_rules(world, player) set_rule(ganons_tower, lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) - if world.mode[player] != 'inverted' and world.logic[player] == 'owglitches': + if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'nologic']: add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.world.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or') set_bunny_rules(world, player, world.mode[player] == 'inverted') @@ -162,18 +165,6 @@ non_crossover_items = (item_name_groups["Small Keys"] | item_name_groups["Big Ke "Small Key (Universal)"} -def crossover_logic(world, player): - """ Simple and not graceful solution to logic loops if you mix no logic and logic. - Making it so that logical progression cannot be placed in no logic worlds.""" - no_logic_players = set() - for other_player in world.player_ids: - if world.logic[other_player] == 'nologic': - no_logic_players.add(other_player) - if no_logic_players: - for location in world.get_locations(): - if location.player in no_logic_players: - forbid_items(location, non_crossover_items, player) - def global_rules(world, player): # ganon can only carry triforce add_item_rule(world.get_location('Ganon', player), lambda item: item.name == 'Triforce' and item.player == player) @@ -1407,8 +1398,8 @@ def set_bunny_rules(world: World, player: int, inverted: bool): def get_rule_to_add(region, location = None, connecting_entrance = None): # In OWG, a location can potentially be superbunny-mirror accessible or # bunny revival accessible. - if world.logic[player] == 'owglitches': - if region.name == 'Swamp Palace (Entrance)': + if world.logic[player] in ['minorglitches', 'owglitches', 'nologic']: + if region.name == 'Swamp Palace (Entrance)': # Need to 0hp revive - not in logic return lambda state: state.has_Pearl(player) if region.name == 'Tower of Hera (Bottom)': # Need to hit the crystal switch return lambda state: state.has_Mirror(player) and state.has_sword(player) or state.has_Pearl(player) @@ -1446,8 +1437,8 @@ def set_bunny_rules(world: World, player: int, inverted: bool): new_path = path + [entrance.access_rule] seen.add(new_region) if not is_link(new_region): - # For OWG, establish superbunny and revival rules. - if world.logic[player] == 'owglitches' and entrance.name not in OverworldGlitchRules.get_invalid_bunny_revival_dungeons(): + # For glitch rulesets, establish superbunny and revival rules. + if world.logic[player] in ['minorglitches', 'owglitches', 'nologic'] and entrance.name not in OverworldGlitchRules.get_invalid_bunny_revival_dungeons(): if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions(): possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has_Mirror(player) and state.has_sword(player)) elif (region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions() @@ -1485,7 +1476,7 @@ def set_bunny_rules(world: World, player: int, inverted: bool): # Add requirements for all locations that are actually in the dark world, except those available to the bunny, including dungeon revival for entrance in world.get_entrances(): if entrance.player == player and is_bunny(entrance.connected_region): - if world.logic[player] == 'owglitches': + if world.logic[player] in ['minorglitches', 'owglitches', 'nologic'] : if entrance.connected_region.type == RegionType.Dungeon: if entrance.parent_region.type != RegionType.Dungeon and entrance.connected_region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons(): add_rule(entrance, get_rule_to_add(entrance.connected_region, None, entrance)) @@ -1493,7 +1484,7 @@ def set_bunny_rules(world: World, player: int, inverted: bool): if entrance.connected_region.name == 'Turtle Rock (Entrance)': add_rule(world.get_entrance('Turtle Rock Entrance Gap', player), get_rule_to_add(entrance.connected_region, None, entrance)) for location in entrance.connected_region.locations: - if world.logic[player] == 'owglitches' and entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances(): + if world.logic[player] in ['minorglitches', 'owglitches', 'nologic'] and entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances(): continue if location.name in bunny_accessible_locations: continue