from typing import Dict, Union from BaseClasses import MultiWorld from Options import Toggle, DefaultOnToggle, DeathLink, Choice, Range, Option, OptionDict from schema import Schema, And, Optional class StartWithJewelryBox(Toggle): "Start with Jewelry Box unlocked" display_name = "Start with Jewelry Box" #class ProgressiveVerticalMovement(Toggle): # "Always find vertical movement in the following order Succubus Hairpin -> Light Wall -> Celestial Sash" # display_name = "Progressive vertical movement" #class ProgressiveKeycards(Toggle): # "Always find Security Keycard's in the following order D -> C -> B -> A" # display_name = "Progressive keycards" class DownloadableItems(DefaultOnToggle): "With the tablet you will be able to download items at terminals" display_name = "Downloadable items" class EyeSpy(Toggle): "Requires Oculus Ring in inventory to be able to break hidden walls." display_name = "Eye Spy" class StartWithMeyef(Toggle): "Start with Meyef, ideal for when you want to play multiplayer." display_name = "Start with Meyef" class QuickSeed(Toggle): "Start with Talaria Attachment, Nyoom!" display_name = "Quick seed" class SpecificKeycards(Toggle): "Keycards can only open corresponding doors" display_name = "Specific Keycards" class Inverted(Toggle): "Start in the past" display_name = "Inverted" #class StinkyMaw(Toggle): # "Require gasmask for Maw" # display_name = "Stinky Maw" class GyreArchives(Toggle): "Gyre locations are in logic. New warps are gated by Merchant Crow and Kobo" display_name = "Gyre Archives" class Cantoran(Toggle): "Cantoran's fight and check are available upon revisiting his room" display_name = "Cantoran" class LoreChecks(Toggle): "Memories and journal entries contain items." display_name = "Lore Checks" class BossRando(Toggle): "Shuffles the positions of all bosses." display_name = "Boss Randomization" class BossScaling(DefaultOnToggle): "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" class DamageRando(Choice): "Randomly nerfs and buffs some orbs and their associated spells as well as some associated rings." display_name = "Damage Rando" option_off = 0 option_allnerfs = 1 option_mostlynerfs = 2 option_balanced = 3 option_mostlybuffs = 4 option_allbuffs = 5 option_manual = 6 alias_true = 2 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" schema = Schema({ Optional("Blue"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Blade"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Fire"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Plasma"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Iron"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Ice"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Wind"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Gun"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Umbra"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Empire"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Eye"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Blood"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("ForbiddenTome"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Shattered"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Nether"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, Optional("Radiant"): { "MinusOdds": And(int, lambda n: n >= 0), "NormalOdds": And(int, lambda n: n >= 0), "PlusOdds": And(int, lambda n: n >= 0) }, }) display_name = "Damage Rando Overrides" default = { "Blue": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Blade": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Fire": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Plasma": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Iron": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Ice": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Wind": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Gun": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Umbra": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Empire": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Eye": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Blood": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "ForbiddenTome": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Shattered": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Nether": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, "Radiant": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 }, } class HpCap(Range): "Sets the number that Lunais's HP maxes out at." display_name = "HP Cap" range_start = 1 range_end = 999 default = 999 class BossHealing(DefaultOnToggle): "Enables/disables healing after boss fights. NOTE: Currently only applicable when Boss Rando is enabled." display_name = "Heal After Bosses" class ShopFill(Choice): """Sets the items for sale in Merchant Crow's shops. Default: No sunglasses or trendy jacket, but sand vials for sale. Randomized: Up to 4 random items in each shop. Vanilla: Keep shops the same as the base game. Empty: Sell no items at the shop.""" display_name = "Shop Inventory" option_default = 0 option_randomized = 1 option_vanilla = 2 option_empty = 3 class ShopWarpShards(DefaultOnToggle): "Shops always sell warp shards (when keys possessed), ignoring inventory setting." display_name = "Always Sell Warp Shards" class ShopMultiplier(Range): "Multiplier for the cost of items in the shop. Set to 0 for free shops." display_name = "Shop Price Multiplier" range_start = 0 range_end = 10 default = 1 class LootPool(Choice): """Sets the items that drop from enemies (does not apply to boss reward checks) Vanilla: Drops are the same as the base game Randomized: Each slot of every enemy's drop table is given a random use item or piece of equipment. Empty: Enemies drop nothing.""" display_name = "Loot Pool" option_vanilla = 0 option_randomized = 1 option_empty = 2 class DropRateCategory(Choice): """Sets the drop rate when 'Loot Pool' is set to 'Random' Tiered: Based on item rarity/value Vanilla: Based on bestiary slot the item is placed into Random: Assigned a random tier/drop rate Fixed: Set by the 'Fixed Drop Rate' setting """ display_name = "Drop Rate Category" option_tiered = 0 option_vanilla = 1 option_randomized = 2 option_fixed = 3 class FixedDropRate(Range): "Base drop rate percentage when 'Drop Rate Category' is set to 'Fixed'" display_name = "Fixed Drop Rate" range_start = 0 range_end = 100 default = 5 class LootTierDistro(Choice): """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 Full Random: Any item has an equal chance of being placed in an enemy's drop slot Inverted Weight: Rarest items show up the most frequently, while common items are the rarest """ display_name = "Loot Tier Distrubution" option_default_weight = 0 option_full_random = 1 option_inverted_weight = 2 class ShowBestiary(Toggle): "All entries in the bestiary are visible, without needing to kill one of a given enemy first" display_name = "Show Bestiary Entries" class ShowDrops(Toggle): "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" # Some options that are available in the timespinner randomizer arent currently implemented timespinner_options: Dict[str, Option] = { "StartWithJewelryBox": StartWithJewelryBox, #"ProgressiveVerticalMovement": ProgressiveVerticalMovement, #"ProgressiveKeycards": ProgressiveKeycards, "DownloadableItems": DownloadableItems, "EyeSpy": EyeSpy, "StartWithMeyef": StartWithMeyef, "QuickSeed": QuickSeed, "SpecificKeycards": SpecificKeycards, "Inverted": Inverted, #"StinkyMaw": StinkyMaw, "GyreArchives": GyreArchives, "Cantoran": Cantoran, "LoreChecks": LoreChecks, "BossRando": BossRando, "BossScaling": BossScaling, "DamageRando": DamageRando, "DamageRandoOverrides": DamageRandoOverrides, "HpCap": HpCap, "BossHealing": BossHealing, "ShopFill": ShopFill, "ShopWarpShards": ShopWarpShards, "ShopMultiplier": ShopMultiplier, "LootPool": LootPool, "DropRateCategory": DropRateCategory, "FixedDropRate": FixedDropRate, "LootTierDistro": LootTierDistro, "ShowBestiary": ShowBestiary, "ShowDrops": ShowDrops, "DeathLink": DeathLink, } def is_option_enabled(world: MultiWorld, player: int, name: str) -> bool: return get_option_value(world, player, name) > 0 def get_option_value(world: MultiWorld, player: int, name: str) -> Union[int, dict]: option = getattr(world, name, None) if option == None: return 0 return option[player].value