diff --git a/worlds/subnautica/Exports.py b/worlds/subnautica/Exports.py index 1a367517..7a69cffc 100644 --- a/worlds/subnautica/Exports.py +++ b/worlds/subnautica/Exports.py @@ -13,11 +13,17 @@ if __name__ == "__main__": sys.path.append(new_home) from worlds.subnautica.Locations import Vector, location_table - from worlds.subnautica.Items import item_table, group_items + from worlds.subnautica.Items import item_table, group_items, items_by_type from NetUtils import encode + export_folder = os.path.join(new_home, "Subnautica Export") + os.makedirs(export_folder, exist_ok=True) + + def in_export_folder(path: str) -> str: + return os.path.join(export_folder, path) + payload = {location_id: location_data["position"] for location_id, location_data in location_table.items()} - with open("locations.json", "w") as f: + with open(in_export_folder("locations.json"), "w") as f: json.dump(payload, f) # copy-paste from Rules @@ -49,17 +55,22 @@ if __name__ == "__main__": "751": [location_id for location_id, location_data in location_table.items() if far_away(location_data["position"])], } - with open("logic.json", "w") as f: + with open(in_export_folder("logic.json"), "w") as f: json.dump(payload, f) - itemcount = sum(item_data["count"] for item_data in item_table.values()) + itemcount = sum(item_data.count for item_data in item_table.values()) assert itemcount == len(location_table), f"{itemcount} != {len(location_table)}" - payload = {item_id: item_data["tech_type"] for item_id, item_data in item_table.items()} + payload = {item_id: item_data.tech_type for item_id, item_data in item_table.items()} import json - with open("items.json", "w") as f: + with open(in_export_folder("items.json"), "w") as f: json.dump(payload, f) - with open("group_items.json", "w") as f: + + with open(in_export_folder("group_items.json"), "w") as f: + # encode to convert set to list f.write(encode(group_items)) - print(f"Subnautica exports dumped to {new_home}") + with open(in_export_folder("item_types.json"), "w") as f: + json.dump(items_by_type, f) + + print(f"Subnautica exports dumped to {in_export_folder('')}") diff --git a/worlds/subnautica/Items.py b/worlds/subnautica/Items.py index 9529e0ba..bffc8432 100644 --- a/worlds/subnautica/Items.py +++ b/worlds/subnautica/Items.py @@ -1,390 +1,154 @@ -from BaseClasses import ItemClassification -from typing import TypedDict, Dict, Set +from BaseClasses import ItemClassification as IC +from typing import NamedTuple, Dict, Set, List +from enum import IntEnum -class ItemDict(TypedDict): - classification: ItemClassification +class ItemType(IntEnum): + technology = 1 + resource = 2 + group = 3 + + +class ItemData(NamedTuple): + classification: IC count: int name: str tech_type: str + type: ItemType = ItemType.technology -item_table: Dict[int, ItemDict] = { - 35000: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Compass', - 'tech_type': 'Compass'}, - 35001: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Lightweight High Capacity Tank', - 'tech_type': 'PlasteelTank'}, - 35002: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Vehicle Upgrade Console', - 'tech_type': 'BaseUpgradeConsole'}, - 35003: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Ultra Glide Fins', - 'tech_type': 'UltraGlideFins'}, - 35004: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Cyclops Sonar Upgrade', - 'tech_type': 'CyclopsSonarModule'}, - 35005: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Reinforced Dive Suit', - 'tech_type': 'ReinforcedDiveSuit'}, - 35006: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Cyclops Thermal Reactor Module', - 'tech_type': 'CyclopsThermalReactorModule'}, - 35007: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Water Filtration Suit', - 'tech_type': 'WaterFiltrationSuit'}, - 35008: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Alien Containment', - 'tech_type': 'BaseWaterPark'}, - 35009: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Creature Decoy', - 'tech_type': 'CyclopsDecoy'}, - 35010: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Cyclops Fire Suppression System', - 'tech_type': 'CyclopsFireSuppressionModule'}, - 35011: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Swim Charge Fins', - 'tech_type': 'SwimChargeFins'}, - 35012: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Repulsion Cannon', - 'tech_type': 'RepulsionCannon'}, - 35013: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Cyclops Decoy Tube Upgrade', - 'tech_type': 'CyclopsDecoyModule'}, - 35014: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Cyclops Shield Generator', - 'tech_type': 'CyclopsShieldModule'}, - 35015: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Cyclops Depth Module MK1', - 'tech_type': 'CyclopsHullModule1'}, - 35016: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Cyclops Docking Bay Repair Module', - 'tech_type': 'CyclopsSeamothRepairModule'}, - 35017: {'classification': ItemClassification.useful, - 'count': 2, - 'name': 'Battery Charger fragment', - 'tech_type': 'BatteryChargerFragment'}, - 35018: {'classification': ItemClassification.filler, - 'count': 2, - 'name': 'Beacon Fragment', - 'tech_type': 'BeaconFragment'}, - 35019: {'classification': ItemClassification.useful, - 'count': 2, - 'name': 'Bioreactor Fragment', - 'tech_type': 'BaseBioReactorFragment'}, - 35020: {'classification': ItemClassification.progression, - 'count': 4, - 'name': 'Cyclops Bridge Fragment', - 'tech_type': 'CyclopsBridgeFragment'}, - 35021: {'classification': ItemClassification.progression, - 'count': 4, - 'name': 'Cyclops Engine Fragment', - 'tech_type': 'CyclopsEngineFragment'}, - 35022: {'classification': ItemClassification.progression, - 'count': 4, - 'name': 'Cyclops Hull Fragment', - 'tech_type': 'CyclopsHullFragment'}, - 35023: {'classification': ItemClassification.filler, - 'count': 2, - 'name': 'Grav Trap Fragment', - 'tech_type': 'GravSphereFragment'}, - 35024: {'classification': ItemClassification.progression, - 'count': 3, - 'name': 'Laser Cutter Fragment', - 'tech_type': 'LaserCutterFragment'}, - 35025: {'classification': ItemClassification.filler, - 'count': 2, - 'name': 'Light Stick Fragment', - 'tech_type': 'TechlightFragment'}, - 35026: {'classification': ItemClassification.progression, - 'count': 5, - 'name': 'Mobile Vehicle Bay Fragment', - 'tech_type': 'ConstructorFragment'}, - 35027: {'classification': ItemClassification.progression, - 'count': 3, - 'name': 'Modification Station Fragment', - 'tech_type': 'WorkbenchFragment'}, - 35028: {'classification': ItemClassification.progression, - 'count': 2, - 'name': 'Moonpool Fragment', - 'tech_type': 'MoonpoolFragment'}, - 35029: {'classification': ItemClassification.useful, - 'count': 3, - 'name': 'Nuclear Reactor Fragment', - 'tech_type': 'BaseNuclearReactorFragment'}, - 35030: {'classification': ItemClassification.useful, - 'count': 2, - 'name': 'Power Cell Charger Fragment', - 'tech_type': 'PowerCellChargerFragment'}, - 35031: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Power Transmitter Fragment', - 'tech_type': 'PowerTransmitterFragment'}, - 35032: {'classification': ItemClassification.progression, - 'count': 6, - 'name': 'Prawn Suit Fragment', - 'tech_type': 'ExosuitFragment'}, - 35033: {'classification': ItemClassification.useful, - 'count': 2, - 'name': 'Prawn Suit Drill Arm Fragment', - 'tech_type': 'ExosuitDrillArmFragment'}, - 35034: {'classification': ItemClassification.useful, - 'count': 2, - 'name': 'Prawn Suit Grappling Arm Fragment', - 'tech_type': 'ExosuitGrapplingArmFragment'}, - 35035: {'classification': ItemClassification.useful, - 'count': 2, - 'name': 'Prawn Suit Propulsion Cannon Fragment', - 'tech_type': 'ExosuitPropulsionArmFragment'}, - 35036: {'classification': ItemClassification.useful, - 'count': 2, - 'name': 'Prawn Suit Torpedo Arm Fragment', - 'tech_type': 'ExosuitTorpedoArmFragment'}, - 35037: {'classification': ItemClassification.useful, - 'count': 3, - 'name': 'Scanner Room Fragment', - 'tech_type': 'BaseMapRoomFragment'}, - 35038: {'classification': ItemClassification.progression, - 'count': 5, - 'name': 'Seamoth Fragment', - 'tech_type': 'SeamothFragment'}, - 35039: {'classification': ItemClassification.progression, - 'count': 2, - 'name': 'Stasis Rifle Fragment', - 'tech_type': 'StasisRifleFragment'}, - 35040: {'classification': ItemClassification.useful, - 'count': 2, - 'name': 'Thermal Plant Fragment', - 'tech_type': 'ThermalPlantFragment'}, - 35041: {'classification': ItemClassification.progression, - 'count': 4, - 'name': 'Seaglide Fragment', - 'tech_type': 'SeaglideFragment'}, - 35042: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Radiation Suit', - 'tech_type': 'RadiationSuit'}, - 35043: {'classification': ItemClassification.progression, - 'count': 2, - 'name': 'Propulsion Cannon Fragment', - 'tech_type': 'PropulsionCannonFragment'}, - 35044: {'classification': ItemClassification.progression_skip_balancing, - 'count': 1, - 'name': 'Neptune Launch Platform', - 'tech_type': 'RocketBase'}, - 35045: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Ion Power Cell', - 'tech_type': 'PrecursorIonPowerCell'}, - 35046: {'classification': ItemClassification.filler, - 'count': 2, - 'name': 'Exterior Growbed', - 'tech_type': 'FarmingTray'}, - 35047: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Picture Frame', - 'tech_type': 'PictureFrameFragment'}, - 35048: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Bench', - 'tech_type': 'Bench'}, - 35049: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Basic Plant Pot', - 'tech_type': 'PlanterPotFragment'}, - 35050: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Interior Growbed', - 'tech_type': 'PlanterBoxFragment'}, - 35051: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Plant Shelf', - 'tech_type': 'PlanterShelfFragment'}, - 35052: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Observatory', - 'tech_type': 'BaseObservatory'}, - 35053: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Multipurpose Room', - 'tech_type': 'BaseRoom'}, - 35054: {'classification': ItemClassification.useful, - 'count': 1, - 'name': 'Bulkhead', - 'tech_type': 'BaseBulkhead'}, - 35055: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Spotlight', - 'tech_type': 'Spotlight'}, - 35056: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Desk', - 'tech_type': 'StarshipDesk'}, - 35057: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Swivel Chair', - 'tech_type': 'StarshipChair'}, - 35058: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Office Chair', - 'tech_type': 'StarshipChair2'}, - 35059: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Command Chair', - 'tech_type': 'StarshipChair3'}, - 35060: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Counter', - 'tech_type': 'LabCounter'}, - 35061: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Single Bed', - 'tech_type': 'NarrowBed'}, - 35062: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Basic Double Bed', - 'tech_type': 'Bed1'}, - 35063: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Quilted Double Bed', - 'tech_type': 'Bed2'}, - 35064: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Coffee Vending Machine', - 'tech_type': 'CoffeeVendingMachine'}, - 35065: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Trash Can', - 'tech_type': 'Trashcans'}, - 35066: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Floodlight', - 'tech_type': 'Techlight'}, - 35067: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Bar Table', - 'tech_type': 'BarTable'}, - 35068: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Vending Machine', - 'tech_type': 'VendingMachine'}, - 35069: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Single Wall Shelf', - 'tech_type': 'SingleWallShelf'}, - 35070: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Wall Shelves', - 'tech_type': 'WallShelves'}, - 35071: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Round Plant Pot', - 'tech_type': 'PlanterPot2'}, - 35072: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Chic Plant Pot', - 'tech_type': 'PlanterPot3'}, - 35073: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Nuclear Waste Disposal', - 'tech_type': 'LabTrashcan'}, - 35074: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Wall Planter', - 'tech_type': 'BasePlanter'}, - 35075: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Ion Battery', - 'tech_type': 'PrecursorIonBattery'}, - 35076: {'classification': ItemClassification.progression_skip_balancing, - 'count': 1, - 'name': 'Neptune Gantry', - 'tech_type': 'RocketBaseLadder'}, - 35077: {'classification': ItemClassification.progression_skip_balancing, - 'count': 1, - 'name': 'Neptune Boosters', - 'tech_type': 'RocketStage1'}, - 35078: {'classification': ItemClassification.progression_skip_balancing, - 'count': 1, - 'name': 'Neptune Fuel Reserve', - 'tech_type': 'RocketStage2'}, - 35079: {'classification': ItemClassification.progression_skip_balancing, - 'count': 1, - 'name': 'Neptune Cockpit', - 'tech_type': 'RocketStage3'}, - 35080: {'classification': ItemClassification.filler, - 'count': 1, - 'name': 'Water Filtration Machine', - 'tech_type': 'BaseFiltrationMachine'}, - 35081: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Ultra High Capacity Tank', - 'tech_type': 'HighCapacityTank'}, - 35082: {'classification': ItemClassification.progression, - 'count': 1, - 'name': 'Large Room', - 'tech_type': 'BaseLargeRoom'}, - # awarded with their rooms, keeping that as-is as they're cosmetic - 35083: {'classification': ItemClassification.filler, - 'count': 0, - 'name': 'Large Room Glass Dome', - 'tech_type': 'BaseLargeGlassDome'}, - 35084: {'classification': ItemClassification.filler, - 'count': 0, - 'name': 'Multipurpose Room Glass Dome', - 'tech_type': 'BaseGlassDome'}, - 35085: {'classification': ItemClassification.filler, - 'count': 0, - 'name': 'Partition', - 'tech_type': 'BasePartition'}, - 35086: {'classification': ItemClassification.filler, - 'count': 0, - 'name': 'Partition Door', - 'tech_type': 'BasePartitionDoor'}, - # new items that the mod implements +def make_resource_bundle_data(display_name: str, internal_name: str = "") -> ItemData: + if not internal_name: + internal_name = display_name + return ItemData(IC.filler, 0, display_name, internal_name, ItemType.resource) + +item_table: Dict[int, ItemData] = { + 35000: ItemData(IC.useful, 1, "Compass", "Compass"), + 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"), + 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"), + 35058: ItemData(IC.filler, 1, "Office Chair", "StarshipChair2"), + 35059: ItemData(IC.filler, 1, "Command Chair", "StarshipChair3"), + 35060: ItemData(IC.filler, 1, "Counter", "LabCounter"), + 35061: ItemData(IC.filler, 1, "Single Bed", "NarrowBed"), + 35062: ItemData(IC.filler, 1, "Basic Double Bed", "Bed1"), + 35063: ItemData(IC.filler, 1, "Quilted Double Bed", "Bed2"), + 35064: ItemData(IC.filler, 1, "Coffee Vending Machine", "CoffeeVendingMachine"), + 35065: ItemData(IC.filler, 1, "Trash Can", "Trashcans"), + 35066: ItemData(IC.filler, 1, "Floodlight", "Techlight"), + 35067: ItemData(IC.filler, 1, "Bar Table", "BarTable"), + 35068: ItemData(IC.filler, 1, "Vending Machine", "VendingMachine"), + 35069: ItemData(IC.filler, 1, "Single Wall Shelf", "SingleWallShelf"), + 35070: ItemData(IC.filler, 1, "Wall Shelves", "WallShelves"), + 35071: ItemData(IC.filler, 1, "Round Plant Pot", "PlanterPot2"), + 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"), + 35085: ItemData(IC.filler, 0, "Partition", "BasePartition"), + 35086: ItemData(IC.filler, 0, "Partition Door", "BasePartitionDoor"), + + # Bundles of items # Awards all furniture as a bundle - 35100: {'classification': ItemClassification.filler, - 'count': 0, - 'name': 'Furniture', - 'tech_type': 'Furniture'}, + 35100: ItemData(IC.filler, 0, "Furniture", "AP_Furniture", ItemType.group), # Awards all farming blueprints as a bundle - 35101: {'classification': ItemClassification.filler, - 'count': 0, - 'name': 'Farming', - 'tech_type': 'Farming'}, + 35101: ItemData(IC.filler, 0, "Farming", "AP_Farming", ItemType.group), + + # Awards multiple resources as a bundle + 35102: ItemData(IC.filler, 0, "Resources Bundle", "AP_Resources", ItemType.group), + + # resource bundles, as convenience/filler + + # ores + 35200: make_resource_bundle_data("Titanium"), + 35201: make_resource_bundle_data("Copper Ore", "Copper"), + 35202: make_resource_bundle_data("Silver Ore", "Silver"), + 35203: make_resource_bundle_data("Gold"), + 35204: make_resource_bundle_data("Lead"), + 35205: make_resource_bundle_data("Diamond"), + 35206: make_resource_bundle_data("Lithium"), + 35207: make_resource_bundle_data("Ruby", "AluminumOxide"), + 35208: make_resource_bundle_data("Nickel Ore", "Nickel"), + 35209: make_resource_bundle_data("Crystalline Sulfur", "Sulphur"), + 35210: make_resource_bundle_data("Salt Deposit", "Salt"), + 35211: make_resource_bundle_data("Kyanite"), + 35212: make_resource_bundle_data("Magnetite"), + 35213: make_resource_bundle_data("Reactor Rod", "ReactorRod"), } -advancement_item_names: Set[str] = set() -non_advancement_item_names: Set[str] = set() +items_by_type: Dict[ItemType, List[int]] = {item_type: [] for item_type in ItemType} for item_id, item_data in item_table.items(): - item_name = item_data["name"] - if ItemClassification.progression in item_data["classification"]: - advancement_item_names.add(item_name) - else: - non_advancement_item_names.add(item_name) + items_by_type[item_data.type].append(item_id) 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}, - 35101: {35049, 35050, 35051, 35071, 35072, 35074} + 35101: {35049, 35050, 35051, 35071, 35072, 35074}, + 35102: set(items_by_type[ItemType.resource]), } diff --git a/worlds/subnautica/__init__.py b/worlds/subnautica/__init__.py index 0807f1a7..1684260c 100644 --- a/worlds/subnautica/__init__.py +++ b/worlds/subnautica/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations import logging import itertools -from typing import List, Dict, Any +from typing import List, Dict, Any, cast from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification from worlds.AutoWorld import World, WebWorld @@ -10,7 +10,7 @@ from . import Items from . import Locations from . import Creatures from . import Options -from .Items import item_table, group_items +from .Items import item_table, group_items, items_by_type, ItemType from .Rules import set_rules logger = logging.getLogger("Subnautica") @@ -40,12 +40,12 @@ class SubnauticaWorld(World): game = "Subnautica" web = SubnaticaWeb() - item_name_to_id = {data["name"]: item_id for item_id, data in Items.item_table.items()} + item_name_to_id = {data.name: item_id for item_id, data in Items.item_table.items()} location_name_to_id = all_locations option_definitions = Options.options - data_version = 9 - required_client_version = (0, 4, 0) + data_version = 10 + required_client_version = (0, 4, 1) creatures_to_scan: List[str] @@ -61,8 +61,8 @@ class SubnauticaWorld(World): self.multiworld.creature_scans[self.player].value ) - self.creatures_to_scan = self.multiworld.random.sample(creature_pool, - self.multiworld.creature_scans[self.player].value) + self.creatures_to_scan = self.multiworld.random.sample( + creature_pool, self.multiworld.creature_scans[self.player].value) def create_regions(self): self.multiworld.regions += [ @@ -94,27 +94,46 @@ class SubnauticaWorld(World): for item_id, item in item_table.items(): if item_id in grouped: - extras += item["count"] + extras += item.count else: - for i in range(item["count"]): - subnautica_item = self.create_item(item["name"]) - if item["name"] == "Neptune Launch Platform": + for i in range(item.count): + subnautica_item = self.create_item(item.name) + if item.name == "Neptune Launch Platform": self.multiworld.get_location("Aurora - Captain Data Terminal", self.player).place_locked_item( subnautica_item) else: pool.append(subnautica_item) - group_amount: int = 3 + group_amount: int = 2 assert len(group_items) * group_amount <= extras - for name in ("Furniture", "Farming"): + for item_id in group_items: + name = item_table[item_id].name for _ in range(group_amount): pool.append(self.create_item(name)) extras -= group_amount - for item_name in self.multiworld.random.choices( - sorted(Items.advancement_item_names - {"Neptune Launch Platform"}), k=extras): + for item_name in self.multiworld.random.sample( + # list of high-count important fragments as priority filler + [ + "Cyclops Engine Fragment", + "Modification Station Fragment", + "Mobile Vehicle Bay Fragment", + "Seamoth Fragment", + "Cyclops Hull Fragment", + "Cyclops Bridge Fragment", + "Prawn Suit Fragment", + "Moonpool Fragment", + ], + k=min(extras, 8)): item = self.create_item(item_name) pool.append(item) + extras -= 1 + + # resource bundle filler + for _ in range(extras): + item = self.create_filler() + item = cast(SubnauticaItem, item) + pool.append(item) self.multiworld.itempool += pool @@ -138,7 +157,7 @@ class SubnauticaWorld(World): item_id: int = self.item_name_to_id[name] return SubnauticaItem(name, - item_table[item_id]["classification"], + item_table[item_id].classification, item_id, player=self.player) def create_region(self, name: str, locations=None, exits=None): @@ -153,6 +172,9 @@ class SubnauticaWorld(World): ret.exits.append(Entrance(self.player, region_exit, ret)) return ret + def get_filler_item_name(self) -> str: + return item_table[self.multiworld.random.choice(items_by_type[ItemType.resource])].name + class SubnauticaLocation(Location): game: str = "Subnautica" diff --git a/worlds/subnautica/test/__init__.py b/worlds/subnautica/test/__init__.py index b10ca2c7..d9388307 100644 --- a/worlds/subnautica/test/__init__.py +++ b/worlds/subnautica/test/__init__.py @@ -8,8 +8,18 @@ class SubnauticaTest(unittest.TestCase): def testIDRange(self): for name, id in subnautica.SubnauticaWorld.location_name_to_id.items(): - with self.subTest(item=name): + with self.subTest(location=name): if "Scan" in name: self.assertLess(self.scancutoff, id) else: self.assertGreater(self.scancutoff, id) + + def testGroupAssociation(self): + from worlds.subnautica import Items + for item_id, item_data in Items.item_table.items(): + if item_data.type == Items.ItemType.group: + with self.subTest(item=item_data.name): + self.assertIn(item_id, Items.group_items) + for item_id in Items.group_items: + with self.subTest(item_id=item_id): + self.assertEqual(Items.item_table[item_id].type, Items.ItemType.group)