From cb3d40624ca63279d2b9344100510a9f7d04b53e Mon Sep 17 00:00:00 2001 From: CaitSith2 Date: Wed, 22 Feb 2023 17:11:27 -0800 Subject: [PATCH] Timespinner: Make RisingTidesOverrides consistent with normal yaml behaviour. (#1474) * Make RisingTidesOverrides consistent with normal yaml behaviour. * Each of the options can be either string directly specifying the option, or dictionary. * If dictionary, ensure that at least one of the options is greater than zero. * Made keys optional * A lot less copy/pasta. --------- Co-authored-by: Jarno Westhof --- worlds/timespinner/Options.py | 73 +++++++++++----------- worlds/timespinner/PreCalculatedWeights.py | 20 +++--- 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/worlds/timespinner/Options.py b/worlds/timespinner/Options.py index 6f4b7ea8..0448e93d 100644 --- a/worlds/timespinner/Options.py +++ b/worlds/timespinner/Options.py @@ -1,7 +1,7 @@ from typing import Dict, Union, List from BaseClasses import MultiWorld from Options import Toggle, DefaultOnToggle, DeathLink, Choice, Range, Option, OptionDict, OptionList -from schema import Schema, And, Optional +from schema import Schema, And, Optional, Or class StartWithJewelryBox(Toggle): @@ -308,47 +308,44 @@ class RisingTides(Toggle): display_name = "Rising Tides" +def rising_tide_option(location: str, with_save_point_option: bool = False) -> Dict[Optional, Or]: + if with_save_point_option: + return { + Optional(location): Or( + And({ + Optional("Dry"): And(int, lambda n: n >= 0), + Optional("Flooded"): And(int, lambda n: n >= 0), + Optional("FloodedWithSavePointAvailable"): And(int, lambda n: n >= 0) + }, lambda d: any(v > 0 for v in d.values())), + "Dry", + "Flooded", + "FloodedWithSavePointAvailable") + } + else: + return { + Optional(location): Or( + And({ + Optional("Dry"): And(int, lambda n: n >= 0), + Optional("Flooded"): And(int, lambda n: n >= 0) + }, lambda d: any(v > 0 for v in d.values())), + "Dry", + "Flooded") + } + + class RisingTidesOverrides(OptionDict): """Odds for specific areas to be flooded or drained, only has effect when RisingTides is on. Areas that are not specified will roll with the default 33% chance of getting flooded or drained""" schema = Schema({ - Optional("Xarion"): { - "Dry": And(int, lambda n: n >= 0), - "Flooded": And(int, lambda n: n >= 0) - }, - Optional("Maw"): { - "Dry": And(int, lambda n: n >= 0), - "Flooded": And(int, lambda n: n >= 0) - }, - Optional("AncientPyramidShaft"): { - "Dry": And(int, lambda n: n >= 0), - "Flooded": And(int, lambda n: n >= 0) - }, - Optional("Sandman"): { - "Dry": And(int, lambda n: n >= 0), - "Flooded": And(int, lambda n: n >= 0) - }, - Optional("CastleMoat"): { - "Dry": And(int, lambda n: n >= 0), - "Flooded": And(int, lambda n: n >= 0) - }, - Optional("CastleBasement"): { - "Dry": And(int, lambda n: n >= 0), - "FloodedWithSavePointAvailable": And(int, lambda n: n >= 0), - "Flooded": And(int, lambda n: n >= 0) - }, - Optional("CastleCourtyard"): { - "Dry": And(int, lambda n: n >= 0), - "Flooded": And(int, lambda n: n >= 0) - }, - Optional("LakeDesolation"): { - "Dry": And(int, lambda n: n >= 0), - "Flooded": And(int, lambda n: n >= 0) - }, - Optional("LakeSerene"): { - "Dry": And(int, lambda n: n >= 0), - "Flooded": And(int, lambda n: n >= 0) - } + **rising_tide_option("Xarion"), + **rising_tide_option("Maw"), + **rising_tide_option("AncientPyramidShaft"), + **rising_tide_option("Sandman"), + **rising_tide_option("CastleMoat"), + **rising_tide_option("CastleBasement", with_save_point_option=True), + **rising_tide_option("CastleCourtyard"), + **rising_tide_option("LakeDesolation"), + **rising_tide_option("LakeSerene") }) display_name = "Rising Tides Overrides" default = { diff --git a/worlds/timespinner/PreCalculatedWeights.py b/worlds/timespinner/PreCalculatedWeights.py index 193bf84d..8501ef73 100644 --- a/worlds/timespinner/PreCalculatedWeights.py +++ b/worlds/timespinner/PreCalculatedWeights.py @@ -21,7 +21,7 @@ class PreCalculatedWeights: dry_lake_serene: bool def __init__(self, world: MultiWorld, player: int): - weights_overrrides: Dict[str, Dict[str, int]] = self.get_flood_weights_overrides(world, player) + weights_overrrides: Dict[str, Union[str, Dict[str, int]]] = self.get_flood_weights_overrides(world, player) self.flood_basement, self.flood_basement_high = \ self.roll_flood_setting_with_available_save(world, player, weights_overrrides, "CastleBasement") @@ -87,8 +87,8 @@ class PreCalculatedWeights: ) @staticmethod - def get_flood_weights_overrides( world: MultiWorld, player: int) -> Dict[str, int]: - weights_overrides_option: Union[int, Dict[str, Dict[str, int]]] = \ + def get_flood_weights_overrides( world: MultiWorld, player: int) -> Dict[str, Union[str, Dict[str, int]]]: + weights_overrides_option: Union[int, Dict[str, Union[str, Dict[str, int]]]] = \ get_option_value(world, player, "RisingTidesOverrides") if weights_overrides_option == 0: @@ -97,26 +97,32 @@ class PreCalculatedWeights: return weights_overrides_option @staticmethod - def roll_flood_setting(world: MultiWorld, player: int, weights: Dict[str, Dict[str, int]], key: str) -> bool: + def roll_flood_setting(world: MultiWorld, player: int, weights: Dict[str, Union[Dict[str, int], str]], key: str) -> bool: if not world or not is_option_enabled(world, player, "RisingTides"): return False weights = weights[key] if key in weights else { "Dry": 67, "Flooded": 33 } - result: str = world.random.choices(list(weights.keys()), weights=list(map(int, weights.values())))[0] + if isinstance(weights, dict): + result: str = world.random.choices(list(weights.keys()), weights=list(map(int, weights.values())))[0] + else: + result: str = weights return result == "Flooded" @staticmethod def roll_flood_setting_with_available_save(world: MultiWorld, player: int, - weights: Dict[str, Dict[str, int]], key: str) -> Tuple[bool, bool]: + weights: Dict[str, Union[Dict[str, int], str]], key: str) -> Tuple[bool, bool]: if not world or not is_option_enabled(world, player, "RisingTides"): return False, False weights = weights[key] if key in weights else {"Dry": 66, "Flooded": 17, "FloodedWithSavePointAvailable": 17} - result: str = world.random.choices(list(weights.keys()), weights=list(map(int, weights.values())))[0] + if isinstance(weights, dict): + result: str = world.random.choices(list(weights.keys()), weights=list(map(int, weights.values())))[0] + else: + result: str = weights if result == "Dry": return False, False