[OC2] Enabled DLC Option (#1688)

- New OC2 option `DLCOptionSet`, which is a list of DLCs whose levels should or shouldn't be used for entrance randomizer (and mention in documentation). By default, DLC owners now need to enable DLCs in weighted settings.
- Throw user-friendly exceptions when contradictory settings are enabled
- Slightly relax generation requirements for sphere 1/2 level permutations
- Write entrance randomizer info in spoiler log
- Skip adding "Dark Green Ramp" to item pool if Kevin Levels are disabled
This commit is contained in:
toasterparty
2023-04-10 18:43:29 -07:00
committed by GitHub
parent 3c3954f5e8
commit c711d803f8
7 changed files with 130 additions and 71 deletions

View File

@@ -1,13 +1,13 @@
from enum import IntEnum
from typing import Callable, Dict, Any, List, Optional
from typing import Any, List, Dict, Set, Callable, Optional, TextIO
from BaseClasses import ItemClassification, CollectionState, Region, Entrance, Location, Tutorial, LocationProgressType
from worlds.AutoWorld import World, WebWorld
from .Overcooked2Levels import Overcooked2Level, Overcooked2GenericLevel, ITEMS_TO_EXCLUDE_IF_NO_DLC
from .Overcooked2Levels import Overcooked2Dlc, Overcooked2Level, Overcooked2GenericLevel
from .Locations import Overcooked2Location, oc2_location_name_to_id, oc2_location_id_to_name
from .Options import overcooked_options, OC2Options, OC2OnToggle, LocationBalancingMode, DeathLinkMode
from .Items import item_table, Overcooked2Item, item_name_to_id, item_id_to_name, item_to_unlock_event, item_frequencies
from .Items import item_table, Overcooked2Item, item_name_to_id, item_id_to_name, item_to_unlock_event, item_frequencies, dlc_exclusives
from .Logic import has_requirements_for_level_star, has_requirements_for_level_access, level_shuffle_factory, is_item_progression, is_useful
@@ -222,17 +222,23 @@ class Overcooked2World(World):
# Helper Data
player_name: str
level_unlock_counts: Dict[int, int] # level_id, stars to purchase
level_mapping: Dict[int, Overcooked2GenericLevel] # level_id, level
enabled_dlc: Set[Overcooked2Dlc]
# Autoworld Hooks
def generate_early(self):
self.player_name = self.multiworld.player_name[self.player]
self.options = self.get_options()
# 0.0 to 1.0 where 1.0 is World Record
self.star_threshold_scale = self.options["StarThresholdScale"] / 100.0
# Parse DLCOptionSet back into enums
self.enabled_dlc = {Overcooked2Dlc(x) for x in self.options["DLCOptionSet"]}
# Generate level unlock requirements such that the levels get harder to unlock
# the further the game has progressed, and levels progress radially rather than linearly
self.level_unlock_counts = level_unlock_requirement_factory(self.options["StarsToWin"])
@@ -244,9 +250,16 @@ class Overcooked2World(World):
self.multiworld.random,
self.options["PrepLevels"] != PrepLevelMode.excluded,
self.options["IncludeHordeLevels"],
self.options["KevinLevels"],
self.enabled_dlc,
self.player_name,
)
else:
self.level_mapping = None
if Overcooked2Dlc.STORY not in self.enabled_dlc:
raise Exception(f"Invalid OC2 settings({self.player_name}) Need either Level Shuffle disabled or 'Story' DLC enabled")
self.enabled_dlc = {Overcooked2Dlc.STORY}
def set_location_priority(self) -> None:
priority_locations = self.get_priority_locations()
@@ -351,17 +364,23 @@ class Overcooked2World(World):
# not used
continue
if not self.options["ShuffleLevelOrder"] and item_name in ITEMS_TO_EXCLUDE_IF_NO_DLC:
# skip DLC items if no DLC
continue
if item_name in dlc_exclusives:
if not any(x in dlc_exclusives[item_name] for x in self.enabled_dlc):
# Item is always useless with these settings
continue
if not self.options["IncludeHordeLevels"] and item_name in ["Calmer Unbread", "Coin Purse"]:
# skip horde-specific items if no horde levels
continue
if not self.options["KevinLevels"] and item_name.startswith("Kevin"):
# skip kevin items if no kevin levels
continue
if not self.options["KevinLevels"]:
if item_name.startswith("Kevin"):
# skip kevin items if no kevin levels
continue
if item_name == "Dark Green Ramp":
# skip dark green ramp if there's no Kevin-1 to reveal it
continue
if is_item_progression(item_name, self.level_mapping, self.options["KevinLevels"]):
# progression.append(item_name)
@@ -425,7 +444,7 @@ class Overcooked2World(World):
# Items get distributed to locations
def fill_json_data(self) -> Dict[str, Any]:
mod_name = f"AP-{self.multiworld.seed_name}-P{self.player}-{self.multiworld.player_name[self.player]}"
mod_name = f"AP-{self.multiworld.seed_name}-P{self.player}-{self.player_name}"
# Serialize Level Order
story_level_order = dict()
@@ -578,6 +597,17 @@ class Overcooked2World(World):
def fill_slot_data(self) -> Dict[str, Any]:
return self.fill_json_data()
def write_spoiler(self, spoiler_handle: TextIO) -> None:
if not self.options["ShuffleLevelOrder"]:
return
world: Overcooked2World = self.multiworld.worlds[self.player]
spoiler_handle.write(f"\n\n{self.player_name}'s Level Order:\n\n")
for overworld_id in world.level_mapping:
overworld_name = Overcooked2GenericLevel(overworld_id).shortname.split("Story ")[1]
kitchen_name = world.level_mapping[overworld_id].shortname
spoiler_handle.write(f'{overworld_name} | {kitchen_name}\n')
def level_unlock_requirement_factory(stars_to_win: int) -> Dict[int, int]:
level_unlock_counts = dict()