def create_dungeons(world):
    for dungeon_info in dungeon_table:
        name = dungeon_info['name']
        hint = dungeon_info['hint'] if 'hint' in dungeon_info else name
        font_color = dungeon_info['font_color'] if 'font_color' in dungeon_info else 'White'
        
        if world.settings.logic_rules == 'glitched':
            if not world.dungeon_mq[name]:
                dungeon_json = os.path.join(data_path('Glitched World'), name + '.json')
            else:
                dungeon_json = os.path.join(data_path('Glitched World'), name + ' MQ.json')
        else:
            if not world.dungeon_mq[name]:
                dungeon_json = os.path.join(data_path('World'), name + '.json')
            else:
                dungeon_json = os.path.join(data_path('World'), name + ' MQ.json')

        
        world.load_regions_from_json(dungeon_json)

        boss_keys = ItemFactory(['Boss Key (%s)' % name] * dungeon_info['boss_key'])
        if not world.dungeon_mq[dungeon_info['name']]:
            small_keys = ItemFactory(['Small Key (%s)' % name] * dungeon_info['small_key'])
        else:
            small_keys = ItemFactory(['Small Key (%s)' % name] * dungeon_info['small_key_mq'])           
        dungeon_items = ItemFactory(['Map (%s)' % name, 
                                     'Compass (%s)' % name] * dungeon_info['dungeon_item'])
        if world.settings.shuffle_mapcompass in ['any_dungeon', 'overworld']:
            for item in dungeon_items:
                item.priority = True

        world.dungeons.append(Dungeon(world, name, hint, font_color, boss_keys, small_keys, dungeon_items))
Example #2
0
def create_dungeons(world):
    for dungeon_info in dungeon_table:
        name = dungeon_info['name']
        hint = dungeon_info['hint'] if 'hint' in dungeon_info else name

        if world.settings.logic_rules == 'glitched':
            if not world.dungeon_mq[name]:
                dungeon_json = os.path.join(data_path('Glitched World'),
                                            name + '.json')
            else:
                dungeon_json = os.path.join(data_path('Glitched World'),
                                            name + ' MQ.json')
        else:
            if not world.dungeon_mq[name]:
                dungeon_json = os.path.join(data_path('World'), name + '.json')
            else:
                dungeon_json = os.path.join(data_path('World'),
                                            name + ' MQ.json')

        world.load_regions_from_json(dungeon_json)

        boss_keys = ItemFactory(['Boss Key (%s)' % name] *
                                dungeon_info['boss_key'])
        if not world.dungeon_mq[dungeon_info['name']]:
            small_keys = ItemFactory(['Small Key (%s)' % name] *
                                     dungeon_info['small_key'])
        else:
            small_keys = ItemFactory(['Small Key (%s)' % name] *
                                     dungeon_info['small_key_mq'])
        dungeon_items = ItemFactory(
            ['Map (%s)' % name, 'Compass (%s)' % name] *
            dungeon_info['dungeon_item'])

        world.dungeons.append(
            Dungeon(world, name, hint, boss_keys, small_keys, dungeon_items))
Example #3
0
def generate_itempool(world):
    junk_pool[:] = list(junk_pool_base)
    if world.junk_ice_traps == 'on': 
        junk_pool.append(('Ice Trap', 10))
    elif world.junk_ice_traps in ['mayhem', 'onslaught']:
        junk_pool[:] = [('Ice Trap', 1)]

    fixed_locations = list(filter(lambda loc: loc.name in fixedlocations, world.get_locations()))
    for location in fixed_locations:
        item = fixedlocations[location.name]
        world.push_item(location, ItemFactory(item, world))
        location.locked = True

    drop_locations = list(filter(lambda loc: loc.type == 'Drop', world.get_locations()))
    for drop_location in drop_locations:
        item = droplocations[drop_location.name]
        world.push_item(drop_location, ItemFactory(item, world))
        drop_location.locked = True

    # set up item pool
    (pool, placed_items) = get_pool_core(world)
    world.itempool = ItemFactory(pool, world)
    for (location, item) in placed_items.items():
        world.push_item(location, ItemFactory(item, world))
        world.get_location(location).locked = True

    world.initialize_items()
    world.distribution.set_complete_itempool(world.itempool)
Example #4
0
 def pool_replace_item(self, item_pools, item_group, player_id, new_item, worlds):
     removed_item = self.pool_remove_item(item_pools, item_group, 1, world_id=player_id)[0]
     item_matcher = lambda item: pattern_matcher(new_item)(item.name)
     if self.item_pool[removed_item.name].count > 1:
         self.item_pool[removed_item.name].count -= 1
     else:
         del self.item_pool[removed_item.name]
     if new_item == "#Junk":
         if self.distribution.settings.enable_distribution_file:
             return ItemFactory(get_junk_item(1, self.base_pool, self.item_pool))[0]
         else:  # Generator settings that add junk to the pool should not be strict about the item_pool definitions
             return ItemFactory(get_junk_item(1))[0]
     return random.choice(list(ItemIterator(item_matcher, worlds[player_id])))
Example #5
0
def try_collect_pieces_of_heart(world, pool):
    n = pool.count('Piece of Heart') + pool.count('Piece of Heart (Treasure Chest Game)')
    if n >= 4:
        for i in range(4):
            if 'Piece of Heart' in pool:
                pool.remove('Piece of Heart')
                world.state.collect(ItemFactory('Piece of Heart'))
            else:
                pool.remove('Piece of Heart (Treasure Chest Game)')
                world.state.collect(ItemFactory('Piece of Heart (Treasure Chest Game)'))
            pool.extend(get_junk_item())
        return True
    return False
Example #6
0
def fill_bosses(world, bossCount=9):
    boss_rewards = ItemFactory(rewardlist, world)
    boss_locations = [
        world.get_location('Queen Gohma'),
        world.get_location('King Dodongo'),
        world.get_location('Barinade'),
        world.get_location('Phantom Ganon'),
        world.get_location('Volvagia'),
        world.get_location('Morpha'),
        world.get_location('Bongo Bongo'),
        world.get_location('Twinrova'),
        world.get_location('Links Pocket')]

    placed_prizes = [loc.item.name for loc in boss_locations if loc.item is not None]
    unplaced_prizes = [item for item in boss_rewards if item.name not in placed_prizes]
    empty_boss_locations = [loc for loc in boss_locations if loc.item is None]
    prizepool = list(unplaced_prizes)
    prize_locs = list(empty_boss_locations)

    while bossCount:
        bossCount -= 1
        random.shuffle(prizepool)
        random.shuffle(prize_locs)
        item = prizepool.pop()
        loc = prize_locs.pop()
        world.push_item(loc, item)
Example #7
0
    def fill_bosses(self, bossCount=9):
        boss_rewards = ItemFactory(self.rewardlist, self)
        boss_locations = [
            self.get_location(loc) for loc in self.boss_location_names
        ]

        placed_prizes = [
            loc.item.name for loc in boss_locations if loc.item is not None
        ]
        unplaced_prizes = [
            item for item in boss_rewards if item.name not in placed_prizes
        ]
        empty_boss_locations = [
            loc for loc in boss_locations if loc.item is None
        ]
        prizepool = list(unplaced_prizes)
        prize_locs = list(empty_boss_locations)

        bossCount -= self.distribution.fill_bosses(self, prize_locs, prizepool)

        while bossCount:
            bossCount -= 1
            random.shuffle(prizepool)
            random.shuffle(prize_locs)
            item = prizepool.pop()
            loc = prize_locs.pop()
            self.push_item(loc, item)
Example #8
0
def generate(input_data):
    settings = getSettings(input_data)

    for trick in SettingsList.logic_tricks.values():
        settings.__dict__[trick['name']] = trick['name'] in settings.allowed_tricks

    worlds = []
    for i in range(0, settings.world_count):
        worlds.append(World(i, settings))
        worlds[-1].ensure_tod_access = False

    for id, world in enumerate(worlds):
        if settings.logic_rules == 'glitched':
            overworld_data = os.path.join(data_path('Glitched World'), 'Overworld.json')
        else:
            overworld_data = os.path.join(data_path('World'), 'Overworld.json')

        # Compile the json rules based on settings
        world.load_regions_from_json(overworld_data)
        create_dungeons(world)
        world.create_internal_locations()

        # Populate drop items
        drop_locations = list(filter(lambda loc: loc.type == 'Drop', world.get_locations()))
        for drop_location in drop_locations:
            item = ItemPool.droplocations[drop_location.name]
            world.push_item(drop_location, ItemFactory(item, world))
            drop_location.locked = True

        return world
Example #9
0
def try_collect_heart_container(world, pool):
    if 'Heart Container' in pool:
        pool.remove('Heart Container')
        pool.extend(get_junk_item())
        world.state.collect(ItemFactory('Heart Container'))
        return True
    return False
Example #10
0
 def collect_starters(self, state):
     for (name, record) in self.starting_items.items():
         for _ in range(record.count):
             try:
                 item = ItemFactory("Bottle" if name == "Bottle with Milk (Half)" else name)
             except KeyError:
                 continue
             state.collect(item)
Example #11
0
def fast_fill(window, locations, itempool):
    random.shuffle(locations)
    while itempool and locations:
        spot_to_fill = locations.pop()
        item_to_place = itempool.pop()
        # Impa can't presently hand out refills at the start of the game.
        # Only replace her item with a rupee if it's junk.
        if spot_to_fill.world.skip_child_zelda and spot_to_fill.name == 'Song from Impa' and item_to_place.name in remove_junk_set:
            item_to_place = ItemFactory('Rupee (1)', spot_to_fill.world)
        spot_to_fill.world.push_item(spot_to_fill, item_to_place)
        window.fillcount += 1
        window.update_progress(5 + (
            (window.fillcount / window.locationcount) * 30))
Example #12
0
def generate_itempool(world):
    junk_pool[:] = list(junk_pool_base)
    if world.junk_ice_traps == 'on': 
        junk_pool.append(('Ice Trap', 10))
    elif world.junk_ice_traps in ['mayhem', 'onslaught']:
        junk_pool[:] = [('Ice Trap', 1)]

    for location, item in eventlocations.items():
        world.push_item(location, ItemFactory(item, world))
        world.get_location(location).locked = True

    # set up item pool
    (pool, placed_items) = get_pool_core(world)
    world.itempool = ItemFactory(pool, world)
    for (location, item) in placed_items.items():
        world.push_item(location, ItemFactory(item, world))
        world.get_location(location).locked = True

    choose_trials(world)
    fill_bosses(world)

    world.initialize_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))
Example #14
0
def validate_worlds(worlds, entrance_placed, locations_to_ensure_reachable,
                    itempool):

    if locations_to_ensure_reachable:
        max_playthrough = Playthrough.max_explore(
            [world.state for world in worlds], itempool)
        # If ALR is enabled, ensure all locations we want to keep reachable are indeed still reachable
        # Otherwise, just continue if the game is still beatable
        if not (worlds[0].check_beatable_only
                and max_playthrough.can_beat_game(False)):
            max_playthrough.visit_locations(locations_to_ensure_reachable)
            for location in locations_to_ensure_reachable:
                if not max_playthrough.visited(location):
                    raise EntranceShuffleError('%s is unreachable' %
                                               location.name)

    if (entrance_placed == None and worlds[0].shuffle_special_interior_entrances) or \
       (entrance_placed != None and entrance_placed.type in ['SpecialInterior', 'Overworld']):
        if not locations_to_ensure_reachable:
            max_playthrough = Playthrough.max_explore(
                [world.state for world in worlds], itempool)

        for world in worlds:
            # Links House entrance should be reachable as child at some point in the seed
            links_house_entrance = get_entrance_replacing(
                world.get_region('Links House'),
                'Kokiri Forest -> Links House')
            if not max_playthrough.state_list[world.id].can_reach(
                    links_house_entrance, age='child'):
                raise EntranceShuffleError(
                    'Links House Entrance is never reachable as child')

            # Temple of Time entrance should be reachable as both ages at some point in the seed
            temple_of_time_entrance = get_entrance_replacing(
                world.get_region('Temple of Time'),
                'Temple of Time Exterior -> Temple of Time')
            if not max_playthrough.state_list[world.id].can_reach(
                    temple_of_time_entrance, age='both'):
                raise EntranceShuffleError(
                    'Temple of Time Entrance is never reachable as both ages')

            # Temple of Time shouldn't be placed inside the Fishing Pond to prevent potential issues with the lake hylia water control
            if temple_of_time_entrance.name == 'Lake Hylia -> Fishing Hole':
                raise EntranceShuffleError(
                    'Temple of Time is placed behind the Fishing Pond')

            # Windmill door entrance should be reachable as both ages at some point in the seed
            windmill_door_entrance = get_entrance_replacing(
                world.get_region('Windmill'), 'Kakariko Village -> Windmill')
            if not max_playthrough.state_list[world.id].can_reach(
                    windmill_door_entrance, age='both'):
                raise EntranceShuffleError(
                    'Windmill Door Entrance is never reachable as both ages')

        # At least one valid starting region with all basic refills should be reachable without using any items at the beginning of the seed
        no_items_playthrough = Playthrough([State(world) for world in worlds])

        valid_starting_regions = ['Kokiri Forest', 'Kakariko Village']
        for world in worlds:
            if not any(region for region in valid_starting_regions
                       if no_items_playthrough.state_list[world.id].can_reach(
                           region)):
                raise EntranceShuffleError('Invalid starting area')

        time_travel_playthrough = Playthrough(
            [world.state.copy() for world in worlds])
        for world in worlds:
            time_travel_playthrough.collect(
                ItemFactory('Time Travel', world=world))
        time_travel_playthrough.visit_locations()

        for world in worlds:
            # For now, we consider that time of day must always be reachable as both ages without having collected any items (except in closed forest)
            # In ER, Time of day logic considers that the root always has access to time passing so this is important to ensure
            if not (any(region for region in time_travel_playthrough.
                        cached_spheres[-1]['child_regions']
                        if region.time_passes and region.world == world)
                    and any(region for region in time_travel_playthrough.
                            cached_spheres[-1]['adult_regions']
                            if region.time_passes and region.world == world)):
                raise EntranceShuffleError(
                    'Time passing is not guaranteed as both ages')

            # When starting as adult, child Link should be able to reach ToT without having collected any items
            # This is important to ensure that the player never loses access to the pedestal after going child
            if world.starting_age == 'adult' and not time_travel_playthrough.state_list[
                    world.id].can_reach('Temple of Time', age='child'):
                raise EntranceShuffleError(
                    'Links House to Temple of Time path as child is not guaranteed'
                )
    return
Example #15
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))
Example #16
0
def get_pool_core(world):
    pool = []
    placed_items = {}

    if world.shuffle_kokiri_sword:
        pool.append('Kokiri Sword')
    else:
        placed_items['Kokiri Sword Chest'] = 'Kokiri Sword'

    if world.open_fountain:
        bottle = random.choice(normal_bottles)
        pool.append(bottle)
    else:
        pool.append('Bottle with Letter')

    if world.shuffle_weird_egg:
        pool.append('Weird Egg')
    else:
        placed_items['Malon Egg'] = 'Weird Egg'

    if world.shuffle_ocarinas:
        pool.extend(['Ocarina'] * 2)
    else:
        placed_items['Gift from Saria'] = 'Ocarina'
        placed_items['Ocarina of Time'] = 'Ocarina'

    if world.dungeon_mq['Deku Tree']:
        skulltula_locations_final = skulltula_locations + [
            'GS Deku Tree MQ Lobby',
            'GS Deku Tree MQ Compass Room',
            'GS Deku Tree MQ Basement Ceiling',
            'GS Deku Tree MQ Basement Back Room']
    else:
        skulltula_locations_final = skulltula_locations + [
            'GS Deku Tree Compass Room',
            'GS Deku Tree Basement Vines',
            'GS Deku Tree Basement Gate',
            'GS Deku Tree Basement Back Room']
    if world.dungeon_mq['Dodongos Cavern']:
        skulltula_locations_final.extend([
            'GS Dodongo\'s Cavern MQ Scrub Room',
            'GS Dodongo\'s Cavern MQ Song of Time Block Room',
            'GS Dodongo\'s Cavern MQ Lizalfos Room',
            'GS Dodongo\'s Cavern MQ Larva Room',
            'GS Dodongo\'s Cavern MQ Back Area'])
    else:
        skulltula_locations_final.extend([
            'GS Dodongo\'s Cavern East Side Room',
            'GS Dodongo\'s Cavern Vines Above Stairs',
            'GS Dodongo\'s Cavern Back Room',
            'GS Dodongo\'s Cavern Alcove Above Stairs',
            'GS Dodongo\'s Cavern Scarecrow'])
    if world.dungeon_mq['Jabu Jabus Belly']:
        skulltula_locations_final.extend([
            'GS Jabu Jabu MQ Tailpasaran Room',
            'GS Jabu Jabu MQ Invisible Enemies Room',
            'GS Jabu Jabu MQ Boomerang Room',
            'GS Jabu Jabu MQ Near Boss'])
    else:
        skulltula_locations_final.extend([
            'GS Jabu Jabu Water Switch Room',
            'GS Jabu Jabu Lobby Basement Lower',
            'GS Jabu Jabu Lobby Basement Upper',
            'GS Jabu Jabu Near Boss'])
    if world.dungeon_mq['Forest Temple']:
        skulltula_locations_final.extend([
            'GS Forest Temple MQ First Hallway',
            'GS Forest Temple MQ Block Push Room',
            'GS Forest Temple MQ Outdoor East',
            'GS Forest Temple MQ Outdoor West',
            'GS Forest Temple MQ Well'])
    else:
        skulltula_locations_final.extend([
            'GS Forest Temple First Room',
            'GS Forest Temple Lobby',
            'GS Forest Temple Outdoor East',
            'GS Forest Temple Outdoor West',
            'GS Forest Temple Basement'])
    if world.dungeon_mq['Fire Temple']:
        skulltula_locations_final.extend([
            'GS Fire Temple MQ Above Fire Wall Maze',
            'GS Fire Temple MQ Fire Wall Maze Center',
            'GS Fire Temple MQ Big Lava Room',
            'GS Fire Temple MQ Fire Wall Maze Side Room',
            'GS Fire Temple MQ East Tower Top'])
    else:
        skulltula_locations_final.extend([
            'GS Fire Temple Song of Time Room',
            'GS Fire Temple Unmarked Bomb Wall',
            'GS Fire Temple East Tower Climb',
            'GS Fire Temple East Tower Top',
            'GS Fire Temple Basement'])
    if world.dungeon_mq['Water Temple']:
        skulltula_locations_final.extend([
            'GS Water Temple MQ Before Upper Water Switch',
            'GS Water Temple MQ North Basement',
            'GS Water Temple MQ Lizalfos Hallway',
            'GS Water Temple MQ Serpent River',
            'GS Water Temple MQ South Basement'])
    else:
        skulltula_locations_final.extend([
            'GS Water Temple South Basement',
            'GS Water Temple Serpent River',
            'GS Water Temple Falling Platform Room',
            'GS Water Temple Central Room',
            'GS Water Temple Near Boss Key Chest'])
    if world.dungeon_mq['Spirit Temple']:
        skulltula_locations_final.extend([
            'GS Spirit Temple MQ Lower Adult Right',
            'GS Spirit Temple MQ Lower Adult Left',
            'GS Spirit Temple MQ Iron Knuckle West',
            'GS Spirit Temple MQ Iron Knuckle North',
            'GS Spirit Temple MQ Sun Block Room'])
    else:
        skulltula_locations_final.extend([
            'GS Spirit Temple Metal Fence',
            'GS Spirit Temple Bomb for Light Room',
            'GS Spirit Temple Hall to West Iron Knuckle',
            'GS Spirit Temple Boulder Room',
            'GS Spirit Temple Lobby'])
    if world.dungeon_mq['Shadow Temple']:
        skulltula_locations_final.extend([
            'GS Shadow Temple MQ Crusher Room',
            'GS Shadow Temple MQ Wind Hint Room',
            'GS Shadow Temple MQ After Wind',
            'GS Shadow Temple MQ After Ship',
            'GS Shadow Temple MQ Near Boss'])
    else:
        skulltula_locations_final.extend([
            'GS Shadow Temple Like Like Room',
            'GS Shadow Temple Crusher Room',
            'GS Shadow Temple Single Giant Pot',
            'GS Shadow Temple Near Ship',
            'GS Shadow Temple Triple Giant Pot'])
    if world.dungeon_mq['Bottom of the Well']:
        skulltula_locations_final.extend([
            'GS Well MQ Basement',
            'GS Well MQ Coffin Room',
            'GS Well MQ West Inner Room'])
    else:
        skulltula_locations_final.extend([
            'GS Well West Inner Room',
            'GS Well East Inner Room',
            'GS Well Like Like Cage'])
    if world.dungeon_mq['Ice Cavern']:
        skulltula_locations_final.extend([
            'GS Ice Cavern MQ Scarecrow',
            'GS Ice Cavern MQ Ice Block',
            'GS Ice Cavern MQ Red Ice'])
    else:
        skulltula_locations_final.extend([
            'GS Ice Cavern Spinning Scythe Room',
            'GS Ice Cavern Heart Piece Room',
            'GS Ice Cavern Push Block Room'])
    if world.tokensanity == 'off':
        for location in skulltula_locations_final:
            placed_items[location] = 'Gold Skulltula Token'
    elif world.tokensanity == 'dungeons':
        for location in skulltula_locations_final:
            if world.get_location(location).scene >= 0x0A:
                placed_items[location] = 'Gold Skulltula Token'
            else:
                pool.append('Gold Skulltula Token')
    else:
        pool.extend(['Gold Skulltula Token'] * 100)


    if world.bombchus_in_logic:
        pool.extend(['Bombchus'] * 4)
        if world.dungeon_mq['Jabu Jabus Belly']:
            pool.extend(['Bombchus'])
        if world.dungeon_mq['Spirit Temple']:
            pool.extend(['Bombchus'] * 2)
        if not world.dungeon_mq['Bottom of the Well']:
            pool.extend(['Bombchus'])
        if world.dungeon_mq['Gerudo Training Grounds']:
            pool.extend(['Bombchus'])

    else:
        pool.extend(['Bombchus (5)'] + ['Bombchus (10)'] * 2)
        if world.dungeon_mq['Jabu Jabus Belly']:
                pool.extend(['Bombchus (10)'])
        if world.dungeon_mq['Spirit Temple']:
                pool.extend(['Bombchus (10)'] * 2)
        if not world.dungeon_mq['Bottom of the Well']:
                pool.extend(['Bombchus (10)'])
        if world.dungeon_mq['Gerudo Training Grounds']:
                pool.extend(['Bombchus (10)'])
        if world.dungeon_mq['Ganons Castle']:
            pool.extend(['Bombchus (10)'])
        else:
            pool.extend(['Bombchus (20)'])

    pool.extend(['Ice Trap'])
    if not world.dungeon_mq['Gerudo Training Grounds']:
        pool.extend(['Ice Trap'])
    if not world.dungeon_mq['Ganons Castle']:
        pool.extend(['Ice Trap'] * 4)

    if world.gerudo_fortress == 'open':
        placed_items['Gerudo Fortress North F1 Carpenter'] = 'Recovery Heart'
        placed_items['Gerudo Fortress North F2 Carpenter'] = 'Recovery Heart'
        placed_items['Gerudo Fortress South F1 Carpenter'] = 'Recovery Heart'
        placed_items['Gerudo Fortress South F2 Carpenter'] = 'Recovery Heart'
    elif world.shuffle_smallkeys == 'keysanity':
        if world.gerudo_fortress == 'fast':
            pool.append('Small Key (Gerudo Fortress)')
            placed_items['Gerudo Fortress North F2 Carpenter'] = 'Recovery Heart'
            placed_items['Gerudo Fortress South F1 Carpenter'] = 'Recovery Heart'
            placed_items['Gerudo Fortress South F2 Carpenter'] = 'Recovery Heart'
        else:
            pool.extend(['Small Key (Gerudo Fortress)'] * 4)
    else:
        if world.gerudo_fortress == 'fast':
            placed_items['Gerudo Fortress North F1 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items['Gerudo Fortress North F2 Carpenter'] = 'Recovery Heart'
            placed_items['Gerudo Fortress South F1 Carpenter'] = 'Recovery Heart'
            placed_items['Gerudo Fortress South F2 Carpenter'] = 'Recovery Heart'
        else:
            placed_items['Gerudo Fortress North F1 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items['Gerudo Fortress North F2 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items['Gerudo Fortress South F1 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items['Gerudo Fortress South F2 Carpenter'] = 'Small Key (Gerudo Fortress)'

    if world.shuffle_gerudo_card and world.gerudo_fortress != 'open':
        pool.append('Gerudo Membership Card')
    else:
        placed_items['Gerudo Fortress Membership Card'] = 'Gerudo Membership Card'

    if world.shopsanity == 'off':
        placed_items.update(vanilla_shop_items)
        if world.bombchus_in_logic:
            placed_items['Kokiri Shop Item 8'] = 'Buy Bombchu (5)'
            placed_items['Castle Town Bazaar Item 4'] = 'Buy Bombchu (5)'
            placed_items['Kakariko Bazaar Item 4'] = 'Buy Bombchu (5)'
        pool.extend(normal_rupees)

    else:
        remain_shop_items = list(vanilla_shop_items.values())
        pool.extend(min_shop_items)
        for item in min_shop_items:
            remain_shop_items.remove(item)

        shop_slots_count = len(remain_shop_items)
        shop_nonitem_count = len(world.shop_prices)
        shop_item_count = shop_slots_count - shop_nonitem_count

        pool.extend(random.sample(remain_shop_items, shop_item_count))
        pool.extend(get_junk_item(shop_nonitem_count))
        if world.shopsanity == '0':
            pool.extend(normal_rupees)
        else:
            pool.extend(shopsanity_rupees)

    if world.shuffle_scrubs != 'off':
        if world.dungeon_mq['Deku Tree']:
            pool.append('Deku Shield')
        if world.dungeon_mq['Dodongos Cavern']:
            pool.extend(['Deku Stick (1)', 'Deku Shield', 'Recovery Heart'])
        else:
            pool.extend(['Deku Nuts (5)', 'Deku Stick (1)', 'Deku Shield'])
        if not world.dungeon_mq['Jabu Jabus Belly']:
            pool.append('Deku Nuts (5)')
        if world.dungeon_mq['Ganons Castle']:
            pool.extend(['Bombs (5)', 'Recovery Heart', 'Rupees (5)', 'Deku Nuts (5)'])
        else:
            pool.extend(['Bombs (5)', 'Recovery Heart', 'Rupees (5)'])
        pool.extend(deku_scrubs_items)
        for _ in range(7):
            pool.append('Arrows (30)' if random.randint(0,3) > 0 else 'Deku Seeds (30)')

    else:
        if world.dungeon_mq['Deku Tree']:
            placed_items['DT MQ Deku Scrub Deku Shield'] = 'Buy Deku Shield'
        if world.dungeon_mq['Dodongos Cavern']:
            placed_items['DC MQ Deku Scrub Deku Sticks'] = 'Buy Deku Stick (1)'
            placed_items['DC MQ Deku Scrub Deku Seeds'] = 'Buy Deku Seeds (30)'
            placed_items['DC MQ Deku Scrub Deku Shield'] = 'Buy Deku Shield'
            placed_items['DC MQ Deku Scrub Red Potion'] = 'Buy Red Potion [30]'
        else:
            placed_items['DC Deku Scrub Deku Nuts'] = 'Buy Deku Nut (5)'
            placed_items['DC Deku Scrub Deku Sticks'] = 'Buy Deku Stick (1)'
            placed_items['DC Deku Scrub Deku Seeds'] = 'Buy Deku Seeds (30)'
            placed_items['DC Deku Scrub Deku Shield'] = 'Buy Deku Shield'
        if not world.dungeon_mq['Jabu Jabus Belly']:
            placed_items['Jabu Deku Scrub Deku Nuts'] = 'Buy Deku Nut (5)'
        if world.dungeon_mq['Ganons Castle']:
            placed_items['GC MQ Deku Scrub Deku Nuts'] = 'Buy Deku Nut (5)'
            placed_items['GC MQ Deku Scrub Bombs'] = 'Buy Bombs (5) [35]'
            placed_items['GC MQ Deku Scrub Arrows'] = 'Buy Arrows (30)'
            placed_items['GC MQ Deku Scrub Red Potion'] = 'Buy Red Potion [30]'
            placed_items['GC MQ Deku Scrub Green Potion'] = 'Buy Green Potion'
        else:
            placed_items['GC Deku Scrub Bombs'] = 'Buy Bombs (5) [35]'
            placed_items['GC Deku Scrub Arrows'] = 'Buy Arrows (30)'
            placed_items['GC Deku Scrub Red Potion'] = 'Buy Red Potion [30]'
            placed_items['GC Deku Scrub Green Potion'] = 'Buy Green Potion'
        placed_items.update(vanilla_deku_scrubs)

    pool.extend(alwaysitems)
    
    if world.dungeon_mq['Deku Tree']:
        pool.extend(DT_MQ)
    else:
        pool.extend(DT_vanilla)
    if world.dungeon_mq['Dodongos Cavern']:
        pool.extend(DC_MQ)
    else:
        pool.extend(DC_vanilla)
    if world.dungeon_mq['Jabu Jabus Belly']:
        pool.extend(JB_MQ)
    if world.dungeon_mq['Forest Temple']:
        pool.extend(FoT_MQ)
    else:
        pool.extend(FoT_vanilla)
    if world.dungeon_mq['Fire Temple']:
        pool.extend(FiT_MQ)
    else:
        pool.extend(FiT_vanilla)
    if world.dungeon_mq['Spirit Temple']:
        pool.extend(SpT_MQ)
    else:
        placed_items['Spirit Temple Nut Crate'] = 'Deku Nut Drop'
        pool.extend(SpT_vanilla)
    if world.dungeon_mq['Shadow Temple']:
        pool.extend(ShT_MQ)
    else:
        pool.extend(ShT_vanilla)
    if not world.dungeon_mq['Bottom of the Well']:
        placed_items['Bottom of the Well Stick Pot'] = 'Deku Stick Drop'
        pool.extend(BW_vanilla)
    if world.dungeon_mq['Gerudo Training Grounds']:
        pool.extend(GTG_MQ)
    else:
        pool.extend(GTG_vanilla)
    if world.dungeon_mq['Ganons Castle']:
        pool.extend(GC_MQ)
    else:
        pool.extend(GC_vanilla)

    for _ in range(normal_bottle_count):
        bottle = random.choice(normal_bottles)
        pool.append(bottle)

    earliest_trade = tradeitemoptions.index(world.logic_earliest_adult_trade)
    latest_trade = tradeitemoptions.index(world.logic_latest_adult_trade)
    if earliest_trade > latest_trade:
        earliest_trade, latest_trade = latest_trade, earliest_trade
    tradeitem = random.choice(tradeitems[earliest_trade:latest_trade+1])
    pool.append(tradeitem)

    pool.extend(songlist)
    if world.start_with_fast_travel:
        pool.remove('Prelude of Light')
        pool.remove('Serenade of Water')
        pool.remove('Farores Wind')
        world.state.collect(ItemFactory('Prelude of Light'))
        world.state.collect(ItemFactory('Serenade of Water'))
        world.state.collect(ItemFactory('Farores Wind'))
        pool.extend(get_junk_item(3))

    if world.shuffle_mapcompass == 'remove' or world.shuffle_mapcompass == 'startwith':
        for item in [item for dungeon in world.dungeons for item in dungeon.dungeon_items]:
            world.state.collect(item)
            pool.extend(get_junk_item())
    if world.shuffle_smallkeys == 'remove':
        for item in [item for dungeon in world.dungeons for item in dungeon.small_keys]:
            world.state.collect(item)
            pool.extend(get_junk_item())
    if world.shuffle_bosskeys == 'remove':
        for item in [item for dungeon in world.dungeons for item in dungeon.boss_key]:
            world.state.collect(item)
            pool.extend(get_junk_item())
    if not world.keysanity and not world.dungeon_mq['Fire Temple']:
        world.state.collect(ItemFactory('Small Key (Fire Temple)'))
    if not world.dungeon_mq['Water Temple']:
        world.state.collect(ItemFactory('Small Key (Water Temple)'))

    if world.item_pool_value == 'plentiful':
        pool.extend(easy_items)
    else:
        pool.extend(normal_items)

    if not world.shuffle_kokiri_sword:
        replace_max_item(pool, 'Kokiri Sword', 0)

    if world.junk_ice_traps == 'off': 
        replace_max_item(pool, 'Ice Trap', 0)
    elif world.junk_ice_traps == 'onslaught':
        for item in [item for item, weight in junk_pool_base] + ['Recovery Heart', 'Bombs (20)', 'Arrows (30)']:
            replace_max_item(pool, item, 0)

    for item,max in item_difficulty_max[world.item_pool_value].items():
        replace_max_item(pool, item, max)

    if world.start_with_wallet:
        replace_max_item(pool, 'Progressive Wallet', 0)
        for i in [1, 2, 3]: # collect wallets
            world.state.collect(ItemFactory('Progressive Wallet'))

    return (pool, placed_items)
Example #17
0
def distribute_items_restrictive(window, worlds, fill_locations=None):
    song_locations = [
        world.get_location(location) for world in worlds for location in [
            'Song from Composers Grave', 'Song from Impa', 'Song from Malon',
            'Song from Saria', 'Song from Ocarina of Time',
            'Song from Windmill', 'Sheik in Forest', 'Sheik at Temple',
            'Sheik in Crater', 'Sheik in Ice Cavern', 'Sheik in Kakariko',
            'Sheik at Colossus'
        ]
    ]

    shop_locations = [
        location for world in worlds
        for location in world.get_unfilled_locations()
        if location.type == 'Shop' and location.price == None
    ]

    # If not passed in, then get a shuffled list of locations to fill in
    if not fill_locations:
        fill_locations = [location for world in worlds for location in world.get_unfilled_locations() \
            if location not in song_locations and \
               location not in shop_locations and \
               location.type != 'GossipStone']
    world_states = [world.state for world in worlds]

    window.locationcount = len(fill_locations) + len(song_locations) + len(
        shop_locations)
    window.fillcount = 0

    # Generate the itempools
    shopitempool = [
        item for world in worlds for item in world.itempool
        if item.type == 'Shop'
    ]
    songitempool = [
        item for world in worlds for item in world.itempool
        if item.type == 'Song'
    ]
    itempool = [
        item for world in worlds for item in world.itempool
        if item.type != 'Shop' and item.type != 'Song'
    ]

    if worlds[0].shuffle_song_items:
        itempool.extend(songitempool)
        fill_locations.extend(song_locations)
        songitempool = []
        song_locations = []

    # add unrestricted dungeon items to main item pool
    itempool.extend([
        item for world in worlds
        for item in world.get_unrestricted_dungeon_items()
    ])
    dungeon_items = [
        item for world in worlds
        for item in world.get_restricted_dungeon_items()
    ]

    random.shuffle(
        itempool
    )  # randomize item placement order. this ordering can greatly affect the location accessibility bias
    progitempool = [item for item in itempool if item.advancement]
    prioitempool = [
        item for item in itempool if not item.advancement and item.priority
    ]
    restitempool = [
        item for item in itempool if not item.advancement and not item.priority
    ]

    cloakable_locations = shop_locations + song_locations + fill_locations
    all_models = shopitempool + dungeon_items + songitempool + itempool
    worlds[0].settings.distribution.fill(
        window, worlds, [shop_locations, song_locations, fill_locations], [
            shopitempool, dungeon_items, songitempool, progitempool,
            prioitempool, restitempool
        ])
    itempool = progitempool + prioitempool + restitempool

    # set ice traps to have the appearance of other random items in the item pool
    ice_traps = [item for item in itempool if item.name == 'Ice Trap']
    # Extend with ice traps manually placed in plandomizer
    ice_traps.extend(
        location.item for location in cloakable_locations
        if (location.name in location_groups['CanSee']
            and location.item is not None and location.item.name == 'Ice Trap'
            and location.item.looks_like_item is None))
    junk_items = remove_junk_items.copy()
    junk_items.remove('Ice Trap')
    major_items = [
        item for (item, data) in item_table.items()
        if data[0] == 'Item' and data[1] and data[2] is not None
    ]
    fake_items = []
    if worlds[0].settings.ice_trap_appearance == 'major_only':
        model_items = [item for item in itempool if item.majoritem]
        if len(
                model_items
        ) == 0:  # All major items were somehow removed from the pool (can happen in plando)
            model_items = ItemFactory(major_items)
    elif worlds[0].settings.ice_trap_appearance == 'junk_only':
        model_items = [item for item in itempool if item.name in junk_items]
        if len(model_items) == 0:  # All junk was removed
            model_items = ItemFactory(junk_items)
    else:  # world[0].settings.ice_trap_appearance == 'anything':
        model_items = [item for item in itempool if item.name != 'Ice Trap']
        if len(
                model_items
        ) == 0:  # All major items and junk were somehow removed from the pool (can happen in plando)
            model_items = ItemFactory(major_items) + ItemFactory(junk_items)
    while len(ice_traps) > len(fake_items):
        # if there are more ice traps than model items, then double up on model items
        fake_items.extend(model_items)
    for random_item in random.sample(fake_items, len(ice_traps)):
        ice_trap = ice_traps.pop(0)
        ice_trap.looks_like_item = random_item

    # Start a search cache here.
    search = Search([world.state for world in worlds])

    # We place all the shop items first. Like songs, they have a more limited
    # set of locations that they can be placed in, so placing them first will
    # reduce the odds of creating unbeatable seeds. This also avoids needing
    # to create item rules for every location for whether they are a shop item
    # or not. This shouldn't have much affect on item bias.
    if shop_locations:
        logger.info('Placing shop items.')
        fill_ownworld_restrictive(window, worlds, search, shop_locations,
                                  shopitempool,
                                  itempool + songitempool + dungeon_items,
                                  "shop")
    # Update the shop item access rules
    for world in worlds:
        set_shop_rules(world)

    search.collect_locations()

    # If there are dungeon items that are restricted to their original dungeon,
    # we must place them first to make sure that there is always a location to
    # place them. This could probably be replaced for more intelligent item
    # placement, but will leave as is for now
    if dungeon_items:
        logger.info('Placing dungeon items.')
        fill_dungeons_restrictive(window, worlds, search, fill_locations,
                                  dungeon_items, itempool + songitempool)
        search.collect_locations()

    # places the songs into the world
    # Currently places songs only at song locations. if there's an option
    # to allow at other locations then they should be in the main pool.
    # Placing songs on their own since they have a relatively high chance
    # of failing compared to other item type. So this way we only have retry
    # the song locations only.
    if not worlds[0].shuffle_song_items:
        logger.info('Placing song items.')
        fill_ownworld_restrictive(window, worlds, search, song_locations,
                                  songitempool, progitempool, "song")
        search.collect_locations()
        fill_locations += [
            location for location in song_locations if location.item is None
        ]

    # Put one item in every dungeon, needs to be done before other items are
    # placed to ensure there is a spot available for them
    if worlds[0].one_item_per_dungeon:
        logger.info('Placing one major item per dungeon.')
        fill_dungeon_unique_item(window, worlds, search, fill_locations,
                                 progitempool)
        search.collect_locations()

    # Place all progression items. This will include keys in keysanity.
    # Items in this group will check for reachability and will be placed
    # such that the game is guaranteed beatable.
    logger.info('Placing progression items.')
    fill_restrictive(window, worlds, search, fill_locations, progitempool)
    search.collect_locations()

    # Place all priority items.
    # These items are items that only check if the item is allowed to be
    # placed in the location, not checking reachability. This is important
    # for things like Ice Traps that can't be found at some locations
    logger.info('Placing priority items.')
    fill_restrictive_fast(window, worlds, fill_locations, prioitempool)

    # Place the rest of the items.
    # No restrictions at all. Places them completely randomly. Since they
    # cannot affect the beatability, we don't need to check them
    logger.info('Placing the rest of the items.')
    fast_fill(window, fill_locations, restitempool)

    # Log unplaced item/location warnings
    for item in progitempool + prioitempool + restitempool:
        logger.error('Unplaced Items: %s [World %d]' %
                     (item.name, item.world.id))
    for location in fill_locations:
        logger.error('Unfilled Locations: %s [World %d]' %
                     (location.name, location.world.id))

    if progitempool + prioitempool + restitempool:
        raise FillError('Not all items are placed.')

    if fill_locations:
        raise FillError('Not all locations have an item.')

    if not search.can_beat_game():
        raise FillError('Cannot beat game!')

    worlds[0].settings.distribution.cloak(worlds, [cloakable_locations],
                                          [all_models])

    for world in worlds:
        for location in world.get_filled_locations():
            # Get the maximum amount of wallets required to purchase an advancement item.
            if world.maximum_wallets < 3 and location.price and location.item.advancement:
                if location.price > 500:
                    world.maximum_wallets = 3
                elif world.maximum_wallets < 2 and location.price > 200:
                    world.maximum_wallets = 2
                elif world.maximum_wallets < 1 and location.price > 99:
                    world.maximum_wallets = 1

            # Get Light Arrow location for later usage.
            if location.item and location.item.name == 'Light Arrows':
                location.item.world.light_arrow_location = location
Example #18
0
def get_pool_core(world):
    pool = []
    placed_items = {}

    if world.shuffle_kokiri_sword:
        pool.append('Kokiri Sword')
    else:
        placed_items['Kokiri Sword Chest'] = 'Kokiri Sword'

    ruto_bottles = 1
    if world.zora_fountain == 'open':
        ruto_bottles = 0
    elif world.item_pool_value == 'plentiful':
        ruto_bottles += 1

    if world.shuffle_weird_egg:
        pool.append('Weird Egg')
    else:
        placed_items['Malon Egg'] = 'Weird Egg'

    if world.shuffle_ocarinas:
        pool.extend(['Ocarina'] * 2)
    else:
        placed_items['Gift from Saria'] = 'Ocarina'
        placed_items['Ocarina of Time'] = 'Ocarina'

    if world.shuffle_cows:
        pool.extend(get_junk_item(10 if world.dungeon_mq['Jabu Jabus Belly'] else 9))
    else:
        placed_items['LLR Stables Left Cow'] = 'Milk'
        placed_items['LLR Stables Right Cow'] = 'Milk'
        placed_items['LLR Tower Left Cow'] = 'Milk'
        placed_items['LLR Tower Right Cow'] = 'Milk'
        placed_items['Links House Cow'] = 'Milk'
        placed_items['Impas House Cow'] = 'Milk'
        placed_items['Gerudo Valley Cow'] = 'Milk'
        placed_items['DMT Grotto Cow'] = 'Milk'
        placed_items['HF Grotto Cow'] = 'Milk'
        if world.dungeon_mq['Jabu Jabus Belly']:
            placed_items['Jabu Jabus Belly MQ Cow'] = 'Milk'

    if world.shuffle_beans:
        if world.distribution.get_starting_item('Magic Bean') < 10:
            pool.append('Magic Bean Pack')
        else:
            pool.extend(get_junk_item())
    else:
        placed_items['Magic Bean Salesman'] = 'Magic Bean'

    if world.dungeon_mq['Deku Tree']:
        skulltula_locations_final = skulltula_locations + [
            'GS Deku Tree MQ Lobby',
            'GS Deku Tree MQ Compass Room',
            'GS Deku Tree MQ Basement Ceiling',
            'GS Deku Tree MQ Basement Back Room']
    else:
        skulltula_locations_final = skulltula_locations + [
            'GS Deku Tree Compass Room',
            'GS Deku Tree Basement Vines',
            'GS Deku Tree Basement Gate',
            'GS Deku Tree Basement Back Room']
    if world.dungeon_mq['Dodongos Cavern']:
        skulltula_locations_final.extend([
            'GS Dodongo\'s Cavern MQ Scrub Room',
            'GS Dodongo\'s Cavern MQ Song of Time Block Room',
            'GS Dodongo\'s Cavern MQ Lizalfos Room',
            'GS Dodongo\'s Cavern MQ Larva Room',
            'GS Dodongo\'s Cavern MQ Back Area'])
    else:
        skulltula_locations_final.extend([
            'GS Dodongo\'s Cavern East Side Room',
            'GS Dodongo\'s Cavern Vines Above Stairs',
            'GS Dodongo\'s Cavern Back Room',
            'GS Dodongo\'s Cavern Alcove Above Stairs',
            'GS Dodongo\'s Cavern Scarecrow'])
    if world.dungeon_mq['Jabu Jabus Belly']:
        skulltula_locations_final.extend([
            'GS Jabu Jabu MQ Tailpasaran Room',
            'GS Jabu Jabu MQ Invisible Enemies Room',
            'GS Jabu Jabu MQ Boomerang Room',
            'GS Jabu Jabu MQ Near Boss'])
    else:
        skulltula_locations_final.extend([
            'GS Jabu Jabu Water Switch Room',
            'GS Jabu Jabu Lobby Basement Lower',
            'GS Jabu Jabu Lobby Basement Upper',
            'GS Jabu Jabu Near Boss'])
    if world.dungeon_mq['Forest Temple']:
        skulltula_locations_final.extend([
            'GS Forest Temple MQ First Hallway',
            'GS Forest Temple MQ Block Push Room',
            'GS Forest Temple MQ Outdoor East',
            'GS Forest Temple MQ Outdoor West',
            'GS Forest Temple MQ Well'])
    else:
        skulltula_locations_final.extend([
            'GS Forest Temple First Room',
            'GS Forest Temple Lobby',
            'GS Forest Temple Outdoor East',
            'GS Forest Temple Outdoor West',
            'GS Forest Temple Basement'])
    if world.dungeon_mq['Fire Temple']:
        skulltula_locations_final.extend([
            'GS Fire Temple MQ Above Fire Wall Maze',
            'GS Fire Temple MQ Fire Wall Maze Center',
            'GS Fire Temple MQ Big Lava Room',
            'GS Fire Temple MQ Fire Wall Maze Side Room',
            'GS Fire Temple MQ East Tower Top'])
    else:
        skulltula_locations_final.extend([
            'GS Fire Temple Song of Time Room',
            'GS Fire Temple Unmarked Bomb Wall',
            'GS Fire Temple East Tower Climb',
            'GS Fire Temple East Tower Top',
            'GS Fire Temple Basement'])
    if world.dungeon_mq['Water Temple']:
        skulltula_locations_final.extend([
            'GS Water Temple MQ Before Upper Water Switch',
            'GS Water Temple MQ North Basement',
            'GS Water Temple MQ Lizalfos Hallway',
            'GS Water Temple MQ Serpent River',
            'GS Water Temple MQ South Basement'])
    else:
        skulltula_locations_final.extend([
            'GS Water Temple South Basement',
            'GS Water Temple Serpent River',
            'GS Water Temple Falling Platform Room',
            'GS Water Temple Central Room',
            'GS Water Temple Near Boss Key Chest'])
    if world.dungeon_mq['Spirit Temple']:
        skulltula_locations_final.extend([
            'GS Spirit Temple MQ Lower Adult Right',
            'GS Spirit Temple MQ Lower Adult Left',
            'GS Spirit Temple MQ Iron Knuckle West',
            'GS Spirit Temple MQ Iron Knuckle North',
            'GS Spirit Temple MQ Sun Block Room'])
    else:
        skulltula_locations_final.extend([
            'GS Spirit Temple Metal Fence',
            'GS Spirit Temple Bomb for Light Room',
            'GS Spirit Temple Hall to West Iron Knuckle',
            'GS Spirit Temple Boulder Room',
            'GS Spirit Temple Lobby'])
    if world.dungeon_mq['Shadow Temple']:
        skulltula_locations_final.extend([
            'GS Shadow Temple MQ Crusher Room',
            'GS Shadow Temple MQ Wind Hint Room',
            'GS Shadow Temple MQ After Wind',
            'GS Shadow Temple MQ After Ship',
            'GS Shadow Temple MQ Near Boss'])
    else:
        skulltula_locations_final.extend([
            'GS Shadow Temple Like Like Room',
            'GS Shadow Temple Crusher Room',
            'GS Shadow Temple Single Giant Pot',
            'GS Shadow Temple Near Ship',
            'GS Shadow Temple Triple Giant Pot'])
    if world.dungeon_mq['Bottom of the Well']:
        skulltula_locations_final.extend([
            'GS Well MQ Basement',
            'GS Well MQ Coffin Room',
            'GS Well MQ West Inner Room'])
    else:
        skulltula_locations_final.extend([
            'GS Well West Inner Room',
            'GS Well East Inner Room',
            'GS Well Like Like Cage'])
    if world.dungeon_mq['Ice Cavern']:
        skulltula_locations_final.extend([
            'GS Ice Cavern MQ Scarecrow',
            'GS Ice Cavern MQ Ice Block',
            'GS Ice Cavern MQ Red Ice'])
    else:
        skulltula_locations_final.extend([
            'GS Ice Cavern Spinning Scythe Room',
            'GS Ice Cavern Heart Piece Room',
            'GS Ice Cavern Push Block Room'])
    if world.tokensanity == 'off':
        for location in skulltula_locations_final:
            placed_items[location] = 'Gold Skulltula Token'
    elif world.tokensanity == 'dungeons':
        for location in skulltula_locations_final:
            if world.get_location(location).scene >= 0x0A:
                placed_items[location] = 'Gold Skulltula Token'
            else:
                pool.append('Gold Skulltula Token')
    elif world.tokensanity == 'overworld':
        for location in skulltula_locations_final:
            if world.get_location(location).scene < 0x0A:
                placed_items[location] = 'Gold Skulltula Token'
            else:
                pool.append('Gold Skulltula Token')
    else:
        pool.extend(['Gold Skulltula Token'] * 100)


    if world.bombchus_in_logic:
        pool.extend(['Bombchus'] * 4)
        if world.dungeon_mq['Jabu Jabus Belly']:
            pool.extend(['Bombchus'])
        if world.dungeon_mq['Spirit Temple']:
            pool.extend(['Bombchus'] * 2)
        if not world.dungeon_mq['Bottom of the Well']:
            pool.extend(['Bombchus'])
        if world.dungeon_mq['Gerudo Training Grounds']:
            pool.extend(['Bombchus'])

    else:
        pool.extend(['Bombchus (5)'] + ['Bombchus (10)'] * 2)
        if world.dungeon_mq['Jabu Jabus Belly']:
                pool.extend(['Bombchus (10)'])
        if world.dungeon_mq['Spirit Temple']:
                pool.extend(['Bombchus (10)'] * 2)
        if not world.dungeon_mq['Bottom of the Well']:
                pool.extend(['Bombchus (10)'])
        if world.dungeon_mq['Gerudo Training Grounds']:
                pool.extend(['Bombchus (10)'])
        if world.dungeon_mq['Ganons Castle']:
            pool.extend(['Bombchus (10)'])
        else:
            pool.extend(['Bombchus (20)'])

    pool.extend(['Ice Trap'])
    if not world.dungeon_mq['Gerudo Training Grounds']:
        pool.extend(['Ice Trap'])
    if not world.dungeon_mq['Ganons Castle']:
        pool.extend(['Ice Trap'] * 4)

    if world.gerudo_fortress == 'open':
        placed_items['Gerudo Fortress North F1 Carpenter'] = 'Recovery Heart'
        placed_items['Gerudo Fortress North F2 Carpenter'] = 'Recovery Heart'
        placed_items['Gerudo Fortress South F1 Carpenter'] = 'Recovery Heart'
        placed_items['Gerudo Fortress South F2 Carpenter'] = 'Recovery Heart'
    elif world.shuffle_smallkeys == 'keysanity':
        if world.gerudo_fortress == 'fast':
            pool.append('Small Key (Gerudo Fortress)')
            placed_items['Gerudo Fortress North F2 Carpenter'] = 'Recovery Heart'
            placed_items['Gerudo Fortress South F1 Carpenter'] = 'Recovery Heart'
            placed_items['Gerudo Fortress South F2 Carpenter'] = 'Recovery Heart'
        else:
            pool.extend(['Small Key (Gerudo Fortress)'] * 4)
    else:
        if world.gerudo_fortress == 'fast':
            placed_items['Gerudo Fortress North F1 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items['Gerudo Fortress North F2 Carpenter'] = 'Recovery Heart'
            placed_items['Gerudo Fortress South F1 Carpenter'] = 'Recovery Heart'
            placed_items['Gerudo Fortress South F2 Carpenter'] = 'Recovery Heart'
        else:
            placed_items['Gerudo Fortress North F1 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items['Gerudo Fortress North F2 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items['Gerudo Fortress South F1 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items['Gerudo Fortress South F2 Carpenter'] = 'Small Key (Gerudo Fortress)'

    if world.shuffle_gerudo_card and world.gerudo_fortress != 'open':
        pool.append('Gerudo Membership Card')
    elif world.shuffle_gerudo_card:
        pending_junk_pool.append('Gerudo Membership Card')
        placed_items['Gerudo Fortress Membership Card'] = 'Ice Trap'
    else:
        placed_items['Gerudo Fortress Membership Card'] = 'Gerudo Membership Card'

    if world.shopsanity == 'off':
        placed_items.update(vanilla_shop_items)
        if world.bombchus_in_logic:
            placed_items['Kokiri Shop Item 8'] = 'Buy Bombchu (5)'
            placed_items['Castle Town Bazaar Item 4'] = 'Buy Bombchu (5)'
            placed_items['Kakariko Bazaar Item 4'] = 'Buy Bombchu (5)'
        pool.extend(normal_rupees)

    else:
        remain_shop_items = list(vanilla_shop_items.values())
        pool.extend(min_shop_items)
        for item in min_shop_items:
            remain_shop_items.remove(item)

        shop_slots_count = len(remain_shop_items)
        shop_nonitem_count = len(world.shop_prices)
        shop_item_count = shop_slots_count - shop_nonitem_count

        pool.extend(random.sample(remain_shop_items, shop_item_count))
        if shop_nonitem_count:
            pool.extend(get_junk_item(shop_nonitem_count))
        if world.shopsanity == '0':
            pool.extend(normal_rupees)
        else:
            pool.extend(shopsanity_rupees)

    if world.shuffle_scrubs != 'off':
        if world.dungeon_mq['Deku Tree']:
            pool.append('Deku Shield')
        if world.dungeon_mq['Dodongos Cavern']:
            pool.extend(['Deku Stick (1)', 'Deku Shield', 'Recovery Heart'])
        else:
            pool.extend(['Deku Nuts (5)', 'Deku Stick (1)', 'Deku Shield'])
        if not world.dungeon_mq['Jabu Jabus Belly']:
            pool.append('Deku Nuts (5)')
        if world.dungeon_mq['Ganons Castle']:
            pool.extend(['Bombs (5)', 'Recovery Heart', 'Rupees (5)', 'Deku Nuts (5)'])
        else:
            pool.extend(['Bombs (5)', 'Recovery Heart', 'Rupees (5)'])
        pool.extend(deku_scrubs_items)
        for _ in range(7):
            pool.append('Arrows (30)' if random.randint(0,3) > 0 else 'Deku Seeds (30)')

    else:
        if world.dungeon_mq['Deku Tree']:
            placed_items['DT MQ Deku Scrub Deku Shield'] = 'Buy Deku Shield'
        if world.dungeon_mq['Dodongos Cavern']:
            placed_items['DC MQ Deku Scrub Deku Sticks'] = 'Buy Deku Stick (1)'
            placed_items['DC MQ Deku Scrub Deku Seeds'] = 'Buy Deku Seeds (30)'
            placed_items['DC MQ Deku Scrub Deku Shield'] = 'Buy Deku Shield'
            placed_items['DC MQ Deku Scrub Red Potion'] = 'Buy Red Potion [30]'
        else:
            placed_items['DC Deku Scrub Deku Nuts'] = 'Buy Deku Nut (5)'
            placed_items['DC Deku Scrub Deku Sticks'] = 'Buy Deku Stick (1)'
            placed_items['DC Deku Scrub Deku Seeds'] = 'Buy Deku Seeds (30)'
            placed_items['DC Deku Scrub Deku Shield'] = 'Buy Deku Shield'
        if not world.dungeon_mq['Jabu Jabus Belly']:
            placed_items['Jabu Deku Scrub Deku Nuts'] = 'Buy Deku Nut (5)'
        if world.dungeon_mq['Ganons Castle']:
            placed_items['GC MQ Deku Scrub Deku Nuts'] = 'Buy Deku Nut (5)'
            placed_items['GC MQ Deku Scrub Bombs'] = 'Buy Bombs (5) [35]'
            placed_items['GC MQ Deku Scrub Arrows'] = 'Buy Arrows (30)'
            placed_items['GC MQ Deku Scrub Red Potion'] = 'Buy Red Potion [30]'
            placed_items['GC MQ Deku Scrub Green Potion'] = 'Buy Green Potion'
        else:
            placed_items['GC Deku Scrub Bombs'] = 'Buy Bombs (5) [35]'
            placed_items['GC Deku Scrub Arrows'] = 'Buy Arrows (30)'
            placed_items['GC Deku Scrub Red Potion'] = 'Buy Red Potion [30]'
            placed_items['GC Deku Scrub Green Potion'] = 'Buy Green Potion'
        placed_items.update(vanilla_deku_scrubs)

    pool.extend(alwaysitems)
    
    if world.dungeon_mq['Deku Tree']:
        pool.extend(DT_MQ)
    else:
        pool.extend(DT_vanilla)
    if world.dungeon_mq['Dodongos Cavern']:
        pool.extend(DC_MQ)
    else:
        pool.extend(DC_vanilla)
    if world.dungeon_mq['Jabu Jabus Belly']:
        pool.extend(JB_MQ)
    if world.dungeon_mq['Forest Temple']:
        pool.extend(FoT_MQ)
    else:
        pool.extend(FoT_vanilla)
    if world.dungeon_mq['Fire Temple']:
        pool.extend(FiT_MQ)
    else:
        pool.extend(FiT_vanilla)
    if world.dungeon_mq['Spirit Temple']:
        pool.extend(SpT_MQ)
    else:
        pool.extend(SpT_vanilla)
    if world.dungeon_mq['Shadow Temple']:
        pool.extend(ShT_MQ)
    else:
        pool.extend(ShT_vanilla)
    if not world.dungeon_mq['Bottom of the Well']:
        pool.extend(BW_vanilla)
    if world.dungeon_mq['Gerudo Training Grounds']:
        pool.extend(GTG_MQ)
    else:
        pool.extend(GTG_vanilla)
    if world.dungeon_mq['Ganons Castle']:
        pool.extend(GC_MQ)
    else:
        pool.extend(GC_vanilla)

    for i in range(bottle_count):
        if i >= ruto_bottles:
            bottle = random.choice(normal_bottles)
            pool.append(bottle)
        else:
            pool.append('Bottle with Letter')

    earliest_trade = tradeitemoptions.index(world.logic_earliest_adult_trade)
    latest_trade = tradeitemoptions.index(world.logic_latest_adult_trade)
    if earliest_trade > latest_trade:
        earliest_trade, latest_trade = latest_trade, earliest_trade
    tradeitem = random.choice(tradeitems[earliest_trade:latest_trade+1])
    world.selected_adult_trade_item = tradeitem
    pool.append(tradeitem)

    pool.extend(songlist)
    if world.free_scarecrow:
        world.state.collect(ItemFactory('Scarecrow Song'))
    
    if world.no_epona_race:
        world.state.collect(ItemFactory('Epona', event=True))

    if world.shuffle_mapcompass == 'remove' or world.shuffle_mapcompass == 'startwith':
        for item in [item for dungeon in world.dungeons for item in dungeon.dungeon_items]:
            world.state.collect(item)
            pool.extend(get_junk_item())
    if world.shuffle_smallkeys == 'remove':
        for item in [item for dungeon in world.dungeons for item in dungeon.small_keys]:
            world.state.collect(item)
            pool.extend(get_junk_item())
    if world.shuffle_bosskeys == 'remove':
        for item in [item for dungeon in world.dungeons if dungeon.name != 'Ganons Castle' for item in dungeon.boss_key]:
            world.state.collect(item)
            pool.extend(get_junk_item())
    if world.shuffle_ganon_bosskey in ['remove', 'triforce']:
        for item in [item for dungeon in world.dungeons if dungeon.name == 'Ganons Castle' for item in dungeon.boss_key]:
            world.state.collect(item)
            pool.extend(get_junk_item())

    if world.shuffle_mapcompass == 'vanilla':
        for location, item in vanillaMC.items():
            try:
                world.get_location(location)
                placed_items[location] = item
            except KeyError:
                continue
    if world.shuffle_smallkeys == 'vanilla':
        for location, item in vanillaSK.items():
            try:
                world.get_location(location)
                placed_items[location] = item
            except KeyError:
                continue
        # Logic cannot handle vanilla key layout in some dungeons
        # this is because vanilla expects the dungeon major item to be
        # locked behind the keys, which is not always true in rando.
        # We can resolve this by starting with some extra keys
        if world.dungeon_mq['Spirit Temple']:
            # Yes somehow you need 3 keys. This dungeon is bonkers
            world.state.collect(ItemFactory('Small Key (Spirit Temple)'))
            world.state.collect(ItemFactory('Small Key (Spirit Temple)'))
            world.state.collect(ItemFactory('Small Key (Spirit Temple)'))
        #if not world.dungeon_mq['Fire Temple']:
        #    world.state.collect(ItemFactory('Small Key (Fire Temple)'))
    if world.shuffle_bosskeys == 'vanilla':
        for location, item in vanillaBK.items():
            try:
                world.get_location(location)
                placed_items[location] = item
            except KeyError:
                continue


    if not world.keysanity and not world.dungeon_mq['Fire Temple']:
        world.state.collect(ItemFactory('Small Key (Fire Temple)'))
    if not world.dungeon_mq['Water Temple']:
        world.state.collect(ItemFactory('Small Key (Water Temple)'))

    if world.triforce_hunt:
        trifroce_count = int(world.triforce_goal_per_world * TriforceCounts[world.item_pool_value])
        pending_junk_pool.extend(['Triforce Piece'] * trifroce_count)

    if world.shuffle_ganon_bosskey in ['lacs_vanilla', 'lacs_medallions', 'lacs_stones', 'lacs_dungeons']:
        placed_items['Zelda'] = 'Boss Key (Ganons Castle)'
    elif world.shuffle_ganon_bosskey == 'vanilla':
        placed_items['Ganons Tower Boss Key Chest'] = 'Boss Key (Ganons Castle)'

    if world.item_pool_value == 'plentiful':
        pool.extend(easy_items)
    else:
        pool.extend(normal_items)

    if not world.shuffle_kokiri_sword:
        replace_max_item(pool, 'Kokiri Sword', 0)

    if world.junk_ice_traps == 'off': 
        replace_max_item(pool, 'Ice Trap', 0)
    elif world.junk_ice_traps == 'onslaught':
        for item in [item for item, weight in junk_pool_base] + ['Recovery Heart', 'Bombs (20)', 'Arrows (30)']:
            replace_max_item(pool, item, 0)

    for item,max in item_difficulty_max[world.item_pool_value].items():
        replace_max_item(pool, item, max)

    # Make sure our pending_junk_pool is empty. If not, remove some random junk here.
    if pending_junk_pool:
        remove_junk_pool, _ = zip(*junk_pool_base)
        remove_junk_pool = list(remove_junk_pool) + ['Recovery Heart', 'Bombs (20)', 'Arrows (30)', 'Ice Trap']

        junk_candidates = [item for item in pool if item in remove_junk_pool]
        while pending_junk_pool:
            pending_item = pending_junk_pool.pop()
            if not junk_candidates:
                raise RuntimeError("Not enough junk exists in item pool for %s to be added." % pending_item)
            junk_item = random.choice(junk_candidates)
            junk_candidates.remove(junk_item)
            pool.remove(junk_item)
            pool.append(pending_item)

    world.distribution.alter_pool(world, pool)

    world.distribution.configure_starting_items_settings(world)
    world.distribution.collect_starters(world.state)

    return (pool, placed_items)
Example #19
0
def get_pool_core(world):
    pool = []
    placed_items = {}

    if world.shuffle_kokiri_sword:
        pool.append('Kokiri Sword')
    else:
        placed_items['KF Kokiri Sword Chest'] = 'Kokiri Sword'

    ruto_bottles = 1
    if world.zora_fountain == 'open':
        ruto_bottles = 0
    elif world.item_pool_value == 'plentiful':
        ruto_bottles += 1

    if world.shuffle_weird_egg:
        pool.append('Weird Egg')
    else:
        placed_items['HC Malon Egg'] = 'Weird Egg'

    if world.shuffle_ocarinas:
        pool.extend(['Ocarina'] * 2)
    else:
        placed_items['LW Gift from Saria'] = 'Ocarina'
        placed_items['HF Ocarina of Time Item'] = 'Ocarina'

    if world.shuffle_cows:
        pool.extend(
            get_junk_item(10 if world.dungeon_mq['Jabu Jabus Belly'] else 9))
    else:
        placed_items['LLR Stables Left Cow'] = 'Milk'
        placed_items['LLR Stables Right Cow'] = 'Milk'
        placed_items['LLR Tower Left Cow'] = 'Milk'
        placed_items['LLR Tower Right Cow'] = 'Milk'
        placed_items['KF Links House Cow'] = 'Milk'
        placed_items['Kak Impas House Cow'] = 'Milk'
        placed_items['GV Cow'] = 'Milk'
        placed_items['DMT Cow Grotto Cow'] = 'Milk'
        placed_items['HF Cow Grotto Cow'] = 'Milk'
        if world.dungeon_mq['Jabu Jabus Belly']:
            placed_items['Jabu Jabus Belly MQ Cow'] = 'Milk'

    if world.shuffle_beans:
        if world.distribution.get_starting_item('Magic Bean') < 10:
            pool.append('Magic Bean Pack')
        else:
            pool.extend(get_junk_item())
    else:
        placed_items['ZR Magic Bean Salesman'] = 'Magic Bean'

    if world.dungeon_mq['Deku Tree']:
        skulltula_locations_final = skulltula_locations + [
            'Deku Tree MQ GS Lobby', 'Deku Tree MQ GS Compass Room',
            'Deku Tree MQ GS Basement Graves Room',
            'Deku Tree MQ GS Basement Back Room'
        ]
    else:
        skulltula_locations_final = skulltula_locations + [
            'Deku Tree GS Compass Room', 'Deku Tree GS Basement Vines',
            'Deku Tree GS Basement Gate', 'Deku Tree GS Basement Back Room'
        ]
    if world.dungeon_mq['Dodongos Cavern']:
        skulltula_locations_final.extend([
            'Dodongos Cavern MQ GS Scrub Room',
            'Dodongos Cavern MQ GS Song of Time Block Room',
            'Dodongos Cavern MQ GS Lizalfos Room',
            'Dodongos Cavern MQ GS Larvae Room',
            'Dodongos Cavern MQ GS Back Area'
        ])
    else:
        skulltula_locations_final.extend([
            'Dodongos Cavern GS Side Room Near Lower Lizalfos',
            'Dodongos Cavern GS Vines Above Stairs',
            'Dodongos Cavern GS Back Room',
            'Dodongos Cavern GS Alcove Above Stairs',
            'Dodongos Cavern GS Scarecrow'
        ])
    if world.dungeon_mq['Jabu Jabus Belly']:
        skulltula_locations_final.extend([
            'Jabu Jabus Belly MQ GS Tailpasaran Room',
            'Jabu Jabus Belly MQ GS Invisible Enemies Room',
            'Jabu Jabus Belly MQ GS Boomerang Chest Room',
            'Jabu Jabus Belly MQ GS Near Boss'
        ])
    else:
        skulltula_locations_final.extend([
            'Jabu Jabus Belly GS Water Switch Room',
            'Jabu Jabus Belly GS Lobby Basement Lower',
            'Jabu Jabus Belly GS Lobby Basement Upper',
            'Jabu Jabus Belly GS Near Boss'
        ])
    if world.dungeon_mq['Forest Temple']:
        skulltula_locations_final.extend([
            'Forest Temple MQ GS First Hallway',
            'Forest Temple MQ GS Block Push Room',
            'Forest Temple MQ GS Raised Island Courtyard',
            'Forest Temple MQ GS Level Island Courtyard',
            'Forest Temple MQ GS Well'
        ])
    else:
        skulltula_locations_final.extend([
            'Forest Temple GS First Room', 'Forest Temple GS Lobby',
            'Forest Temple GS Raised Island Courtyard',
            'Forest Temple GS Level Island Courtyard',
            'Forest Temple GS Basement'
        ])
    if world.dungeon_mq['Fire Temple']:
        skulltula_locations_final.extend([
            'Fire Temple MQ GS Above Fire Wall Maze',
            'Fire Temple MQ GS Fire Wall Maze Center',
            'Fire Temple MQ GS Big Lava Room Open Door',
            'Fire Temple MQ GS Fire Wall Maze Side Room',
            'Fire Temple MQ GS Skull On Fire'
        ])
    else:
        skulltula_locations_final.extend([
            'Fire Temple GS Song of Time Room', 'Fire Temple GS Boulder Maze',
            'Fire Temple GS Scarecrow Climb', 'Fire Temple GS Scarecrow Top',
            'Fire Temple GS Boss Key Loop'
        ])
    if world.dungeon_mq['Water Temple']:
        skulltula_locations_final.extend([
            'Water Temple MQ GS Before Upper Water Switch',
            'Water Temple MQ GS Freestanding Key Area',
            'Water Temple MQ GS Lizalfos Hallway', 'Water Temple MQ GS River',
            'Water Temple MQ GS Triple Wall Torch'
        ])
    else:
        skulltula_locations_final.extend([
            'Water Temple GS Behind Gate', 'Water Temple GS River',
            'Water Temple GS Falling Platform Room',
            'Water Temple GS Central Pillar',
            'Water Temple GS Near Boss Key Chest'
        ])
    if world.dungeon_mq['Spirit Temple']:
        skulltula_locations_final.extend([
            'Spirit Temple MQ GS Symphony Room',
            'Spirit Temple MQ GS Leever Room',
            'Spirit Temple MQ GS Nine Thrones Room West',
            'Spirit Temple MQ GS Nine Thrones Room North',
            'Spirit Temple MQ GS Sun Block Room'
        ])
    else:
        skulltula_locations_final.extend([
            'Spirit Temple GS Metal Fence',
            'Spirit Temple GS Sun on Floor Room',
            'Spirit Temple GS Hall After Sun Block Room',
            'Spirit Temple GS Boulder Room', 'Spirit Temple GS Lobby'
        ])
    if world.dungeon_mq['Shadow Temple']:
        skulltula_locations_final.extend([
            'Shadow Temple MQ GS Falling Spikes Room',
            'Shadow Temple MQ GS Wind Hint Room',
            'Shadow Temple MQ GS After Wind', 'Shadow Temple MQ GS After Ship',
            'Shadow Temple MQ GS Near Boss'
        ])
    else:
        skulltula_locations_final.extend([
            'Shadow Temple GS Like Like Room',
            'Shadow Temple GS Falling Spikes Room',
            'Shadow Temple GS Single Giant Pot', 'Shadow Temple GS Near Ship',
            'Shadow Temple GS Triple Giant Pot'
        ])
    if world.dungeon_mq['Bottom of the Well']:
        skulltula_locations_final.extend([
            'Bottom of the Well MQ GS Basement',
            'Bottom of the Well MQ GS Coffin Room',
            'Bottom of the Well MQ GS West Inner Room'
        ])
    else:
        skulltula_locations_final.extend([
            'Bottom of the Well GS West Inner Room',
            'Bottom of the Well GS East Inner Room',
            'Bottom of the Well GS Like Like Cage'
        ])
    if world.dungeon_mq['Ice Cavern']:
        skulltula_locations_final.extend([
            'Ice Cavern MQ GS Scarecrow', 'Ice Cavern MQ GS Ice Block',
            'Ice Cavern MQ GS Red Ice'
        ])
    else:
        skulltula_locations_final.extend([
            'Ice Cavern GS Spinning Scythe Room',
            'Ice Cavern GS Heart Piece Room', 'Ice Cavern GS Push Block Room'
        ])
    if world.tokensanity == 'off':
        for location in skulltula_locations_final:
            placed_items[location] = 'Gold Skulltula Token'
    elif world.tokensanity == 'dungeons':
        for location in skulltula_locations_final:
            if world.get_location(location).scene >= 0x0A:
                placed_items[location] = 'Gold Skulltula Token'
            else:
                pool.append('Gold Skulltula Token')
    elif world.tokensanity == 'overworld':
        for location in skulltula_locations_final:
            if world.get_location(location).scene < 0x0A:
                placed_items[location] = 'Gold Skulltula Token'
            else:
                pool.append('Gold Skulltula Token')
    else:
        pool.extend(['Gold Skulltula Token'] * 100)

    if world.bombchus_in_logic:
        pool.extend(['Bombchus'] * 4)
        if world.dungeon_mq['Jabu Jabus Belly']:
            pool.extend(['Bombchus'])
        if world.dungeon_mq['Spirit Temple']:
            pool.extend(['Bombchus'] * 2)
        if not world.dungeon_mq['Bottom of the Well']:
            pool.extend(['Bombchus'])
        if world.dungeon_mq['Gerudo Training Grounds']:
            pool.extend(['Bombchus'])

    else:
        pool.extend(['Bombchus (5)'] + ['Bombchus (10)'] * 2)
        if world.dungeon_mq['Jabu Jabus Belly']:
            pool.extend(['Bombchus (10)'])
        if world.dungeon_mq['Spirit Temple']:
            pool.extend(['Bombchus (10)'] * 2)
        if not world.dungeon_mq['Bottom of the Well']:
            pool.extend(['Bombchus (10)'])
        if world.dungeon_mq['Gerudo Training Grounds']:
            pool.extend(['Bombchus (10)'])
        if world.dungeon_mq['Ganons Castle']:
            pool.extend(['Bombchus (10)'])
        else:
            pool.extend(['Bombchus (20)'])

    pool.extend(['Ice Trap'])
    if not world.dungeon_mq['Gerudo Training Grounds']:
        pool.extend(['Ice Trap'])
    if not world.dungeon_mq['Ganons Castle']:
        pool.extend(['Ice Trap'] * 4)

    if world.gerudo_fortress == 'open':
        placed_items['GF North F1 Carpenter'] = 'Recovery Heart'
        placed_items['GF North F2 Carpenter'] = 'Recovery Heart'
        placed_items['GF South F1 Carpenter'] = 'Recovery Heart'
        placed_items['GF South F2 Carpenter'] = 'Recovery Heart'
    elif world.shuffle_smallkeys == 'keysanity':
        if world.gerudo_fortress == 'fast':
            pool.append('Small Key (Gerudo Fortress)')
            placed_items['GF North F2 Carpenter'] = 'Recovery Heart'
            placed_items['GF South F1 Carpenter'] = 'Recovery Heart'
            placed_items['GF South F2 Carpenter'] = 'Recovery Heart'
        else:
            pool.extend(['Small Key (Gerudo Fortress)'] * 4)
    else:
        if world.gerudo_fortress == 'fast':
            placed_items[
                'GF North F1 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items['GF North F2 Carpenter'] = 'Recovery Heart'
            placed_items['GF South F1 Carpenter'] = 'Recovery Heart'
            placed_items['GF South F2 Carpenter'] = 'Recovery Heart'
        else:
            placed_items[
                'GF North F1 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items[
                'GF North F2 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items[
                'GF South F1 Carpenter'] = 'Small Key (Gerudo Fortress)'
            placed_items[
                'GF South F2 Carpenter'] = 'Small Key (Gerudo Fortress)'

    if world.shuffle_gerudo_card and world.gerudo_fortress != 'open':
        pool.append('Gerudo Membership Card')
    elif world.shuffle_gerudo_card:
        pending_junk_pool.append('Gerudo Membership Card')
        placed_items['GF Gerudo Membership Card'] = 'Ice Trap'
    else:
        placed_items['GF Gerudo Membership Card'] = 'Gerudo Membership Card'

    if world.shopsanity == 'off':
        placed_items.update(vanilla_shop_items)
        if world.bombchus_in_logic:
            placed_items['KF Shop Item 8'] = 'Buy Bombchu (5)'
            placed_items['Market Bazaar Item 4'] = 'Buy Bombchu (5)'
            placed_items['Kak Bazaar Item 4'] = 'Buy Bombchu (5)'
        pool.extend(normal_rupees)

    else:
        remain_shop_items = list(vanilla_shop_items.values())
        pool.extend(min_shop_items)
        for item in min_shop_items:
            remain_shop_items.remove(item)

        shop_slots_count = len(remain_shop_items)
        shop_nonitem_count = len(world.shop_prices)
        shop_item_count = shop_slots_count - shop_nonitem_count

        pool.extend(random.sample(remain_shop_items, shop_item_count))
        if shop_nonitem_count:
            pool.extend(get_junk_item(shop_nonitem_count))
        if world.shopsanity == '0':
            pool.extend(normal_rupees)
        else:
            pool.extend(shopsanity_rupees)

    if world.shuffle_scrubs != 'off':
        if world.dungeon_mq['Deku Tree']:
            pool.append('Deku Shield')
        if world.dungeon_mq['Dodongos Cavern']:
            pool.extend(['Deku Stick (1)', 'Deku Shield', 'Recovery Heart'])
        else:
            pool.extend(['Deku Nuts (5)', 'Deku Stick (1)', 'Deku Shield'])
        if not world.dungeon_mq['Jabu Jabus Belly']:
            pool.append('Deku Nuts (5)')
        if world.dungeon_mq['Ganons Castle']:
            pool.extend(
                ['Bombs (5)', 'Recovery Heart', 'Rupees (5)', 'Deku Nuts (5)'])
        else:
            pool.extend(['Bombs (5)', 'Recovery Heart', 'Rupees (5)'])
        pool.extend(deku_scrubs_items)
        for _ in range(7):
            pool.append('Arrows (30)'
                        if random.randint(0, 3) > 0 else 'Deku Seeds (30)')

    else:
        if world.dungeon_mq['Deku Tree']:
            placed_items['Deku Tree MQ Deku Scrub'] = 'Buy Deku Shield'
        if world.dungeon_mq['Dodongos Cavern']:
            placed_items[
                'Dodongos Cavern MQ Deku Scrub Lobby Rear'] = 'Buy Deku Stick (1)'
            placed_items[
                'Dodongos Cavern MQ Deku Scrub Lobby Front'] = 'Buy Deku Seeds (30)'
            placed_items[
                'Dodongos Cavern MQ Deku Scrub Staircase'] = 'Buy Deku Shield'
            placed_items[
                'Dodongos Cavern MQ Deku Scrub Side Room Near Lower Lizalfos'] = 'Buy Red Potion [30]'
        else:
            placed_items[
                'Dodongos Cavern Deku Scrub Near Bomb Bag Left'] = 'Buy Deku Nut (5)'
            placed_items[
                'Dodongos Cavern Deku Scrub Side Room Near Dodongos'] = 'Buy Deku Stick (1)'
            placed_items[
                'Dodongos Cavern Deku Scrub Near Bomb Bag Right'] = 'Buy Deku Seeds (30)'
            placed_items[
                'Dodongos Cavern Deku Scrub Lobby'] = 'Buy Deku Shield'
        if not world.dungeon_mq['Jabu Jabus Belly']:
            placed_items['Jabu Jabus Belly Deku Scrub'] = 'Buy Deku Nut (5)'
        if world.dungeon_mq['Ganons Castle']:
            placed_items[
                'Ganons Castle MQ Deku Scrub Right'] = 'Buy Deku Nut (5)'
            placed_items[
                'Ganons Castle MQ Deku Scrub Center-Left'] = 'Buy Bombs (5) [35]'
            placed_items[
                'Ganons Castle MQ Deku Scrub Center'] = 'Buy Arrows (30)'
            placed_items[
                'Ganons Castle MQ Deku Scrub Center-Right'] = 'Buy Red Potion [30]'
            placed_items[
                'Ganons Castle MQ Deku Scrub Left'] = 'Buy Green Potion'
        else:
            placed_items[
                'Ganons Castle Deku Scrub Center-Left'] = 'Buy Bombs (5) [35]'
            placed_items[
                'Ganons Castle Deku Scrub Center-Right'] = 'Buy Arrows (30)'
            placed_items[
                'Ganons Castle Deku Scrub Right'] = 'Buy Red Potion [30]'
            placed_items['Ganons Castle Deku Scrub Left'] = 'Buy Green Potion'
        placed_items.update(vanilla_deku_scrubs)

    pool.extend(alwaysitems)

    if world.dungeon_mq['Deku Tree']:
        pool.extend(DT_MQ)
    else:
        pool.extend(DT_vanilla)
    if world.dungeon_mq['Dodongos Cavern']:
        pool.extend(DC_MQ)
    else:
        pool.extend(DC_vanilla)
    if world.dungeon_mq['Jabu Jabus Belly']:
        pool.extend(JB_MQ)
    if world.dungeon_mq['Forest Temple']:
        pool.extend(FoT_MQ)
    else:
        pool.extend(FoT_vanilla)
    if world.dungeon_mq['Fire Temple']:
        pool.extend(FiT_MQ)
    else:
        pool.extend(FiT_vanilla)
    if world.dungeon_mq['Spirit Temple']:
        pool.extend(SpT_MQ)
    else:
        pool.extend(SpT_vanilla)
    if world.dungeon_mq['Shadow Temple']:
        pool.extend(ShT_MQ)
    else:
        pool.extend(ShT_vanilla)
    if not world.dungeon_mq['Bottom of the Well']:
        pool.extend(BW_vanilla)
    if world.dungeon_mq['Gerudo Training Grounds']:
        pool.extend(GTG_MQ)
    else:
        pool.extend(GTG_vanilla)
    if world.dungeon_mq['Ganons Castle']:
        pool.extend(GC_MQ)
    else:
        pool.extend(GC_vanilla)

    for i in range(bottle_count):
        if i >= ruto_bottles:
            bottle = random.choice(normal_bottles)
            pool.append(bottle)
        else:
            pool.append('Ruto\'s Letter')

    earliest_trade = tradeitemoptions.index(world.logic_earliest_adult_trade)
    latest_trade = tradeitemoptions.index(world.logic_latest_adult_trade)
    if earliest_trade > latest_trade:
        earliest_trade, latest_trade = latest_trade, earliest_trade
    tradeitem = random.choice(tradeitems[earliest_trade:latest_trade + 1])
    world.selected_adult_trade_item = tradeitem
    pool.append(tradeitem)

    pool.extend(songlist)
    if world.free_scarecrow:
        world.state.collect(ItemFactory('Scarecrow Song'))

    if world.no_epona_race:
        world.state.collect(ItemFactory('Epona', event=True))

    if world.shuffle_mapcompass == 'remove' or world.shuffle_mapcompass == 'startwith':
        for item in [
                item for dungeon in world.dungeons
                for item in dungeon.dungeon_items
        ]:
            world.state.collect(item)
            pool.extend(get_junk_item())
    if world.shuffle_smallkeys == 'remove':
        for item in [
                item for dungeon in world.dungeons
                for item in dungeon.small_keys
        ]:
            world.state.collect(item)
            pool.extend(get_junk_item())
    if world.shuffle_bosskeys == 'remove':
        for item in [
                item for dungeon in world.dungeons
                if dungeon.name != 'Ganons Castle' for item in dungeon.boss_key
        ]:
            world.state.collect(item)
            pool.extend(get_junk_item())
    if world.shuffle_ganon_bosskey in ['remove', 'triforce']:
        for item in [
                item for dungeon in world.dungeons
                if dungeon.name == 'Ganons Castle' for item in dungeon.boss_key
        ]:
            world.state.collect(item)
            pool.extend(get_junk_item())

    if world.shuffle_mapcompass == 'vanilla':
        for location, item in vanillaMC.items():
            try:
                world.get_location(location)
                placed_items[location] = item
            except KeyError:
                continue
    if world.shuffle_smallkeys == 'vanilla':
        for location, item in vanillaSK.items():
            try:
                world.get_location(location)
                placed_items[location] = item
            except KeyError:
                continue
        # Logic cannot handle vanilla key layout in some dungeons
        # this is because vanilla expects the dungeon major item to be
        # locked behind the keys, which is not always true in rando.
        # We can resolve this by starting with some extra keys
        if world.dungeon_mq['Spirit Temple']:
            # Yes somehow you need 3 keys. This dungeon is bonkers
            world.state.collect(ItemFactory('Small Key (Spirit Temple)'))
            world.state.collect(ItemFactory('Small Key (Spirit Temple)'))
            world.state.collect(ItemFactory('Small Key (Spirit Temple)'))
        #if not world.dungeon_mq['Fire Temple']:
        #    world.state.collect(ItemFactory('Small Key (Fire Temple)'))
    if world.shuffle_bosskeys == 'vanilla':
        for location, item in vanillaBK.items():
            try:
                world.get_location(location)
                placed_items[location] = item
            except KeyError:
                continue

    if not world.keysanity and not world.dungeon_mq['Fire Temple']:
        world.state.collect(ItemFactory('Small Key (Fire Temple)'))
    if not world.dungeon_mq['Water Temple']:
        world.state.collect(ItemFactory('Small Key (Water Temple)'))

    if world.triforce_hunt:
        triforce_count = int(
            round(world.triforce_goal_per_world *
                  TriforceCounts[world.item_pool_value]))
        pending_junk_pool.extend(['Triforce Piece'] * triforce_count)

    if world.shuffle_ganon_bosskey in [
            'lacs_vanilla', 'lacs_medallions', 'lacs_stones', 'lacs_dungeons'
    ]:
        placed_items['ToT Light Arrows Cutscene'] = 'Boss Key (Ganons Castle)'
    elif world.shuffle_ganon_bosskey == 'vanilla':
        placed_items[
            'Ganons Tower Boss Key Chest'] = 'Boss Key (Ganons Castle)'

    if world.item_pool_value == 'plentiful':
        pool.extend(easy_items)
    else:
        pool.extend(normal_items)

    if not world.shuffle_kokiri_sword:
        replace_max_item(pool, 'Kokiri Sword', 0)

    if world.junk_ice_traps == 'off':
        replace_max_item(pool, 'Ice Trap', 0)
    elif world.junk_ice_traps == 'onslaught':
        for item in [item for item, weight in junk_pool_base
                     ] + ['Recovery Heart', 'Bombs (20)', 'Arrows (30)']:
            replace_max_item(pool, item, 0)

    for item, max in item_difficulty_max[world.item_pool_value].items():
        replace_max_item(pool, item, max)

    world.distribution.alter_pool(world, pool)

    # Make sure our pending_junk_pool is empty. If not, remove some random junk here.
    if pending_junk_pool:
        for item in set(pending_junk_pool):
            # Ensure pending_junk_pool contents don't exceed values given by distribution file
            if item in world.distribution.item_pool:
                while pending_junk_pool.count(
                        item) > world.distribution.item_pool[item].count:
                    pending_junk_pool.remove(item)
                # Remove pending junk already added to the pool by alter_pool from the pending_junk_pool
                if item in pool:
                    count = pool.count(item)
                    for _ in range(count):
                        pending_junk_pool.remove(item)

        remove_junk_pool, _ = zip(*junk_pool_base)
        remove_junk_pool = list(remove_junk_pool) + [
            'Recovery Heart', 'Bombs (20)', 'Arrows (30)', 'Ice Trap'
        ]

        junk_candidates = [item for item in pool if item in remove_junk_pool]
        while pending_junk_pool:
            pending_item = pending_junk_pool.pop()
            if not junk_candidates:
                raise RuntimeError(
                    "Not enough junk exists in item pool for %s to be added." %
                    pending_item)
            junk_item = random.choice(junk_candidates)
            junk_candidates.remove(junk_item)
            pool.remove(junk_item)
            pool.append(pending_item)

    world.distribution.configure_starting_items_settings(world)
    world.distribution.collect_starters(world.state)

    return (pool, placed_items)