Core: auto alias (#1022)

* Test: check that default templates can be parsed into Option objects
This commit is contained in:
Fabian Dill 2022-09-16 00:32:30 +02:00 committed by GitHub
parent 156e9e0e43
commit af11fa5150
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 70 additions and 38 deletions

View File

@ -26,13 +26,18 @@ class AssembleOptions(abc.ABCMeta):
attrs["name_lookup"].update({option_id: name for name, option_id in new_options.items()}) attrs["name_lookup"].update({option_id: name for name, option_id in new_options.items()})
options.update(new_options) options.update(new_options)
# apply aliases, without name_lookup # apply aliases, without name_lookup
aliases = {name[6:].lower(): option_id for name, option_id in attrs.items() if aliases = {name[6:].lower(): option_id for name, option_id in attrs.items() if
name.startswith("alias_")} name.startswith("alias_")}
assert "random" not in aliases, "Choice option 'random' cannot be manually assigned." assert "random" not in aliases, "Choice option 'random' cannot be manually assigned."
# auto-alias Off and On being parsed as True and False
if "off" in options:
options["false"] = options["off"]
if "on" in options:
options["true"] = options["on"]
options.update(aliases) options.update(aliases)
# auto-validate schema on __init__ # auto-validate schema on __init__

View File

@ -274,14 +274,12 @@ Define a property `option_<name> = <number>` per selectable value and
`default = <number>` to set the default selection. Aliases can be set by `default = <number>` to set the default selection. Aliases can be set by
defining a property `alias_<name> = <same number>`. defining a property `alias_<name> = <same number>`.
One special case where aliases are required is when option name is `yes`, `no`,
`on` or `off` because they parse to `True` or `False`:
```python ```python
option_off = 0 option_off = 0
option_on = 1 option_on = 1
option_some = 2 option_some = 2
alias_false = 0 alias_disabled = 0
alias_true = 1 alias_enabled = 1
default = 0 default = 0
``` ```

View File

@ -14,9 +14,20 @@ class TestFileGeneration(unittest.TestCase):
def testOptions(self): def testOptions(self):
WebHost.create_options_files() WebHost.create_options_files()
self.assertTrue(os.path.exists(os.path.join(self.correct_path, "static", "generated", "configs"))) target = os.path.join(self.correct_path, "static", "generated", "configs")
self.assertTrue(os.path.exists(target))
self.assertFalse(os.path.exists(os.path.join(self.incorrect_path, "static", "generated", "configs"))) self.assertFalse(os.path.exists(os.path.join(self.incorrect_path, "static", "generated", "configs")))
# folder seems fine, so now we try to generate Options based on the default file
from WebHostLib.check import roll_options
file: os.DirEntry
for file in os.scandir(target):
if file.is_file() and file.name.endswith(".yaml"):
with self.subTest(file=file.name):
with open(file) as f:
for value in roll_options({file.name: f.read()})[0].values():
self.assertTrue(value is True, f"Default Options for template {file.name} cannot be run.")
def testTutorial(self): def testTutorial(self):
WebHost.create_ordered_tutorials_file() WebHost.create_ordered_tutorials_file()
self.assertTrue(os.path.exists(os.path.join(self.correct_path, "static", "generated", "tutorials.json"))) self.assertTrue(os.path.exists(os.path.join(self.correct_path, "static", "generated", "tutorials.json")))

View File

@ -39,8 +39,6 @@ class OpenPyramid(Choice):
option_auto = 3 option_auto = 3
default = option_goal default = option_goal
alias_true = option_open
alias_false = option_closed
alias_yes = option_open alias_yes = option_open
alias_no = option_closed alias_no = option_closed
@ -159,8 +157,6 @@ class Progressive(Choice):
option_off = 0 option_off = 0
option_grouped_random = 1 option_grouped_random = 1
option_on = 2 option_on = 2
alias_false = 0
alias_true = 2
default = 2 default = 2
def want_progressives(self, random): def want_progressives(self, random):
@ -202,8 +198,6 @@ class Hints(Choice):
option_on = 2 option_on = 2
option_full = 3 option_full = 3
default = 2 default = 2
alias_false = 0
alias_true = 2
class Scams(Choice): class Scams(Choice):
@ -213,7 +207,6 @@ class Scams(Choice):
option_king_zora = 1 option_king_zora = 1
option_bottle_merchant = 2 option_bottle_merchant = 2
option_all = 3 option_all = 3
alias_false = 0
@property @property
def gives_king_zora_hint(self): def gives_king_zora_hint(self):
@ -293,7 +286,6 @@ class HeartBeep(Choice):
option_half = 2 option_half = 2
option_quarter = 3 option_quarter = 3
option_off = 4 option_off = 4
alias_false = 4
class HeartColor(Choice): class HeartColor(Choice):

View File

@ -137,8 +137,6 @@ class Progressive(Choice):
option_off = 0 option_off = 0
option_grouped_random = 1 option_grouped_random = 1
option_on = 2 option_on = 2
alias_false = 0
alias_true = 2
default = 2 default = 2
def want_progressives(self, random): def want_progressives(self, random):

View File

@ -409,7 +409,6 @@ class DeathLink(Choice):
shade: DeathLink functions like a normal death if you do not already have a shade, shadeless otherwise. shade: DeathLink functions like a normal death if you do not already have a shade, shadeless otherwise.
""" """
option_off = 0 option_off = 0
alias_false = 0
alias_no = 0 alias_no = 0
alias_true = 1 alias_true = 1
alias_on = 1 alias_on = 1
@ -435,10 +434,8 @@ class CostSanity(Choice):
These costs can be in Geo (except Grubfather, Seer and Eggshop), Grubs, Charms, Essence and/or Rancid Eggs These costs can be in Geo (except Grubfather, Seer and Eggshop), Grubs, Charms, Essence and/or Rancid Eggs
""" """
option_off = 0 option_off = 0
alias_false = 0
alias_no = 0 alias_no = 0
option_on = 1 option_on = 1
alias_true = 1
alias_yes = 1 alias_yes = 1
option_shopsonly = 2 option_shopsonly = 2
option_notshops = 3 option_notshops = 3

View File

@ -101,7 +101,6 @@ class InteriorEntrances(Choice):
option_off = 0 option_off = 0
option_simple = 1 option_simple = 1
option_all = 2 option_all = 2
alias_false = 0
alias_true = 2 alias_true = 2
@ -141,7 +140,6 @@ class MixEntrancePools(Choice):
option_off = 0 option_off = 0
option_indoor = 1 option_indoor = 1
option_all = 2 option_all = 2
alias_false = 0
class DecoupleEntrances(Toggle): class DecoupleEntrances(Toggle):
@ -308,7 +306,6 @@ class ShopShuffle(Choice):
option_off = 0 option_off = 0
option_fixed_number = 1 option_fixed_number = 1
option_random_number = 2 option_random_number = 2
alias_false = 0
class ShopSlots(Range): class ShopSlots(Range):
@ -326,7 +323,6 @@ class TokenShuffle(Choice):
option_dungeons = 1 option_dungeons = 1
option_overworld = 2 option_overworld = 2
option_all = 3 option_all = 3
alias_false = 0
class ScrubShuffle(Choice): class ScrubShuffle(Choice):
@ -336,7 +332,6 @@ class ScrubShuffle(Choice):
option_low = 1 option_low = 1
option_regular = 2 option_regular = 2
option_random_prices = 3 option_random_prices = 3
alias_false = 0
alias_affordable = 1 alias_affordable = 1
alias_expensive = 2 alias_expensive = 2
@ -569,7 +564,6 @@ class Hints(Choice):
option_agony = 2 option_agony = 2
option_always = 3 option_always = 3
default = 3 default = 3
alias_false = 0
class MiscHints(DefaultOnToggle): class MiscHints(DefaultOnToggle):
@ -673,8 +667,6 @@ class IceTraps(Choice):
option_mayhem = 3 option_mayhem = 3
option_onslaught = 4 option_onslaught = 4
default = 1 default = 1
alias_false = 0
alias_true = 2
alias_extra = 2 alias_extra = 2
@ -742,7 +734,6 @@ class Music(Choice):
option_normal = 0 option_normal = 0
option_off = 1 option_off = 1
option_randomized = 2 option_randomized = 2
alias_false = 1
class BackgroundMusic(Music): class BackgroundMusic(Music):

View File

@ -122,8 +122,6 @@ class AreaRandomization(Choice):
option_off = 0 option_off = 0
option_light = 1 option_light = 1
option_on = 2 option_on = 2
alias_false = 0
alias_true = 2
default = 0 default = 0
class AreaLayout(Toggle): class AreaLayout(Toggle):

View File

@ -1,48 +1,57 @@
import typing import typing
from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink, Choice from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink, Choice
class EnableCoinStars(DefaultOnToggle): class EnableCoinStars(DefaultOnToggle):
"""Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything""" """Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything"""
display_name = "Enable 100 Coin Stars" display_name = "Enable 100 Coin Stars"
class StrictCapRequirements(DefaultOnToggle): class StrictCapRequirements(DefaultOnToggle):
"""If disabled, Stars that expect special caps may have to be acquired without the caps""" """If disabled, Stars that expect special caps may have to be acquired without the caps"""
display_name = "Strict Cap Requirements" display_name = "Strict Cap Requirements"
class StrictCannonRequirements(DefaultOnToggle): class StrictCannonRequirements(DefaultOnToggle):
"""If disabled, Stars that expect cannons may have to be acquired without them. Only makes a difference if Buddy Checks are enabled""" """If disabled, Stars that expect cannons may have to be acquired without them. Only makes a difference if Buddy Checks are enabled"""
display_name = "Strict Cannon Requirements" display_name = "Strict Cannon Requirements"
class FirstBowserStarDoorCost(Range): class FirstBowserStarDoorCost(Range):
"""How many stars are required at the Star Door to Bowser in the Dark World""" """How many stars are required at the Star Door to Bowser in the Dark World"""
range_start = 0 range_start = 0
range_end = 50 range_end = 50
default = 8 default = 8
class BasementStarDoorCost(Range): class BasementStarDoorCost(Range):
"""How many stars are required at the Star Door in the Basement""" """How many stars are required at the Star Door in the Basement"""
range_start = 0 range_start = 0
range_end = 70 range_end = 70
default = 30 default = 30
class SecondFloorStarDoorCost(Range): class SecondFloorStarDoorCost(Range):
"""How many stars are required to access the third floor""" """How many stars are required to access the third floor"""
range_start = 0 range_start = 0
range_end = 90 range_end = 90
default = 50 default = 50
class MIPS1Cost(Range): class MIPS1Cost(Range):
"""How many stars are required to spawn MIPS the first time""" """How many stars are required to spawn MIPS the first time"""
range_start = 0 range_start = 0
range_end = 40 range_end = 40
default = 15 default = 15
class MIPS2Cost(Range): class MIPS2Cost(Range):
"""How many stars are required to spawn MIPS the secound time. Must be bigger or equal MIPS1Cost""" """How many stars are required to spawn MIPS the secound time. Must be bigger or equal MIPS1Cost"""
range_start = 0 range_start = 0
range_end = 80 range_end = 80
default = 50 default = 50
class StarsToFinish(Range): class StarsToFinish(Range):
"""How many stars are required at the infinite stairs""" """How many stars are required at the infinite stairs"""
display_name = "Endless Stairs Stars" display_name = "Endless Stairs Stars"
@ -50,35 +59,40 @@ class StarsToFinish(Range):
range_end = 100 range_end = 100
default = 70 default = 70
class AmountOfStars(Range): class AmountOfStars(Range):
"""How many stars exist. Disabling 100 Coin Stars removes 15 from the Pool. At least max of any Cost set""" """How many stars exist. Disabling 100 Coin Stars removes 15 from the Pool. At least max of any Cost set"""
range_start = 35 range_start = 35
range_end = 120 range_end = 120
default = 120 default = 120
class AreaRandomizer(Choice): class AreaRandomizer(Choice):
"""Randomize Entrances""" """Randomize Entrances"""
display_name = "Entrance Randomizer" display_name = "Entrance Randomizer"
alias_false = 0
option_Off = 0 option_Off = 0
option_Courses_Only = 1 option_Courses_Only = 1
option_Courses_and_Secrets = 2 option_Courses_and_Secrets = 2
class BuddyChecks(Toggle): class BuddyChecks(Toggle):
"""Bob-omb Buddies are checks, Cannon Unlocks are items""" """Bob-omb Buddies are checks, Cannon Unlocks are items"""
display_name = "Bob-omb Buddy Checks" display_name = "Bob-omb Buddy Checks"
class ExclamationBoxes(Choice): class ExclamationBoxes(Choice):
"""Include 1Up Exclamation Boxes during randomization""" """Include 1Up Exclamation Boxes during randomization"""
display_name = "Randomize 1Up !-Blocks" display_name = "Randomize 1Up !-Blocks"
option_Off = 0 option_Off = 0
option_1Ups_Only = 1 option_1Ups_Only = 1
class ProgressiveKeys(DefaultOnToggle): class ProgressiveKeys(DefaultOnToggle):
"""Keys will first grant you access to the Basement, then to the Secound Floor""" """Keys will first grant you access to the Basement, then to the Secound Floor"""
display_name = "Progressive Keys" display_name = "Progressive Keys"
sm64_options: typing.Dict[str,type(Option)] = {
sm64_options: typing.Dict[str, type(Option)] = {
"AreaRandomizer": AreaRandomizer, "AreaRandomizer": AreaRandomizer,
"ProgressiveKeys": ProgressiveKeys, "ProgressiveKeys": ProgressiveKeys,
"EnableCoinStars": EnableCoinStars, "EnableCoinStars": EnableCoinStars,
@ -93,5 +107,5 @@ sm64_options: typing.Dict[str,type(Option)] = {
"StarsToFinish": StarsToFinish, "StarsToFinish": StarsToFinish,
"death_link": DeathLink, "death_link": DeathLink,
"BuddyChecks": BuddyChecks, "BuddyChecks": BuddyChecks,
"ExclamationBoxes": ExclamationBoxes "ExclamationBoxes": ExclamationBoxes,
} }

View File

@ -9,6 +9,7 @@ from .Regions import create_regions, sm64courses, sm64entrances_s, sm64_internal
from BaseClasses import Item, Tutorial, ItemClassification from BaseClasses import Item, Tutorial, ItemClassification
from ..AutoWorld import World, WebWorld from ..AutoWorld import World, WebWorld
class SM64Web(WebWorld): class SM64Web(WebWorld):
tutorials = [Tutorial( tutorials = [Tutorial(
"Multiworld Setup Guide", "Multiworld Setup Guide",

View File

@ -107,7 +107,6 @@ class HeartBeepSpeed(Choice):
option_Half = 2 option_Half = 2
option_Normal = 3 option_Normal = 3
option_Double = 4 option_Double = 4
alias_false = 0
default = 3 default = 3
class HeartColor(Choice): class HeartColor(Choice):

View File

@ -21,8 +21,6 @@ class OffOnFullChoice(Choice):
option_on = 1 option_on = 1
option_full = 2 option_full = 2
alias_chaos = 2 alias_chaos = 2
alias_false = 0
alias_true = 1
class Difficulty(EvermizerFlags, Choice): class Difficulty(EvermizerFlags, Choice):

View File

@ -3,66 +3,82 @@ from BaseClasses import MultiWorld
from Options import Toggle, DefaultOnToggle, DeathLink, Choice, Range, Option, OptionDict from Options import Toggle, DefaultOnToggle, DeathLink, Choice, Range, Option, OptionDict
from schema import Schema, And, Optional from schema import Schema, And, Optional
class StartWithJewelryBox(Toggle): class StartWithJewelryBox(Toggle):
"Start with Jewelry Box unlocked" "Start with Jewelry Box unlocked"
display_name = "Start with Jewelry Box" display_name = "Start with Jewelry Box"
#class ProgressiveVerticalMovement(Toggle): #class ProgressiveVerticalMovement(Toggle):
# "Always find vertical movement in the following order Succubus Hairpin -> Light Wall -> Celestial Sash" # "Always find vertical movement in the following order Succubus Hairpin -> Light Wall -> Celestial Sash"
# display_name = "Progressive vertical movement" # display_name = "Progressive vertical movement"
#class ProgressiveKeycards(Toggle): #class ProgressiveKeycards(Toggle):
# "Always find Security Keycard's in the following order D -> C -> B -> A" # "Always find Security Keycard's in the following order D -> C -> B -> A"
# display_name = "Progressive keycards" # display_name = "Progressive keycards"
class DownloadableItems(DefaultOnToggle): class DownloadableItems(DefaultOnToggle):
"With the tablet you will be able to download items at terminals" "With the tablet you will be able to download items at terminals"
display_name = "Downloadable items" display_name = "Downloadable items"
class EyeSpy(Toggle): class EyeSpy(Toggle):
"Requires Oculus Ring in inventory to be able to break hidden walls." "Requires Oculus Ring in inventory to be able to break hidden walls."
display_name = "Eye Spy" display_name = "Eye Spy"
class StartWithMeyef(Toggle): class StartWithMeyef(Toggle):
"Start with Meyef, ideal for when you want to play multiplayer." "Start with Meyef, ideal for when you want to play multiplayer."
display_name = "Start with Meyef" display_name = "Start with Meyef"
class QuickSeed(Toggle): class QuickSeed(Toggle):
"Start with Talaria Attachment, Nyoom!" "Start with Talaria Attachment, Nyoom!"
display_name = "Quick seed" display_name = "Quick seed"
class SpecificKeycards(Toggle): class SpecificKeycards(Toggle):
"Keycards can only open corresponding doors" "Keycards can only open corresponding doors"
display_name = "Specific Keycards" display_name = "Specific Keycards"
class Inverted(Toggle): class Inverted(Toggle):
"Start in the past" "Start in the past"
display_name = "Inverted" display_name = "Inverted"
#class StinkyMaw(Toggle): #class StinkyMaw(Toggle):
# "Require gasmask for Maw" # "Require gasmask for Maw"
# display_name = "Stinky Maw" # display_name = "Stinky Maw"
class GyreArchives(Toggle): class GyreArchives(Toggle):
"Gyre locations are in logic. New warps are gated by Merchant Crow and Kobo" "Gyre locations are in logic. New warps are gated by Merchant Crow and Kobo"
display_name = "Gyre Archives" display_name = "Gyre Archives"
class Cantoran(Toggle): class Cantoran(Toggle):
"Cantoran's fight and check are available upon revisiting his room" "Cantoran's fight and check are available upon revisiting his room"
display_name = "Cantoran" display_name = "Cantoran"
class LoreChecks(Toggle): class LoreChecks(Toggle):
"Memories and journal entries contain items." "Memories and journal entries contain items."
display_name = "Lore Checks" display_name = "Lore Checks"
class BossRando(Toggle): class BossRando(Toggle):
"Shuffles the positions of all bosses." "Shuffles the positions of all bosses."
display_name = "Boss Randomization" display_name = "Boss Randomization"
class BossScaling(DefaultOnToggle): class BossScaling(DefaultOnToggle):
"When Boss Rando is enabled, scales the bosses' HP, XP, and ATK to the stats of the location they replace (Reccomended)" "When Boss Rando is enabled, scales the bosses' HP, XP, and ATK to the stats of the location they replace (Reccomended)"
display_name = "Scale Random Boss Stats" display_name = "Scale Random Boss Stats"
class DamageRando(Choice): class DamageRando(Choice):
"Randomly nerfs and buffs some orbs and their associated spells as well as some associated rings." "Randomly nerfs and buffs some orbs and their associated spells as well as some associated rings."
display_name = "Damage Rando" display_name = "Damage Rando"
@ -73,9 +89,9 @@ class DamageRando(Choice):
option_mostlybuffs = 4 option_mostlybuffs = 4
option_allbuffs = 5 option_allbuffs = 5
option_manual = 6 option_manual = 6
alias_false = 0
alias_true = 2 alias_true = 2
class DamageRandoOverrides(OptionDict): class DamageRandoOverrides(OptionDict):
"Manual +/-/normal odds for an orb. Put 0 if you don't want a certain nerf or buff to be a possibility. Orbs that you don't specify will roll with 1/1/1 as odds" "Manual +/-/normal odds for an orb. Put 0 if you don't want a certain nerf or buff to be a possibility. Orbs that you don't specify will roll with 1/1/1 as odds"
schema = Schema({ schema = Schema({
@ -180,6 +196,7 @@ class DamageRandoOverrides(OptionDict):
"Radiant": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Radiant": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
} }
class HpCap(Range): class HpCap(Range):
"Sets the number that Lunais's HP maxes out at." "Sets the number that Lunais's HP maxes out at."
display_name = "HP Cap" display_name = "HP Cap"
@ -187,10 +204,12 @@ class HpCap(Range):
range_end = 999 range_end = 999
default = 999 default = 999
class BossHealing(DefaultOnToggle): class BossHealing(DefaultOnToggle):
"Enables/disables healing after boss fights. NOTE: Currently only applicable when Boss Rando is enabled." "Enables/disables healing after boss fights. NOTE: Currently only applicable when Boss Rando is enabled."
display_name = "Heal After Bosses" display_name = "Heal After Bosses"
class ShopFill(Choice): class ShopFill(Choice):
"""Sets the items for sale in Merchant Crow's shops. """Sets the items for sale in Merchant Crow's shops.
Default: No sunglasses or trendy jacket, but sand vials for sale. Default: No sunglasses or trendy jacket, but sand vials for sale.
@ -203,10 +222,12 @@ class ShopFill(Choice):
option_vanilla = 2 option_vanilla = 2
option_empty = 3 option_empty = 3
class ShopWarpShards(DefaultOnToggle): class ShopWarpShards(DefaultOnToggle):
"Shops always sell warp shards (when keys possessed), ignoring inventory setting." "Shops always sell warp shards (when keys possessed), ignoring inventory setting."
display_name = "Always Sell Warp Shards" display_name = "Always Sell Warp Shards"
class ShopMultiplier(Range): class ShopMultiplier(Range):
"Multiplier for the cost of items in the shop. Set to 0 for free shops." "Multiplier for the cost of items in the shop. Set to 0 for free shops."
display_name = "Shop Price Multiplier" display_name = "Shop Price Multiplier"
@ -214,6 +235,7 @@ class ShopMultiplier(Range):
range_end = 10 range_end = 10
default = 1 default = 1
class LootPool(Choice): class LootPool(Choice):
"""Sets the items that drop from enemies (does not apply to boss reward checks) """Sets the items that drop from enemies (does not apply to boss reward checks)
Vanilla: Drops are the same as the base game Vanilla: Drops are the same as the base game
@ -224,6 +246,7 @@ class LootPool(Choice):
option_randomized = 1 option_randomized = 1
option_empty = 2 option_empty = 2
class DropRateCategory(Choice): class DropRateCategory(Choice):
"""Sets the drop rate when 'Loot Pool' is set to 'Random' """Sets the drop rate when 'Loot Pool' is set to 'Random'
Tiered: Based on item rarity/value Tiered: Based on item rarity/value
@ -237,6 +260,7 @@ class DropRateCategory(Choice):
option_randomized = 2 option_randomized = 2
option_fixed = 3 option_fixed = 3
class FixedDropRate(Range): class FixedDropRate(Range):
"Base drop rate percentage when 'Drop Rate Category' is set to 'Fixed'" "Base drop rate percentage when 'Drop Rate Category' is set to 'Fixed'"
display_name = "Fixed Drop Rate" display_name = "Fixed Drop Rate"
@ -244,6 +268,7 @@ class FixedDropRate(Range):
range_end = 100 range_end = 100
default = 5 default = 5
class LootTierDistro(Choice): class LootTierDistro(Choice):
"""Sets how often items of each rarity tier are placed when 'Loot Pool' is set to 'Random' """Sets how often items of each rarity tier are placed when 'Loot Pool' is set to 'Random'
Default Weight: Rarer items will be assigned to enemy drop slots less frequently than common items Default Weight: Rarer items will be assigned to enemy drop slots less frequently than common items
@ -255,14 +280,17 @@ class LootTierDistro(Choice):
option_full_random = 1 option_full_random = 1
option_inverted_weight = 2 option_inverted_weight = 2
class ShowBestiary(Toggle): class ShowBestiary(Toggle):
"All entries in the bestiary are visible, without needing to kill one of a given enemy first" "All entries in the bestiary are visible, without needing to kill one of a given enemy first"
display_name = "Show Bestiary Entries" display_name = "Show Bestiary Entries"
class ShowDrops(Toggle): class ShowDrops(Toggle):
"All item drops in the bestiary are visible, without needing an enemy to drop one of a given item first" "All item drops in the bestiary are visible, without needing an enemy to drop one of a given item first"
display_name = "Show Bestiary Item Drops" display_name = "Show Bestiary Item Drops"
# Some options that are available in the timespinner randomizer arent currently implemented # Some options that are available in the timespinner randomizer arent currently implemented
timespinner_options: Dict[str, Option] = { timespinner_options: Dict[str, Option] = {
"StartWithJewelryBox": StartWithJewelryBox, "StartWithJewelryBox": StartWithJewelryBox,
@ -296,9 +324,11 @@ timespinner_options: Dict[str, Option] = {
"DeathLink": DeathLink, "DeathLink": DeathLink,
} }
def is_option_enabled(world: MultiWorld, player: int, name: str) -> bool: def is_option_enabled(world: MultiWorld, player: int, name: str) -> bool:
return get_option_value(world, player, name) > 0 return get_option_value(world, player, name) > 0
def get_option_value(world: MultiWorld, player: int, name: str) -> Union[int, dict]: def get_option_value(world: MultiWorld, player: int, name: str) -> Union[int, dict]:
option = getattr(world, name, None) option = getattr(world, name, None)
if option == None: if option == None: