diff --git a/BaseClasses.py b/BaseClasses.py index 0c2925e5..6e1ebd41 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1538,6 +1538,11 @@ class Region(object): self.crystal_switch = False def can_reach(self, state): + from Utils import stack_size3a + from DungeonGenerator import GenerationException + if stack_size3a() > 500: + raise GenerationException(f'Infinite loop detected for "{self.name}" located at \'Region.can_reach\'') + if state.stale[self.player]: state.update_reachable_regions(self.player) return self in state.reachable_regions[self.player] diff --git a/CHANGELOG.md b/CHANGELOG.md index dd9cae91..e2990d4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +### 0.2.3.5 +- Fixed issue with multiworld generation +- Added infinite loop detection +- Move mirror portal off screen during mirror bonk in Crossed OW + ### 0.2.3.4 - Fixed major issue with subsequent seeds using same seed/settings resulting different - Flute Shuffle now awards separated regions a prorated number of flute spots based on size diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 2b51eaaf..337eb453 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -40,9 +40,9 @@ def link_entrances(world, player): connect_simple(world, 'Other World S&Q', 'Pyramid Area', player) else: entrance_pool.remove('Pyramid Hole') - entrance_pool.add('Inverted Pyramid Hole') + entrance_pool.append('Inverted Pyramid Hole') entrance_pool.remove('Pyramid Entrance') - entrance_pool.add('Inverted Pyramid Entrance') + entrance_pool.append('Inverted Pyramid Entrance') drop_connections.append(tuple(('Inverted Pyramid Hole', 'Pyramid'))) dropexit_connections.append(tuple(('Inverted Pyramid Entrance', 'Pyramid Exit'))) connect_simple(world, 'Other World S&Q', 'Hyrule Castle Ledge', player) @@ -1476,8 +1476,8 @@ def junk_fill_inaccessible(world, player): from DoorShuffle import find_inaccessible_regions find_inaccessible_regions(world, player) - for player in range(1, world.players + 1): - world.key_logic[player] = {} + for p in range(1, world.players + 1): + world.key_logic[p] = {} base_world = copy_world(world) base_world.override_bomb_check = True world.key_logic = {} @@ -1641,8 +1641,8 @@ def build_accessible_entrance_list(world, start_region, player, assumed_inventor from Main import copy_world from Items import ItemFactory - for player in range(1, world.players + 1): - world.key_logic[player] = {} + for p in range(1, world.players + 1): + world.key_logic[p] = {} base_world = copy_world(world) base_world.override_bomb_check = True world.key_logic = {} @@ -1751,8 +1751,8 @@ def can_reach(world, entrance_name, region_name, player): from Items import ItemFactory from DoorShuffle import find_inaccessible_regions - for player in range(1, world.players + 1): - world.key_logic[player] = {} + for p in range(1, world.players + 1): + world.key_logic[p] = {} base_world = copy_world(world) base_world.override_bomb_check = True world.key_logic = {} diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 04f2d575..3f753b85 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -4,7 +4,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel -__version__ = '0.2.3.4-u' +__version__ = '0.2.3.5-u' def link_overworld(world, player): # setup mandatory connections @@ -853,8 +853,8 @@ def build_sectors(world, player): from OWEdges import OWTileRegions # perform accessibility check on duplicate world - for player in range(1, world.players + 1): - world.key_logic[player] = {} + for p in range(1, world.players + 1): + world.key_logic[p] = {} base_world = copy_world(world) world.key_logic = {} @@ -906,8 +906,12 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F from Main import copy_world from BaseClasses import CollectionState from Items import ItemFactory + from Utils import stack_size3a def explore_region(region_name, region=None): + if stack_size3a() > 500: + raise GenerationException(f'Infinite loop detected for "{start_region}" located at \'build_accessible_region_list\'') + explored_regions.append(region_name) if not region: region = base_world.get_region(region_name, player) @@ -925,8 +929,8 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F explore_region(exit.connected_region.name, exit.connected_region) if build_copy_world: - for player in range(1, world.players + 1): - world.key_logic[player] = {} + for p in range(1, world.players + 1): + world.key_logic[p] = {} base_world = copy_world(world) base_world.override_bomb_check = True world.key_logic = {} diff --git a/Rom.py b/Rom.py index 6c57d091..8ff75fbd 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'c5903112ffe302d1e9a3878ee720d1b3' +RANDOMIZERBASEHASH = 'c1a00c60144c6cc5a6ef5f00617a9b38' class JsonRom(object): diff --git a/Utils.py b/Utils.py index deed090d..99702a98 100644 --- a/Utils.py +++ b/Utils.py @@ -7,6 +7,7 @@ import sys import xml.etree.ElementTree as ET from collections import defaultdict from functools import reduce +from itertools import count def int16_as_bytes(value): @@ -674,6 +675,21 @@ def extract_data_from_jp_rom(rom): print() +def stack_size3a(size=2): + # See reference: https://stackoverflow.com/questions/34115298/how-do-i-get-the-current-depth-of-the-python-interpreter-stack + """Get stack size for caller's frame.""" + frame = sys._getframe(size) + try: + for size in count(size, 8): + frame = frame.f_back.f_back.f_back.f_back.\ + f_back.f_back.f_back.f_back + except AttributeError: + while frame: + frame = frame.f_back + size += 1 + return size - 1 + + class bidict(dict): def __init__(self, *args, **kwargs): super(bidict, self).__init__(*args, **kwargs) diff --git a/asm/owrando.asm b/asm/owrando.asm index f239b436..065cbfd8 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -160,6 +160,12 @@ OWPreserveMirrorSprite: pla : lda #$de : pha ; in vanilla, if in dark world, jump to $05afdf rtl } +OWMirrorSpriteMove: +{ + lda.l OWMode+1 : and.b #!FLAG_OW_CROSSED : beq + + lda $1acf : eor #$80 : sta $1acf + + lda #$2c : jml.l $07A985 ; what we wrote over +} OWFluteCancel: { diff --git a/data/base2current.bps b/data/base2current.bps index e229d170..bac70e13 100644 Binary files a/data/base2current.bps and b/data/base2current.bps differ