from .Items import UndertaleItem, item_table, required_armor, required_weapons, non_key_items, key_items, \ junk_weights_all, plot_items, junk_weights_neutral, junk_weights_pacifist, junk_weights_genocide from .Locations import UndertaleAdvancement, advancement_table, exclusion_table from .Regions import undertale_regions, link_undertale_areas from .Rules import set_rules, set_completion_rules from worlds.generic.Rules import exclusion_rules from BaseClasses import Region, Entrance, Tutorial, Item from .Options import undertale_options from worlds.AutoWorld import World, WebWorld from worlds.LauncherComponents import Component, components, Type from multiprocessing import Process def run_client(): print('running undertale client') from .UndertaleClient import main # lazy import p = Process(target=main) p.start() components.append(Component("Undertale Client", "UndertaleClient")) # components.append(Component("Undertale Client", func=run_client)) def data_path(file_name: str): import pkgutil return pkgutil.get_data(__name__, "data/" + file_name) class UndertaleWeb(WebWorld): tutorials = [Tutorial( "Multiworld Setup Guide", "A guide to setting up the Archipelago Undertale software on your computer. This guide covers " "single-player, multiworld, and related software.", "English", "setup_en.md", "setup/en", ["Mewlif"] )] class UndertaleWorld(World): """ Undertale is an RPG where every choice you make matters. You could choose to hurt all the enemies, eventually causing genocide of the monster species. Or you can spare all the enemies, befriending them and freeing them from their underground prison. """ game = "Undertale" option_definitions = undertale_options web = UndertaleWeb() item_name_to_id = {name: data.code for name, data in item_table.items()} location_name_to_id = {name: data.id for name, data in advancement_table.items()} data_version = 7 def _get_undertale_data(self): return { "world_seed": self.multiworld.per_slot_randoms[self.player].getrandbits(32), "seed_name": self.multiworld.seed_name, "player_name": self.multiworld.get_player_name(self.player), "player_id": self.player, "client_version": self.required_client_version, "race": self.multiworld.is_race, "route": self.multiworld.route_required[self.player].current_key, "starting_area": self.multiworld.starting_area[self.player].current_key, "temy_armor_include": bool(self.multiworld.temy_include[self.player].value), "only_flakes": bool(self.multiworld.only_flakes[self.player].value), "no_equips": bool(self.multiworld.no_equips[self.player].value), "key_hunt": bool(self.multiworld.key_hunt[self.player].value), "key_pieces": self.multiworld.key_pieces[self.player].value, "rando_love": bool(self.multiworld.rando_love[self.player].value), "rando_stats": bool(self.multiworld.rando_stats[self.player].value), "prog_armor": bool(self.multiworld.prog_armor[self.player].value), "prog_weapons": bool(self.multiworld.prog_weapons[self.player].value), "rando_item_button": bool(self.multiworld.rando_item_button[self.player].value) } def create_items(self): self.multiworld.get_location("Undyne Date", self.player).place_locked_item(self.create_item("Undyne Date")) self.multiworld.get_location("Alphys Date", self.player).place_locked_item(self.create_item("Alphys Date")) self.multiworld.get_location("Papyrus Date", self.player).place_locked_item(self.create_item("Papyrus Date")) # Generate item pool itempool = [] if self.multiworld.route_required[self.player] == "all_routes": junk_pool = junk_weights_all.copy() elif self.multiworld.route_required[self.player] == "genocide": junk_pool = junk_weights_genocide.copy() elif self.multiworld.route_required[self.player] == "neutral": junk_pool = junk_weights_neutral.copy() elif self.multiworld.route_required[self.player] == "pacifist": junk_pool = junk_weights_pacifist.copy() else: junk_pool = junk_weights_all.copy() # Add all required progression items for name, num in key_items.items(): itempool += [name] * num for name, num in required_armor.items(): itempool += [name] * num for name, num in required_weapons.items(): itempool += [name] * num for name, num in non_key_items.items(): itempool += [name] * num if self.multiworld.rando_item_button[self.player]: itempool += ["ITEM"] else: self.multiworld.push_precollected(self.create_item("ITEM")) self.multiworld.push_precollected(self.create_item("FIGHT")) self.multiworld.push_precollected(self.create_item("ACT")) self.multiworld.push_precollected(self.create_item("MERCY")) if self.multiworld.route_required[self.player] == "genocide": itempool = [item for item in itempool if item != "Popato Chisps" and item != "Stained Apron" and item != "Nice Cream" and item != "Hot Cat" and item != "Hot Dog...?" and item != "Punch Card"] elif self.multiworld.route_required[self.player] == "neutral": itempool = [item for item in itempool if item != "Popato Chisps" and item != "Hot Cat" and item != "Hot Dog...?"] if self.multiworld.route_required[self.player] == "pacifist" or \ self.multiworld.route_required[self.player] == "all_routes": itempool += ["Undyne Letter EX"] else: itempool.remove("Complete Skeleton") itempool.remove("Fish") itempool.remove("DT Extractor") itempool.remove("Hush Puppy") if self.multiworld.key_hunt[self.player]: itempool += ["Key Piece"] * self.multiworld.key_pieces[self.player].value else: itempool += ["Left Home Key"] itempool += ["Right Home Key"] if not self.multiworld.rando_love[self.player] or \ (self.multiworld.route_required[self.player] != "genocide" and self.multiworld.route_required[self.player] != "all_routes"): itempool = [item for item in itempool if not item == "LOVE"] if not self.multiworld.rando_stats[self.player] or \ (self.multiworld.route_required[self.player] != "genocide" and self.multiworld.route_required[self.player] != "all_routes"): itempool = [item for item in itempool if not (item == "ATK Up" or item == "DEF Up" or item == "HP Up")] if self.multiworld.temy_include[self.player]: itempool += ["temy armor"] if self.multiworld.no_equips[self.player]: itempool = [item for item in itempool if item not in required_armor and item not in required_weapons] else: if self.multiworld.prog_armor[self.player]: itempool = [item if (item not in required_armor and not item == "temy armor") else "Progressive Armor" for item in itempool] if self.multiworld.prog_weapons[self.player]: itempool = [item if item not in required_weapons else "Progressive Weapons" for item in itempool] if self.multiworld.route_required[self.player] == "genocide" or \ self.multiworld.route_required[self.player] == "all_routes": if not self.multiworld.only_flakes[self.player]: itempool += ["Snowman Piece"] * 2 if not self.multiworld.no_equips[self.player]: itempool = ["Real Knife" if item == "Worn Dagger" else "The Locket" if item == "Heart Locket" else item for item in itempool] if self.multiworld.only_flakes[self.player]: itempool = [item for item in itempool if item not in non_key_items] starting_key = self.multiworld.starting_area[self.player].current_key.title() + " Key" itempool.remove(starting_key) self.multiworld.push_precollected(self.create_item(starting_key)) # Choose locations to automatically exclude based on settings exclusion_pool = set() exclusion_pool.update(exclusion_table[self.multiworld.route_required[self.player].current_key]) if not self.multiworld.rando_love[self.player] or \ (self.multiworld.route_required[self.player] != "genocide" and self.multiworld.route_required[self.player] != "all_routes"): exclusion_pool.update(exclusion_table["NoLove"]) if not self.multiworld.rando_stats[self.player] or \ (self.multiworld.route_required[self.player] != "genocide" and self.multiworld.route_required[self.player] != "all_routes"): exclusion_pool.update(exclusion_table["NoStats"]) # Choose locations to automatically exclude based on settings exclusion_checks = set() exclusion_checks.update(["Nicecream Punch Card", "Hush Trade"]) exclusion_rules(self.multiworld, self.player, exclusion_checks) # Fill remaining items with randomly generated junk or Temmie Flakes if not self.multiworld.only_flakes[self.player]: itempool += self.multiworld.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()), k=len(self.location_names)-len(itempool)-len(exclusion_pool)) else: itempool += ["Temmie Flakes"] * (len(self.location_names) - len(itempool) - len(exclusion_pool)) # Convert itempool into real items itempool = [item for item in map(lambda name: self.create_item(name), itempool)] self.multiworld.itempool += itempool def set_rules(self): set_rules(self.multiworld, self.player) set_completion_rules(self.multiworld, self.player) def create_regions(self): def UndertaleRegion(region_name: str, exits=[]): ret = Region(region_name, self.player, self.multiworld) ret.locations += [UndertaleAdvancement(self.player, loc_name, loc_data.id, ret) for loc_name, loc_data in advancement_table.items() if loc_data.region == region_name and (loc_name not in exclusion_table["NoStats"] or (self.multiworld.rando_stats[self.player] and (self.multiworld.route_required[self.player] == "genocide" or self.multiworld.route_required[self.player] == "all_routes"))) and (loc_name not in exclusion_table["NoLove"] or (self.multiworld.rando_love[self.player] and (self.multiworld.route_required[self.player] == "genocide" or self.multiworld.route_required[self.player] == "all_routes"))) and loc_name not in exclusion_table[self.multiworld.route_required[self.player].current_key]] for exit in exits: ret.exits.append(Entrance(self.player, exit, ret)) return ret self.multiworld.regions += [UndertaleRegion(*r) for r in undertale_regions] link_undertale_areas(self.multiworld, self.player) def fill_slot_data(self): slot_data = self._get_undertale_data() for option_name in undertale_options: option = getattr(self.multiworld, option_name)[self.player] if (option_name == "rando_love" or option_name == "rando_stats") and \ self.multiworld.route_required[self.player] != "genocide" and \ self.multiworld.route_required[self.player] != "all_routes": option.value = False if slot_data.get(option_name, None) is None and type(option.value) in {str, int}: slot_data[option_name] = int(option.value) return slot_data def create_item(self, name: str) -> Item: item_data = item_table[name] item = UndertaleItem(name, item_data.classification, item_data.code, self.player) return item