def copy_dynamic_regions_and_locations(world, ret):
    for region in world.dynamic_regions:
        new_reg = Region(region.name, region.type, region.hint_text,
                         region.player)
        ret.regions.append(new_reg)
        ret.initialize_regions([new_reg])
        ret.dynamic_regions.append(new_reg)

        # Note: ideally exits should be copied here, but the current use case (Take anys) do not require this

        if region.shop:
            new_reg.shop = Shop(new_reg, region.shop.room_id, region.shop.type,
                                region.shop.shopkeeper_config,
                                region.shop.custom, region.shop.locked)
            ret.shops.append(new_reg.shop)

    for location in world.dynamic_locations:
        new_reg = ret.get_region(location.parent_region.name,
                                 location.parent_region.player)
        new_loc = Location(location.player, location.name, location.address,
                           location.crystal, location.hint_text, new_reg)
        # todo: this is potentially dangerous. later refactor so we
        # can apply dynamic region rules on top of copied world like other rules
        new_loc.access_rule = location.access_rule
        new_loc.always_allow = location.always_allow
        new_loc.item_rule = location.item_rule
        new_reg.locations.append(new_loc)

        ret.clear_location_cache()
Exemple #2
0
    def create_regions(self):
        locations = get_options(self.world, 'locations', self.player)
        rules = get_options(self.world, 'rules', self.player)
        menu_region = self.ff1_locations.create_menu_region(
            self.player, locations, rules)
        terminated_event = Location(self.player, CHAOS_TERMINATED_EVENT,
                                    EventId, menu_region)
        terminated_item = Item(CHAOS_TERMINATED_EVENT, True, EventId,
                               self.player)
        terminated_event.place_locked_item(terminated_item)

        items = get_options(self.world, 'items', self.player)
        goal_rule = generate_rule([[
            name for name in items.keys()
            if name in FF1_PROGRESSION_LIST and name != "Shard"
        ]], self.player)
        if "Shard" in items.keys():

            def goal_rule_and_shards(state):
                return goal_rule(state) and state.has("Shard", self.player, 32)

            terminated_event.access_rule = goal_rule_and_shards

        menu_region.locations.append(terminated_event)
        self.world.regions += [menu_region]
Exemple #3
0
    def create_menu_region(player: int, locations: Dict[str, int],
                           rules: Dict[str, List[List[str]]]) -> Region:
        menu_region = Region("Menu", RegionType.Generic, "Menu", player)
        for name, address in locations.items():
            location = Location(player, name, address, menu_region)
            ## TODO REMOVE WHEN LOGIC FOR TOFR IS CORRECT
            if "ToFR" in name:
                rules_list = [[
                    "Rod", "Cube", "Lute", "Key", "Chime", "Oxyale", "Ship",
                    "Canoe", "Floater", "Canal", "Crown", "Crystal", "Herb",
                    "Tnt", "Adamant", "Slab", "Ruby", "Bottle"
                ]]
                location.access_rule = generate_rule(rules_list, player)
            elif name in rules:
                rules_list = rules[name]
                location.access_rule = generate_rule(rules_list, player)
            menu_region.locations.append(location)

        return menu_region
Exemple #4
0
def create_location(player: int, location_data: LocationData, region: Region,
                    location_cache: List[Location]) -> Location:
    location = Location(player, location_data.name, location_data.code, region)
    location.access_rule = location_data.rule

    if id is None:
        location.event = True
        location.locked = True

    location_cache.append(location)

    return location
def generate_itempool(world, player: int):
    if world.difficulty[player] not in difficulties:
        raise NotImplementedError(f"Diffulty {world.difficulty[player]}")
    if world.goal[player] not in {'ganon', 'pedestal', 'dungeons', 'triforcehunt', 'localtriforcehunt',
                                  'ganontriforcehunt', 'localganontriforcehunt', 'crystals'}:
        raise NotImplementedError(f"Goal {world.goal[player]}")
    if world.mode[player] not in {'open', 'standard', 'inverted'}:
        raise NotImplementedError(f"Mode {world.mode[player]}")
    if world.timer[player] not in {False, 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'}:
        raise NotImplementedError(f"Timer {world.mode[player]}")

    if world.timer[player] in ['ohko', 'timed-ohko']:
        world.can_take_damage[player] = False
    if world.goal[player] in ['pedestal', 'triforcehunt', 'localtriforcehunt']:
        world.push_item(world.get_location('Ganon', player), ItemFactory('Nothing', player), False)
    else:
        world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False)

    if world.goal[player] in ['triforcehunt', 'localtriforcehunt']:
        region = world.get_region('Light World', player)

        loc = Location(player, "Murahdahla", parent=region)
        loc.access_rule = lambda state: state.has_triforce_pieces(state.world.treasure_hunt_count[player], player)

        region.locations.append(loc)
        world.dynamic_locations.append(loc)

        world.clear_location_cache()

        world.push_item(loc, ItemFactory('Triforce', player), False)
        loc.event = True
        loc.locked = True

    world.get_location('Ganon', player).event = True
    world.get_location('Ganon', player).locked = True
    world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False)
    world.get_location('Agahnim 1', player).event = True
    world.get_location('Agahnim 1', player).locked = True
    world.push_item(world.get_location('Agahnim 2', player), ItemFactory('Beat Agahnim 2', player), False)
    world.get_location('Agahnim 2', player).event = True
    world.get_location('Agahnim 2', player).locked = True
    world.push_item(world.get_location('Dark Blacksmith Ruins', player), ItemFactory('Pick Up Purple Chest', player), False)
    world.get_location('Dark Blacksmith Ruins', player).event = True
    world.get_location('Dark Blacksmith Ruins', player).locked = True
    world.push_item(world.get_location('Frog', player), ItemFactory('Get Frog', player), False)
    world.get_location('Frog', player).event = True
    world.get_location('Frog', player).locked = True
    world.push_item(world.get_location('Missing Smith', player), ItemFactory('Return Smith', player), False)
    world.get_location('Missing Smith', player).event = True
    world.get_location('Missing Smith', player).locked = True
    world.push_item(world.get_location('Floodgate', player), ItemFactory('Open Floodgate', player), False)
    world.get_location('Floodgate', player).event = True
    world.get_location('Floodgate', player).locked = True

    # set up item pool
    if world.custom:
        (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count,
         treasure_hunt_icon) = make_custom_item_pool(world, player)
        world.rupoor_cost = min(world.customitemarray[69], 9999)
    else:
        (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon) = get_pool_core(
            world, player)

    for item in precollected_items:
        world.push_precollected(ItemFactory(item, player))

    if world.mode[player] == 'standard' and not world.state.has_melee_weapon(player):
        if "Link's Uncle" not in placed_items:
            found_sword = False
            found_bow = False
            possible_weapons = []
            for item in pool:
                if item in ['Progressive Sword', 'Fighter Sword', 'Master Sword', 'Tempered Sword', 'Golden Sword']:
                    if not found_sword and world.swords[player] != 'swordless':
                        found_sword = True
                        possible_weapons.append(item)
                if item in ['Progressive Bow', 'Bow'] and not found_bow:
                    found_bow = True
                    possible_weapons.append(item)
                if item in ['Hammer', 'Bombs (10)', 'Fire Rod', 'Cane of Somaria', 'Cane of Byrna']:
                    if item not in possible_weapons:
                        possible_weapons.append(item)
            starting_weapon = world.random.choice(possible_weapons)
            placed_items["Link's Uncle"] = starting_weapon
            pool.remove(starting_weapon)
        if placed_items["Link's Uncle"] in ['Bow', 'Progressive Bow', 'Bombs (10)', 'Cane of Somaria', 'Cane of Byrna'] and world.enemy_health[player] not in ['default', 'easy']:
            world.escape_assist[player].append('bombs')

    for (location, item) in placed_items.items():
        world.push_item(world.get_location(location, player), ItemFactory(item, player), False)
        world.get_location(location, player).event = True
        world.get_location(location, player).locked = True

    items = ItemFactory(pool, player)

    if clock_mode is not None:
        world.clock_mode[player] = clock_mode

    if treasure_hunt_count is not None:
        world.treasure_hunt_count[player] = treasure_hunt_count
    if treasure_hunt_icon is not None:
        world.treasure_hunt_icon[player] = treasure_hunt_icon

    world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player
                           and ((item.smallkey and world.keyshuffle[player])
                                or (item.bigkey and world.bigkeyshuffle[player])
                                or (item.map and world.mapshuffle[player])
                                or (item.compass and world.compassshuffle[player]))])

    # logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
    # rather than making all hearts/heart pieces progression items (which slows down generation considerably)
    # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
    if world.difficulty[player] in ['normal', 'hard'] and not (world.custom and world.customitemarray[30] == 0):
        [item for item in items if item.name == 'Boss Heart Container'][0].advancement = True
    elif world.difficulty[player] in ['expert'] and not (world.custom and world.customitemarray[29] < 4):
        adv_heart_pieces = [item for item in items if item.name == 'Piece of Heart'][0:4]
        for hp in adv_heart_pieces:
            hp.advancement = True

    beeweights = {0: {None: 100},
                  1: {None: 75, 'trap': 25},
                  2: {None: 40, 'trap': 40, 'bee': 20},
                  3: {'trap': 50, 'bee': 50},
                  4: {'trap': 100}}
    def beemizer(item):
        if world.beemizer[item.player] and not item.advancement and not item.priority and not item.type:
            choice = world.random.choices(list(beeweights[world.beemizer[item.player]].keys()),
                                          weights=list(beeweights[world.beemizer[item.player]].values()))[0]
            return item if not choice else ItemFactory("Bee Trap", player) if choice == 'trap' else ItemFactory("Bee", player)
        return item

    progressionitems = [item for item in items if item.advancement or item.priority or item.type]
    nonprogressionitems = [beemizer(item) for item in items if not item.advancement and not item.priority and not item.type]
    world.random.shuffle(nonprogressionitems)

    triforce_pieces = world.triforce_pieces_available[player]
    if 'triforcehunt' in world.goal[player] and triforce_pieces > 30:
        progressionitems += [ItemFactory("Triforce Piece", player)] * (triforce_pieces - 30)
        nonprogressionitems = nonprogressionitems[(triforce_pieces - 30):]

    world.itempool += progressionitems + nonprogressionitems

    # shuffle medallions
    mm_medallion = ['Ether', 'Quake', 'Bombos'][world.random.randint(0, 2)]
    tr_medallion = ['Ether', 'Quake', 'Bombos'][world.random.randint(0, 2)]
    world.required_medallions[player] = (mm_medallion, tr_medallion)

    place_bosses(world, player)
    set_up_shops(world, player)

    if world.retro[player]:
        set_up_take_anys(world, player)

    create_dynamic_shop_locations(world, player)
Exemple #6
0
def generate_itempool(world, player):
    if (world.difficulty[player] not in ['normal', 'hard', 'expert']
            or world.goal[player] not in [
                'ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'
            ] or world.mode[player] not in ['open', 'standard', 'inverted']
            or world.timer not in [
                'none', 'display', 'timed', 'timed-ohko', 'ohko',
                'timed-countdown'
            ] or world.progressive not in ['on', 'off', 'random']):
        raise NotImplementedError('Not supported yet')

    if world.timer in ['ohko', 'timed-ohko']:
        world.can_take_damage = False

    if world.goal[player] in ['pedestal', 'triforcehunt']:
        world.push_item(world.get_location('Ganon', player),
                        ItemFactory('Nothing', player), False)
    else:
        world.push_item(world.get_location('Ganon', player),
                        ItemFactory('Triforce', player), False)

    if world.goal[player] in ['triforcehunt']:
        region = world.get_region('Light World', player)

        loc = Location(player, "Murahdahla", parent=region)
        loc.access_rule = lambda state: state.item_count(
            'Triforce Piece', player) + state.item_count(
                'Power Star', player) > state.world.treasure_hunt_count[player]
        region.locations.append(loc)
        world.dynamic_locations.append(loc)

        world.clear_location_cache()

        world.push_item(loc, ItemFactory('Triforce', player), False)
        loc.event = True
        loc.locked = True

    world.get_location('Ganon', player).event = True
    world.get_location('Ganon', player).locked = True
    world.push_item(world.get_location('Agahnim 1', player),
                    ItemFactory('Beat Agahnim 1', player), False)
    world.get_location('Agahnim 1', player).event = True
    world.get_location('Agahnim 1', player).locked = True
    world.push_item(world.get_location('Agahnim 2', player),
                    ItemFactory('Beat Agahnim 2', player), False)
    world.get_location('Agahnim 2', player).event = True
    world.get_location('Agahnim 2', player).locked = True
    world.push_item(world.get_location('Dark Blacksmith Ruins', player),
                    ItemFactory('Pick Up Purple Chest', player), False)
    world.get_location('Dark Blacksmith Ruins', player).event = True
    world.get_location('Dark Blacksmith Ruins', player).locked = True
    world.push_item(world.get_location('Frog', player),
                    ItemFactory('Get Frog', player), False)
    world.get_location('Frog', player).event = True
    world.get_location('Frog', player).locked = True
    world.push_item(world.get_location('Missing Smith', player),
                    ItemFactory('Return Smith', player), False)
    world.get_location('Missing Smith', player).event = True
    world.get_location('Missing Smith', player).locked = True
    world.push_item(world.get_location('Floodgate', player),
                    ItemFactory('Open Floodgate', player), False)
    world.get_location('Floodgate', player).event = True
    world.get_location('Floodgate', player).locked = True
    world.push_item(world.get_location('Trench 1 Switch', player),
                    ItemFactory('Trench 1 Filled', player), False)
    world.get_location('Trench 1 Switch', player).event = True
    world.get_location('Trench 1 Switch', player).locked = True
    world.push_item(world.get_location('Trench 2 Switch', player),
                    ItemFactory('Trench 2 Filled', player), False)
    world.get_location('Trench 2 Switch', player).event = True
    world.get_location('Trench 2 Switch', player).locked = True
    world.push_item(world.get_location('Swamp Drain', player),
                    ItemFactory('Drained Swamp', player), False)
    world.get_location('Swamp Drain', player).event = True
    world.get_location('Swamp Drain', player).locked = True
    world.push_item(world.get_location('Attic Cracked Floor', player),
                    ItemFactory('Shining Light', player), False)
    world.get_location('Attic Cracked Floor', player).event = True
    world.get_location('Attic Cracked Floor', player).locked = True
    world.push_item(world.get_location('Suspicious Maiden', player),
                    ItemFactory('Maiden Rescued', player), False)
    world.get_location('Suspicious Maiden', player).event = True
    world.get_location('Suspicious Maiden', player).locked = True
    world.push_item(world.get_location('Revealing Light', player),
                    ItemFactory('Maiden Unmasked', player), False)
    world.get_location('Revealing Light', player).event = True
    world.get_location('Revealing Light', player).locked = True
    world.push_item(world.get_location('Ice Block Drop', player),
                    ItemFactory('Convenient Block', player), False)
    world.get_location('Ice Block Drop', player).event = True
    world.get_location('Ice Block Drop', player).locked = True
    if world.mode[player] == 'standard':
        world.push_item(world.get_location('Zelda Pickup', player),
                        ItemFactory('Zelda Herself', player), False)
        world.get_location('Zelda Pickup', player).event = True
        world.get_location('Zelda Pickup', player).locked = True
        world.push_item(world.get_location('Zelda Drop Off', player),
                        ItemFactory('Zelda Delivered', player), False)
        world.get_location('Zelda Drop Off', player).event = True
        world.get_location('Zelda Drop Off', player).locked = True

    # set up item pool
    if world.custom:
        (pool, placed_items, precollected_items, clock_mode,
         treasure_hunt_count, treasure_hunt_icon,
         lamps_needed_for_dark_rooms) = make_custom_item_pool(
             world.progressive, world.shuffle[player],
             world.difficulty[player], world.timer, world.goal[player],
             world.mode[player], world.swords[player], world.retro[player],
             world.customitemarray)
        world.rupoor_cost = min(world.customitemarray["rupoorcost"], 9999)
    else:
        (pool, placed_items, precollected_items, clock_mode,
         treasure_hunt_count, treasure_hunt_icon,
         lamps_needed_for_dark_rooms) = get_pool_core(
             world.progressive, world.shuffle[player],
             world.difficulty[player], world.timer, world.goal[player],
             world.mode[player], world.swords[player], world.retro[player],
             world.doorShuffle[player])

    if player in world.pool_adjustment.keys():
        amt = world.pool_adjustment[player]
        if amt < 0:
            for i in range(0, amt):
                pool.remove('Rupees (20)')
        elif amt > 0:
            for i in range(0, amt):
                pool.append('Rupees (20)')

    for item in precollected_items:
        world.push_precollected(ItemFactory(item, player))

    if world.mode[player] == 'standard' and not world.state.has_blunt_weapon(
            player):
        if "Link's Uncle" not in placed_items:
            found_sword = False
            found_bow = False
            possible_weapons = []
            for item in pool:
                if item in [
                        'Progressive Sword', 'Fighter Sword', 'Master Sword',
                        'Tempered Sword', 'Golden Sword'
                ]:
                    if not found_sword and world.swords[player] != 'swordless':
                        found_sword = True
                        possible_weapons.append(item)
                if item in ['Progressive Bow', 'Bow'] and not found_bow:
                    found_bow = True
                    possible_weapons.append(item)
                if item in [
                        'Hammer', 'Bombs (10)', 'Fire Rod', 'Cane of Somaria',
                        'Cane of Byrna'
                ]:
                    if item not in possible_weapons:
                        possible_weapons.append(item)
            starting_weapon = random.choice(possible_weapons)
            placed_items["Link's Uncle"] = starting_weapon
            pool.remove(starting_weapon)
        if placed_items["Link's Uncle"] in [
                'Bow', 'Progressive Bow', 'Bombs (10)', 'Cane of Somaria',
                'Cane of Byrna'
        ] and world.enemy_health[player] not in ['default', 'easy']:
            world.escape_assist[player].append('bombs')

    for (location, item) in placed_items.items():
        world.push_item(world.get_location(location, player),
                        ItemFactory(item, player), False)
        world.get_location(location, player).event = True
        world.get_location(location, player).locked = True

    items = ItemFactory(pool, player)

    world.lamps_needed_for_dark_rooms = lamps_needed_for_dark_rooms

    if clock_mode is not None:
        world.clock_mode = clock_mode

    if treasure_hunt_count is not None:
        world.treasure_hunt_count[player] = treasure_hunt_count
    if treasure_hunt_icon is not None:
        world.treasure_hunt_icon[player] = treasure_hunt_icon

    world.itempool.extend([
        item for item in get_dungeon_item_pool(world)
        if item.player == player and (
            (item.smallkey and world.keyshuffle[player]) or
            (item.bigkey and world.bigkeyshuffle[player]) or
            (item.map and world.mapshuffle[player]) or
            (item.compass and world.compassshuffle[player]))
    ])

    # logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
    # rather than making all hearts/heart pieces progression items (which slows down generation considerably)
    # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
    if world.difficulty[player] in [
            'normal', 'hard'
    ] and not (world.custom and world.customitemarray["heartcontainer"] == 0):
        [item for item in items
         if item.name == 'Boss Heart Container'][0].advancement = True
    elif world.difficulty[player] in [
            'expert'
    ] and not (world.custom and world.customitemarray["heartpiece"] < 4):
        adv_heart_pieces = [
            item for item in items if item.name == 'Piece of Heart'
        ][0:4]
        for hp in adv_heart_pieces:
            hp.advancement = True

    beeweights = {
        0: {
            None: 100
        },
        1: {
            None: 75,
            'trap': 25
        },
        2: {
            None: 40,
            'trap': 40,
            'bee': 20
        },
        3: {
            'trap': 50,
            'bee': 50
        },
        4: {
            'trap': 100
        }
    }

    def beemizer(item):
        if world.beemizer[
                item.
                player] and not item.advancement and not item.priority and not item.type:
            choice = random.choices(
                list(beeweights[world.beemizer[item.player]].keys()),
                weights=list(
                    beeweights[world.beemizer[item.player]].values()))[0]
            return item if not choice else ItemFactory(
                "Bee Trap", player) if choice == 'trap' else ItemFactory(
                    "Bee", player)
        return item

    world.itempool += [beemizer(item) for item in items]

    # shuffle medallions
    mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
    tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
    world.required_medallions[player] = (mm_medallion, tr_medallion)

    place_bosses(world, player)
    set_up_shops(world, player)

    if world.retro[player]:
        set_up_take_anys(world, player)

    create_dynamic_shop_locations(world, player)
Exemple #7
0
def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = None):
    if not baked_server_options:
        baked_server_options = get_options()["server_options"]
    if args.outputpath:
        os.makedirs(args.outputpath, exist_ok=True)
        output_path.cached_path = args.outputpath

    start = time.perf_counter()
    # initialize the world
    world = MultiWorld(args.multi)

    logger = logging.getLogger()
    world.set_seed(seed, args.race, str(args.outputname if args.outputname else world.seed))

    world.shuffle = args.shuffle.copy()
    world.logic = args.logic.copy()
    world.mode = args.mode.copy()
    world.difficulty = args.difficulty.copy()
    world.item_functionality = args.item_functionality.copy()
    world.timer = args.timer.copy()
    world.goal = args.goal.copy()
    world.open_pyramid = args.open_pyramid.copy()
    world.boss_shuffle = args.shufflebosses.copy()
    world.enemy_health = args.enemy_health.copy()
    world.enemy_damage = args.enemy_damage.copy()
    world.beemizer_total_chance = args.beemizer_total_chance.copy()
    world.beemizer_trap_chance = args.beemizer_trap_chance.copy()
    world.timer = args.timer.copy()
    world.countdown_start_time = args.countdown_start_time.copy()
    world.red_clock_time = args.red_clock_time.copy()
    world.blue_clock_time = args.blue_clock_time.copy()
    world.green_clock_time = args.green_clock_time.copy()
    world.dungeon_counters = args.dungeon_counters.copy()
    world.triforce_pieces_available = args.triforce_pieces_available.copy()
    world.triforce_pieces_required = args.triforce_pieces_required.copy()
    world.shop_shuffle = args.shop_shuffle.copy()
    world.shuffle_prizes = args.shuffle_prizes.copy()
    world.sprite_pool = args.sprite_pool.copy()
    world.dark_room_logic = args.dark_room_logic.copy()
    world.plando_items = args.plando_items.copy()
    world.plando_texts = args.plando_texts.copy()
    world.plando_connections = args.plando_connections.copy()
    world.required_medallions = args.required_medallions.copy()
    world.game = args.game.copy()
    world.player_name = args.name.copy()
    world.enemizer = args.enemizercli
    world.sprite = args.sprite.copy()
    world.glitch_triforce = args.glitch_triforce  # This is enabled/disabled globally, no per player option.

    world.set_options(args)
    world.set_item_links()
    world.state = CollectionState(world)
    logger.info('Archipelago Version %s  -  Seed: %s\n', __version__, world.seed)

    logger.info("Found World Types:")
    longest_name = max(len(text) for text in AutoWorld.AutoWorldRegister.world_types)
    numlength = 8
    for name, cls in AutoWorld.AutoWorldRegister.world_types.items():
        if not cls.hidden:
            logger.info(f"  {name:{longest_name}}: {len(cls.item_names):3} "
                        f"Items (IDs: {min(cls.item_id_to_name):{numlength}} - "
                        f"{max(cls.item_id_to_name):{numlength}}) | "
                        f"{len(cls.location_names):3} "
                        f"Locations (IDs: {min(cls.location_id_to_name):{numlength}} - "
                        f"{max(cls.location_id_to_name):{numlength}})")

    AutoWorld.call_stage(world, "assert_generate")

    AutoWorld.call_all(world, "generate_early")

    logger.info('')

    for player in world.player_ids:
        for item_name, count in world.start_inventory[player].value.items():
            for _ in range(count):
                world.push_precollected(world.create_item(item_name, player))

    for player in world.player_ids:
        if player in world.get_game_players("A Link to the Past"):
            # enforce pre-defined local items.
            if world.goal[player] in ["localtriforcehunt", "localganontriforcehunt"]:
                world.local_items[player].value.add('Triforce Piece')

            # Not possible to place pendants/crystals out side of boss prizes yet.
            world.non_local_items[player].value -= item_name_groups['Pendants']
            world.non_local_items[player].value -= item_name_groups['Crystals']

        # items can't be both local and non-local, prefer local
        world.non_local_items[player].value -= world.local_items[player].value

    logger.info('Creating World.')
    AutoWorld.call_all(world, "create_regions")

    logger.info('Creating Items.')
    AutoWorld.call_all(world, "create_items")

    logger.info('Calculating Access Rules.')
    if world.players > 1:
        for player in world.player_ids:
            locality_rules(world, player)
        group_locality_rules(world)
    else:
        world.non_local_items[1].value = set()
        world.local_items[1].value = set()

    AutoWorld.call_all(world, "set_rules")

    for player in world.player_ids:
        exclusion_rules(world, player, world.exclude_locations[player].value)
        world.priority_locations[player].value -= world.exclude_locations[player].value
        for location_name in world.priority_locations[player].value:
            world.get_location(location_name, player).progress_type = LocationProgressType.PRIORITY

    AutoWorld.call_all(world, "generate_basic")

    # temporary home for item links, should be moved out of Main
    for group_id, group in world.groups.items():
        def find_common_pool(players: Set[int], shared_pool: Set[str]):
            classifications = collections.defaultdict(int)
            counters = {player: {name: 0 for name in shared_pool} for player in players}
            for item in world.itempool:
                if item.player in counters and item.name in shared_pool:
                    counters[item.player][item.name] += 1
                    classifications[item.name] |= item.classification

            for player in players.copy():
                if all([counters[player][item] == 0 for item in shared_pool]):
                    players.remove(player)
                    del(counters[player])

            if not players:
                return None, None

            for item in shared_pool:
                count = min(counters[player][item] for player in players)
                if count:
                    for player in players:
                        counters[player][item] = count
                else:
                    for player in players:
                        del(counters[player][item])
            return counters, classifications

        common_item_count, classifications = find_common_pool(group["players"], group["item_pool"])
        if not common_item_count:
            continue

        new_itempool = []
        for item_name, item_count in next(iter(common_item_count.values())).items():
            for _ in range(item_count):
                new_item = group["world"].create_item(item_name)
                # mangle together all original classification bits
                new_item.classification |= classifications[item_name]
                new_itempool.append(new_item)

        region = Region("Menu", RegionType.Generic, "ItemLink", group_id, world)
        world.regions.append(region)
        locations = region.locations = []
        for item in world.itempool:
            count = common_item_count.get(item.player, {}).get(item.name, 0)
            if count:
                loc = Location(group_id, f"Item Link: {item.name} -> {world.player_name[item.player]} {count}",
                               None, region)
                loc.access_rule = lambda state, item_name = item.name, group_id_ = group_id, count_ = count: \
                    state.has(item_name, group_id_, count_)

                locations.append(loc)
                loc.place_locked_item(item)
                common_item_count[item.player][item.name] -= 1
            else:
                new_itempool.append(item)

        itemcount = len(world.itempool)
        world.itempool = new_itempool

        while itemcount > len(world.itempool):
            items_to_add = []
            for player in group["players"]:
                if group["replacement_items"][player]:
                    items_to_add.append(AutoWorld.call_single(world, "create_item", player,
                                                                group["replacement_items"][player]))
                else:
                    items_to_add.append(AutoWorld.call_single(world, "create_filler", player))
            world.random.shuffle(items_to_add)
            world.itempool.extend(items_to_add[:itemcount - len(world.itempool)])

    if any(world.item_links.values()):
        world._recache()
        world._all_state = None

    logger.info("Running Item Plando")

    for item in world.itempool:
        item.world = world

    distribute_planned(world)

    logger.info('Running Pre Main Fill.')

    AutoWorld.call_all(world, "pre_fill")

    logger.info(f'Filling the world with {len(world.itempool)} items.')

    if world.algorithm == 'flood':
        flood_items(world)  # different algo, biased towards early game progress items
    elif world.algorithm == 'balanced':
        distribute_items_restrictive(world)

    AutoWorld.call_all(world, 'post_fill')

    if world.players > 1:
        balance_multiworld_progression(world)

    logger.info(f'Beginning output...')
    outfilebase = 'AP_' + world.seed_name

    output = tempfile.TemporaryDirectory()
    with output as temp_dir:
        with concurrent.futures.ThreadPoolExecutor(world.players + 2) as pool:
            check_accessibility_task = pool.submit(world.fulfills_accessibility)

            output_file_futures = [pool.submit(AutoWorld.call_stage, world, "generate_output", temp_dir)]
            for player in world.player_ids:
                # skip starting a thread for methods that say "pass".
                if AutoWorld.World.generate_output.__code__ is not world.worlds[player].generate_output.__code__:
                    output_file_futures.append(
                        pool.submit(AutoWorld.call_single, world, "generate_output", player, temp_dir))

            def get_entrance_to_region(region: Region):
                for entrance in region.entrances:
                    if entrance.parent_region.type in (RegionType.DarkWorld, RegionType.LightWorld, RegionType.Generic):
                        return entrance
                for entrance in region.entrances:  # BFS might be better here, trying DFS for now.
                    return get_entrance_to_region(entrance.parent_region)

            # collect ER hint info
            er_hint_data = {player: {} for player in world.get_game_players("A Link to the Past") if
                            world.shuffle[player] != "vanilla" or world.retro_caves[player]}

            for region in world.regions:
                if region.player in er_hint_data and region.locations:
                    main_entrance = get_entrance_to_region(region)
                    for location in region.locations:
                        if type(location.address) == int:  # skips events and crystals
                            if lookup_vanilla_location_to_entrance[location.address] != main_entrance.name:
                                er_hint_data[region.player][location.address] = main_entrance.name

            checks_in_area = {player: {area: list() for area in ordered_areas}
                              for player in range(1, world.players + 1)}

            for player in range(1, world.players + 1):
                checks_in_area[player]["Total"] = 0

            for location in world.get_filled_locations():
                if type(location.address) is int:
                    main_entrance = get_entrance_to_region(location.parent_region)
                    if location.game != "A Link to the Past":
                        checks_in_area[location.player]["Light World"].append(location.address)
                    elif location.parent_region.dungeon:
                        dungeonname = {'Inverted Agahnims Tower': 'Agahnims Tower',
                                       'Inverted Ganons Tower': 'Ganons Tower'} \
                            .get(location.parent_region.dungeon.name, location.parent_region.dungeon.name)
                        checks_in_area[location.player][dungeonname].append(location.address)
                    elif location.parent_region.type == RegionType.LightWorld:
                        checks_in_area[location.player]["Light World"].append(location.address)
                    elif location.parent_region.type == RegionType.DarkWorld:
                        checks_in_area[location.player]["Dark World"].append(location.address)
                    elif main_entrance.parent_region.type == RegionType.LightWorld:
                        checks_in_area[location.player]["Light World"].append(location.address)
                    elif main_entrance.parent_region.type == RegionType.DarkWorld:
                        checks_in_area[location.player]["Dark World"].append(location.address)
                    checks_in_area[location.player]["Total"] += 1

            oldmancaves = []
            takeanyregions = ["Old Man Sword Cave", "Take-Any #1", "Take-Any #2", "Take-Any #3", "Take-Any #4"]
            for index, take_any in enumerate(takeanyregions):
                for region in [world.get_region(take_any, player) for player in
                               world.get_game_players("A Link to the Past") if world.retro_caves[player]]:
                    item = world.create_item(
                        region.shop.inventory[(0 if take_any == "Old Man Sword Cave" else 1)]['item'],
                        region.player)
                    player = region.player
                    location_id = SHOP_ID_START + total_shop_slots + index

                    main_entrance = get_entrance_to_region(region)
                    if main_entrance.parent_region.type == RegionType.LightWorld:
                        checks_in_area[player]["Light World"].append(location_id)
                    else:
                        checks_in_area[player]["Dark World"].append(location_id)
                    checks_in_area[player]["Total"] += 1

                    er_hint_data[player][location_id] = main_entrance.name
                    oldmancaves.append(((location_id, player), (item.code, player)))

            FillDisabledShopSlots(world)

            def write_multidata():
                import NetUtils
                slot_data = {}
                client_versions = {}
                games = {}
                minimum_versions = {"server": AutoWorld.World.required_server_version, "clients": client_versions}
                slot_info = {}
                names = [[name for player, name in sorted(world.player_name.items())]]
                for slot in world.player_ids:
                    player_world: AutoWorld.World = world.worlds[slot]
                    minimum_versions["server"] = max(minimum_versions["server"], player_world.required_server_version)
                    client_versions[slot] = player_world.required_client_version
                    games[slot] = world.game[slot]
                    slot_info[slot] = NetUtils.NetworkSlot(names[0][slot - 1], world.game[slot],
                                                           world.player_types[slot])
                for slot, group in world.groups.items():
                    games[slot] = world.game[slot]
                    slot_info[slot] = NetUtils.NetworkSlot(group["name"], world.game[slot], world.player_types[slot],
                                                           group_members=sorted(group["players"]))
                precollected_items = {player: [item.code for item in world_precollected if type(item.code) == int]
                                      for player, world_precollected in world.precollected_items.items()}
                precollected_hints = {player: set() for player in range(1, world.players + 1 + len(world.groups))}


                for slot in world.player_ids:
                    slot_data[slot] = world.worlds[slot].fill_slot_data()

                def precollect_hint(location):
                    entrance = er_hint_data.get(location.player, {}).get(location.address, "")
                    hint = NetUtils.Hint(location.item.player, location.player, location.address,
                                         location.item.code, False, entrance, location.item.flags)
                    precollected_hints[location.player].add(hint)
                    if location.item.player not in world.groups:
                        precollected_hints[location.item.player].add(hint)
                    else:
                        for player in world.groups[location.item.player]["players"]:
                            precollected_hints[player].add(hint)

                locations_data: Dict[int, Dict[int, Tuple[int, int, int]]] = {player: {} for player in world.player_ids}
                for location in world.get_filled_locations():
                    if type(location.address) == int:
                        assert location.item.code is not None, "item code None should be event, " \
                                                               "location.address should then also be None"
                        locations_data[location.player][location.address] = \
                            location.item.code, location.item.player, location.item.flags
                        if location.name in world.start_location_hints[location.player]:
                            precollect_hint(location)
                        elif location.item.name in world.start_hints[location.item.player]:
                            precollect_hint(location)
                        elif any([location.item.name in world.start_hints[player]
                                  for player in world.groups.get(location.item.player, {}).get("players", [])]):
                            precollect_hint(location)

                multidata = {
                    "slot_data": slot_data,
                    "slot_info": slot_info,
                    "names": names,  # TODO: remove around 0.2.5 in favor of slot_info
                    "games": games,  # TODO: remove around 0.2.5 in favor of slot_info
                    "connect_names": {name: (0, player) for player, name in world.player_name.items()},
                    "remote_items": {player for player in world.player_ids if
                                     world.worlds[player].remote_items},
                    "remote_start_inventory": {player for player in world.player_ids if
                                               world.worlds[player].remote_start_inventory},
                    "locations": locations_data,
                    "checks_in_area": checks_in_area,
                    "server_options": baked_server_options,
                    "er_hint_data": er_hint_data,
                    "precollected_items": precollected_items,
                    "precollected_hints": precollected_hints,
                    "version": tuple(version_tuple),
                    "tags": ["AP"],
                    "minimum_versions": minimum_versions,
                    "seed_name": world.seed_name
                }
                AutoWorld.call_all(world, "modify_multidata", multidata)

                multidata = zlib.compress(pickle.dumps(multidata), 9)

                with open(os.path.join(temp_dir, f'{outfilebase}.archipelago'), 'wb') as f:
                    f.write(bytes([3]))  # version of format
                    f.write(multidata)

            multidata_task = pool.submit(write_multidata)
            if not check_accessibility_task.result():
                if not world.can_beat_game():
                    raise Exception("Game appears as unbeatable. Aborting.")
                else:
                    logger.warning("Location Accessibility requirements not fulfilled.")

            # retrieve exceptions via .result() if they occurred.
            multidata_task.result()
            for i, future in enumerate(concurrent.futures.as_completed(output_file_futures), start=1):
                if i % 10 == 0 or i == len(output_file_futures):
                    logger.info(f'Generating output files ({i}/{len(output_file_futures)}).')
                future.result()

        if args.spoiler > 1:
            logger.info('Calculating playthrough.')
            create_playthrough(world)

        if args.spoiler:
            world.spoiler.to_file(os.path.join(temp_dir, '%s_Spoiler.txt' % outfilebase))

        zipfilename = output_path(f"AP_{world.seed_name}.zip")
        logger.info(f'Creating final archive at {zipfilename}.')
        with zipfile.ZipFile(zipfilename, mode="w", compression=zipfile.ZIP_DEFLATED,
                             compresslevel=9) as zf:
            for file in os.scandir(temp_dir):
                zf.write(file.path, arcname=file.name)

    logger.info('Done. Enjoy. Total Time: %s', time.perf_counter() - start)
    return world
def generate_itempool(world, player: int):
    if world.difficulty[player] not in difficulties:
        raise NotImplementedError(f"Diffulty {world.difficulty[player]}")
    if world.goal[player] not in {
            'ganon', 'pedestal', 'dungeons', 'triforcehunt',
            'localtriforcehunt', 'icerodhunt', 'ganontriforcehunt',
            'localganontriforcehunt', 'crystals', 'ganonpedestal'
    }:
        raise NotImplementedError(f"Goal {world.goal[player]}")
    if world.mode[player] not in {'open', 'standard', 'inverted'}:
        raise NotImplementedError(f"Mode {world.mode[player]}")
    if world.timer[player] not in {
            False, 'display', 'timed', 'timed-ohko', 'ohko', 'timed-countdown'
    }:
        raise NotImplementedError(f"Timer {world.mode[player]}")

    if world.timer[player] in ['ohko', 'timed-ohko']:
        world.can_take_damage[player] = False
    if world.goal[player] in [
            'pedestal', 'triforcehunt', 'localtriforcehunt', 'icerodhunt'
    ]:
        world.push_item(world.get_location('Ganon', player),
                        ItemFactory('Nothing', player), False)
    else:
        world.push_item(world.get_location('Ganon', player),
                        ItemFactory('Triforce', player), False)

    if world.goal[player] in ['triforcehunt', 'localtriforcehunt']:
        region = world.get_region('Light World', player)

        loc = Location(player, "Murahdahla", parent=region)
        loc.access_rule = lambda state: state.has_triforce_pieces(
            state.world.treasure_hunt_count[player], player)

        region.locations.append(loc)
        world.dynamic_locations.append(loc)

        world.clear_location_cache()

        world.push_item(loc, ItemFactory('Triforce', player), False)
        loc.event = True
        loc.locked = True

    if world.goal[player] == 'icerodhunt':
        world.progression_balancing[player] = False
        loc = world.get_location('Turtle Rock - Boss', player)
        world.push_item(loc, ItemFactory('Triforce', player), False)
        if world.boss_shuffle[player] != 'none':
            if 'turtle rock-' not in world.boss_shuffle[player]:
                world.boss_shuffle[
                    player] = f'Turtle Rock-Trinexx;{world.boss_shuffle[player]}'
            else:
                logging.warning(
                    f'Cannot guarantee that Trinexx is the boss of Turtle Rock for player {player}'
                )
        loc.event = True
        loc.locked = True
        forbid_items_for_player(
            loc, {
                'Red Pendant', 'Green Pendant', 'Blue Pendant', 'Crystal 5',
                'Crystal 6'
            }, player)
        itemdiff = difficulties[world.difficulty[player]]
        itempool = []
        itempool.extend(itemdiff.alwaysitems)
        itempool.remove('Ice Rod')

        itempool.extend(['Single Arrow', 'Sanctuary Heart Container'])
        itempool.extend(['Boss Heart Container'] *
                        itemdiff.boss_heart_container_limit)
        itempool.extend(['Piece of Heart'] * itemdiff.heart_piece_limit)
        itempool.extend(itemdiff.bottles)
        itempool.extend(itemdiff.basicbow)
        itempool.extend(itemdiff.basicarmor)
        if world.swords[player] != 'swordless':
            itempool.extend(itemdiff.basicsword)
        itempool.extend(itemdiff.basicmagic)
        itempool.extend(itemdiff.basicglove)
        itempool.extend(itemdiff.basicshield)
        itempool.extend(itemdiff.legacyinsanity)
        itempool.extend(['Rupees (300)'] * 34)
        itempool.extend(['Bombs (10)'] * 5)
        itempool.extend(['Arrows (10)'] * 7)
        if world.keyshuffle[player] == 'universal':
            itempool.extend(itemdiff.universal_keys)
            itempool.append('Small Key (Universal)')

        for item in itempool:
            world.push_precollected(ItemFactory(item, player))

    world.get_location('Ganon', player).event = True
    world.get_location('Ganon', player).locked = True
    event_pairs = [('Agahnim 1', 'Beat Agahnim 1'),
                   ('Agahnim 2', 'Beat Agahnim 2'),
                   ('Dark Blacksmith Ruins', 'Pick Up Purple Chest'),
                   ('Frog', 'Get Frog'), ('Missing Smith', 'Return Smith'),
                   ('Floodgate', 'Open Floodgate'),
                   ('Agahnim 1', 'Beat Agahnim 1'),
                   ('Flute Activation Spot', 'Activated Flute')]
    for location_name, event_name in event_pairs:
        location = world.get_location(location_name, player)
        event = ItemFactory(event_name, player)
        world.push_item(location, event, False)
        location.event = location.locked = True

    # set up item pool
    additional_triforce_pieces = 0
    if world.custom:
        (pool, placed_items, precollected_items, clock_mode,
         treasure_hunt_count,
         treasure_hunt_icon) = make_custom_item_pool(world, player)
        world.rupoor_cost = min(world.customitemarray[67], 9999)
    else:
        pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, \
        treasure_hunt_icon, additional_triforce_pieces = get_pool_core(world, player)

    for item in precollected_items:
        world.push_precollected(ItemFactory(item, player))

    if world.mode[player] == 'standard' and not world.state.has_melee_weapon(
            player):
        if "Link's Uncle" not in placed_items:
            found_sword = False
            found_bow = False
            possible_weapons = []
            for item in pool:
                if item in [
                        'Progressive Sword', 'Fighter Sword', 'Master Sword',
                        'Tempered Sword', 'Golden Sword'
                ]:
                    if not found_sword and world.swords[player] != 'swordless':
                        found_sword = True
                        possible_weapons.append(item)
                if item in ['Progressive Bow', 'Bow'] and not found_bow:
                    found_bow = True
                    possible_weapons.append(item)
                if item in [
                        'Hammer', 'Bombs (10)', 'Fire Rod', 'Cane of Somaria',
                        'Cane of Byrna'
                ]:
                    if item not in possible_weapons:
                        possible_weapons.append(item)
            starting_weapon = world.random.choice(possible_weapons)
            placed_items["Link's Uncle"] = starting_weapon
            pool.remove(starting_weapon)
        if placed_items["Link's Uncle"] in [
                'Bow', 'Progressive Bow', 'Bombs (10)', 'Cane of Somaria',
                'Cane of Byrna'
        ] and world.enemy_health[player] not in ['default', 'easy']:
            world.escape_assist[player].append('bombs')

    for (location, item) in placed_items.items():
        world.push_item(world.get_location(location, player),
                        ItemFactory(item, player), False)
        world.get_location(location, player).event = True
        world.get_location(location, player).locked = True

    items = ItemFactory(pool, player)

    if clock_mode is not None:
        world.clock_mode[player] = clock_mode

    if treasure_hunt_count is not None:
        world.treasure_hunt_count[player] = treasure_hunt_count % 999
    if treasure_hunt_icon is not None:
        world.treasure_hunt_icon[player] = treasure_hunt_icon

    dungeon_items = [
        item for item in get_dungeon_item_pool(world)
        if item.player == player and (
            (item.smallkey and world.keyshuffle[player]) or
            (item.bigkey and world.bigkeyshuffle[player]) or
            (item.map and world.mapshuffle[player]) or
            (item.compass and world.compassshuffle[player])
            or world.goal[player] == 'icerodhunt')
    ]

    if world.goal[player] == 'icerodhunt':
        for item in dungeon_items:
            world.itempool.append(
                ItemFactory(GetBeemizerItem(world, player, 'Nothing'), player))
            world.push_precollected(item)
    else:
        world.itempool.extend([item for item in dungeon_items])

    # logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
    # rather than making all hearts/heart pieces progression items (which slows down generation considerably)
    # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
    if world.goal[player] != 'icerodhunt' and world.difficulty[player] in [
            'easy', 'normal', 'hard'
    ] and not (world.custom and world.customitemarray[30] == 0):
        next(item for item in items
             if item.name == 'Boss Heart Container').advancement = True
    elif world.goal[player] != 'icerodhunt' and world.difficulty[player] in [
            'expert'
    ] and not (world.custom and world.customitemarray[29] < 4):
        adv_heart_pieces = (item for item in items
                            if item.name == 'Piece of Heart')
        for i in range(4):
            next(adv_heart_pieces).advancement = True

    progressionitems = []
    nonprogressionitems = []
    for item in items:
        if item.advancement or item.type:
            progressionitems.append(item)
        else:
            nonprogressionitems.append(
                GetBeemizerItem(world, item.player, item))
    world.random.shuffle(nonprogressionitems)

    if additional_triforce_pieces:
        if additional_triforce_pieces > len(nonprogressionitems):
            raise FillError(
                f"Not enough non-progression items to replace with Triforce pieces found for player "
                f"{world.get_player_names(player)}.")
        progressionitems += [ItemFactory("Triforce Piece", player)
                             ] * additional_triforce_pieces
        nonprogressionitems.sort(key=lambda item: int("Heart" in item.name)
                                 )  # try to keep hearts in the pool
        nonprogressionitems = nonprogressionitems[additional_triforce_pieces:]
        world.random.shuffle(nonprogressionitems)

    # shuffle medallions
    if world.required_medallions[player][0] == "random":
        mm_medallion = world.random.choice(['Ether', 'Quake', 'Bombos'])
    else:
        mm_medallion = world.required_medallions[player][0]
    if world.required_medallions[player][1] == "random":
        tr_medallion = world.random.choice(['Ether', 'Quake', 'Bombos'])
    else:
        tr_medallion = world.required_medallions[player][1]
    world.required_medallions[player] = (mm_medallion, tr_medallion)

    place_bosses(world, player)
    set_up_shops(world, player)

    if world.shop_shuffle[player]:
        shuffle_shops(world, nonprogressionitems, player)
    create_dynamic_shop_locations(world, player)

    world.itempool += progressionitems + nonprogressionitems

    if world.retro[player]:
        set_up_take_anys(world, player)  # depends on world.itempool to be set
Exemple #9
0
def generate_itempool(world, player):
    if (world.difficulty not in ['normal', 'hard', 'expert']
            or world.goal not in [
                'ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'
            ] or world.mode not in ['open', 'standard', 'inverted']
            or world.timer not in [
                'none', 'display', 'timed', 'timed-ohko', 'ohko',
                'timed-countdown'
            ] or world.progressive not in ['on', 'off', 'random']):
        raise NotImplementedError('Not supported yet')

    if world.timer in ['ohko', 'timed-ohko']:
        world.can_take_damage = False

    if world.goal in ['pedestal', 'triforcehunt']:
        world.push_item(world.get_location('Ganon', player),
                        ItemFactory('Nothing', player), False)
    else:
        world.push_item(world.get_location('Ganon', player),
                        ItemFactory('Triforce', player), False)

    if world.goal in ['triforcehunt']:
        if world.mode == 'inverted':
            region = world.get_region('Light World', player)
        else:
            region = world.get_region('Hyrule Castle Courtyard', player)

        loc = Location(player, "Murahdahla", parent=region)
        loc.access_rule = lambda state: state.item_count(
            'Triforce Piece', player) + state.item_count(
                'Power Star', player) > state.world.treasure_hunt_count
        region.locations.append(loc)
        world.dynamic_locations.append(loc)

        world.clear_location_cache()

        world.push_item(loc, ItemFactory('Triforce', player), False)
        loc.event = True
        loc.locked = True

    world.get_location('Ganon', player).event = True
    world.get_location('Ganon', player).locked = True
    world.push_item(world.get_location('Agahnim 1', player),
                    ItemFactory('Beat Agahnim 1', player), False)
    world.get_location('Agahnim 1', player).event = True
    world.get_location('Agahnim 1', player).locked = True
    world.push_item(world.get_location('Agahnim 2', player),
                    ItemFactory('Beat Agahnim 2', player), False)
    world.get_location('Agahnim 2', player).event = True
    world.get_location('Agahnim 2', player).locked = True
    world.push_item(world.get_location('Dark Blacksmith Ruins', player),
                    ItemFactory('Pick Up Purple Chest', player), False)
    world.get_location('Dark Blacksmith Ruins', player).event = True
    world.get_location('Dark Blacksmith Ruins', player).locked = True
    world.push_item(world.get_location('Frog', player),
                    ItemFactory('Get Frog', player), False)
    world.get_location('Frog', player).event = True
    world.get_location('Frog', player).locked = True
    world.push_item(world.get_location('Missing Smith', player),
                    ItemFactory('Return Smith', player), False)
    world.get_location('Missing Smith', player).event = True
    world.get_location('Missing Smith', player).locked = True
    world.push_item(world.get_location('Floodgate', player),
                    ItemFactory('Open Floodgate', player), False)
    world.get_location('Floodgate', player).event = True
    world.get_location('Floodgate', player).locked = True

    # set up item pool
    if world.custom:
        (pool, placed_items, precollected_items, clock_mode,
         treasure_hunt_count, treasure_hunt_icon,
         lamps_needed_for_dark_rooms) = make_custom_item_pool(
             world.progressive, world.shuffle, world.difficulty, world.timer,
             world.goal, world.mode, world.swords, world.retro,
             world.customitemarray)
        world.rupoor_cost = min(world.customitemarray[67], 9999)
    else:
        (pool, placed_items, precollected_items, clock_mode,
         treasure_hunt_count,
         treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(
             world.progressive, world.shuffle, world.difficulty, world.timer,
             world.goal, world.mode, world.swords, world.retro)
    world.itempool += ItemFactory(pool, player)
    for item in precollected_items:
        world.push_precollected(ItemFactory(item, player))
    for (location, item) in placed_items:
        world.push_item(world.get_location(location, player),
                        ItemFactory(item, player), False)
        world.get_location(location, player).event = True
        world.get_location(location, player).locked = True
    world.lamps_needed_for_dark_rooms = lamps_needed_for_dark_rooms
    if clock_mode is not None:
        world.clock_mode = clock_mode
    if treasure_hunt_count is not None:
        world.treasure_hunt_count = treasure_hunt_count
    if treasure_hunt_icon is not None:
        world.treasure_hunt_icon = treasure_hunt_icon

    if world.keysanity:
        world.itempool.extend([
            item for item in get_dungeon_item_pool(world)
            if item.player == player
        ])

    # logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
    # rather than making all hearts/heart pieces progression items (which slows down generation considerably)
    # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode)
    if world.difficulty in [
            'normal', 'hard'
    ] and not (world.custom and world.customitemarray[30] == 0):
        [
            item for item in world.itempool
            if item.name == 'Boss Heart Container' and item.player == player
        ][0].advancement = True
    elif world.difficulty in [
            'expert'
    ] and not (world.custom and world.customitemarray[29] < 4):
        adv_heart_pieces = [
            item for item in world.itempool
            if item.name == 'Piece of Heart' and item.player == player
        ][0:4]
        for hp in adv_heart_pieces:
            hp.advancement = True

    # shuffle medallions
    mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
    tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]
    world.required_medallions[player] = (mm_medallion, tr_medallion)

    place_bosses(world, player)
    set_up_shops(world, player)

    if world.retro:
        set_up_take_anys(world, player)

    create_dynamic_shop_locations(world, player)