| 
									
										
										
										
											2022-08-30 17:14:34 +02:00
										 |  |  | from typing import TYPE_CHECKING, Dict, Callable, Optional | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | from worlds.generic.Rules import set_rule, add_rule | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | from .Locations import location_table, LocationDict | 
					
						
							| 
									
										
										
										
											2022-08-30 17:14:34 +02:00
										 |  |  | from .Creatures import all_creatures, aggressive, suffix, hatchable, containment | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | from .Options import AggressiveScanLogic, SwimRule | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | import math | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  | if TYPE_CHECKING: | 
					
						
							|  |  |  |     from . import SubnauticaWorld | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  |     from BaseClasses import CollectionState, Location | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_seaglide(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Seaglide Fragment", player, 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_modification_station(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Modification Station Fragment", player, 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_mobile_vehicle_bay(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Mobile Vehicle Bay Fragment", player, 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_moonpool(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Moonpool Fragment", player, 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_vehicle_upgrade_console(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Vehicle Upgrade Console", player) and \ | 
					
						
							|  |  |  |            has_moonpool(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_seamoth(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Seamoth Fragment", player, 3) and \ | 
					
						
							|  |  |  |            has_mobile_vehicle_bay(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_seamoth_depth_module_mk1(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return has_vehicle_upgrade_console(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_seamoth_depth_module_mk2(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return has_seamoth_depth_module_mk1(state, player) and \ | 
					
						
							|  |  |  |            has_modification_station(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_seamoth_depth_module_mk3(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return has_seamoth_depth_module_mk2(state, player) and \ | 
					
						
							|  |  |  |            has_modification_station(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_cyclops_bridge(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Cyclops Bridge Fragment", player, 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_cyclops_engine(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Cyclops Engine Fragment", player, 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_cyclops_hull(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Cyclops Hull Fragment", player, 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_cyclops(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return has_cyclops_bridge(state, player) and \ | 
					
						
							|  |  |  |            has_cyclops_engine(state, player) and \ | 
					
						
							|  |  |  |            has_cyclops_hull(state, player) and \ | 
					
						
							|  |  |  |            has_mobile_vehicle_bay(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_cyclops_depth_module_mk1(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2023-02-16 00:40:19 +01:00
										 |  |  |     # Crafted in the Cyclops, so we don't need to check for crafting station | 
					
						
							|  |  |  |     return state.has("Cyclops Depth Module MK1", player) | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_cyclops_depth_module_mk2(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return has_cyclops_depth_module_mk1(state, player) and \ | 
					
						
							|  |  |  |            has_modification_station(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_cyclops_depth_module_mk3(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return has_cyclops_depth_module_mk2(state, player) and \ | 
					
						
							|  |  |  |            has_modification_station(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_prawn(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Prawn Suit Fragment", player, 4) and \ | 
					
						
							|  |  |  |            has_mobile_vehicle_bay(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_prawn_propulsion_arm(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Prawn Suit Propulsion Cannon Fragment", player, 2) and \ | 
					
						
							|  |  |  |            has_vehicle_upgrade_console(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_prawn_depth_module_mk1(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return has_vehicle_upgrade_console(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_prawn_depth_module_mk2(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return has_prawn_depth_module_mk1(state, player) and \ | 
					
						
							|  |  |  |            has_modification_station(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_laser_cutter(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return state.has("Laser Cutter Fragment", player, 3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_stasis_rifle(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  |     return state.has("Stasis Rifle Fragment", player, 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_containment(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2022-12-17 17:42:02 +01:00
										 |  |  |     return state.has("Alien Containment", player) and has_utility_room(state, player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def has_utility_room(state: "CollectionState", player: int) -> bool: | 
					
						
							|  |  |  |     return state.has("Large Room", player) or state.has("Multipurpose Room", player) | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | # Either we have propulsion cannon, or prawn + propulsion cannon arm | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_propulsion_cannon(state: "CollectionState", player: int) -> bool: | 
					
						
							|  |  |  |     return state.has("Propulsion Cannon Fragment", player, 2) | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-05 23:07:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_cyclops_shield(state: "CollectionState", player: int) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return has_cyclops(state, player) and \ | 
					
						
							|  |  |  |            state.has("Cyclops Shield Generator", player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | def has_ultra_high_capacity_tank(state: "CollectionState", player: int) -> bool: | 
					
						
							|  |  |  |     return has_modification_station(state, player) and state.has("Ultra High Capacity Tank", player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def has_lightweight_high_capacity_tank(state: "CollectionState", player: int) -> bool: | 
					
						
							|  |  |  |     return has_modification_station(state, player) and state.has("Lightweight High Capacity Tank", player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def has_ultra_glide_fins(state: "CollectionState", player: int) -> bool: | 
					
						
							|  |  |  |     return has_modification_station(state, player) and state.has("Ultra Glide Fins", player) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | # Swim depth rules: | 
					
						
							|  |  |  | # Rebreather, high capacity tank and fins are available from the start. | 
					
						
							|  |  |  | # All tests for those were done without inventory for light weight. | 
					
						
							|  |  |  | # Fins and ultra Fins are better than charge fins, so we ignore charge fins. | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | # swim speeds: https://subnautica.fandom.com/wiki/Swimming_Speed | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_max_swim_depth(state: "CollectionState", player: int) -> int: | 
					
						
							|  |  |  |     swim_rule: SwimRule = state.multiworld.swim_rule[player] | 
					
						
							|  |  |  |     depth: int = swim_rule.base_depth | 
					
						
							| 
									
										
										
										
											2023-01-29 22:12:39 +01:00
										 |  |  |     if swim_rule.consider_items: | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  |         if has_seaglide(state, player): | 
					
						
							|  |  |  |             if has_ultra_high_capacity_tank(state, player): | 
					
						
							|  |  |  |                 depth += 350  # It's about 800m. Give some room | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 depth += 200  # It's about 650m. Give some room | 
					
						
							|  |  |  |         # seaglide and fins cannot be used together | 
					
						
							|  |  |  |         elif has_ultra_glide_fins(state, player): | 
					
						
							|  |  |  |             if has_ultra_high_capacity_tank(state, player): | 
					
						
							|  |  |  |                 depth += 150 | 
					
						
							|  |  |  |             elif has_lightweight_high_capacity_tank(state, player): | 
					
						
							|  |  |  |                 depth += 75 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 depth += 50 | 
					
						
							|  |  |  |         elif has_ultra_high_capacity_tank(state, player): | 
					
						
							|  |  |  |             depth += 100 | 
					
						
							|  |  |  |         elif has_lightweight_high_capacity_tank(state, player): | 
					
						
							|  |  |  |             depth += 25 | 
					
						
							|  |  |  |     return depth | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | def get_seamoth_max_depth(state: "CollectionState", player: int): | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     if has_seamoth(state, player): | 
					
						
							|  |  |  |         if has_seamoth_depth_module_mk3(state, player): | 
					
						
							|  |  |  |             return 900 | 
					
						
							| 
									
										
										
										
											2021-10-05 23:07:03 +02:00
										 |  |  |         elif has_seamoth_depth_module_mk2(state, player):  # Will never be the case, 3 is craftable | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |             return 500 | 
					
						
							|  |  |  |         elif has_seamoth_depth_module_mk1(state, player): | 
					
						
							|  |  |  |             return 300 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return 200 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | def get_cyclops_max_depth(state: "CollectionState", player): | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     if has_cyclops(state, player): | 
					
						
							|  |  |  |         if has_cyclops_depth_module_mk3(state, player): | 
					
						
							|  |  |  |             return 1700 | 
					
						
							| 
									
										
										
										
											2021-10-05 23:07:03 +02:00
										 |  |  |         elif has_cyclops_depth_module_mk2(state, player):  # Will never be the case, 3 is craftable | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |             return 1300 | 
					
						
							|  |  |  |         elif has_cyclops_depth_module_mk1(state, player): | 
					
						
							|  |  |  |             return 900 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return 500 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | def get_prawn_max_depth(state: "CollectionState", player): | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     if has_prawn(state, player): | 
					
						
							|  |  |  |         if has_prawn_depth_module_mk2(state, player): | 
					
						
							|  |  |  |             return 1700 | 
					
						
							|  |  |  |         elif has_prawn_depth_module_mk1(state, player): | 
					
						
							|  |  |  |             return 1300 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return 900 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | def get_max_depth(state: "CollectionState", player: int): | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  |     return get_max_swim_depth(state, player) + max( | 
					
						
							|  |  |  |         get_seamoth_max_depth(state, player), | 
					
						
							|  |  |  |         get_cyclops_max_depth(state, player), | 
					
						
							|  |  |  |         get_prawn_max_depth(state, player) | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | def can_access_location(state: "CollectionState", player: int, loc: LocationDict) -> bool: | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     need_laser_cutter = loc.get("need_laser_cutter", False) | 
					
						
							|  |  |  |     if need_laser_cutter and not has_laser_cutter(state, player): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |     need_propulsion_cannon = loc.get("need_propulsion_cannon", False) | 
					
						
							|  |  |  |     if need_propulsion_cannon and not has_propulsion_cannon(state, player): | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |     pos = loc["position"] | 
					
						
							|  |  |  |     pos_x = pos["x"] | 
					
						
							|  |  |  |     pos_y = pos["y"] | 
					
						
							|  |  |  |     pos_z = pos["z"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     aurora_dist = math.sqrt((pos_x - 1038.0) ** 2 + (pos_y - -3.4) ** 2 + (pos_z - -163.1) ** 2) | 
					
						
							|  |  |  |     need_radiation_suit = aurora_dist < 950 | 
					
						
							|  |  |  |     if need_radiation_suit and not state.has("Radiation Suit", player): | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Seaglide doesn't unlock anything specific, but just allows for faster movement.  | 
					
						
							|  |  |  |     # Otherwise the game is painfully slow. | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |     map_center_dist = math.sqrt(pos_x ** 2 + pos_z ** 2) | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     if (map_center_dist > 800 or pos_y < -200) and not has_seaglide(state, player): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |     depth = -pos_y  # y-up | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     return get_max_depth(state, player) >= depth | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  | def set_location_rule(world, player: int, loc: LocationDict): | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |     set_rule(world.get_location(loc["name"], player), lambda state: can_access_location(state, player, loc)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | def can_scan_creature(state: "CollectionState", player: int, creature: str) -> bool: | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  |     if not has_seaglide(state, player): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     return get_max_depth(state, player) >= all_creatures[creature] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | def set_creature_rule(world, player: int, creature_name: str) -> "Location": | 
					
						
							|  |  |  |     location = world.get_location(creature_name + suffix, player) | 
					
						
							|  |  |  |     set_rule(location, | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  |              lambda state: can_scan_creature(state, player, creature_name)) | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  |     return location | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-30 17:14:34 +02:00
										 |  |  | def get_aggression_rule(option: AggressiveScanLogic, creature_name: str) -> \ | 
					
						
							|  |  |  |         Optional[Callable[["CollectionState", int], bool]]: | 
					
						
							|  |  |  |     """Get logic rule for a creature scan location.""" | 
					
						
							|  |  |  |     if creature_name not in hatchable and option != option.option_none:  # can only be done via stasis | 
					
						
							|  |  |  |         return has_stasis_rifle | 
					
						
							|  |  |  |     # otherwise allow option preference | 
					
						
							|  |  |  |     return aggression_rules.get(option.value, None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  | aggression_rules: Dict[int, Callable[["CollectionState", int], bool]] = { | 
					
						
							|  |  |  |     AggressiveScanLogic.option_stasis: has_stasis_rifle, | 
					
						
							|  |  |  |     AggressiveScanLogic.option_containment: has_containment, | 
					
						
							|  |  |  |     AggressiveScanLogic.option_either: lambda state, player: | 
					
						
							|  |  |  |     has_stasis_rifle(state, player) or has_containment(state, player) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def set_rules(subnautica_world: "SubnauticaWorld"): | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |     player = subnautica_world.player | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     world = subnautica_world.multiworld | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for loc in location_table.values(): | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  |         set_location_rule(world, player, loc) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  |     if subnautica_world.creatures_to_scan: | 
					
						
							| 
									
										
										
										
											2022-08-30 17:14:34 +02:00
										 |  |  |         option = world.creature_scan_logic[player] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-22 23:35:41 +02:00
										 |  |  |         for creature_name in subnautica_world.creatures_to_scan: | 
					
						
							|  |  |  |             location = set_creature_rule(world, player, creature_name) | 
					
						
							| 
									
										
										
										
											2022-08-30 17:14:34 +02:00
										 |  |  |             if creature_name in containment:  # there is no other way, hard-required containment | 
					
						
							|  |  |  |                 add_rule(location, lambda state: has_containment(state, player)) | 
					
						
							|  |  |  |             elif creature_name in aggressive: | 
					
						
							|  |  |  |                 rule = get_aggression_rule(option, creature_name) | 
					
						
							|  |  |  |                 if rule: | 
					
						
							|  |  |  |                     add_rule(location, | 
					
						
							|  |  |  |                              lambda state, loc_rule=get_aggression_rule(option, creature_name): loc_rule(state, player)) | 
					
						
							| 
									
										
										
										
											2022-07-16 16:45:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |     # Victory locations | 
					
						
							| 
									
										
										
										
											2022-08-30 17:14:34 +02:00
										 |  |  |     set_rule(world.get_location("Neptune Launch", player), | 
					
						
							|  |  |  |              lambda state: | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  |              get_max_depth(state, player) >= 1444 and | 
					
						
							|  |  |  |              has_mobile_vehicle_bay(state, player) and | 
					
						
							|  |  |  |              state.has("Neptune Launch Platform", player) and | 
					
						
							|  |  |  |              state.has("Neptune Gantry", player) and | 
					
						
							|  |  |  |              state.has("Neptune Boosters", player) and | 
					
						
							|  |  |  |              state.has("Neptune Fuel Reserve", player) and | 
					
						
							|  |  |  |              state.has("Neptune Cockpit", player) and | 
					
						
							|  |  |  |              state.has("Ion Power Cell", player) and | 
					
						
							|  |  |  |              state.has("Ion Battery", player) and | 
					
						
							|  |  |  |              has_cyclops_shield(state, player)) | 
					
						
							| 
									
										
										
										
											2021-07-17 12:07:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  |     set_rule(world.get_location("Disable Quarantine", player), lambda state: | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  |     get_max_depth(state, player) >= 1444) | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     set_rule(world.get_location("Full Infection", player), lambda state: | 
					
						
							| 
									
										
										
										
											2022-11-28 07:43:04 +01:00
										 |  |  |     get_max_depth(state, player) >= 900) | 
					
						
							| 
									
										
										
										
											2022-07-15 17:41:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     room = world.get_location("Aurora Drive Room - Upgrade Console", player) | 
					
						
							|  |  |  |     set_rule(world.get_location("Repair Aurora Drive", player), lambda state: room.can_reach(state)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     world.completion_condition[player] = lambda state: state.has("Victory", player) |