Subnautica: add Options to Creature Scans (#950)

This commit is contained in:
Fabian Dill
2022-08-22 23:35:41 +02:00
committed by GitHub
parent 11cbc0b40b
commit c695f91198
5 changed files with 109 additions and 51 deletions

View File

@@ -1,122 +1,128 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Dict, Callable
from worlds.generic.Rules import set_rule
from worlds.generic.Rules import set_rule, add_rule
from .Locations import location_table, LocationDict
from .Creatures import all_creatures, aggressive, suffix
from .Options import AggressiveScanLogic
import math
if TYPE_CHECKING:
from . import SubnauticaWorld
from BaseClasses import CollectionState, Location
def has_seaglide(state, player: int):
def has_seaglide(state: "CollectionState", player: int):
return state.has("Seaglide Fragment", player, 2)
def has_modification_station(state, player: int):
def has_modification_station(state: "CollectionState", player: int):
return state.has("Modification Station Fragment", player, 3)
def has_mobile_vehicle_bay(state, player: int):
def has_mobile_vehicle_bay(state: "CollectionState", player: int):
return state.has("Mobile Vehicle Bay Fragment", player, 3)
def has_moonpool(state, player: int):
def has_moonpool(state: "CollectionState", player: int):
return state.has("Moonpool Fragment", player, 2)
def has_vehicle_upgrade_console(state, player: int):
def has_vehicle_upgrade_console(state: "CollectionState", player: int):
return state.has("Vehicle Upgrade Console", player) and \
has_moonpool(state, player)
def has_seamoth(state, player: int):
def has_seamoth(state: "CollectionState", player: int):
return state.has("Seamoth Fragment", player, 3) and \
has_mobile_vehicle_bay(state, player)
def has_seamoth_depth_module_mk1(state, player: int):
def has_seamoth_depth_module_mk1(state: "CollectionState", player: int):
return has_vehicle_upgrade_console(state, player)
def has_seamoth_depth_module_mk2(state, player: int):
def has_seamoth_depth_module_mk2(state: "CollectionState", player: int):
return has_seamoth_depth_module_mk1(state, player) and \
has_modification_station(state, player)
def has_seamoth_depth_module_mk3(state, player: int):
def has_seamoth_depth_module_mk3(state: "CollectionState", player: int):
return has_seamoth_depth_module_mk2(state, player) and \
has_modification_station(state, player)
def has_cyclops_bridge(state, player: int):
def has_cyclops_bridge(state: "CollectionState", player: int):
return state.has("Cyclops Bridge Fragment", player, 3)
def has_cyclops_engine(state, player: int):
def has_cyclops_engine(state: "CollectionState", player: int):
return state.has("Cyclops Engine Fragment", player, 3)
def has_cyclops_hull(state, player: int):
def has_cyclops_hull(state: "CollectionState", player: int):
return state.has("Cyclops Hull Fragment", player, 3)
def has_cyclops(state, player: int):
def has_cyclops(state: "CollectionState", player: int):
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)
def has_cyclops_depth_module_mk1(state, player: int):
def has_cyclops_depth_module_mk1(state: "CollectionState", player: int):
return state.has("Cyclops Depth Module MK1", player) and \
has_modification_station(state, player)
def has_cyclops_depth_module_mk2(state, player: int):
def has_cyclops_depth_module_mk2(state: "CollectionState", player: int):
return has_cyclops_depth_module_mk1(state, player) and \
has_modification_station(state, player)
def has_cyclops_depth_module_mk3(state, player: int):
def has_cyclops_depth_module_mk3(state: "CollectionState", player: int):
return has_cyclops_depth_module_mk2(state, player) and \
has_modification_station(state, player)
def has_prawn(state, player: int):
def has_prawn(state: "CollectionState", player: int):
return state.has("Prawn Suit Fragment", player, 4) and \
has_mobile_vehicle_bay(state, player)
def has_praw_propulsion_arm(state, player: int):
def has_prawn_propulsion_arm(state: "CollectionState", player: int):
return state.has("Prawn Suit Propulsion Cannon Fragment", player, 2) and \
has_vehicle_upgrade_console(state, player)
def has_prawn_depth_module_mk1(state, player: int):
def has_prawn_depth_module_mk1(state: "CollectionState", player: int):
return has_vehicle_upgrade_console(state, player)
def has_prawn_depth_module_mk2(state, player: int):
def has_prawn_depth_module_mk2(state: "CollectionState", player: int):
return has_prawn_depth_module_mk1(state, player) and \
has_modification_station(state, player)
def has_laser_cutter(state, player: int):
def has_laser_cutter(state: "CollectionState", player: int):
return state.has("Laser Cutter Fragment", player, 3)
def has_stasis_rile(state, player: int):
def has_stasis_rifle(state: "CollectionState", player: int):
return state.has("Stasis Rifle Fragment", player, 2)
def has_containment(state: "CollectionState", player: int):
return state.has("Alien Containment Fragment", player, 2) and state.has("Multipurpose Room", player)
# Either we have propulsion cannon, or prawn + propulsion cannon arm
def has_propulsion_cannon(state, player: int):
def has_propulsion_cannon(state: "CollectionState", player: int):
return state.has("Propulsion Cannon Fragment", player, 2) or \
(has_prawn(state, player) and has_praw_propulsion_arm(state, player))
(has_prawn(state, player) and has_prawn_propulsion_arm(state, player))
def has_cyclops_shield(state, player: int):
def has_cyclops_shield(state: "CollectionState", player: int):
return has_cyclops(state, player) and \
state.has("Cyclops Shield Generator", player)
@@ -129,7 +135,7 @@ def has_cyclops_shield(state, player: int):
# negligeable with from high capacity tank. 430m -> 460m
# Fins are not used when using seaglide
#
def get_max_swim_depth(state, player: int):
def get_max_swim_depth(state: "CollectionState", player: int):
# TODO, Make this a difficulty setting.
# Only go up to 200m without any submarines for now.
return 200
@@ -140,7 +146,7 @@ def get_max_swim_depth(state, player: int):
# has_ultra_glide_fins = state.has("Ultra Glide Fins", player)
# max_depth = 400 # More like 430m. Give some room
# if has_seaglide(state, player: int):
# if has_seaglide(state: "CollectionState", player: int):
# if has_ultra_high_capacity_tank:
# max_depth = 750 # It's about 50m more. Give some room
# else:
@@ -156,7 +162,7 @@ def get_max_swim_depth(state, player: int):
# return max_depth
def get_seamoth_max_depth(state, player: int):
def get_seamoth_max_depth(state: "CollectionState", player: int):
if has_seamoth(state, player):
if has_seamoth_depth_module_mk3(state, player):
return 900
@@ -170,7 +176,7 @@ def get_seamoth_max_depth(state, player: int):
return 0
def get_cyclops_max_depth(state, player):
def get_cyclops_max_depth(state: "CollectionState", player):
if has_cyclops(state, player):
if has_cyclops_depth_module_mk3(state, player):
return 1700
@@ -184,7 +190,7 @@ def get_cyclops_max_depth(state, player):
return 0
def get_prawn_max_depth(state, player):
def get_prawn_max_depth(state: "CollectionState", player):
if has_prawn(state, player):
if has_prawn_depth_module_mk2(state, player):
return 1700
@@ -196,7 +202,7 @@ def get_prawn_max_depth(state, player):
return 0
def get_max_depth(state, player: int):
def get_max_depth(state: "CollectionState", player: int):
# TODO, Difficulty option, we can add vehicle depth + swim depth
# But at this point, we have to consider traver distance in caves, not
# just depth
@@ -206,7 +212,7 @@ def get_max_depth(state, player: int):
get_prawn_max_depth(state, player))
def can_access_location(state, player: int, loc: LocationDict) -> bool:
def can_access_location(state: "CollectionState", player: int, loc: LocationDict) -> bool:
need_laser_cutter = loc.get("need_laser_cutter", False)
if need_laser_cutter and not has_laser_cutter(state, player):
return False
@@ -239,17 +245,25 @@ def set_location_rule(world, player: int, loc: LocationDict):
set_rule(world.get_location(loc["name"], player), lambda state: can_access_location(state, player, loc))
def can_scan_creature(state, player: int, creature: str) -> bool:
def can_scan_creature(state: "CollectionState", player: int, creature: str) -> bool:
if not has_seaglide(state, player):
return False
if creature in aggressive and not has_stasis_rile(state, player):
return False
return get_max_depth(state, player) >= all_creatures[creature]
def set_creature_rule(world, player, creature_name: str):
set_rule(world.get_location(creature_name + suffix, player),
def set_creature_rule(world, player: int, creature_name: str) -> "Location":
location = world.get_location(creature_name + suffix, player)
set_rule(location,
lambda state: can_scan_creature(state, player, creature_name))
return location
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)
}
def set_rules(subnautica_world: "SubnauticaWorld"):
@@ -259,8 +273,12 @@ def set_rules(subnautica_world: "SubnauticaWorld"):
for loc in location_table.values():
set_location_rule(world, player, loc)
for creature_name in subnautica_world.creatures_to_scan:
set_creature_rule(world, player, creature_name)
if subnautica_world.creatures_to_scan:
aggressive_rule = aggression_rules.get(world.creature_scan_logic[player], None)
for creature_name in subnautica_world.creatures_to_scan:
location = set_creature_rule(world, player, creature_name)
if creature_name in aggressive and aggressive_rule:
add_rule(location, lambda state: aggressive_rule(state, player))
# Victory locations
set_rule(world.get_location("Neptune Launch", player), lambda state: