mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 12:11:33 -06:00
Core: move option results to the World class instead of MultiWorld (#993)
🤞 * map option objects to a `World.options` dict * convert RoR2 to options dict system for testing * add temp behavior for lttp with notes * copy/paste bad * convert `set_default_common_options` to a namespace property * reorganize test call order * have fill_restrictive use the new options system * update world api * update soe tests * fix world api * core: auto initialize a dataclass on the World class with the option results * core: auto initialize a dataclass on the World class with the option results: small tying improvement * add `as_dict` method to the options dataclass * fix namespace issues with tests * have current option updates use `.value` instead of changing the option * update ror2 to use the new options system again * revert the junk pool dict since it's cased differently * fix begin_with_loop typo * write new and old options to spoiler * change factorio option behavior back * fix comparisons * move common and per_game_common options to new system * core: automatically create missing options_dataclass from legacy option_definitions * remove spoiler special casing and add back the Factorio option changing but in new system * give ArchipIDLE the default options_dataclass so its options get generated and spoilered properly * reimplement `inspect.get_annotations` * move option info generation for webhost to new system * need to include Common and PerGame common since __annotations__ doesn't include super * use get_type_hints for the options dictionary * typing.get_type_hints returns the bases too. * forgot to sweep through generate * sweep through all the tests * swap to a metaclass property * move remaining usages from get_type_hints to metaclass property * move remaining usages from __annotations__ to metaclass property * move remaining usages from legacy dictionaries to metaclass property * remove legacy dictionaries * cache the metaclass property * clarify inheritance in world api * move the messenger to new options system * add an assert for my dumb * update the doc * rename o to options * missed a spot * update new messenger options * comment spacing Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com> * fix tests * fix missing import * make the documentation definition more accurate * use options system for loc creation * type cast MessengerWorld * fix typo and use quotes for cast * LTTP: set random seed in tests * ArchipIdle: remove change here as it's default on AutoWorld * Stardew: Need to set state because `set_default_common_options` used to * The Messenger: update shop rando and helpers to new system; optimize imports * Add a kwarg to `as_dict` to do the casing for you * RoR2: use new kwarg for less code * RoR2: revert some accidental reverts * The Messenger: remove an unnecessary variable * remove TypeVar that isn't used * CommonOptions not abstract * Docs: fix mistake in options api.md Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com> * create options for item link worlds * revert accidental doc removals * Item Links: set default options on group * change Zillion to new options dataclass * remove unused parameter to function * use TypeGuard for Literal narrowing * move dlc quest to new api * move overcooked 2 to new api * fixed some missed code in oc2 * - Tried to be compliant with 993 (WIP?) * - I think it all works now * - Removed last trace of me touching core * typo * It now passes all tests! * Improve options, fix all issues I hope * - Fixed init options * dlcquest: fix bad imports * missed a file * - Reduce code duplication * add as_dict documentation * - Use .items(), get option name more directly, fix slot data content * - Remove generic options from the slot data * improve slot data documentation * remove `CommonOptions.get_value` (#21) * better slot data description Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --------- Co-authored-by: el-u <109771707+el-u@users.noreply.github.com> Co-authored-by: Doug Hoskisson <beauxq@users.noreply.github.com> Co-authored-by: Doug Hoskisson <beauxq@yahoo.com> Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> Co-authored-by: Alex Gilbert <alexgilbert@yahoo.com>
This commit is contained in:
88
Options.py
88
Options.py
@@ -2,6 +2,9 @@ from __future__ import annotations
|
||||
|
||||
import abc
|
||||
import logging
|
||||
from copy import deepcopy
|
||||
from dataclasses import dataclass
|
||||
import functools
|
||||
import math
|
||||
import numbers
|
||||
import random
|
||||
@@ -211,6 +214,12 @@ class NumericOption(Option[int], numbers.Integral, abc.ABC):
|
||||
else:
|
||||
return self.value > other
|
||||
|
||||
def __ge__(self, other: typing.Union[int, NumericOption]) -> bool:
|
||||
if isinstance(other, NumericOption):
|
||||
return self.value >= other.value
|
||||
else:
|
||||
return self.value >= other
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return bool(self.value)
|
||||
|
||||
@@ -896,10 +905,55 @@ class ProgressionBalancing(SpecialRange):
|
||||
}
|
||||
|
||||
|
||||
common_options = {
|
||||
"progression_balancing": ProgressionBalancing,
|
||||
"accessibility": Accessibility
|
||||
}
|
||||
class OptionsMetaProperty(type):
|
||||
def __new__(mcs,
|
||||
name: str,
|
||||
bases: typing.Tuple[type, ...],
|
||||
attrs: typing.Dict[str, typing.Any]) -> "OptionsMetaProperty":
|
||||
for attr_type in attrs.values():
|
||||
assert not isinstance(attr_type, AssembleOptions),\
|
||||
f"Options for {name} should be type hinted on the class, not assigned"
|
||||
return super().__new__(mcs, name, bases, attrs)
|
||||
|
||||
@property
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def type_hints(cls) -> typing.Dict[str, typing.Type[Option[typing.Any]]]:
|
||||
"""Returns type hints of the class as a dictionary."""
|
||||
return typing.get_type_hints(cls)
|
||||
|
||||
|
||||
@dataclass
|
||||
class CommonOptions(metaclass=OptionsMetaProperty):
|
||||
progression_balancing: ProgressionBalancing
|
||||
accessibility: Accessibility
|
||||
|
||||
def as_dict(self, *option_names: str, casing: str = "snake") -> typing.Dict[str, typing.Any]:
|
||||
"""
|
||||
Returns a dictionary of [str, Option.value]
|
||||
|
||||
:param option_names: names of the options to return
|
||||
:param casing: case of the keys to return. Supports `snake`, `camel`, `pascal`, `kebab`
|
||||
"""
|
||||
option_results = {}
|
||||
for option_name in option_names:
|
||||
if option_name in type(self).type_hints:
|
||||
if casing == "snake":
|
||||
display_name = option_name
|
||||
elif casing == "camel":
|
||||
split_name = [name.title() for name in option_name.split("_")]
|
||||
split_name[0] = split_name[0].lower()
|
||||
display_name = "".join(split_name)
|
||||
elif casing == "pascal":
|
||||
display_name = "".join([name.title() for name in option_name.split("_")])
|
||||
elif casing == "kebab":
|
||||
display_name = option_name.replace("_", "-")
|
||||
else:
|
||||
raise ValueError(f"{casing} is invalid casing for as_dict. "
|
||||
"Valid names are 'snake', 'camel', 'pascal', 'kebab'.")
|
||||
option_results[display_name] = getattr(self, option_name).value
|
||||
else:
|
||||
raise ValueError(f"{option_name} not found in {tuple(type(self).type_hints)}")
|
||||
return option_results
|
||||
|
||||
|
||||
class LocalItems(ItemSet):
|
||||
@@ -1020,17 +1074,16 @@ class ItemLinks(OptionList):
|
||||
link.setdefault("link_replacement", None)
|
||||
|
||||
|
||||
per_game_common_options = {
|
||||
**common_options, # can be overwritten per-game
|
||||
"local_items": LocalItems,
|
||||
"non_local_items": NonLocalItems,
|
||||
"start_inventory": StartInventory,
|
||||
"start_hints": StartHints,
|
||||
"start_location_hints": StartLocationHints,
|
||||
"exclude_locations": ExcludeLocations,
|
||||
"priority_locations": PriorityLocations,
|
||||
"item_links": ItemLinks
|
||||
}
|
||||
@dataclass
|
||||
class PerGameCommonOptions(CommonOptions):
|
||||
local_items: LocalItems
|
||||
non_local_items: NonLocalItems
|
||||
start_inventory: StartInventory
|
||||
start_hints: StartHints
|
||||
start_location_hints: StartLocationHints
|
||||
exclude_locations: ExcludeLocations
|
||||
priority_locations: PriorityLocations
|
||||
item_links: ItemLinks
|
||||
|
||||
|
||||
def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], generate_hidden: bool = True):
|
||||
@@ -1071,10 +1124,7 @@ def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], ge
|
||||
|
||||
for game_name, world in AutoWorldRegister.world_types.items():
|
||||
if not world.hidden or generate_hidden:
|
||||
all_options: typing.Dict[str, AssembleOptions] = {
|
||||
**per_game_common_options,
|
||||
**world.option_definitions
|
||||
}
|
||||
all_options: typing.Dict[str, AssembleOptions] = world.options_dataclass.type_hints
|
||||
|
||||
with open(local_path("data", "options.yaml")) as f:
|
||||
file_data = f.read()
|
||||
|
Reference in New Issue
Block a user