mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -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:
@@ -6,7 +6,7 @@ from .Rules import set_rules
|
||||
from .RoR2Environments import *
|
||||
|
||||
from BaseClasses import Region, Entrance, Item, ItemClassification, MultiWorld, Tutorial
|
||||
from .Options import ror2_options, ItemWeights
|
||||
from .Options import ItemWeights, ROR2Options
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from .Regions import create_regions
|
||||
|
||||
@@ -28,8 +28,9 @@ class RiskOfRainWorld(World):
|
||||
Combine loot in surprising ways and master each character until you become the havoc you feared upon your
|
||||
first crash landing.
|
||||
"""
|
||||
game: str = "Risk of Rain 2"
|
||||
option_definitions = ror2_options
|
||||
game = "Risk of Rain 2"
|
||||
options_dataclass = ROR2Options
|
||||
options: ROR2Options
|
||||
topology_present = False
|
||||
|
||||
item_name_to_id = item_table
|
||||
@@ -46,45 +47,44 @@ class RiskOfRainWorld(World):
|
||||
|
||||
def generate_early(self) -> None:
|
||||
# figure out how many revivals should exist in the pool
|
||||
if self.multiworld.goal[self.player] == "classic":
|
||||
total_locations = self.multiworld.total_locations[self.player].value
|
||||
if self.options.goal == "classic":
|
||||
total_locations = self.options.total_locations.value
|
||||
else:
|
||||
total_locations = len(
|
||||
orderedstage_location.get_locations(
|
||||
chests=self.multiworld.chests_per_stage[self.player].value,
|
||||
shrines=self.multiworld.shrines_per_stage[self.player].value,
|
||||
scavengers=self.multiworld.scavengers_per_stage[self.player].value,
|
||||
scanners=self.multiworld.scanner_per_stage[self.player].value,
|
||||
altars=self.multiworld.altars_per_stage[self.player].value,
|
||||
dlc_sotv=self.multiworld.dlc_sotv[self.player].value
|
||||
chests=self.options.chests_per_stage.value,
|
||||
shrines=self.options.shrines_per_stage.value,
|
||||
scavengers=self.options.scavengers_per_stage.value,
|
||||
scanners=self.options.scanner_per_stage.value,
|
||||
altars=self.options.altars_per_stage.value,
|
||||
dlc_sotv=self.options.dlc_sotv.value
|
||||
)
|
||||
)
|
||||
self.total_revivals = int(self.multiworld.total_revivals[self.player].value / 100 *
|
||||
self.total_revivals = int(self.options.total_revivals.value / 100 *
|
||||
total_locations)
|
||||
# self.total_revivals = self.multiworld.total_revivals[self.player].value
|
||||
if self.multiworld.start_with_revive[self.player].value:
|
||||
if self.options.start_with_revive:
|
||||
self.total_revivals -= 1
|
||||
|
||||
def create_items(self) -> None:
|
||||
# shortcut for starting_inventory... The start_with_revive option lets you start with a Dio's Best Friend
|
||||
if self.multiworld.start_with_revive[self.player]:
|
||||
if self.options.start_with_revive:
|
||||
self.multiworld.push_precollected(self.multiworld.create_item("Dio's Best Friend", self.player))
|
||||
|
||||
environments_pool = {}
|
||||
# only mess with the environments if they are set as items
|
||||
if self.multiworld.goal[self.player] == "explore":
|
||||
if self.options.goal == "explore":
|
||||
|
||||
# figure out all available ordered stages for each tier
|
||||
environment_available_orderedstages_table = environment_vanilla_orderedstages_table
|
||||
if self.multiworld.dlc_sotv[self.player]:
|
||||
if self.options.dlc_sotv:
|
||||
environment_available_orderedstages_table = collapse_dict_list_vertical(environment_available_orderedstages_table, environment_sotv_orderedstages_table)
|
||||
|
||||
environments_pool = shift_by_offset(environment_vanilla_table, environment_offest)
|
||||
|
||||
if self.multiworld.dlc_sotv[self.player]:
|
||||
if self.options.dlc_sotv:
|
||||
environment_offset_table = shift_by_offset(environment_sotv_table, environment_offest)
|
||||
environments_pool = {**environments_pool, **environment_offset_table}
|
||||
environments_to_precollect = 5 if self.multiworld.begin_with_loop[self.player].value else 1
|
||||
environments_to_precollect = 5 if self.options.begin_with_loop else 1
|
||||
# percollect environments for each stage (or just stage 1)
|
||||
for i in range(environments_to_precollect):
|
||||
unlock = self.multiworld.random.choices(list(environment_available_orderedstages_table[i].keys()), k=1)
|
||||
@@ -100,19 +100,19 @@ class RiskOfRainWorld(World):
|
||||
for env_name, _ in environments_pool.items():
|
||||
itempool += [env_name]
|
||||
|
||||
if self.multiworld.goal[self.player] == "classic":
|
||||
if self.options.goal == "classic":
|
||||
# classic mode
|
||||
total_locations = self.multiworld.total_locations[self.player].value
|
||||
total_locations = self.options.total_locations.value
|
||||
else:
|
||||
# explore mode
|
||||
total_locations = len(
|
||||
orderedstage_location.get_locations(
|
||||
chests=self.multiworld.chests_per_stage[self.player].value,
|
||||
shrines=self.multiworld.shrines_per_stage[self.player].value,
|
||||
scavengers=self.multiworld.scavengers_per_stage[self.player].value,
|
||||
scanners=self.multiworld.scanner_per_stage[self.player].value,
|
||||
altars=self.multiworld.altars_per_stage[self.player].value,
|
||||
dlc_sotv=self.multiworld.dlc_sotv[self.player].value
|
||||
chests=self.options.chests_per_stage.value,
|
||||
shrines=self.options.shrines_per_stage.value,
|
||||
scavengers=self.options.scavengers_per_stage.value,
|
||||
scanners=self.options.scanner_per_stage.value,
|
||||
altars=self.options.altars_per_stage.value,
|
||||
dlc_sotv=self.options.dlc_sotv.value
|
||||
)
|
||||
)
|
||||
# Create junk items
|
||||
@@ -138,9 +138,9 @@ class RiskOfRainWorld(World):
|
||||
|
||||
def create_junk_pool(self) -> Dict:
|
||||
# if presets are enabled generate junk_pool from the selected preset
|
||||
pool_option = self.multiworld.item_weights[self.player].value
|
||||
pool_option = self.options.item_weights.value
|
||||
junk_pool: Dict[str, int] = {}
|
||||
if self.multiworld.item_pool_presets[self.player]:
|
||||
if self.options.item_pool_presets:
|
||||
# generate chaos weights if the preset is chosen
|
||||
if pool_option == ItemWeights.option_chaos:
|
||||
for name, max_value in item_pool_weights[pool_option].items():
|
||||
@@ -149,31 +149,31 @@ class RiskOfRainWorld(World):
|
||||
junk_pool = item_pool_weights[pool_option].copy()
|
||||
else: # generate junk pool from user created presets
|
||||
junk_pool = {
|
||||
"Item Scrap, Green": self.multiworld.green_scrap[self.player].value,
|
||||
"Item Scrap, Red": self.multiworld.red_scrap[self.player].value,
|
||||
"Item Scrap, Yellow": self.multiworld.yellow_scrap[self.player].value,
|
||||
"Item Scrap, White": self.multiworld.white_scrap[self.player].value,
|
||||
"Common Item": self.multiworld.common_item[self.player].value,
|
||||
"Uncommon Item": self.multiworld.uncommon_item[self.player].value,
|
||||
"Legendary Item": self.multiworld.legendary_item[self.player].value,
|
||||
"Boss Item": self.multiworld.boss_item[self.player].value,
|
||||
"Lunar Item": self.multiworld.lunar_item[self.player].value,
|
||||
"Void Item": self.multiworld.void_item[self.player].value,
|
||||
"Equipment": self.multiworld.equipment[self.player].value
|
||||
"Item Scrap, Green": self.options.green_scrap.value,
|
||||
"Item Scrap, Red": self.options.red_scrap.value,
|
||||
"Item Scrap, Yellow": self.options.yellow_scrap.value,
|
||||
"Item Scrap, White": self.options.white_scrap.value,
|
||||
"Common Item": self.options.common_item.value,
|
||||
"Uncommon Item": self.options.uncommon_item.value,
|
||||
"Legendary Item": self.options.legendary_item.value,
|
||||
"Boss Item": self.options.boss_item.value,
|
||||
"Lunar Item": self.options.lunar_item.value,
|
||||
"Void Item": self.options.void_item.value,
|
||||
"Equipment": self.options.equipment.value
|
||||
}
|
||||
|
||||
# remove lunar items from the pool if they're disabled in the yaml unless lunartic is rolled
|
||||
if not (self.multiworld.enable_lunar[self.player] or pool_option == ItemWeights.option_lunartic):
|
||||
if not (self.options.enable_lunar or pool_option == ItemWeights.option_lunartic):
|
||||
junk_pool.pop("Lunar Item")
|
||||
# remove void items from the pool
|
||||
if not (self.multiworld.dlc_sotv[self.player] or pool_option == ItemWeights.option_void):
|
||||
if not (self.options.dlc_sotv or pool_option == ItemWeights.option_void):
|
||||
junk_pool.pop("Void Item")
|
||||
|
||||
return junk_pool
|
||||
|
||||
def create_regions(self) -> None:
|
||||
|
||||
if self.multiworld.goal[self.player] == "classic":
|
||||
if self.options.goal == "classic":
|
||||
# classic mode
|
||||
menu = create_region(self.multiworld, self.player, "Menu")
|
||||
self.multiworld.regions.append(menu)
|
||||
@@ -182,7 +182,7 @@ class RiskOfRainWorld(World):
|
||||
victory_region = create_region(self.multiworld, self.player, "Victory")
|
||||
self.multiworld.regions.append(victory_region)
|
||||
petrichor = create_region(self.multiworld, self.player, "Petrichor V",
|
||||
get_classic_item_pickups(self.multiworld.total_locations[self.player].value))
|
||||
get_classic_item_pickups(self.options.total_locations.value))
|
||||
self.multiworld.regions.append(petrichor)
|
||||
|
||||
# classic mode can get to victory from the beginning of the game
|
||||
@@ -200,21 +200,13 @@ class RiskOfRainWorld(World):
|
||||
create_events(self.multiworld, self.player)
|
||||
|
||||
def fill_slot_data(self):
|
||||
options_dict = self.options.as_dict("item_pickup_step", "shrine_use_step", "goal", "total_locations",
|
||||
"chests_per_stage", "shrines_per_stage", "scavengers_per_stage",
|
||||
"scanner_per_stage", "altars_per_stage", "total_revivals", "start_with_revive",
|
||||
"final_stage_death", "death_link", casing="camel")
|
||||
return {
|
||||
"itemPickupStep": self.multiworld.item_pickup_step[self.player].value,
|
||||
"shrineUseStep": self.multiworld.shrine_use_step[self.player].value,
|
||||
"goal": self.multiworld.goal[self.player].value,
|
||||
**options_dict,
|
||||
"seed": "".join(self.multiworld.per_slot_randoms[self.player].choice(string.digits) for _ in range(16)),
|
||||
"totalLocations": self.multiworld.total_locations[self.player].value,
|
||||
"chestsPerStage": self.multiworld.chests_per_stage[self.player].value,
|
||||
"shrinesPerStage": self.multiworld.shrines_per_stage[self.player].value,
|
||||
"scavengersPerStage": self.multiworld.scavengers_per_stage[self.player].value,
|
||||
"scannerPerStage": self.multiworld.scanner_per_stage[self.player].value,
|
||||
"altarsPerStage": self.multiworld.altars_per_stage[self.player].value,
|
||||
"totalRevivals": self.multiworld.total_revivals[self.player].value,
|
||||
"startWithDio": self.multiworld.start_with_revive[self.player].value,
|
||||
"finalStageDeath": self.multiworld.final_stage_death[self.player].value,
|
||||
"deathLink": self.multiworld.death_link[self.player].value,
|
||||
}
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
@@ -241,12 +233,12 @@ class RiskOfRainWorld(World):
|
||||
|
||||
|
||||
def create_events(world: MultiWorld, player: int) -> None:
|
||||
total_locations = world.total_locations[player].value
|
||||
total_locations = world.worlds[player].options.total_locations.value
|
||||
num_of_events = total_locations // 25
|
||||
if total_locations / 25 == num_of_events:
|
||||
num_of_events -= 1
|
||||
world_region = world.get_region("Petrichor V", player)
|
||||
if world.goal[player] == "classic":
|
||||
if world.worlds[player].options.goal == "classic":
|
||||
# only setup Pickups when using classic_mode
|
||||
for i in range(num_of_events):
|
||||
event_loc = RiskOfRainLocation(player, f"Pickup{(i + 1) * 25}", None, world_region)
|
||||
@@ -254,7 +246,7 @@ def create_events(world: MultiWorld, player: int) -> None:
|
||||
event_loc.access_rule = \
|
||||
lambda state, i=i: state.can_reach(f"ItemPickup{((i + 1) * 25) - 1}", "Location", player)
|
||||
world_region.locations.append(event_loc)
|
||||
elif world.goal[player] == "explore":
|
||||
elif world.worlds[player].options.goal == "explore":
|
||||
for n in range(1, 6):
|
||||
|
||||
event_region = world.get_region(f"OrderedStage_{n}", player)
|
||||
|
||||
Reference in New Issue
Block a user