def load_regions_from_json(self, file_path): json_string = "" with io.open(file_path, 'r') as file: for line in file.readlines(): json_string += line.split('#')[0].replace('\n', ' ') region_json = json.loads(json_string) for region in region_json: new_region = Region(region['region_name']) new_region.world = self if 'dungeon' in region: new_region.dungeon = region['dungeon'] if 'locations' in region: for location, rule in region['locations'].items(): new_location = LocationFactory(location) new_location.parent_region = new_region if self.logic_rules != 'none': new_location.access_rule = parse_rule_string( rule, self) new_location.world = self new_region.locations.append(new_location) if 'exits' in region: for exit, rule in region['exits'].items(): new_exit = Entrance('%s -> %s' % (new_region.name, exit), new_region) new_exit.connected_region = exit if self.logic_rules != 'none': new_exit.access_rule = parse_rule_string(rule, self) new_region.exits.append(new_exit) self.regions.append(new_region)
def load_regions_from_json(self, file_path): json_string = "" with io.open(file_path, 'r') as file: for line in file.readlines(): json_string += line.split('#')[0].replace('\n', ' ') json_string = re.sub(' +', ' ', json_string) try: region_json = json.loads(json_string) except json.JSONDecodeError as error: raise Exception("JSON parse error around text:\n" + \ json_string[error.pos-35:error.pos+35] + "\n" + \ " ^^\n") for region in region_json: new_region = Region(region['region_name']) new_region.world = self if 'scene' in region: new_region.scene = region['scene'] if 'hint' in region: new_region.hint = region['hint'] if 'dungeon' in region: new_region.dungeon = region['dungeon'] if 'time_passes' in region: new_region.time_passes = region['time_passes'] new_region.provides_time = TimeOfDay.ALL if new_region.name == 'Ganons Castle Grounds': new_region.provides_time = TimeOfDay.DAMPE if 'locations' in region: for location, rule in region['locations'].items(): new_location = LocationFactory(location) new_location.parent_region = new_region new_location.rule_string = rule if self.logic_rules != 'none': self.parser.parse_spot_rule(new_location) new_location.world = self new_region.locations.append(new_location) if 'events' in region: for event, rule in region['events'].items(): # Allow duplicate placement of events lname = '%s from %s' % (event, new_region.name) new_location = Location(lname, type='Event', parent=new_region) new_location.rule_string = rule if self.logic_rules != 'none': self.parser.parse_spot_rule(new_location) new_location.world = self new_region.locations.append(new_location) MakeEventItem(event, new_location) if 'exits' in region: for exit, rule in region['exits'].items(): new_exit = Entrance('%s -> %s' % (new_region.name, exit), new_region) new_exit.connected_region = exit new_exit.rule_string = rule if self.logic_rules != 'none': self.parser.parse_spot_rule(new_exit) new_region.exits.append(new_exit) self.regions.append(new_region)
def cloak(self, worlds, location_pools, model_pools): for (name, record) in pattern_dict_items(self.locations): if record.model is None: continue player_id = self.id if record.player is None else record.player - 1 world = worlds[player_id] try: location = LocationFactory(name) except KeyError: raise RuntimeError('Unknown location in world %d: %s' % (world.id + 1, name)) if location.type == 'Boss': continue location = pull_item_or_location(location_pools, world, name) if location is None: raise RuntimeError('Location already cloaked in world %d: %s' % (self.id + 1, name)) model = pull_item_or_location(model_pools, world, record.model, remove=False) if model is None: raise RuntimeError('Unknown model in world %d: %s' % (self.id + 1, record.model)) if can_cloak(location.item, model): location.item.looks_like_item = model
def fill_bosses(self, world, prize_locs, prizepool): count = 0 for (name, record) in pattern_dict_items(self.locations, prizepool): boss = pull_item_or_location([prize_locs], world, name) if boss is None: try: location = LocationFactory(name) except KeyError: raise RuntimeError('Unknown boss in world %d: %s' % (world.id + 1, name)) if location.type == 'Boss': raise RuntimeError( 'Boss or already placed in world %d: %s' % (world.id + 1, name)) else: continue if record.player is not None and (record.player - 1) != self.id: raise RuntimeError( 'A boss can only give rewards in its own world') reward = pull_item_or_location([prizepool], world, record.item) if reward is None: if record.item not in item_groups['DungeonReward']: raise RuntimeError( 'Cannot place non-dungeon reward %s in world %d on location %s.' % (record.item, self.id + 1, name)) if IsItem(record.item): raise RuntimeError( 'Reward already placed in world %d: %s' % (world.id + 1, record.item)) else: raise RuntimeError('Reward unknown in world %d: %s' % (world.id + 1, record.item)) count += 1 world.push_item(boss, reward, True) return count
def fill(self, window, worlds, location_pools, item_pools): world = worlds[self.id] for (location_name, record) in pattern_dict_items(self.locations): if record.item is None: continue player_id = self.id if record.player is None else record.player - 1 location_matcher = lambda loc: loc.world.id == world.id and loc.name == location_name location = pull_first_element(location_pools, location_matcher) if location is None: try: location = LocationFactory(location_name) except KeyError: raise RuntimeError('Unknown location in world %d: %s' % (world.id + 1, name)) if location.type == 'Boss': continue else: raise RuntimeError( 'Location already filled in world %d: %s' % (self.id + 1, location_name)) try: item = self.pool_remove_item(item_pools, record.item, 1, world_id=player_id)[0] except KeyError: try: self.pool_remove_item(item_pools, "#Junk", 1, world_id=player_id) item_matcher = lambda item: pattern_matcher(record.item)( item.name) item = random.choice( list(ItemIterator(item_matcher, worlds[player_id]))) except KeyError: raise RuntimeError( 'Too many items were added to world %d, and not enough junk is available to be removed.' % (self.id + 1)) if record.price is not None: item.price = record.price location.world.push_item(location, item, True) if item.advancement: playthrough = Playthrough.max_explore( [world.state for world in worlds], itertools.chain.from_iterable(item_pools)) if not playthrough.can_beat_game(False): raise FillError( '%s in world %d is not reachable without %s in world %d!' % (location.name, self.id + 1, item.name, player_id + 1)) window.fillcount += 1 window.update_progress(5 + ( (window.fillcount / window.locationcount) * 30))
def load_regions_from_json(self, file_path): region_json = read_json(file_path) for region in region_json: new_region = Region(region['region_name']) new_region.world = self if 'scene' in region: new_region.scene = region['scene'] if 'hint' in region: new_region.hint = region['hint'] if 'dungeon' in region: new_region.dungeon = region['dungeon'] if 'time_passes' in region: new_region.time_passes = region['time_passes'] new_region.provides_time = TimeOfDay.ALL if new_region.name == 'Ganons Castle Grounds': new_region.provides_time = TimeOfDay.DAMPE if 'locations' in region: for location, rule in region['locations'].items(): new_location = LocationFactory(location) new_location.parent_region = new_region new_location.rule_string = rule if self.logic_rules != 'none': self.parser.parse_spot_rule(new_location) if new_location.never: # We still need to fill the location even if ALR is off. logging.getLogger('').debug('Unreachable location: %s', new_location.name) new_location.world = self new_region.locations.append(new_location) if 'events' in region: for event, rule in region['events'].items(): # Allow duplicate placement of events lname = '%s from %s' % (event, new_region.name) new_location = Location(lname, type='Event', parent=new_region) new_location.rule_string = rule if self.logic_rules != 'none': self.parser.parse_spot_rule(new_location) if new_location.never: logging.getLogger('').debug('Dropping unreachable event: %s', new_location.name) else: new_location.world = self new_region.locations.append(new_location) MakeEventItem(event, new_location) if 'exits' in region: for exit, rule in region['exits'].items(): new_exit = Entrance('%s -> %s' % (new_region.name, exit), new_region) new_exit.connected_region = exit new_exit.rule_string = rule if self.logic_rules != 'none': self.parser.parse_spot_rule(new_exit) if new_exit.never: logging.getLogger('').debug('Dropping unreachable exit: %s', new_exit.name) else: new_region.exits.append(new_exit) self.regions.append(new_region)
def load_regions_from_json(self, file_path): json_string = "" with io.open(file_path, 'r') as file: for line in file.readlines(): json_string += line.split('#')[0].replace('\n', ' ') json_string = re.sub(' +', ' ', json_string) try: region_json = json.loads(json_string) except json.JSONDecodeError as error: raise Exception("JSON parse error around text:\n" + \ json_string[error.pos-35:error.pos+35] + "\n" + \ " ^^\n") for region in region_json: new_region = Region(region['region_name']) new_region.world = self if 'scene' in region: new_region.scene = region['scene'] if 'hint' in region: new_region.hint = region['hint'] if 'dungeon' in region: new_region.dungeon = region['dungeon'] if 'time_passes' in region: new_region.time_passes = region['time_passes'] if 'locations' in region: for location, rule in region['locations'].items(): new_location = LocationFactory(location) new_location.parent_region = new_region if self.logic_rules != 'none': new_location.access_rule = parse_rule_string( rule, self) new_location.world = self new_region.locations.append(new_location) if 'exits' in region: for exit, rule in region['exits'].items(): new_exit = Entrance('%s -> %s' % (new_region.name, exit), new_region) new_exit.connected_region = exit if self.logic_rules != 'none': new_exit.access_rule = parse_rule_string(rule, self) new_region.exits.append(new_exit) self.regions.append(new_region)
def fill(self, window, worlds, location_pools, item_pools): world = worlds[self.id] locations = {} if self.locations: locations = { loc: self.locations[loc] for loc in random.sample(self.locations.keys(), len(self.locations)) } for starting_item in self.starting_items: for _ in range(self.starting_items[starting_item].count): try: if starting_item in item_groups['DungeonReward']: continue item = None if starting_item in item_groups['Bottle']: item = self.pool_replace_item(item_pools, "#Bottle", self.id, "#Junk", worlds) elif starting_item in item_groups['AdultTrade']: item = self.pool_replace_item(item_pools, "#AdultTrade", self.id, "#Junk", worlds) elif IsItem(starting_item): try: item = self.pool_replace_item( item_pools, starting_item, self.id, "#Junk", worlds) except KeyError: pass # If a normal item exceeds the item pool count, continue. except KeyError: raise RuntimeError( 'Started with too many "%s" in world %d, and not enough "%s" are available in the item pool to be removed.' % (starting_item, self.id + 1, starting_item)) if starting_item in item_groups['Song']: self.song_as_items = True # Update item_pool if item is not None: if item not in self.item_pool: self.item_pool[item.name] = ItemPoolRecord({ 'type': 'set', 'count': 1 }) else: self.item_pool[item.name].count += 1 item_pools[5].append(ItemFactory(item.name, world)) for (location_name, record) in pattern_dict_items(locations, world.itempool, []): if record.item is None: continue player_id = self.id if record.player is None else record.player - 1 location_matcher = lambda loc: loc.world.id == world.id and loc.name == location_name location = pull_first_element(location_pools, location_matcher) if location is None: try: location = LocationFactory(location_name) except KeyError: raise RuntimeError('Unknown location in world %d: %s' % (world.id + 1, location_name)) if location.type == 'Boss': continue elif location.name in world.disabled_locations: continue else: raise RuntimeError( 'Location already filled in world %d: %s' % (self.id + 1, location_name)) if record.item in item_groups['DungeonReward']: raise RuntimeError( 'Cannot place dungeon reward %s in world %d in location %s.' % (record.item, self.id + 1, location_name)) if record.item == '#Junk' and location.type == 'Song' and not world.shuffle_song_items: record.item = '#JunkSong' ignore_pools = None is_invert = pattern_matcher(record.item)('!') if is_invert and location.type != 'Song' and not world.shuffle_song_items: ignore_pools = [2] if is_invert and location.type == 'Song' and not world.shuffle_song_items: ignore_pools = [i for i in range(len(item_pools)) if i != 2] try: item = self.pool_remove_item(item_pools, record.item, 1, world_id=player_id, ignore_pools=ignore_pools)[0] except KeyError: if location.type == 'Shop' and "Buy" in record.item: try: self.pool_remove_item([item_pools[0]], "Buy *", 1, world_id=player_id) item = ItemFactory([record.item], world=world)[0] except KeyError: raise RuntimeError( 'Too many shop buy items were added to world %d, and not enough shop buy items are available in the item pool to be removed.' % (self.id + 1)) elif record.item in item_groups['Bottle']: try: item = self.pool_replace_item(item_pools, "#Bottle", player_id, record.item, worlds) except KeyError: raise RuntimeError( 'Too many bottles were added to world %d, and not enough bottles are available in the item pool to be removed.' % (self.id + 1)) elif record.item in item_groups['AdultTrade']: try: item = self.pool_replace_item(item_pools, "#AdultTrade", player_id, record.item, worlds) except KeyError: raise RuntimeError( 'Too many adult trade items were added to world %d, and not enough adult trade items are available in the item pool to be removed.' % (self.id + 1)) else: try: item = self.pool_replace_item(item_pools, "#Junk", player_id, record.item, worlds) except KeyError: raise RuntimeError( 'Too many items were added to world %d, and not enough junk is available to be removed.' % (self.id + 1)) # Update item_pool if item.name not in self.item_pool: self.item_pool[item.name] = ItemPoolRecord({ 'type': 'set', 'count': 1 }) else: self.item_pool[item.name].count += 1 except IndexError: raise RuntimeError( 'Unknown item %s being placed on location %s in world %d.' % (record.item, location, self.id + 1)) if record.price is not None and item.type != 'Shop': location.price = record.price world.shop_prices[location.name] = record.price if location.type == 'Song' and item.type != 'Song': self.song_as_items = True location.world.push_item(location, item, True) if item.advancement: search = Search.max_explore( [world.state for world in worlds], itertools.chain.from_iterable(item_pools)) if not search.can_beat_game(False): raise FillError( '%s in world %d is not reachable without %s in world %d!' % (location.name, self.id + 1, item.name, player_id + 1)) window.fillcount += 1 window.update_progress(5 + ( (window.fillcount / window.locationcount) * 30))
def fill(self, window, worlds, location_pools, item_pools): """Fills the world with restrictions defined in a plandomizer JSON file. :param window: :param worlds: A list of the world objects that define the rules of each game world. :param location_pools: A list containing all of the location pools. 0: Shop Locations 1: Song Locations 2: Fill locations :param item_pools: A list containing all of the item pools. 0: Shop Items 1: Dungeon Items 2: Songs 3: Progression Items 4: Priority Items 5: The rest of the Item pool """ world = worlds[self.id] locations = {} if self.locations: locations = {loc: self.locations[loc] for loc in random.sample(self.locations.keys(), len(self.locations))} used_items = [] for (location_name, record) in pattern_dict_items(locations, world.itempool, used_items): if record.item is None: continue player_id = self.id if record.player is None else record.player - 1 location_matcher = lambda loc: loc.world.id == world.id and loc.name.lower() == location_name.lower() location = pull_first_element(location_pools, location_matcher) if location is None: try: location = LocationFactory(location_name) except KeyError: raise RuntimeError('Unknown location in world %d: %s' % (world.id + 1, location_name)) if location.type == 'Boss': continue elif location.name in world.disabled_locations: continue else: raise RuntimeError('Location already filled in world %d: %s' % (self.id + 1, location_name)) if record.item in item_groups['DungeonReward']: raise RuntimeError('Cannot place dungeon reward %s in world %d in location %s.' % (record.item, self.id + 1, location_name)) if record.item == '#Junk' and location.type == 'Song' and not world.shuffle_song_items: record.item = '#JunkSong' ignore_pools = None is_invert = pattern_matcher(record.item)('!') if is_invert and location.type != 'Song' and not world.shuffle_song_items: ignore_pools = [2] if is_invert and location.type == 'Song' and not world.shuffle_song_items: ignore_pools = [i for i in range(len(item_pools)) if i != 2] try: if record.item == "#Bottle": try: item = self.pool_replace_item(item_pools, "#Bottle", player_id, record.item, worlds) # Update item_pool if item.name not in self.item_pool: self.item_pool[item.name] = ItemPoolRecord() else: self.item_pool[item.name].count += 1 except KeyError: raise RuntimeError( 'Too many bottles were added to world %d, and not enough bottles are available in the item pool to be removed.' % ( self.id + 1)) elif record.item == "#AdultTrade": try: item = self.pool_replace_item(item_pools, "#AdultTrade", player_id, record.item, worlds) # Update item_pool if item.name not in self.item_pool: self.item_pool[item.name] = ItemPoolRecord() else: self.item_pool[item.name].count += 1 except KeyError: raise RuntimeError( 'Too many adult trade items were added to world %d, and not enough adult trade items are available in the item pool to be removed.' % ( self.id + 1)) else: item = self.pool_remove_item(item_pools, record.item, 1, world_id=player_id, ignore_pools=ignore_pools)[0] except KeyError: if location.type == 'Shop' and "Buy" in record.item: try: self.pool_remove_item([item_pools[0]], "Buy *", 1, world_id=player_id) item = ItemFactory([record.item], world=world)[0] except KeyError: raise RuntimeError('Too many shop buy items were added to world %d, and not enough shop buy items are available in the item pool to be removed.' % (self.id + 1)) elif record.item in item_groups['Bottle']: try: item = self.pool_replace_item(item_pools, "#Bottle", player_id, record.item, worlds) except KeyError: raise RuntimeError('Too many bottles were added to world %d, and not enough bottles are available in the item pool to be removed.' % (self.id + 1)) elif record.item in item_groups['AdultTrade']: try: item = self.pool_replace_item(item_pools, "#AdultTrade", player_id, record.item, worlds) except KeyError: raise RuntimeError('Too many adult trade items were added to world %d, and not enough adult trade items are available in the item pool to be removed.' % (self.id + 1)) elif record.item == "Weird Egg": # If Letter has not been shown to guard before obtaining a second weird egg a softlock can occur # if there are important items at deku theater or an important location locked behind the gate # or if Keaton Mask gets overwritten before giving it to the guard. try: item = self.pool_replace_item(item_pools, "Weird Egg", player_id, record.item, worlds) except KeyError: raise RuntimeError('Weird Egg already placed in World %d.' % (self.id + 1)) else: try: item = self.pool_replace_item(item_pools, "#Junk", player_id, record.item, worlds) except KeyError: raise RuntimeError('Too many items were added to world %d, and not enough junk is available to be removed.' % (self.id + 1)) # Update item_pool if item.name not in self.item_pool: self.item_pool[item.name] = ItemPoolRecord() else: self.item_pool[item.name].count += 1 except IndexError: raise RuntimeError('Unknown item %s being placed on location %s in world %d.' % (record.item, location, self.id + 1)) if record.price is not None and item.type != 'Shop': location.price = record.price world.shop_prices[location.name] = record.price if location.type == 'Song' and item.type != 'Song': self.song_as_items = True location.world.push_item(location, item, True) if item.advancement: search = Search.max_explore([world.state for world in worlds], itertools.chain.from_iterable(item_pools)) if not search.can_beat_game(False): raise FillError('%s in world %d is not reachable without %s in world %d!' % (location.name, self.id + 1, item.name, player_id + 1)) window.fillcount += 1 window.update_progress(5 + ((window.fillcount / window.locationcount) * 30))