| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  | from enum import Enum | 
					
						
							|  |  |  | from typing import Callable, Dict, Any, List, Optional | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from BaseClasses import ItemClassification, CollectionState, Region, Entrance, Location, RegionType, Tutorial | 
					
						
							|  |  |  | from worlds.AutoWorld import World, WebWorld | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from .Overcooked2Levels import Overcooked2Level, Overcooked2GenericLevel | 
					
						
							|  |  |  | from .Locations import Overcooked2Location, oc2_location_name_to_id, oc2_location_id_to_name | 
					
						
							|  |  |  | from .Options import overcooked_options, OC2Options, OC2OnToggle | 
					
						
							|  |  |  | from .Items import item_table, Overcooked2Item, item_name_to_id, item_id_to_name, item_to_unlock_event, item_frequencies | 
					
						
							|  |  |  | from .Logic import has_requirements_for_level_star, has_requirements_for_level_access, level_shuffle_factory, is_item_progression, is_useful | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Overcooked2Web(WebWorld): | 
					
						
							|  |  |  |     theme = "partyTime" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bug_report_page = "https://github.com/toasterparty/oc2-modding/issues" | 
					
						
							|  |  |  |     setup_en = Tutorial( | 
					
						
							|  |  |  |         "Multiworld Setup Tutorial", | 
					
						
							|  |  |  |         "A guide to setting up the Overcooked! 2 randomizer on your computer.", | 
					
						
							|  |  |  |         "English", | 
					
						
							|  |  |  |         "setup_en.md", | 
					
						
							|  |  |  |         "setup/en", | 
					
						
							|  |  |  |         ["toasterparty"] | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     tutorials = [setup_en] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PrepLevelMode(Enum): | 
					
						
							|  |  |  |     original = 0 | 
					
						
							|  |  |  |     excluded = 1 | 
					
						
							|  |  |  |     ayce = 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Overcooked2World(World): | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2022-11-07 10:50:30 -08:00
										 |  |  |     Overcooked! 2 is a frantically paced arcade cooking game where | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  |     players race against the clock to complete orders for points. Bring | 
					
						
							|  |  |  |     peace to the Onion Kingdom once again by recovering lost items and abilities, | 
					
						
							|  |  |  |     earning stars to unlock levels, and defeating the unbread horde. Levels are | 
					
						
							|  |  |  |     randomized to increase gameplay variety. Play with up to 4 friends. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Autoworld API | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     game = "Overcooked! 2" | 
					
						
							|  |  |  |     web = Overcooked2Web() | 
					
						
							|  |  |  |     required_client_version = (0, 3, 4) | 
					
						
							|  |  |  |     option_definitions = overcooked_options | 
					
						
							|  |  |  |     topology_present: bool = False | 
					
						
							|  |  |  |     remote_items: bool = True | 
					
						
							|  |  |  |     remote_start_inventory: bool = False | 
					
						
							|  |  |  |     data_version = 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     item_name_to_id = item_name_to_id | 
					
						
							|  |  |  |     item_id_to_name = item_id_to_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     location_id_to_name = oc2_location_id_to_name | 
					
						
							|  |  |  |     location_name_to_id = oc2_location_name_to_id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     options: Dict[str, Any] | 
					
						
							|  |  |  |     itempool: List[Overcooked2Item] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Helper Functions | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def is_level_horde(self, level_id: int) -> bool: | 
					
						
							|  |  |  |         return self.options["IncludeHordeLevels"] and \ | 
					
						
							|  |  |  |             (self.level_mapping is not None) and \ | 
					
						
							|  |  |  |             level_id in self.level_mapping.keys() and \ | 
					
						
							|  |  |  |             self.level_mapping[level_id].is_horde | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_item(self, item: str, classification: ItemClassification = ItemClassification.progression) -> Overcooked2Item: | 
					
						
							|  |  |  |         return Overcooked2Item(item, classification, self.item_name_to_id[item], self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_event(self, event: str, classification: ItemClassification) -> Overcooked2Item: | 
					
						
							|  |  |  |         return Overcooked2Item(event, classification, None, self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def place_event(self, location_name: str, item_name: str, | 
					
						
							|  |  |  |                     classification: ItemClassification = ItemClassification.progression_skip_balancing): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         location: Location = self.multiworld.get_location(location_name, self.player) | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  |         location.place_locked_item(self.create_event(item_name, classification)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_region(self, region_name: str): | 
					
						
							|  |  |  |         region = Region( | 
					
						
							|  |  |  |             region_name, | 
					
						
							|  |  |  |             RegionType.Generic, | 
					
						
							|  |  |  |             region_name, | 
					
						
							|  |  |  |             self.player, | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             self.multiworld, | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         self.multiworld.regions.append(region) | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def connect_regions(self, source: str, target: str, rule: Optional[Callable[[CollectionState], bool]] = None): | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         sourceRegion = self.multiworld.get_region(source, self.player) | 
					
						
							|  |  |  |         targetRegion = self.multiworld.get_region(target, self.player) | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         connection = Entrance(self.player, '', sourceRegion) | 
					
						
							|  |  |  |         if rule: | 
					
						
							|  |  |  |             connection.access_rule = rule | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         sourceRegion.exits.append(connection) | 
					
						
							|  |  |  |         connection.connect(targetRegion) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_level_location( | 
					
						
							|  |  |  |         self, | 
					
						
							|  |  |  |         region_name: str, | 
					
						
							|  |  |  |         location_name: str, | 
					
						
							|  |  |  |         level_id: int, | 
					
						
							|  |  |  |         stars: int, | 
					
						
							|  |  |  |         is_event: bool = False, | 
					
						
							|  |  |  |     ) -> None: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if is_event: | 
					
						
							|  |  |  |             location_id = None | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             location_id = level_id | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         region = self.multiworld.get_region(region_name, self.player) | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  |         location = Overcooked2Location( | 
					
						
							|  |  |  |             self.player, | 
					
						
							|  |  |  |             location_name, | 
					
						
							|  |  |  |             location_id, | 
					
						
							|  |  |  |             region, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         location.event = is_event | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # if level_id is none, then it's the 6-6 edge case | 
					
						
							|  |  |  |         if level_id is None: | 
					
						
							|  |  |  |             level_id = 36 | 
					
						
							|  |  |  |         if self.level_mapping is not None and level_id in self.level_mapping: | 
					
						
							|  |  |  |             level = self.level_mapping[level_id] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             level = Overcooked2GenericLevel(level_id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         completion_condition: Callable[[CollectionState], bool] = \ | 
					
						
							|  |  |  |             lambda state, level=level, stars=stars: \ | 
					
						
							|  |  |  |             has_requirements_for_level_star(state, level, stars, self.player) | 
					
						
							|  |  |  |         location.access_rule = completion_condition | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         region.locations.append( | 
					
						
							|  |  |  |             location | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_options(self) -> Dict[str, Any]: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         return OC2Options({option.__name__: getattr(self.multiworld, name)[self.player].result | 
					
						
							|  |  |  |                           if issubclass(option, OC2OnToggle) else getattr(self.multiworld, name)[self.player].value | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  |                            for name, option in overcooked_options.items()}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Helper Data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     level_unlock_counts: Dict[int, int]  # level_id, stars to purchase | 
					
						
							|  |  |  |     level_mapping: Dict[int, Overcooked2GenericLevel]  # level_id, level | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Autoworld Hooks | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def generate_early(self): | 
					
						
							|  |  |  |         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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # 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"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Assign new kitchens to each spot on the overworld using pure random chance and nothing else | 
					
						
							|  |  |  |         if self.options["ShuffleLevelOrder"]: | 
					
						
							|  |  |  |             self.level_mapping = \ | 
					
						
							|  |  |  |                 level_shuffle_factory( | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                     self.multiworld.random, | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  |                     self.options["PrepLevels"] != PrepLevelMode.excluded.value, | 
					
						
							|  |  |  |                     self.options["IncludeHordeLevels"], | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.level_mapping = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_regions(self) -> None: | 
					
						
							|  |  |  |         # Menu -> Overworld | 
					
						
							|  |  |  |         self.add_region("Menu") | 
					
						
							|  |  |  |         self.add_region("Overworld") | 
					
						
							|  |  |  |         self.connect_regions("Menu", "Overworld") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for level in Overcooked2Level(): | 
					
						
							|  |  |  |             if not self.options["KevinLevels"] and level.level_id > 36: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Create Region (e.g. "1-1") | 
					
						
							|  |  |  |             self.add_region(level.level_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Add Location to house progression item (1-star) | 
					
						
							|  |  |  |             if level.level_id == 36: | 
					
						
							|  |  |  |                 # 6-6 doesn't have progression, but it does have victory condition which is placed later | 
					
						
							|  |  |  |                 self.add_level_location( | 
					
						
							|  |  |  |                     level.level_name, | 
					
						
							|  |  |  |                     level.location_name_item, | 
					
						
							|  |  |  |                     None, | 
					
						
							|  |  |  |                     1, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 # Location to house progression item | 
					
						
							|  |  |  |                 self.add_level_location( | 
					
						
							|  |  |  |                     level.level_name, | 
					
						
							|  |  |  |                     level.location_name_item, | 
					
						
							|  |  |  |                     level.level_id, | 
					
						
							|  |  |  |                     1, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 # Location to house level completed event | 
					
						
							|  |  |  |                 self.add_level_location( | 
					
						
							|  |  |  |                     level.level_name, | 
					
						
							|  |  |  |                     level.location_name_level_complete, | 
					
						
							|  |  |  |                     level.level_id, | 
					
						
							|  |  |  |                     1, | 
					
						
							|  |  |  |                     is_event=True, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Add Locations to house star aquisition events, except for horde levels | 
					
						
							|  |  |  |             if not self.is_level_horde(level.level_id): | 
					
						
							|  |  |  |                 for n in [1, 2, 3]: | 
					
						
							|  |  |  |                     self.add_level_location( | 
					
						
							|  |  |  |                         level.level_name, | 
					
						
							|  |  |  |                         level.location_name_star_event(n), | 
					
						
							|  |  |  |                         level.level_id, | 
					
						
							|  |  |  |                         n, | 
					
						
							|  |  |  |                         is_event=True, | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Overworld -> Level | 
					
						
							|  |  |  |             required_star_count: int = self.level_unlock_counts[level.level_id] | 
					
						
							|  |  |  |             if level.level_id % 6 != 1 and level.level_id <= 36: | 
					
						
							|  |  |  |                 previous_level_completed_event_name: str = Overcooked2GenericLevel( | 
					
						
							|  |  |  |                     level.level_id - 1).shortname.split(" ")[1] + " Level Complete" | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 previous_level_completed_event_name = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             level_access_rule: Callable[[CollectionState], bool] = \ | 
					
						
							|  |  |  |                 lambda state, level_name=level.level_name, previous_level_completed_event_name=previous_level_completed_event_name, required_star_count=required_star_count: \ | 
					
						
							|  |  |  |                 has_requirements_for_level_access(state, level_name, previous_level_completed_event_name, required_star_count, self.player) | 
					
						
							|  |  |  |             self.connect_regions("Overworld", level.level_name, level_access_rule) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Level --> Overworld | 
					
						
							|  |  |  |             self.connect_regions(level.level_name, "Overworld") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         completion_condition: Callable[[CollectionState], bool] = lambda state: \ | 
					
						
							|  |  |  |             state.has("Victory", self.player) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         self.multiworld.completion_condition[self.player] = completion_condition | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def create_items(self): | 
					
						
							|  |  |  |         self.itempool = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Make Items | 
					
						
							|  |  |  |         # useful = list() | 
					
						
							|  |  |  |         # filler = list() | 
					
						
							|  |  |  |         # progression = list() | 
					
						
							|  |  |  |         for item_name in item_table: | 
					
						
							|  |  |  |             if not self.options["IncludeHordeLevels"] and item_name in ["Calmer Unbread", "Coin Purse"]: | 
					
						
							|  |  |  |                 # skip items which are irrelevant to the seed | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             if not self.options["KevinLevels"] and item_name.startswith("Kevin"): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if is_item_progression(item_name, self.level_mapping, self.options["KevinLevels"]): | 
					
						
							|  |  |  |                 # print(f"{item_name} is progression") | 
					
						
							|  |  |  |                 # progression.append(item_name) | 
					
						
							|  |  |  |                 classification = ItemClassification.progression | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 # print(f"{item_name} is filler") | 
					
						
							|  |  |  |                 if (is_useful(item_name)): | 
					
						
							|  |  |  |                     # useful.append(item_name) | 
					
						
							|  |  |  |                     classification = ItemClassification.useful | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     # filler.append(item_name) | 
					
						
							|  |  |  |                     classification = ItemClassification.filler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if item_name in item_frequencies: | 
					
						
							|  |  |  |                 freq = item_frequencies[item_name] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 while freq > 0: | 
					
						
							|  |  |  |                     self.itempool.append(self.create_item(item_name, classification)) | 
					
						
							|  |  |  |                     classification = ItemClassification.useful  # only the first progressive item can be progression | 
					
						
							|  |  |  |                     freq -= 1 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.itempool.append(self.create_item(item_name, classification)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # print(f"progression: {progression}") | 
					
						
							|  |  |  |         # print(f"useful: {useful}") | 
					
						
							|  |  |  |         # print(f"filler: {filler}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Fill any free space with filler | 
					
						
							|  |  |  |         pool_count = len(oc2_location_name_to_id) | 
					
						
							|  |  |  |         if not self.options["KevinLevels"]: | 
					
						
							|  |  |  |             pool_count -= 8 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         while len(self.itempool) < pool_count: | 
					
						
							|  |  |  |             self.itempool.append(self.create_item("Bonus Star", ItemClassification.useful)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         self.multiworld.itempool += self.itempool | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def set_rules(self): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def generate_basic(self) -> None: | 
					
						
							|  |  |  |         # Add Events (Star Acquisition) | 
					
						
							|  |  |  |         for level in Overcooked2Level(): | 
					
						
							|  |  |  |             if not self.options["KevinLevels"] and level.level_id > 36: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if level.level_id != 36: | 
					
						
							|  |  |  |                 self.place_event(level.location_name_level_complete, level.event_name_level_complete) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if self.is_level_horde(level.level_id): | 
					
						
							|  |  |  |                 continue  # horde levels don't have star rewards | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for n in [1, 2, 3]: | 
					
						
							|  |  |  |                 self.place_event(level.location_name_star_event(n), "Star") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Add Victory Condition | 
					
						
							|  |  |  |         self.place_event("6-6 Completed", "Victory") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Items get distributed to locations | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def fill_json_data(self) -> Dict[str, Any]: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         mod_name = f"AP-{self.multiworld.seed_name}-P{self.player}-{self.multiworld.player_name[self.player]}" | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Serialize Level Order | 
					
						
							|  |  |  |         story_level_order = dict() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self.options["ShuffleLevelOrder"]: | 
					
						
							|  |  |  |             for level_id in self.level_mapping: | 
					
						
							|  |  |  |                 level: Overcooked2GenericLevel = self.level_mapping[level_id] | 
					
						
							|  |  |  |                 story_level_order[str(level_id)] = { | 
					
						
							|  |  |  |                     "DLC": level.dlc.value, | 
					
						
							|  |  |  |                     "LevelID": level.level_id, | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         custom_level_order = dict() | 
					
						
							|  |  |  |         custom_level_order["Story"] = story_level_order | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Serialize Unlock Requirements | 
					
						
							|  |  |  |         level_purchase_requirements = dict() | 
					
						
							|  |  |  |         for level_id in self.level_unlock_counts: | 
					
						
							|  |  |  |             level_purchase_requirements[str(level_id)] = self.level_unlock_counts[level_id] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Override Vanilla Unlock Chain Behavior | 
					
						
							|  |  |  |         # (all worlds accessible from the start and progressible in any order) | 
					
						
							|  |  |  |         level_unlock_requirements = dict() | 
					
						
							|  |  |  |         level_force_reveal = [ | 
					
						
							|  |  |  |             1,   # 1-1 | 
					
						
							|  |  |  |             7,   # 2-1 | 
					
						
							|  |  |  |             13,  # 3-1 | 
					
						
							|  |  |  |             19,  # 4-1 | 
					
						
							|  |  |  |             25,  # 5-1 | 
					
						
							|  |  |  |             31,  # 6-1 | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         for level_id in range(1, 37): | 
					
						
							|  |  |  |             if (level_id not in level_force_reveal): | 
					
						
							|  |  |  |                 level_unlock_requirements[str(level_id)] = level_id - 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Set Kevin Unlock Requirements | 
					
						
							|  |  |  |         if self.options["KevinLevels"]: | 
					
						
							|  |  |  |             def kevin_level_to_keyholder_level_id(level_id: int) -> Optional[int]: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                 location = self.multiworld.find_item(f"Kevin-{level_id-36}", self.player) | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  |                 if location.player != self.player: | 
					
						
							|  |  |  |                     return None  # This kevin level will be unlocked by the server at runtime | 
					
						
							|  |  |  |                 level_id = oc2_location_name_to_id[location.name] | 
					
						
							|  |  |  |                 return level_id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for level_id in range(37, 45): | 
					
						
							|  |  |  |                 keyholder_level_id = kevin_level_to_keyholder_level_id(level_id) | 
					
						
							|  |  |  |                 if keyholder_level_id is not None: | 
					
						
							|  |  |  |                     level_unlock_requirements[str(level_id)] = keyholder_level_id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Place Items at Level Completion Screens (local only) | 
					
						
							|  |  |  |         on_level_completed: Dict[str, list[Dict[str, str]]] = dict() | 
					
						
							| 
									
										
										
										
											2022-11-02 10:11:46 -05:00
										 |  |  |         locations = self.multiworld.get_filled_locations(self.player) | 
					
						
							|  |  |  |         for location in locations: | 
					
						
							|  |  |  |             if location.item.code is None: | 
					
						
							|  |  |  |                 continue  # it's an event | 
					
						
							|  |  |  |             if location.item.player != self.player: | 
					
						
							|  |  |  |                 continue  # not for us | 
					
						
							|  |  |  |             level_id = str(oc2_location_name_to_id[location.name]) | 
					
						
							|  |  |  |             on_level_completed[level_id] = [item_to_unlock_event(location.item.name)] | 
					
						
							| 
									
										
										
										
											2022-10-13 10:57:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Put it all together | 
					
						
							|  |  |  |         star_threshold_scale = self.options["StarThresholdScale"] / 100 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         base_data = { | 
					
						
							|  |  |  |             # Changes Inherent to rando | 
					
						
							|  |  |  |             "DisableAllMods": False, | 
					
						
							|  |  |  |             "UnlockAllChefs": True, | 
					
						
							|  |  |  |             "UnlockAllDLC": True, | 
					
						
							|  |  |  |             "DisplayFPS": True, | 
					
						
							|  |  |  |             "SkipTutorial": True, | 
					
						
							|  |  |  |             "SkipAllOnionKing": True, | 
					
						
							|  |  |  |             "SkipTutorialPopups": True, | 
					
						
							|  |  |  |             "RevealAllLevels": False, | 
					
						
							|  |  |  |             "PurchaseAllLevels": False, | 
					
						
							|  |  |  |             "CheatsEnabled": False, | 
					
						
							|  |  |  |             "ImpossibleTutorial": True, | 
					
						
							|  |  |  |             "ForbidDLC": True, | 
					
						
							|  |  |  |             "ForceSingleSaveSlot": True, | 
					
						
							|  |  |  |             "DisableNGP": True, | 
					
						
							|  |  |  |             "LevelForceReveal": level_force_reveal, | 
					
						
							|  |  |  |             "SaveFolderName": mod_name, | 
					
						
							|  |  |  |             "CustomOrderTimeoutPenalty": 10, | 
					
						
							|  |  |  |             "LevelForceHide": [37, 38, 39, 40, 41, 42, 43, 44], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Game Modifications | 
					
						
							|  |  |  |             "LevelPurchaseRequirements": level_purchase_requirements, | 
					
						
							|  |  |  |             "Custom66TimerScale": max(0.4, (1.0 - star_threshold_scale)), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             "CustomLevelOrder": custom_level_order, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Items (Starting Inventory) | 
					
						
							|  |  |  |             "CustomOrderLifetime": 70.0,  # 100 is original | 
					
						
							|  |  |  |             "DisableWood": True, | 
					
						
							|  |  |  |             "DisableCoal": True, | 
					
						
							|  |  |  |             "DisableOnePlate": True, | 
					
						
							|  |  |  |             "DisableFireExtinguisher": True, | 
					
						
							|  |  |  |             "DisableBellows": True, | 
					
						
							|  |  |  |             "PlatesStartDirty": True, | 
					
						
							|  |  |  |             "MaxTipCombo": 2, | 
					
						
							|  |  |  |             "DisableDash": True, | 
					
						
							|  |  |  |             "WeakDash": True, | 
					
						
							|  |  |  |             "DisableThrow": True, | 
					
						
							|  |  |  |             "DisableCatch": True, | 
					
						
							|  |  |  |             "DisableControlStick": True, | 
					
						
							|  |  |  |             "DisableWokDrag": True, | 
					
						
							|  |  |  |             "DisableRampButton": True, | 
					
						
							|  |  |  |             "WashTimeMultiplier": 1.4, | 
					
						
							|  |  |  |             "BurnSpeedMultiplier": 1.43, | 
					
						
							|  |  |  |             "MaxOrdersOnScreenOffset": -2, | 
					
						
							|  |  |  |             "ChoppingTimeScale": 1.4, | 
					
						
							|  |  |  |             "BackpackMovementScale": 0.75, | 
					
						
							|  |  |  |             "RespawnTime": 10.0, | 
					
						
							|  |  |  |             "CarnivalDispenserRefactoryTime": 4.0, | 
					
						
							|  |  |  |             "LevelUnlockRequirements": level_unlock_requirements, | 
					
						
							|  |  |  |             "LockedEmotes": [0, 1, 2, 3, 4, 5], | 
					
						
							|  |  |  |             "StarOffset": 0, | 
					
						
							|  |  |  |             "AggressiveHorde": True, | 
					
						
							|  |  |  |             "DisableEarnHordeMoney": True, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Item Unlocking | 
					
						
							|  |  |  |             "OnLevelCompleted": on_level_completed, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Set remaining data in the options dict | 
					
						
							|  |  |  |         bugs = ["FixDoubleServing", "FixSinkBug", "FixControlStickThrowBug", "FixEmptyBurnerThrow"] | 
					
						
							|  |  |  |         for bug in bugs: | 
					
						
							|  |  |  |             self.options[bug] = self.options["FixBugs"] | 
					
						
							|  |  |  |         self.options["PreserveCookingProgress"] = self.options["AlwaysPreserveCookingProgress"] | 
					
						
							|  |  |  |         self.options["TimerAlwaysStarts"] = self.options["PrepLevels"] == PrepLevelMode.ayce.value | 
					
						
							|  |  |  |         self.options["LevelTimerScale"] = 0.666 if self.options["ShorterLevelDuration"] else 1.0 | 
					
						
							|  |  |  |         self.options["LeaderboardScoreScale"] = { | 
					
						
							|  |  |  |             "FourStars": 1.0, | 
					
						
							|  |  |  |             "ThreeStars": star_threshold_scale, | 
					
						
							|  |  |  |             "TwoStars": star_threshold_scale * 0.75, | 
					
						
							|  |  |  |             "OneStar": star_threshold_scale * 0.35, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         base_data.update(self.options) | 
					
						
							|  |  |  |         return base_data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def fill_slot_data(self) -> Dict[str, Any]: | 
					
						
							|  |  |  |         return self.fill_json_data() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def level_unlock_requirement_factory(stars_to_win: int) -> Dict[int, int]: | 
					
						
							|  |  |  |     level_unlock_counts = dict() | 
					
						
							|  |  |  |     level = 1 | 
					
						
							|  |  |  |     sublevel = 1 | 
					
						
							|  |  |  |     for n in range(1, 37): | 
					
						
							|  |  |  |         progress: float = float(n)/36.0 | 
					
						
							|  |  |  |         progress *= progress  # x^2 curve | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         star_count = int(progress*float(stars_to_win)) | 
					
						
							|  |  |  |         min = (n-1)*3 | 
					
						
							|  |  |  |         if (star_count > min): | 
					
						
							|  |  |  |             star_count = min | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         level_id = (level-1)*6 + sublevel | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # print("%d-%d (%d) = %d" % (level, sublevel, level_id, star_count)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         level_unlock_counts[level_id] = star_count | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         level += 1 | 
					
						
							|  |  |  |         if level > 6: | 
					
						
							|  |  |  |             level = 1 | 
					
						
							|  |  |  |             sublevel += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # force sphere 1 to 0 stars to help keep our promises to the item fill algo | 
					
						
							|  |  |  |     level_unlock_counts[1] = 0  # 1-1 | 
					
						
							|  |  |  |     level_unlock_counts[7] = 0  # 2-1 | 
					
						
							|  |  |  |     level_unlock_counts[19] = 0  # 4-1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Force 5-1 into sphere 1 to help things out | 
					
						
							|  |  |  |     level_unlock_counts[25] = 1  # 5-1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for n in range(37, 46): | 
					
						
							|  |  |  |         level_unlock_counts[n] = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return level_unlock_counts |