Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subnautica: ability to remove vehicles either from logic or entirely #3437

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 94 additions & 17 deletions worlds/subnautica/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
from . import locations
from . import creatures
from . import options
from .items import item_table, group_items, items_by_type, ItemType
from .items import (
item_table, group_items, items_by_type, ItemType,
base_item_table, non_vehicle_depth_table,
seamoth_table, prawn_table, cyclops_table,
)
from .rules import set_rules

logger = logging.getLogger("Subnautica")
Expand Down Expand Up @@ -102,25 +106,74 @@ def create_regions(self):
# refer to rules.py
set_rules = set_rules

def get_theoretical_swim_depth(self):
depth: int = self.options.swim_rule.base_depth

if self.options.swim_rule.consider_items:
return depth + 350

return depth

def create_items(self):
# Generate item pool
pool: List[SubnauticaItem] = []
extras = self.options.creature_scans.value

grouped = set(itertools.chain.from_iterable(group_items.values()))

for item_id, item in item_table.items():
for item_id, item in base_item_table.items():
if item_id in grouped:
extras += item.count
else:
for i in range(item.count):
for _ in range(item.count):
subnautica_item = self.create_item(item.name)
if item.name == "Neptune Launch Platform":
self.get_location("Aurora - Captain Data Terminal").place_locked_item(
subnautica_item)
elif item.name == "Cyclops Shield Generator":
if self.options.include_cyclops.value == 2 \
and self.options.goal.get_event_name() != "Neptune Launch":
extras += 1
else:
pool.append(subnautica_item)
else:
pool.append(subnautica_item)

for item_id, item in seamoth_table.items():
if self.options.include_seamoth.value < 2:
for _ in range(item.count):
pool.append(self.create_item(item.name))
else:
extras += item.count

for item_id, item in prawn_table.items():
if self.options.include_prawn.value < 2:
for _ in range(item.count):
pool.append(self.create_item(item.name))
else:
extras += item.count

for item_id, item in cyclops_table.items():
if self.options.include_cyclops.value < 2:
for _ in range(item.count):
pool.append(self.create_item(item.name))
else:
extras += item.count

# If we can't make the necessary depth by traditional (vehicle) means, use the alternates
# Shift the items to progression as part of that change
seamoth_can_make_it: bool = False
if self.options.include_seamoth.value == 0 and self.get_theoretical_swim_depth() + 900 > 1443:
seamoth_can_make_it = True

for item_id, item in non_vehicle_depth_table.items():
for _ in range(item.count):
if seamoth_can_make_it is False and self.options.include_prawn.value > 0 and \
self.options.include_cyclops.value > 0:
pool.append(self.create_shifted_item(item.name, ItemClassification.progression))
else:
pool.append(self.create_item(item.name))

group_amount: int = 2
assert len(group_items) * group_amount <= extras
for item_id in group_items:
Expand All @@ -129,20 +182,37 @@ def create_items(self):
pool.append(self.create_item(name))
extras -= group_amount

for item_name in self.random.sample(
# list of high-count important fragments as priority filler
[
"Cyclops Engine Fragment",
"Cyclops Hull Fragment",
"Cyclops Bridge Fragment",
"Seamoth Fragment",
"Prawn Suit Fragment",
"Mobile Vehicle Bay Fragment",
"Modification Station Fragment",
"Moonpool Fragment",
"Laser Cutter Fragment",
],
k=min(extras, 9)):
# list of high-count important fragments as priority filler
num = 2
priority_filler: List[str] = [
"Modification Station Fragment",
"Laser Cutter Fragment",
]

# There are edge cases where we don't need these; don't make extra priority filler if we don't need them
# We're wasting a single item here with moonpool fragments for the Cyclops... meh
if self.options.include_seamoth.value < 2 or \
self.options.include_prawn.value < 2 or \
self.options.include_cyclops.value < 2 or \
self.options.goal.get_event_name() == "Neptune Launch":
num += 2
priority_filler.append("Mobile Vehicle Bay Fragment")
priority_filler.append("Moonpool Fragment")

# Vehicle priority filler
if self.options.include_seamoth.value < 2:
priority_filler.append("Seamoth Fragment")
num += 1
if self.options.include_prawn.value < 2:
priority_filler.append("Prawn Suit Fragment")
num += 1
if self.options.include_cyclops.value < 2:
priority_filler.append("Cyclops Engine Fragment")
priority_filler.append("Cyclops Hull Fragment")
priority_filler.append("Cyclops Bridge Fragment")
num += 3

for item_name in self.random.sample(priority_filler, k=min(extras, num)):
item = self.create_item(item_name)
pool.append(item)
extras -= 1
Expand All @@ -165,6 +235,9 @@ def fill_slot_data(self) -> Dict[str, Any]:
"creatures_to_scan": self.creatures_to_scan,
"death_link": self.options.death_link.value,
"free_samples": self.options.free_samples.value,
"include_seamoth": self.options.include_seamoth.value,
"include_prawn": self.options.include_prawn.value,
"include_cyclops": self.options.include_cyclops.value,
}

return slot_data
Expand All @@ -176,6 +249,10 @@ def create_item(self, name: str) -> SubnauticaItem:
item_table[item_id].classification,
item_id, player=self.player)

def create_shifted_item(self, name: str, cls) -> SubnauticaItem:
item_id: int = self.item_name_to_id[name]
return SubnauticaItem(name, cls, item_id, player=self.player)

def get_filler_item_name(self) -> str:
item_names, cum_item_weights = self.options.filler_items_distribution.weights_pair
return self.random.choices(item_names,
Expand Down
120 changes: 78 additions & 42 deletions worlds/subnautica/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,62 +23,54 @@ def make_resource_bundle_data(display_name: str, internal_name: str = "") -> Ite
return ItemData(IC.filler, 0, display_name, internal_name, ItemType.resource)


item_table: Dict[int, ItemData] = {
35000: ItemData(IC.useful, 1, "Compass", "Compass"),
progression_table = {
35001: ItemData(IC.progression, 1, "Lightweight High Capacity Tank", "PlasteelTank"),
35002: ItemData(IC.progression, 1, "Vehicle Upgrade Console", "BaseUpgradeConsole"),
35003: ItemData(IC.progression, 1, "Ultra Glide Fins", "UltraGlideFins"),
35004: ItemData(IC.useful, 1, "Cyclops Sonar Upgrade", "CyclopsSonarModule"),
35005: ItemData(IC.useful, 1, "Reinforced Dive Suit", "ReinforcedDiveSuit"),
35006: ItemData(IC.useful, 1, "Cyclops Thermal Reactor Module", "CyclopsThermalReactorModule"),
35007: ItemData(IC.filler, 1, "Water Filtration Suit", "WaterFiltrationSuit"),
35008: ItemData(IC.progression, 1, "Alien Containment", "BaseWaterPark"),
35009: ItemData(IC.useful, 1, "Creature Decoy", "CyclopsDecoy"),
35010: ItemData(IC.useful, 1, "Cyclops Fire Suppression System", "CyclopsFireSuppressionModule"),
35011: ItemData(IC.useful, 1, "Swim Charge Fins", "SwimChargeFins"),
35012: ItemData(IC.useful, 1, "Repulsion Cannon", "RepulsionCannon"),
35013: ItemData(IC.useful, 1, "Cyclops Decoy Tube Upgrade", "CyclopsDecoyModule"),
35014: ItemData(IC.progression, 1, "Cyclops Shield Generator", "CyclopsShieldModule"),
35015: ItemData(IC.progression, 1, "Cyclops Depth Module MK1", "CyclopsHullModule1"),
35016: ItemData(IC.useful, 1, "Cyclops Docking Bay Repair Module", "CyclopsSeamothRepairModule"),
35017: ItemData(IC.useful, 2, "Battery Charger fragment", "BatteryChargerFragment"),
35018: ItemData(IC.filler, 2, "Beacon Fragment", "BeaconFragment"),
35019: ItemData(IC.useful, 2, "Bioreactor Fragment", "BaseBioReactorFragment"),
35020: ItemData(IC.progression, 4, "Cyclops Bridge Fragment", "CyclopsBridgeFragment"),
35021: ItemData(IC.progression, 4, "Cyclops Engine Fragment", "CyclopsEngineFragment"),
35022: ItemData(IC.progression, 4, "Cyclops Hull Fragment", "CyclopsHullFragment"),
35023: ItemData(IC.filler, 2, "Grav Trap Fragment", "GravSphereFragment"),
35024: ItemData(IC.progression, 3, "Laser Cutter Fragment", "LaserCutterFragment"),
35025: ItemData(IC.filler, 2, "Light Stick Fragment", "TechlightFragment"),
35026: ItemData(IC.progression, 5, "Mobile Vehicle Bay Fragment", "ConstructorFragment"),
35027: ItemData(IC.progression, 3, "Modification Station Fragment", "WorkbenchFragment"),
35028: ItemData(IC.progression, 2, "Moonpool Fragment", "MoonpoolFragment"),
35029: ItemData(IC.useful, 3, "Nuclear Reactor Fragment", "BaseNuclearReactorFragment"),
35030: ItemData(IC.useful, 2, "Power Cell Charger Fragment", "PowerCellChargerFragment"),
35031: ItemData(IC.filler, 1, "Power Transmitter Fragment", "PowerTransmitterFragment"),
35032: ItemData(IC.progression, 6, "Prawn Suit Fragment", "ExosuitFragment"),
35033: ItemData(IC.useful, 2, "Prawn Suit Drill Arm Fragment", "ExosuitDrillArmFragment"),
35034: ItemData(IC.useful, 2, "Prawn Suit Grappling Arm Fragment", "ExosuitGrapplingArmFragment"),
35035: ItemData(IC.useful, 2, "Prawn Suit Propulsion Cannon Fragment", "ExosuitPropulsionArmFragment"),
35036: ItemData(IC.useful, 2, "Prawn Suit Torpedo Arm Fragment", "ExosuitTorpedoArmFragment"),
35037: ItemData(IC.useful, 3, "Scanner Room Fragment", "BaseMapRoomFragment"),
35038: ItemData(IC.progression, 5, "Seamoth Fragment", "SeamothFragment"),
35039: ItemData(IC.progression, 2, "Stasis Rifle Fragment", "StasisRifleFragment"),
35040: ItemData(IC.useful, 2, "Thermal Plant Fragment", "ThermalPlantFragment"),
35041: ItemData(IC.progression, 4, "Seaglide Fragment", "SeaglideFragment"),
35042: ItemData(IC.progression, 1, "Radiation Suit", "RadiationSuit"),
35043: ItemData(IC.progression, 2, "Propulsion Cannon Fragment", "PropulsionCannonFragment"),
35044: ItemData(IC.progression_skip_balancing, 1, "Neptune Launch Platform", "RocketBase"),
35045: ItemData(IC.progression, 1, "Ion Power Cell", "PrecursorIonPowerCell"),
35046: ItemData(IC.filler, 2, "Exterior Growbed", "FarmingTray"),
35053: ItemData(IC.progression, 1, "Multipurpose Room", "BaseRoom"),
35075: ItemData(IC.progression, 1, "Ion Battery", "PrecursorIonBattery"),
35076: ItemData(IC.progression_skip_balancing, 1, "Neptune Gantry", "RocketBaseLadder"),
35077: ItemData(IC.progression_skip_balancing, 1, "Neptune Boosters", "RocketStage1"),
35078: ItemData(IC.progression_skip_balancing, 1, "Neptune Fuel Reserve", "RocketStage2"),
35079: ItemData(IC.progression_skip_balancing, 1, "Neptune Cockpit", "RocketStage3"),
35081: ItemData(IC.progression, 1, "Ultra High Capacity Tank", "HighCapacityTank"),
35082: ItemData(IC.progression, 1, "Large Room", "BaseLargeRoom"),
}

useful_table = {
35000: ItemData(IC.useful, 1, "Compass", "Compass"),
35005: ItemData(IC.useful, 1, "Reinforced Dive Suit", "ReinforcedDiveSuit"),
35011: ItemData(IC.useful, 1, "Swim Charge Fins", "SwimChargeFins"),
35012: ItemData(IC.useful, 1, "Repulsion Cannon", "RepulsionCannon"),
35017: ItemData(IC.useful, 2, "Battery Charger fragment", "BatteryChargerFragment"),
35030: ItemData(IC.useful, 2, "Power Cell Charger Fragment", "PowerCellChargerFragment"),
35037: ItemData(IC.useful, 3, "Scanner Room Fragment", "BaseMapRoomFragment"),
35054: ItemData(IC.useful, 1, "Bulkhead", "BaseBulkhead"),
}

junk_table = {
35007: ItemData(IC.filler, 1, "Water Filtration Suit", "WaterFiltrationSuit"),
35018: ItemData(IC.filler, 2, "Beacon Fragment", "BeaconFragment"),
35023: ItemData(IC.filler, 2, "Grav Trap Fragment", "GravSphereFragment"),
35025: ItemData(IC.filler, 2, "Light Stick Fragment", "TechlightFragment"),
35047: ItemData(IC.filler, 1, "Picture Frame", "PictureFrameFragment"),
35048: ItemData(IC.filler, 1, "Bench", "Bench"),
35049: ItemData(IC.filler, 1, "Basic Plant Pot", "PlanterPotFragment"),
35050: ItemData(IC.filler, 1, "Interior Growbed", "PlanterBoxFragment"),
35051: ItemData(IC.filler, 1, "Plant Shelf", "PlanterShelfFragment"),
35052: ItemData(IC.filler, 1, "Observatory", "BaseObservatory"),
35053: ItemData(IC.progression, 1, "Multipurpose Room", "BaseRoom"),
35054: ItemData(IC.useful, 1, "Bulkhead", "BaseBulkhead"),
35055: ItemData(IC.filler, 1, "Spotlight", "Spotlight"),
35056: ItemData(IC.filler, 1, "Desk", "StarshipDesk"),
35057: ItemData(IC.filler, 1, "Swivel Chair", "StarshipChair"),
Expand All @@ -99,14 +91,8 @@ def make_resource_bundle_data(display_name: str, internal_name: str = "") -> Ite
35072: ItemData(IC.filler, 1, "Chic Plant Pot", "PlanterPot3"),
35073: ItemData(IC.filler, 1, "Nuclear Waste Disposal", "LabTrashcan"),
35074: ItemData(IC.filler, 1, "Wall Planter", "BasePlanter"),
35075: ItemData(IC.progression, 1, "Ion Battery", "PrecursorIonBattery"),
35076: ItemData(IC.progression_skip_balancing, 1, "Neptune Gantry", "RocketBaseLadder"),
35077: ItemData(IC.progression_skip_balancing, 1, "Neptune Boosters", "RocketStage1"),
35078: ItemData(IC.progression_skip_balancing, 1, "Neptune Fuel Reserve", "RocketStage2"),
35079: ItemData(IC.progression_skip_balancing, 1, "Neptune Cockpit", "RocketStage3"),
35080: ItemData(IC.filler, 1, "Water Filtration Machine", "BaseFiltrationMachine"),
35081: ItemData(IC.progression, 1, "Ultra High Capacity Tank", "HighCapacityTank"),
35082: ItemData(IC.progression, 1, "Large Room", "BaseLargeRoom"),

# awarded with their rooms, keeping that as-is as they"re cosmetic
35083: ItemData(IC.filler, 0, "Large Room Glass Dome", "BaseLargeGlassDome"),
35084: ItemData(IC.filler, 0, "Multipurpose Room Glass Dome", "BaseGlassDome"),
Expand Down Expand Up @@ -141,6 +127,55 @@ def make_resource_bundle_data(display_name: str, internal_name: str = "") -> Ite
35213: make_resource_bundle_data("Reactor Rod", "ReactorRod"),
}

non_vehicle_depth_table = {
35019: ItemData(IC.useful, 2, "Bioreactor Fragment", "BaseBioReactorFragment"),
35029: ItemData(IC.useful, 3, "Nuclear Reactor Fragment", "BaseNuclearReactorFragment"),
35031: ItemData(IC.useful, 1, "Power Transmitter Fragment", "PowerTransmitterFragment"),
35040: ItemData(IC.useful, 2, "Thermal Plant Fragment", "ThermalPlantFragment"),
35046: ItemData(IC.useful, 2, "Exterior Growbed", "FarmingTray"),
}

seamoth_table = {
35038: ItemData(IC.progression, 5, "Seamoth Fragment", "SeamothFragment"),
}

prawn_table = {
35032: ItemData(IC.progression, 6, "Prawn Suit Fragment", "ExosuitFragment"),
35033: ItemData(IC.useful, 2, "Prawn Suit Drill Arm Fragment", "ExosuitDrillArmFragment"),
35034: ItemData(IC.useful, 2, "Prawn Suit Grappling Arm Fragment", "ExosuitGrapplingArmFragment"),
35035: ItemData(IC.useful, 2, "Prawn Suit Propulsion Cannon Fragment", "ExosuitPropulsionArmFragment"),
35036: ItemData(IC.useful, 2, "Prawn Suit Torpedo Arm Fragment", "ExosuitTorpedoArmFragment"),
}

cyclops_table = {
35004: ItemData(IC.useful, 1, "Cyclops Sonar Upgrade", "CyclopsSonarModule"),
35006: ItemData(IC.useful, 1, "Cyclops Thermal Reactor Module", "CyclopsThermalReactorModule"),
35009: ItemData(IC.useful, 1, "Creature Decoy", "CyclopsDecoy"),
35010: ItemData(IC.useful, 1, "Cyclops Fire Suppression System", "CyclopsFireSuppressionModule"),
35013: ItemData(IC.useful, 1, "Cyclops Decoy Tube Upgrade", "CyclopsDecoyModule"),
35015: ItemData(IC.progression, 1, "Cyclops Depth Module MK1", "CyclopsHullModule1"),
35016: ItemData(IC.useful, 1, "Cyclops Docking Bay Repair Module", "CyclopsSeamothRepairModule"),
35020: ItemData(IC.progression, 4, "Cyclops Bridge Fragment", "CyclopsBridgeFragment"),
35021: ItemData(IC.progression, 4, "Cyclops Engine Fragment", "CyclopsEngineFragment"),
35022: ItemData(IC.progression, 4, "Cyclops Hull Fragment", "CyclopsHullFragment"),
}

base_item_table: Dict[int, ItemData] = {
**progression_table,
**useful_table,
**junk_table,
}

item_table: Dict[int, ItemData] = {
**progression_table,
**useful_table,
**junk_table,
**non_vehicle_depth_table,
**seamoth_table,
**prawn_table,
**cyclops_table
}


items_by_type: Dict[ItemType, List[int]] = {item_type: [] for item_type in ItemType}
for item_id, item_data in item_table.items():
Expand All @@ -152,6 +187,7 @@ def make_resource_bundle_data(display_name: str, internal_name: str = "") -> Ite
group_items: Dict[int, Set[int]] = {
35100: {35025, 35047, 35048, 35056, 35057, 35058, 35059, 35060, 35061, 35062, 35063, 35064, 35065, 35067, 35068,
35069, 35070, 35073, 35074},
# Removing 35050 from here will allow the Interior Growbed to spawn (but takes an extra item)
35101: {35049, 35050, 35051, 35071, 35072, 35074},
35102: set(items_by_type[ItemType.resource]),
}
Loading
Loading