Undertale: Fixes a major logic bug, and updates Undertale to use the new Options API (#3528)

* Updated the options definitions to the new api

* Fixed the wrong base class being used for UndertaleOptions

* Undertale: Added get_filler_item_name to Undertale, changed multiworld.per_slot_randoms to self.random, removed some unused imports in options.py, and fixed rules.py still using state.multiworld instead of world.options, and simplified the set_completion_rules function in rules.py

* Undertale: Fixed it trying to add strings to the finished item pool

* fixed 1000g item not being in the key items pool for Undertale

* Removed ".copy()" for the junk_weights, reformatted the requested lines to have less new lines, and changed "itempool += [self.create_filler()]" to "itempool.append(self.create_filler())"
This commit is contained in:
Mewlif 2024-06-21 12:21:46 -04:00 committed by GitHub
parent ce37bed7c6
commit 40c9dfd3bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 136 additions and 118 deletions

View File

@ -105,7 +105,6 @@ item_table = {
non_key_items = { non_key_items = {
"Butterscotch Pie": 1, "Butterscotch Pie": 1,
"500G": 2, "500G": 2,
"1000G": 2,
"Face Steak": 1, "Face Steak": 1,
"Snowman Piece": 1, "Snowman Piece": 1,
"Instant Noodles": 1, "Instant Noodles": 1,
@ -147,6 +146,7 @@ plot_items = {
key_items = { key_items = {
"Complete Skeleton": 1, "Complete Skeleton": 1,
"Fish": 1, "Fish": 1,
"1000G": 2,
"DT Extractor": 1, "DT Extractor": 1,
"Mettaton Plush": 1, "Mettaton Plush": 1,
"Punch Card": 3, "Punch Card": 3,

View File

@ -1,5 +1,5 @@
import typing from Options import Choice, Toggle, Range, PerGameCommonOptions
from Options import Choice, Option, Toggle, Range from dataclasses import dataclass
class RouteRequired(Choice): class RouteRequired(Choice):
@ -86,17 +86,17 @@ class RandoBattleOptions(Toggle):
default = 0 default = 0
undertale_options: typing.Dict[str, type(Option)] = { @dataclass
"route_required": RouteRequired, class UndertaleOptions(PerGameCommonOptions):
"starting_area": StartingArea, route_required: RouteRequired
"key_hunt": KeyHunt, starting_area: StartingArea
"key_pieces": KeyPieces, key_hunt: KeyHunt
"rando_love": RandomizeLove, key_pieces: KeyPieces
"rando_stats": RandomizeStats, rando_love: RandomizeLove
"temy_include": IncludeTemy, rando_stats: RandomizeStats
"no_equips": NoEquips, temy_include: IncludeTemy
"only_flakes": OnlyFlakes, no_equips: NoEquips
"prog_armor": ProgressiveArmor, only_flakes: OnlyFlakes
"prog_weapons": ProgressiveWeapons, prog_armor: ProgressiveArmor
"rando_item_button": RandoBattleOptions, prog_weapons: ProgressiveWeapons
} rando_item_button: RandoBattleOptions

View File

@ -1,18 +1,22 @@
from worlds.generic.Rules import set_rule, add_rule, add_item_rule from worlds.generic.Rules import set_rule, add_rule
from BaseClasses import MultiWorld, CollectionState from BaseClasses import CollectionState
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from . import UndertaleWorld
def _undertale_is_route(state: CollectionState, player: int, route: int): def _undertale_is_route(world: "UndertaleWorld", route: int):
if route == 3: if route == 3:
return state.multiworld.route_required[player].current_key == "all_routes" return world.options.route_required.current_key == "all_routes"
if state.multiworld.route_required[player].current_key == "all_routes": if world.options.route_required.current_key == "all_routes":
return True return True
if route == 0: if route == 0:
return state.multiworld.route_required[player].current_key == "neutral" return world.options.route_required.current_key == "neutral"
if route == 1: if route == 1:
return state.multiworld.route_required[player].current_key == "pacifist" return world.options.route_required.current_key == "pacifist"
if route == 2: if route == 2:
return state.multiworld.route_required[player].current_key == "genocide" return world.options.route_required.current_key == "genocide"
return False return False
@ -27,7 +31,7 @@ def _undertale_has_plot(state: CollectionState, player: int, item: str):
return state.has("DT Extractor", player) return state.has("DT Extractor", player)
def _undertale_can_level(state: CollectionState, exp: int, lvl: int): def _undertale_can_level(exp: int, lvl: int):
if exp >= 10 and lvl == 1: if exp >= 10 and lvl == 1:
return True return True
elif exp >= 30 and lvl == 2: elif exp >= 30 and lvl == 2:
@ -70,7 +74,9 @@ def _undertale_can_level(state: CollectionState, exp: int, lvl: int):
# Sets rules on entrances and advancements that are always applied # Sets rules on entrances and advancements that are always applied
def set_rules(multiworld: MultiWorld, player: int): def set_rules(world: "UndertaleWorld"):
player = world.player
multiworld = world.multiworld
set_rule(multiworld.get_entrance("Ruins Hub", player), lambda state: state.has("Ruins Key", player)) set_rule(multiworld.get_entrance("Ruins Hub", player), lambda state: state.has("Ruins Key", player))
set_rule(multiworld.get_entrance("Snowdin Hub", player), lambda state: state.has("Snowdin Key", player)) set_rule(multiworld.get_entrance("Snowdin Hub", player), lambda state: state.has("Snowdin Key", player))
set_rule(multiworld.get_entrance("Waterfall Hub", player), lambda state: state.has("Waterfall Key", player)) set_rule(multiworld.get_entrance("Waterfall Hub", player), lambda state: state.has("Waterfall Key", player))
@ -81,8 +87,8 @@ def set_rules(multiworld: MultiWorld, player: int):
set_rule(multiworld.get_entrance("New Home Exit", player), set_rule(multiworld.get_entrance("New Home Exit", player),
lambda state: (state.has("Left Home Key", player) and lambda state: (state.has("Left Home Key", player) and
state.has("Right Home Key", player)) or state.has("Right Home Key", player)) or
state.has("Key Piece", player, state.multiworld.key_pieces[player].value)) state.has("Key Piece", player, world.options.key_pieces.value))
if _undertale_is_route(multiworld.state, player, 1): if _undertale_is_route(world, 1):
set_rule(multiworld.get_entrance("Papyrus\" Home Entrance", player), set_rule(multiworld.get_entrance("Papyrus\" Home Entrance", player),
lambda state: _undertale_has_plot(state, player, "Complete Skeleton")) lambda state: _undertale_has_plot(state, player, "Complete Skeleton"))
set_rule(multiworld.get_entrance("Undyne\"s Home Entrance", player), set_rule(multiworld.get_entrance("Undyne\"s Home Entrance", player),
@ -90,7 +96,7 @@ def set_rules(multiworld: MultiWorld, player: int):
set_rule(multiworld.get_entrance("Lab Elevator", player), set_rule(multiworld.get_entrance("Lab Elevator", player),
lambda state: state.has("Alphys Date", player) and state.has("DT Extractor", player) and lambda state: state.has("Alphys Date", player) and state.has("DT Extractor", player) and
((state.has("Left Home Key", player) and state.has("Right Home Key", player)) or ((state.has("Left Home Key", player) and state.has("Right Home Key", player)) or
state.has("Key Piece", player, state.multiworld.key_pieces[player].value))) state.has("Key Piece", player, world.options.key_pieces.value)))
set_rule(multiworld.get_location("Alphys Date", player), set_rule(multiworld.get_location("Alphys Date", player),
lambda state: state.can_reach("New Home", "Region", player) and state.has("Undyne Letter EX", player) lambda state: state.can_reach("New Home", "Region", player) and state.has("Undyne Letter EX", player)
and state.has("Undyne Date", player)) and state.has("Undyne Date", player))
@ -101,7 +107,10 @@ def set_rules(multiworld: MultiWorld, player: int):
set_rule(multiworld.get_location("True Lab Plot", player), set_rule(multiworld.get_location("True Lab Plot", player),
lambda state: state.can_reach("New Home", "Region", player) lambda state: state.can_reach("New Home", "Region", player)
and state.can_reach("Letter Quest", "Location", player) and state.can_reach("Letter Quest", "Location", player)
and state.can_reach("Alphys Date", "Location", player)) and state.can_reach("Alphys Date", "Location", player)
and ((state.has("Left Home Key", player) and
state.has("Right Home Key", player)) or
state.has("Key Piece", player, world.options.key_pieces.value)))
set_rule(multiworld.get_location("Chisps Machine", player), set_rule(multiworld.get_location("Chisps Machine", player),
lambda state: state.can_reach("True Lab", "Region", player)) lambda state: state.can_reach("True Lab", "Region", player))
set_rule(multiworld.get_location("Dog Sale 1", player), set_rule(multiworld.get_location("Dog Sale 1", player),
@ -118,7 +127,7 @@ def set_rules(multiworld: MultiWorld, player: int):
lambda state: state.can_reach("News Show", "Region", player) and state.has("Hot Dog...?", player, 1)) lambda state: state.can_reach("News Show", "Region", player) and state.has("Hot Dog...?", player, 1))
set_rule(multiworld.get_location("Letter Quest", player), set_rule(multiworld.get_location("Letter Quest", player),
lambda state: state.can_reach("Last Corridor", "Region", player) and state.has("Undyne Date", player)) lambda state: state.can_reach("Last Corridor", "Region", player) and state.has("Undyne Date", player))
if (not _undertale_is_route(multiworld.state, player, 2)) or _undertale_is_route(multiworld.state, player, 3): if (not _undertale_is_route(world, 2)) or _undertale_is_route(world, 3):
set_rule(multiworld.get_location("Nicecream Punch Card", player), set_rule(multiworld.get_location("Nicecream Punch Card", player),
lambda state: state.has("Punch Card", player, 3) and state.can_reach("Waterfall", "Region", player)) lambda state: state.has("Punch Card", player, 3) and state.can_reach("Waterfall", "Region", player))
set_rule(multiworld.get_location("Nicecream Snowdin", player), set_rule(multiworld.get_location("Nicecream Snowdin", player),
@ -129,26 +138,26 @@ def set_rules(multiworld: MultiWorld, player: int):
lambda state: state.can_reach("Waterfall", "Region", player)) lambda state: state.can_reach("Waterfall", "Region", player))
set_rule(multiworld.get_location("Apron Hidden", player), set_rule(multiworld.get_location("Apron Hidden", player),
lambda state: state.can_reach("Cooking Show", "Region", player)) lambda state: state.can_reach("Cooking Show", "Region", player))
if _undertale_is_route(multiworld.state, player, 2) and \ if _undertale_is_route(world, 2) and \
(bool(multiworld.rando_love[player].value) or bool(multiworld.rando_stats[player].value)): (bool(world.options.rando_love.value) or bool(world.options.rando_stats.value)):
maxlv = 1 maxlv = 1
exp = 190 exp = 190
curarea = "Old Home" curarea = "Old Home"
while maxlv < 20: while maxlv < 20:
maxlv += 1 maxlv += 1
if multiworld.rando_love[player]: if world.options.rando_love:
set_rule(multiworld.get_location(("LOVE " + str(maxlv)), player), lambda state: False) set_rule(multiworld.get_location(("LOVE " + str(maxlv)), player), lambda state: False)
if multiworld.rando_stats[player]: if world.options.rando_stats:
set_rule(multiworld.get_location(("ATK "+str(maxlv)), player), lambda state: False) set_rule(multiworld.get_location(("ATK "+str(maxlv)), player), lambda state: False)
set_rule(multiworld.get_location(("HP "+str(maxlv)), player), lambda state: False) set_rule(multiworld.get_location(("HP "+str(maxlv)), player), lambda state: False)
if maxlv in {5, 9, 13, 17}: if maxlv in {5, 9, 13, 17}:
set_rule(multiworld.get_location(("DEF "+str(maxlv)), player), lambda state: False) set_rule(multiworld.get_location(("DEF "+str(maxlv)), player), lambda state: False)
maxlv = 1 maxlv = 1
while maxlv < 20: while maxlv < 20:
while _undertale_can_level(multiworld.state, exp, maxlv): while _undertale_can_level(exp, maxlv):
maxlv += 1 maxlv += 1
if multiworld.rando_stats[player]: if world.options.rando_stats:
if curarea == "Old Home": if curarea == "Old Home":
add_rule(multiworld.get_location(("ATK "+str(maxlv)), player), add_rule(multiworld.get_location(("ATK "+str(maxlv)), player),
lambda state: (state.can_reach("Old Home", "Region", player)), combine="or") lambda state: (state.can_reach("Old Home", "Region", player)), combine="or")
@ -197,7 +206,7 @@ def set_rules(multiworld: MultiWorld, player: int):
if maxlv == 5 or maxlv == 9 or maxlv == 13 or maxlv == 17: if maxlv == 5 or maxlv == 9 or maxlv == 13 or maxlv == 17:
add_rule(multiworld.get_location(("DEF "+str(maxlv)), player), add_rule(multiworld.get_location(("DEF "+str(maxlv)), player),
lambda state: (state.can_reach("New Home Exit", "Entrance", player)), combine="or") lambda state: (state.can_reach("New Home Exit", "Entrance", player)), combine="or")
if multiworld.rando_love[player]: if world.options.rando_love:
if curarea == "Old Home": if curarea == "Old Home":
add_rule(multiworld.get_location(("LOVE "+str(maxlv)), player), add_rule(multiworld.get_location(("LOVE "+str(maxlv)), player),
lambda state: (state.can_reach("Old Home", "Region", player)), combine="or") lambda state: (state.can_reach("Old Home", "Region", player)), combine="or")
@ -307,9 +316,9 @@ def set_rules(multiworld: MultiWorld, player: int):
# Sets rules on completion condition # Sets rules on completion condition
def set_completion_rules(multiworld: MultiWorld, player: int): def set_completion_rules(world: "UndertaleWorld"):
completion_requirements = lambda state: state.can_reach("Barrier", "Region", player) player = world.player
if _undertale_is_route(multiworld.state, player, 1): multiworld = world.multiworld
completion_requirements = lambda state: state.can_reach("True Lab", "Region", player) multiworld.completion_condition[player] = lambda state: state.can_reach("Barrier", "Region", player)
if _undertale_is_route(world, 1):
multiworld.completion_condition[player] = lambda state: completion_requirements(state) multiworld.completion_condition[player] = lambda state: state.can_reach("True Lab", "Region", player)

View File

@ -5,9 +5,9 @@ from .Regions import undertale_regions, link_undertale_areas
from .Rules import set_rules, set_completion_rules from .Rules import set_rules, set_completion_rules
from worlds.generic.Rules import exclusion_rules from worlds.generic.Rules import exclusion_rules
from BaseClasses import Region, Entrance, Tutorial, Item from BaseClasses import Region, Entrance, Tutorial, Item
from .Options import undertale_options from .Options import UndertaleOptions
from worlds.AutoWorld import World, WebWorld from worlds.AutoWorld import World, WebWorld
from worlds.LauncherComponents import Component, components, Type from worlds.LauncherComponents import Component, components
from multiprocessing import Process from multiprocessing import Process
@ -46,7 +46,8 @@ class UndertaleWorld(World):
from their underground prison. from their underground prison.
""" """
game = "Undertale" game = "Undertale"
option_definitions = undertale_options options_dataclass = UndertaleOptions
options: UndertaleOptions
web = UndertaleWeb() web = UndertaleWeb()
item_name_to_id = {name: data.code for name, data in item_table.items()} item_name_to_id = {name: data.code for name, data in item_table.items()}
@ -54,39 +55,55 @@ class UndertaleWorld(World):
def _get_undertale_data(self): def _get_undertale_data(self):
return { return {
"world_seed": self.multiworld.per_slot_randoms[self.player].getrandbits(32), "world_seed": self.random.getrandbits(32),
"seed_name": self.multiworld.seed_name, "seed_name": self.multiworld.seed_name,
"player_name": self.multiworld.get_player_name(self.player), "player_name": self.multiworld.get_player_name(self.player),
"player_id": self.player, "player_id": self.player,
"client_version": self.required_client_version, "client_version": self.required_client_version,
"race": self.multiworld.is_race, "race": self.multiworld.is_race,
"route": self.multiworld.route_required[self.player].current_key, "route": self.options.route_required.current_key,
"starting_area": self.multiworld.starting_area[self.player].current_key, "starting_area": self.options.starting_area.current_key,
"temy_armor_include": bool(self.multiworld.temy_include[self.player].value), "temy_armor_include": bool(self.options.temy_include.value),
"only_flakes": bool(self.multiworld.only_flakes[self.player].value), "only_flakes": bool(self.options.only_flakes.value),
"no_equips": bool(self.multiworld.no_equips[self.player].value), "no_equips": bool(self.options.no_equips.value),
"key_hunt": bool(self.multiworld.key_hunt[self.player].value), "key_hunt": bool(self.options.key_hunt.value),
"key_pieces": self.multiworld.key_pieces[self.player].value, "key_pieces": self.options.key_pieces.value,
"rando_love": bool(self.multiworld.rando_love[self.player].value), "rando_love": bool(self.options.rando_love.value),
"rando_stats": bool(self.multiworld.rando_stats[self.player].value), "rando_stats": bool(self.options.rando_stats.value),
"prog_armor": bool(self.multiworld.prog_armor[self.player].value), "prog_armor": bool(self.options.prog_armor.value),
"prog_weapons": bool(self.multiworld.prog_weapons[self.player].value), "prog_weapons": bool(self.options.prog_weapons.value),
"rando_item_button": bool(self.multiworld.rando_item_button[self.player].value) "rando_item_button": bool(self.options.rando_item_button.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): def create_items(self):
self.multiworld.get_location("Undyne Date", self.player).place_locked_item(self.create_item("Undyne Date")) 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("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")) self.multiworld.get_location("Papyrus Date", self.player).place_locked_item(self.create_item("Papyrus Date"))
# Generate item pool # Generate item pool
itempool = [] itempool = []
if self.multiworld.route_required[self.player] == "all_routes": if self.options.route_required == "all_routes":
junk_pool = junk_weights_all.copy() junk_pool = junk_weights_all.copy()
elif self.multiworld.route_required[self.player] == "genocide": elif self.options.route_required == "genocide":
junk_pool = junk_weights_genocide.copy() junk_pool = junk_weights_genocide.copy()
elif self.multiworld.route_required[self.player] == "neutral": elif self.options.route_required == "neutral":
junk_pool = junk_weights_neutral.copy() junk_pool = junk_weights_neutral.copy()
elif self.multiworld.route_required[self.player] == "pacifist": elif self.options.route_required == "pacifist":
junk_pool = junk_weights_pacifist.copy() junk_pool = junk_weights_pacifist.copy()
else: else:
junk_pool = junk_weights_all.copy() junk_pool = junk_weights_all.copy()
@ -99,73 +116,68 @@ class UndertaleWorld(World):
itempool += [name] * num itempool += [name] * num
for name, num in non_key_items.items(): for name, num in non_key_items.items():
itempool += [name] * num itempool += [name] * num
if self.multiworld.rando_item_button[self.player]: if self.options.rando_item_button:
itempool += ["ITEM"] itempool += ["ITEM"]
else: else:
self.multiworld.push_precollected(self.create_item("ITEM")) self.multiworld.push_precollected(self.create_item("ITEM"))
self.multiworld.push_precollected(self.create_item("FIGHT")) self.multiworld.push_precollected(self.create_item("FIGHT"))
self.multiworld.push_precollected(self.create_item("ACT")) self.multiworld.push_precollected(self.create_item("ACT"))
self.multiworld.push_precollected(self.create_item("MERCY")) self.multiworld.push_precollected(self.create_item("MERCY"))
if self.multiworld.route_required[self.player] == "genocide": if self.options.route_required == "genocide":
itempool = [item for item in itempool if item != "Popato Chisps" and item != "Stained Apron" and 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"] item != "Nice Cream" and item != "Hot Cat" and item != "Hot Dog...?" and item != "Punch Card"]
elif self.multiworld.route_required[self.player] == "neutral": elif self.options.route_required == "neutral":
itempool = [item for item in itempool if item != "Popato Chisps" and item != "Hot Cat" and itempool = [item for item in itempool if item != "Popato Chisps" and item != "Hot Cat" and
item != "Hot Dog...?"] item != "Hot Dog...?"]
if self.multiworld.route_required[self.player] == "pacifist" or \ if self.options.route_required == "pacifist" or self.options.route_required == "all_routes":
self.multiworld.route_required[self.player] == "all_routes":
itempool += ["Undyne Letter EX"] itempool += ["Undyne Letter EX"]
else: else:
itempool.remove("Complete Skeleton") itempool.remove("Complete Skeleton")
itempool.remove("Fish") itempool.remove("Fish")
itempool.remove("DT Extractor") itempool.remove("DT Extractor")
itempool.remove("Hush Puppy") itempool.remove("Hush Puppy")
if self.multiworld.key_hunt[self.player]: if self.options.key_hunt:
itempool += ["Key Piece"] * self.multiworld.key_pieces[self.player].value itempool += ["Key Piece"] * self.options.key_pieces.value
else: else:
itempool += ["Left Home Key"] itempool += ["Left Home Key"]
itempool += ["Right Home Key"] itempool += ["Right Home Key"]
if not self.multiworld.rando_love[self.player] or \ if not self.options.rando_love or \
(self.multiworld.route_required[self.player] != "genocide" and (self.options.route_required != "genocide" and self.options.route_required != "all_routes"):
self.multiworld.route_required[self.player] != "all_routes"):
itempool = [item for item in itempool if not item == "LOVE"] itempool = [item for item in itempool if not item == "LOVE"]
if not self.multiworld.rando_stats[self.player] or \ if not self.options.rando_stats or \
(self.multiworld.route_required[self.player] != "genocide" and (self.options.route_required != "genocide" and self.options.route_required != "all_routes"):
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")] 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]: if self.options.temy_include:
itempool += ["temy armor"] itempool += ["temy armor"]
if self.multiworld.no_equips[self.player]: if self.options.no_equips:
itempool = [item for item in itempool if item not in required_armor and item not in required_weapons] itempool = [item for item in itempool if item not in required_armor and item not in required_weapons]
else: else:
if self.multiworld.prog_armor[self.player]: if self.options.prog_armor:
itempool = [item if (item not in required_armor and not item == "temy armor") else itempool = [item if (item not in required_armor and not item == "temy armor") else
"Progressive Armor" for item in itempool] "Progressive Armor" for item in itempool]
if self.multiworld.prog_weapons[self.player]: if self.options.prog_weapons:
itempool = [item if item not in required_weapons else "Progressive Weapons" for item in itempool] itempool = [item if item not in required_weapons else "Progressive Weapons" for item in itempool]
if self.multiworld.route_required[self.player] == "genocide" or \ if self.options.route_required == "genocide" or \
self.multiworld.route_required[self.player] == "all_routes": self.options.route_required == "all_routes":
if not self.multiworld.only_flakes[self.player]: if not self.options.only_flakes:
itempool += ["Snowman Piece"] * 2 itempool += ["Snowman Piece"] * 2
if not self.multiworld.no_equips[self.player]: if not self.options.no_equips:
itempool = ["Real Knife" if item == "Worn Dagger" else "The Locket" itempool = ["Real Knife" if item == "Worn Dagger" else "The Locket"
if item == "Heart Locket" else item for item in itempool] if item == "Heart Locket" else item for item in itempool]
if self.multiworld.only_flakes[self.player]: if self.options.only_flakes:
itempool = [item for item in itempool if item not in non_key_items] 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" starting_key = self.options.starting_area.current_key.title() + " Key"
itempool.remove(starting_key) itempool.remove(starting_key)
self.multiworld.push_precollected(self.create_item(starting_key)) self.multiworld.push_precollected(self.create_item(starting_key))
# Choose locations to automatically exclude based on settings # Choose locations to automatically exclude based on settings
exclusion_pool = set() exclusion_pool = set()
exclusion_pool.update(exclusion_table[self.multiworld.route_required[self.player].current_key]) exclusion_pool.update(exclusion_table[self.options.route_required.current_key])
if not self.multiworld.rando_love[self.player] or \ if not self.options.rando_love or \
(self.multiworld.route_required[self.player] != "genocide" and (self.options.route_required != "genocide" and self.options.route_required != "all_routes"):
self.multiworld.route_required[self.player] != "all_routes"):
exclusion_pool.update(exclusion_table["NoLove"]) exclusion_pool.update(exclusion_table["NoLove"])
if not self.multiworld.rando_stats[self.player] or \ if not self.options.rando_stats or \
(self.multiworld.route_required[self.player] != "genocide" and (self.options.route_required != "genocide" and self.options.route_required != "all_routes"):
self.multiworld.route_required[self.player] != "all_routes"):
exclusion_pool.update(exclusion_table["NoStats"]) exclusion_pool.update(exclusion_table["NoStats"])
# Choose locations to automatically exclude based on settings # Choose locations to automatically exclude based on settings
@ -173,20 +185,17 @@ class UndertaleWorld(World):
exclusion_checks.update(["Nicecream Punch Card", "Hush Trade"]) exclusion_checks.update(["Nicecream Punch Card", "Hush Trade"])
exclusion_rules(self.multiworld, self.player, exclusion_checks) 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 # Convert itempool into real items
itempool = [item for item in map(lambda name: self.create_item(name), itempool)] 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 self.multiworld.itempool += itempool
def set_rules(self): def set_rules(self):
set_rules(self.multiworld, self.player) set_rules(self)
set_completion_rules(self.multiworld, self.player) set_completion_rules(self)
def create_regions(self): def create_regions(self):
def UndertaleRegion(region_name: str, exits=[]): def UndertaleRegion(region_name: str, exits=[]):
@ -195,14 +204,14 @@ class UndertaleWorld(World):
for loc_name, loc_data in advancement_table.items() for loc_name, loc_data in advancement_table.items()
if loc_data.region == region_name and if loc_data.region == region_name and
(loc_name not in exclusion_table["NoStats"] or (loc_name not in exclusion_table["NoStats"] or
(self.multiworld.rando_stats[self.player] and (self.options.rando_stats and
(self.multiworld.route_required[self.player] == "genocide" or (self.options.route_required == "genocide" or
self.multiworld.route_required[self.player] == "all_routes"))) and self.options.route_required == "all_routes"))) and
(loc_name not in exclusion_table["NoLove"] or (loc_name not in exclusion_table["NoLove"] or
(self.multiworld.rando_love[self.player] and (self.options.rando_love and
(self.multiworld.route_required[self.player] == "genocide" or (self.options.route_required == "genocide" or
self.multiworld.route_required[self.player] == "all_routes"))) and self.options.route_required == "all_routes"))) and
loc_name not in exclusion_table[self.multiworld.route_required[self.player].current_key]] loc_name not in exclusion_table[self.options.route_required.current_key]]
for exit in exits: for exit in exits:
ret.exits.append(Entrance(self.player, exit, ret)) ret.exits.append(Entrance(self.player, exit, ret))
return ret return ret
@ -212,11 +221,11 @@ class UndertaleWorld(World):
def fill_slot_data(self): def fill_slot_data(self):
slot_data = self._get_undertale_data() slot_data = self._get_undertale_data()
for option_name in undertale_options: for option_name in self.options.as_dict():
option = getattr(self.multiworld, option_name)[self.player] option = getattr(self.multiworld, option_name)[self.player]
if (option_name == "rando_love" or option_name == "rando_stats") and \ if (option_name == "rando_love" or option_name == "rando_stats") and \
self.multiworld.route_required[self.player] != "genocide" and \ self.options.route_required != "genocide" and \
self.multiworld.route_required[self.player] != "all_routes": self.options.route_required != "all_routes":
option.value = False option.value = False
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}: if slot_data.get(option_name, None) is None and type(option.value) in {str, int}:
slot_data[option_name] = int(option.value) slot_data[option_name] = int(option.value)