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
Esempio n. 2
0
    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
Esempio n. 3
0
    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))
Esempio n. 6
0
    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