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:
Aaron Wagener
2023-10-10 15:30:20 -05:00
committed by GitHub
parent a7b4914bb7
commit 7193182294
69 changed files with 1587 additions and 1603 deletions

View File

@@ -4,10 +4,12 @@ from dataclasses import dataclass
from random import Random
from typing import Optional, Dict, Protocol, List, FrozenSet
from . import options, data
from . import data
from .options import StardewValleyOptions
from .data.fish_data import legendary_fish, special_fish, all_fish
from .data.museum_data import all_museum_items
from .data.villagers_data import all_villagers
from .options import ExcludeGingerIsland, Friendsanity, ArcadeMachineLocations, SpecialOrderLocations, Cropsanity, Fishsanity, Museumsanity, FestivalLocations, SkillProgression, BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression
from .strings.goal_names import Goal
from .strings.region_names import Region
@@ -133,12 +135,12 @@ def initialize_groups():
initialize_groups()
def extend_cropsanity_locations(randomized_locations: List[LocationData], world_options):
if world_options[options.Cropsanity] == options.Cropsanity.option_disabled:
def extend_cropsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.cropsanity == Cropsanity.option_disabled:
return
cropsanity_locations = locations_by_tag[LocationTags.CROPSANITY]
cropsanity_locations = filter_ginger_island(world_options, cropsanity_locations)
cropsanity_locations = filter_ginger_island(options, cropsanity_locations)
randomized_locations.extend(cropsanity_locations)
@@ -157,56 +159,56 @@ def extend_help_wanted_quests(randomized_locations: List[LocationData], desired_
randomized_locations.append(location_table[f"Help Wanted: Gathering {batch + 1}"])
def extend_fishsanity_locations(randomized_locations: List[LocationData], world_options, random: Random):
def extend_fishsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random):
prefix = "Fishsanity: "
if world_options[options.Fishsanity] == options.Fishsanity.option_none:
if options.fishsanity == Fishsanity.option_none:
return
elif world_options[options.Fishsanity] == options.Fishsanity.option_legendaries:
elif options.fishsanity == Fishsanity.option_legendaries:
randomized_locations.extend(location_table[f"{prefix}{legendary.name}"] for legendary in legendary_fish)
elif world_options[options.Fishsanity] == options.Fishsanity.option_special:
elif options.fishsanity == Fishsanity.option_special:
randomized_locations.extend(location_table[f"{prefix}{special.name}"] for special in special_fish)
elif world_options[options.Fishsanity] == options.Fishsanity.option_randomized:
elif options.fishsanity == Fishsanity.option_randomized:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if random.random() < 0.4]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
elif world_options[options.Fishsanity] == options.Fishsanity.option_all:
randomized_locations.extend(filter_ginger_island(options, fish_locations))
elif options.fishsanity == Fishsanity.option_all:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
elif world_options[options.Fishsanity] == options.Fishsanity.option_exclude_legendaries:
randomized_locations.extend(filter_ginger_island(options, fish_locations))
elif options.fishsanity == Fishsanity.option_exclude_legendaries:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish not in legendary_fish]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
elif world_options[options.Fishsanity] == options.Fishsanity.option_exclude_hard_fish:
randomized_locations.extend(filter_ginger_island(options, fish_locations))
elif options.fishsanity == Fishsanity.option_exclude_hard_fish:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish.difficulty < 80]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
elif world_options[options.Fishsanity] == options.Fishsanity.option_only_easy_fish:
randomized_locations.extend(filter_ginger_island(options, fish_locations))
elif options.fishsanity == Fishsanity.option_only_easy_fish:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish.difficulty < 50]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
randomized_locations.extend(filter_ginger_island(options, fish_locations))
def extend_museumsanity_locations(randomized_locations: List[LocationData], museumsanity: int, random: Random):
def extend_museumsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random):
prefix = "Museumsanity: "
if museumsanity == options.Museumsanity.option_none:
if options.museumsanity == Museumsanity.option_none:
return
elif museumsanity == options.Museumsanity.option_milestones:
elif options.museumsanity == Museumsanity.option_milestones:
randomized_locations.extend(locations_by_tag[LocationTags.MUSEUM_MILESTONES])
elif museumsanity == options.Museumsanity.option_randomized:
elif options.museumsanity == Museumsanity.option_randomized:
randomized_locations.extend(location_table[f"{prefix}{museum_item.name}"]
for museum_item in all_museum_items if random.random() < 0.4)
elif museumsanity == options.Museumsanity.option_all:
elif options.museumsanity == Museumsanity.option_all:
randomized_locations.extend(location_table[f"{prefix}{museum_item.name}"] for museum_item in all_museum_items)
def extend_friendsanity_locations(randomized_locations: List[LocationData], world_options: options.StardewOptions):
if world_options[options.Friendsanity] == options.Friendsanity.option_none:
def extend_friendsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.friendsanity == Friendsanity.option_none:
return
exclude_leo = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true
exclude_non_bachelors = world_options[options.Friendsanity] == options.Friendsanity.option_bachelors
exclude_locked_villagers = world_options[options.Friendsanity] == options.Friendsanity.option_starting_npcs or \
world_options[options.Friendsanity] == options.Friendsanity.option_bachelors
include_post_marriage_hearts = world_options[options.Friendsanity] == options.Friendsanity.option_all_with_marriage
heart_size = world_options[options.FriendsanityHeartSize]
exclude_leo = options.exclude_ginger_island == ExcludeGingerIsland.option_true
exclude_non_bachelors = options.friendsanity == Friendsanity.option_bachelors
exclude_locked_villagers = options.friendsanity == Friendsanity.option_starting_npcs or \
options.friendsanity == Friendsanity.option_bachelors
include_post_marriage_hearts = options.friendsanity == Friendsanity.option_all_with_marriage
heart_size = options.friendsanity_heart_size
for villager in all_villagers:
if villager.mod_name not in world_options[options.Mods] and villager.mod_name is not None:
if villager.mod_name not in options.mods and villager.mod_name is not None:
continue
if not villager.available and exclude_locked_villagers:
continue
@@ -228,38 +230,38 @@ def extend_friendsanity_locations(randomized_locations: List[LocationData], worl
randomized_locations.append(location_table[f"Friendsanity: Pet {heart} <3"])
def extend_festival_locations(randomized_locations: List[LocationData], festival_option: int):
if festival_option == options.FestivalLocations.option_disabled:
def extend_festival_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.festival_locations == FestivalLocations.option_disabled:
return
festival_locations = locations_by_tag[LocationTags.FESTIVAL]
randomized_locations.extend(festival_locations)
extend_hard_festival_locations(randomized_locations, festival_option)
extend_hard_festival_locations(randomized_locations, options)
def extend_hard_festival_locations(randomized_locations, festival_option: int):
if festival_option != options.FestivalLocations.option_hard:
def extend_hard_festival_locations(randomized_locations, options: StardewValleyOptions):
if options.festival_locations != FestivalLocations.option_hard:
return
hard_festival_locations = locations_by_tag[LocationTags.FESTIVAL_HARD]
randomized_locations.extend(hard_festival_locations)
def extend_special_order_locations(randomized_locations: List[LocationData], world_options):
if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_disabled:
def extend_special_order_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.special_order_locations == SpecialOrderLocations.option_disabled:
return
include_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_false
board_locations = filter_disabled_locations(world_options, locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD])
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
board_locations = filter_disabled_locations(options, locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD])
randomized_locations.extend(board_locations)
if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_board_qi and include_island:
include_arcade = world_options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_disabled
if options.special_order_locations == SpecialOrderLocations.option_board_qi and include_island:
include_arcade = options.arcade_machine_locations != ArcadeMachineLocations.option_disabled
qi_orders = [location for location in locations_by_tag[LocationTags.SPECIAL_ORDER_QI] if include_arcade or LocationTags.JUNIMO_KART not in location.tags]
randomized_locations.extend(qi_orders)
def extend_walnut_purchase_locations(randomized_locations: List[LocationData], world_options):
if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true:
def extend_walnut_purchase_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
return
randomized_locations.append(location_table["Repair Ticket Machine"])
randomized_locations.append(location_table["Repair Boat Hull"])
@@ -269,82 +271,82 @@ def extend_walnut_purchase_locations(randomized_locations: List[LocationData], w
randomized_locations.extend(locations_by_tag[LocationTags.WALNUT_PURCHASE])
def extend_mandatory_locations(randomized_locations: List[LocationData], world_options):
def extend_mandatory_locations(randomized_locations: List[LocationData], options):
mandatory_locations = [location for location in locations_by_tag[LocationTags.MANDATORY]]
filtered_mandatory_locations = filter_disabled_locations(world_options, mandatory_locations)
filtered_mandatory_locations = filter_disabled_locations(options, mandatory_locations)
randomized_locations.extend(filtered_mandatory_locations)
def extend_backpack_locations(randomized_locations: List[LocationData], world_options):
if world_options[options.BackpackProgression] == options.BackpackProgression.option_vanilla:
def extend_backpack_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.backpack_progression == BackpackProgression.option_vanilla:
return
backpack_locations = [location for location in locations_by_tag[LocationTags.BACKPACK]]
filtered_backpack_locations = filter_modded_locations(world_options, backpack_locations)
filtered_backpack_locations = filter_modded_locations(options, backpack_locations)
randomized_locations.extend(filtered_backpack_locations)
def extend_elevator_locations(randomized_locations: List[LocationData], world_options):
if world_options[options.ElevatorProgression] == options.ElevatorProgression.option_vanilla:
def extend_elevator_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.elevator_progression == ElevatorProgression.option_vanilla:
return
elevator_locations = [location for location in locations_by_tag[LocationTags.ELEVATOR]]
filtered_elevator_locations = filter_modded_locations(world_options, elevator_locations)
filtered_elevator_locations = filter_modded_locations(options, elevator_locations)
randomized_locations.extend(filtered_elevator_locations)
def create_locations(location_collector: StardewLocationCollector,
world_options: options.StardewOptions,
options: StardewValleyOptions,
random: Random):
randomized_locations = []
extend_mandatory_locations(randomized_locations, world_options)
extend_backpack_locations(randomized_locations, world_options)
extend_mandatory_locations(randomized_locations, options)
extend_backpack_locations(randomized_locations, options)
if not world_options[options.ToolProgression] == options.ToolProgression.option_vanilla:
if not options.tool_progression == ToolProgression.option_vanilla:
randomized_locations.extend(locations_by_tag[LocationTags.TOOL_UPGRADE])
extend_elevator_locations(randomized_locations, world_options)
extend_elevator_locations(randomized_locations, options)
if not world_options[options.SkillProgression] == options.SkillProgression.option_vanilla:
if not options.skill_progression == SkillProgression.option_vanilla:
for location in locations_by_tag[LocationTags.SKILL_LEVEL]:
if location.mod_name is None or location.mod_name in world_options[options.Mods]:
if location.mod_name is None or location.mod_name in options.mods:
randomized_locations.append(location_table[location.name])
if not world_options[options.BuildingProgression] == options.BuildingProgression.option_vanilla:
if not options.building_progression == BuildingProgression.option_vanilla:
for location in locations_by_tag[LocationTags.BUILDING_BLUEPRINT]:
if location.mod_name is None or location.mod_name in world_options[options.Mods]:
if location.mod_name is None or location.mod_name in options.mods:
randomized_locations.append(location_table[location.name])
if world_options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_disabled:
if options.arcade_machine_locations != ArcadeMachineLocations.option_disabled:
randomized_locations.extend(locations_by_tag[LocationTags.ARCADE_MACHINE_VICTORY])
if world_options[options.ArcadeMachineLocations] == options.ArcadeMachineLocations.option_full_shuffling:
if options.arcade_machine_locations == ArcadeMachineLocations.option_full_shuffling:
randomized_locations.extend(locations_by_tag[LocationTags.ARCADE_MACHINE])
extend_cropsanity_locations(randomized_locations, world_options)
extend_help_wanted_quests(randomized_locations, world_options[options.HelpWantedLocations])
extend_fishsanity_locations(randomized_locations, world_options, random)
extend_museumsanity_locations(randomized_locations, world_options[options.Museumsanity], random)
extend_friendsanity_locations(randomized_locations, world_options)
extend_cropsanity_locations(randomized_locations, options)
extend_help_wanted_quests(randomized_locations, options.help_wanted_locations.value)
extend_fishsanity_locations(randomized_locations, options, random)
extend_museumsanity_locations(randomized_locations, options, random)
extend_friendsanity_locations(randomized_locations, options)
extend_festival_locations(randomized_locations, world_options[options.FestivalLocations])
extend_special_order_locations(randomized_locations, world_options)
extend_walnut_purchase_locations(randomized_locations, world_options)
extend_festival_locations(randomized_locations, options)
extend_special_order_locations(randomized_locations, options)
extend_walnut_purchase_locations(randomized_locations, options)
for location_data in randomized_locations:
location_collector(location_data.name, location_data.code, location_data.region)
def filter_ginger_island(world_options: options.StardewOptions, locations: List[LocationData]) -> List[LocationData]:
include_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_false
def filter_ginger_island(options: StardewValleyOptions, locations: List[LocationData]) -> List[LocationData]:
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
return [location for location in locations if include_island or LocationTags.GINGER_ISLAND not in location.tags]
def filter_modded_locations(world_options: options.StardewOptions, locations: List[LocationData]) -> List[LocationData]:
current_mod_names = world_options[options.Mods]
def filter_modded_locations(options: StardewValleyOptions, locations: List[LocationData]) -> List[LocationData]:
current_mod_names = options.mods
return [location for location in locations if location.mod_name is None or location.mod_name in current_mod_names]
def filter_disabled_locations(world_options: options.StardewOptions, locations: List[LocationData]) -> List[LocationData]:
locations_first_pass = filter_ginger_island(world_options, locations)
locations_second_pass = filter_modded_locations(world_options, locations_first_pass)
def filter_disabled_locations(options: StardewValleyOptions, locations: List[LocationData]) -> List[LocationData]:
locations_first_pass = filter_ginger_island(options, locations)
locations_second_pass = filter_modded_locations(options, locations_first_pass)
return locations_second_pass