Exemplo n.º 1
0
def set_entrances_based_rules(worlds):

    # Use the states with all items available in the pools for this seed
    complete_itempool = [item for world in worlds for item in world.get_itempool_with_dungeon_items()]
    search = Search([world.state for world in worlds])
    search.collect_all(complete_itempool)
    search.collect_locations()

    for world in worlds:
        for location in world.get_locations():
            if location.type == 'Shop':
                # If All Locations Reachable is on, prevent shops only ever reachable as child from containing Buy Goron Tunic and Buy Zora Tunic items
                if not world.check_beatable_only:
                    if not search.can_reach(location.parent_region, age='adult'):
                        forbid_item(location, 'Buy Goron Tunic')
                        forbid_item(location, 'Buy Zora Tunic')
Exemplo n.º 2
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