Move hint info gathering to stage_generate_output
only loops over world locations once rather than many times
This commit is contained in:
		
							parent
							
								
									89984a0d09
								
							
						
					
					
						commit
						82ae21420d
					
				|  | @ -640,21 +640,12 @@ def buildWorldGossipHints(world, checkedLocations=None): | |||
|     # Seed the RNG | ||||
|     world.hint_rng = world.world.slot_seeds[world.player] | ||||
| 
 | ||||
|     # Gather woth, barren, major items | ||||
|     world.gather_hint_data() | ||||
| 
 | ||||
|     # rebuild hint exclusion list | ||||
|     hintExclusions(world, clear_cache=True) | ||||
| 
 | ||||
|     world.barren_dungeon = 0 | ||||
|     world.woth_dungeon = 0 | ||||
| 
 | ||||
|     # search = Search.max_explore([w.state for w in orlds]) | ||||
|     # for stone in gossipLocations.values(): | ||||
|     #     stone.reachable = ( | ||||
|     #         search.spot_access(world.get_location(stone.location)) | ||||
|     #         and search.state_list[world.id].guarantee_hint()) | ||||
| 
 | ||||
|     if checkedLocations is None: | ||||
|         checkedLocations = {player: set() for player in world.world.player_ids} | ||||
| 
 | ||||
|  | @ -669,8 +660,6 @@ def buildWorldGossipHints(world, checkedLocations=None): | |||
| 
 | ||||
|     stoneIDs = list(gossipLocations.keys()) | ||||
| 
 | ||||
|     # world.distribution.configure_gossip(stoneIDs) | ||||
| 
 | ||||
|     if 'disabled' in world.hint_dist_user: | ||||
|         for stone_name in world.hint_dist_user['disabled']: | ||||
|             try: | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ location_id_offset = 67000 | |||
| 
 | ||||
| # OoT's generate_output doesn't benefit from more than 2 threads, instead it uses a lot of memory. | ||||
| i_o_limiter = threading.Semaphore(2) | ||||
| hint_data_available = threading.Event() | ||||
| 
 | ||||
| 
 | ||||
| class OOTWorld(World): | ||||
|  | @ -646,8 +647,10 @@ class OOTWorld(World): | |||
|             impa.event = True | ||||
|             self.world.itempool.remove(item_to_place) | ||||
| 
 | ||||
|     # For now we will always output a patch file. | ||||
|     def generate_output(self, output_directory: str): | ||||
|         if self.hints != 'none': | ||||
|             hint_data_available.wait() | ||||
| 
 | ||||
|         with i_o_limiter: | ||||
|             # Make ice traps appear as other random items | ||||
|             ice_traps = [loc.item for loc in self.get_locations() if loc.item.name == 'Ice Trap'] | ||||
|  | @ -655,8 +658,7 @@ class OOTWorld(World): | |||
|                 trap.looks_like_item = self.create_item(self.world.slot_seeds[self.player].choice(self.fake_items).name) | ||||
| 
 | ||||
|             outfile_name = f"AP_{self.world.seed_name}_P{self.player}_{self.world.get_player_name(self.player)}" | ||||
|             rom = Rom( | ||||
|                 file=get_options()['oot_options']['rom_file'])  # a ROM must be provided, cannot produce patches without it | ||||
|             rom = Rom(file=get_options()['oot_options']['rom_file']) | ||||
|             if self.hints != 'none': | ||||
|                 buildWorldGossipHints(self) | ||||
|             patch_rom(self, rom) | ||||
|  | @ -665,6 +667,43 @@ class OOTWorld(World): | |||
|             create_patch_file(rom, output_path(output_directory, outfile_name + '.apz5')) | ||||
|             rom.restore() | ||||
| 
 | ||||
|     # Gathers hint data for OoT. Loops over all world locations for woth, barren, and major item locations. | ||||
|     def stage_generate_output(world: MultiWorld, output_directory: str): | ||||
|         items_by_region = {player: {} for player in world.get_game_players("Ocarina of Time") if world.worlds[player].hints != 'none'} | ||||
|         if items_by_region: | ||||
|             for player in items_by_region: | ||||
|                 for r in world.worlds[player].regions: | ||||
|                     items_by_region[player][r.hint_text] = {'dungeon': False, 'weight': 0, 'prog_items': 0} | ||||
|                 for d in world.worlds[player].dungeons: | ||||
|                     items_by_region[player][d.hint_text] = {'dungeon': True, 'weight': 0, 'prog_items': 0} | ||||
|                 del (items_by_region[player]["Link's Pocket"]) | ||||
|                 del (items_by_region[player][None]) | ||||
| 
 | ||||
|             for loc in world.get_locations(): | ||||
|                 player = loc.item.player | ||||
|                 autoworld = world.worlds[player] | ||||
|                 if ((player in items_by_region and (autoworld.is_major_item(loc.item) or loc.item.name in autoworld.item_added_hint_types['item']))  | ||||
|                             or (loc.player in items_by_region and loc.name in autoworld.added_hint_types['item'])): | ||||
|                     autoworld.major_item_locations.append(loc) | ||||
| 
 | ||||
|                 if loc.game == "Ocarina of Time": | ||||
|                     if loc.item.code and (not loc.locked or loc.item.type == 'Song'):  # shuffled item | ||||
|                         hint_area = get_hint_area(loc) | ||||
|                         items_by_region[loc.player][hint_area]['weight'] += 1 | ||||
|                         if loc.item.advancement: | ||||
|                             # Non-locked progression. Increment counter | ||||
|                             items_by_region[loc.player][hint_area]['prog_items'] += 1 | ||||
|                             # Skip item at location and see if game is still beatable | ||||
|                             state = CollectionState(world) | ||||
|                             state.locations_checked.add(loc) | ||||
|                             if not world.can_beat_game(state): | ||||
|                                 world.worlds[loc.player].required_locations.append(loc) | ||||
| 
 | ||||
|             for autoworld in world.get_game_worlds("Ocarina of Time"): | ||||
|                 autoworld.empty_areas = {region: info for (region, info) in items_by_region[autoworld.player].items() if not info['prog_items']} | ||||
|         hint_data_available.set() | ||||
| 
 | ||||
| 
 | ||||
|     # Helper functions | ||||
|     def get_shuffled_entrances(self): | ||||
|         return [] | ||||
|  | @ -708,37 +747,3 @@ class OOTWorld(World): | |||
|             return False | ||||
| 
 | ||||
|         return True | ||||
| 
 | ||||
|     # Run this once for to gather up all required locations (for WOTH), barren regions (for foolish), and location of major items. | ||||
|     # required_locations and major_item_locations need to be ordered for deterministic hints. | ||||
|     def gather_hint_data(self): | ||||
|         if self.required_locations and self.empty_areas and self.major_item_locations: | ||||
|             return | ||||
| 
 | ||||
|         items_by_region = {} | ||||
|         for r in self.regions: | ||||
|             items_by_region[r.hint_text] = {'dungeon': False, 'weight': 0, 'prog_items': 0} | ||||
|         for d in self.dungeons: | ||||
|             items_by_region[d.hint_text] = {'dungeon': True, 'weight': 0, 'prog_items': 0} | ||||
|         del (items_by_region["Link's Pocket"]) | ||||
|         del (items_by_region[None]) | ||||
| 
 | ||||
|         for loc in self.get_locations(): | ||||
|             if loc.item.code:  # is a real item | ||||
|                 hint_area = get_hint_area(loc) | ||||
|                 items_by_region[hint_area]['weight'] += 1 | ||||
|                 if loc.item.advancement and (not loc.locked or loc.item.type == 'Song'): | ||||
|                     # Non-locked progression. Increment counter | ||||
|                     items_by_region[hint_area]['prog_items'] += 1 | ||||
|                     # Skip item at location and see if game is still beatable | ||||
|                     state = CollectionState(self.world) | ||||
|                     state.locations_checked.add(loc) | ||||
|                     if not self.world.can_beat_game(state): | ||||
|                         self.required_locations.append(loc) | ||||
|         self.empty_areas = {region: info for (region, info) in items_by_region.items() if not info['prog_items']} | ||||
| 
 | ||||
|         for loc in self.world.get_filled_locations(): | ||||
|             if (loc.item.player == self.player and self.is_major_item(loc.item) | ||||
|                     or (loc.item.player == self.player and loc.item.name in self.item_added_hint_types['item']) | ||||
|                     or (loc.name in self.added_hint_types['item'] and loc.player == self.player)): | ||||
|                 self.major_item_locations.append(loc) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue