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 pool_remove_item(self, pools, item_name, count, world_id=None, use_base_pool=True, ignore_pools=None): removed_items = [] base_remove_matcher = pattern_matcher(item_name) remove_matcher = lambda item: base_remove_matcher(item) and ((item in self.base_pool) ^ (not use_base_pool)) if world_id is None: predicate = remove_matcher else: predicate = lambda item: item.world.id == world_id and remove_matcher(item.name) for i in range(count): removed_item = pull_random_element(pools, predicate, ignore_pools=ignore_pools) if removed_item is None: if not use_base_pool: if IsItem(item_name): raise KeyError('No remaining items matching "%s" to be removed.' % (item_name)) else: raise KeyError('No items matching "%s"' % (item_name)) else: removed_items.extend(self.pool_remove_item(pools, item_name, count - i, world_id=world_id, use_base_pool=False)) break if use_base_pool: if world_id is None: self.base_pool.remove(removed_item) else: self.base_pool.remove(removed_item.name) removed_items.append(removed_item) return removed_items
def pool_add_item(self, pool, item_name, count): if item_name == '#Junk': added_items = get_junk_item(count, pool=pool, plando_pool=self.item_pool) elif is_pattern(item_name): add_matcher = lambda item: pattern_matcher(item_name)(item.name) candidates = [ item.name for item in ItemIterator(predicate=add_matcher) if item.name not in self.item_pool or self.item_pool[item.name].count != 0 ] # Only allow items to be candidates if they haven't been set to 0 if len(candidates) == 0: raise RuntimeError("Unknown item, or item set to 0 in the item pool could not be added: " + item_name) added_items = random_choices(candidates, k=count) else: if not IsItem(item_name): raise RuntimeError("Unknown item could not be added: " + item_name) added_items = [item_name] * count for item in added_items: pool.append(item) return added_items
def pool_add_item(self, pool, item_name, count): added_items = [] if item_name == '#Junk': added_items = get_junk_item(count) elif is_pattern(item_name): add_matcher = lambda item: pattern_matcher(item_name)(item.name) candidates = [ item.name for item in ItemIterator(predicate=add_matcher) ] if len(candidates) == 0: raise RuntimeError("Unknown item could not be added: " + item_name) added_items = random_choices(candidates, k=count) else: if not IsItem(item_name): raise RuntimeError("Unknown item could not be added: " + item_name) added_items = [item_name] * count for item in added_items: pool.append(item) return added_items
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 alter_pool(self, world, pool): self.base_pool = list(pool) pool_size = len(pool) bottle_matcher = pattern_matcher("#Bottle") trade_matcher = pattern_matcher("#AdultTrade") bottles = 0 for item_name, record in self.item_pool.items(): if record.type == 'add': self.pool_add_item(pool, item_name, record.count) if record.type == 'remove': self.pool_remove_item([pool], item_name, record.count) for item_name, record in self.item_pool.items(): if record.type == 'set': if item_name == '#Junk': raise ValueError('#Junk item group cannot have a set number of items') predicate = pattern_matcher(item_name) pool_match = [item for item in pool if predicate(item)] for item in pool_match: self.base_pool.remove(item) add_count = record.count - len(pool_match) if add_count > 0: added_items = self.pool_add_item(pool, item_name, add_count) for item in added_items: if bottle_matcher(item): bottles += 1 elif trade_matcher(item): self.pool_remove_item([pool], "#AdultTrade", 1) else: removed_items = self.pool_remove_item([pool], item_name, -add_count) for item in removed_items: if bottle_matcher(item): bottles -= 1 elif trade_matcher(item): self.pool_add_item(pool, "#AdultTrade", 1) if bottles > 0: self.pool_remove_item([pool], '#Bottle', bottles) else: self.pool_add_item(pool, '#Bottle', -bottles) for item_name, record in self.starting_items.items(): if bottle_matcher(item_name): self.pool_remove_item([pool], "#Bottle", record.count) elif trade_matcher(item_name): self.pool_remove_item([pool], "#AdultTrade", record.count) elif IsItem(item_name): try: self.pool_remove_item([pool], item_name, record.count) except KeyError: pass if item_name in item_groups["Song"]: self.song_as_items = True junk_to_add = pool_size - len(pool) if junk_to_add > 0: junk_items = self.pool_add_item(pool, "#Junk", junk_to_add) else: junk_items = self.pool_remove_item([pool], "#Junk", -junk_to_add) return pool