232 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
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 UndertaleOptions
 | 
						|
from worlds.AutoWorld import World, WebWorld
 | 
						|
from worlds.LauncherComponents import Component, components
 | 
						|
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"
 | 
						|
    options_dataclass = UndertaleOptions
 | 
						|
    options: UndertaleOptions
 | 
						|
    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()}
 | 
						|
 | 
						|
    def _get_undertale_data(self):
 | 
						|
        return {
 | 
						|
            "world_seed": self.random.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.options.route_required.current_key,
 | 
						|
            "starting_area": self.options.starting_area.current_key,
 | 
						|
            "temy_armor_include": bool(self.options.temy_include.value),
 | 
						|
            "only_flakes": bool(self.options.only_flakes.value),
 | 
						|
            "no_equips": bool(self.options.no_equips.value),
 | 
						|
            "key_hunt": bool(self.options.key_hunt.value),
 | 
						|
            "key_pieces": int(self.options.key_pieces.value),
 | 
						|
            "rando_love": bool(self.options.rando_love and (self.options.route_required == "genocide" or self.options.route_required == "all_routes")),
 | 
						|
            "rando_stats": bool(self.options.rando_stats and (self.options.route_required == "genocide" or self.options.route_required == "all_routes")),
 | 
						|
            "prog_armor": bool(self.options.prog_armor.value),
 | 
						|
            "prog_weapons": bool(self.options.prog_weapons.value),
 | 
						|
            "rando_item_button": bool(self.options.rando_item_button.value),
 | 
						|
            "route_required": int(self.options.route_required.value),
 | 
						|
            "temy_include": int(self.options.temy_include.value)
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
    def get_filler_item_name(self):
 | 
						|
        if self.options.route_required == "all_routes":
 | 
						|
            junk_pool = junk_weights_all
 | 
						|
        elif self.options.route_required == "genocide":
 | 
						|
            junk_pool = junk_weights_genocide
 | 
						|
        elif self.options.route_required == "neutral":
 | 
						|
            junk_pool = junk_weights_neutral
 | 
						|
        elif self.options.route_required == "pacifist":
 | 
						|
            junk_pool = junk_weights_pacifist
 | 
						|
        else:
 | 
						|
            junk_pool = junk_weights_all
 | 
						|
        if not self.options.only_flakes:
 | 
						|
            return self.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()))[0]
 | 
						|
        else:
 | 
						|
            return "Temmie Flakes"
 | 
						|
 | 
						|
    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.options.route_required == "all_routes":
 | 
						|
            junk_pool = junk_weights_all.copy()
 | 
						|
        elif self.options.route_required == "genocide":
 | 
						|
            junk_pool = junk_weights_genocide.copy()
 | 
						|
        elif self.options.route_required == "neutral":
 | 
						|
            junk_pool = junk_weights_neutral.copy()
 | 
						|
        elif self.options.route_required == "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.options.rando_item_button:
 | 
						|
            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.options.route_required == "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.options.route_required == "neutral":
 | 
						|
            itempool = [item for item in itempool if item != "Popato Chisps" and item != "Hot Cat" and
 | 
						|
                        item != "Hot Dog...?"]
 | 
						|
        if self.options.route_required == "pacifist" or self.options.route_required == "all_routes":
 | 
						|
            itempool += ["Undyne Letter EX"]
 | 
						|
        else:
 | 
						|
            itempool.remove("Complete Skeleton")
 | 
						|
            itempool.remove("Fish")
 | 
						|
            itempool.remove("DT Extractor")
 | 
						|
            itempool.remove("Hush Puppy")
 | 
						|
        if self.options.key_hunt:
 | 
						|
            itempool += ["Key Piece"] * self.options.key_pieces.value
 | 
						|
        else:
 | 
						|
            itempool += ["Left Home Key"]
 | 
						|
            itempool += ["Right Home Key"]
 | 
						|
        if not self.options.rando_love or \
 | 
						|
                (self.options.route_required != "genocide" and self.options.route_required != "all_routes"):
 | 
						|
            itempool = [item for item in itempool if not item == "LOVE"]
 | 
						|
        if not self.options.rando_stats or \
 | 
						|
                (self.options.route_required != "genocide" and self.options.route_required != "all_routes"):
 | 
						|
            itempool = [item for item in itempool if not (item == "ATK Up" or item == "DEF Up" or item == "HP Up")]
 | 
						|
        if self.options.temy_include:
 | 
						|
            itempool += ["temy armor"]
 | 
						|
        if self.options.no_equips:
 | 
						|
            itempool = [item for item in itempool if item not in required_armor and item not in required_weapons]
 | 
						|
        else:
 | 
						|
            if self.options.prog_armor:
 | 
						|
                itempool = [item if (item not in required_armor and not item == "temy armor") else
 | 
						|
                            "Progressive Armor" for item in itempool]
 | 
						|
            if self.options.prog_weapons:
 | 
						|
                itempool = [item if item not in required_weapons else "Progressive Weapons" for item in itempool]
 | 
						|
        if self.options.route_required == "genocide" or \
 | 
						|
                self.options.route_required == "all_routes":
 | 
						|
            if not self.options.only_flakes:
 | 
						|
                itempool += ["Snowman Piece"] * 2
 | 
						|
            if not self.options.no_equips:
 | 
						|
                itempool = ["Real Knife" if item == "Worn Dagger" else "The Locket"
 | 
						|
                            if item == "Heart Locket" else item for item in itempool]
 | 
						|
        if self.options.only_flakes:
 | 
						|
            itempool = [item for item in itempool if item not in non_key_items]
 | 
						|
 | 
						|
        starting_key = self.options.starting_area.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.options.route_required.current_key])
 | 
						|
        if not self.options.rando_love or \
 | 
						|
                (self.options.route_required != "genocide" and self.options.route_required != "all_routes"):
 | 
						|
            exclusion_pool.update(exclusion_table["NoLove"])
 | 
						|
        if not self.options.rando_stats or \
 | 
						|
                (self.options.route_required != "genocide" and self.options.route_required != "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)
 | 
						|
 | 
						|
        # Convert itempool into real items
 | 
						|
        itempool = [item for item in map(lambda name: self.create_item(name), itempool)]
 | 
						|
        # Fill remaining items with randomly generated junk or Temmie Flakes
 | 
						|
        while len(itempool) < len(self.multiworld.get_unfilled_locations(self.player)):
 | 
						|
            itempool.append(self.create_filler())
 | 
						|
 | 
						|
        self.multiworld.itempool += itempool
 | 
						|
 | 
						|
    def set_rules(self):
 | 
						|
        set_rules(self)
 | 
						|
        set_completion_rules(self)
 | 
						|
 | 
						|
    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.options.rando_stats and
 | 
						|
                               (self.options.route_required == "genocide" or
 | 
						|
                                self.options.route_required == "all_routes"))) and
 | 
						|
                              (loc_name not in exclusion_table["NoLove"] or
 | 
						|
                              (self.options.rando_love and
 | 
						|
                               (self.options.route_required == "genocide" or
 | 
						|
                                self.options.route_required == "all_routes"))) and
 | 
						|
                              loc_name not in exclusion_table[self.options.route_required.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):
 | 
						|
        return self._get_undertale_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
 |