Add logic for including nologic seeds in multiworlds (overworld glitch rules)

nologic seeds are generated as beatable only with no progression balancing, where it is assumed the player can always win no matter where items are placed.
Add superbunny and dungeon revival to minor glitch ruleset.
This commit is contained in:
compiling 2020-07-12 20:19:45 +10:00
parent ec49fa026b
commit 1594d82fb2
2 changed files with 22 additions and 32 deletions

View File

@ -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):

View File

@ -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