| 
									
										
										
										
											2023-03-23 21:21:11 +01:00
										 |  |  | from __future__ import annotations | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | import logging | 
					
						
							| 
									
										
										
										
											2023-03-23 21:21:11 +01:00
										 |  |  | import itertools | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  | from typing import List, Dict, Any, cast | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-14 20:36:25 +02:00
										 |  |  | from BaseClasses import Region, Location, Item, Tutorial, ItemClassification | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | from worlds.AutoWorld import World, WebWorld | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  | from . import items | 
					
						
							|  |  |  | from . import locations | 
					
						
							|  |  |  | from . import creatures | 
					
						
							|  |  |  | from . import options | 
					
						
							|  |  |  | from .items import item_table, group_items, items_by_type, ItemType | 
					
						
							|  |  |  | from .rules import set_rules | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | logger = logging.getLogger("Subnautica") | 
					
						
							| 
									
										
										
										
											2022-05-11 13:05:53 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SubnaticaWeb(WebWorld): | 
					
						
							|  |  |  |     tutorials = [Tutorial( | 
					
						
							|  |  |  |         "Multiworld Setup Guide", | 
					
						
							|  |  |  |         "A guide to setting up the Subnautica randomizer connected to an Archipelago Multiworld", | 
					
						
							|  |  |  |         "English", | 
					
						
							|  |  |  |         "setup_en.md", | 
					
						
							|  |  |  |         "setup/en", | 
					
						
							|  |  |  |         ["Berserker"] | 
					
						
							|  |  |  |     )] | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  | all_locations = {data["name"]: loc_id for loc_id, data in locations.location_table.items()} | 
					
						
							|  |  |  | all_locations.update(creatures.creature_locations) | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | class SubnauticaWorld(World): | 
					
						
							| 
									
										
										
										
											2021-08-31 17:28:46 -04:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Subnautica is an undersea exploration game. Stranded on an alien world, you become infected by | 
					
						
							|  |  |  |     an unknown bacteria. The planet's automatic quarantine will shoot you down if you try to leave. | 
					
						
							|  |  |  |     You must find a cure for yourself, build an escape rocket, and leave the planet. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-02-16 00:40:19 +01:00
										 |  |  |     game = "Subnautica" | 
					
						
							| 
									
										
										
										
											2022-05-11 13:05:53 -05:00
										 |  |  |     web = SubnaticaWeb() | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |     item_name_to_id = {data.name: item_id for item_id, data in items.item_table.items()} | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  |     location_name_to_id = all_locations | 
					
						
							| 
									
										
										
										
											2024-04-14 20:36:25 +02:00
										 |  |  |     options_dataclass = options.SubnauticaOptions | 
					
						
							|  |  |  |     options: options.SubnauticaOptions | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |     data_version = 10 | 
					
						
							|  |  |  |     required_client_version = (0, 4, 1) | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  |     creatures_to_scan: List[str] | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def generate_early(self) -> None: | 
					
						
							| 
									
										
										
										
											2024-04-14 20:36:25 +02:00
										 |  |  |         if not self.options.filler_items_distribution.weights_pair[1][-1]: | 
					
						
							|  |  |  |             raise Exception("Filler Items Distribution needs at least one positive weight.") | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |         if self.options.early_seaglide: | 
					
						
							| 
									
										
										
										
											2022-11-28 07:03:09 +01:00
										 |  |  |             self.multiworld.local_early_items[self.player]["Seaglide Fragment"] = 2 | 
					
						
							| 
									
										
										
										
											2022-10-27 03:00:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |         scan_option: options.AggressiveScanLogic = self.options.creature_scan_logic | 
					
						
							| 
									
										
										
										
											2022-08-30 17:14:34 +02:00
										 |  |  |         creature_pool = scan_option.get_pool() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |         self.options.creature_scans.value = min( | 
					
						
							| 
									
										
										
										
											2022-08-30 17:14:34 +02:00
										 |  |  |             len(creature_pool), | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |             self.options.creature_scans.value | 
					
						
							| 
									
										
										
										
											2022-08-30 17:14:34 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |         self.creatures_to_scan = self.random.sample( | 
					
						
							|  |  |  |             creature_pool, self.options.creature_scans.value) | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def create_regions(self): | 
					
						
							| 
									
										
										
										
											2023-10-26 00:03:14 +02:00
										 |  |  |         # Create Regions | 
					
						
							|  |  |  |         menu_region = Region("Menu", self.player, self.multiworld) | 
					
						
							|  |  |  |         planet_region = Region("Planet 4546B", self.player, self.multiworld) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Link regions together | 
					
						
							|  |  |  |         menu_region.connect(planet_region, "Lifepod 5") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Create regular locations | 
					
						
							|  |  |  |         location_names = itertools.chain((location["name"] for location in locations.location_table.values()), | 
					
						
							|  |  |  |                                          (creature + creatures.suffix for creature in self.creatures_to_scan)) | 
					
						
							|  |  |  |         for location_name in location_names: | 
					
						
							|  |  |  |             loc_id = self.location_name_to_id[location_name] | 
					
						
							|  |  |  |             location = SubnauticaLocation(self.player, location_name, loc_id, planet_region) | 
					
						
							|  |  |  |             planet_region.locations.append(location) | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-26 00:03:14 +02:00
										 |  |  |         # Create events | 
					
						
							|  |  |  |         goal_event_name = self.options.goal.get_event_name() | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |         for event in locations.events: | 
					
						
							| 
									
										
										
										
											2023-10-26 00:03:14 +02:00
										 |  |  |             location = SubnauticaLocation(self.player, event, None, planet_region) | 
					
						
							|  |  |  |             planet_region.locations.append(location) | 
					
						
							|  |  |  |             location.place_locked_item( | 
					
						
							| 
									
										
										
										
											2023-02-16 20:22:14 +01:00
										 |  |  |                 SubnauticaItem(event, ItemClassification.progression, None, player=self.player)) | 
					
						
							| 
									
										
										
										
											2023-10-26 00:03:14 +02:00
										 |  |  |             if event == goal_event_name: | 
					
						
							|  |  |  |                 # make the goal event the victory "item" | 
					
						
							|  |  |  |                 location.item.name = "Victory" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Register regions to multiworld | 
					
						
							|  |  |  |         self.multiworld.regions += [ | 
					
						
							|  |  |  |             menu_region, | 
					
						
							|  |  |  |             planet_region | 
					
						
							|  |  |  |         ] | 
					
						
							| 
									
										
										
										
											2023-02-16 20:22:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-14 20:36:25 +02:00
										 |  |  |     # refer to rules.py | 
					
						
							| 
									
										
										
										
											2023-02-16 20:22:14 +01:00
										 |  |  |     set_rules = set_rules | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def create_items(self): | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |         # Generate item pool | 
					
						
							| 
									
										
										
										
											2023-03-23 21:21:11 +01:00
										 |  |  |         pool: List[SubnauticaItem] = [] | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |         extras = self.options.creature_scans.value | 
					
						
							| 
									
										
										
										
											2023-03-23 21:21:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         grouped = set(itertools.chain.from_iterable(group_items.values())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for item_id, item in item_table.items(): | 
					
						
							|  |  |  |             if item_id in grouped: | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |                 extras += item.count | 
					
						
							| 
									
										
										
										
											2023-03-23 21:21:11 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |                 for i in range(item.count): | 
					
						
							|  |  |  |                     subnautica_item = self.create_item(item.name) | 
					
						
							|  |  |  |                     if item.name == "Neptune Launch Platform": | 
					
						
							| 
									
										
										
										
											2024-03-03 06:33:48 +01:00
										 |  |  |                         self.get_location("Aurora - Captain Data Terminal").place_locked_item( | 
					
						
							| 
									
										
										
										
											2023-03-23 21:21:11 +01:00
										 |  |  |                             subnautica_item) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         pool.append(subnautica_item) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |         group_amount: int = 2 | 
					
						
							| 
									
										
										
										
											2023-03-23 21:21:11 +01:00
										 |  |  |         assert len(group_items) * group_amount <= extras | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |         for item_id in group_items: | 
					
						
							|  |  |  |             name = item_table[item_id].name | 
					
						
							| 
									
										
										
										
											2023-03-23 21:21:11 +01:00
										 |  |  |             for _ in range(group_amount): | 
					
						
							|  |  |  |                 pool.append(self.create_item(name)) | 
					
						
							|  |  |  |             extras -= group_amount | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-03 06:33:48 +01:00
										 |  |  |         for item_name in self.random.sample( | 
					
						
							| 
									
										
										
										
											2024-04-14 20:36:25 +02:00
										 |  |  |                 # list of high-count important fragments as priority filler | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |                 [ | 
					
						
							|  |  |  |                     "Cyclops Engine Fragment", | 
					
						
							|  |  |  |                     "Cyclops Hull Fragment", | 
					
						
							|  |  |  |                     "Cyclops Bridge Fragment", | 
					
						
							| 
									
										
										
										
											2023-09-15 19:28:55 +02:00
										 |  |  |                     "Seamoth Fragment", | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |                     "Prawn Suit Fragment", | 
					
						
							| 
									
										
										
										
											2023-09-15 19:28:55 +02:00
										 |  |  |                     "Mobile Vehicle Bay Fragment", | 
					
						
							|  |  |  |                     "Modification Station Fragment", | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |                     "Moonpool Fragment", | 
					
						
							| 
									
										
										
										
											2023-09-15 19:28:55 +02:00
										 |  |  |                     "Laser Cutter Fragment", | 
					
						
							| 
									
										
										
										
											2024-04-14 20:36:25 +02:00
										 |  |  |                 ], | 
					
						
							| 
									
										
										
										
											2023-09-15 19:28:55 +02:00
										 |  |  |                 k=min(extras, 9)): | 
					
						
							| 
									
										
										
										
											2021-10-05 23:07:03 +02:00
										 |  |  |             item = self.create_item(item_name) | 
					
						
							|  |  |  |             pool.append(item) | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |             extras -= 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # resource bundle filler | 
					
						
							|  |  |  |         for _ in range(extras): | 
					
						
							|  |  |  |             item = self.create_filler() | 
					
						
							|  |  |  |             item = cast(SubnauticaItem, item) | 
					
						
							|  |  |  |             pool.append(item) | 
					
						
							| 
									
										
										
										
											2021-09-17 04:32:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         self.multiworld.itempool += pool | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |     def fill_slot_data(self) -> Dict[str, Any]: | 
					
						
							|  |  |  |         vanilla_tech: List[str] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         slot_data: Dict[str, Any] = { | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |             "goal": self.options.goal.current_key, | 
					
						
							|  |  |  |             "swim_rule": self.options.swim_rule.current_key, | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |             "vanilla_tech": vanilla_tech, | 
					
						
							| 
									
										
										
										
											2022-07-21 15:39:34 +02:00
										 |  |  |             "creatures_to_scan": self.creatures_to_scan, | 
					
						
							| 
									
										
										
										
											2023-10-15 04:51:52 +02:00
										 |  |  |             "death_link": self.options.death_link.value, | 
					
						
							|  |  |  |             "free_samples": self.options.free_samples.value, | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return slot_data | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 21:21:11 +01:00
										 |  |  |     def create_item(self, name: str) -> SubnauticaItem: | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |         item_id: int = self.item_name_to_id[name] | 
					
						
							| 
									
										
										
										
											2022-06-17 03:23:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |         return SubnauticaItem(name, | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |                               item_table[item_id].classification, | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |                               item_id, player=self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  |     def get_filler_item_name(self) -> str: | 
					
						
							| 
									
										
										
										
											2024-04-14 20:36:25 +02:00
										 |  |  |         item_names, cum_item_weights = self.options.filler_items_distribution.weights_pair | 
					
						
							|  |  |  |         return self.random.choices(item_names, | 
					
						
							|  |  |  |                                    cum_weights=cum_item_weights, | 
					
						
							|  |  |  |                                    k=1)[0] | 
					
						
							| 
									
										
										
										
											2023-05-25 22:57:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | class SubnauticaLocation(Location): | 
					
						
							|  |  |  |     game: str = "Subnautica" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SubnauticaItem(Item): | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |     game: str = "Subnautica" |