parent
							
								
									9fa1f4e85f
								
							
						
					
					
						commit
						a4b61118cf
					
				|  | @ -14,7 +14,7 @@ class LocationData(NamedTuple): | ||||||
|     rule: Callable[[CollectionState], bool] = lambda state: True |     rule: Callable[[CollectionState], bool] = lambda state: True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_locations(world: Optional[MultiWorld], player: Optional[int],  | def get_location_datas(world: Optional[MultiWorld], player: Optional[int],  | ||||||
|                   precalculated_weights: PreCalculatedWeights) -> Tuple[LocationData, ...]: |                   precalculated_weights: PreCalculatedWeights) -> Tuple[LocationData, ...]: | ||||||
| 
 | 
 | ||||||
|     flooded: PreCalculatedWeights = precalculated_weights |     flooded: PreCalculatedWeights = precalculated_weights | ||||||
|  |  | ||||||
|  | @ -1,64 +1,64 @@ | ||||||
| from typing import List, Set, Dict, Tuple, Optional, Callable | from typing import List, Set, Dict, Tuple, Optional, Callable | ||||||
| from BaseClasses import CollectionState, MultiWorld, Region, Entrance, Location | from BaseClasses import CollectionState, MultiWorld, Region, Entrance, Location | ||||||
| from .Options import is_option_enabled | from .Options import is_option_enabled | ||||||
| from .Locations import LocationData | from .Locations import LocationData, get_location_datas | ||||||
| from .PreCalculatedWeights import PreCalculatedWeights | from .PreCalculatedWeights import PreCalculatedWeights | ||||||
| from .LogicExtensions import TimespinnerLogic | from .LogicExtensions import TimespinnerLogic | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location], | def create_regions_and_locations(world: MultiWorld, player: int, precalculated_weights: PreCalculatedWeights): | ||||||
|                    precalculated_weights: PreCalculatedWeights): |     locationn_datas: Tuple[LocationData] = get_location_datas(world, player, precalculated_weights) | ||||||
| 
 | 
 | ||||||
|     locations_per_region = get_locations_per_region(locations) |     locations_per_region: Dict[str, List[LocationData]] = split_location_datas_per_region(locationn_datas) | ||||||
| 
 | 
 | ||||||
|     regions = [ |     regions = [ | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Menu'), |         create_region(world, player, locations_per_region, 'Menu'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Tutorial'), |         create_region(world, player, locations_per_region, 'Tutorial'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Lake desolation'), |         create_region(world, player, locations_per_region, 'Lake desolation'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Upper lake desolation'), |         create_region(world, player, locations_per_region, 'Upper lake desolation'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Lower lake desolation'), |         create_region(world, player, locations_per_region, 'Lower lake desolation'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Eastern lake desolation'), |         create_region(world, player, locations_per_region, 'Eastern lake desolation'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Library'), |         create_region(world, player, locations_per_region, 'Library'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Library top'), |         create_region(world, player, locations_per_region, 'Library top'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower left'), |         create_region(world, player, locations_per_region, 'Varndagroth tower left'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (upper)'), |         create_region(world, player, locations_per_region, 'Varndagroth tower right (upper)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (lower)'), |         create_region(world, player, locations_per_region, 'Varndagroth tower right (lower)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (elevator)'), |         create_region(world, player, locations_per_region, 'Varndagroth tower right (elevator)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (Sirens)'), |         create_region(world, player, locations_per_region, 'Sealed Caves (Sirens)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Military Fortress'), |         create_region(world, player, locations_per_region, 'Military Fortress'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Military Fortress (hangar)'), |         create_region(world, player, locations_per_region, 'Military Fortress (hangar)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'The lab'), |         create_region(world, player, locations_per_region, 'The lab'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'The lab (power off)'), |         create_region(world, player, locations_per_region, 'The lab (power off)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'The lab (upper)'), |         create_region(world, player, locations_per_region, 'The lab (upper)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Emperors tower'), |         create_region(world, player, locations_per_region, 'Emperors tower'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Skeleton Shaft'), |         create_region(world, player, locations_per_region, 'Skeleton Shaft'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (upper)'), |         create_region(world, player, locations_per_region, 'Sealed Caves (upper)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (Xarion)'), |         create_region(world, player, locations_per_region, 'Sealed Caves (Xarion)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Refugee Camp'), |         create_region(world, player, locations_per_region, 'Refugee Camp'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Forest'), |         create_region(world, player, locations_per_region, 'Forest'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Left Side forest Caves'), |         create_region(world, player, locations_per_region, 'Left Side forest Caves'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Upper Lake Serene'), |         create_region(world, player, locations_per_region, 'Upper Lake Serene'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Lower Lake Serene'), |         create_region(world, player, locations_per_region, 'Lower Lake Serene'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (upper)'), |         create_region(world, player, locations_per_region, 'Caves of Banishment (upper)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (Maw)'), |         create_region(world, player, locations_per_region, 'Caves of Banishment (Maw)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (Sirens)'), |         create_region(world, player, locations_per_region, 'Caves of Banishment (Sirens)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Castle Ramparts'), |         create_region(world, player, locations_per_region, 'Castle Ramparts'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Castle Keep'), |         create_region(world, player, locations_per_region, 'Castle Keep'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Castle Basement'), |         create_region(world, player, locations_per_region, 'Castle Basement'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Royal towers (lower)'), |         create_region(world, player, locations_per_region, 'Royal towers (lower)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Royal towers'), |         create_region(world, player, locations_per_region, 'Royal towers'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Royal towers (upper)'), |         create_region(world, player, locations_per_region, 'Royal towers (upper)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Temporal Gyre'), |         create_region(world, player, locations_per_region, 'Temporal Gyre'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (entrance)'), |         create_region(world, player, locations_per_region, 'Ancient Pyramid (entrance)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (left)'), |         create_region(world, player, locations_per_region, 'Ancient Pyramid (left)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (right)'), |         create_region(world, player, locations_per_region, 'Ancient Pyramid (right)'), | ||||||
|         create_region(world, player, locations_per_region, location_cache, 'Space time continuum') |         create_region(world, player, locations_per_region, 'Space time continuum') | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     if is_option_enabled(world, player, "GyreArchives"): |     if is_option_enabled(world, player, "GyreArchives"): | ||||||
|         regions.extend([ |         regions.extend([ | ||||||
|             create_region(world, player, locations_per_region, location_cache, 'Ravenlord\'s Lair'), |             create_region(world, player, locations_per_region, 'Ravenlord\'s Lair'), | ||||||
|             create_region(world, player, locations_per_region, location_cache, 'Ifrit\'s Lair'), |             create_region(world, player, locations_per_region, 'Ifrit\'s Lair'), | ||||||
|         ]) |         ]) | ||||||
| 
 | 
 | ||||||
|     if __debug__: |     if __debug__: | ||||||
|  | @ -70,127 +70,126 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData | ||||||
| 
 | 
 | ||||||
|     flooded: PreCalculatedWeights = precalculated_weights |     flooded: PreCalculatedWeights = precalculated_weights | ||||||
|     logic = TimespinnerLogic(world, player, precalculated_weights) |     logic = TimespinnerLogic(world, player, precalculated_weights) | ||||||
|     names: Dict[str, int] = {} |  | ||||||
| 
 | 
 | ||||||
|     connect(world, player, names, 'Lake desolation', 'Lower lake desolation', lambda state: logic.has_timestop(state) or state.has('Talaria Attachment', player) or flooded.flood_lake_desolation) |     connect(world, player, 'Lake desolation', 'Lower lake desolation', lambda state: logic.has_timestop(state) or state.has('Talaria Attachment', player) or flooded.flood_lake_desolation) | ||||||
|     connect(world, player, names, 'Lake desolation', 'Upper lake desolation', lambda state: logic.has_fire(state) and state.can_reach('Upper Lake Serene', 'Region', player)) |     connect(world, player, 'Lake desolation', 'Upper lake desolation', lambda state: logic.has_fire(state) and state.can_reach('Upper Lake Serene', 'Region', player)) | ||||||
|     connect(world, player, names, 'Lake desolation', 'Skeleton Shaft', lambda state: logic.has_doublejump(state) or flooded.flood_lake_desolation) |     connect(world, player, 'Lake desolation', 'Skeleton Shaft', lambda state: logic.has_doublejump(state) or flooded.flood_lake_desolation) | ||||||
|     connect(world, player, names, 'Lake desolation', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Lake desolation', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Upper lake desolation', 'Lake desolation') |     connect(world, player, 'Upper lake desolation', 'Lake desolation') | ||||||
|     connect(world, player, names, 'Upper lake desolation', 'Eastern lake desolation') |     connect(world, player, 'Upper lake desolation', 'Eastern lake desolation') | ||||||
|     connect(world, player, names, 'Lower lake desolation', 'Lake desolation')  |     connect(world, player, 'Lower lake desolation', 'Lake desolation')  | ||||||
|     connect(world, player, names, 'Lower lake desolation', 'Eastern lake desolation') |     connect(world, player, 'Lower lake desolation', 'Eastern lake desolation') | ||||||
|     connect(world, player, names, 'Eastern lake desolation', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Eastern lake desolation', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Eastern lake desolation', 'Library') |     connect(world, player, 'Eastern lake desolation', 'Library') | ||||||
|     connect(world, player, names, 'Eastern lake desolation', 'Lower lake desolation') |     connect(world, player, 'Eastern lake desolation', 'Lower lake desolation') | ||||||
|     connect(world, player, names, 'Eastern lake desolation', 'Upper lake desolation', lambda state: logic.has_fire(state) and state.can_reach('Upper Lake Serene', 'Region', player)) |     connect(world, player, 'Eastern lake desolation', 'Upper lake desolation', lambda state: logic.has_fire(state) and state.can_reach('Upper Lake Serene', 'Region', player)) | ||||||
|     connect(world, player, names, 'Library', 'Eastern lake desolation') |     connect(world, player, 'Library', 'Eastern lake desolation') | ||||||
|     connect(world, player, names, 'Library', 'Library top', lambda state: logic.has_doublejump(state) or state.has('Talaria Attachment', player))  |     connect(world, player, 'Library', 'Library top', lambda state: logic.has_doublejump(state) or state.has('Talaria Attachment', player))  | ||||||
|     connect(world, player, names, 'Library', 'Varndagroth tower left', logic.has_keycard_D) |     connect(world, player, 'Library', 'Varndagroth tower left', logic.has_keycard_D) | ||||||
|     connect(world, player, names, 'Library', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Library', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Library top', 'Library') |     connect(world, player, 'Library top', 'Library') | ||||||
|     connect(world, player, names, 'Varndagroth tower left', 'Library') |     connect(world, player, 'Varndagroth tower left', 'Library') | ||||||
|     connect(world, player, names, 'Varndagroth tower left', 'Varndagroth tower right (upper)', logic.has_keycard_C) |     connect(world, player, 'Varndagroth tower left', 'Varndagroth tower right (upper)', logic.has_keycard_C) | ||||||
|     connect(world, player, names, 'Varndagroth tower left', 'Varndagroth tower right (lower)', logic.has_keycard_B) |     connect(world, player, 'Varndagroth tower left', 'Varndagroth tower right (lower)', logic.has_keycard_B) | ||||||
|     connect(world, player, names, 'Varndagroth tower left', 'Sealed Caves (Sirens)', lambda state: logic.has_keycard_B(state) and state.has('Elevator Keycard', player)) |     connect(world, player, 'Varndagroth tower left', 'Sealed Caves (Sirens)', lambda state: logic.has_keycard_B(state) and state.has('Elevator Keycard', player)) | ||||||
|     connect(world, player, names, 'Varndagroth tower left', 'Refugee Camp', lambda state: state.has('Timespinner Wheel', player) and state.has('Timespinner Spindle', player)) |     connect(world, player, 'Varndagroth tower left', 'Refugee Camp', lambda state: state.has('Timespinner Wheel', player) and state.has('Timespinner Spindle', player)) | ||||||
|     connect(world, player, names, 'Varndagroth tower right (upper)', 'Varndagroth tower left') |     connect(world, player, 'Varndagroth tower right (upper)', 'Varndagroth tower left') | ||||||
|     connect(world, player, names, 'Varndagroth tower right (upper)', 'Varndagroth tower right (elevator)', lambda state: state.has('Elevator Keycard', player)) |     connect(world, player, 'Varndagroth tower right (upper)', 'Varndagroth tower right (elevator)', lambda state: state.has('Elevator Keycard', player)) | ||||||
|     connect(world, player, names, 'Varndagroth tower right (elevator)', 'Varndagroth tower right (upper)') |     connect(world, player, 'Varndagroth tower right (elevator)', 'Varndagroth tower right (upper)') | ||||||
|     connect(world, player, names, 'Varndagroth tower right (elevator)', 'Varndagroth tower right (lower)') |     connect(world, player, 'Varndagroth tower right (elevator)', 'Varndagroth tower right (lower)') | ||||||
|     connect(world, player, names, 'Varndagroth tower right (lower)', 'Varndagroth tower left', logic.has_keycard_B) |     connect(world, player, 'Varndagroth tower right (lower)', 'Varndagroth tower left', logic.has_keycard_B) | ||||||
|     connect(world, player, names, 'Varndagroth tower right (lower)', 'Varndagroth tower right (elevator)', lambda state: state.has('Elevator Keycard', player)) |     connect(world, player, 'Varndagroth tower right (lower)', 'Varndagroth tower right (elevator)', lambda state: state.has('Elevator Keycard', player)) | ||||||
|     connect(world, player, names, 'Varndagroth tower right (lower)', 'Sealed Caves (Sirens)', lambda state: logic.has_keycard_B(state) and state.has('Elevator Keycard', player)) |     connect(world, player, 'Varndagroth tower right (lower)', 'Sealed Caves (Sirens)', lambda state: logic.has_keycard_B(state) and state.has('Elevator Keycard', player)) | ||||||
|     connect(world, player, names, 'Varndagroth tower right (lower)', 'Military Fortress', logic.can_kill_all_3_bosses) |     connect(world, player, 'Varndagroth tower right (lower)', 'Military Fortress', logic.can_kill_all_3_bosses) | ||||||
|     connect(world, player, names, 'Varndagroth tower right (lower)', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Varndagroth tower right (lower)', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Sealed Caves (Sirens)', 'Varndagroth tower left', lambda state: state.has('Elevator Keycard', player)) |     connect(world, player, 'Sealed Caves (Sirens)', 'Varndagroth tower left', lambda state: state.has('Elevator Keycard', player)) | ||||||
|     connect(world, player, names, 'Sealed Caves (Sirens)', 'Varndagroth tower right (lower)', lambda state: state.has('Elevator Keycard', player)) |     connect(world, player, 'Sealed Caves (Sirens)', 'Varndagroth tower right (lower)', lambda state: state.has('Elevator Keycard', player)) | ||||||
|     connect(world, player, names, 'Sealed Caves (Sirens)', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Sealed Caves (Sirens)', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Military Fortress', 'Varndagroth tower right (lower)', logic.can_kill_all_3_bosses) |     connect(world, player, 'Military Fortress', 'Varndagroth tower right (lower)', logic.can_kill_all_3_bosses) | ||||||
|     connect(world, player, names, 'Military Fortress', 'Temporal Gyre', lambda state: state.has('Timespinner Wheel', player)) |     connect(world, player, 'Military Fortress', 'Temporal Gyre', lambda state: state.has('Timespinner Wheel', player)) | ||||||
|     connect(world, player, names, 'Military Fortress', 'Military Fortress (hangar)', logic.has_doublejump) |     connect(world, player, 'Military Fortress', 'Military Fortress (hangar)', logic.has_doublejump) | ||||||
|     connect(world, player, names, 'Military Fortress (hangar)', 'Military Fortress') |     connect(world, player, 'Military Fortress (hangar)', 'Military Fortress') | ||||||
|     connect(world, player, names, 'Military Fortress (hangar)', 'The lab', lambda state: logic.has_keycard_B(state) and logic.has_doublejump(state)) |     connect(world, player, 'Military Fortress (hangar)', 'The lab', lambda state: logic.has_keycard_B(state) and logic.has_doublejump(state)) | ||||||
|     connect(world, player, names, 'Temporal Gyre', 'Military Fortress') |     connect(world, player, 'Temporal Gyre', 'Military Fortress') | ||||||
|     connect(world, player, names, 'The lab', 'Military Fortress') |     connect(world, player, 'The lab', 'Military Fortress') | ||||||
|     connect(world, player, names, 'The lab', 'The lab (power off)', logic.has_doublejump_of_npc) |     connect(world, player, 'The lab', 'The lab (power off)', logic.has_doublejump_of_npc) | ||||||
|     connect(world, player, names, 'The lab (power off)', 'The lab') |     connect(world, player, 'The lab (power off)', 'The lab') | ||||||
|     connect(world, player, names, 'The lab (power off)', 'The lab (upper)', logic.has_forwarddash_doublejump) |     connect(world, player, 'The lab (power off)', 'The lab (upper)', logic.has_forwarddash_doublejump) | ||||||
|     connect(world, player, names, 'The lab (upper)', 'The lab (power off)') |     connect(world, player, 'The lab (upper)', 'The lab (power off)') | ||||||
|     connect(world, player, names, 'The lab (upper)', 'Emperors tower', logic.has_forwarddash_doublejump) |     connect(world, player, 'The lab (upper)', 'Emperors tower', logic.has_forwarddash_doublejump) | ||||||
|     connect(world, player, names, 'The lab (upper)', 'Ancient Pyramid (entrance)', lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player)) |     connect(world, player, 'The lab (upper)', 'Ancient Pyramid (entrance)', lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player)) | ||||||
|     connect(world, player, names, 'Emperors tower', 'The lab (upper)') |     connect(world, player, 'Emperors tower', 'The lab (upper)') | ||||||
|     connect(world, player, names, 'Skeleton Shaft', 'Lake desolation') |     connect(world, player, 'Skeleton Shaft', 'Lake desolation') | ||||||
|     connect(world, player, names, 'Skeleton Shaft', 'Sealed Caves (upper)', logic.has_keycard_A) |     connect(world, player, 'Skeleton Shaft', 'Sealed Caves (upper)', logic.has_keycard_A) | ||||||
|     connect(world, player, names, 'Skeleton Shaft', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Skeleton Shaft', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Sealed Caves (upper)', 'Skeleton Shaft') |     connect(world, player, 'Sealed Caves (upper)', 'Skeleton Shaft') | ||||||
|     connect(world, player, names, 'Sealed Caves (upper)', 'Sealed Caves (Xarion)', lambda state: logic.has_teleport(state) or logic.has_doublejump(state)) |     connect(world, player, 'Sealed Caves (upper)', 'Sealed Caves (Xarion)', lambda state: logic.has_teleport(state) or logic.has_doublejump(state)) | ||||||
|     connect(world, player, names, 'Sealed Caves (Xarion)', 'Sealed Caves (upper)', logic.has_doublejump) |     connect(world, player, 'Sealed Caves (Xarion)', 'Sealed Caves (upper)', logic.has_doublejump) | ||||||
|     connect(world, player, names, 'Sealed Caves (Xarion)', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Sealed Caves (Xarion)', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Refugee Camp', 'Forest') |     connect(world, player, 'Refugee Camp', 'Forest') | ||||||
|     #connect(world, player, names, 'Refugee Camp', 'Library', lambda state: not is_option_enabled(world, player, "Inverted")) |     #connect(world, player, 'Refugee Camp', 'Library', lambda state: not is_option_enabled(world, player, "Inverted")) | ||||||
|     connect(world, player, names, 'Refugee Camp', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Refugee Camp', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Forest', 'Refugee Camp') |     connect(world, player, 'Forest', 'Refugee Camp') | ||||||
|     connect(world, player, names, 'Forest', 'Left Side forest Caves', lambda state: state.has('Talaria Attachment', player) or logic.has_timestop(state)) |     connect(world, player, 'Forest', 'Left Side forest Caves', lambda state: state.has('Talaria Attachment', player) or logic.has_timestop(state)) | ||||||
|     connect(world, player, names, 'Forest', 'Caves of Banishment (Sirens)') |     connect(world, player, 'Forest', 'Caves of Banishment (Sirens)') | ||||||
|     connect(world, player, names, 'Forest', 'Castle Ramparts') |     connect(world, player, 'Forest', 'Castle Ramparts') | ||||||
|     connect(world, player, names, 'Left Side forest Caves', 'Forest') |     connect(world, player, 'Left Side forest Caves', 'Forest') | ||||||
|     connect(world, player, names, 'Left Side forest Caves', 'Upper Lake Serene', logic.has_timestop) |     connect(world, player, 'Left Side forest Caves', 'Upper Lake Serene', logic.has_timestop) | ||||||
|     connect(world, player, names, 'Left Side forest Caves', 'Lower Lake Serene', lambda state: state.has('Water Mask', player) or flooded.dry_lake_serene) |     connect(world, player, 'Left Side forest Caves', 'Lower Lake Serene', lambda state: state.has('Water Mask', player) or flooded.dry_lake_serene) | ||||||
|     connect(world, player, names, 'Left Side forest Caves', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Left Side forest Caves', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Upper Lake Serene', 'Left Side forest Caves') |     connect(world, player, 'Upper Lake Serene', 'Left Side forest Caves') | ||||||
|     connect(world, player, names, 'Upper Lake Serene', 'Lower Lake Serene', lambda state: state.has('Water Mask', player)) |     connect(world, player, 'Upper Lake Serene', 'Lower Lake Serene', lambda state: state.has('Water Mask', player)) | ||||||
|     connect(world, player, names, 'Lower Lake Serene', 'Upper Lake Serene') |     connect(world, player, 'Lower Lake Serene', 'Upper Lake Serene') | ||||||
|     connect(world, player, names, 'Lower Lake Serene', 'Left Side forest Caves') |     connect(world, player, 'Lower Lake Serene', 'Left Side forest Caves') | ||||||
|     connect(world, player, names, 'Lower Lake Serene', 'Caves of Banishment (upper)') |     connect(world, player, 'Lower Lake Serene', 'Caves of Banishment (upper)') | ||||||
|     connect(world, player, names, 'Caves of Banishment (upper)', 'Upper Lake Serene', lambda state: state.has('Water Mask', player) or flooded.dry_lake_serene) |     connect(world, player, 'Caves of Banishment (upper)', 'Upper Lake Serene', lambda state: state.has('Water Mask', player) or flooded.dry_lake_serene) | ||||||
|     connect(world, player, names, 'Caves of Banishment (upper)', 'Caves of Banishment (Maw)', lambda state: logic.has_doublejump(state) or state.has_any({'Gas Mask', 'Twin Pyramid Key'}, player)) |     connect(world, player, 'Caves of Banishment (upper)', 'Caves of Banishment (Maw)', lambda state: logic.has_doublejump(state) or state.has_any({'Gas Mask', 'Twin Pyramid Key'}, player)) | ||||||
|     connect(world, player, names, 'Caves of Banishment (upper)', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Caves of Banishment (upper)', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Caves of Banishment (Maw)', 'Caves of Banishment (upper)', lambda state: logic.has_doublejump(state) if not flooded.flood_maw else state.has('Water Mask', player)) |     connect(world, player, 'Caves of Banishment (Maw)', 'Caves of Banishment (upper)', lambda state: logic.has_doublejump(state) if not flooded.flood_maw else state.has('Water Mask', player)) | ||||||
|     connect(world, player, names, 'Caves of Banishment (Maw)', 'Caves of Banishment (Sirens)', lambda state: state.has('Gas Mask', player)) |     connect(world, player, 'Caves of Banishment (Maw)', 'Caves of Banishment (Sirens)', lambda state: state.has_any({'Gas Mask', 'Talaria Attachment'}, player) ) | ||||||
|     connect(world, player, names, 'Caves of Banishment (Maw)', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Caves of Banishment (Maw)', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Caves of Banishment (Sirens)', 'Forest') |     connect(world, player, 'Caves of Banishment (Sirens)', 'Forest') | ||||||
|     connect(world, player, names, 'Castle Ramparts', 'Forest') |     connect(world, player, 'Castle Ramparts', 'Forest') | ||||||
|     connect(world, player, names, 'Castle Ramparts', 'Castle Keep') |     connect(world, player, 'Castle Ramparts', 'Castle Keep') | ||||||
|     connect(world, player, names, 'Castle Ramparts', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Castle Ramparts', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Castle Keep', 'Castle Ramparts') |     connect(world, player, 'Castle Keep', 'Castle Ramparts') | ||||||
|     connect(world, player, names, 'Castle Keep', 'Castle Basement', lambda state: state.has('Water Mask', player) or not flooded.flood_basement) |     connect(world, player, 'Castle Keep', 'Castle Basement', lambda state: state.has('Water Mask', player) or not flooded.flood_basement) | ||||||
|     connect(world, player, names, 'Castle Keep', 'Royal towers (lower)', logic.has_doublejump) |     connect(world, player, 'Castle Keep', 'Royal towers (lower)', logic.has_doublejump) | ||||||
|     connect(world, player, names, 'Castle Keep', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Castle Keep', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Royal towers (lower)', 'Castle Keep') |     connect(world, player, 'Royal towers (lower)', 'Castle Keep') | ||||||
|     connect(world, player, names, 'Royal towers (lower)', 'Royal towers', lambda state: state.has('Timespinner Wheel', player) or logic.has_forwarddash_doublejump(state)) |     connect(world, player, 'Royal towers (lower)', 'Royal towers', lambda state: state.has('Timespinner Wheel', player) or logic.has_forwarddash_doublejump(state)) | ||||||
|     connect(world, player, names, 'Royal towers (lower)', 'Space time continuum', logic.has_teleport) |     connect(world, player, 'Royal towers (lower)', 'Space time continuum', logic.has_teleport) | ||||||
|     connect(world, player, names, 'Royal towers', 'Royal towers (lower)') |     connect(world, player, 'Royal towers', 'Royal towers (lower)') | ||||||
|     connect(world, player, names, 'Royal towers', 'Royal towers (upper)', logic.has_doublejump) |     connect(world, player, 'Royal towers', 'Royal towers (upper)', logic.has_doublejump) | ||||||
|     connect(world, player, names, 'Royal towers (upper)', 'Royal towers') |     connect(world, player, 'Royal towers (upper)', 'Royal towers') | ||||||
|     #connect(world, player, names, 'Ancient Pyramid (entrance)', 'The lab (upper)', lambda state: not is_option_enabled(world, player, "EnterSandman")) |     #connect(world, player, 'Ancient Pyramid (entrance)', 'The lab (upper)', lambda state: not is_option_enabled(world, player, "EnterSandman")) | ||||||
|     connect(world, player, names, 'Ancient Pyramid (entrance)', 'Ancient Pyramid (left)', logic.has_doublejump) |     connect(world, player, 'Ancient Pyramid (entrance)', 'Ancient Pyramid (left)', logic.has_doublejump) | ||||||
|     connect(world, player, names, 'Ancient Pyramid (left)', 'Ancient Pyramid (entrance)') |     connect(world, player, 'Ancient Pyramid (left)', 'Ancient Pyramid (entrance)') | ||||||
|     connect(world, player, names, 'Ancient Pyramid (left)', 'Ancient Pyramid (right)', lambda state: logic.has_upwarddash(state) or flooded.flood_pyramid_shaft) |     connect(world, player, 'Ancient Pyramid (left)', 'Ancient Pyramid (right)', lambda state: logic.has_upwarddash(state) or flooded.flood_pyramid_shaft) | ||||||
|     connect(world, player, names, 'Ancient Pyramid (right)', 'Ancient Pyramid (left)', lambda state: logic.has_upwarddash(state) or flooded.flood_pyramid_shaft) |     connect(world, player, 'Ancient Pyramid (right)', 'Ancient Pyramid (left)', lambda state: logic.has_upwarddash(state) or flooded.flood_pyramid_shaft) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Lake desolation', lambda state: logic.can_teleport_to(state, "Present", "GateLakeDesolation")) |     connect(world, player, 'Space time continuum', 'Lake desolation', lambda state: logic.can_teleport_to(state, "Present", "GateLakeDesolation")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Lower lake desolation', lambda state: logic.can_teleport_to(state, "Present", "GateKittyBoss")) |     connect(world, player, 'Space time continuum', 'Lower lake desolation', lambda state: logic.can_teleport_to(state, "Present", "GateKittyBoss")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Library', lambda state: logic.can_teleport_to(state, "Present", "GateLeftLibrary")) |     connect(world, player, 'Space time continuum', 'Library', lambda state: logic.can_teleport_to(state, "Present", "GateLeftLibrary")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Varndagroth tower right (lower)', lambda state: logic.can_teleport_to(state, "Present", "GateMilitaryGate")) |     connect(world, player, 'Space time continuum', 'Varndagroth tower right (lower)', lambda state: logic.can_teleport_to(state, "Present", "GateMilitaryGate")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Skeleton Shaft', lambda state: logic.can_teleport_to(state, "Present", "GateSealedCaves")) |     connect(world, player, 'Space time continuum', 'Skeleton Shaft', lambda state: logic.can_teleport_to(state, "Present", "GateSealedCaves")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Sealed Caves (Sirens)', lambda state: logic.can_teleport_to(state, "Present", "GateSealedSirensCave")) |     connect(world, player, 'Space time continuum', 'Sealed Caves (Sirens)', lambda state: logic.can_teleport_to(state, "Present", "GateSealedSirensCave")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Upper Lake Serene', lambda state: logic.can_teleport_to(state, "Past", "GateLakeSereneLeft")) |     connect(world, player, 'Space time continuum', 'Upper Lake Serene', lambda state: logic.can_teleport_to(state, "Past", "GateLakeSereneLeft")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Left Side forest Caves', lambda state: logic.can_teleport_to(state, "Past", "GateLakeSereneRight")) |     connect(world, player, 'Space time continuum', 'Left Side forest Caves', lambda state: logic.can_teleport_to(state, "Past", "GateLakeSereneRight")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Refugee Camp', lambda state: logic.can_teleport_to(state, "Past", "GateAccessToPast")) |     connect(world, player, 'Space time continuum', 'Refugee Camp', lambda state: logic.can_teleport_to(state, "Past", "GateAccessToPast")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Castle Ramparts', lambda state: logic.can_teleport_to(state, "Past", "GateCastleRamparts")) |     connect(world, player, 'Space time continuum', 'Castle Ramparts', lambda state: logic.can_teleport_to(state, "Past", "GateCastleRamparts")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Castle Keep', lambda state: logic.can_teleport_to(state, "Past", "GateCastleKeep")) |     connect(world, player, 'Space time continuum', 'Castle Keep', lambda state: logic.can_teleport_to(state, "Past", "GateCastleKeep")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Royal towers (lower)', lambda state: logic.can_teleport_to(state, "Past", "GateRoyalTowers")) |     connect(world, player, 'Space time continuum', 'Royal towers (lower)', lambda state: logic.can_teleport_to(state, "Past", "GateRoyalTowers")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Caves of Banishment (Maw)', lambda state: logic.can_teleport_to(state, "Past", "GateMaw")) |     connect(world, player, 'Space time continuum', 'Caves of Banishment (Maw)', lambda state: logic.can_teleport_to(state, "Past", "GateMaw")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Caves of Banishment (upper)', lambda state: logic.can_teleport_to(state, "Past", "GateCavesOfBanishment")) |     connect(world, player, 'Space time continuum', 'Caves of Banishment (upper)', lambda state: logic.can_teleport_to(state, "Past", "GateCavesOfBanishment")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Ancient Pyramid (entrance)', lambda state: logic.can_teleport_to(state, "Time", "GateGyre") or (not is_option_enabled(world, player, "UnchainedKeys") and is_option_enabled(world, player, "EnterSandman"))) |     connect(world, player, 'Space time continuum', 'Ancient Pyramid (entrance)', lambda state: logic.can_teleport_to(state, "Time", "GateGyre") or (not is_option_enabled(world, player, "UnchainedKeys") and is_option_enabled(world, player, "EnterSandman"))) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Ancient Pyramid (left)', lambda state: logic.can_teleport_to(state, "Time", "GateLeftPyramid")) |     connect(world, player, 'Space time continuum', 'Ancient Pyramid (left)', lambda state: logic.can_teleport_to(state, "Time", "GateLeftPyramid")) | ||||||
|     connect(world, player, names, 'Space time continuum', 'Ancient Pyramid (right)', lambda state: logic.can_teleport_to(state, "Time", "GateRightPyramid")) |     connect(world, player, 'Space time continuum', 'Ancient Pyramid (right)', lambda state: logic.can_teleport_to(state, "Time", "GateRightPyramid")) | ||||||
| 
 | 
 | ||||||
|     if is_option_enabled(world, player, "GyreArchives"): |     if is_option_enabled(world, player, "GyreArchives"): | ||||||
|         connect(world, player, names, 'The lab (upper)', 'Ravenlord\'s Lair', lambda state: state.has('Merchant Crow', player)) |         connect(world, player, 'The lab (upper)', 'Ravenlord\'s Lair', lambda state: state.has('Merchant Crow', player)) | ||||||
|         connect(world, player, names, 'Ravenlord\'s Lair', 'The lab (upper)') |         connect(world, player, 'Ravenlord\'s Lair', 'The lab (upper)') | ||||||
|         connect(world, player, names, 'Library top', 'Ifrit\'s Lair', lambda state: state.has('Kobo', player) and state.can_reach('Refugee Camp', 'Region', player)) |         connect(world, player, 'Library top', 'Ifrit\'s Lair', lambda state: state.has('Kobo', player) and state.can_reach('Refugee Camp', 'Region', player)) | ||||||
|         connect(world, player, names, 'Ifrit\'s Lair', 'Library top') |         connect(world, player, 'Ifrit\'s Lair', 'Library top') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def throwIfAnyLocationIsNotAssignedToARegion(regions: List[Region], regionNames: Set[str]): | def throwIfAnyLocationIsNotAssignedToARegion(regions: List[Region], regionNames: Set[str]): | ||||||
|  | @ -203,7 +202,7 @@ def throwIfAnyLocationIsNotAssignedToARegion(regions: List[Region], regionNames: | ||||||
|         raise Exception("Timespinner: the following regions are used in locations: {}, but no such region exists".format(regionNames - existingRegions)) |         raise Exception("Timespinner: the following regions are used in locations: {}, but no such region exists".format(regionNames - existingRegions)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def create_location(player: int, location_data: LocationData, region: Region, location_cache: List[Location]) -> Location: | def create_location(player: int, location_data: LocationData, region: Region) -> Location: | ||||||
|     location = Location(player, location_data.name, location_data.code, region) |     location = Location(player, location_data.name, location_data.code, region) | ||||||
|     location.access_rule = location_data.rule |     location.access_rule = location_data.rule | ||||||
| 
 | 
 | ||||||
|  | @ -211,17 +210,15 @@ def create_location(player: int, location_data: LocationData, region: Region, lo | ||||||
|         location.event = True |         location.event = True | ||||||
|         location.locked = True |         location.locked = True | ||||||
| 
 | 
 | ||||||
|     location_cache.append(location) |  | ||||||
| 
 |  | ||||||
|     return location |     return location | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], location_cache: List[Location], name: str) -> Region: | def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], name: str) -> Region: | ||||||
|     region = Region(name, player, world) |     region = Region(name, player, world) | ||||||
| 
 | 
 | ||||||
|     if name in locations_per_region: |     if name in locations_per_region: | ||||||
|         for location_data in locations_per_region[name]: |         for location_data in locations_per_region[name]: | ||||||
|             location = create_location(player, location_data, region, location_cache) |             location = create_location(player, location_data, region) | ||||||
|             region.locations.append(location) |             region.locations.append(location) | ||||||
| 
 | 
 | ||||||
|     return region |     return region | ||||||
|  | @ -250,19 +247,13 @@ def connectStartingRegion(world: MultiWorld, player: int): | ||||||
|     space_time_continuum.exits.append(teleport_back_to_start) |     space_time_continuum.exits.append(teleport_back_to_start) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def connect(world: MultiWorld, player: int, used_names: Dict[str, int], source: str, target: str,  | def connect(world: MultiWorld, player: int, source: str, target: str,  | ||||||
|             rule: Optional[Callable[[CollectionState], bool]] = None): |             rule: Optional[Callable[[CollectionState], bool]] = None): | ||||||
|  |      | ||||||
|     sourceRegion = world.get_region(source, player) |     sourceRegion = world.get_region(source, player) | ||||||
|     targetRegion = world.get_region(target, player) |     targetRegion = world.get_region(target, player) | ||||||
| 
 | 
 | ||||||
|     if target not in used_names: |     connection = Entrance(player, "", sourceRegion) | ||||||
|         used_names[target] = 1 |  | ||||||
|         name = target |  | ||||||
|     else: |  | ||||||
|         used_names[target] += 1 |  | ||||||
|         name = target + (' ' * used_names[target]) |  | ||||||
| 
 |  | ||||||
|     connection = Entrance(player, name, sourceRegion) |  | ||||||
| 
 | 
 | ||||||
|     if rule: |     if rule: | ||||||
|         connection.access_rule = rule |         connection.access_rule = rule | ||||||
|  | @ -271,7 +262,7 @@ def connect(world: MultiWorld, player: int, used_names: Dict[str, int], source: | ||||||
|     connection.connect(targetRegion) |     connection.connect(targetRegion) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_locations_per_region(locations: Tuple[LocationData, ...]) -> Dict[str, List[LocationData]]: | def split_location_datas_per_region(locations: Tuple[LocationData, ...]) -> Dict[str, List[LocationData]]: | ||||||
|     per_region: Dict[str, List[LocationData]]  = {} |     per_region: Dict[str, List[LocationData]]  = {} | ||||||
| 
 | 
 | ||||||
|     for location in locations: |     for location in locations: | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| from typing import Dict, List, Set, Tuple, TextIO | from typing import Dict, List, Set, Tuple, TextIO, Union | ||||||
| from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification | from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification | ||||||
| from .Items import get_item_names_per_category, item_table, starter_melee_weapons, starter_spells, filler_items | from .Items import get_item_names_per_category, item_table, starter_melee_weapons, starter_spells, filler_items | ||||||
| from .Locations import get_locations, EventId | from .Locations import get_location_datas, EventId | ||||||
| from .Options import is_option_enabled, get_option_value, timespinner_options | from .Options import is_option_enabled, get_option_value, timespinner_options | ||||||
| from .PreCalculatedWeights import PreCalculatedWeights | from .PreCalculatedWeights import PreCalculatedWeights | ||||||
| from .Regions import create_regions | from .Regions import create_regions_and_locations | ||||||
| from worlds.AutoWorld import World, WebWorld | from worlds.AutoWorld import World, WebWorld | ||||||
| 
 | 
 | ||||||
| class TimespinnerWebWorld(WebWorld): | class TimespinnerWebWorld(WebWorld): | ||||||
|  | @ -29,7 +29,6 @@ class TimespinnerWebWorld(WebWorld): | ||||||
| 
 | 
 | ||||||
|     tutorials = [setup, setup_de] |     tutorials = [setup, setup_de] | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| class TimespinnerWorld(World): | class TimespinnerWorld(World): | ||||||
|     """ |     """ | ||||||
|     Timespinner is a beautiful metroidvania inspired by classic 90s action-platformers. |     Timespinner is a beautiful metroidvania inspired by classic 90s action-platformers. | ||||||
|  | @ -44,21 +43,16 @@ class TimespinnerWorld(World): | ||||||
|     required_client_version = (0, 3, 7) |     required_client_version = (0, 3, 7) | ||||||
| 
 | 
 | ||||||
|     item_name_to_id = {name: data.code for name, data in item_table.items()} |     item_name_to_id = {name: data.code for name, data in item_table.items()} | ||||||
|     location_name_to_id = {location.name: location.code for location in get_locations(None, None, None)} |     location_name_to_id = {location.name: location.code for location in get_location_datas(None, None, None)} | ||||||
|     item_name_groups = get_item_names_per_category() |     item_name_groups = get_item_names_per_category() | ||||||
| 
 | 
 | ||||||
|     locked_locations: List[str] |  | ||||||
|     location_cache: List[Location] |  | ||||||
|     precalculated_weights: PreCalculatedWeights |     precalculated_weights: PreCalculatedWeights | ||||||
| 
 | 
 | ||||||
|     def __init__(self, world: MultiWorld, player: int): |     def __init__(self, world: MultiWorld, player: int): | ||||||
|         super().__init__(world, player) |         super().__init__(world, player) | ||||||
| 
 |  | ||||||
|         self.locked_locations = [] |  | ||||||
|         self.location_cache = [] |  | ||||||
|         self.precalculated_weights = PreCalculatedWeights(world, player) |         self.precalculated_weights = PreCalculatedWeights(world, player) | ||||||
| 
 | 
 | ||||||
|     def generate_early(self): |     def generate_early(self) -> None: | ||||||
|         # in generate_early the start_inventory isnt copied over to precollected_items yet, so we can still modify the options directly |         # in generate_early the start_inventory isnt copied over to precollected_items yet, so we can still modify the options directly | ||||||
|         if self.multiworld.start_inventory[self.player].value.pop('Meyef', 0) > 0: |         if self.multiworld.start_inventory[self.player].value.pop('Meyef', 0) > 0: | ||||||
|             self.multiworld.StartWithMeyef[self.player].value = self.multiworld.StartWithMeyef[self.player].option_true |             self.multiworld.StartWithMeyef[self.player].value = self.multiworld.StartWithMeyef[self.player].option_true | ||||||
|  | @ -67,44 +61,27 @@ class TimespinnerWorld(World): | ||||||
|         if self.multiworld.start_inventory[self.player].value.pop('Jewelry Box', 0) > 0: |         if self.multiworld.start_inventory[self.player].value.pop('Jewelry Box', 0) > 0: | ||||||
|             self.multiworld.StartWithJewelryBox[self.player].value = self.multiworld.StartWithJewelryBox[self.player].option_true |             self.multiworld.StartWithJewelryBox[self.player].value = self.multiworld.StartWithJewelryBox[self.player].option_true | ||||||
| 
 | 
 | ||||||
|     def create_regions(self): |     def create_regions(self) -> None:  | ||||||
|         locations = get_locations(self.multiworld, self.player, self.precalculated_weights) |         create_regions_and_locations(self.multiworld, self.player, self.precalculated_weights) | ||||||
|         create_regions(self.multiworld, self.player, locations, self.location_cache, self.precalculated_weights) |  | ||||||
| 
 | 
 | ||||||
|     def create_item(self, name: str) -> Item: |     def create_items(self) -> None:  | ||||||
|         return create_item_with_correct_settings(self.multiworld, self.player, name) |         self.create_and_assign_event_items() | ||||||
| 
 | 
 | ||||||
|     def get_filler_item_name(self) -> str: |         excluded_items: Set[str] = self.get_excluded_items() | ||||||
|         trap_chance: int = get_option_value(self.multiworld, self.player, "TrapChance") |  | ||||||
|         enabled_traps: List[str] = get_option_value(self.multiworld, self.player, "Traps") |  | ||||||
| 
 | 
 | ||||||
|         if self.multiworld.random.random() < (trap_chance / 100) and enabled_traps: |         self.assign_starter_items(excluded_items) | ||||||
|             return self.multiworld.random.choice(enabled_traps) |  | ||||||
|         else: |  | ||||||
|             return self.multiworld.random.choice(filler_items)  |  | ||||||
| 
 | 
 | ||||||
|     def set_rules(self): |         self.multiworld.itempool += self.get_item_pool(excluded_items) | ||||||
|         setup_events(self.player, self.locked_locations, self.location_cache) |  | ||||||
| 
 | 
 | ||||||
|  |     def set_rules(self) -> None: | ||||||
|         final_boss: str |         final_boss: str | ||||||
|         if is_option_enabled(self.multiworld, self.player, "DadPercent"): |         if self.is_option_enabled("DadPercent"): | ||||||
|             final_boss = "Killed Emperor" |             final_boss = "Killed Emperor" | ||||||
|         else: |         else: | ||||||
|             final_boss = "Killed Nightmare" |             final_boss = "Killed Nightmare" | ||||||
| 
 | 
 | ||||||
|         self.multiworld.completion_condition[self.player] = lambda state: state.has(final_boss, self.player)  |         self.multiworld.completion_condition[self.player] = lambda state: state.has(final_boss, self.player)  | ||||||
| 
 | 
 | ||||||
|     def generate_basic(self): |  | ||||||
|         excluded_items: Set[str] = get_excluded_items(self, self.multiworld, self.player) |  | ||||||
| 
 |  | ||||||
|         assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations) |  | ||||||
| 
 |  | ||||||
|         pool = get_item_pool(self.multiworld, self.player, excluded_items) |  | ||||||
| 
 |  | ||||||
|         fill_item_pool_with_dummy_items(self, self.multiworld, self.player, self.locked_locations, self.location_cache, pool) |  | ||||||
| 
 |  | ||||||
|         self.multiworld.itempool += pool |  | ||||||
| 
 |  | ||||||
|     def fill_slot_data(self) -> Dict[str, object]: |     def fill_slot_data(self) -> Dict[str, object]: | ||||||
|         slot_data: Dict[str, object] = {} |         slot_data: Dict[str, object] = {} | ||||||
| 
 | 
 | ||||||
|  | @ -112,12 +89,12 @@ class TimespinnerWorld(World): | ||||||
| 
 | 
 | ||||||
|         for option_name in timespinner_options: |         for option_name in timespinner_options: | ||||||
|             if (option_name not in ap_specific_settings): |             if (option_name not in ap_specific_settings): | ||||||
|                 slot_data[option_name] = get_option_value(self.multiworld, self.player, option_name) |                 slot_data[option_name] = self.get_option_value(option_name) | ||||||
| 
 | 
 | ||||||
|         slot_data["StinkyMaw"] = True |         slot_data["StinkyMaw"] = True | ||||||
|         slot_data["ProgressiveVerticalMovement"] = False |         slot_data["ProgressiveVerticalMovement"] = False | ||||||
|         slot_data["ProgressiveKeycards"] = False |         slot_data["ProgressiveKeycards"] = False | ||||||
|         slot_data["PersonalItems"] = get_personal_items(self.player, self.location_cache) |         slot_data["PersonalItems"] = self.get_personal_items() | ||||||
|         slot_data["PyramidKeysGate"] = self.precalculated_weights.pyramid_keys_unlock |         slot_data["PyramidKeysGate"] = self.precalculated_weights.pyramid_keys_unlock | ||||||
|         slot_data["PresentGate"] = self.precalculated_weights.present_key_unlock |         slot_data["PresentGate"] = self.precalculated_weights.present_key_unlock | ||||||
|         slot_data["PastGate"] = self.precalculated_weights.past_key_unlock |         slot_data["PastGate"] = self.precalculated_weights.past_key_unlock | ||||||
|  | @ -135,17 +112,17 @@ class TimespinnerWorld(World): | ||||||
| 
 | 
 | ||||||
|         return slot_data |         return slot_data | ||||||
| 
 | 
 | ||||||
|     def write_spoiler_header(self, spoiler_handle: TextIO): |     def write_spoiler_header(self, spoiler_handle: TextIO) -> None: | ||||||
|         if is_option_enabled(self.multiworld, self.player, "UnchainedKeys"): |         if self.is_option_enabled("UnchainedKeys"): | ||||||
|             spoiler_handle.write(f'Modern Warp Beacon unlock:       {self.precalculated_weights.present_key_unlock}\n') |             spoiler_handle.write(f'Modern Warp Beacon unlock:       {self.precalculated_weights.present_key_unlock}\n') | ||||||
|             spoiler_handle.write(f'Timeworn Warp Beacon unlock:     {self.precalculated_weights.past_key_unlock}\n') |             spoiler_handle.write(f'Timeworn Warp Beacon unlock:     {self.precalculated_weights.past_key_unlock}\n') | ||||||
| 
 | 
 | ||||||
|             if is_option_enabled(self.multiworld, self.player, "EnterSandman"): |             if self.is_option_enabled("EnterSandman"): | ||||||
|                 spoiler_handle.write(f'Mysterious Warp Beacon unlock:   {self.precalculated_weights.time_key_unlock}\n') |                 spoiler_handle.write(f'Mysterious Warp Beacon unlock:   {self.precalculated_weights.time_key_unlock}\n') | ||||||
|         else: |         else: | ||||||
|             spoiler_handle.write(f'Twin Pyramid Keys unlock:        {self.precalculated_weights.pyramid_keys_unlock}\n') |             spoiler_handle.write(f'Twin Pyramid Keys unlock:        {self.precalculated_weights.pyramid_keys_unlock}\n') | ||||||
|         |         | ||||||
|         if is_option_enabled(self.multiworld, self.player, "RisingTides"): |         if self.is_option_enabled("RisingTides"): | ||||||
|             flooded_areas: List[str] = [] |             flooded_areas: List[str] = [] | ||||||
| 
 | 
 | ||||||
|             if self.precalculated_weights.flood_basement: |             if self.precalculated_weights.flood_basement: | ||||||
|  | @ -177,36 +154,72 @@ class TimespinnerWorld(World): | ||||||
| 
 | 
 | ||||||
|             spoiler_handle.write(f'Flooded Areas:                   {flooded_areas_string}\n') |             spoiler_handle.write(f'Flooded Areas:                   {flooded_areas_string}\n') | ||||||
| 
 | 
 | ||||||
|  |     def create_item(self, name: str) -> Item: | ||||||
|  |         data = item_table[name] | ||||||
| 
 | 
 | ||||||
| def get_excluded_items(self: TimespinnerWorld, world: MultiWorld, player: int) -> Set[str]: |         if data.useful: | ||||||
|  |             classification = ItemClassification.useful | ||||||
|  |         elif data.progression: | ||||||
|  |             classification = ItemClassification.progression | ||||||
|  |         elif data.trap: | ||||||
|  |             classification = ItemClassification.trap | ||||||
|  |         else: | ||||||
|  |             classification = ItemClassification.filler | ||||||
|  |              | ||||||
|  |         item = Item(name, classification, data.code, self.player) | ||||||
|  | 
 | ||||||
|  |         if not item.advancement: | ||||||
|  |             return item | ||||||
|  | 
 | ||||||
|  |         if (name == 'Tablet' or name == 'Library Keycard V') and not self.is_option_enabled("DownloadableItems"): | ||||||
|  |             item.classification = ItemClassification.filler | ||||||
|  |         elif name == 'Oculus Ring' and not self.is_option_enabled("EyeSpy"): | ||||||
|  |             item.classification = ItemClassification.filler | ||||||
|  |         elif (name == 'Kobo' or name == 'Merchant Crow') and not self.is_option_enabled("GyreArchives"): | ||||||
|  |             item.classification = ItemClassification.filler | ||||||
|  |         elif name in {"Timeworn Warp Beacon", "Modern Warp Beacon", "Mysterious Warp Beacon"} \ | ||||||
|  |                 and not self.is_option_enabled("UnchainedKeys"): | ||||||
|  |             item.classification = ItemClassification.filler | ||||||
|  | 
 | ||||||
|  |         return item | ||||||
|  | 
 | ||||||
|  |     def get_filler_item_name(self) -> str: | ||||||
|  |         trap_chance: int = self.get_option_value("TrapChance") | ||||||
|  |         enabled_traps: List[str] = self.get_option_value("Traps") | ||||||
|  | 
 | ||||||
|  |         if self.multiworld.random.random() < (trap_chance / 100) and enabled_traps: | ||||||
|  |             return self.multiworld.random.choice(enabled_traps) | ||||||
|  |         else: | ||||||
|  |             return self.multiworld.random.choice(filler_items)  | ||||||
|  | 
 | ||||||
|  |     def get_excluded_items(self) -> Set[str]: | ||||||
|         excluded_items: Set[str] = set() |         excluded_items: Set[str] = set() | ||||||
| 
 | 
 | ||||||
|     if is_option_enabled(world, player, "StartWithJewelryBox"): |         if self.is_option_enabled("StartWithJewelryBox"): | ||||||
|             excluded_items.add('Jewelry Box') |             excluded_items.add('Jewelry Box') | ||||||
|     if is_option_enabled(world, player, "StartWithMeyef"): |         if self.is_option_enabled("StartWithMeyef"): | ||||||
|             excluded_items.add('Meyef') |             excluded_items.add('Meyef') | ||||||
|     if is_option_enabled(world, player, "QuickSeed"): |         if self.is_option_enabled("QuickSeed"): | ||||||
|             excluded_items.add('Talaria Attachment') |             excluded_items.add('Talaria Attachment') | ||||||
| 
 | 
 | ||||||
|     if is_option_enabled(world, player, "UnchainedKeys"): |         if self.is_option_enabled("UnchainedKeys"): | ||||||
|             excluded_items.add('Twin Pyramid Key') |             excluded_items.add('Twin Pyramid Key') | ||||||
| 
 | 
 | ||||||
|         if not is_option_enabled(world, player, "EnterSandman"): |             if not self.is_option_enabled("EnterSandman"): | ||||||
|                 excluded_items.add('Mysterious Warp Beacon') |                 excluded_items.add('Mysterious Warp Beacon') | ||||||
|         else: |         else: | ||||||
|             excluded_items.add('Timeworn Warp Beacon') |             excluded_items.add('Timeworn Warp Beacon') | ||||||
|             excluded_items.add('Modern Warp Beacon') |             excluded_items.add('Modern Warp Beacon') | ||||||
|             excluded_items.add('Mysterious Warp Beacon') |             excluded_items.add('Mysterious Warp Beacon') | ||||||
| 
 | 
 | ||||||
|     for item in world.precollected_items[player]: |         for item in self.multiworld.precollected_items[self.player]: | ||||||
|             if item.name not in self.item_name_groups['UseItem']: |             if item.name not in self.item_name_groups['UseItem']: | ||||||
|                 excluded_items.add(item.name) |                 excluded_items.add(item.name) | ||||||
| 
 | 
 | ||||||
|         return excluded_items |         return excluded_items | ||||||
| 
 | 
 | ||||||
| 
 |     def assign_starter_items(self, excluded_items: Set[str]) -> None: | ||||||
| def assign_starter_items(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str]): |         non_local_items: Set[str] = self.multiworld.non_local_items[self.player].value | ||||||
|     non_local_items = world.non_local_items[player].value |  | ||||||
| 
 | 
 | ||||||
|         local_starter_melee_weapons = tuple(item for item in starter_melee_weapons if item not in non_local_items) |         local_starter_melee_weapons = tuple(item for item in starter_melee_weapons if item not in non_local_items) | ||||||
|         if not local_starter_melee_weapons: |         if not local_starter_melee_weapons: | ||||||
|  | @ -222,88 +235,50 @@ def assign_starter_items(world: MultiWorld, player: int, excluded_items: Set[str | ||||||
|             else: |             else: | ||||||
|                 local_starter_spells = ('Lightwall',) |                 local_starter_spells = ('Lightwall',) | ||||||
| 
 | 
 | ||||||
|     assign_starter_item(world, player, excluded_items, locked_locations, 'Tutorial: Yo Momma 1', local_starter_melee_weapons) |         self.assign_starter_item(excluded_items, 'Tutorial: Yo Momma 1', local_starter_melee_weapons) | ||||||
|     assign_starter_item(world, player, excluded_items, locked_locations, 'Tutorial: Yo Momma 2', local_starter_spells) |         self.assign_starter_item(excluded_items, 'Tutorial: Yo Momma 2', local_starter_spells) | ||||||
| 
 | 
 | ||||||
| 
 |     def assign_starter_item(self, excluded_items: Set[str], location: str, item_list: Tuple[str, ...]) -> None: | ||||||
| def assign_starter_item(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str], |         item_name = self.multiworld.random.choice(item_list) | ||||||
|         location: str, item_list: Tuple[str, ...]): |  | ||||||
| 
 |  | ||||||
|     item_name = world.random.choice(item_list) |  | ||||||
| 
 | 
 | ||||||
|         excluded_items.add(item_name) |         excluded_items.add(item_name) | ||||||
| 
 | 
 | ||||||
|     item = create_item_with_correct_settings(world, player, item_name) |         item = self.create_item(item_name) | ||||||
| 
 | 
 | ||||||
|     world.get_location(location, player).place_locked_item(item) |         self.multiworld.get_location(location, self.player).place_locked_item(item) | ||||||
| 
 | 
 | ||||||
|     locked_locations.append(location) |     def get_item_pool(self, excluded_items: Set[str]) -> List[Item]: | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def get_item_pool(world: MultiWorld, player: int, excluded_items: Set[str]) -> List[Item]: |  | ||||||
|         pool: List[Item] = [] |         pool: List[Item] = [] | ||||||
| 
 | 
 | ||||||
|         for name, data in item_table.items(): |         for name, data in item_table.items(): | ||||||
|             if name not in excluded_items: |             if name not in excluded_items: | ||||||
|                 for _ in range(data.count): |                 for _ in range(data.count): | ||||||
|                 item = create_item_with_correct_settings(world, player, name) |                     item = self.create_item(name) | ||||||
|  |                     pool.append(item) | ||||||
|  | 
 | ||||||
|  |         for _ in range(len(self.multiworld.get_unfilled_locations(self.player)) - len(pool)): | ||||||
|  |             item = self.create_item(self.get_filler_item_name()) | ||||||
|             pool.append(item) |             pool.append(item) | ||||||
| 
 | 
 | ||||||
|         return pool |         return pool | ||||||
| 
 | 
 | ||||||
| 
 |     def create_and_assign_event_items(self) -> None: | ||||||
| def fill_item_pool_with_dummy_items(self: TimespinnerWorld, world: MultiWorld, player: int, locked_locations: List[str], |         for location in self.multiworld.get_locations(self.player): | ||||||
|                                     location_cache: List[Location], pool: List[Item]): |  | ||||||
|     for _ in range(len(location_cache) - len(locked_locations) - len(pool)): |  | ||||||
|         item = create_item_with_correct_settings(world, player, self.get_filler_item_name()) |  | ||||||
|         pool.append(item) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def create_item_with_correct_settings(world: MultiWorld, player: int, name: str) -> Item: |  | ||||||
|     data = item_table[name] |  | ||||||
| 
 |  | ||||||
|     if data.useful: |  | ||||||
|         classification = ItemClassification.useful |  | ||||||
|     elif data.progression: |  | ||||||
|         classification = ItemClassification.progression |  | ||||||
|     elif data.trap: |  | ||||||
|         classification = ItemClassification.trap |  | ||||||
|     else: |  | ||||||
|         classification = ItemClassification.filler |  | ||||||
|          |  | ||||||
|     item = Item(name, classification, data.code, player) |  | ||||||
| 
 |  | ||||||
|     if not item.advancement: |  | ||||||
|         return item |  | ||||||
| 
 |  | ||||||
|     if (name == 'Tablet' or name == 'Library Keycard V') and not is_option_enabled(world, player, "DownloadableItems"): |  | ||||||
|         item.classification = ItemClassification.filler |  | ||||||
|     elif name == 'Oculus Ring' and not is_option_enabled(world, player, "EyeSpy"): |  | ||||||
|         item.classification = ItemClassification.filler |  | ||||||
|     elif (name == 'Kobo' or name == 'Merchant Crow') and not is_option_enabled(world, player, "GyreArchives"): |  | ||||||
|         item.classification = ItemClassification.filler |  | ||||||
|     elif name in {"Timeworn Warp Beacon", "Modern Warp Beacon", "Mysterious Warp Beacon"} \ |  | ||||||
|             and not is_option_enabled(world, player, "UnchainedKeys"): |  | ||||||
|         item.classification = ItemClassification.filler |  | ||||||
| 
 |  | ||||||
|     return item |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def setup_events(player: int, locked_locations: List[str], location_cache: List[Location]): |  | ||||||
|     for location in location_cache: |  | ||||||
|             if location.address == EventId: |             if location.address == EventId: | ||||||
|             item = Item(location.name, ItemClassification.progression, EventId, player) |                 item = Item(location.name, ItemClassification.progression, EventId, self.player) | ||||||
| 
 |  | ||||||
|             locked_locations.append(location.name) |  | ||||||
| 
 |  | ||||||
|                 location.place_locked_item(item) |                 location.place_locked_item(item) | ||||||
| 
 | 
 | ||||||
| 
 |     def get_personal_items(self) -> Dict[int, int]: | ||||||
| def get_personal_items(player: int, locations: List[Location]) -> Dict[int, int]: |  | ||||||
|         personal_items: Dict[int, int] = {} |         personal_items: Dict[int, int] = {} | ||||||
| 
 | 
 | ||||||
|     for location in locations: |         for location in self.multiworld.get_locations(self.player): | ||||||
|         if location.address and location.item and location.item.code and location.item.player == player: |             if location.address and location.item and location.item.code and location.item.player == self.player: | ||||||
|                 personal_items[location.address] = location.item.code |                 personal_items[location.address] = location.item.code | ||||||
| 
 | 
 | ||||||
|         return personal_items |         return personal_items | ||||||
|  |      | ||||||
|  |     def is_option_enabled(self, option: str) -> bool: | ||||||
|  |         return is_option_enabled(self.multiworld, self.player, option) | ||||||
|  | 
 | ||||||
|  |     def get_option_value(self, option: str) -> Union[int, Dict, List]: | ||||||
|  |         return get_option_value(self.multiworld, self.player, option) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue