def create_regions(world: MultiWorld, player: int): regOvr = Region("Menu", RegionType.Generic, "Dimension VVVVVV", player, world) locOvr_names = ["Overworld (Pipe-shaped Segment)", "Overworld (Left of Ship)", "Overworld (Square Room)", "Overworld (Sad Elephant)", "It's a Secret to Nobody", "Trench Warfare", "NPC Trinket", "V"] regOvr.locations += [V6Location(player, loc_name, location_table[loc_name], regOvr) for loc_name in locOvr_names] world.regions.append(regOvr) regLab = Region("Laboratory", RegionType.Generic, "Laboratory", player, world) locLab_names = ["Young Man, It's Worth the Challenge", "Overworld (Outside Entanglement Generator)", "The Tantalizing Trinket", "Purest Unobtainium"] regLab.locations += [V6Location(player, loc_name, location_table[loc_name], regLab) for loc_name in locLab_names] world.regions.append(regLab) regTow = Region("The Tower", RegionType.Generic, "The Tower", player, world) locTow_names = ["The Tower 1", "The Tower 2"] regTow.locations += [V6Location(player, loc_name, location_table[loc_name], regTow) for loc_name in locTow_names] world.regions.append(regTow) regSp2 = Region("Space Station 2", RegionType.Generic, "Space Station 2", player, world) locSp2_names = ["One Way Room", "You Just Keep Coming Back", "Clarion Call", "Prize for the Reckless", "Doing things the hard way"] regSp2.locations += [V6Location(player, loc_name, location_table[loc_name], regSp2) for loc_name in locSp2_names] world.regions.append(regSp2) regWrp = Region("Warp Zone", RegionType.Generic, "Warp Zone", player, world) locWrp_names = ["Edge Games"] regWrp.locations += [V6Location(player, loc_name, location_table[loc_name], regWrp) for loc_name in locWrp_names] world.regions.append(regWrp)
def create_region(world: MultiWorld, player: int, name: str, locat: WitnessPlayerLocations, region_locations=None, exits=None): """ Create an Archipelago Region for The Witness """ ret = Region(name, RegionType.Generic, name, player) ret.world = world if region_locations: for location in region_locations: loc_id = locat.CHECK_LOCATION_TABLE[location] check_hex = -1 if location in StaticWitnessLogic.CHECKS_BY_NAME: check_hex = int( StaticWitnessLogic.CHECKS_BY_NAME[location]["checkHex"], 0) location = WitnessLocation(player, location, loc_id, ret, check_hex) ret.locations.append(location) if exits: for single_exit in exits: ret.exits.append(Entrance(player, single_exit, ret)) return ret
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()
def create_regions(self): player = self.player menu = Region("Menu", RegionType.Generic, "Menu", player, self.world) crash = Entrance(player, "Crash Land", menu) menu.exits.append(crash) nauvis = Region("Nauvis", RegionType.Generic, "Nauvis", player, self.world) skip_silo = self.world.silo[self.player].value == Silo.option_spawn for tech_name, tech_id in base_tech_table.items(): if skip_silo and tech_name == "rocket-silo": continue tech = Location(player, tech_name, tech_id, nauvis) nauvis.locations.append(tech) tech.game = "Factorio" location = Location(player, "Rocket Launch", None, nauvis) nauvis.locations.append(location) location.game = "Factorio" event = FactorioItem("Victory", ItemClassification.progression, None, player) event.game = "Factorio" self.world.push_item(location, event, False) location.event = location.locked = True for ingredient in self.world.max_science_pack[ self.player].get_allowed_packs(): location = Location(player, f"Automate {ingredient}", None, nauvis) location.game = "Factorio" nauvis.locations.append(location) event = FactorioItem(f"Automated {ingredient}", ItemClassification.progression, None, player) self.world.push_item(location, event, False) location.event = location.locked = True crash.connect(nauvis) self.world.regions += [menu, nauvis]
def ChecksFinderRegion(region_name: str, exits=[]): ret = Region(region_name, RegionType.Generic, region_name, self.player, self.world) ret.locations = [ChecksFinderAdvancement(self.player, loc_name, loc_data.id, ret) for loc_name, loc_data in advancement_table.items() if loc_data.region == region_name] for exit in exits: ret.exits.append(Entrance(self.player, exit, ret)) return ret
def MCRegion(region_name: str, exits=[]): ret = Region(region_name, None, region_name, self.player, self.world) ret.locations = [ MinecraftAdvancement(self.player, loc_name, loc_data.id, ret) for loc_name, loc_data in advancement_table.items() if loc_data.region == region_name ] for exit in exits: ret.exits.append(Entrance(self.player, exit, ret)) return ret
def factorio_create_regions(world: MultiWorld, player: int): menu = Region("Menu", None, "Menu", player) crash = Entrance(player, "Crash Land", menu) menu.exits.append(crash) nauvis = Region("Nauvis", None, "Nauvis", player) nauvis.world = menu.world = world for tech_name, tech_id in tech_table.items(): tech = Location(player, tech_name, tech_id, nauvis) nauvis.locations.append(tech) tech.game = "Factorio" crash.connect(nauvis) world.regions += [menu, nauvis]
def create_region(world: MultiWorld, player: int, name: str, location_names=None, exits=None) -> Region: ret = Region(name, RegionType.Generic, name, player) ret.world = world if location_names: for location in location_names: loc_id = HKWorld.location_name_to_id.get(location, None) location = HKLocation(player, location, loc_id, ret) ret.locations.append(location) if exits: for exit in exits: ret.exits.append(Entrance(player, exit, ret)) return ret
def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None): ret = Region(name, RegionType.LightWorld, name, player) ret.world = world if locations: for loc in locations: location = self.locations[loc] location.parent_region = ret ret.locations.append(location) if exits: for exit in exits: ret.exits.append(Entrance(player, exit, ret)) return ret
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], location_cache: List[Location], name: str) -> Region: region = Region(name, RegionType.Generic, name, player) region.world = world if name in locations_per_region: for location_data in locations_per_region[name]: location = create_location(player, location_data, region, location_cache) region.locations.append(location) return region
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): ret = Region(name, None, name, player) ret.world = world if locations: for location in locations: loc_id = lookup_name_to_id.get(location, 0) location = HKLocation(player, location, loc_id, ret) ret.locations.append(location) if exits: for exit in exits: ret.exits.append(Entrance(player, exit, ret)) return ret
def copy_dynamic_regions_and_locations(world, ret): for region in world.dynamic_regions: new_reg = Region(region.name, region.type) ret.regions.append(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.replaceable) ret.shops.append(new_reg.shop) for location in world.dynamic_locations: new_loc = Location(location.name, location.address, location.crystal, location.hint_text, location.parent_region) new_reg = ret.get_region(location.parent_region.name) new_reg.locations.append(new_loc)
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): ret = Region(name, RegionType.Generic, name, player) ret.world = world if locations: for location in locations: loc_id = location_table.get(location, 0) location = SpireLocation(player, location, loc_id, ret) ret.locations.append(location) if exits: for exit in exits: ret.exits.append(Entrance(player, exit, ret)) return ret
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): # Shamelessly stolen from the ROR2 definition, lol ret = Region(name, RegionType.Generic, name, player) ret.world = world if locations: for location in locations: loc_id = location_table.get(location, 0) location = LegacyLocation(player, location, loc_id, ret) ret.locations.append(location) if exits: for exit in exits: ret.exits.append(Entrance(player, exit, ret)) return ret
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): ret = Region(name, RegionType.Generic, name, player) ret.world = world if locations: for location in locations: loc_id = locations_lookup_name_to_id.get(location, 0) locationObj = RaftLocation(player, location, loc_id, ret) ret.locations.append(locationObj) if exits: for exit in exits: ret.exits.append( Entrance(player, getConnectionName(name, exit), ret)) return ret
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None): region = Region(name, RegionType.Generic, name, player) region.world = world if locations: for location_name in locations.keys(): location = ArchipIDLELocation(player, location_name, locations[location_name], region) region.locations.append(location) if exits: for _exit in exits: region.exits.append(Entrance(player, _exit, region)) return region
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None): # Shamelessly stolen from the ROR2 definition ret = Region(name, None, name, player) ret.world = world if locations: for location in locations: loc_id = active_locations.get(location, 0) if loc_id: location = SA2BLocation(player, location, loc_id, ret) ret.locations.append(location) if exits: for exit in exits: ret.exits.append(Entrance(player, exit, ret)) return ret
def _create_region(name, type, locations=None, exits=None): ret = Region(name, type) if locations is None: locations = [] if exits is None: exits = [] for exit in exits: ret.exits.append(Entrance(exit, ret)) for location in locations: address, address2, default, type = location_table[location] ret.locations.append(Location(location, address, address2, default, type, ret)) return ret
def create_region(name, locations=None, exits=None): ret = Region(name) if locations is None: locations = [] if exits is None: exits = [] for exit in exits: ret.exits.append(Entrance(exit, ret)) for location in locations: address, crystal, hint_text = location_table[location] ret.locations.append( Location(location, address, crystal, hint_text, ret)) return ret
def create_region(name, locations=None, exits=None): ret = Region(name) if locations is None: locations = [] if exits is None: exits = [] ret.add_exits(*exits) ret.add_locations(*locations) return ret
def set_up_take_anys(world, player): # these are references, do not modify these lists in-place if world.mode[player] == 'inverted': take_any_locs = take_any_locations_inverted else: take_any_locs = take_any_locations regions = world.random.sample(take_any_locs, 5) old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player) world.regions.append(old_man_take_any) world.dynamic_regions.append(old_man_take_any) reg = regions.pop() entrance = world.get_region(reg, player).entrances[0] connect_entrance(world, entrance.name, old_man_take_any.name, player) entrance.target = 0x58 old_man_take_any.shop = TakeAny(old_man_take_any, 0x0112, 0xE2, True, True, total_shop_slots) world.shops.append(old_man_take_any.shop) swords = [ item for item in world.itempool if item.type == 'Sword' and item.player == player ] if swords: sword = world.random.choice(swords) world.itempool.remove(sword) world.itempool.append(ItemFactory('Rupees (20)', player)) old_man_take_any.shop.add_inventory(0, sword.name, 0, 0, create_location=True) else: old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0) for num in range(4): take_any = Region("Take-Any #{}".format(num + 1), RegionType.Cave, 'a cave of choice', player) world.regions.append(take_any) world.dynamic_regions.append(take_any) target, room_id = world.random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)]) reg = regions.pop() entrance = world.get_region(reg, player).entrances[0] connect_entrance(world, entrance.name, take_any.name, player) entrance.target = target take_any.shop = TakeAny(take_any, room_id, 0xE3, True, True, total_shop_slots + num + 1) world.shops.append(take_any.shop) take_any.shop.add_inventory(0, 'Blue Potion', 0, 0) take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0) world.initialize_regions()
def set_up_take_anys(world, player): if world.mode == 'inverted' and 'Dark Sanctuary Hint' in take_any_locations: take_any_locations.remove('Dark Sanctuary Hint') regions = random.sample(take_any_locations, 5) old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player) world.regions.append(old_man_take_any) world.dynamic_regions.append(old_man_take_any) reg = regions.pop() entrance = world.get_region(reg, player).entrances[0] connect_entrance(world, entrance, old_man_take_any, player) entrance.target = 0x58 old_man_take_any.shop = Shop(old_man_take_any, 0x0112, ShopType.TakeAny, 0xE2, True) world.shops.append(old_man_take_any.shop) old_man_take_any.shop.active = True swords = [ item for item in world.itempool if item.type == 'Sword' and item.player == player ] if swords: sword = random.choice(swords) world.itempool.remove(sword) world.itempool.append(ItemFactory('Rupees (20)', player)) old_man_take_any.shop.add_inventory(0, sword.name, 0, 0, create_location=True) else: old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0) for num in range(4): take_any = Region("Take-Any #{}".format(num + 1), RegionType.Cave, 'a cave of choice', player) world.regions.append(take_any) world.dynamic_regions.append(take_any) target, room_id = random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)]) reg = regions.pop() entrance = world.get_region(reg, player).entrances[0] connect_entrance(world, entrance, take_any, player) entrance.target = target take_any.shop = Shop(take_any, room_id, ShopType.TakeAny, 0xE3, True) world.shops.append(take_any.shop) take_any.shop.active = True take_any.shop.add_inventory(0, 'Blue Potion', 0, 0) take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0) world.intialize_regions()
def generate_multi_world(players: int = 1) -> MultiWorld: multi_world = MultiWorld(players) multi_world.player_name = {} for i in range(players): player_id = i + 1 world = World(multi_world, player_id) multi_world.game[player_id] = world multi_world.worlds[player_id] = world multi_world.player_name[player_id] = "Test Player " + str(player_id) region = Region("Menu", RegionType.Generic, "Menu Region Hint", player_id, multi_world) multi_world.regions.append(region) multi_world.set_seed(0) multi_world.set_default_common_options() return multi_world
def create_regions(self): # TODO: generate *some* regions from locations' requirements? r = Region('Menu', RegionType.Generic, 'Menu', self.player, self.world) r.exits = [Entrance(self.player, 'New Game', r)] self.world.regions += [r] r = Region('Ingame', RegionType.Generic, 'Ingame', self.player, self.world) r.locations = [SoELocation(self.player, loc.name, self.location_name_to_id[loc.name], r) for loc in _locations] r.locations.append(SoELocation(self.player, 'Done', None, r)) self.world.regions += [r] self.world.get_entrance('New Game', self.player).connect(self.world.get_region('Ingame', self.player))
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
def generate_region( self, parent: Region, size: int, access_rule: CollectionRule = lambda state: True) -> Region: region_tag = "_region" + str(len(self.regions)) region_name = "player" + str(self.id) + region_tag region = Region("player" + str(self.id) + region_tag, RegionType.Generic, "Region Hint", self.id, self.world) self.locations += generate_locations(size, self.id, None, region, region_tag) entrance = Entrance(self.id, region_name + "_entrance", parent) parent.exits.append(entrance) entrance.connect(region) entrance.access_rule = access_rule self.regions.append(region) self.world.regions.append(region) return region
def create_regions(world: MultiWorld, player: int): regSS = Region("Menu", RegionType.Generic, "Castle Area", player, world) locSS_names = [name for name, id in locSS_table.items()] locSS_names += [name for name, id in locCap_table.items()] regSS.locations += [ SM64Location(player, loc_name, location_table[loc_name], regSS) for loc_name in locSS_names ] world.regions.append(regSS) regBoB = Region("Bob-omb Battlefield", RegionType.Generic, "Bob-omb Battlefield", player, world) locBoB_names = [name for name, id in locBoB_table.items()] regBoB.locations += [ SM64Location(player, loc_name, location_table[loc_name], regBoB) for loc_name in locBoB_names ] if (world.EnableCoinStars[player].value): regBoB.locations.append( SM64Location(player, "BoB: 100 Coins", location_table["BoB: 100 Coins"], regBoB)) world.regions.append(regBoB) regWhomp = Region("Whomp's Fortress", RegionType.Generic, "Whomp's Fortress", player, world) locWhomp_names = [name for name, id in locWhomp_table.items()] regWhomp.locations += [ SM64Location(player, loc_name, location_table[loc_name], regWhomp) for loc_name in locWhomp_names ] if (world.EnableCoinStars[player].value): regWhomp.locations.append( SM64Location(player, "WF: 100 Coins", location_table["WF: 100 Coins"], regWhomp)) world.regions.append(regWhomp) regJRB = Region("Jolly Roger Bay", RegionType.Generic, "Jolly Roger Bay", player, world) locJRB_names = [name for name, id in locJRB_table.items()] regJRB.locations += [ SM64Location(player, loc_name, location_table[loc_name], regJRB) for loc_name in locJRB_names ] if (world.EnableCoinStars[player].value): regJRB.locations.append( SM64Location(player, "JRB: 100 Coins", location_table["JRB: 100 Coins"], regJRB)) world.regions.append(regJRB) regCCM = Region("Cool, Cool Mountain", RegionType.Generic, "Cool, Cool Mountain", player, world) locCCM_names = [name for name, id in locCCM_table.items()] regCCM.locations += [ SM64Location(player, loc_name, location_table[loc_name], regCCM) for loc_name in locCCM_names ] if (world.EnableCoinStars[player].value): regCCM.locations.append( SM64Location(player, "CCM: 100 Coins", location_table["CCM: 100 Coins"], regCCM)) world.regions.append(regCCM) regBBH = Region("Big Boo's Haunt", RegionType.Generic, "Big Boo's Haunt", player, world) locBBH_names = [name for name, id in locBBH_table.items()] regBBH.locations += [ SM64Location(player, loc_name, location_table[loc_name], regBBH) for loc_name in locBBH_names ] if (world.EnableCoinStars[player].value): regBBH.locations.append( SM64Location(player, "BBH: 100 Coins", location_table["BBH: 100 Coins"], regBBH)) world.regions.append(regBBH) regBitDW = Region("Bowser in the Dark World", RegionType.Generic, "Bowser in the Dark World", player, world) locBitDW_names = [name for name, id in locBitDW_table.items()] regBitDW.locations += [ SM64Location(player, loc_name, location_table[loc_name], regBitDW) for loc_name in locBitDW_names ] world.regions.append(regBitDW) regBasement = Region("Basement", RegionType.Generic, "Basement", player, world) world.regions.append(regBasement) regHMC = Region("Hazy Maze Cave", RegionType.Generic, "Hazy Maze Cave", player, world) locHMC_names = [name for name, id in locHMC_table.items()] regHMC.locations += [ SM64Location(player, loc_name, location_table[loc_name], regHMC) for loc_name in locHMC_names ] if (world.EnableCoinStars[player].value): regHMC.locations.append( SM64Location(player, "HMC: 100 Coins", location_table["HMC: 100 Coins"], regHMC)) world.regions.append(regHMC) regLLL = Region("Lethal Lava Land", RegionType.Generic, "Lethal Lava Land", player, world) locLLL_names = [name for name, id in locLLL_table.items()] regLLL.locations += [ SM64Location(player, loc_name, location_table[loc_name], regLLL) for loc_name in locLLL_names ] if (world.EnableCoinStars[player].value): regLLL.locations.append( SM64Location(player, "LLL: 100 Coins", location_table["LLL: 100 Coins"], regLLL)) world.regions.append(regLLL) regSSL = Region("Shifting Sand Land", RegionType.Generic, "Shifting Sand Land", player, world) locSSL_names = [name for name, id in locSSL_table.items()] regSSL.locations += [ SM64Location(player, loc_name, location_table[loc_name], regSSL) for loc_name in locSSL_names ] if (world.EnableCoinStars[player].value): regSSL.locations.append( SM64Location(player, "SSL: 100 Coins", location_table["SSL: 100 Coins"], regSSL)) world.regions.append(regSSL) regDDD = Region("Dire, Dire Docks", RegionType.Generic, "Dire, Dire Docks", player, world) locDDD_names = [name for name, id in locDDD_table.items()] regDDD.locations += [ SM64Location(player, loc_name, location_table[loc_name], regDDD) for loc_name in locDDD_names ] if (world.EnableCoinStars[player].value): regDDD.locations.append( SM64Location(player, "DDD: 100 Coins", location_table["DDD: 100 Coins"], regDDD)) world.regions.append(regDDD) regBitFS = Region("Bowser in the Fire Sea", RegionType.Generic, "Bowser in the Fire Sea", player, world) locBitFS_names = [name for name, id in locBitFS_table.items()] regBitFS.locations += [ SM64Location(player, loc_name, location_table[loc_name], regBitFS) for loc_name in locBitFS_names ] world.regions.append(regBitFS) regFloor2 = Region("Second Floor", RegionType.Generic, "Second Floor", player, world) world.regions.append(regFloor2) regSL = Region("Snowman's Land", RegionType.Generic, "Snowman's Land", player, world) locSL_names = [name for name, id in locSL_table.items()] regSL.locations += [ SM64Location(player, loc_name, location_table[loc_name], regSL) for loc_name in locSL_names ] if (world.EnableCoinStars[player].value): regSL.locations.append( SM64Location(player, "SL: 100 Coins", location_table["SL: 100 Coins"], regSL)) world.regions.append(regSL) regWDW = Region("Wet-Dry World", RegionType.Generic, "Wet-Dry World", player, world) locWDW_names = [name for name, id in locWDW_table.items()] regWDW.locations += [ SM64Location(player, loc_name, location_table[loc_name], regWDW) for loc_name in locWDW_names ] if (world.EnableCoinStars[player].value): regWDW.locations.append( SM64Location(player, "WDW: 100 Coins", location_table["WDW: 100 Coins"], regWDW)) world.regions.append(regWDW) regTTM = Region("Tall, Tall Mountain", RegionType.Generic, "Tall, Tall Mountain", player, world) locTTM_names = [name for name, id in locTTM_table.items()] regTTM.locations += [ SM64Location(player, loc_name, location_table[loc_name], regTTM) for loc_name in locTTM_names ] if (world.EnableCoinStars[player].value): regTTM.locations.append( SM64Location(player, "TTM: 100 Coins", location_table["TTM: 100 Coins"], regTTM)) world.regions.append(regTTM) regTHI = Region("Tiny-Huge Island", RegionType.Generic, "Tiny-Huge Island", player, world) locTHI_names = [name for name, id in locTHI_table.items()] regTHI.locations += [ SM64Location(player, loc_name, location_table[loc_name], regTHI) for loc_name in locTHI_names ] if (world.EnableCoinStars[player].value): regTHI.locations.append( SM64Location(player, "THI: 100 Coins", location_table["THI: 100 Coins"], regTHI)) world.regions.append(regTHI) regFloor3 = Region("Third Floor", RegionType.Generic, "Third Floor", player, world) world.regions.append(regFloor3) regTTC = Region("Tick Tock Clock", RegionType.Generic, "Tick Tock Clock", player, world) locTTC_names = [name for name, id in locTTC_table.items()] regTTC.locations += [ SM64Location(player, loc_name, location_table[loc_name], regTTC) for loc_name in locTTC_names ] if (world.EnableCoinStars[player].value): regTTC.locations.append( SM64Location(player, "TTC: 100 Coins", location_table["TTC: 100 Coins"], regTTC)) world.regions.append(regTTC) regRR = Region("Rainbow Ride", RegionType.Generic, "Rainbow Ride", player, world) locRR_names = [name for name, id in locRR_table.items()] regRR.locations += [ SM64Location(player, loc_name, location_table[loc_name], regRR) for loc_name in locRR_names ] if (world.EnableCoinStars[player].value): regRR.locations.append( SM64Location(player, "RR: 100 Coins", location_table["RR: 100 Coins"], regRR)) world.regions.append(regRR)
def create_region(self, name: str): return Region(name, RegionType.Generic, name, self.player, self.world)
def create_regions(world: MultiWorld, player: int): regions = ["First", "Second", "Third", "Last"] bosses = ["Meridian", "Ataraxia", "Merodach"] for x, name in enumerate(regions): fullname = f"{name} Quarter" insidename = fullname if x == 0: insidename = "Menu" region = Region(insidename, RegionType.Generic, fullname, player, world) for store in [ "Alpha Cache", "Beta Cache", "Gamma Cache", "Reward Chest" ]: for y in range(1, 7): loc_name = f"{store} {(x * 6) + y}" region.locations += [ MeritousLocation(player, loc_name, location_table[loc_name], region) ] if x < 3: storage_loc = f"PSI Key Storage {x + 1}" region.locations += [ MeritousLocation(player, storage_loc, location_table[storage_loc], region) ] region.exits += _generate_entrances(player, [f"To {bosses[x]}"], region) else: locations_end_game = [ "Place of Power", "The Last Place You'll Look" ] region.locations += [ MeritousLocation(player, loc_name, location_table[loc_name], region) for loc_name in locations_end_game ] region.exits += _generate_entrances(player, [ "Back to the entrance", "Back to the entrance with the Knife" ], region) world.regions += [region] for x, boss in enumerate(bosses): boss_region = Region(boss, RegionType.Generic, boss, player, world) boss_region.locations += [ MeritousLocation(player, boss, location_table[boss], boss_region), MeritousLocation(player, f"{boss} Defeat", None, boss_region) ] boss_region.exits = _generate_entrances( player, [f"To {regions[x + 1]} Quarter"], boss_region) world.regions.append(boss_region) region_final_boss = Region("Final Boss", RegionType.Generic, "Final Boss", player, world) region_final_boss.locations = [ MeritousLocation(player, "Wervyn Anixil", None, region_final_boss) ] world.regions.append(region_final_boss) region_tfb = Region("True Final Boss", RegionType.Generic, "True Final Boss", player, world) region_tfb.locations = [ MeritousLocation(player, "Wervyn Anixil?", None, region_tfb) ] world.regions.append(region_tfb) entrance_map = { "To Meridian": { "to": "Meridian", "rule": lambda state: state.has_group("PSI Keys", player, 1) and state. has_group("Important Artifacts", player, 1) }, "To Second Quarter": { "to": "Second Quarter", "rule": lambda state: state.has("Meridian Defeated", player) }, "To Ataraxia": { "to": "Ataraxia", "rule": lambda state: state.has_group("PSI Keys", player, 2) and state. has_group("Important Artifacts", player, 2) }, "To Third Quarter": { "to": "Third Quarter", "rule": lambda state: state.has("Ataraxia Defeated", player) }, "To Merodach": { "to": "Merodach", "rule": lambda state: state.has_group("PSI Keys", player, 3) and state. has_group("Important Artifacts", player, 3) }, "To Last Quarter": { "to": "Last Quarter", "rule": lambda state: state.has("Merodach Defeated", player) }, "Back to the entrance": { "to": "Final Boss", "rule": lambda state: state.has("Cursed Seal", player) }, "Back to the entrance with the Knife": { "to": "True Final Boss", "rule": lambda state: state.has_all(["Cursed Seal", "Agate Knife"], player) } } for entrance in entrance_map: connection_data = entrance_map[entrance] connection = world.get_entrance(entrance, player) connection.access_rule = connection_data["rule"] connection.connect(world.get_region(connection_data["to"], player))
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