Esempio n. 1
0
    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))
    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. 3
0
    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
                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 == '#Junk' and location.type == 'Song' and not world.shuffle_song_items:
                record.item = '#JunkSong'

            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 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:
                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))