From 7d5b20ccfc352c704f4a9f28f37058803e6635d7 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 4 Jul 2021 16:18:21 +0200 Subject: [PATCH] Remove temporary solution "OptionSets" in favor of AutoWorld's Options --- BaseClasses.py | 12 +- Mystery.py | 17 +-- Options.py | 116 +----------------- test/dungeons/TestDungeon.py | 9 +- test/inverted/TestInverted.py | 11 +- .../TestInvertedMinor.py | 11 +- test/inverted_owg/TestInvertedOWG.py | 11 +- test/minor_glitches/TestMinor.py | 11 +- test/owg/TestVanillaOWG.py | 11 +- test/vanilla/TestVanilla.py | 10 +- worlds/AutoWorld.py | 2 +- worlds/alttp/Options.py | 77 ++++++++++++ worlds/alttp/__init__.py | 3 +- 13 files changed, 145 insertions(+), 156 deletions(-) create mode 100644 worlds/alttp/Options.py diff --git a/BaseClasses.py b/BaseClasses.py index 7c9fd130..ad2e63f8 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -142,17 +142,13 @@ class MultiWorld(): def set_options(self, args): - import Options from worlds import AutoWorld - for option_set in Options.option_sets: - for option in option_set: - setattr(self, option, getattr(args, option, {})) - for world in AutoWorld.AutoWorldRegister.world_types.values(): - for option in world.options: - setattr(self, option, getattr(args, option, {})) for player in self.player_ids: self.custom_data[player] = {} - self.worlds[player] = AutoWorld.AutoWorldRegister.world_types[self.game[player]](self, player) + world_type = AutoWorld.AutoWorldRegister.world_types[self.game[player]] + for option in world_type.options: + setattr(self, option, getattr(args, option, {})) + self.worlds[player] = world_type(self, player) def secure(self): self.random = secrets.SystemRandom() diff --git a/Mystery.py b/Mystery.py index 60cedea7..504b6f45 100644 --- a/Mystery.py +++ b/Mystery.py @@ -9,6 +9,7 @@ from collections import Counter import string import ModuleUpdate +from worlds.alttp import Options as LttPOptions from worlds.generic import PlandoItem, PlandoConnection ModuleUpdate.update() @@ -546,9 +547,7 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b ret.startinventory = startitems ret.start_hints = set(game_weights.get('start_hints', [])) - if ret.game == "A Link to the Past": - roll_alttp_settings(ret, game_weights, plando_options) - elif ret.game in AutoWorldRegister.world_types: + if ret.game in AutoWorldRegister.world_types: for option_name, option in AutoWorldRegister.world_types[ret.game].options.items(): if option_name in game_weights: if issubclass(option, Options.OptionDict): @@ -569,18 +568,14 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b get_choice("exit", placement), get_choice("direction", placement, "both") )) + elif ret.game == "A Link to the Past": + roll_alttp_settings(ret, game_weights, plando_options) else: raise Exception(f"Unsupported game {ret.game}") return ret def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options): - for option_name, option in Options.alttp_options.items(): - if option_name in weights: - setattr(ret, option_name, option.from_any(get_choice(option_name, weights))) - else: - setattr(ret, option_name, option(option.default)) - glitches_required = get_choice('glitches_required', weights) if glitches_required not in [None, 'none', 'no_logic', 'overworld_glitches', 'hybrid_major_glitches', 'minor_glitches']: logging.warning("Only NMG, OWG, HMG and No Logic supported") @@ -631,7 +626,7 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options): extra_pieces = get_choice('triforce_pieces_mode', weights, 'available') - ret.triforce_pieces_required = Options.TriforcePieces.from_any(get_choice('triforce_pieces_required', weights, 20)) + ret.triforce_pieces_required = LttPOptions.TriforcePieces.from_any(get_choice('triforce_pieces_required', weights, 20)) # sum a percentage to required if extra_pieces == 'percentage': @@ -639,7 +634,7 @@ def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options): ret.triforce_pieces_available = int(round(ret.triforce_pieces_required * percentage, 0)) # vanilla mode (specify how many pieces are) elif extra_pieces == 'available': - ret.triforce_pieces_available = Options.TriforcePieces.from_any( + ret.triforce_pieces_available = LttPOptions.TriforcePieces.from_any( get_choice('triforce_pieces_available', weights, 30)) # required pieces + fixed extra elif extra_pieces == 'extra': diff --git a/Options.py b/Options.py index 904d5125..e62f5150 100644 --- a/Options.py +++ b/Options.py @@ -21,38 +21,6 @@ class AssembleOptions(type): name.startswith("alias_")}) return super(AssembleOptions, mcs).__new__(mcs, name, bases, attrs) - -class AssembleCategoryPath(type): - def __new__(mcs, name, bases, attrs): - path = [] - for base in bases: - if hasattr(base, "segment"): - path += base.segment - path += attrs["segment"] - attrs["path"] = path - return super(AssembleCategoryPath, mcs).__new__(mcs, name, bases, attrs) - - -class RootCategory(metaclass=AssembleCategoryPath): - segment = [] - - -class LttPCategory(RootCategory): - segment = ["A Link to the Past"] - - -class LttPRomCategory(LttPCategory): - segment = ["rom"] - - -class FactorioCategory(RootCategory): - segment = ["Factorio"] - - -class MinecraftCategory(RootCategory): - segment = ["Minecraft"] - - class Option(metaclass=AssembleOptions): value: int name_lookup: typing.Dict[int, str] @@ -216,98 +184,18 @@ class OptionDict(Option): return str(self.value) -class Logic(Choice): - option_no_glitches = 0 - option_minor_glitches = 1 - option_overworld_glitches = 2 - option_hybrid_major_glitches = 3 - option_no_logic = 4 - alias_owg = 2 - alias_hmg = 3 - - -class Objective(Choice): - option_crystals = 0 - # option_pendants = 1 - option_triforce_pieces = 2 - option_pedestal = 3 - option_bingo = 4 - - local_objective = Toggle # local triforce pieces, local dungeon prizes etc. -class Goal(Choice): - option_kill_ganon = 0 - option_kill_ganon_and_gt_agahnim = 1 - option_hand_in = 2 - - class Accessibility(Choice): option_locations = 0 option_items = 1 option_beatable = 2 -class Crystals(Range): - range_start = 0 - range_end = 7 - - -class CrystalsTower(Crystals): - default = 7 - - -class CrystalsGanon(Crystals): - default = 7 - - -class TriforcePieces(Range): - default = 30 - range_start = 1 - range_end = 90 - - -class ShopItemSlots(Range): - range_start = 0 - range_end = 30 - - -class WorldState(Choice): - option_standard = 1 - option_open = 0 - option_inverted = 2 - - -class Bosses(Choice): - option_vanilla = 0 - option_simple = 1 - option_full = 2 - option_chaos = 3 - option_singularity = 4 - - -class Enemies(Choice): - option_vanilla = 0 - option_shuffled = 1 - option_chaos = 2 - - -alttp_options: typing.Dict[str, type(Option)] = { - "crystals_needed_for_gt": CrystalsTower, - "crystals_needed_for_ganon": CrystalsGanon, - "shop_item_slots": ShopItemSlots, -} - -# replace with World.options -option_sets = ( - # minecraft_options, - # factorio_options, - alttp_options, - # hollow_knight_options -) - if __name__ == "__main__": + + from worlds.alttp.Options import Logic import argparse mapshuffle = Toggle compassshuffle = Toggle diff --git a/test/dungeons/TestDungeon.py b/test/dungeons/TestDungeon.py index 7483009d..1ebc6b29 100644 --- a/test/dungeons/TestDungeon.py +++ b/test/dungeons/TestDungeon.py @@ -1,4 +1,5 @@ import unittest +from argparse import Namespace from BaseClasses import MultiWorld, CollectionState from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool @@ -8,14 +9,16 @@ from worlds.alttp.Items import ItemFactory from worlds.alttp.Regions import create_regions from worlds.alttp.Shops import create_shops from worlds.alttp.Rules import set_rules -from Options import alttp_options +from worlds import AutoWorld class TestDungeon(unittest.TestCase): def setUp(self): self.world = MultiWorld(1) - for option_name, option in alttp_options.items(): - setattr(self.world, option_name, {1: option.default}) + args = Namespace() + for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].options.items(): + setattr(args, name, {1: option.from_any(option.default)}) + self.world.set_options(args) self.starting_regions = [] # Where to start exploring self.remove_exits = [] # Block dungeon exits self.world.difficulty_requirements[1] = difficulties['normal'] diff --git a/test/inverted/TestInverted.py b/test/inverted/TestInverted.py index f3a716cc..cb38adc8 100644 --- a/test/inverted/TestInverted.py +++ b/test/inverted/TestInverted.py @@ -1,3 +1,5 @@ +from argparse import Namespace + from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_inverted_entrances @@ -8,13 +10,16 @@ from worlds.alttp.Regions import mark_light_world_regions from worlds.alttp.Shops import create_shops from worlds.alttp.Rules import set_rules from test.TestBase import TestBase -from Options import alttp_options + +from worlds import AutoWorld class TestInverted(TestBase): def setUp(self): self.world = MultiWorld(1) - for option_name, option in alttp_options.items(): - setattr(self.world, option_name, {1: option.default}) + args = Namespace() + for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].options.items(): + setattr(args, name, {1: option.from_any(option.default)}) + self.world.set_options(args) self.world.difficulty_requirements[1] = difficulties['normal'] self.world.mode[1] = "inverted" create_inverted_regions(self.world, 1) diff --git a/test/inverted_minor_glitches/TestInvertedMinor.py b/test/inverted_minor_glitches/TestInvertedMinor.py index 41e93c6e..ac698d29 100644 --- a/test/inverted_minor_glitches/TestInvertedMinor.py +++ b/test/inverted_minor_glitches/TestInvertedMinor.py @@ -1,3 +1,5 @@ +from argparse import Namespace + from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_inverted_entrances @@ -8,13 +10,16 @@ from worlds.alttp.Regions import mark_light_world_regions from worlds.alttp.Shops import create_shops from worlds.alttp.Rules import set_rules from test.TestBase import TestBase -from Options import alttp_options + +from worlds import AutoWorld class TestInvertedMinor(TestBase): def setUp(self): self.world = MultiWorld(1) - for option_name, option in alttp_options.items(): - setattr(self.world, option_name, {1: option.default}) + args = Namespace() + for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].options.items(): + setattr(args, name, {1: option.from_any(option.default)}) + self.world.set_options(args) self.world.mode[1] = "inverted" self.world.logic[1] = "minorglitches" self.world.difficulty_requirements[1] = difficulties['normal'] diff --git a/test/inverted_owg/TestInvertedOWG.py b/test/inverted_owg/TestInvertedOWG.py index 1a2c377a..754a2702 100644 --- a/test/inverted_owg/TestInvertedOWG.py +++ b/test/inverted_owg/TestInvertedOWG.py @@ -1,3 +1,5 @@ +from argparse import Namespace + from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_inverted_entrances @@ -8,14 +10,17 @@ from worlds.alttp.Regions import mark_light_world_regions from worlds.alttp.Shops import create_shops from worlds.alttp.Rules import set_rules from test.TestBase import TestBase -from Options import alttp_options + +from worlds import AutoWorld class TestInvertedOWG(TestBase): def setUp(self): self.world = MultiWorld(1) - for option_name, option in alttp_options.items(): - setattr(self.world, option_name, {1: option.default}) + args = Namespace() + for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].options.items(): + setattr(args, name, {1: option.from_any(option.default)}) + self.world.set_options(args) self.world.logic[1] = "owglitches" self.world.mode[1] = "inverted" self.world.difficulty_requirements[1] = difficulties['normal'] diff --git a/test/minor_glitches/TestMinor.py b/test/minor_glitches/TestMinor.py index b573bac1..101d2c6e 100644 --- a/test/minor_glitches/TestMinor.py +++ b/test/minor_glitches/TestMinor.py @@ -1,3 +1,5 @@ +from argparse import Namespace + from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_entrances @@ -8,13 +10,16 @@ from worlds.alttp.Regions import create_regions from worlds.alttp.Shops import create_shops from worlds.alttp.Rules import set_rules from test.TestBase import TestBase -from Options import alttp_options + +from worlds import AutoWorld class TestMinor(TestBase): def setUp(self): self.world = MultiWorld(1) - for option_name, option in alttp_options.items(): - setattr(self.world, option_name, {1: option.default}) + args = Namespace() + for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].options.items(): + setattr(args, name, {1: option.from_any(option.default)}) + self.world.set_options(args) self.world.logic[1] = "minorglitches" self.world.difficulty_requirements[1] = difficulties['normal'] create_regions(self.world, 1) diff --git a/test/owg/TestVanillaOWG.py b/test/owg/TestVanillaOWG.py index a4082b31..5149c24c 100644 --- a/test/owg/TestVanillaOWG.py +++ b/test/owg/TestVanillaOWG.py @@ -1,3 +1,5 @@ +from argparse import Namespace + from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_entrances @@ -8,14 +10,17 @@ from worlds.alttp.Regions import create_regions from worlds.alttp.Shops import create_shops from worlds.alttp.Rules import set_rules from test.TestBase import TestBase -from Options import alttp_options + +from worlds import AutoWorld class TestVanillaOWG(TestBase): def setUp(self): self.world = MultiWorld(1) - for option_name, option in alttp_options.items(): - setattr(self.world, option_name, {1: option.default}) + args = Namespace() + for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].options.items(): + setattr(args, name, {1: option.from_any(option.default)}) + self.world.set_options(args) self.world.difficulty_requirements[1] = difficulties['normal'] self.world.logic[1] = "owglitches" create_regions(self.world, 1) diff --git a/test/vanilla/TestVanilla.py b/test/vanilla/TestVanilla.py index 5e1dde2b..9eb5d0e0 100644 --- a/test/vanilla/TestVanilla.py +++ b/test/vanilla/TestVanilla.py @@ -1,3 +1,5 @@ +from argparse import Namespace + from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_entrances @@ -8,13 +10,15 @@ from worlds.alttp.Regions import create_regions from worlds.alttp.Shops import create_shops from worlds.alttp.Rules import set_rules from test.TestBase import TestBase -from Options import alttp_options +from worlds import AutoWorld class TestVanilla(TestBase): def setUp(self): self.world = MultiWorld(1) - for option_name, option in alttp_options.items(): - setattr(self.world, option_name, {1: option.default}) + args = Namespace() + for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].options.items(): + setattr(args, name, {1: option.from_any(option.default)}) + self.world.set_options(args) self.world.logic[1] = "noglitches" self.world.difficulty_requirements[1] = difficulties['normal'] create_regions(self.world, 1) diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index 50824d5d..f4f6b0a4 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -50,7 +50,7 @@ class World(metaclass=AutoWorldRegister): """Collect an item into state""" if item.advancement: state.prog_items[item.name, item.player] += 1 - return True # indicate that a logical state change has occured + return True # indicate that a logical state change has occured return False def get_required_client_version(self) -> tuple: diff --git a/worlds/alttp/Options.py b/worlds/alttp/Options.py new file mode 100644 index 00000000..cdfe36cc --- /dev/null +++ b/worlds/alttp/Options.py @@ -0,0 +1,77 @@ +import typing + +from Options import Choice, Range, Option + + +class Logic(Choice): + option_no_glitches = 0 + option_minor_glitches = 1 + option_overworld_glitches = 2 + option_hybrid_major_glitches = 3 + option_no_logic = 4 + alias_owg = 2 + alias_hmg = 3 + + +class Objective(Choice): + option_crystals = 0 + # option_pendants = 1 + option_triforce_pieces = 2 + option_pedestal = 3 + option_bingo = 4 + + +class Goal(Choice): + option_kill_ganon = 0 + option_kill_ganon_and_gt_agahnim = 1 + option_hand_in = 2 + + +class Crystals(Range): + range_start = 0 + range_end = 7 + + +class CrystalsTower(Crystals): + default = 7 + + +class CrystalsGanon(Crystals): + default = 7 + + +class TriforcePieces(Range): + default = 30 + range_start = 1 + range_end = 90 + + +class ShopItemSlots(Range): + range_start = 0 + range_end = 30 + + +class WorldState(Choice): + option_standard = 1 + option_open = 0 + option_inverted = 2 + + +class Bosses(Choice): + option_vanilla = 0 + option_simple = 1 + option_full = 2 + option_chaos = 3 + option_singularity = 4 + + +class Enemies(Choice): + option_vanilla = 0 + option_shuffled = 1 + option_chaos = 2 + +alttp_options: typing.Dict[str, type(Option)] = { + "crystals_needed_for_gt": CrystalsTower, + "crystals_needed_for_ganon": CrystalsGanon, + "shop_item_slots": ShopItemSlots, +} \ No newline at end of file diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index 81274d4c..6dc1c4f0 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -2,10 +2,11 @@ from typing import Optional from BaseClasses import Location, Item, CollectionState from ..AutoWorld import World +from .Options import alttp_options class ALTTPWorld(World): game: str = "A Link to the Past" - + options = alttp_options def collect(self, state: CollectionState, item: Item) -> bool: if item.name.startswith('Progressive '): if 'Sword' in item.name: