[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:
parent
3c3954f5e8
commit
c711d803f8
|
@ -1,6 +1,6 @@
|
||||||
from BaseClasses import Item
|
from BaseClasses import Item
|
||||||
from typing import NamedTuple, Dict
|
from typing import NamedTuple, Dict
|
||||||
|
from .Overcooked2Levels import Overcooked2Dlc
|
||||||
|
|
||||||
class ItemData(NamedTuple):
|
class ItemData(NamedTuple):
|
||||||
code: int
|
code: int
|
||||||
|
@ -77,6 +77,18 @@ item_frequencies = {
|
||||||
"Ok Emote": 0,
|
"Ok Emote": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dlc_exclusives = {
|
||||||
|
"Wood" : {Overcooked2Dlc.CAMPFIRE_COOK_OFF},
|
||||||
|
"Coal Bucket" : {Overcooked2Dlc.NIGHT_OF_THE_HANGRY_HORDE},
|
||||||
|
"Bellows" : {Overcooked2Dlc.SURF_N_TURF},
|
||||||
|
"Control Stick Batteries" : {Overcooked2Dlc.STORY, Overcooked2Dlc.SURF_N_TURF, Overcooked2Dlc.NIGHT_OF_THE_HANGRY_HORDE, Overcooked2Dlc.CARNIVAL_OF_CHAOS, Overcooked2Dlc.SEASONAL},
|
||||||
|
"Wok Wheels" : {Overcooked2Dlc.SEASONAL},
|
||||||
|
"Lightweight Backpack" : {Overcooked2Dlc.CAMPFIRE_COOK_OFF},
|
||||||
|
"Faster Condiment/Drink Switch" : {Overcooked2Dlc.SEASONAL, Overcooked2Dlc.CARNIVAL_OF_CHAOS},
|
||||||
|
"Calmer Unbread" : {Overcooked2Dlc.SEASONAL, Overcooked2Dlc.NIGHT_OF_THE_HANGRY_HORDE},
|
||||||
|
"Coin Purse" : {Overcooked2Dlc.SEASONAL, Overcooked2Dlc.NIGHT_OF_THE_HANGRY_HORDE},
|
||||||
|
}
|
||||||
|
|
||||||
item_name_to_config_name = {
|
item_name_to_config_name = {
|
||||||
"Wood" : "DisableWood" ,
|
"Wood" : "DisableWood" ,
|
||||||
"Coal Bucket" : "DisableCoal" ,
|
"Coal Bucket" : "DisableCoal" ,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from BaseClasses import CollectionState
|
from BaseClasses import CollectionState
|
||||||
from .Overcooked2Levels import Overcooked2GenericLevel, Overcooked2Dlc, Overcooked2Level, OverworldRegion, overworld_region_by_level
|
from .Overcooked2Levels import Overcooked2GenericLevel, Overcooked2Dlc, Overcooked2Level, OverworldRegion, overworld_region_by_level
|
||||||
from typing import Dict
|
from typing import Dict, Set
|
||||||
from random import Random
|
from random import Random
|
||||||
|
|
||||||
def has_requirements_for_level_access(state: CollectionState, level_name: str, previous_level_completed_event_name: str,
|
def has_requirements_for_level_access(state: CollectionState, level_name: str, previous_level_completed_event_name: str,
|
||||||
|
@ -132,11 +132,18 @@ def level_shuffle_factory(
|
||||||
rng: Random,
|
rng: Random,
|
||||||
shuffle_prep_levels: bool,
|
shuffle_prep_levels: bool,
|
||||||
shuffle_horde_levels: bool,
|
shuffle_horde_levels: bool,
|
||||||
|
kevin_levels: bool,
|
||||||
|
enabled_dlc: Set[Overcooked2Dlc],
|
||||||
|
player_name: str,
|
||||||
) -> Dict[int, Overcooked2GenericLevel]: # return <story_level_id, level>
|
) -> Dict[int, Overcooked2GenericLevel]: # return <story_level_id, level>
|
||||||
|
|
||||||
# Create a list of all valid levels for selection
|
# Create a list of all valid levels for selection
|
||||||
# (excludes tutorial, throne and sometimes horde/prep levels)
|
# (excludes tutorial, throne and sometimes horde/prep levels)
|
||||||
pool = list()
|
pool = list()
|
||||||
for dlc in Overcooked2Dlc:
|
for dlc in Overcooked2Dlc:
|
||||||
|
if dlc not in enabled_dlc:
|
||||||
|
continue
|
||||||
|
|
||||||
for level_id in range(dlc.start_level_id, dlc.end_level_id):
|
for level_id in range(dlc.start_level_id, dlc.end_level_id):
|
||||||
if level_id in dlc.excluded_levels():
|
if level_id in dlc.excluded_levels():
|
||||||
continue
|
continue
|
||||||
|
@ -151,25 +158,55 @@ def level_shuffle_factory(
|
||||||
Overcooked2GenericLevel(level_id, dlc)
|
Overcooked2GenericLevel(level_id, dlc)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if kevin_levels:
|
||||||
|
level_count = 43
|
||||||
|
else:
|
||||||
|
level_count = 35
|
||||||
|
|
||||||
|
if len(pool) < level_count:
|
||||||
|
if shuffle_prep_levels:
|
||||||
|
prep_text = ""
|
||||||
|
else:
|
||||||
|
prep_text = " NON-PREP"
|
||||||
|
|
||||||
|
raise Exception(f"Invalid OC2 settings({player_name}): OC2 needs at least {level_count}{prep_text} levels in the level pool (currently has {len(pool)})")
|
||||||
|
|
||||||
# Sort the pool to eliminate risk
|
# Sort the pool to eliminate risk
|
||||||
pool.sort(key=lambda x: int(x.dlc)*1000 + x.level_id)
|
pool.sort(key=lambda x: int(x.dlc)*1000 + x.level_id)
|
||||||
|
|
||||||
result: Dict[int, Overcooked2GenericLevel] = dict()
|
result: Dict[int, Overcooked2GenericLevel] = dict()
|
||||||
story = Overcooked2Dlc.STORY
|
story = Overcooked2Dlc.STORY
|
||||||
|
|
||||||
|
attempts = 0
|
||||||
|
|
||||||
while len(result) == 0 or not meets_minimum_sphere_one_requirements(result):
|
while len(result) == 0 or not meets_minimum_sphere_one_requirements(result):
|
||||||
|
if attempts >= 15:
|
||||||
|
raise Exception("Failed to create valid Overcooked2 level shuffle permutation in a reasonable amount of attempts")
|
||||||
|
|
||||||
result.clear()
|
result.clear()
|
||||||
|
|
||||||
# Shuffle the pool, using the provided RNG
|
# Shuffle the pool, using the provided RNG
|
||||||
rng.shuffle(pool)
|
rng.shuffle(pool)
|
||||||
|
|
||||||
# Return the first 44 levels and assign those to each level
|
# Handle level assignment
|
||||||
for level_id in range(story.start_level_id, story.end_level_id):
|
|
||||||
if level_id not in story.excluded_levels():
|
level_id = 0
|
||||||
result[level_id] = pool[level_id-1]
|
placed = 0
|
||||||
elif level_id == 36:
|
for level in pool:
|
||||||
# Level 6-6 is exempt from shuffling
|
level_id += 1
|
||||||
result[level_id] = Overcooked2GenericLevel(level_id)
|
while level_id in story.excluded_levels():
|
||||||
|
level_id += 1
|
||||||
|
|
||||||
|
result[level_id] = level
|
||||||
|
placed += 1
|
||||||
|
|
||||||
|
if placed >= level_count:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Level 6-6 is exempt from shuffling
|
||||||
|
result[36] = Overcooked2GenericLevel(36)
|
||||||
|
|
||||||
|
attempts += 1
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -178,12 +215,12 @@ def meets_minimum_sphere_one_requirements(
|
||||||
levels: Dict[int, Overcooked2GenericLevel],
|
levels: Dict[int, Overcooked2GenericLevel],
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
# 1-1, 2-1, and 4-1 are garunteed to be accessible on
|
# 1-1, 2-1, and 4-1 are guaranteed to be accessible on
|
||||||
# the overworld without requiring a ramp or additional stars
|
# the overworld without requiring a ramp or additional stars
|
||||||
sphere_one = [1, 7, 19]
|
sphere_one = [1, 7, 19]
|
||||||
|
|
||||||
# 1-2, 2-2, 3-1 and 5-1 are almost always the next thing unlocked
|
# 1-2, 2-2, 3-1, 5-1 and 6-1 are almost always the next thing unlocked
|
||||||
sphere_twoish = [2, 8, 13, 25]
|
sphere_twoish = [2, 8, 13, 25, 31]
|
||||||
|
|
||||||
# Peek the logic for sphere one and see how many are possible
|
# Peek the logic for sphere one and see how many are possible
|
||||||
# with no items
|
# with no items
|
||||||
|
@ -199,7 +236,7 @@ def meets_minimum_sphere_one_requirements(
|
||||||
|
|
||||||
return sphere_one_count >= 2 and \
|
return sphere_one_count >= 2 and \
|
||||||
sphere_twoish_count >= 2 and \
|
sphere_twoish_count >= 2 and \
|
||||||
sphere_one_count + sphere_twoish_count >= 6
|
sphere_one_count + sphere_twoish_count >= 5
|
||||||
|
|
||||||
|
|
||||||
def is_completable_no_items(level: Overcooked2GenericLevel) -> bool:
|
def is_completable_no_items(level: Overcooked2GenericLevel) -> bool:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
from Options import Toggle, DefaultOnToggle, Range, Choice
|
from Options import DefaultOnToggle, Toggle, Range, Choice, OptionSet
|
||||||
|
from .Overcooked2Levels import Overcooked2Dlc
|
||||||
|
|
||||||
class LocationBalancingMode(IntEnum):
|
class LocationBalancingMode(IntEnum):
|
||||||
disabled = 0
|
disabled = 0
|
||||||
|
@ -87,6 +87,13 @@ class ShuffleLevelOrder(OC2OnToggle):
|
||||||
display_name = "Shuffle Level Order"
|
display_name = "Shuffle Level Order"
|
||||||
|
|
||||||
|
|
||||||
|
class DLCOptionSet(OptionSet):
|
||||||
|
"""Which DLCs should be included when 'Shuffle Level Order' is enabled?'"""
|
||||||
|
display_name = "Enabled DLC"
|
||||||
|
default = {"Story", "Seasonal"}
|
||||||
|
valid_keys = [dlc.value for dlc in Overcooked2Dlc]
|
||||||
|
|
||||||
|
|
||||||
class IncludeHordeLevels(OC2OnToggle):
|
class IncludeHordeLevels(OC2OnToggle):
|
||||||
"""Includes "Horde Defense" levels in the pool of possible kitchens when Shuffle Level Order is enabled. Also adds
|
"""Includes "Horde Defense" levels in the pool of possible kitchens when Shuffle Level Order is enabled. Also adds
|
||||||
two horde-specific items into the item pool."""
|
two horde-specific items into the item pool."""
|
||||||
|
@ -170,6 +177,7 @@ overcooked_options = {
|
||||||
|
|
||||||
# randomization options
|
# randomization options
|
||||||
"shuffle_level_order": ShuffleLevelOrder,
|
"shuffle_level_order": ShuffleLevelOrder,
|
||||||
|
"include_dlcs": DLCOptionSet,
|
||||||
"include_horde_levels": IncludeHordeLevels,
|
"include_horde_levels": IncludeHordeLevels,
|
||||||
"prep_levels": PrepLevels,
|
"prep_levels": PrepLevels,
|
||||||
"kevin_levels": KevinLevels,
|
"kevin_levels": KevinLevels,
|
||||||
|
|
|
@ -4,11 +4,11 @@ from typing import List
|
||||||
|
|
||||||
class Overcooked2Dlc(Enum):
|
class Overcooked2Dlc(Enum):
|
||||||
STORY = "Story"
|
STORY = "Story"
|
||||||
|
SEASONAL = "Seasonal"
|
||||||
SURF_N_TURF = "Surf 'n' Turf"
|
SURF_N_TURF = "Surf 'n' Turf"
|
||||||
CAMPFIRE_COOK_OFF = "Campfire Cook Off"
|
CAMPFIRE_COOK_OFF = "Campfire Cook Off"
|
||||||
NIGHT_OF_THE_HANGRY_HORDE = "Night of the Hangry Horde"
|
NIGHT_OF_THE_HANGRY_HORDE = "Night of the Hangry Horde"
|
||||||
CARNIVAL_OF_CHAOS = "Carnival of Chaos"
|
CARNIVAL_OF_CHAOS = "Carnival of Chaos"
|
||||||
SEASONAL = "Seasonal"
|
|
||||||
# CHRISTMAS = "Christmas"
|
# CHRISTMAS = "Christmas"
|
||||||
# CHINESE_NEW_YEAR = "Chinese New Year"
|
# CHINESE_NEW_YEAR = "Chinese New Year"
|
||||||
# WINTER_WONDERLAND = "Winter Wonderland"
|
# WINTER_WONDERLAND = "Winter Wonderland"
|
||||||
|
@ -87,31 +87,6 @@ class Overcooked2Dlc(Enum):
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def exclusive_items(self) -> List[str]:
|
|
||||||
"""Returns list of items exclusive to this DLC"""
|
|
||||||
if self == Overcooked2Dlc.SURF_N_TURF:
|
|
||||||
return ["Bellows"]
|
|
||||||
if self == Overcooked2Dlc.CAMPFIRE_COOK_OFF:
|
|
||||||
return ["Wood"]
|
|
||||||
if self == Overcooked2Dlc.NIGHT_OF_THE_HANGRY_HORDE:
|
|
||||||
return ["Coal Bucket"]
|
|
||||||
if self == Overcooked2Dlc.CARNIVAL_OF_CHAOS:
|
|
||||||
return ["Faster Condiment/Drink Switch"]
|
|
||||||
if self == Overcooked2Dlc.SEASONAL:
|
|
||||||
return ["Wok Wheels"]
|
|
||||||
|
|
||||||
return []
|
|
||||||
|
|
||||||
ITEMS_TO_EXCLUDE_IF_NO_DLC = [
|
|
||||||
"Wood",
|
|
||||||
"Coal Bucket",
|
|
||||||
"Bellows",
|
|
||||||
"Coin Purse",
|
|
||||||
"Wok Wheels",
|
|
||||||
"Lightweight Backpack",
|
|
||||||
"Faster Condiment/Drink Switch",
|
|
||||||
"Calmer Unbread",
|
|
||||||
]
|
|
||||||
|
|
||||||
class Overcooked2GameWorld(IntEnum):
|
class Overcooked2GameWorld(IntEnum):
|
||||||
ONE = 1
|
ONE = 1
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from enum import IntEnum
|
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 BaseClasses import ItemClassification, CollectionState, Region, Entrance, Location, Tutorial, LocationProgressType
|
||||||
from worlds.AutoWorld import World, WebWorld
|
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 .Locations import Overcooked2Location, oc2_location_name_to_id, oc2_location_id_to_name
|
||||||
from .Options import overcooked_options, OC2Options, OC2OnToggle, LocationBalancingMode, DeathLinkMode
|
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
|
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
|
# Helper Data
|
||||||
|
|
||||||
|
player_name: str
|
||||||
level_unlock_counts: Dict[int, int] # level_id, stars to purchase
|
level_unlock_counts: Dict[int, int] # level_id, stars to purchase
|
||||||
level_mapping: Dict[int, Overcooked2GenericLevel] # level_id, level
|
level_mapping: Dict[int, Overcooked2GenericLevel] # level_id, level
|
||||||
|
enabled_dlc: Set[Overcooked2Dlc]
|
||||||
|
|
||||||
# Autoworld Hooks
|
# Autoworld Hooks
|
||||||
|
|
||||||
def generate_early(self):
|
def generate_early(self):
|
||||||
|
self.player_name = self.multiworld.player_name[self.player]
|
||||||
self.options = self.get_options()
|
self.options = self.get_options()
|
||||||
|
|
||||||
# 0.0 to 1.0 where 1.0 is World Record
|
# 0.0 to 1.0 where 1.0 is World Record
|
||||||
self.star_threshold_scale = self.options["StarThresholdScale"] / 100.0
|
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
|
# 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
|
# the further the game has progressed, and levels progress radially rather than linearly
|
||||||
self.level_unlock_counts = level_unlock_requirement_factory(self.options["StarsToWin"])
|
self.level_unlock_counts = level_unlock_requirement_factory(self.options["StarsToWin"])
|
||||||
|
@ -244,9 +250,16 @@ class Overcooked2World(World):
|
||||||
self.multiworld.random,
|
self.multiworld.random,
|
||||||
self.options["PrepLevels"] != PrepLevelMode.excluded,
|
self.options["PrepLevels"] != PrepLevelMode.excluded,
|
||||||
self.options["IncludeHordeLevels"],
|
self.options["IncludeHordeLevels"],
|
||||||
|
self.options["KevinLevels"],
|
||||||
|
self.enabled_dlc,
|
||||||
|
self.player_name,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.level_mapping = None
|
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:
|
def set_location_priority(self) -> None:
|
||||||
priority_locations = self.get_priority_locations()
|
priority_locations = self.get_priority_locations()
|
||||||
|
@ -351,17 +364,23 @@ class Overcooked2World(World):
|
||||||
# not used
|
# not used
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not self.options["ShuffleLevelOrder"] and item_name in ITEMS_TO_EXCLUDE_IF_NO_DLC:
|
if item_name in dlc_exclusives:
|
||||||
# skip DLC items if no DLC
|
if not any(x in dlc_exclusives[item_name] for x in self.enabled_dlc):
|
||||||
continue
|
# Item is always useless with these settings
|
||||||
|
continue
|
||||||
|
|
||||||
if not self.options["IncludeHordeLevels"] and item_name in ["Calmer Unbread", "Coin Purse"]:
|
if not self.options["IncludeHordeLevels"] and item_name in ["Calmer Unbread", "Coin Purse"]:
|
||||||
# skip horde-specific items if no horde levels
|
# skip horde-specific items if no horde levels
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not self.options["KevinLevels"] and item_name.startswith("Kevin"):
|
if not self.options["KevinLevels"]:
|
||||||
# skip kevin items if no kevin levels
|
if item_name.startswith("Kevin"):
|
||||||
continue
|
# 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"]):
|
if is_item_progression(item_name, self.level_mapping, self.options["KevinLevels"]):
|
||||||
# progression.append(item_name)
|
# progression.append(item_name)
|
||||||
|
@ -425,7 +444,7 @@ class Overcooked2World(World):
|
||||||
# Items get distributed to locations
|
# Items get distributed to locations
|
||||||
|
|
||||||
def fill_json_data(self) -> Dict[str, Any]:
|
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
|
# Serialize Level Order
|
||||||
story_level_order = dict()
|
story_level_order = dict()
|
||||||
|
@ -578,6 +597,17 @@ class Overcooked2World(World):
|
||||||
def fill_slot_data(self) -> Dict[str, Any]:
|
def fill_slot_data(self) -> Dict[str, Any]:
|
||||||
return self.fill_json_data()
|
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]:
|
def level_unlock_requirement_factory(stars_to_win: int) -> Dict[int, int]:
|
||||||
level_unlock_counts = dict()
|
level_unlock_counts = dict()
|
||||||
|
|
|
@ -51,8 +51,11 @@ To completely remove *OC2-Modding*, navigate to your game's installation folder
|
||||||
|
|
||||||
1. Visit the [Player Settings](../../../../games/Overcooked!%202/player-settings) page and configure the game-specific settings to taste
|
1. Visit the [Player Settings](../../../../games/Overcooked!%202/player-settings) page and configure the game-specific settings to taste
|
||||||
|
|
||||||
|
*By default, these settings will only use levels from the base game and the "Seasonal" free DLC updates. If you own any of the paid DLC, you may select individual DLC packs to include/exclude on the [Weighted Settings](../../../../weighted-settings) page*
|
||||||
|
|
||||||
2. Export your yaml file and use it to generate a new randomized game
|
2. Export your yaml file and use it to generate a new randomized game
|
||||||
- (For instructions on how to generate an Archipelago game, refer to the [Archipelago Web Guide](../../../../tutorial/Archipelago/using_website/en))
|
|
||||||
|
*For instructions on how to generate an Archipelago game, refer to the [Archipelago Web Guide](../../../../tutorial/Archipelago/using_website/en)*
|
||||||
|
|
||||||
## Joining a MultiWorld Game
|
## Joining a MultiWorld Game
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from worlds.AutoWorld import AutoWorldRegister
|
||||||
from test.general import setup_solo_multiworld
|
from test.general import setup_solo_multiworld
|
||||||
|
|
||||||
from worlds.overcooked2.Items import *
|
from worlds.overcooked2.Items import *
|
||||||
from worlds.overcooked2.Overcooked2Levels import Overcooked2Dlc, Overcooked2Level, OverworldRegion, overworld_region_by_level, level_id_to_shortname, ITEMS_TO_EXCLUDE_IF_NO_DLC
|
from worlds.overcooked2.Overcooked2Levels import Overcooked2Dlc, Overcooked2Level, OverworldRegion, overworld_region_by_level, level_id_to_shortname
|
||||||
from worlds.overcooked2.Logic import level_logic, overworld_region_logic, level_shuffle_factory
|
from worlds.overcooked2.Logic import level_logic, overworld_region_logic, level_shuffle_factory
|
||||||
from worlds.overcooked2.Locations import oc2_location_name_to_id
|
from worlds.overcooked2.Locations import oc2_location_name_to_id
|
||||||
|
|
||||||
|
@ -67,26 +67,24 @@ class Overcooked2Test(unittest.TestCase):
|
||||||
|
|
||||||
def testOvercooked2ShuffleFactory(self):
|
def testOvercooked2ShuffleFactory(self):
|
||||||
previous_runs = set()
|
previous_runs = set()
|
||||||
|
|
||||||
|
# Test uniqueness
|
||||||
for seed in range(0, 5):
|
for seed in range(0, 5):
|
||||||
levels = level_shuffle_factory(Random(seed), True, False)
|
levels = level_shuffle_factory(Random(seed), True, False, True, {x for x in Overcooked2Dlc}, "test")
|
||||||
self.assertEqual(len(levels), 44)
|
self.assertEqual(len(levels), 44)
|
||||||
previous_level_id = None
|
|
||||||
for level_id in levels.keys():
|
|
||||||
if previous_level_id is not None:
|
|
||||||
self.assertEqual(previous_level_id+1, level_id)
|
|
||||||
previous_level_id = level_id
|
|
||||||
|
|
||||||
self.assertNotIn(levels[15], previous_runs)
|
self.assertNotIn((levels[5], levels[15]), previous_runs)
|
||||||
previous_runs.add(levels[15])
|
previous_runs.add((levels[5], levels[15]))
|
||||||
|
|
||||||
levels = level_shuffle_factory(Random(123), False, True)
|
# Test kevin = false
|
||||||
self.assertEqual(len(levels), 44)
|
levels = level_shuffle_factory(Random(123), False, True, False, {x for x in Overcooked2Dlc}, "test")
|
||||||
|
self.assertEqual(len(levels), 36)
|
||||||
|
|
||||||
def testLevelNameRepresentation(self):
|
def testLevelNameRepresentation(self):
|
||||||
shortnames = [level.as_generic_level.shortname for level in Overcooked2Level()]
|
shortnames = [level.as_generic_level.shortname for level in Overcooked2Level()]
|
||||||
|
|
||||||
for shortname in shortnames:
|
for shortname in shortnames:
|
||||||
self.assertIn(shortname, level_logic.keys())
|
self.assertIn(shortname, level_logic)
|
||||||
|
|
||||||
self.assertEqual(len(level_logic), len(level_id_to_shortname))
|
self.assertEqual(len(level_logic), len(level_id_to_shortname))
|
||||||
|
|
||||||
|
@ -142,13 +140,9 @@ class Overcooked2Test(unittest.TestCase):
|
||||||
|
|
||||||
self.assertLessEqual(number_of_items, len(oc2_location_name_to_id), "Too many items (before fillers placed)")
|
self.assertLessEqual(number_of_items, len(oc2_location_name_to_id), "Too many items (before fillers placed)")
|
||||||
|
|
||||||
def testExclusiveItems(self):
|
def testDlcExclusives(self):
|
||||||
for dlc in Overcooked2Dlc:
|
for item in dlc_exclusives:
|
||||||
for item in dlc.exclusive_items():
|
self.assertIn(item, item_table)
|
||||||
self.assertIn(item, item_table.keys())
|
|
||||||
|
|
||||||
for item in ITEMS_TO_EXCLUDE_IF_NO_DLC:
|
|
||||||
self.assertIn(item, item_table.keys())
|
|
||||||
|
|
||||||
def testLevelCounts(self):
|
def testLevelCounts(self):
|
||||||
for dlc in Overcooked2Dlc:
|
for dlc in Overcooked2Dlc:
|
||||||
|
|
Loading…
Reference in New Issue